wspromisify 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.cjs CHANGED
@@ -1 +1,681 @@
1
- "use strict";const t=Symbol("Placeholder"),e=e=>{let n=0;for(const s of e)s!==t&&n++;return n},n=(e,n)=>{const s=e.length,i=e.slice(),r=n.length;let o=r,c=0;for(;o&&c<s;c++)i[c]===t&&(i[c]=n[r-o],o--);for(c=s;o;c++,o--)i[c]=n[r-o];return i},s=(t,i,r)=>{const o=t.length-i.length-e(r);if(o<1)return t(...n(i,r));{const e=(...e)=>s(t,n(i,r),e);return e.$args_left=o,e}},i=t=>(...n)=>t.length>e(n)?s(t,[],n):t(...n);function r(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 o(t){return i(t)}const c=t=>t.length,a=/^(.*?)(8|16|32|64)(Clamped)?Array$/,l={__proto__:!0,constructor:!0,prototype:!0},h=void 0,u=1/0,d=t=>typeof t,f=t=>null===t,m=t=>"number"==d(t),p=t=>f(t)||(t=>t===h)(t),g=t=>!(t in l),{isNaN:_}=Number,y={u:"U",b:"B",n:"N",s:"S",f:"F",o:"O"},b=Symbol(),w=t=>y[t[0]]+t.slice(1),k=t=>{const e=d(t);return"object"===e?f(t)?"Null":t.constructor?.name||w(e):"number"===e&&_(t)?"NaN":w(e)},S=r((t,e)=>k(e)===t),v=r((t,e)=>t===e),P=r((t,e)=>{const n=k(t);if(v(n,k(e))&&(v(n,"Object")||v(n,"Array")||(t=>a.test(t))(n))){if(f(t)||f(e))return v(t,e);if(v(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(v(n,e)&&s in t||v(n,t)&&s in e&&P(t[s],e[s])))return!1;return!0}return v(t,e)}),A=t=>()=>t,E=r((t,e)=>(e.push(t),e)),N=o((t,e,n)=>n.reduce(t,e)),j=t=>r((e,n)=>{for(let s in n)if(g(s))switch(k(n[s])){case"Array":if(t>1&&"Array"===k(e[s]))switch(t){case 2:const i=e[s],r=n[s];for(const e in r)i[e]?j(t)(i[e],r[e]):i[e]=r[e];break;case 3:e[s].push(...n[s])}else e[s]=n[s];break;case"Object":if("Object"===k(e[s])){j(t)(e[s],n[s]);break}default:e[s]=n[s]}return e}),q=j(1),z=(t,e)=>{const n=c(e);for(let s=t;s<n;s++)e[s]=e[s+1];return e.length=n-1,e},O=new Set,x=r((t,e)=>{let n,s=c(e);for(let i=0;i<s;i++){n=t(e[i]),O.has(n)?(z(i,e),s--,i--):O.add(n)}return O.clear(),e});x(t=>t);const W=i((t,e,n,s)=>t(s)?e(s):n(s)),$=(...e)=>(...n)=>{let s,i=!0;for(let r=c(e)-1;r>-1;r--)i?(i=!1,s=e[r](...n)):s=s===t?e[r]():e[r](s);return s},C=r((t,e)=>e[t]),M=o((t,e,n)=>n.slice(t,m(e)?e:u)),B=C(0);M(1,u);const T=r((t,e)=>t*e),L=r((t,e)=>e.find(t)),R=r((t,e)=>(t(e),e)),I=A(!0),U=A(!1),Z=()=>{},D=r((t,e)=>N((e,n)=>L(e=>t(n,e),e)?e:E(n,e),[],e));D(P);const F=t=>{let e,n=!1;return function(...s){return n?e:(n=!0,e=t(...s))}},J=(t,e,n)=>c(e)?p(n)?t:$(s=>s in n?J(t,M(1,u,e),n[s]):t,B)(e):n,G=o(J);G(h),$(W(P(b),U,I),G(b));const H=Symbol("Placeholder"),K=t=>{let e=0;for(const n of t)n!==H&&e++;return e},Q=(t,e)=>{const n=t.length,s=t.slice(),i=e.length;let r=i,o=0;for(;r&&o<n;o++)s[o]===H&&(s[o]=e[i-r],r--);for(o=n;r;o++,r--)s[o]=e[i-r];return s},V=(t,e,n)=>{const s=t.length-e.length-K(n);if(s<1)return t(...Q(e,n));{const i=(...s)=>V(t,Q(e,n),s);return i.$args_left=s,i}},X=t=>(...e)=>t.length>K(e)?V(t,[],e):t(...e);function Y(t){return function(e,...n){return n.length>0?e===H?(t=>function(e){return e===H?t:t(e)})(e=>t(e,n[0])):t(e,n[0]):n=>t(e,n)}}function tt(t){return X(t)}const et=t=>t.length,nt=/^(.*?)(8|16|32|64)(Clamped)?Array$/,st=void 0,it=1/0,rt=t=>typeof t,ot=t=>null===t,ct={u:"U",b:"B",n:"N",s:"S",f:"F"},at=Symbol(),lt=t=>{const e=rt(t);return"object"===e?ot(t)?"Null":t.constructor.name:ct[e[0]]+e.slice(1)},ht=Y((t,e)=>lt(e)===t),ut=Y((t,e)=>t===e),dt=Y((t,e)=>{const n=lt(t);if(ut(n,lt(e))&&(ut(n,"Object")||ut(n,"Array")||(s=n,nt.test(s)))){if(ot(t)||ot(e))return ut(t,e);if(ut(t,e))return!0;for(const n of[t,e])for(const s in n)if(!(ut(n,e)&&s in t||ut(n,t)&&s in e&&dt(t[s],e[s])))return!1;return!0}var s;return ut(t,e)}),ft=Y((t,e)=>(e.push(t),e)),mt=tt((t,e,n)=>n.reduce(t,e)),pt=X((t,e,n,s)=>t(s)?e(s):n(s)),gt=tt((t,e,n)=>pt(t,e,vt,n)),_t=(...t)=>(...e)=>{let n,s=!0;for(let i=et(t)-1;i>-1;i--)s?(s=!1,n=t[i](...e)):n=n===H?t[i]():t[i](n);return n},yt=Y((t,e)=>e[t]),bt=tt((t,e,n)=>n.slice(t,(t=>"number"==rt(t))(e)?e:it)),wt=yt(0);bt(1,it);const kt=Y((t,e)=>e.find(t)),St=t=>()=>t,vt=t=>t,Pt=Y((t,e)=>e.split(t)),At=St(!0),Et=St(!1),Nt=Y((t,e)=>mt((e,n)=>kt(e=>t(n,e),e)?e:ft(n,e),[],e))(dt),jt=(t,e,n)=>et(e)?(t=>ot(t)||(t=>t===st)(t))(n)?t:_t(s=>s in n?jt(t,bt(1,it,e),n[s]):t,wt)(e):n,qt=tt(jt);qt(st),_t(pt(dt(at),Et,At),qt(at));const zt=Y((t,e)=>e.map(t)),{floor:Ot}=Math,xt="0123456789abcdefghijklmnopqrstuvwxyz",Wt=ht("String"),$t=gt(Wt,Pt("")),Ct=_t(t=>Object.fromEntries(t),zt((t,e)=>[t,e]),$t);class Mt{is_str;delim;abc;abclen;c2pos;standard;setABC(t,e=""){if(this.is_str=Wt(t),this.delim=e,!_t(ut(et(n=t)),et,Nt,$t)(n))throw new Error("Not all chars are unique!");var n;this.abc=t,this.abclen=et(t),this.standard=!!this.is_str&&xt.startsWith(t),this.c2pos=Ct(t)}zip(t){const{abc:e,abclen:n,delim:s}=this;let i="",r=!0;for(;t>0;)i=e[t%n]+(r?"":s)+i,t=Ot(t/n),r=!1;return i||"0"}unzip(t){const{standard:e,abclen:n,c2pos:s,delim:i,is_str:r}=this;if("0"===t)return 0;if(e)return parseInt(t,n);const o=r?t:t.split(i),c=et(o);let a=0;for(let t=0;t<c;t++)a+=s[o[t]]*n**(c-t-1);return a}constructor(t,e){e?this.setABC(t,e):this.setABC(t||xt+"ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}const Bt=new Mt;Bt.setABC.bind(Bt),Bt.zip.bind(Bt),Bt.unzip.bind(Bt);const Tt=(()=>{try{return WebSocket||null}catch{return null}})(),Lt=(t,e,n)=>t.addEventListener(e,n),Rt=(t,e)=>setTimeout(e,t),{min:It,random:Ut}=Math,Zt=t=>{if(null===Tt&&!("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=q({data_type:"json",log:()=>null,timer:!1,url:"",timeout:1.4,reconnect:{stop_after:45,on_timeout:!0,on_break:!0,time_fn:({base:t,max:e,jitter:n},s)=>It(e,t**(s+Ut()*n)),params:{base:2,max:20,jitter:.1}},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,out:"ping",in:"pong"}},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},{random:Dt}=Math,Ft=null,Jt=1/0,Gt=Promise.resolve(Ft),Ht="message",Kt="message-ext",Qt=new Mt,Vt=()=>Date.now()/1e3,Xt=T(1e3),Yt=t=>t&&clearTimeout(t),te=t=>null===t,ee=S("String"),ne=S("Object"),se=(t,e,n,s=.5)=>{const i=Xt(s),r=setTimeout(()=>{const s=t.indexOf(e);~s&&(t.splice(s),n(`could not close in ${i}ms!`))},1e3*i);t.push((...t)=>{Yt(r),e(...t)})},ie=t=>{const e=Qt.zip(2147483637*Dt()|0);return e in t?ie(t):e},re=(t,...e)=>{for(const n of t)n(...e);return t},oe=t=>(t.splice(0),t),ce=(t,e)=>e(t);exports.WebSocketClient=class{ws=Ft;intentionally_closed=!1;reconnect_timeout=Ft;queue={send:new Map,on_ready:[],on_close:[],on_ready_fail:[]};handlers={open:[],close:[],message:[],[Kt]:[],error:[],timeout:[]};config={};ping_timer=Ft;idle_timer=Ft;zombie_timer=Ft;router=ce;get opened(){return 1===this.ws?.readyState}call(t,...e){for(const n of this.handlers[t])n(...e)}log(t,e=Ft,n=Ft){const{config:s}=this;setTimeout(()=>{te(n)?s.timer?s.log(t,Ft,e):s.log(t,e):s.log(t,n,e)})}resetPing(){const{config:{ping:t},ping_timer:e}=this;t&&(Yt(e),this.ping_timer=Rt(Xt(t.interval),async()=>{const{ping_timer:e,opened:n}=this;n?(this.ws.send(t.out),this.resetPing()):Yt(e)}))}resetZombieProbe(){const{config:t}=this;if(t.ping){const e=t.ping.timeout;Yt(this.zombie_timer),e!==1/0&&(this.zombie_timer=Rt(Xt(e||t.timeout),()=>this.close().catch(Z)))}}resetIdle(){const{config:{max_idle_time:t},idle_timer:e}=this;t!==1/0&&(Yt(e),this.idle_timer=Rt(Xt(t),()=>this.opened&&this.close()))}_reconnecting=!1;reconnect_start=0;async reconnect(t=0){if(this._reconnecting&&0===t)return;this.log("reconnect"),this._reconnecting=!0,this.reconnect_start=Vt(),p(this.ws)||this.terminate();const{queue:e}=this;if(t>0&&p(await this.connect()))oe(re(e.on_ready)),oe(e.on_ready_fail),this._reconnecting=!1,this.reconnect_timeout=Ft;else{const{stop_after:n,time_fn:s,params:i}=this.config.reconnect;Vt()-this.reconnect_start>n?(this.terminate(),oe(re(e.on_ready_fail)),oe(e.on_ready),this._reconnecting=!1,this.reconnect_timeout=Ft):this.reconnect_timeout=Rt(Xt(s(i,t)),this.reconnect.bind(this,t+1))}}resetReconnect(){te(this.reconnect_timeout)||(Yt(this.reconnect_timeout),this.reconnect_timeout=Ft)}initSocket(t){const{queue:e,config:n,router:s}=this;this.ws=t,oe(re(this.queue.on_ready));const{id_key:i,data_key:r}=n.server;this.call("open",t);for(const{msg:n}of e.send.values())t.send(n);this.resetReconnect(),this.resetZombieProbe(),this.resetPing(),this.resetIdle(),Lt(t,"close",async(...t)=>{this.ws=Ft,oe(re(e.on_close)),this.call("close",...t),!this.intentionally_closed&&n.reconnect.on_break&&this.reconnect()});const{ping:o}=n,c=t=>{try{const e=n.decode(t),{send:s}=this.queue;let o=!1;if(ne(e)&&i in e){const t=e[i];if(s.has(t)){const n=s.get(t),i=e[r],c=n.sent_time?Vt()-n.sent_time:Ft;this.log(Ht,i,c),n.ff(i),o=!0}}const c=o?Ht:Kt;this.log(c,e),this.call(c,{data:e})}catch(e){console.error(e,`WSP: Decode error. Got: ${t}`)}};Lt(t,Ht,t=>{const e=ee(t.data)?t.data:new Uint8Array(t.data);this.resetZombieProbe(),this.resetPing(),o&&P(e,o.in)||s(e,c)})}_opening=!1;connect(){return this.opened||this._opening||this.opened||this._opening?Gt:new Promise(t=>{this._opening=!0;const e=this.config,n=e.socket||e.adapter(e.url,e.protocols);if(p(n)||n.readyState>1)return this._opening=!1,this.ws=Ft,this.log("error","ready() on closing or closed state! status 2."),t(2);const s=F(e=>{this._opening=!1,t(e)});Lt(n,"error",F(t=>{this.ws=Ft,this.log("error","status 3. Err: "+(t.message||t)),this.call("error",t),s(3)})),n.readyState?(this.initSocket(n),s(Ft)):Lt(n,"open",F(()=>{this.log("open"),this.initSocket(n),s(Ft)}))})}get socket(){return this.ws}async ready(t=Jt){return new Promise((e,n)=>{const{on_ready:s}=this.queue;this.config.lazy||this.opened?e():t===Jt?s.push(e):se(s,e,n)})}on(t,e,n=I,s=!1){const i=t=>n(t)&&e(t);return s?Lt(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)}terminate(){this.ws?.close(),this.ws=Ft,this.intentionally_closed=!0}async close(t=.5){return new Promise((e,n)=>{te(this.ws)?e(Ft):(se(this.queue.on_close,e,n,t),this.terminate())})}open(){if(!this.opened)return this.intentionally_closed=!1,this.connect()}addEventListener(t,e,n={}){return this.on(t,e,n.predicate,n.raw)}removeEventListener(t,e,n={}){return this.off(t,e,n.raw)}async prepareMessage(t,e={}){this.log("send",t);const{config:n,queue:{send:s,on_ready_fail:i}}=this,{pipes:r,server:{data_key:o}}=n,{top:c}=e,a=ie(s);if(ne(c)&&o in c)throw new Error("\n Attempting to set data key/token via send() options!\n ");for(const e of r)t=e(t);const[l,h]=await Promise.all([n.encode(a,t,n),this.connect()]);if(h)throw new Error("ERR while opening connection > "+h);const u=c?.timeout||n.timeout,d=R(()=>s.delete(a));return{id:a,msg:l,timeout:e=>Rt(Xt(u),()=>{if(s.has(a)){this.call("timeout",t),d();const s=()=>e({"Websocket timeout expired":u,"for the message":t});n.reconnect.on_timeout?(i.push(s),this.reconnect()):s()}}),cleanup:d,send:()=>this.opened&&(this.ws.send(l),this.resetPing(),this.resetIdle())}}async send(t,e={}){const{id:n,msg:s,timeout:i,cleanup:r,send:o}=await this.prepareMessage(t,e),{queue:{send:c},config:a}=this;return new Promise((t,e)=>{const r=i(e);c.set(n,{msg:s,data_type:a.data_type,sent_time:a.timer?Vt():Ft,ff(e){Yt(r),t(e)}}),o()}).finally(r)}async*stream(t,e={}){const{id:n,msg:s,timeout:i,cleanup:r,send:o}=await this.prepareMessage(t,e),{queue:{send:c},config:a}=this;let l,h=!1,u=Ft;for(c.set(n,{msg:s,ff:t=>{t?.done&&(delete t.done,h=!0,setTimeout(r)),l(t)},data_type:a.data_type,sent_time:a.timer?Vt():Ft}),o();!h;)yield await new Promise((t,e)=>{u=i(e),l=t}).catch(r).finally(()=>Yt(u))}route(t){this.router=t}constructor(t){this.config=Zt(t),this.config.lazy||this.connect()}};
1
+ 'use strict';
2
+
3
+ const __ = Symbol('Placeholder');
4
+ const countArgs = (s) => {
5
+ let i = 0;
6
+ for (const v of s)
7
+ v !== __ && i++;
8
+ return i;
9
+ };
10
+ // TODO: try to make it mutable.
11
+ // { 0: __, 1: 10 }, [ 11 ]
12
+ const addArgs = (args, _args) => {
13
+ const len = args.length;
14
+ const new_args = args.slice();
15
+ const _args_len = _args.length;
16
+ let _args_left = _args_len;
17
+ let i = 0;
18
+ for (; _args_left && i < len; i++) {
19
+ if (new_args[i] === __) {
20
+ new_args[i] = _args[_args_len - _args_left];
21
+ _args_left--;
22
+ }
23
+ }
24
+ for (i = len; _args_left; i++, _args_left--) {
25
+ new_args[i] = _args[_args_len - _args_left];
26
+ }
27
+ return new_args;
28
+ };
29
+ const _curry = (fn, args, new_args) => {
30
+ const args2add = fn.length - args.length - countArgs(new_args);
31
+ if (args2add < 1) {
32
+ return fn(...addArgs(args, new_args));
33
+ }
34
+ else {
35
+ const curried = (...__args) => _curry(fn, addArgs(args, new_args), __args);
36
+ curried.$args_left = args2add;
37
+ return curried;
38
+ }
39
+ };
40
+ const curry = (fn) => ((...args) => fn.length > countArgs(args)
41
+ ? _curry(fn, [], args)
42
+ : fn(...args));
43
+ const endlessph = (fn) => {
44
+ function _endlessph(a) {
45
+ return a === __ ? fn : fn(a);
46
+ }
47
+ return _endlessph;
48
+ };
49
+ const zero = 0;
50
+ function curry2(fn) {
51
+ function curried2(a, ...args) {
52
+ return args.length > zero
53
+ ? a === __
54
+ ? endlessph((a) => fn(a, args[zero]))
55
+ : fn(a, args[zero])
56
+ : (b) => fn(a, b);
57
+ }
58
+ return curried2;
59
+ }
60
+ function curry3(fn) {
61
+ // type p0 = Parameters<Func>[0]
62
+ // type p1 = Parameters<Func>[1]
63
+ // type p2 = Parameters<Func>[2]
64
+ // type ReturnT = ReturnType<Func>
65
+ // TODO: optimize.
66
+ // Cannot use ts-toolbelt due to this error:
67
+ // Excessive stack depth comparing types 'GapsOf<?, L2>' and 'GapsOf<?, L2>'
68
+ return curry(fn);
69
+ }
70
+
71
+ const length = (s) => s.length;
72
+ const is_typed_arr = (x) => ArrayBuffer.isView(x);
73
+
74
+ const unsafe_props = { '__proto__': true, 'constructor': true, 'prototype': true };
75
+ const undef = undefined;
76
+ const nul = null;
77
+ const inf$1 = Infinity;
78
+ const to = (s) => typeof s;
79
+ const isNull$1 = (s) => (s === nul);
80
+ const isUndef = (s) => (s === undef);
81
+ const isNum = (s) => (to(s) == 'number');
82
+ const isNil = (s) => (isNull$1(s) || isUndef(s));
83
+ const isSafe = (prop) => !(prop in unsafe_props);
84
+
85
+ const { isNaN } = Number;
86
+ // It's faster that toUpperCase() !
87
+ const caseMap = { u: 'U', b: 'B', n: 'N', s: 'S', f: 'F', o: 'O' };
88
+ const symbol = Symbol();
89
+ const cap_type = (t) => caseMap[t[0]] + t.slice(1);
90
+ const type = (s) => {
91
+ const t = to(s);
92
+ return t === 'object'
93
+ ? isNull$1(s) ? 'Null' : (s.constructor?.name || cap_type(t))
94
+ : t === 'number' && isNaN(s) ? 'NaN'
95
+ : cap_type(t);
96
+ };
97
+ const typeIs = curry2((t, s) => type(s) === t);
98
+ const eq = curry2((a, b) => a === b);
99
+ const equals = curry2((a, b) => {
100
+ if (a === b)
101
+ return true;
102
+ const typea = type(a);
103
+ const ta = is_typed_arr(a);
104
+ if (eq(typea, type(b)) && (eq(typea, 'Object') || eq(typea, 'Array') || ta)) {
105
+ if (ta) {
106
+ if (typea === 'Buffer')
107
+ return a.equals(b);
108
+ const len = length(a);
109
+ if (len !== length(b))
110
+ return false;
111
+ for (let i = 0; i < len; i++)
112
+ if (a[i] !== b[i])
113
+ return false;
114
+ return true;
115
+ }
116
+ if (isNull$1(a) || isNull$1(b))
117
+ return eq(a, b);
118
+ for (const v of [a, b])
119
+ for (const k in v)
120
+ if (!(v === b && (k in a)) &&
121
+ !(v === a && (k in b) && equals(a[k], b[k])))
122
+ return false;
123
+ return true;
124
+ }
125
+ return false;
126
+ });
127
+ const always = (s) => () => s;
128
+ const identity = (s) => s;
129
+ const z$1 = 0;
130
+ /* qflat, qflatShallow, qreduceAsync */
131
+ const qappend = curry2((s, xs) => { xs.push(s); return xs; });
132
+ const qreduce = curry3((fn, accum, arr) => arr.reduce(fn, accum));
133
+ // strategy is for arrays: 1->replace, 2->merge, 3->push.
134
+ const mergeDeep$1 = (strategy) => curry2((o1, o2) => {
135
+ for (let k in o2) {
136
+ if (isSafe(k))
137
+ switch (type(o2[k])) {
138
+ case 'Array':
139
+ if (strategy > 1 && type(o1[k]) === 'Array')
140
+ switch (strategy) {
141
+ case 2:
142
+ const o1k = o1[k], o2k = o2[k];
143
+ for (const i in o2k)
144
+ if (o1k[i])
145
+ mergeDeep$1(strategy)(o1k[i], o2k[i]);
146
+ else
147
+ o1k[i] = o2k[i];
148
+ break;
149
+ case 3: o1[k].push(...o2[k]);
150
+ }
151
+ else
152
+ o1[k] = o2[k];
153
+ break;
154
+ case 'Object':
155
+ if (type(o1[k]) === 'Object') {
156
+ mergeDeep$1(strategy)(o1[k], o2[k]);
157
+ break;
158
+ }
159
+ default:
160
+ o1[k] = o2[k];
161
+ break;
162
+ }
163
+ }
164
+ return o1;
165
+ });
166
+ const qmergeDeep = mergeDeep$1(1);
167
+ /** Should be faster than .splice() 'cause does not make a new array. */
168
+ const rmel = (index, xs) => {
169
+ const len = length(xs);
170
+ for (let i = index; i < len; i++)
171
+ xs[i] = xs[i + 1];
172
+ xs.length = len - 1;
173
+ return xs;
174
+ };
175
+ const seen = new Set();
176
+ const quniqWith = curry2((getter, xs) => {
177
+ let size = length(xs), cur;
178
+ for (let i = z$1; i < size; i++) {
179
+ const x = xs[i];
180
+ cur = getter(x);
181
+ if (seen.has(cur)) {
182
+ rmel(i, xs);
183
+ size--;
184
+ i--;
185
+ }
186
+ else
187
+ seen.add(cur);
188
+ }
189
+ seen.clear();
190
+ return xs;
191
+ });
192
+ quniqWith(identity);
193
+ const ifElse = curry((cond, pipeYes, pipeNo, s) => cond(s) ? pipeYes(s) : pipeNo(s));
194
+ const compose = ((...fns) => (...args) => {
195
+ let first = true;
196
+ let s;
197
+ for (let i = length(fns) - 1; i > -1; i--) {
198
+ if (first) {
199
+ first = false;
200
+ s = fns[i](...args);
201
+ }
202
+ else
203
+ s = s === __ ? fns[i]() : fns[i](s);
204
+ }
205
+ return s;
206
+ });
207
+ const nth = curry2((i, data) => data[i]);
208
+ // FIXME: these types. Somewhere in curry2.
209
+ // const x = nth(0)([1,2,3])
210
+ // const y = nth(0)('123')
211
+ // const z = nth(0)(new Uint8Array([0,2,3]))
212
+ const slice = curry3((from, to, o) => o.slice(from, (isNum(to) ? to : inf$1)));
213
+ /** @returns first element of an array or a string. */
214
+ const head = nth(0);
215
+ /** @returns all elements of an array or a string after first one. */
216
+ slice(1, inf$1);
217
+ /**@param a @param b @returns a×b */
218
+ const multiply = curry2((a, b) => a * b);
219
+ const find = curry2((fn, s) => s.find(fn));
220
+ const tap = curry2((fn, x) => { fn(x); return x; });
221
+ const T = always(true);
222
+ const F$1 = always(false);
223
+ const noop = (() => { });
224
+ /** @param cond (x, y): bool @param xs any[] @returns xs without duplicates, using cond as a comparator. */
225
+ const uniqWith = curry2((cond, xs) => qreduce((accum, x) => find((y) => cond(x, y), accum) ? accum : qappend(x, accum), [], xs));
226
+ /** @param xs any[] @returns xs without duplicates. */
227
+ uniqWith(equals);
228
+ const once = (fn) => {
229
+ let done = false, cache;
230
+ return function (...args) {
231
+ if (done)
232
+ return cache;
233
+ done = true;
234
+ return cache = fn(...args);
235
+ };
236
+ };
237
+ const _pathOr = (_default, path, o) => length(path)
238
+ ? isNil(o)
239
+ ? _default
240
+ : compose((k) => k in o ? _pathOr(_default, slice(1, inf$1, path), o[k]) : _default, head)(path)
241
+ : o;
242
+ const pathOr = curry3(_pathOr); // it's more performant due to recursion there.
243
+ pathOr(undef);
244
+ compose(ifElse(equals(symbol), F$1, T), pathOr(symbol));
245
+
246
+ const t=Symbol("Placeholder"),r=r=>{let n=0;for(const e of r)e!==t&&n++;return n},n=(r,n)=>{const e=r.length,s=r.slice(),i=n.length;let o=i,c=0;for(;o&&c<e;c++)s[c]===t&&(s[c]=n[i-o],o--);for(c=e;o;c++,o--)s[c]=n[i-o];return s},e=(t,s,i)=>{const o=t.length-s.length-r(i);if(o<1)return t(...n(s,i));{const r=(...r)=>e(t,n(s,i),r);return r.$args_left=o,r}},s=t=>(...n)=>t.length>r(n)?e(t,[],n):t(...n);function i(r){return function(n,...e){return e.length>0?n===t?(r=>function(n){return n===t?r:r(n)})((t=>r(t,e[0]))):r(n,e[0]):t=>r(n,t)}}function o(t){return s(t)}const c=t=>t.length,l=/^(.*?)(8|16|32|64)(Clamped)?Array$/,u=void 0,a=1/0,f=t=>typeof t,h=t=>null===t,d=t=>"number"==f(t),b=t=>h(t)||(t=>t===u)(t),p={u:"U",b:"B",n:"N",s:"S",f:"F"},m=Symbol(),g=t=>{const r=f(t);return "object"===r?h(t)?"Null":t.constructor.name:p[r[0]]+r.slice(1)},A=i(((t,r)=>g(r)===t)),y=i(((t,r)=>t===r)),B=i(((t,r)=>{const n=g(t);if(y(n,g(r))&&(y(n,"Object")||y(n,"Array")||(e=n,l.test(e)))){if(h(t)||h(r))return y(t,r);if(y(t,r))return true;for(const n of [t,r])for(const e in n)if(!(y(n,r)&&e in t||y(n,t)&&e in r&&B(t[e],r[e])))return false;return true}var e;return y(t,r)})),C=i(((t,r)=>(r.push(t),r))),z=o(((t,r,n)=>n.reduce(t,r))),S=s(((t,r,n,e)=>t(e)?r(e):n(e))),_=o(((t,r,n)=>S(t,r,q,n))),j=(...r)=>(...n)=>{let e,s=true;for(let i=c(r)-1;i>-1;i--)s?(s=false,e=r[i](...n)):e=e===t?r[i]():r[i](e);return e},v=i(((t,r)=>r[t])),w=o(((t,r,n)=>n.slice(t,d(r)?r:a))),N=v(0);w(1,a);const E=i(((t,r)=>r.find(t))),O=t=>()=>t,q=t=>t,x=i(((t,r)=>r.split(t))),F=O(true),I=O(false),M=i(((t,r)=>z(((r,n)=>E((r=>t(n,r)),r)?r:C(n,r)),[],r)))(B),P=(t,r,n)=>c(r)?b(n)?t:j((e=>e in n?P(t,w(1,a,r),n[e]):t),N)(r):n,U=o(P);U(u),j(S(B(m),I,F),U(m));const W=i(((t,r)=>r.map(t))),{floor:$}=Math,k="0123456789abcdefghijklmnopqrstuvwxyz",D=A("String"),G=_(D,x("")),H=j((t=>Object.fromEntries(t)),W(((t,r)=>[t,r])),G);class J{is_str;delim;abc;abclen;c2pos;standard;setABC(t,r=""){if(this.is_str=D(t),this.delim=r,!j(y(c(n=t)),c,M,G)(n))throw new Error("Not all chars are unique!");var n;this.abc=t,this.abclen=c(t),this.standard=!!this.is_str&&k.startsWith(t),this.c2pos=H(t);}zip(t){const{abc:r,abclen:n,delim:e}=this;let s="",i=true;for(;t>0;)s=r[t%n]+(i?"":e)+s,t=$(t/n),i=false;return s||"0"}unzip(t){const{standard:r,abclen:n,c2pos:e,delim:s,is_str:i}=this;if("0"===t)return 0;if(r)return parseInt(t,n);const o=i?t:t.split(s),l=c(o);let u=0;for(let t=0;t<l;t++)u+=e[o[t]]*n**(l-t-1);return u}constructor(t,r){r?this.setABC(t,r):this.setABC(t||k+"ABCDEFGHIJKLMNOPQRSTUVWXYZ");}}const K=new J;K.setABC.bind(K);K.zip.bind(K);K.unzip.bind(K);
247
+
248
+ const native_ws = (() => { try {
249
+ return WebSocket || null;
250
+ }
251
+ catch {
252
+ return null;
253
+ } })();
254
+ const add_event = (o, e, handler) => {
255
+ return o.addEventListener(e, handler);
256
+ };
257
+ const rm_event = (o, e, handler) => {
258
+ return o.removeEventListener(e, handler);
259
+ };
260
+ const sett = (a, b) => setTimeout(b, a);
261
+
262
+ const { min, random: random$1 } = Math;
263
+ const default_config = () => ({
264
+ // Debug features.
265
+ log: (() => null),
266
+ timer: false,
267
+ // Set up.
268
+ url: '',
269
+ timeout: 1.4,
270
+ reconnect: {
271
+ stop_after: 45,
272
+ on_timeout: true,
273
+ on_break: true,
274
+ time_fn: ({ base, max, jitter }, attempt) => min(max, base ** (attempt + random$1() * jitter)),
275
+ params: { base: 2, max: 20, jitter: .1 }
276
+ },
277
+ max_idle_time: Infinity,
278
+ lazy: false,
279
+ socket: null,
280
+ adapter: ((host, protocols) => new WebSocket(host, protocols)),
281
+ encode: (key, data, { server }) => JSON.stringify({
282
+ [server.id_key]: key,
283
+ [server.data_key]: data
284
+ }),
285
+ decode: (rawMessage) => JSON.parse(rawMessage),
286
+ protocols: [], pipes: [],
287
+ server: { id_key: 'id', data_key: 'data' },
288
+ ping: { interval: 55, timeout: 30, out: 'ping', in: 'pong' }
289
+ });
290
+ const processConfig = (config) => {
291
+ if (native_ws === null && !('adapter' in config))
292
+ throw new Error(`
293
+ This platform has no native WebSocket implementation.
294
+ Please use 'ws' package as an adapter.
295
+ See https://github.com/houd1ni/WebsocketPromisify/issues/23
296
+ `);
297
+ const full_config = qmergeDeep(default_config(), config);
298
+ const url = full_config.url;
299
+ if (url[0] == '/')
300
+ try {
301
+ const protocol = location.protocol.includes('s:') ? 'wss' : 'ws';
302
+ full_config.url = `${protocol}://${location.hostname}:${location.port}${url}`;
303
+ }
304
+ catch (e) {
305
+ throw new Error('WSP: URL starting with / in non-browser environment!');
306
+ }
307
+ return full_config;
308
+ };
309
+
310
+ const { random } = Math;
311
+ const MAX_32 = 2 ** 31 - 1;
312
+ const nil = null, inf = Infinity;
313
+ const resolved = Promise.resolve(nil);
314
+ const label_message = 'message';
315
+ const label_message_ext = 'message-ext';
316
+ const zipnum = new J();
317
+ const dnow = () => Date.now();
318
+ const now = () => dnow() / 1e3;
319
+ const ms = multiply(1e3);
320
+ const clearTO = (to) => to && clearTimeout(to);
321
+ const isNull = (x) => x === null;
322
+ const isStr = typeIs('String');
323
+ const isObj = typeIs('Object');
324
+ const timeout_rm = (q, ff, rj, timeout = .5) => {
325
+ const timeout_ms = ms(timeout);
326
+ const rm = setTimeout(() => {
327
+ const i = q.indexOf(ff);
328
+ if (~i) {
329
+ q.splice(i);
330
+ rj(`could not close in ${timeout_ms}ms!`);
331
+ }
332
+ }, timeout_ms * 1e3);
333
+ q.push((...ps) => { clearTO(rm); ff(...ps); });
334
+ };
335
+ const genid = (q) => {
336
+ const id = zipnum.zip((random() * (MAX_32 - 10)) | 0);
337
+ return id in q ? genid(q) : id;
338
+ };
339
+ const call_q = (q, ...args) => {
340
+ for (const fn of q)
341
+ fn(...args);
342
+ return q;
343
+ };
344
+ const clear_q = (q) => { q.splice(0); return q; };
345
+ const default_router = (d, next) => next(d);
346
+ class WebSocketClient {
347
+ ws = nil;
348
+ intentionally_closed = false;
349
+ reconnect_timeout = nil;
350
+ queue = {
351
+ send: new Map(),
352
+ on_ready: [],
353
+ on_close: [],
354
+ on_ready_fail: []
355
+ };
356
+ handlers = {
357
+ open: [], close: [], message: [], [label_message_ext]: [], error: [], timeout: []
358
+ };
359
+ config = {};
360
+ ping_timer = nil;
361
+ idle_timer = nil;
362
+ zombie_timer = nil;
363
+ router = default_router;
364
+ get opened() { return this.ws?.readyState === 1; } // The only opened state.
365
+ call(event_name, ...args) {
366
+ for (const h of this.handlers[event_name])
367
+ h(...args);
368
+ }
369
+ log(event, message = nil, time = nil) {
370
+ const { config } = this;
371
+ setTimeout(() => {
372
+ if (isNull(time))
373
+ if (config.timer)
374
+ config.log(event, nil, message);
375
+ else
376
+ config.log(event, message);
377
+ else
378
+ config.log(event, time, message);
379
+ });
380
+ }
381
+ resetPing() {
382
+ const { config: { ping }, ping_timer } = this;
383
+ if (ping) {
384
+ clearTO(ping_timer);
385
+ this.ping_timer = sett(ms(ping.interval), async () => {
386
+ const { ping_timer, opened } = this;
387
+ if (opened) {
388
+ this.ws.send(ping.out);
389
+ this.resetPing();
390
+ }
391
+ else
392
+ clearTO(ping_timer);
393
+ });
394
+ }
395
+ }
396
+ resetZombieProbe() {
397
+ const { config } = this;
398
+ if (config.ping) {
399
+ const z_timeout = config.ping.timeout;
400
+ clearTO(this.zombie_timer);
401
+ if (z_timeout !== Infinity)
402
+ this.zombie_timer = sett(ms(z_timeout || config.timeout), () => this.close().catch(noop));
403
+ }
404
+ }
405
+ // FIXME: Make some version where it could work faster (for streaming).
406
+ resetIdle() {
407
+ const { config: { max_idle_time: time }, idle_timer } = this;
408
+ if (time !== Infinity) {
409
+ clearTO(idle_timer);
410
+ this.idle_timer = sett(ms(time), () => this.opened && this.close());
411
+ }
412
+ }
413
+ _reconnecting = false;
414
+ reconnect_start = 0;
415
+ async reconnect(attempt = 0) {
416
+ if (this._reconnecting && attempt === 0)
417
+ return;
418
+ this.log('reconnect');
419
+ this._reconnecting = true;
420
+ this.reconnect_start = now();
421
+ if (!isNil(this.ws))
422
+ this.terminate();
423
+ const { queue } = this;
424
+ if (attempt > 0 && isNil(await this.connect())) {
425
+ clear_q(call_q(queue.on_ready));
426
+ clear_q(queue.on_ready_fail);
427
+ this._reconnecting = false;
428
+ this.reconnect_timeout = nil;
429
+ }
430
+ else {
431
+ const { stop_after, time_fn, params } = this.config.reconnect;
432
+ if (now() - this.reconnect_start > stop_after) {
433
+ this.terminate();
434
+ clear_q(call_q(queue.on_ready_fail));
435
+ clear_q(queue.on_ready);
436
+ this._reconnecting = false;
437
+ this.reconnect_timeout = nil;
438
+ }
439
+ else
440
+ this.reconnect_timeout = sett(ms(time_fn(params, attempt)), this.reconnect.bind(this, attempt + 1));
441
+ }
442
+ }
443
+ resetReconnect() {
444
+ if (!isNull(this.reconnect_timeout)) {
445
+ clearTO(this.reconnect_timeout);
446
+ this.reconnect_timeout = nil;
447
+ }
448
+ }
449
+ initSocket(ws) {
450
+ const { queue, config, router } = this;
451
+ this.ws = ws;
452
+ clear_q(call_q(this.queue.on_ready));
453
+ const { id_key, data_key } = config.server;
454
+ // works also on previously opened sockets that do not fire 'open' event.
455
+ this.call('open', ws);
456
+ for (const { msg } of queue.send.values())
457
+ ws.send(msg);
458
+ this.resetReconnect();
459
+ this.resetZombieProbe();
460
+ this.resetPing();
461
+ this.resetIdle();
462
+ add_event(ws, 'close', async (...e) => {
463
+ this.ws = nil;
464
+ clear_q(call_q(queue.on_close));
465
+ this.call('close', ...e);
466
+ if (!this.intentionally_closed && config.reconnect.on_break)
467
+ this.reconnect();
468
+ });
469
+ const { ping } = config;
470
+ const handle_msg = (raw) => {
471
+ try {
472
+ const data = config.decode(raw);
473
+ const { send: send_q } = this.queue;
474
+ if (isObj(data) && id_key in data) {
475
+ const id = data[id_key];
476
+ if (send_q.has(id)) {
477
+ const q = send_q.get(id);
478
+ const d = data[data_key];
479
+ const time = q.sent_time ? (dnow() - q.sent_time) : nil;
480
+ this.log(label_message, d, time);
481
+ this.call(label_message, d);
482
+ q.ff(d);
483
+ }
484
+ }
485
+ else {
486
+ this.log(label_message_ext, data);
487
+ this.call(label_message_ext, { data });
488
+ }
489
+ }
490
+ catch (err) {
491
+ console.error(err, `WSP: Decode error. Got: ${raw}`);
492
+ }
493
+ };
494
+ add_event(ws, label_message, (e) => {
495
+ const raw = isStr(e.data) ? e.data : new Uint8Array(e.data);
496
+ this.resetZombieProbe();
497
+ this.resetPing();
498
+ if (!ping || !equals(raw, ping.in))
499
+ router(raw, handle_msg);
500
+ });
501
+ }
502
+ _opening = false;
503
+ /** returns status if won't open or null if ok. */
504
+ connect() {
505
+ if (this.opened || this._opening)
506
+ return resolved;
507
+ return this.opened || this._opening ? resolved : new Promise((ff) => {
508
+ this._opening = true;
509
+ const config = this.config;
510
+ const ws = config.socket || config.adapter(config.url, config.protocols);
511
+ if (isNil(ws) || ws.readyState > 1) {
512
+ this._opening = false;
513
+ this.ws = nil;
514
+ this.log('error', 'ready() on closing or closed state! status 2.');
515
+ return ff(2);
516
+ }
517
+ const ffo = once((s) => { this._opening = false; ff(s); });
518
+ add_event(ws, 'error', once((e) => {
519
+ this.ws = nil; // Some network error: Connection refused or so.
520
+ this.log('error', 'status 3. Err: ' + (e.message || e));
521
+ this.call('error', e);
522
+ ffo(3);
523
+ }));
524
+ if (ws.readyState) { // Because 'open' won't be envoked on opened socket.
525
+ this.initSocket(ws);
526
+ ffo(nil);
527
+ }
528
+ else
529
+ add_event(ws, 'open', once(() => {
530
+ this.log('open');
531
+ this.initSocket(ws);
532
+ ffo(nil);
533
+ }));
534
+ });
535
+ }
536
+ get socket() { return this.ws; }
537
+ async ready(timeout = inf) {
538
+ return new Promise((ff, rj) => {
539
+ const { on_ready } = this.queue;
540
+ if (this.config.lazy || this.opened)
541
+ ff();
542
+ else if (timeout === inf)
543
+ on_ready.push(ff);
544
+ else
545
+ timeout_rm(on_ready, ff, rj);
546
+ });
547
+ }
548
+ on(event_name, handler, predicate = T, raw = false) {
549
+ const _handler = (event) => predicate(event) && handler(event);
550
+ if (raw)
551
+ add_event(this.ws, event_name, _handler);
552
+ else
553
+ this.handlers[event_name].push(_handler);
554
+ return _handler;
555
+ }
556
+ off(event_name, handler, raw = false) {
557
+ if (raw)
558
+ return rm_event(this.ws, event_name, handler);
559
+ const handlers = this.handlers[event_name];
560
+ const i = handlers.indexOf(handler);
561
+ if (~i)
562
+ handlers.splice(i, 1);
563
+ }
564
+ terminate() {
565
+ this.ws?.close();
566
+ this.ws = nil;
567
+ this.intentionally_closed = true;
568
+ }
569
+ async close(timeout = .5) {
570
+ return new Promise((ff, rj) => {
571
+ if (isNull(this.ws))
572
+ ff(nil);
573
+ else {
574
+ timeout_rm(this.queue.on_close, ff, rj, timeout);
575
+ this.terminate();
576
+ }
577
+ });
578
+ }
579
+ open() {
580
+ if (!this.opened) {
581
+ this.intentionally_closed = false;
582
+ return this.connect();
583
+ }
584
+ }
585
+ addEventListener(e, cb, opts = {}) { return this.on(e, cb, opts.predicate, opts.raw); }
586
+ removeEventListener(e, handler, opts = {}) { return this.off(e, handler, opts.raw); }
587
+ // TODO: Сделать сэттер элементов конфигурации чтобы двигать таймауты.
588
+ // И эвент, когда схема наша, а соответствующего элемента очереди не ма.
589
+ // Или добавить флажок к эвенту 'message'.F
590
+ // И событие 'line' со значением on: boolean. Критерии?
591
+ async prepareMessage(message_data, opts = {}) {
592
+ this.log('send', message_data);
593
+ const { config, queue: { send: send_q, on_ready_fail } } = this;
594
+ const { pipes, server: { data_key } } = config;
595
+ const { top } = opts;
596
+ const id = genid(send_q);
597
+ if (isObj(top) && data_key in top)
598
+ throw new Error(`
599
+ Attempting to set data key/token via send() options!
600
+ `);
601
+ for (const pipe of pipes)
602
+ message_data = pipe(message_data);
603
+ const [msg, err] = await Promise.all([
604
+ config.encode(id, message_data, config),
605
+ this.connect()
606
+ ]);
607
+ if (err)
608
+ throw new Error('ERR while opening connection > ' + err);
609
+ const timeout_time = top?.timeout || config.timeout;
610
+ const cleanup = tap(() => send_q.delete(id));
611
+ const timeout = (rj) => sett(ms(timeout_time), () => {
612
+ if (send_q.has(id)) {
613
+ this.call('timeout', message_data);
614
+ cleanup();
615
+ const reject = () => rj({
616
+ 'Websocket timeout expired': timeout_time,
617
+ 'for the message': message_data
618
+ });
619
+ if (config.reconnect.on_timeout) {
620
+ on_ready_fail.push(reject);
621
+ this.reconnect();
622
+ }
623
+ else
624
+ reject();
625
+ }
626
+ });
627
+ const send = () => this.opened && (this.ws.send(msg),
628
+ this.resetPing(),
629
+ this.resetIdle());
630
+ return { id, msg, timeout, cleanup, send };
631
+ }
632
+ /** .send(your_data) wraps request to server with {id: `unique_id`, data: `actually your data`},
633
+ returns a Promise that will be rejected after a timeout or
634
+ resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
635
+ */
636
+ async send(message_data, opts = {}) {
637
+ const { id, msg, timeout, cleanup, send } = await this.prepareMessage(message_data, opts);
638
+ const { queue: { send: send_q }, config } = this;
639
+ return new Promise((ff, rj) => {
640
+ const to = timeout(rj);
641
+ send_q.set(id, {
642
+ msg,
643
+ sent_time: config.timer ? dnow() : nil,
644
+ ff(x) { clearTO(to); ff(x); }
645
+ });
646
+ send();
647
+ }).finally(cleanup);
648
+ }
649
+ // TODO: stream timeouts in the config ?..
650
+ async *stream(message_data, opts = {}) {
651
+ const { id, msg, timeout, cleanup, send } = await this.prepareMessage(message_data, opts);
652
+ const { queue: { send: send_q }, config } = this;
653
+ let done = false, fulfill, to = nil;
654
+ send_q.set(id, {
655
+ msg,
656
+ ff: (msg) => {
657
+ if (msg?.done) {
658
+ delete msg.done;
659
+ done = true;
660
+ setTimeout(cleanup);
661
+ }
662
+ fulfill(msg);
663
+ },
664
+ sent_time: config.timer ? dnow() : nil
665
+ });
666
+ send();
667
+ while (!done)
668
+ yield await new Promise((ff, rj) => {
669
+ to = timeout(rj);
670
+ fulfill = ff;
671
+ }).catch(cleanup).finally(() => clearTO(to));
672
+ }
673
+ route(handler) { this.router = handler; }
674
+ constructor(user_config) {
675
+ this.config = processConfig(user_config);
676
+ if (!this.config.lazy)
677
+ this.connect();
678
+ }
679
+ }
680
+
681
+ exports.WebSocketClient = WebSocketClient;