wspromisify 2.8.3 → 2.9.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 @@
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}})(),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:{}}},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=B(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=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(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()}})),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.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 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:B(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({},I,e),n=t.url;if("/"==n[0])try{const e=location.protocol.includes("s:")?"wss":"ws";t.url=`${e}://${location.hostname}:${location.port}${n}`}catch(e){throw new Error("WSP: URL starting with / in non-browser environment!")}return t})(e),this.config.lazy||this.connect()}};
1
+ "use strict";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}})(),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:{}}},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=B(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=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(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()}})),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.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 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:B(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({},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};
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.3",
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.1",
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,13 +2,15 @@ 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))
11
12
  const ping_send_opts: wsc.SendOptions = {_is_ping: true}
13
+ const clearTO = (to: NodeJS.Timeout|null) => to && clearTimeout(to)
12
14
 
13
15
  type EventHandler<T extends keyof WebSocketEventMap> = AnyFunc<any, [WebSocketEventMap[T]]>
14
16
  type EventHandlers = {
@@ -18,6 +20,10 @@ type EventHandlers = {
18
20
  message: AnyFunc<any, [WebSocketEventMap['message'] & {data: any}]>[]
19
21
  timeout: AnyFunc<any, [data: any]>[]
20
22
  }
23
+ const genid = (q: AnyObject) => {
24
+ const id = zipnum.zip((random()*(MAX_32-10))|0)
25
+ return id in q ? genid(q) : id
26
+ }
21
27
 
22
28
  class WebSocketClient {
23
29
  private ws: wsc.Socket|null = null
@@ -170,7 +176,8 @@ class WebSocketClient {
170
176
  public get socket() { return this.ws }
171
177
  public async ready() {
172
178
  return new Promise<void>((ff) => {
173
- if(this.opened) ff()
179
+ if(this.config.lazy) ff() // FIXME: (possibly) breaking change ?? At least minor ver bump with a notice!!!
180
+ else if(this.opened) ff()
174
181
  else this.onReadyQueue.push(ff)
175
182
  })
176
183
  }
@@ -220,30 +227,27 @@ class WebSocketClient {
220
227
  }
221
228
  }
222
229
 
223
- /** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
224
- returns a Promise that will be rejected after a timeout or
225
- resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
226
- */
227
- public async send<RequestDataType = any, ResponseDataType = any>(
230
+ // TODO: Сделать сэттер элементов конфигурации чтобы двигать таймауты.
231
+ // И эвент, когда схема наша, а соответствующего элемента очереди не ма.
232
+ // Или добавить флажок к эвенту 'message'.
233
+ // И событие 'line' со значением on: boolean. Критерии?
234
+ private async prepareMessage<RequestDataType = any>(
228
235
  message_data: RequestDataType,
229
236
  opts = <wsc.SendOptions>{}
230
- ): Promise<ResponseDataType> {
231
- this.log('send', message_data)
237
+ ) {
238
+ this.log(opts._is_ping ? 'ping' : 'send', message_data)
232
239
  const {config, queue} = this
233
- const message = {}
234
240
  const {pipes, server: {data_key}} = config
235
241
  const {top, _is_ping} = opts
236
-
237
- const message_id = zipnum.zip((Math.random()*(MAX_32-10))|0)
242
+ const id = genid(queue)
238
243
  if(typeof top === 'object') {
239
244
  if(top[data_key]) {
240
- throw new Error('Attempting to set data key/token via send() options!')
245
+ throw new Error(`Attempting to set data key/token via ${opts._is_ping ? 'ping' : 'send'}() options!`)
241
246
  }
242
- Object.assign(message, top)
243
247
  }
244
248
  for(const pipe of pipes) message_data = pipe(message_data)
245
249
  const [msg, err] = await Promise.all([
246
- config.encode(message_id, message_data, config),
250
+ config.encode(id, message_data, config),
247
251
  this.connect()
248
252
  ])
249
253
  if(err) throw new Error('ERR while opening connection #'+err)
@@ -252,25 +256,61 @@ class WebSocketClient {
252
256
  this.resetPing()
253
257
  if(!_is_ping) this.resetIdle()
254
258
  }
259
+ const cleanup = () => delete this.queue[id]
260
+ const timeout = (rj: AnyFunc) => sett(config.timeout, () => {
261
+ if(id in queue) {
262
+ this.call('timeout', message_data)
263
+ rj({'Websocket timeout expired': config.timeout, 'for the message': message_data})
264
+ cleanup()
265
+ }
266
+ })
267
+ return { message_id: id, msg, timeout, cleanup }
268
+ }
255
269
 
256
- return new Promise((ff, rj) => {
257
- this.queue[message_id] = {
258
- msg, ff(x: any) {
259
- clearTimeout(this.timeout) // from this object!
260
- delete queue[message_id]
261
- ff(x)
262
- },
270
+ /** .send(your_data) wraps request to server with {id: `hash`, data: `actually your data`},
271
+ returns a Promise that will be rejected after a timeout or
272
+ resolved if server returns the same signature: {id: `same_hash`, data: `response data`}.
273
+ */
274
+ public async send<RequestDataType = any, ResponseDataType = any>(
275
+ message_data: RequestDataType,
276
+ opts = <wsc.SendOptions>{}
277
+ ): Promise<ResponseDataType> {
278
+ const {message_id, msg, timeout, cleanup} = await this.prepareMessage(message_data, opts)
279
+ const {queue, config} = this
280
+
281
+ return new Promise<ResponseDataType>((ff, rj) => {
282
+ const to = timeout(rj)
283
+ queue[message_id] = {
284
+ msg,
263
285
  data_type: config.data_type,
264
286
  sent_time: config.timer ? Date.now() : null,
265
- timeout: sett(config.timeout, () => {
266
- if(message_id in this.queue) {
267
- this.call('timeout', message_data)
268
- rj({'Websocket timeout expired': config.timeout, 'for the message': message_data})
269
- delete queue[message_id]
270
- }
271
- })
287
+ ff(x: any) {
288
+ clearTO(to)
289
+ ff(x)
290
+ }
272
291
  }
273
- })
292
+ }).finally(cleanup)
293
+ }
294
+
295
+ public async *stream<RequestDataType = any, ResponseDataType = any>(
296
+ message_data: RequestDataType,
297
+ opts = <wsc.SendOptions>{}
298
+ ): AsyncGenerator<ResponseDataType, void, unknown> {
299
+ const {message_id, msg, timeout, cleanup} = await this.prepareMessage(message_data, opts)
300
+ const {queue, config} = this
301
+ let done = false, fulfill: AnyFunc, to: NodeJS.Timeout|null = null
302
+ queue[message_id] = {
303
+ msg,
304
+ ff: (msg: ResponseDataType&{done?: boolean}) => {
305
+ fulfill(msg)
306
+ if(msg?.done) { cleanup(); done=true }
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
+ clearTO(to), to=timeout(rj), fulfill=ff
313
+ }).finally(() => {clearTO(to), to=null})
274
314
  }
275
315
 
276
316
  // TODO: Add .on handlers to config!
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,56 @@
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) chunks.push(chunk)
30
+
31
+ clearTimeout(to)
32
+
33
+ // Verify we got all chunks
34
+ if (chunks.length !== 3) {
35
+ return rj(`Expected 3 chunks, got ${chunks.length}`)
36
+ }
37
+
38
+ // Verify chunks are in order and have correct data
39
+ for (let i = 0; i < 3; i++) {
40
+ if (chunks[i].chunk !== i + 1) {
41
+ return rj(`Chunk ${i} should be ${i + 1}, got ${chunks[i].chunk}`)
42
+ }
43
+ }
44
+
45
+ // Verify last chunk has done flag
46
+ if (!chunks[2].done) {
47
+ return rj('Last chunk should have done flag')
48
+ }
49
+ ff()
50
+ } catch (error) {
51
+ clearTimeout(to)
52
+ console.log('STREAM-REAL TEST FAILED:', error)
53
+ rj('stream real error: ' + error)
54
+ }
55
+ })
56
+ ))
@@ -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