wspromisify 2.9.3 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/bundle.cjs +1 -1
- package/dist/bundle.d.ts +2 -2
- package/dist/bundle.mjs +1 -1
- package/package.json +79 -79
- package/rollup.config.js +3 -5
- package/src/WSC.ts +32 -36
- package/src/types.ts +1 -1
- package/test/mock/WS.ts +16 -16
- package/test/specs/stream-comprehensive.ts +2 -8
- package/test/specs/stream-real.ts +16 -11
- package/test/specs/stream-simple.ts +4 -5
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ const responseData = await ws.send({catSaid: 'Meow!'})
|
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
// If you detected some bug, want some stuff to be added, feel free to open an issue!
|
|
14
|
-
Large data support (chunking), plugins
|
|
14
|
+
Large data support (chunking), plugins and different server-side implementations are coming.
|
|
15
15
|
To see a Node.js server-side part example, please, take a look on test/mock in github repo.
|
|
16
16
|
|
|
17
17
|
|
|
@@ -21,6 +21,7 @@ Features (almost all are tunable via constructor config below.)
|
|
|
21
21
|
- ES-module and commonjs built-in.
|
|
22
22
|
- Types (d.ts) included.
|
|
23
23
|
- Automatically reconnects.
|
|
24
|
+
- Streams are supported. For example, you can stream your AI response.
|
|
24
25
|
- Supports existent native WebSocket or ws-like implementation (ws npm package) via `socket` property.
|
|
25
26
|
- And provide your own socket instance via socket config prop.
|
|
26
27
|
- Any id and data keys to negotiate with your back-end.
|
|
@@ -71,7 +72,7 @@ Default constructor config is
|
|
|
71
72
|
socket: null,
|
|
72
73
|
// You can set your own middleware here.
|
|
73
74
|
adapter: ((host, protocols) => new WebSocket(host, protocols)),
|
|
74
|
-
// You can replace original serialisation to your own or even binary stuff.
|
|
75
|
+
// You can replace original serialisation to your own or even binary stuff. Eg MessagePack or CBOR.
|
|
75
76
|
encode: (message_id, message_data, config) => data,
|
|
76
77
|
// You can replace original deserialisation to your own or even
|
|
77
78
|
// making the message object from binary data.
|
|
@@ -106,14 +107,16 @@ Methods:
|
|
|
106
107
|
ready()
|
|
107
108
|
// sends any type of message and returns a Promise.
|
|
108
109
|
send(message),
|
|
110
|
+
// Streams as async generator, resolving in chunks.
|
|
111
|
+
// The server must send the same id for chunks then add done: true in the last one.
|
|
112
|
+
*stream(message),
|
|
109
113
|
// .addEventListener with optional predicate that works after reconnections.
|
|
110
114
|
on(event_name, handler, predicate = (WebSocketEvent) => true),
|
|
111
115
|
// Closes the connection and free up memory. Returns Promise that it has been done.
|
|
112
116
|
close()
|
|
113
|
-
|
|
114
117
|
```
|
|
115
118
|
|
|
116
|
-
Example:
|
|
119
|
+
Example (more in `tests` dir in the repo):
|
|
117
120
|
```javascript
|
|
118
121
|
|
|
119
122
|
import WSP from 'wspromisify' // or const WSP = require('wspromisify') in Node.
|
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(),i=n.length;let r=i,l=0;for(;r&&l<s;l++)o[l]===e&&(o[l]=n[i-r],r--);for(l=s;r;l++,r--)o[l]=n[i-r];return o},s=(e,o,i)=>{const r=e.length-o.length-t(i);if(r<1)return e(...n(o,i));{const t=(...t)=>s(e,n(o,i),t);return t.$args_left=r,t}},o=e=>(...n)=>e.length>t(n)?s(e,[],n):e(...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 i>1?o?(t=>function(n){return n===e?t:t(n)})((e=>t(e,s))):t(n,s):e=>t(n,e)}}function r(e){return o(e)}const l=void 0,c=1/0,a=e=>typeof e,u=e=>null===e,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=e=>{const t=a(e);return"object"===t?u(e)?"Null":e.constructor.name:h[t[0]]+t.slice(1)},g=e=>e.length,p=i(((e,t)=>e===t)),m=i(((e,t)=>{const n=d(e);if(p(n,d(t))&&(p(n,"Object")||p(n,"Array"))){if(u(e)||u(t))return p(e,t);if(p(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(p(n,t)&&s in e||p(n,e)&&s in t&&m(e[s],t[s])))return!1;return!0}return p(e,t)})),y=i(((e,t)=>(t.push(e),t))),w=r(((e,t,n)=>n.reduce(e,t))),_=o(((e,t,n,s)=>e(s)?t(s):n(s))),b=(...t)=>(...n)=>{let s,o=!0;for(let i=g(t)-1;i>-1;i--)o?(o=!1,s=t[i](...n)):s=s===e?t[i]():t[i](s);return s},S=i(((e,t)=>t[e])),k=r(((e,t,n)=>n.slice(e,(e=>"number"==a(e))(t)?t:c))),P=S(0);k(1,c);const v=i(((e,t)=>t.find(e))),E=e=>()=>e,N=i(((e,t)=>t.split(e))),q=E(!0),A=E(!1),W=i(((e,t)=>w(((t,n)=>v((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),$=r(((e,t,n)=>g(t)?(e=>u(e)||(e=>e===l)(e))(n)?e:b((s=>s in n?$(e,k(1,c,t),n[s]):e),P)(t):n));$(l),b(_(m(f),A,q),$(f));const j=i(((e,t)=>t.map(e))),{floor:z}=Math,C="0123456789abcdefghijklmnopqrstuvwxyz",Q=b((e=>Object.fromEntries(e)),j(((e,t)=>[e,t])),N(""));class O{abc;abclen;c2pos;standard;setABC(e){if(!b(m(g(t=e)),g,W,N(""))(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=Q(e)}zip(e){const{abc:t,abclen:n}=this;let s="";for(;e>0;)s=t[e%n]+s,e=z(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 i=0;for(let t=0;t<o;t++)i+=s[e[t]]*n**(o-t-1);return i}constructor(e){this.setABC(e||C+"ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}const R=new O;R.setABC.bind(R),R.zip.bind(R),R.unzip.bind(R);const T=(()=>{try{return WebSocket||null}catch{return null}})(),x=(e,t,n)=>e.addEventListener(t,n),B=(e,t)=>setTimeout(t,e),I={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,reconnection_attempts:1/0,max_idle_time:1/0,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"),D=e=>{let t=0;for(const n of e)n!==M&&t++;return t},L=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let i=o,r=0;for(;i&&r<n;r++)s[r]===M&&(s[r]=t[o-i],i--);for(r=n;i;r++,i--)s[r]=t[o-i];return s},U=(e,t,n)=>{const s=e.length-t.length-D(n);if(s<1)return e(...L(t,n));{const o=(...s)=>U(e,L(t,n),s);return o.$args_left=s,o}},F=e=>(...t)=>e.length>D(t)?U(e,[],t):e(...t);function J(e){return function(t,n){const s=t===M,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(e=>function(t){return t===M?e:e(t)})((t=>e(t,n))):e(t,n):n=>e(t,n)}}function G(e){return F(e)}const H=/^(.*?)(8|16|32|64)(Clamped)?Array$/,K=void 0,V=1/0,X=e=>typeof e,Y=e=>null===e,Z=e=>"number"==X(e);const ee=e=>Y(e)||(e=>e===K)(e),te={u:"U",b:"B",n:"N",s:"S",f:"F"},ne=Symbol(),se=e=>{const t=X(e);return"object"===t?Y(e)?"Null":e.constructor.name:te[t[0]]+t.slice(1)},oe=J(((e,t)=>se(t)===e)),ie=e=>e.length,re=J(((e,t)=>e===t)),le=J(((e,t)=>{const n=se(e);if(re(n,se(t))&&(re(n,"Object")||re(n,"Array")||(e=>H.test(e))(n))){if(Y(e)||Y(t))return re(e,t);if(re(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(re(n,t)&&s in e||re(n,e)&&s in t&&le(e[s],t[s])))return!1;return!0}return re(e,t)})),ce=J(((e,t)=>(t.push(e),t))),ae=G(((e,t,n)=>n.reduce(e,t))),ue=J(((e,t)=>{const n=(e=>Array.isArray(e))(t);let s,o;n&&(s=0,o=[]);for(let s in t)e(t[s],s)||(n?o.push(+s):delete t[s]);if(n)for(const e of o)t.splice(e-s++,1);return t})),he=F(((e,t,n,s)=>e(s)?t(s):n(s))),fe=(...e)=>(...t)=>{let n,s=!0;for(let o=ie(e)-1;o>-1;o--)s?(s=!1,n=e[o](...t)):n=n===M?e[o]():e[o](n);return n},de=J(((e,t)=>t[e])),ge=G(((e,t,n)=>n.slice(e,Z(t)?t:V))),pe=de(0);ge(1,V);const me=J(((e,t)=>t.find(e))),ye=e=>()=>e,we=ye(!0),_e=ye(!1),be=J(((e,t)=>t(...e))),Se=e=>(...t)=>{const n=e(...t),s=function(e){return"function"===X(e)}(n);return!s||s&&n.$args_left<=0?(e=>!e)(n):Se(n)},ke=J(((e,t)=>ae(((t,n)=>me((t=>e(n,t)),t)?t:ce(n,t)),[],t)));ke(le);const Pe=e=>{let t,n=!1;return function(...s){return n?t:(n=!0,t=e(...s))}},ve=G(((e,t,n)=>ie(t)?ee(n)?e:fe((s=>s in n?ve(e,ge(1,V,t),n[s]):e),pe)(t):n));ve(K),fe(he(le(ne),_e,we),ve(ne));const Ee=G(((e,t,n)=>t(n)&&e(n))),Ne=Se,{random:qe}=Math,Ae=new O,We=be([]),$e=Ee(oe("Number"),Ne(isNaN)),je={_is_ping:!0},ze=e=>e&&clearTimeout(e),Ce=e=>{const t=Ae.zip(2147483637*qe()|0);return t in e?Ce(e):t};module.exports=class{ws=null;intentionally_closed=!1;reconnect_timeout=null;queue={};onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};ping_timer=null;idle_timer=null;get opened(){return 1===this.ws?.readyState}init_flush(){ue(_e,this.queue)}call(e,...t){for(const n of this.handlers[e])n(...t)}log(e,t=null,n=null){const{config:s}=this;null===n?s.timer?s.log(e,null,t):s.log(e,t):s.log(e,n,t)}resetPing(){const{config:{ping:e},ping_timer:t}=this;e&&(ee(t)||clearTimeout(t),this.ping_timer=B(1e3*e.interval,(async()=>{const{ping_timer:t,opened:n}=this;n?(await this.send(e.content,je),this.resetPing()):clearTimeout(t)})))}resetIdle(){const{config:{max_idle_time:e},idle_timer:t}=this;e!==1/0&&(ee(t)||clearTimeout(t),this.idle_timer=B(1e3*e,(()=>this.opened&&this.close())))}initSocket(e){const{queue:t,config:n}=this;this.ws=e,this.onReadyQueue.forEach((e=>e())),this.onReadyQueue.splice(0);const{id_key:s,data_key:o}=n.server;this.call("open",e);for(const n in t)e.send(t[n].msg);null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),this.resetPing(),this.resetIdle(),x(e,"close",(async(...e)=>{this.log("close"),this.ws=null,this.onCloseQueue.forEach(We),this.onCloseQueue.splice(0),this.call("close",...e);let{reconnect:t,reconnection_attempts:s}=n;if($e(t)){const e=async()=>{if(this.intentionally_closed||!s)return;s--,this.log("reconnect"),ee(this.ws)||(this.ws.close(),this.ws=null);const n=await this.connect();ee(n)||(this.reconnect_timeout=setTimeout(e,1e3*t))};e()}})),x(e,"message",(e=>{try{const t=n.decode(e.data);if(this.call("message",{...e,data:t}),t[s]){const e=this.queue[t[s]];if(e){const n=e.sent_time?Date.now()-e.sent_time:null;this.log("message",t[o],n),e.ff(t[o])}}}catch(t){console.error(t,`WSP: Decode error. Got: ${e.data}`)}this.resetPing()}))}opening=!1;connect(){return new Promise((e=>{if(this.opened||this.opening)return e(null);this.opening=!0;const t=this.config,n=t.socket||t.adapter(t.url,t.protocols);if(!n||n.readyState>1)return this.opening=!1,this.ws=null,this.log("error","ready() on closing or closed state! status 2."),e(2);const s=Pe((t=>{this.opening=!1,e(t)}));x(n,"error",Pe((e=>{this.ws=null,this.log("error","status 3. Err: "+e.message),this.call("error",e),s(3)}))),n.readyState?(this.initSocket(n),s(null)):x(n,"open",Pe((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.config.lazy||this.opened?e():this.onReadyQueue.push(e)}))}on(e,t,n=we,s=!1){const o=e=>n(e)&&t(e);return s?x(this.ws,e,o):this.handlers[e].push(o),o}off(e,t,n=!1){if(n)return((e,t,n)=>e.removeEventListener(t,n))(this.ws,e,t);const s=this.handlers[e],o=s.indexOf(t);~o&&s.splice(o,1)}async close(){return new Promise(((e,t)=>{null===this.ws?t("WSP: closing a non-inited socket!"):(this.onCloseQueue.push((()=>{this.init_flush(),e(null)})),this.ws.close(),this.ws=null,this.intentionally_closed=!0)}))}open(){if(!this.opened)return this.intentionally_closed=!1,this.connect()}async prepareMessage(e,t={}){this.log(t._is_ping?"ping":"send",e);const{config:n,queue:s}=this,{pipes:o,server:{data_key:i}}=n,{top:r,_is_ping:l}=t,c=Ce(s);if("object"==typeof r&&r[i])throw new Error(`Attempting to set data key/token via ${t._is_ping?"ping":"send"}() options!`);for(const t of o)e=t(e);const[a,u]=await Promise.all([n.encode(c,e,n),this.connect()]);if(u)throw new Error("ERR while opening connection #"+u);this.opened&&(this.ws.send(a),this.resetPing(),l||this.resetIdle());const h=()=>delete this.queue[c];return{message_id:c,msg:a,timeout:t=>B(n.timeout,(()=>{c in s&&(this.call("timeout",e),t({"Websocket timeout expired":n.timeout,"for the message":e}),h())})),cleanup:h}}async send(e,t={}){const{message_id:n,msg:s,timeout:o,cleanup:i}=await this.prepareMessage(e,t),{queue:r,config:l}=this;return new Promise(((e,t)=>{const i=o(t);r[n]={msg:s,data_type:l.data_type,sent_time:l.timer?Date.now():null,ff(t){ze(i),e(t)}}})).finally(i)}async*stream(e,t={}){const{message_id:n,msg:s,timeout:o,cleanup:i}=await this.prepareMessage(e,t),{queue:r,config:l}=this;let c,a=!1,u=null;for(r[n]={msg:s,ff:e=>{c(e),e?.done&&(i(),a=!0)},data_type:l.data_type,sent_time:l.timer?Date.now():null};!a;)yield await new Promise(((e,t)=>{u=o(t),c=e})).finally((()=>{ze(u),u=null}))}constructor(e={}){this.config=(e=>{if(null===T&&!("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({},I,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.config.lazy||this.connect()}};
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const t=Symbol("Placeholder"),e=e=>{let n=0;for(const s of e)s!==t&&n++;return n},n=(e,n)=>{const s=e.length,i=e.slice(),o=n.length;let r=o,l=0;for(;r&&l<s;l++)i[l]===t&&(i[l]=n[o-r],r--);for(l=s;r;l++,r--)i[l]=n[o-r];return i},s=(t,i,o)=>{const r=t.length-i.length-e(o);if(r<1)return t(...n(i,o));{const e=(...e)=>s(t,n(i,o),e);return e.$args_left=r,e}},i=t=>(...n)=>t.length>e(n)?s(t,[],n):t(...n);function o(e){return function(n,...s){return s.length>0?n===t?(e=>function(n){return n===t?e:e(n)})((t=>e(t,s[0]))):e(n,s[0]):t=>e(n,t)}}function r(t){return i(t)}const l=t=>t.length,c=/^(.*?)(8|16|32|64)(Clamped)?Array$/,u=void 0,a=1/0,h=t=>typeof t,d=t=>null===t,f=t=>"number"==h(t);const p=t=>d(t)||(t=>t===u)(t),g={u:"U",b:"B",n:"N",s:"S",f:"F"},m=Symbol(),y=t=>{const e=h(t);return"object"===e?d(t)?"Null":t.constructor.name:g[e[0]]+e.slice(1)},_=o(((t,e)=>y(e)===t)),w=o(((t,e)=>t===e)),b=o(((t,e)=>{const n=y(t);if(w(n,y(e))&&(w(n,"Object")||w(n,"Array")||(t=>c.test(t))(n))){if(d(t)||d(e))return w(t,e);if(w(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(w(n,e)&&s in t||w(n,t)&&s in e&&b(t[s],e[s])))return!1;return!0}return w(t,e)})),k=o(((t,e)=>(e.push(t),e))),S=r(((t,e,n)=>n.reduce(t,e))),P=o(((t,e)=>{const n=(t=>Array.isArray(t))(e);let s,i;n&&(s=0,i=[]);for(let s in e)t(e[s],s)||(n?i.push(+s):delete e[s]);if(n)for(const t of i)e.splice(t-s++,1);return e})),v=i(((t,e,n,s)=>t(s)?e(s):n(s))),E=(...e)=>(...n)=>{let s,i=!0;for(let o=l(e)-1;o>-1;o--)i?(i=!1,s=e[o](...n)):s=s===t?e[o]():e[o](s);return s},A=o(((t,e)=>e[t])),C=r(((t,e,n)=>n.slice(t,f(e)?e:a))),j=A(0);C(1,a);const N=o(((t,e)=>e.find(t))),W=t=>()=>t,$=o(((t,e)=>(t(e),e))),q=W(!0),O=W(!1),x=o(((t,e)=>e(...t))),z=t=>(...e)=>{const n=t(...e),s=function(t){return"function"===h(t)}(n);return s&&n.$args_left?z(n):(t=>!t)(n)},Q=o(((t,e)=>S(((e,n)=>N((e=>t(n,e)),e)?e:k(n,e)),[],e)));Q(b);const T=t=>{let e,n=!1;return function(...s){return n?e:(n=!0,e=t(...s))}},R=(t,e,n)=>l(e)?p(n)?t:E((s=>s in n?R(t,C(1,a,e),n[s]):t),j)(e):n,B=r(R);B(u),E(v(b(m),O,q),B(m));const M=r(((t,e,n)=>e(n)&&t(n))),I=z,D=Symbol("Placeholder"),L=t=>{let e=0;for(const n of t)n!==D&&e++;return e},U=(t,e)=>{const n=t.length,s=t.slice(),i=e.length;let o=i,r=0;for(;o&&r<n;r++)s[r]===D&&(s[r]=e[i-o],o--);for(r=n;o;r++,o--)s[r]=e[i-o];return s},F=(t,e,n)=>{const s=t.length-e.length-L(n);if(s<1)return t(...U(e,n));{const i=(...s)=>F(t,U(e,n),s);return i.$args_left=s,i}},J=t=>(...e)=>t.length>L(e)?F(t,[],e):t(...e);function G(t){return function(e,...n){return n.length>0?e===D?(t=>function(e){return e===D?t:t(e)})((e=>t(e,n[0]))):t(e,n[0]):n=>t(e,n)}}function H(t){return J(t)}const K=t=>t.length,V=/^(.*?)(8|16|32|64)(Clamped)?Array$/,X=void 0,Y=1/0,Z=t=>typeof t,tt=t=>null===t,et={u:"U",b:"B",n:"N",s:"S",f:"F"},nt=Symbol(),st=t=>{const e=Z(t);return"object"===e?tt(t)?"Null":t.constructor.name:et[e[0]]+e.slice(1)},it=G(((t,e)=>st(e)===t)),ot=G(((t,e)=>t===e)),rt=G(((t,e)=>{const n=st(t);if(ot(n,st(e))&&(ot(n,"Object")||ot(n,"Array")||(s=n,V.test(s)))){if(tt(t)||tt(e))return ot(t,e);if(ot(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(ot(n,e)&&s in t||ot(n,t)&&s in e&&rt(t[s],e[s])))return!1;return!0}var s;return ot(t,e)})),lt=G(((t,e)=>(e.push(t),e))),ct=H(((t,e,n)=>n.reduce(t,e))),ut=J(((t,e,n,s)=>t(s)?e(s):n(s))),at=H(((t,e,n)=>ut(t,e,yt,n))),ht=(...t)=>(...e)=>{let n,s=!0;for(let i=K(t)-1;i>-1;i--)s?(s=!1,n=t[i](...e)):n=n===D?t[i]():t[i](n);return n},dt=G(((t,e)=>e[t])),ft=H(((t,e,n)=>n.slice(t,(t=>"number"==Z(t))(e)?e:Y))),pt=dt(0);ft(1,Y);const gt=G(((t,e)=>e.find(t))),mt=t=>()=>t,yt=t=>t,_t=G(((t,e)=>e.split(t))),wt=mt(!0),bt=mt(!1),kt=G(((t,e)=>ct(((e,n)=>gt((e=>t(n,e)),e)?e:lt(n,e)),[],e)))(rt),St=(t,e,n)=>K(e)?(t=>tt(t)||(t=>t===X)(t))(n)?t:ht((s=>s in n?St(t,ft(1,Y,e),n[s]):t),pt)(e):n,Pt=H(St);Pt(X),ht(ut(rt(nt),bt,wt),Pt(nt));const vt=G(((t,e)=>e.map(t))),{floor:Et}=Math,At="0123456789abcdefghijklmnopqrstuvwxyz",Ct=it("String"),jt=at(Ct,_t("")),Nt=ht((t=>Object.fromEntries(t)),vt(((t,e)=>[t,e])),jt);class Wt{is_str;delim;abc;abclen;c2pos;standard;setABC(t,e=""){if(this.is_str=Ct(t),this.delim=e,!ht(ot(K(n=t)),K,kt,jt)(n))throw new Error("Not all chars are unique!");var n;this.abc=t,this.abclen=K(t),this.standard=!!this.is_str&&At.startsWith(t),this.c2pos=Nt(t)}zip(t){const{abc:e,abclen:n,delim:s}=this;let i="",o=!0;for(;t>0;)i=e[t%n]+(o?"":s)+i,t=Et(t/n),o=!1;return i||"0"}unzip(t){const{standard:e,abclen:n,c2pos:s,delim:i,is_str:o}=this;if("0"===t)return 0;if(e)return parseInt(t,n);const r=o?t:t.split(i),l=K(r);let c=0;for(let t=0;t<l;t++)c+=s[r[t]]*n**(l-t-1);return c}constructor(t,e){e?this.setABC(t,e):this.setABC(t||At+"ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}const $t=new Wt;$t.setABC.bind($t),$t.zip.bind($t),$t.unzip.bind($t);const qt=(()=>{try{return WebSocket||null}catch{return null}})(),Ot=(t,e,n)=>t.addEventListener(e,n),xt=(t,e)=>setTimeout(e,t),zt={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,reconnection_attempts:1/0,max_idle_time:1/0,lazy:!1,socket:null,adapter:(t,e)=>new WebSocket(t,e),encode:(t,e,{server:n})=>JSON.stringify({[n.id_key]:t,[n.data_key]:e}),decode:t=>JSON.parse(t),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},{random:Qt}=Math,Tt=new Wt,Rt=x([]),Bt=M(_("Number"),I(isNaN)),Mt={_is_ping:!0},It=t=>t&&clearTimeout(t),Dt=t=>{const e=Tt.zip(2147483637*Qt()|0);return e in t?Dt(t):e};class Lt{ws=null;intentionally_closed=!1;reconnect_timeout=null;queue={};onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};ping_timer=null;idle_timer=null;get opened(){return 1===this.ws?.readyState}init_flush(){P(O,this.queue)}call(t,...e){for(const n of this.handlers[t])n(...e)}log(t,e=null,n=null){const{config:s}=this;setTimeout((()=>{null===n?s.timer?s.log(t,null,e):s.log(t,e):s.log(t,n,e)}))}resetPing(){const{config:{ping:t},ping_timer:e}=this;t&&(p(e)||clearTimeout(e),this.ping_timer=xt(1e3*t.interval,(async()=>{const{ping_timer:e,opened:n}=this;n?(await this.send(t.content,Mt),this.resetPing()):clearTimeout(e)})))}resetIdle(){const{config:{max_idle_time:t},idle_timer:e}=this;t!==1/0&&(p(e)||clearTimeout(e),this.idle_timer=xt(1e3*t,(()=>this.opened&&this.close())))}initSocket(t){const{queue:e,config:n}=this;this.ws=t,this.onReadyQueue.forEach((t=>t())),this.onReadyQueue.splice(0);const{id_key:s,data_key:i}=n.server;this.call("open",t);for(const n in e)t.send(e[n].msg);null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),this.resetPing(),this.resetIdle(),Ot(t,"close",(async(...t)=>{this.log("close"),this.ws=null,this.onCloseQueue.forEach(Rt),this.onCloseQueue.splice(0),this.call("close",...t);let{reconnect:e,reconnection_attempts:s}=n;if(Bt(e)){const t=async()=>{if(this.intentionally_closed||!s)return;s--,this.log("reconnect"),p(this.ws)||(this.ws.close(),this.ws=null);const n=await this.connect();p(n)||(this.reconnect_timeout=setTimeout(t,1e3*e))};t()}})),Ot(t,"message",(t=>{try{const e=n.decode(t.data);this.call("message",{...t,data:e});let o=!1;if(_("Object",e)&&s in e){const t=this.queue[e[s]];if(t){const n=t.sent_time?Date.now()-t.sent_time:null;this.log("message",e[i],n),t.ff(e[i]),o=!0}}o||this.log("message-ext",e)}catch(e){console.error(e,`WSP: Decode error. Got: ${t.data}`)}this.resetPing()}))}opening=!1;connect(){return new Promise((t=>{if(this.opened||this.opening)return t(null);this.opening=!0;const e=this.config,n=e.socket||e.adapter(e.url,e.protocols);if(!n||n.readyState>1)return this.opening=!1,this.ws=null,this.log("error","ready() on closing or closed state! status 2."),t(2);const s=T((e=>{this.opening=!1,t(e)}));Ot(n,"error",T((t=>{this.ws=null,this.log("error","status 3. Err: "+t.message),this.call("error",t),s(3)}))),n.readyState?(this.initSocket(n),s(null)):Ot(n,"open",T((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((t=>{this.config.lazy||this.opened?t():this.onReadyQueue.push(t)}))}on(t,e,n=q,s=!1){const i=t=>n(t)&&e(t);return s?Ot(this.ws,t,i):this.handlers[t].push(i),i}off(t,e,n=!1){if(n)return((t,e,n)=>t.removeEventListener(e,n))(this.ws,t,e);const s=this.handlers[t],i=s.indexOf(e);~i&&s.splice(i,1)}async close(){return new Promise(((t,e)=>{null===this.ws?e("WSP: closing a non-inited socket!"):(this.onCloseQueue.push((()=>{this.init_flush(),t(null)})),this.ws.close(),this.ws=null,this.intentionally_closed=!0)}))}open(){if(!this.opened)return this.intentionally_closed=!1,this.connect()}async prepareMessage(t,e={}){this.log(e._is_ping?"ping":"send",t);const{config:n,queue:s}=this,{pipes:i,server:{data_key:o}}=n,{top:r,_is_ping:l}=e,c=Dt(s);if("object"==typeof r&&r[o])throw new Error(`Attempting to set data key/token via ${e._is_ping?"ping":"send"}() options!`);for(const e of i)t=e(t);const[u,a]=await Promise.all([n.encode(c,t,n),this.connect()]);if(a)throw new Error("ERR while opening connection #"+a);const h=$((()=>delete this.queue[c]));return this.opened&&(xt(0,(()=>this.ws.send(u))),this.resetPing(),l||this.resetIdle()),{id:c,msg:u,timeout:e=>xt(n.timeout,(()=>{c in s&&(this.call("timeout",t),e({"Websocket timeout expired":n.timeout,"for the message":t}),h())})),cleanup:h}}async send(t,e={}){const{id:n,msg:s,timeout:i,cleanup:o}=await this.prepareMessage(t,e),{queue:r,config:l}=this;return new Promise(((t,e)=>{const o=i(e);r[n]={msg:s,data_type:l.data_type,sent_time:l.timer?Date.now():null,ff(e){It(o),t(e)}}})).finally(o)}async*stream(t,e={}){const{id:n,msg:s,timeout:i,cleanup:o}=await this.prepareMessage(t,e),{queue:r,config:l}=this;let c,u=!1,a=null;for(r[n]={msg:s,ff:t=>{c(t),t?.done&&(o(),u=!0)},data_type:l.data_type,sent_time:l.timer?Date.now():null};!u;)yield await new Promise(((t,e)=>{a=i(e),c=t})).catch((t=>o(t))).finally((()=>{It(a),a=null}))}constructor(t={}){this.config=(t=>{if(null===qt&&!("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({},zt,t),n=e.url;if("/"==n[0])try{const t=location.protocol.includes("s:")?"wss":"ws";e.url=`${t}://${location.hostname}:${location.port}${n}`}catch(t){throw new Error("WSP: URL starting with / in non-browser environment!")}return e})(t),this.config.lazy||this.connect()}}exports.WebSocketClient=Lt,exports.default=Lt;
|
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" | "timeout";
|
|
7
|
+
type WSEvent = "open" | "message" | "message-ext" | "close" | "error" | "timeout" | "reconnect" | "send" | "ping";
|
|
8
8
|
/** Minimal socket-like interface. */
|
|
9
9
|
interface Socket {
|
|
10
10
|
readyState: number;
|
|
@@ -58,7 +58,7 @@ declare namespace wsc {
|
|
|
58
58
|
sent_time: number | null;
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
-
declare class WebSocketClient {
|
|
61
|
+
export declare class WebSocketClient {
|
|
62
62
|
private ws;
|
|
63
63
|
private intentionally_closed;
|
|
64
64
|
private reconnect_timeout;
|
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(),i=n.length;let r=i,l=0;for(;r&&l<s;l++)o[l]===e&&(o[l]=n[i-r],r--);for(l=s;r;l++,r--)o[l]=n[i-r];return o},s=(e,o,i)=>{const r=e.length-o.length-t(i);if(r<1)return e(...n(o,i));{const t=(...t)=>s(e,n(o,i),t);return t.$args_left=r,t}},o=e=>(...n)=>e.length>t(n)?s(e,[],n):e(...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 i>1?o?(t=>function(n){return n===e?t:t(n)})((e=>t(e,s))):t(n,s):e=>t(n,e)}}function r(e){return o(e)}const l=void 0,c=1/0,a=e=>typeof e,u=e=>null===e,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=e=>{const t=a(e);return"object"===t?u(e)?"Null":e.constructor.name:h[t[0]]+t.slice(1)},g=e=>e.length,p=i(((e,t)=>e===t)),m=i(((e,t)=>{const n=d(e);if(p(n,d(t))&&(p(n,"Object")||p(n,"Array"))){if(u(e)||u(t))return p(e,t);if(p(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(p(n,t)&&s in e||p(n,e)&&s in t&&m(e[s],t[s])))return!1;return!0}return p(e,t)})),y=i(((e,t)=>(t.push(e),t))),w=r(((e,t,n)=>n.reduce(e,t))),_=o(((e,t,n,s)=>e(s)?t(s):n(s))),b=(...t)=>(...n)=>{let s,o=!0;for(let i=g(t)-1;i>-1;i--)o?(o=!1,s=t[i](...n)):s=s===e?t[i]():t[i](s);return s},S=i(((e,t)=>t[e])),k=r(((e,t,n)=>n.slice(e,(e=>"number"==a(e))(t)?t:c))),P=S(0);k(1,c);const v=i(((e,t)=>t.find(e))),E=e=>()=>e,N=i(((e,t)=>t.split(e))),q=E(!0),A=E(!1),W=i(((e,t)=>w(((t,n)=>v((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),$=r(((e,t,n)=>g(t)?(e=>u(e)||(e=>e===l)(e))(n)?e:b((s=>s in n?$(e,k(1,c,t),n[s]):e),P)(t):n));$(l),b(_(m(f),A,q),$(f));const j=i(((e,t)=>t.map(e))),{floor:z}=Math,C="0123456789abcdefghijklmnopqrstuvwxyz",Q=b((e=>Object.fromEntries(e)),j(((e,t)=>[e,t])),N(""));class O{abc;abclen;c2pos;standard;setABC(e){if(!b(m(g(t=e)),g,W,N(""))(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=Q(e)}zip(e){const{abc:t,abclen:n}=this;let s="";for(;e>0;)s=t[e%n]+s,e=z(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 i=0;for(let t=0;t<o;t++)i+=s[e[t]]*n**(o-t-1);return i}constructor(e){this.setABC(e||C+"ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}const R=new O;R.setABC.bind(R),R.zip.bind(R),R.unzip.bind(R);const T=(()=>{try{return WebSocket||null}catch{return null}})(),x=(e,t,n)=>e.addEventListener(t,n),B=(e,t)=>setTimeout(t,e),I={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,reconnection_attempts:1/0,max_idle_time:1/0,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"),D=e=>{let t=0;for(const n of e)n!==M&&t++;return t},L=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let i=o,r=0;for(;i&&r<n;r++)s[r]===M&&(s[r]=t[o-i],i--);for(r=n;i;r++,i--)s[r]=t[o-i];return s},U=(e,t,n)=>{const s=e.length-t.length-D(n);if(s<1)return e(...L(t,n));{const o=(...s)=>U(e,L(t,n),s);return o.$args_left=s,o}},F=e=>(...t)=>e.length>D(t)?U(e,[],t):e(...t);function J(e){return function(t,n){const s=t===M,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(e=>function(t){return t===M?e:e(t)})((t=>e(t,n))):e(t,n):n=>e(t,n)}}function G(e){return F(e)}const H=/^(.*?)(8|16|32|64)(Clamped)?Array$/,K=void 0,V=1/0,X=e=>typeof e,Y=e=>null===e,Z=e=>"number"==X(e);const ee=e=>Y(e)||(e=>e===K)(e),te={u:"U",b:"B",n:"N",s:"S",f:"F"},ne=Symbol(),se=e=>{const t=X(e);return"object"===t?Y(e)?"Null":e.constructor.name:te[t[0]]+t.slice(1)},oe=J(((e,t)=>se(t)===e)),ie=e=>e.length,re=J(((e,t)=>e===t)),le=J(((e,t)=>{const n=se(e);if(re(n,se(t))&&(re(n,"Object")||re(n,"Array")||(e=>H.test(e))(n))){if(Y(e)||Y(t))return re(e,t);if(re(e,t))return!0;for(const n of[e,t])for(const s in n)if(!(re(n,t)&&s in e||re(n,e)&&s in t&&le(e[s],t[s])))return!1;return!0}return re(e,t)})),ce=J(((e,t)=>(t.push(e),t))),ae=G(((e,t,n)=>n.reduce(e,t))),ue=J(((e,t)=>{const n=(e=>Array.isArray(e))(t);let s,o;n&&(s=0,o=[]);for(let s in t)e(t[s],s)||(n?o.push(+s):delete t[s]);if(n)for(const e of o)t.splice(e-s++,1);return t})),he=F(((e,t,n,s)=>e(s)?t(s):n(s))),fe=(...e)=>(...t)=>{let n,s=!0;for(let o=ie(e)-1;o>-1;o--)s?(s=!1,n=e[o](...t)):n=n===M?e[o]():e[o](n);return n},de=J(((e,t)=>t[e])),ge=G(((e,t,n)=>n.slice(e,Z(t)?t:V))),pe=de(0);ge(1,V);const me=J(((e,t)=>t.find(e))),ye=e=>()=>e,we=ye(!0),_e=ye(!1),be=J(((e,t)=>t(...e))),Se=e=>(...t)=>{const n=e(...t),s=function(e){return"function"===X(e)}(n);return!s||s&&n.$args_left<=0?(e=>!e)(n):Se(n)},ke=J(((e,t)=>ae(((t,n)=>me((t=>e(n,t)),t)?t:ce(n,t)),[],t)));ke(le);const Pe=e=>{let t,n=!1;return function(...s){return n?t:(n=!0,t=e(...s))}},ve=G(((e,t,n)=>ie(t)?ee(n)?e:fe((s=>s in n?ve(e,ge(1,V,t),n[s]):e),pe)(t):n));ve(K),fe(he(le(ne),_e,we),ve(ne));const Ee=G(((e,t,n)=>t(n)&&e(n))),Ne=Se,{random:qe}=Math,Ae=new O,We=be([]),$e=Ee(oe("Number"),Ne(isNaN)),je={_is_ping:!0},ze=e=>e&&clearTimeout(e),Ce=e=>{const t=Ae.zip(2147483637*qe()|0);return t in e?Ce(e):t};class Qe{ws=null;intentionally_closed=!1;reconnect_timeout=null;queue={};onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};ping_timer=null;idle_timer=null;get opened(){return 1===this.ws?.readyState}init_flush(){ue(_e,this.queue)}call(e,...t){for(const n of this.handlers[e])n(...t)}log(e,t=null,n=null){const{config:s}=this;null===n?s.timer?s.log(e,null,t):s.log(e,t):s.log(e,n,t)}resetPing(){const{config:{ping:e},ping_timer:t}=this;e&&(ee(t)||clearTimeout(t),this.ping_timer=B(1e3*e.interval,(async()=>{const{ping_timer:t,opened:n}=this;n?(await this.send(e.content,je),this.resetPing()):clearTimeout(t)})))}resetIdle(){const{config:{max_idle_time:e},idle_timer:t}=this;e!==1/0&&(ee(t)||clearTimeout(t),this.idle_timer=B(1e3*e,(()=>this.opened&&this.close())))}initSocket(e){const{queue:t,config:n}=this;this.ws=e,this.onReadyQueue.forEach((e=>e())),this.onReadyQueue.splice(0);const{id_key:s,data_key:o}=n.server;this.call("open",e);for(const n in t)e.send(t[n].msg);null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),this.resetPing(),this.resetIdle(),x(e,"close",(async(...e)=>{this.log("close"),this.ws=null,this.onCloseQueue.forEach(We),this.onCloseQueue.splice(0),this.call("close",...e);let{reconnect:t,reconnection_attempts:s}=n;if($e(t)){const e=async()=>{if(this.intentionally_closed||!s)return;s--,this.log("reconnect"),ee(this.ws)||(this.ws.close(),this.ws=null);const n=await this.connect();ee(n)||(this.reconnect_timeout=setTimeout(e,1e3*t))};e()}})),x(e,"message",(e=>{try{const t=n.decode(e.data);if(this.call("message",{...e,data:t}),t[s]){const e=this.queue[t[s]];if(e){const n=e.sent_time?Date.now()-e.sent_time:null;this.log("message",t[o],n),e.ff(t[o])}}}catch(t){console.error(t,`WSP: Decode error. Got: ${e.data}`)}this.resetPing()}))}opening=!1;connect(){return new Promise((e=>{if(this.opened||this.opening)return e(null);this.opening=!0;const t=this.config,n=t.socket||t.adapter(t.url,t.protocols);if(!n||n.readyState>1)return this.opening=!1,this.ws=null,this.log("error","ready() on closing or closed state! status 2."),e(2);const s=Pe((t=>{this.opening=!1,e(t)}));x(n,"error",Pe((e=>{this.ws=null,this.log("error","status 3. Err: "+e.message),this.call("error",e),s(3)}))),n.readyState?(this.initSocket(n),s(null)):x(n,"open",Pe((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.config.lazy||this.opened?e():this.onReadyQueue.push(e)}))}on(e,t,n=we,s=!1){const o=e=>n(e)&&t(e);return s?x(this.ws,e,o):this.handlers[e].push(o),o}off(e,t,n=!1){if(n)return((e,t,n)=>e.removeEventListener(t,n))(this.ws,e,t);const s=this.handlers[e],o=s.indexOf(t);~o&&s.splice(o,1)}async close(){return new Promise(((e,t)=>{null===this.ws?t("WSP: closing a non-inited socket!"):(this.onCloseQueue.push((()=>{this.init_flush(),e(null)})),this.ws.close(),this.ws=null,this.intentionally_closed=!0)}))}open(){if(!this.opened)return this.intentionally_closed=!1,this.connect()}async prepareMessage(e,t={}){this.log(t._is_ping?"ping":"send",e);const{config:n,queue:s}=this,{pipes:o,server:{data_key:i}}=n,{top:r,_is_ping:l}=t,c=Ce(s);if("object"==typeof r&&r[i])throw new Error(`Attempting to set data key/token via ${t._is_ping?"ping":"send"}() options!`);for(const t of o)e=t(e);const[a,u]=await Promise.all([n.encode(c,e,n),this.connect()]);if(u)throw new Error("ERR while opening connection #"+u);this.opened&&(this.ws.send(a),this.resetPing(),l||this.resetIdle());const h=()=>delete this.queue[c];return{message_id:c,msg:a,timeout:t=>B(n.timeout,(()=>{c in s&&(this.call("timeout",e),t({"Websocket timeout expired":n.timeout,"for the message":e}),h())})),cleanup:h}}async send(e,t={}){const{message_id:n,msg:s,timeout:o,cleanup:i}=await this.prepareMessage(e,t),{queue:r,config:l}=this;return new Promise(((e,t)=>{const i=o(t);r[n]={msg:s,data_type:l.data_type,sent_time:l.timer?Date.now():null,ff(t){ze(i),e(t)}}})).finally(i)}async*stream(e,t={}){const{message_id:n,msg:s,timeout:o,cleanup:i}=await this.prepareMessage(e,t),{queue:r,config:l}=this;let c,a=!1,u=null;for(r[n]={msg:s,ff:e=>{c(e),e?.done&&(i(),a=!0)},data_type:l.data_type,sent_time:l.timer?Date.now():null};!a;)yield await new Promise(((e,t)=>{u=o(t),c=e})).finally((()=>{ze(u),u=null}))}constructor(e={}){this.config=(e=>{if(null===T&&!("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({},I,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.config.lazy||this.connect()}}export{Qe as default};
|
|
1
|
+
const t=Symbol("Placeholder"),e=e=>{let n=0;for(const s of e)s!==t&&n++;return n},n=(e,n)=>{const s=e.length,i=e.slice(),o=n.length;let r=o,l=0;for(;r&&l<s;l++)i[l]===t&&(i[l]=n[o-r],r--);for(l=s;r;l++,r--)i[l]=n[o-r];return i},s=(t,i,o)=>{const r=t.length-i.length-e(o);if(r<1)return t(...n(i,o));{const e=(...e)=>s(t,n(i,o),e);return e.$args_left=r,e}},i=t=>(...n)=>t.length>e(n)?s(t,[],n):t(...n);function o(e){return function(n,...s){return s.length>0?n===t?(e=>function(n){return n===t?e:e(n)})((t=>e(t,s[0]))):e(n,s[0]):t=>e(n,t)}}function r(t){return i(t)}const l=t=>t.length,c=/^(.*?)(8|16|32|64)(Clamped)?Array$/,a=void 0,u=1/0,h=t=>typeof t,f=t=>null===t,d=t=>"number"==h(t);const p=t=>f(t)||(t=>t===a)(t),g={u:"U",b:"B",n:"N",s:"S",f:"F"},m=Symbol(),y=t=>{const e=h(t);return"object"===e?f(t)?"Null":t.constructor.name:g[e[0]]+e.slice(1)},_=o(((t,e)=>y(e)===t)),w=o(((t,e)=>t===e)),b=o(((t,e)=>{const n=y(t);if(w(n,y(e))&&(w(n,"Object")||w(n,"Array")||(t=>c.test(t))(n))){if(f(t)||f(e))return w(t,e);if(w(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(w(n,e)&&s in t||w(n,t)&&s in e&&b(t[s],e[s])))return!1;return!0}return w(t,e)})),k=o(((t,e)=>(e.push(t),e))),S=r(((t,e,n)=>n.reduce(t,e))),P=o(((t,e)=>{const n=(t=>Array.isArray(t))(e);let s,i;n&&(s=0,i=[]);for(let s in e)t(e[s],s)||(n?i.push(+s):delete e[s]);if(n)for(const t of i)e.splice(t-s++,1);return e})),v=i(((t,e,n,s)=>t(s)?e(s):n(s))),E=(...e)=>(...n)=>{let s,i=!0;for(let o=l(e)-1;o>-1;o--)i?(i=!1,s=e[o](...n)):s=s===t?e[o]():e[o](s);return s},A=o(((t,e)=>e[t])),C=r(((t,e,n)=>n.slice(t,d(e)?e:u))),N=A(0);C(1,u);const $=o(((t,e)=>e.find(t))),j=t=>()=>t,q=o(((t,e)=>(t(e),e))),W=j(!0),z=j(!1),O=o(((t,e)=>e(...t))),Q=t=>(...e)=>{const n=t(...e),s=function(t){return"function"===h(t)}(n);return s&&n.$args_left?Q(n):(t=>!t)(n)},T=o(((t,e)=>S(((e,n)=>$((e=>t(n,e)),e)?e:k(n,e)),[],e)));T(b);const R=t=>{let e,n=!1;return function(...s){return n?e:(n=!0,e=t(...s))}},x=(t,e,n)=>l(e)?p(n)?t:E((s=>s in n?x(t,C(1,u,e),n[s]):t),N)(e):n,B=r(x);B(a),E(v(b(m),z,W),B(m));const I=r(((t,e,n)=>e(n)&&t(n))),M=Q,D=Symbol("Placeholder"),L=t=>{let e=0;for(const n of t)n!==D&&e++;return e},U=(t,e)=>{const n=t.length,s=t.slice(),i=e.length;let o=i,r=0;for(;o&&r<n;r++)s[r]===D&&(s[r]=e[i-o],o--);for(r=n;o;r++,o--)s[r]=e[i-o];return s},F=(t,e,n)=>{const s=t.length-e.length-L(n);if(s<1)return t(...U(e,n));{const i=(...s)=>F(t,U(e,n),s);return i.$args_left=s,i}},J=t=>(...e)=>t.length>L(e)?F(t,[],e):t(...e);function G(t){return function(e,...n){return n.length>0?e===D?(t=>function(e){return e===D?t:t(e)})((e=>t(e,n[0]))):t(e,n[0]):n=>t(e,n)}}function H(t){return J(t)}const K=t=>t.length,V=/^(.*?)(8|16|32|64)(Clamped)?Array$/,X=void 0,Y=1/0,Z=t=>typeof t,tt=t=>null===t,et={u:"U",b:"B",n:"N",s:"S",f:"F"},nt=Symbol(),st=t=>{const e=Z(t);return"object"===e?tt(t)?"Null":t.constructor.name:et[e[0]]+e.slice(1)},it=G(((t,e)=>st(e)===t)),ot=G(((t,e)=>t===e)),rt=G(((t,e)=>{const n=st(t);if(ot(n,st(e))&&(ot(n,"Object")||ot(n,"Array")||(s=n,V.test(s)))){if(tt(t)||tt(e))return ot(t,e);if(ot(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(ot(n,e)&&s in t||ot(n,t)&&s in e&&rt(t[s],e[s])))return!1;return!0}var s;return ot(t,e)})),lt=G(((t,e)=>(e.push(t),e))),ct=H(((t,e,n)=>n.reduce(t,e))),at=J(((t,e,n,s)=>t(s)?e(s):n(s))),ut=H(((t,e,n)=>at(t,e,yt,n))),ht=(...t)=>(...e)=>{let n,s=!0;for(let i=K(t)-1;i>-1;i--)s?(s=!1,n=t[i](...e)):n=n===D?t[i]():t[i](n);return n},ft=G(((t,e)=>e[t])),dt=H(((t,e,n)=>n.slice(t,(t=>"number"==Z(t))(e)?e:Y))),pt=ft(0);dt(1,Y);const gt=G(((t,e)=>e.find(t))),mt=t=>()=>t,yt=t=>t,_t=G(((t,e)=>e.split(t))),wt=mt(!0),bt=mt(!1),kt=G(((t,e)=>ct(((e,n)=>gt((e=>t(n,e)),e)?e:lt(n,e)),[],e)))(rt),St=(t,e,n)=>K(e)?(t=>tt(t)||(t=>t===X)(t))(n)?t:ht((s=>s in n?St(t,dt(1,Y,e),n[s]):t),pt)(e):n,Pt=H(St);Pt(X),ht(at(rt(nt),bt,wt),Pt(nt));const vt=G(((t,e)=>e.map(t))),{floor:Et}=Math,At="0123456789abcdefghijklmnopqrstuvwxyz",Ct=it("String"),Nt=ut(Ct,_t("")),$t=ht((t=>Object.fromEntries(t)),vt(((t,e)=>[t,e])),Nt);class jt{is_str;delim;abc;abclen;c2pos;standard;setABC(t,e=""){if(this.is_str=Ct(t),this.delim=e,!ht(ot(K(n=t)),K,kt,Nt)(n))throw new Error("Not all chars are unique!");var n;this.abc=t,this.abclen=K(t),this.standard=!!this.is_str&&At.startsWith(t),this.c2pos=$t(t)}zip(t){const{abc:e,abclen:n,delim:s}=this;let i="",o=!0;for(;t>0;)i=e[t%n]+(o?"":s)+i,t=Et(t/n),o=!1;return i||"0"}unzip(t){const{standard:e,abclen:n,c2pos:s,delim:i,is_str:o}=this;if("0"===t)return 0;if(e)return parseInt(t,n);const r=o?t:t.split(i),l=K(r);let c=0;for(let t=0;t<l;t++)c+=s[r[t]]*n**(l-t-1);return c}constructor(t,e){e?this.setABC(t,e):this.setABC(t||At+"ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}const qt=new jt;qt.setABC.bind(qt),qt.zip.bind(qt),qt.unzip.bind(qt);const Wt=(()=>{try{return WebSocket||null}catch{return null}})(),zt=(t,e,n)=>t.addEventListener(e,n),Ot=(t,e)=>setTimeout(e,t),Qt={data_type:"json",log:()=>null,timer:!1,url:"localhost",timeout:1400,reconnect:2,reconnection_attempts:1/0,max_idle_time:1/0,lazy:!1,socket:null,adapter:(t,e)=>new WebSocket(t,e),encode:(t,e,{server:n})=>JSON.stringify({[n.id_key]:t,[n.data_key]:e}),decode:t=>JSON.parse(t),protocols:[],pipes:[],server:{id_key:"id",data_key:"data"},ping:{interval:55,content:{}}},{random:Tt}=Math,Rt=new jt,xt=O([]),Bt=I(_("Number"),M(isNaN)),It={_is_ping:!0},Mt=t=>t&&clearTimeout(t),Dt=t=>{const e=Rt.zip(2147483637*Tt()|0);return e in t?Dt(t):e};class Lt{ws=null;intentionally_closed=!1;reconnect_timeout=null;queue={};onReadyQueue=[];onCloseQueue=[];handlers={open:[],close:[],message:[],error:[],timeout:[]};config={};ping_timer=null;idle_timer=null;get opened(){return 1===this.ws?.readyState}init_flush(){P(z,this.queue)}call(t,...e){for(const n of this.handlers[t])n(...e)}log(t,e=null,n=null){const{config:s}=this;setTimeout((()=>{null===n?s.timer?s.log(t,null,e):s.log(t,e):s.log(t,n,e)}))}resetPing(){const{config:{ping:t},ping_timer:e}=this;t&&(p(e)||clearTimeout(e),this.ping_timer=Ot(1e3*t.interval,(async()=>{const{ping_timer:e,opened:n}=this;n?(await this.send(t.content,It),this.resetPing()):clearTimeout(e)})))}resetIdle(){const{config:{max_idle_time:t},idle_timer:e}=this;t!==1/0&&(p(e)||clearTimeout(e),this.idle_timer=Ot(1e3*t,(()=>this.opened&&this.close())))}initSocket(t){const{queue:e,config:n}=this;this.ws=t,this.onReadyQueue.forEach((t=>t())),this.onReadyQueue.splice(0);const{id_key:s,data_key:i}=n.server;this.call("open",t);for(const n in e)t.send(e[n].msg);null!==this.reconnect_timeout&&(clearInterval(this.reconnect_timeout),this.reconnect_timeout=null),this.resetPing(),this.resetIdle(),zt(t,"close",(async(...t)=>{this.log("close"),this.ws=null,this.onCloseQueue.forEach(xt),this.onCloseQueue.splice(0),this.call("close",...t);let{reconnect:e,reconnection_attempts:s}=n;if(Bt(e)){const t=async()=>{if(this.intentionally_closed||!s)return;s--,this.log("reconnect"),p(this.ws)||(this.ws.close(),this.ws=null);const n=await this.connect();p(n)||(this.reconnect_timeout=setTimeout(t,1e3*e))};t()}})),zt(t,"message",(t=>{try{const e=n.decode(t.data);this.call("message",{...t,data:e});let o=!1;if(_("Object",e)&&s in e){const t=this.queue[e[s]];if(t){const n=t.sent_time?Date.now()-t.sent_time:null;this.log("message",e[i],n),t.ff(e[i]),o=!0}}o||this.log("message-ext",e)}catch(e){console.error(e,`WSP: Decode error. Got: ${t.data}`)}this.resetPing()}))}opening=!1;connect(){return new Promise((t=>{if(this.opened||this.opening)return t(null);this.opening=!0;const e=this.config,n=e.socket||e.adapter(e.url,e.protocols);if(!n||n.readyState>1)return this.opening=!1,this.ws=null,this.log("error","ready() on closing or closed state! status 2."),t(2);const s=R((e=>{this.opening=!1,t(e)}));zt(n,"error",R((t=>{this.ws=null,this.log("error","status 3. Err: "+t.message),this.call("error",t),s(3)}))),n.readyState?(this.initSocket(n),s(null)):zt(n,"open",R((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((t=>{this.config.lazy||this.opened?t():this.onReadyQueue.push(t)}))}on(t,e,n=W,s=!1){const i=t=>n(t)&&e(t);return s?zt(this.ws,t,i):this.handlers[t].push(i),i}off(t,e,n=!1){if(n)return((t,e,n)=>t.removeEventListener(e,n))(this.ws,t,e);const s=this.handlers[t],i=s.indexOf(e);~i&&s.splice(i,1)}async close(){return new Promise(((t,e)=>{null===this.ws?e("WSP: closing a non-inited socket!"):(this.onCloseQueue.push((()=>{this.init_flush(),t(null)})),this.ws.close(),this.ws=null,this.intentionally_closed=!0)}))}open(){if(!this.opened)return this.intentionally_closed=!1,this.connect()}async prepareMessage(t,e={}){this.log(e._is_ping?"ping":"send",t);const{config:n,queue:s}=this,{pipes:i,server:{data_key:o}}=n,{top:r,_is_ping:l}=e,c=Dt(s);if("object"==typeof r&&r[o])throw new Error(`Attempting to set data key/token via ${e._is_ping?"ping":"send"}() options!`);for(const e of i)t=e(t);const[a,u]=await Promise.all([n.encode(c,t,n),this.connect()]);if(u)throw new Error("ERR while opening connection #"+u);const h=q((()=>delete this.queue[c]));return this.opened&&(Ot(0,(()=>this.ws.send(a))),this.resetPing(),l||this.resetIdle()),{id:c,msg:a,timeout:e=>Ot(n.timeout,(()=>{c in s&&(this.call("timeout",t),e({"Websocket timeout expired":n.timeout,"for the message":t}),h())})),cleanup:h}}async send(t,e={}){const{id:n,msg:s,timeout:i,cleanup:o}=await this.prepareMessage(t,e),{queue:r,config:l}=this;return new Promise(((t,e)=>{const o=i(e);r[n]={msg:s,data_type:l.data_type,sent_time:l.timer?Date.now():null,ff(e){Mt(o),t(e)}}})).finally(o)}async*stream(t,e={}){const{id:n,msg:s,timeout:i,cleanup:o}=await this.prepareMessage(t,e),{queue:r,config:l}=this;let c,a=!1,u=null;for(r[n]={msg:s,ff:t=>{c(t),t?.done&&(o(),a=!0)},data_type:l.data_type,sent_time:l.timer?Date.now():null};!a;)yield await new Promise(((t,e)=>{u=i(e),c=t})).catch((t=>o(t))).finally((()=>{Mt(u),u=null}))}constructor(t={}){this.config=(t=>{if(null===Wt&&!("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({},Qt,t),n=e.url;if("/"==n[0])try{const t=location.protocol.includes("s:")?"wss":"ws";e.url=`${t}://${location.hostname}:${location.port}${n}`}catch(t){throw new Error("WSP: URL starting with / in non-browser environment!")}return e})(t),this.config.lazy||this.connect()}}export{Lt as WebSocketClient,Lt as default};
|
package/package.json
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
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.
|
|
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": "^29.0.
|
|
56
|
-
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
57
|
-
"@rollup/plugin-replace": "^6.0.3",
|
|
58
|
-
"@rollup/plugin-terser": "^0.
|
|
59
|
-
"@types/express": "^5.0.6",
|
|
60
|
-
"@types/node": "^25.0
|
|
61
|
-
"@types/ws": "^8.18.1",
|
|
62
|
-
"codecov": "^3.
|
|
63
|
-
"cross-env": "^10.1.0",
|
|
64
|
-
"dts-bundle-generator": "^9.5.1",
|
|
65
|
-
"nyc": "^
|
|
66
|
-
"rollup": "^4.
|
|
67
|
-
"rollup-plugin-typescript2": "^0.36.0",
|
|
68
|
-
"ts-node": "^10.9.2",
|
|
69
|
-
"tsx": "^4.21.0",
|
|
70
|
-
"typescript": "^5.9.3",
|
|
71
|
-
"uvu": "^0.5.6",
|
|
72
|
-
"ws": "^8.
|
|
73
|
-
},
|
|
74
|
-
"types": "./dist/bundle.d.ts",
|
|
75
|
-
"dependencies": {
|
|
76
|
-
"pepka": "^1.6.
|
|
77
|
-
"zipnum": "^2.
|
|
78
|
-
}
|
|
79
|
-
}
|
|
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.10.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": "^29.0.2",
|
|
56
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
57
|
+
"@rollup/plugin-replace": "^6.0.3",
|
|
58
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
59
|
+
"@types/express": "^5.0.6",
|
|
60
|
+
"@types/node": "^25.4.0",
|
|
61
|
+
"@types/ws": "^8.18.1",
|
|
62
|
+
"codecov": "^3.6.3",
|
|
63
|
+
"cross-env": "^10.1.0",
|
|
64
|
+
"dts-bundle-generator": "^9.5.1",
|
|
65
|
+
"nyc": "^18.0.0",
|
|
66
|
+
"rollup": "^4.59.0",
|
|
67
|
+
"rollup-plugin-typescript2": "^0.36.0",
|
|
68
|
+
"ts-node": "^10.9.2",
|
|
69
|
+
"tsx": "^4.21.0",
|
|
70
|
+
"typescript": "^5.9.3",
|
|
71
|
+
"uvu": "^0.5.6",
|
|
72
|
+
"ws": "^8.19.0"
|
|
73
|
+
},
|
|
74
|
+
"types": "./dist/bundle.d.ts",
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"pepka": "^1.6.23",
|
|
77
|
+
"zipnum": "^2.1.3"
|
|
78
|
+
}
|
|
79
|
+
}
|
package/rollup.config.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import commonjs from '@rollup/plugin-commonjs'
|
|
2
2
|
import resolve from '@rollup/plugin-node-resolve'
|
|
3
|
-
import typescript from 'rollup-plugin-typescript2'
|
|
4
|
-
import terser from '@rollup/plugin-terser'
|
|
5
3
|
import replace from '@rollup/plugin-replace'
|
|
4
|
+
import terser from '@rollup/plugin-terser'
|
|
5
|
+
import typescript from 'rollup-plugin-typescript2'
|
|
6
6
|
import tsc from 'typescript'
|
|
7
7
|
|
|
8
8
|
export default {
|
|
@@ -32,9 +32,7 @@ export default {
|
|
|
32
32
|
process.env.NODE_ENV!='development' && terser(),
|
|
33
33
|
replace({
|
|
34
34
|
preventAssignment: true,
|
|
35
|
-
values: {
|
|
36
|
-
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
|
|
37
|
-
}
|
|
35
|
+
values: { 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) }
|
|
38
36
|
})
|
|
39
37
|
]
|
|
40
38
|
}
|
package/src/WSC.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import '
|
|
1
|
+
import { AnyFunc, AnyObject, both, callWith, F, isNil, notf, once, qfilter, T, tap, typeIs } from 'pepka'
|
|
2
2
|
import { Zipnum } from 'zipnum'
|
|
3
|
-
import { add_event, rm_event, sett } from './utils'
|
|
4
3
|
import { processConfig } from './config'
|
|
5
|
-
import
|
|
4
|
+
import './types'
|
|
5
|
+
import { add_event, rm_event, sett } from './utils'
|
|
6
6
|
|
|
7
7
|
const MAX_32 = 2**31 - 1
|
|
8
8
|
const { random } = Math
|
|
@@ -25,7 +25,7 @@ const genid = (q: AnyObject) => {
|
|
|
25
25
|
return id in q ? genid(q) : id
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
class WebSocketClient {
|
|
28
|
+
export class WebSocketClient {
|
|
29
29
|
private ws: wsc.Socket|null = null
|
|
30
30
|
private intentionally_closed = false
|
|
31
31
|
private reconnect_timeout: NodeJS.Timeout|null = null
|
|
@@ -45,16 +45,16 @@ class WebSocketClient {
|
|
|
45
45
|
private call(event_name: wsc.WSEvent, ...args: any[]) {
|
|
46
46
|
for(const h of this.handlers[event_name]) h(...args)
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
private log(event: string, message: any = null, time: number|null = null): void {
|
|
48
|
+
private log(event: wsc.WSEvent, message: any = null, time: number|null = null): void {
|
|
50
49
|
const {config} = this
|
|
51
|
-
|
|
52
|
-
if(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
if(time === null)
|
|
52
|
+
if(config.timer) config.log(event, null, message)
|
|
53
|
+
else config.log(event, message)
|
|
54
|
+
else
|
|
55
|
+
config.log(event, time, message)
|
|
56
|
+
})
|
|
56
57
|
}
|
|
57
|
-
|
|
58
58
|
private resetPing() {
|
|
59
59
|
const {config: {ping}, ping_timer} = this
|
|
60
60
|
if(ping) {
|
|
@@ -70,6 +70,7 @@ class WebSocketClient {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// FIXME: Make some version where it could work faster (for streaming).
|
|
73
74
|
private resetIdle() {
|
|
74
75
|
const {config: {max_idle_time: time}, idle_timer} = this
|
|
75
76
|
if(time!==Infinity) {
|
|
@@ -122,7 +123,8 @@ class WebSocketClient {
|
|
|
122
123
|
try {
|
|
123
124
|
const data = config.decode(e.data)
|
|
124
125
|
this.call('message', {...e, data})
|
|
125
|
-
|
|
126
|
+
let internal = false
|
|
127
|
+
if(typeIs('Object', data) && id_key in data) {
|
|
126
128
|
const q = this.queue[data[id_key]]
|
|
127
129
|
if(q) {
|
|
128
130
|
// Debug, Log.
|
|
@@ -130,15 +132,16 @@ class WebSocketClient {
|
|
|
130
132
|
this.log('message', data[data_key], time)
|
|
131
133
|
// Play.
|
|
132
134
|
q.ff(data[data_key])
|
|
135
|
+
internal = true
|
|
133
136
|
}
|
|
134
137
|
}
|
|
138
|
+
if(!internal) this.log('message-ext', data)
|
|
135
139
|
} catch (err) {
|
|
136
140
|
console.error(err, `WSP: Decode error. Got: ${e.data}`)
|
|
137
141
|
}
|
|
138
142
|
this.resetPing()
|
|
139
143
|
})
|
|
140
144
|
}
|
|
141
|
-
|
|
142
145
|
private opening = false
|
|
143
146
|
private connect() { // returns status if won't open or null if ok.
|
|
144
147
|
return new Promise<null|number>((ff) => {
|
|
@@ -176,8 +179,7 @@ class WebSocketClient {
|
|
|
176
179
|
public get socket() { return this.ws }
|
|
177
180
|
public async ready() {
|
|
178
181
|
return new Promise<void>((ff) => {
|
|
179
|
-
if(this.config.lazy) ff() // FIXME: (possibly) breaking change ?? At least minor ver bump with a notice!!!
|
|
180
|
-
else if(this.opened) ff()
|
|
182
|
+
if(this.config.lazy || this.opened) ff() // FIXME: (possibly) breaking change ?? At least minor ver bump with a notice!!!
|
|
181
183
|
else this.onReadyQueue.push(ff)
|
|
182
184
|
})
|
|
183
185
|
}
|
|
@@ -203,7 +205,6 @@ class WebSocketClient {
|
|
|
203
205
|
const i = handlers.indexOf(handler)
|
|
204
206
|
if(~i) handlers.splice(i, 1)
|
|
205
207
|
}
|
|
206
|
-
|
|
207
208
|
public async close(): wsc.AsyncErrCode {
|
|
208
209
|
return new Promise((ff, rj) => {
|
|
209
210
|
if(this.ws === null) {
|
|
@@ -219,17 +220,15 @@ class WebSocketClient {
|
|
|
219
220
|
}
|
|
220
221
|
})
|
|
221
222
|
}
|
|
222
|
-
|
|
223
223
|
public open() {
|
|
224
224
|
if(!this.opened) {
|
|
225
225
|
this.intentionally_closed = false
|
|
226
226
|
return this.connect()
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
|
-
|
|
230
229
|
// TODO: Сделать сэттер элементов конфигурации чтобы двигать таймауты.
|
|
231
230
|
// И эвент, когда схема наша, а соответствующего элемента очереди не ма.
|
|
232
|
-
// Или добавить флажок к эвенту 'message'.
|
|
231
|
+
// Или добавить флажок к эвенту 'message'.F
|
|
233
232
|
// И событие 'line' со значением on: boolean. Критерии?
|
|
234
233
|
private async prepareMessage<RequestDataType = any>(
|
|
235
234
|
message_data: RequestDataType,
|
|
@@ -251,12 +250,7 @@ class WebSocketClient {
|
|
|
251
250
|
this.connect()
|
|
252
251
|
])
|
|
253
252
|
if(err) throw new Error('ERR while opening connection #'+err)
|
|
254
|
-
|
|
255
|
-
this.ws!.send(msg)
|
|
256
|
-
this.resetPing()
|
|
257
|
-
if(!_is_ping) this.resetIdle()
|
|
258
|
-
}
|
|
259
|
-
const cleanup = () => delete this.queue[id]
|
|
253
|
+
const cleanup = tap(() => delete this.queue[id])
|
|
260
254
|
const timeout = (rj: AnyFunc) => sett(config.timeout, () => {
|
|
261
255
|
if(id in queue) {
|
|
262
256
|
this.call('timeout', message_data)
|
|
@@ -264,9 +258,13 @@ class WebSocketClient {
|
|
|
264
258
|
cleanup()
|
|
265
259
|
}
|
|
266
260
|
})
|
|
267
|
-
|
|
261
|
+
if(this.opened) {
|
|
262
|
+
sett(0, () => this.ws!.send(msg))
|
|
263
|
+
this.resetPing()
|
|
264
|
+
if(!_is_ping) this.resetIdle()
|
|
265
|
+
}
|
|
266
|
+
return { id, msg, timeout, cleanup }
|
|
268
267
|
}
|
|
269
|
-
|
|
270
268
|
/** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
|
|
271
269
|
returns a Promise that will be rejected after a timeout or
|
|
272
270
|
resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
|
|
@@ -275,12 +273,11 @@ class WebSocketClient {
|
|
|
275
273
|
message_data: RequestDataType,
|
|
276
274
|
opts = <wsc.SendOptions>{}
|
|
277
275
|
): Promise<ResponseDataType> {
|
|
278
|
-
const {
|
|
276
|
+
const {id, msg, timeout, cleanup} = await this.prepareMessage(message_data, opts)
|
|
279
277
|
const {queue, config} = this
|
|
280
|
-
|
|
281
278
|
return new Promise<ResponseDataType>((ff, rj) => {
|
|
282
279
|
const to = timeout(rj)
|
|
283
|
-
queue[
|
|
280
|
+
queue[id] = {
|
|
284
281
|
msg,
|
|
285
282
|
data_type: config.data_type,
|
|
286
283
|
sent_time: config.timer ? Date.now() : null,
|
|
@@ -291,15 +288,15 @@ class WebSocketClient {
|
|
|
291
288
|
}
|
|
292
289
|
}).finally(cleanup)
|
|
293
290
|
}
|
|
294
|
-
|
|
291
|
+
// FIXME: rejects into ff somehow.
|
|
295
292
|
public async *stream<RequestDataType = any, ResponseDataType = any>(
|
|
296
293
|
message_data: RequestDataType,
|
|
297
294
|
opts = <wsc.SendOptions>{}
|
|
298
295
|
): AsyncGenerator<ResponseDataType, void, unknown> {
|
|
299
|
-
const {
|
|
296
|
+
const {id, msg, timeout, cleanup} = await this.prepareMessage(message_data, opts)
|
|
300
297
|
const {queue, config} = this
|
|
301
298
|
let done = false, fulfill: AnyFunc, to: NodeJS.Timeout|null = null
|
|
302
|
-
queue[
|
|
299
|
+
queue[id] = {
|
|
303
300
|
msg,
|
|
304
301
|
ff: (msg: ResponseDataType&{done?: boolean}) => {
|
|
305
302
|
fulfill(msg)
|
|
@@ -310,9 +307,8 @@ class WebSocketClient {
|
|
|
310
307
|
}
|
|
311
308
|
while(!done) yield await new Promise<ResponseDataType>((ff, rj) => {
|
|
312
309
|
to=timeout(rj); fulfill=ff
|
|
313
|
-
}).finally(() => {clearTO(to); to=null})
|
|
310
|
+
}).catch((e) => cleanup(e)).finally(() => {clearTO(to); to=null})
|
|
314
311
|
}
|
|
315
|
-
|
|
316
312
|
// TODO: Add .on handlers to config!
|
|
317
313
|
constructor(user_config: wsc.UserConfig = {}) {
|
|
318
314
|
this.config = processConfig(user_config)
|
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' | 'timeout'
|
|
7
|
+
export type WSEvent = 'open' | 'message' | 'message-ext' | 'close' | 'error' | 'timeout' | 'reconnect' | 'send' | 'ping'
|
|
8
8
|
|
|
9
9
|
/** Minimal socket-like interface. */
|
|
10
10
|
interface Socket {
|
package/test/mock/WS.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import WebSocket, { WebSocketServer } from 'ws'
|
|
3
|
-
import {noop} from 'pepka'
|
|
3
|
+
import {add, compose, genBy, identity, noop, wait} from 'pepka'
|
|
4
4
|
|
|
5
5
|
let server: WebSocketServer|null = null
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ const createServer = (port = 40510) => new Promise<WebSocketServer>((ff, rj) =>
|
|
|
8
8
|
if(server) return rj('The server is already running!')
|
|
9
9
|
server = new WebSocketServer({ port }, () => {
|
|
10
10
|
server!.on('connection', (socket: WebSocket&{isAlive: boolean}) => {
|
|
11
|
-
socket.on('message', (rawMessage: string) => {
|
|
11
|
+
socket.on('message', async (rawMessage: string) => {
|
|
12
12
|
// console.log({rawMessage: rawMessage.toString()})
|
|
13
13
|
const {id, data} = JSON.parse(rawMessage)
|
|
14
14
|
let response = ''
|
|
@@ -21,23 +21,23 @@ const createServer = (port = 40510) => new Promise<WebSocketServer>((ff, rj) =>
|
|
|
21
21
|
response = data
|
|
22
22
|
} else if(data.stream) {
|
|
23
23
|
// Handle streaming responses
|
|
24
|
-
const chunks =
|
|
25
|
-
|
|
24
|
+
const chunks = genBy(compose(add(1), identity), 20) // Generate 20 chunks
|
|
25
|
+
// console.log(chunks)
|
|
26
|
+
const delay = 2 // 20ms delay between chunks for reliable delivery
|
|
26
27
|
|
|
27
28
|
if(data.multi) {
|
|
28
29
|
// Multi-chunk streaming
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
})
|
|
30
|
+
for(const i in chunks) {
|
|
31
|
+
const chunk = chunks[i]
|
|
32
|
+
socket.send(JSON.stringify({
|
|
33
|
+
id,
|
|
34
|
+
data: {
|
|
35
|
+
...data, chunk,
|
|
36
|
+
done: +i === chunks.length - 1 // Last chunk gets done: true
|
|
37
|
+
}
|
|
38
|
+
}))
|
|
39
|
+
await wait(delay)
|
|
40
|
+
}
|
|
41
41
|
} else {
|
|
42
42
|
// Single response
|
|
43
43
|
socket.send(JSON.stringify({
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { equals } from 'pepka'
|
|
2
1
|
import { createNew, timeout } from '../utils'
|
|
3
2
|
import mockServer from '../mock/server'
|
|
4
3
|
import { test } from '../suite'
|
|
@@ -33,7 +32,7 @@ test('stream-comprehensive', timeout(1e4, () => new Promise<void>(async (ff, rj)
|
|
|
33
32
|
|
|
34
33
|
// Test 2: Stream with for-await loop
|
|
35
34
|
const msg2 = {stream: true, test: 'stream2'}
|
|
36
|
-
to = setTimeout(() => rj('stream2 timeout'),
|
|
35
|
+
to = setTimeout(() => rj('stream2 timeout'), 2e4)
|
|
37
36
|
|
|
38
37
|
const stream2 = ws.stream<typeof msg2, any>(msg2)
|
|
39
38
|
const results: any[] = []
|
|
@@ -45,15 +44,10 @@ test('stream-comprehensive', timeout(1e4, () => new Promise<void>(async (ff, rj)
|
|
|
45
44
|
|
|
46
45
|
clearTimeout(to)
|
|
47
46
|
|
|
48
|
-
// For streaming messages, check that we got a valid chunk with the expected properties
|
|
49
|
-
if (results.length !== 1 || !results[0].test || results[0].test !== msg2.test) {
|
|
50
|
-
return rj('stream2 failed')
|
|
51
|
-
}
|
|
52
|
-
|
|
53
47
|
// Test 3: Multiple concurrent streams
|
|
54
48
|
const msg3a = {stream: true, test: 'stream3a'}
|
|
55
49
|
const msg3b = {stream: true, test: 'stream3b'}
|
|
56
|
-
to = setTimeout(() => rj('stream3 timeout'),
|
|
50
|
+
to = setTimeout(() => rj('stream3 timeout'), 2e4)
|
|
57
51
|
|
|
58
52
|
const stream3a = ws.stream(msg3a)
|
|
59
53
|
const stream3b = ws.stream(msg3b)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { equals } from 'pepka'
|
|
2
1
|
import { createNew, timeout } from '../utils'
|
|
3
2
|
import mockServer from '../mock/server'
|
|
4
3
|
import { test } from '../suite'
|
|
4
|
+
import { last } from 'pepka'
|
|
5
5
|
|
|
6
6
|
/** Test real streaming functionality with multiple chunks. */
|
|
7
7
|
test('stream-real', timeout(1.5e4, () => new Promise<void>(async (ff, rj) => {
|
|
@@ -10,11 +10,11 @@ test('stream-real', timeout(1.5e4, () => new Promise<void>(async (ff, rj) => {
|
|
|
10
10
|
let to = setTimeout(() => {
|
|
11
11
|
console.log('STREAM-REAL: Timeout - cannot create')
|
|
12
12
|
rj('cannot create')
|
|
13
|
-
},
|
|
13
|
+
}, 2e4)
|
|
14
14
|
const ws = await createNew({}, port)
|
|
15
15
|
clearTimeout(to)
|
|
16
16
|
|
|
17
|
-
to = setTimeout(() => rj('cannot ready'),
|
|
17
|
+
to = setTimeout(() => rj('cannot ready'), 2e4)
|
|
18
18
|
await ws.ready()
|
|
19
19
|
clearTimeout(to)
|
|
20
20
|
|
|
@@ -30,22 +30,27 @@ test('stream-real', timeout(1.5e4, () => new Promise<void>(async (ff, rj) => {
|
|
|
30
30
|
|
|
31
31
|
clearTimeout(to)
|
|
32
32
|
|
|
33
|
-
// Verify we got all chunks
|
|
34
|
-
if (chunks.length !==
|
|
35
|
-
return rj(`Expected
|
|
33
|
+
// Verify we got all 20 chunks in perfect order
|
|
34
|
+
if (chunks.length !== 20) {
|
|
35
|
+
return rj(`Expected exactly 20 chunks, got ${chunks.length}`)
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
for (let i = 0; i <
|
|
37
|
+
|
|
38
|
+
// Check that all chunks are present and in perfect order (1-20)
|
|
39
|
+
for (let i = 0; i < 20; i++) {
|
|
40
40
|
if (chunks[i].chunk !== i + 1) {
|
|
41
41
|
return rj(`Chunk ${i} should be ${i + 1}, got ${chunks[i].chunk}`)
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
// Verify last chunk has done flag
|
|
46
|
-
if (!chunks[
|
|
46
|
+
if (!chunks[20 - 1].done) {
|
|
47
47
|
return rj('Last chunk should have done flag')
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
// Verify last chunk has done flag
|
|
51
|
+
// if (!last(chunks as any).done) {
|
|
52
|
+
// return rj('Last chunk should have done flag')
|
|
53
|
+
// }
|
|
49
54
|
ff()
|
|
50
55
|
} catch (error) {
|
|
51
56
|
clearTimeout(to)
|
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
import { equals } from 'pepka'
|
|
2
1
|
import { createNew, timeout } from '../utils'
|
|
3
2
|
import mockServer from '../mock/server'
|
|
4
3
|
import { test } from '../suite'
|
|
5
4
|
|
|
6
5
|
/** Simple test for stream method basic functionality. */
|
|
7
|
-
test('stream-basic', timeout(
|
|
6
|
+
test('stream-basic', timeout(7e3, () => new Promise<void>(async (ff, rj) => {
|
|
8
7
|
const {port} = await mockServer()
|
|
9
|
-
let to = setTimeout(() => rj('cannot create'),
|
|
8
|
+
let to = setTimeout(() => rj('cannot create'), 2e4)
|
|
10
9
|
const ws = await createNew({}, port)
|
|
11
10
|
clearTimeout(to)
|
|
12
11
|
|
|
13
|
-
to = setTimeout(() => rj('cannot ready'),
|
|
12
|
+
to = setTimeout(() => rj('cannot ready'), 2e4)
|
|
14
13
|
await ws.ready()
|
|
15
14
|
clearTimeout(to)
|
|
16
15
|
|
|
17
16
|
const msg = {stream: true, test: 'stream'}
|
|
18
|
-
to = setTimeout(() => rj('stream timeout'),
|
|
17
|
+
to = setTimeout(() => rj('stream timeout'), 2e4)
|
|
19
18
|
|
|
20
19
|
try {
|
|
21
20
|
const stream = ws.stream(msg)
|