wspromisify 2.8.2 → 2.9.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/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,u=e=>typeof e,a=e=>null===e,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=e=>{const t=u(e);return"object"===t?a(e)?"Null":e.constructor.name:h[t[0]]+t.slice(1)},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(a(e)||a(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"==u(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))),j=E(!0),A=E(!1),W=i(((e,t)=>w(((t,n)=>v((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),q=r(((e,t,n)=>g(t)?(e=>a(e)||(e=>e===l)(e))(n)?e:b((s=>s in n?q(e,k(1,c,t),n[s]):e),P)(t):n));q(l),b(_(m(f),A,j),q(f));const C=i(((e,t)=>t.map(e))),{floor:O}=Math,Q="0123456789abcdefghijklmnopqrstuvwxyz",$=b((e=>Object.fromEntries(e)),C(((e,t)=>[e,t])),N(""));class z{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=Q.startsWith(e),this.c2pos=$(e)}zip(e){const{abc:t,abclen:n}=this;let s="";for(;e>0;)s=t[e%n]+s,e=O(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||Q+"ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}const R=new z;R.setABC.bind(R),R.zip.bind(R),R.unzip.bind(R);const T=(()=>{try{return WebSocket||null}catch{return null}})(),I=(e,t,n)=>e.addEventListener(t,n),x=(e,t)=>setTimeout(t,e),B={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:{}}},D=Symbol("Placeholder"),L=e=>{let t=0;for(const n of e)n!==D&&t++;return t},U=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let i=o,r=0;for(;i&&r<n;r++)s[r]===D&&(s[r]=t[o-i],i--);for(r=n;i;r++,i--)s[r]=t[o-i];return s},F=(e,t,n)=>{const s=e.length-t.length-L(n);if(s<1)return e(...U(t,n));{const o=(...s)=>F(e,U(t,n),s);return o.$args_left=s,o}},J=e=>(...t)=>e.length>L(t)?F(e,[],t):e(...t);function M(e){return function(t,n){const s=t===D,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(e=>function(t){return t===D?e:e(t)})((t=>e(t,n))):e(t,n):n=>e(t,n)}}function G(e){return J(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=M(((e,t)=>se(t)===e)),ie=e=>e.length,re=M(((e,t)=>e===t)),le=M(((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=M(((e,t)=>(t.push(e),t))),ue=G(((e,t,n)=>n.reduce(e,t))),ae=M(((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=J(((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===D?e[o]():e[o](n);return n},de=M(((e,t)=>t[e])),ge=G(((e,t,n)=>n.slice(e,Z(t)?t:V))),pe=de(0);ge(1,V);const me=M(((e,t)=>t.find(e))),ye=e=>()=>e,we=ye(!0),_e=ye(!1),be=M(((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=M(((e,t)=>ue(((t,n)=>me((t=>e(n,t)),t)?t:ce(n,t)),[],t)));ke(le);const Pe=e=>{let t,n=!1;return(...s)=>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,je=new z,Ae=be([]),We=Ee(oe("Number"),Ne(isNaN)),qe={_is_ping:!0};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(){ae(_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=x(1e3*e.interval,(async()=>{const{ping_timer:t,opened:n}=this;n?(await this.send(e.content,qe),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=x(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(),I(e,"close",(async(...e)=>{this.log("close"),this.ws=null,this.onCloseQueue.forEach(Ae),this.onCloseQueue.splice(0),this.call("close",...e);let{reconnect:t,reconnection_attempts:s}=n;if(We(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()}})),I(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(),this.resetIdle()}))}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)}));I(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)):I(n,"open",Pe((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.opened?e():this.onReadyQueue.push(e)}))}on(e,t,n=we,s=!1){const o=e=>n(e)&&t(e);return s?I(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 send(e,t={}){this.log("send",e);const{config:n,queue:s}=this,o={},{pipes:i,server:{data_key:r}}=n,{top:l,_is_ping:c}=t,u=je.zip(2147483637*Math.random()|0);if("object"==typeof l){if(l[r])throw new Error("Attempting to set data key/token via send() options!");Object.assign(o,l)}for(const t of i)e=t(e);const[a,h]=await Promise.all([n.encode(u,e,n),this.connect()]);if(h)throw new Error("ERR while opening connection #"+h);return this.opened&&(this.ws.send(a),this.resetPing(),c||this.resetIdle()),new Promise(((t,o)=>{this.queue[u]={msg:a,ff(e){clearTimeout(this.timeout),delete s[u],t(e)},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{u in this.queue&&(this.call("timeout",e),o({"Websocket timeout expired":n.timeout,"for the message":e}),delete s[u])}))}}))}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({},B,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";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,u=e=>typeof e,a=e=>null===e,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=e=>{const t=u(e);return"object"===t?a(e)?"Null":e.constructor.name:h[t[0]]+t.slice(1)},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(a(e)||a(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"==u(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=>a(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 T{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 O=new T;O.setABC.bind(O),O.zip.bind(O),O.unzip.bind(O);const R=(()=>{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))),ue=G(((e,t,n)=>n.reduce(e,t))),ae=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)=>ue(((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 T,We=be([]),$e=Ee(oe("Number"),Ne(isNaN)),je={_is_ping:!0},ze=e=>{const t=Ae.zip(2147483637*qe()|0);return t in e?ze(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(){ae(_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=ze(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[u,a]=await Promise.all([n.encode(c,e,n),this.connect()]);if(a)throw new Error("ERR while opening connection #"+a);this.opened&&(this.ws.send(u),this.resetPing(),l||this.resetIdle());const h=()=>delete this.queue[c];return{message_id:c,msg:u,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){clearTimeout(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,u=!1,a=null;for(r[n]={msg:s,ff:e=>{a&&(clearTimeout(a),a=null),e?.done&&(i(),u=!0),c(e)},data_type:l.data_type,sent_time:l.timer?Date.now():null};!u;)yield await new Promise(((e,t)=>{a=o(t),c=e}))}constructor(e={}){this.config=(e=>{if(null===R&&!("adapter"in e))throw new Error("\n This platform has no native WebSocket implementation.\n Please use 'ws' package as an adapter.\n See https://github.com/houd1ni/WebsocketPromisify/issues/23\n ");const t=Object.assign({},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()}};
package/dist/bundle.d.ts CHANGED
@@ -56,7 +56,6 @@ declare namespace wsc {
56
56
  ff(x: any): any;
57
57
  data_type: DataType;
58
58
  sent_time: number | null;
59
- timeout: NodeJS.Timeout;
60
59
  }
61
60
  }
62
61
  declare class WebSocketClient {
@@ -85,11 +84,13 @@ declare class WebSocketClient {
85
84
  off(event_name: wsc.WSEvent, handler: (data: any) => any, raw?: boolean): void;
86
85
  close(): wsc.AsyncErrCode;
87
86
  open(): Promise<number | null> | undefined;
87
+ private prepareMessage;
88
88
  /** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
89
89
  returns a Promise that will be rejected after a timeout or
90
90
  resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
91
91
  */
92
92
  send<RequestDataType = any, ResponseDataType = any>(message_data: RequestDataType, opts?: wsc.SendOptions): Promise<ResponseDataType>;
93
+ stream<RequestDataType = any, ResponseDataType = any>(message_data: RequestDataType, opts?: wsc.SendOptions): AsyncGenerator<ResponseDataType, void, unknown>;
93
94
  constructor(user_config?: wsc.UserConfig);
94
95
  }
95
96
 
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,u=e=>typeof e,a=e=>null===e,h={u:"U",b:"B",n:"N",s:"S",f:"F"},f=Symbol(),d=e=>{const t=u(e);return"object"===t?a(e)?"Null":e.constructor.name:h[t[0]]+t.slice(1)},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(a(e)||a(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"==u(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))),j=E(!0),A=E(!1),W=i(((e,t)=>w(((t,n)=>v((t=>e(n,t)),t)?t:y(n,t)),[],t)))(m),q=r(((e,t,n)=>g(t)?(e=>a(e)||(e=>e===l)(e))(n)?e:b((s=>s in n?q(e,k(1,c,t),n[s]):e),P)(t):n));q(l),b(_(m(f),A,j),q(f));const C=i(((e,t)=>t.map(e))),{floor:O}=Math,Q="0123456789abcdefghijklmnopqrstuvwxyz",$=b((e=>Object.fromEntries(e)),C(((e,t)=>[e,t])),N(""));class z{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=Q.startsWith(e),this.c2pos=$(e)}zip(e){const{abc:t,abclen:n}=this;let s="";for(;e>0;)s=t[e%n]+s,e=O(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||Q+"ABCDEFGHIJKLMNOPQRSTUVWXYZ")}}const R=new z;R.setABC.bind(R),R.zip.bind(R),R.unzip.bind(R);const T=(()=>{try{return WebSocket||null}catch{return null}})(),I=(e,t,n)=>e.addEventListener(t,n),x=(e,t)=>setTimeout(t,e),B={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:{}}},D=Symbol("Placeholder"),L=e=>{let t=0;for(const n of e)n!==D&&t++;return t},U=(e,t)=>{const n=e.length,s=e.slice(),o=t.length;let i=o,r=0;for(;i&&r<n;r++)s[r]===D&&(s[r]=t[o-i],i--);for(r=n;i;r++,i--)s[r]=t[o-i];return s},F=(e,t,n)=>{const s=e.length-t.length-L(n);if(s<1)return e(...U(t,n));{const o=(...s)=>F(e,U(t,n),s);return o.$args_left=s,o}},J=e=>(...t)=>e.length>L(t)?F(e,[],t):e(...t);function M(e){return function(t,n){const s=t===D,o=arguments.length;if(1===o&&s)throw new Error("Senseless placeholder usage.");return o>1?s?(e=>function(t){return t===D?e:e(t)})((t=>e(t,n))):e(t,n):n=>e(t,n)}}function G(e){return J(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=M(((e,t)=>se(t)===e)),ie=e=>e.length,re=M(((e,t)=>e===t)),le=M(((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=M(((e,t)=>(t.push(e),t))),ue=G(((e,t,n)=>n.reduce(e,t))),ae=M(((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=J(((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===D?e[o]():e[o](n);return n},de=M(((e,t)=>t[e])),ge=G(((e,t,n)=>n.slice(e,Z(t)?t:V))),pe=de(0);ge(1,V);const me=M(((e,t)=>t.find(e))),ye=e=>()=>e,we=ye(!0),_e=ye(!1),be=M(((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=M(((e,t)=>ue(((t,n)=>me((t=>e(n,t)),t)?t:ce(n,t)),[],t)));ke(le);const Pe=e=>{let t,n=!1;return(...s)=>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,je=new z,Ae=be([]),We=Ee(oe("Number"),Ne(isNaN)),qe={_is_ping:!0};class Ce{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(){ae(_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=x(1e3*e.interval,(async()=>{const{ping_timer:t,opened:n}=this;n?(await this.send(e.content,qe),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=x(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(),I(e,"close",(async(...e)=>{this.log("close"),this.ws=null,this.onCloseQueue.forEach(Ae),this.onCloseQueue.splice(0),this.call("close",...e);let{reconnect:t,reconnection_attempts:s}=n;if(We(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()}})),I(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(),this.resetIdle()}))}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)}));I(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)):I(n,"open",Pe((()=>{this.log("open"),this.initSocket(n),s(null)})))}))}get socket(){return this.ws}async ready(){return new Promise((e=>{this.opened?e():this.onReadyQueue.push(e)}))}on(e,t,n=we,s=!1){const o=e=>n(e)&&t(e);return s?I(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 send(e,t={}){this.log("send",e);const{config:n,queue:s}=this,o={},{pipes:i,server:{data_key:r}}=n,{top:l,_is_ping:c}=t,u=je.zip(2147483637*Math.random()|0);if("object"==typeof l){if(l[r])throw new Error("Attempting to set data key/token via send() options!");Object.assign(o,l)}for(const t of i)e=t(e);const[a,h]=await Promise.all([n.encode(u,e,n),this.connect()]);if(h)throw new Error("ERR while opening connection #"+h);return this.opened&&(this.ws.send(a),this.resetPing(),c||this.resetIdle()),new Promise(((t,o)=>{this.queue[u]={msg:a,ff(e){clearTimeout(this.timeout),delete s[u],t(e)},data_type:n.data_type,sent_time:n.timer?Date.now():null,timeout:x(n.timeout,(()=>{u in this.queue&&(this.call("timeout",e),o({"Websocket timeout expired":n.timeout,"for the message":e}),delete s[u])}))}}))}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({},B,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{Ce as default};
1
+ const e=Symbol("Placeholder"),t=t=>{let n=0;for(const s of t)s!==e&&n++;return n},n=(t,n)=>{const s=t.length,o=t.slice(),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 T{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 O=new T;O.setABC.bind(O),O.zip.bind(O),O.unzip.bind(O);const R=(()=>{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 T,We=be([]),$e=Ee(oe("Number"),Ne(isNaN)),je={_is_ping:!0},ze=e=>{const t=Ae.zip(2147483637*qe()|0);return t in e?ze(e):t};class Ce{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=ze(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){clearTimeout(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=>{u&&(clearTimeout(u),u=null),e?.done&&(i(),a=!0),c(e)},data_type:l.data_type,sent_time:l.timer?Date.now():null};!a;)yield await new Promise(((e,t)=>{u=o(t),c=e}))}constructor(e={}){this.config=(e=>{if(null===R&&!("adapter"in e))throw new Error("\n This platform has no native WebSocket implementation.\n Please use 'ws' package as an adapter.\n See https://github.com/houd1ni/WebsocketPromisify/issues/23\n ");const t=Object.assign({},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{Ce 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.8.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": "^28.0.6",
56
- "@rollup/plugin-node-resolve": "^16.0.1",
57
- "@rollup/plugin-replace": "^6.0.2",
58
- "@rollup/plugin-terser": "^0.4.4",
59
- "@types/express": "^5.0.3",
60
- "@types/node": "^24.1.0",
61
- "@types/ws": "^8.18.1",
62
- "codecov": "^3.8.3",
63
- "cross-env": "^10.0.0",
64
- "dts-bundle-generator": "^9.5.1",
65
- "nyc": "^17.1.0",
66
- "rollup": "^4.46.0",
67
- "rollup-plugin-typescript2": "^0.36.0",
68
- "ts-node": "^10.9.2",
69
- "tsx": "^4.20.3",
70
- "typescript": "^5.8.3",
71
- "uvu": "^0.5.6",
72
- "ws": "^8.18.3"
73
- },
74
- "types": "./dist/bundle.d.ts",
75
- "dependencies": {
76
- "pepka": "^1.6.5",
77
- "zipnum": "^2.0.0"
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.9.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.0",
56
+ "@rollup/plugin-node-resolve": "^16.0.3",
57
+ "@rollup/plugin-replace": "^6.0.3",
58
+ "@rollup/plugin-terser": "^0.4.4",
59
+ "@types/express": "^5.0.6",
60
+ "@types/node": "^25.0.3",
61
+ "@types/ws": "^8.18.1",
62
+ "codecov": "^3.8.3",
63
+ "cross-env": "^10.1.0",
64
+ "dts-bundle-generator": "^9.5.1",
65
+ "nyc": "^17.1.0",
66
+ "rollup": "^4.54.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.18.3"
73
+ },
74
+ "types": "./dist/bundle.d.ts",
75
+ "dependencies": {
76
+ "pepka": "^1.6.10",
77
+ "zipnum": "^2.0.0"
78
+ }
79
+ }
package/src/WSC.ts CHANGED
@@ -2,9 +2,10 @@ import './types'
2
2
  import { Zipnum } from 'zipnum'
3
3
  import { add_event, rm_event, sett } from './utils'
4
4
  import { processConfig } from './config'
5
- import { AnyFunc, both, callWith, F, isNil, notf, once, qfilter, T, typeIs } from 'pepka'
5
+ import { AnyFunc, AnyObject, both, callWith, F, isNil, notf, once, qfilter, T, typeIs } from 'pepka'
6
6
 
7
7
  const MAX_32 = 2**31 - 1
8
+ const { random } = Math
8
9
  const zipnum = new Zipnum()
9
10
  const callit = callWith([])
10
11
  const isNumber = both(typeIs('Number'), notf(isNaN))
@@ -18,6 +19,10 @@ type EventHandlers = {
18
19
  message: AnyFunc<any, [WebSocketEventMap['message'] & {data: any}]>[]
19
20
  timeout: AnyFunc<any, [data: any]>[]
20
21
  }
22
+ const genid = (q: AnyObject) => {
23
+ const id = zipnum.zip((random()*(MAX_32-10))|0)
24
+ return id in q ? genid(q) : id
25
+ }
21
26
 
22
27
  class WebSocketClient {
23
28
  private ws: wsc.Socket|null = null
@@ -130,7 +135,6 @@ class WebSocketClient {
130
135
  console.error(err, `WSP: Decode error. Got: ${e.data}`)
131
136
  }
132
137
  this.resetPing()
133
- this.resetIdle()
134
138
  })
135
139
  }
136
140
 
@@ -171,7 +175,8 @@ class WebSocketClient {
171
175
  public get socket() { return this.ws }
172
176
  public async ready() {
173
177
  return new Promise<void>((ff) => {
174
- if(this.opened) ff()
178
+ if(this.config.lazy) ff() // FIXME: (possibly) breaking change ?? At least minor ver bump with a notice!!!
179
+ else if(this.opened) ff()
175
180
  else this.onReadyQueue.push(ff)
176
181
  })
177
182
  }
@@ -221,30 +226,27 @@ class WebSocketClient {
221
226
  }
222
227
  }
223
228
 
224
- /** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
225
- returns a Promise that will be rejected after a timeout or
226
- resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
227
- */
228
- public async send<RequestDataType = any, ResponseDataType = any>(
229
+ // TODO: Сделать сэттер элементов конфигурации чтобы двигать таймауты.
230
+ // И эвент, когда схема наша, а соответствующего элемента очереди не ма.
231
+ // Или добавить флажок к эвенту 'message'.
232
+ // И событие 'line' со значением on: boolean. Критерии?
233
+ private async prepareMessage<RequestDataType = any>(
229
234
  message_data: RequestDataType,
230
235
  opts = <wsc.SendOptions>{}
231
- ): Promise<ResponseDataType> {
232
- this.log('send', message_data)
236
+ ) {
237
+ this.log(opts._is_ping ? 'ping' : 'send', message_data)
233
238
  const {config, queue} = this
234
- const message = {}
235
239
  const {pipes, server: {data_key}} = config
236
240
  const {top, _is_ping} = opts
237
-
238
- const message_id = zipnum.zip((Math.random()*(MAX_32-10))|0)
241
+ const id = genid(queue)
239
242
  if(typeof top === 'object') {
240
243
  if(top[data_key]) {
241
- throw new Error('Attempting to set data key/token via send() options!')
244
+ throw new Error(`Attempting to set data key/token via ${opts._is_ping ? 'ping' : 'send'}() options!`)
242
245
  }
243
- Object.assign(message, top)
244
246
  }
245
247
  for(const pipe of pipes) message_data = pipe(message_data)
246
248
  const [msg, err] = await Promise.all([
247
- config.encode(message_id, message_data, config),
249
+ config.encode(id, message_data, config),
248
250
  this.connect()
249
251
  ])
250
252
  if(err) throw new Error('ERR while opening connection #'+err)
@@ -253,24 +255,61 @@ class WebSocketClient {
253
255
  this.resetPing()
254
256
  if(!_is_ping) this.resetIdle()
255
257
  }
258
+ const cleanup = () => delete this.queue[id]
259
+ const timeout = (rj: AnyFunc) => sett(config.timeout, () => {
260
+ if(id in queue) {
261
+ this.call('timeout', message_data)
262
+ rj({'Websocket timeout expired': config.timeout, 'for the message': message_data})
263
+ cleanup()
264
+ }
265
+ })
266
+ return { message_id: id, msg, timeout, cleanup }
267
+ }
256
268
 
257
- return new Promise((ff, rj) => {
258
- this.queue[message_id] = {
259
- msg, ff(x: any) {
260
- clearTimeout(this.timeout) // from this object!
261
- delete queue[message_id]
262
- ff(x)
263
- },
269
+ /** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
270
+ returns a Promise that will be rejected after a timeout or
271
+ resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
272
+ */
273
+ public async send<RequestDataType = any, ResponseDataType = any>(
274
+ message_data: RequestDataType,
275
+ opts = <wsc.SendOptions>{}
276
+ ): Promise<ResponseDataType> {
277
+ const {message_id, msg, timeout, cleanup} = await this.prepareMessage(message_data, opts)
278
+ const {queue, config} = this
279
+
280
+ return new Promise<ResponseDataType>((ff, rj) => {
281
+ const to = timeout(rj)
282
+ queue[message_id] = {
283
+ msg,
264
284
  data_type: config.data_type,
265
285
  sent_time: config.timer ? Date.now() : null,
266
- timeout: sett(config.timeout, () => {
267
- if(message_id in this.queue) {
268
- this.call('timeout', message_data)
269
- rj({'Websocket timeout expired': config.timeout, 'for the message': message_data})
270
- delete queue[message_id]
271
- }
272
- })
286
+ ff(x: any) {
287
+ clearTimeout(to)
288
+ ff(x)
289
+ }
273
290
  }
291
+ }).finally(cleanup)
292
+ }
293
+
294
+ public async *stream<RequestDataType = any, ResponseDataType = any>(
295
+ message_data: RequestDataType,
296
+ opts = <wsc.SendOptions>{}
297
+ ): AsyncGenerator<ResponseDataType, void, unknown> {
298
+ const {message_id, msg, timeout, cleanup} = await this.prepareMessage(message_data, opts)
299
+ const {queue, config} = this
300
+ let done = false, fulfill: AnyFunc, to: NodeJS.Timeout|null = null
301
+ queue[message_id] = {
302
+ msg,
303
+ ff: (msg: ResponseDataType&{done?: boolean}) => {
304
+ if(to) {clearTimeout(to); to=null}
305
+ if(msg?.done) { cleanup(); done=true }
306
+ fulfill(msg)
307
+ },
308
+ data_type: config.data_type,
309
+ sent_time: config.timer ? Date.now() : null
310
+ }
311
+ while(!done) yield await new Promise<ResponseDataType>((ff, rj) => {
312
+ to = timeout(rj), fulfill=ff
274
313
  })
275
314
  }
276
315
 
package/src/types.ts CHANGED
@@ -16,7 +16,7 @@ declare namespace wsc {
16
16
  }
17
17
 
18
18
  export type AsyncErrCode = Promise<number | null | {}>
19
-
19
+
20
20
  export type EventHandler = (e: any) => void
21
21
 
22
22
  export type DataPipe = (message: any) => any
@@ -63,7 +63,6 @@ declare namespace wsc {
63
63
  export interface Message {
64
64
  msg: any, ff(x: any): any,
65
65
  data_type: DataType,
66
- sent_time: number | null,
67
- timeout: NodeJS.Timeout
66
+ sent_time: number | null
68
67
  }
69
68
  }
package/test/index.ts CHANGED
@@ -1,21 +1,24 @@
1
- import './specs/close'
2
- import './specs/drops'
3
- import './specs/echo'
4
- import './specs/existing-socket'
5
- import './specs/lazy'
6
- import './specs/ready'
7
- import './specs/reconnect'
8
- import './specs/lazy-send-before-open'
9
- import './specs/socket'
10
- import './specs/no-native-throws'
11
- import mockServer from './mock/server'
12
- import {test} from './suite'
13
-
14
- const {shutDown, isRunning} = await mockServer()
15
- test.after(() => {
16
- setTimeout(async () => {
17
- if(isRunning()) await shutDown()
18
- process.exit()
19
- }, 100)
20
- })
1
+ import './specs/close'
2
+ import './specs/drops'
3
+ import './specs/echo'
4
+ import './specs/existing-socket'
5
+ import './specs/lazy'
6
+ import './specs/ready'
7
+ import './specs/reconnect'
8
+ import './specs/lazy-send-before-open'
9
+ import './specs/socket'
10
+ import './specs/no-native-throws'
11
+ import './specs/stream-simple'
12
+ import './specs/stream-comprehensive'
13
+ import './specs/stream-real'
14
+ import mockServer from './mock/server'
15
+ import {test} from './suite'
16
+
17
+ const {shutDown, isRunning} = await mockServer()
18
+ test.after(() => {
19
+ setTimeout(async () => {
20
+ if(isRunning()) await shutDown()
21
+ process.exit()
22
+ }, 100)
23
+ })
21
24
  test.run()
package/test/mock/WS.ts CHANGED
@@ -19,6 +19,37 @@ const createServer = (port = 40510) => new Promise<WebSocketServer>((ff, rj) =>
19
19
  return null
20
20
  } else if(data.echo) {
21
21
  response = data
22
+ } else if(data.stream) {
23
+ // Handle streaming responses
24
+ const chunks = data.chunks || [1, 2, 3] // Default to 3 chunks
25
+ const delay = data.delay || 100 // Default delay between chunks
26
+
27
+ if(data.multi) {
28
+ // Multi-chunk streaming
29
+ chunks.forEach((chunk: any, index: number) => {
30
+ setTimeout(() => {
31
+ socket.send(JSON.stringify({
32
+ id,
33
+ data: {
34
+ ...data,
35
+ chunk: chunk,
36
+ done: index === chunks.length - 1 // Last chunk gets done: true
37
+ }
38
+ }))
39
+ }, index * delay)
40
+ })
41
+ } else {
42
+ // Single response
43
+ socket.send(JSON.stringify({
44
+ id,
45
+ data: {
46
+ ...data,
47
+ chunk: chunks[0],
48
+ done: true
49
+ }
50
+ }))
51
+ }
52
+ return null
22
53
  }
23
54
  socket.send(JSON.stringify({ id, data: response }))
24
55
  return null
@@ -25,12 +25,10 @@ test('existing_socket', () => new Promise(async (ff, rj) => {
25
25
  const ws2_0 = new WS(existing_addr)
26
26
 
27
27
  ws2_0.addEventListener('open', async () => {
28
- console.log('START')
29
28
  const ws2 = await createNew({socket: ws2_0}, port)
30
- console.log({ws1})
31
29
 
32
30
  if(ws2.socket?.readyState !== 1) return rj('Bad echo.')
33
-
31
+
34
32
  const msg2 = {echo: true, msg: 'existing_socket!'}
35
33
  const response2 = await ws2.send(msg2)
36
34
 
@@ -0,0 +1,80 @@
1
+ import { equals } from 'pepka'
2
+ import { createNew, timeout } from '../utils'
3
+ import mockServer from '../mock/server'
4
+ import { test } from '../suite'
5
+
6
+ /** Comprehensive test for stream method. */
7
+ test('stream-comprehensive', timeout(1e4, () => new Promise<void>(async (ff, rj) => {
8
+
9
+ const {port} = await mockServer()
10
+ let to = setTimeout(() => rj('cannot create'), 2e2)
11
+ const ws = await createNew({}, port)
12
+ clearTimeout(to)
13
+
14
+ to = setTimeout(() => rj('cannot ready'), 2e2)
15
+ await ws.ready()
16
+ clearTimeout(to)
17
+
18
+ // Test 1: Basic stream functionality
19
+ const msg1 = {stream: true, test: 'stream1', chunks: [1], delay: 10}
20
+ to = setTimeout(() => rj('stream1 timeout'), 2e2)
21
+
22
+ try {
23
+ const stream1 = ws.stream(msg1)
24
+ const result1 = await stream1.next()
25
+
26
+ // Check that we got a valid response with the expected test property
27
+ if (result1.done || !result1.value?.test || result1.value.test !== msg1.test) {
28
+ clearTimeout(to)
29
+ return rj('stream1 failed')
30
+ }
31
+
32
+ clearTimeout(to)
33
+
34
+ // Test 2: Stream with for-await loop
35
+ const msg2 = {stream: true, test: 'stream2'}
36
+ to = setTimeout(() => rj('stream2 timeout'), 2e2)
37
+
38
+ const stream2 = ws.stream<typeof msg2, any>(msg2)
39
+ const results: any[] = []
40
+
41
+ for await (const chunk of stream2) {
42
+ results.push(chunk)
43
+ break // We expect only one chunk
44
+ }
45
+
46
+ clearTimeout(to)
47
+
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
+ // Test 3: Multiple concurrent streams
54
+ const msg3a = {stream: true, test: 'stream3a'}
55
+ const msg3b = {stream: true, test: 'stream3b'}
56
+ to = setTimeout(() => rj('stream3 timeout'), 2e2)
57
+
58
+ const stream3a = ws.stream(msg3a)
59
+ const stream3b = ws.stream(msg3b)
60
+
61
+ const result3a = await stream3a.next()
62
+ const result3b = await stream3b.next()
63
+
64
+ clearTimeout(to)
65
+
66
+ // For streaming messages, check that we got valid chunks with the expected properties
67
+ if (result3a.done || result3b.done ||
68
+ !result3a.value?.test || result3a.value.test !== msg3a.test ||
69
+ !result3b.value?.test || result3b.value.test !== msg3b.test) {
70
+ return rj('stream3 failed')
71
+ }
72
+
73
+ ff()
74
+ } catch (error) {
75
+ clearTimeout(to)
76
+ console.log('STREAM-COMPREHENSIVE TEST FAILED:', error)
77
+ rj('stream comprehensive error: ' + error)
78
+ }
79
+ })
80
+ ))
@@ -0,0 +1,58 @@
1
+ import { equals } from 'pepka'
2
+ import { createNew, timeout } from '../utils'
3
+ import mockServer from '../mock/server'
4
+ import { test } from '../suite'
5
+
6
+ /** Test real streaming functionality with multiple chunks. */
7
+ test('stream-real', timeout(1.5e4, () => new Promise<void>(async (ff, rj) => {
8
+
9
+ const {port} = await mockServer()
10
+ let to = setTimeout(() => {
11
+ console.log('STREAM-REAL: Timeout - cannot create')
12
+ rj('cannot create')
13
+ }, 2e2)
14
+ const ws = await createNew({}, port)
15
+ clearTimeout(to)
16
+
17
+ to = setTimeout(() => rj('cannot ready'), 2e2)
18
+ await ws.ready()
19
+ clearTimeout(to)
20
+
21
+ // Test real streaming with multiple chunks
22
+ const streamMsg = {stream: true, multi: true, test: 'real-stream', chunks: [1, 2, 3], delay: 50}
23
+ to = setTimeout(() => rj('stream timeout'), 5e3)
24
+
25
+ try {
26
+ const stream = ws.stream<typeof streamMsg, any>(streamMsg)
27
+ const chunks: any[] = []
28
+
29
+ for await (const chunk of stream) {
30
+ chunks.push(chunk)
31
+ }
32
+
33
+ clearTimeout(to)
34
+
35
+ // Verify we got all chunks
36
+ if (chunks.length !== 3) {
37
+ return rj(`Expected 3 chunks, got ${chunks.length}`)
38
+ }
39
+
40
+ // Verify chunks are in order and have correct data
41
+ for (let i = 0; i < 3; i++) {
42
+ if (chunks[i].chunk !== i + 1) {
43
+ return rj(`Chunk ${i} should be ${i + 1}, got ${chunks[i].chunk}`)
44
+ }
45
+ }
46
+
47
+ // Verify last chunk has done flag
48
+ if (!chunks[2].done) {
49
+ return rj('Last chunk should have done flag')
50
+ }
51
+ ff()
52
+ } catch (error) {
53
+ clearTimeout(to)
54
+ console.log('STREAM-REAL TEST FAILED:', error)
55
+ rj('stream real error: ' + error)
56
+ }
57
+ })
58
+ ))
@@ -0,0 +1,46 @@
1
+ import { equals } from 'pepka'
2
+ import { createNew, timeout } from '../utils'
3
+ import mockServer from '../mock/server'
4
+ import { test } from '../suite'
5
+
6
+ /** Simple test for stream method basic functionality. */
7
+ test('stream-basic', timeout(5e3, () => new Promise<void>(async (ff, rj) => {
8
+ const {port} = await mockServer()
9
+ let to = setTimeout(() => rj('cannot create'), 2e2)
10
+ const ws = await createNew({}, port)
11
+ clearTimeout(to)
12
+
13
+ to = setTimeout(() => rj('cannot ready'), 2e2)
14
+ await ws.ready()
15
+ clearTimeout(to)
16
+
17
+ const msg = {stream: true, test: 'stream'}
18
+ to = setTimeout(() => rj('stream timeout'), 2e2)
19
+
20
+ try {
21
+ const stream = ws.stream(msg)
22
+
23
+ // Test that stream is an AsyncGenerator
24
+ if (typeof stream[Symbol.asyncIterator] !== 'function') {
25
+ clearTimeout(to)
26
+ return rj('stream is not an AsyncGenerator')
27
+ }
28
+
29
+ // Test that we can iterate over it
30
+ const iterator = stream[Symbol.asyncIterator]()
31
+ const firstResult = await iterator.next()
32
+
33
+ clearTimeout(to)
34
+
35
+ // For streaming messages, check that we got a valid chunk with the expected properties
36
+ if (!firstResult.done && firstResult.value && firstResult.value.test === msg.test) {
37
+ ff()
38
+ } else {
39
+ rj(`stream did not return expected value. Got: ${JSON.stringify(firstResult.value)}, Expected test: ${msg.test}`)
40
+ }
41
+ } catch (error) {
42
+ clearTimeout(to)
43
+ rj('stream error: ' + error)
44
+ }
45
+ })
46
+ ))
package/test/utils.ts CHANGED
@@ -12,7 +12,7 @@ export const createNew = (config = {} as wsc.UserConfig, port: number) => new Pr
12
12
  }, config))
13
13
  if(ws.socket?.readyState===1 || config.lazy) return ff(ws)
14
14
  ws.on('error', rj)
15
- ws.on('open', () => {console.log('OPEN!'); ff(ws)})
15
+ ws.on('open', () => {ff(ws)})
16
16
  })
17
17
 
18
18
  // Inspired by tinchoz49 https://github.com/lukeed/uvu/issues/33#issuecomment-879870292