shared-reducer 5.0.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -56,7 +56,7 @@ type TypedEvent<Type extends string, T extends Event> = T & {
56
56
 
57
57
  interface ConnectionInfo {
58
58
  url: string;
59
- token?: string;
59
+ token?: string | undefined;
60
60
  }
61
61
  type ConnectionGetter = (signal: AbortSignal) => MaybePromise<ConnectionInfo>;
62
62
  interface DisconnectDetail {
@@ -100,6 +100,7 @@ declare class SharedReducer<T, SpecT> extends TypedEventTarget<SharedReducerEven
100
100
  getState(): Readonly<T> | undefined;
101
101
  private _warn;
102
102
  private readonly _handleConnected;
103
+ private readonly _handleConnectionFailure;
103
104
  private readonly _handleDisconnected;
104
105
  close(): void;
105
106
  }
package/frontend/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";class t{t;i;h=null;o=null;l=null;u=0;constructor(t,s){this.t=t,this.i=s,this._=this._.bind(this)}trigger(t){this.stop(),this.l=t,this._()}schedule(t){this.l!==t&&(this.o&&this.stop(),this.l=t,null===this.h&&("hidden"===global.document?.visibilityState?global.addEventListener?.("visibilitychange",this._):(global.addEventListener?.("online",this._),this.h=setTimeout(this._,this.t(this.u))),global.addEventListener?.("pageshow",this._),global.addEventListener?.("focus",this._),++this.u))}stop(){this.l=null,this.o?.(),this.o=null,this.m()}async _(){if(this.o||!this.l)return;this.m();const t=new AbortController;let s=()=>null;this.o=()=>{s(),t.abort()};try{await Promise.race([this.l(t.signal),new Promise(((t,e)=>{const i=setTimeout(e,this.i);s=()=>clearTimeout(i)}))]),this.l=null,this.u=0}catch(s){if(!t.signal.aborted){t.abort();const s=this.l;s&&(this.l=null,this.schedule(s))}}finally{s(),this.o=null}}m(){null!==this.h&&(clearTimeout(this.h),this.h=null,global.removeEventListener?.("online",this._),global.removeEventListener?.("pageshow",this._),global.removeEventListener?.("visibilitychange",this._),global.removeEventListener?.("focus",this._))}}const s=({base:t=2,initialDelay:s,maxDelay:e,randomness:i=0})=>n=>Math.min(Math.pow(t,n)*s,e)*(1-Math.random()*i);function e(t,s,e){const i=[],n=[];function h(){if(n.length>0){const e=t.combine(n);i.push(e),s=t.update(s,e),n.length=0}}return function(t,s){let e={p:t,v:0,C:null};for(;e;)if(e.v>=e.p.length)e=e.C;else{const t=s(e.p[e.v]);++e.v,t&&t.length&&(e={p:t,v:0,C:e})}}(e,(t=>{if("function"==typeof t){h();return t(s)}return t&&n.push(t),null})),h(),{S:s,T:t.combine(i)}}class i extends EventTarget{addEventListener(t,s,e){super.addEventListener(t,s,e)}removeEventListener(t,s,e){super.removeEventListener(t,s,e)}dispatchEvent(t){return super.dispatchEvent(t)}}const n=(t,s)=>new CustomEvent(t,{detail:s});class h extends i{k;I;P=null;L=!1;constructor(t,s){super(),this.k=t,this.I=s,this.$=this.$.bind(this),this.I.trigger(this.$)}async $(t){const{url:s,token:e}=await this.k(t);t.throwIfAborted();const i=new AbortController,h=i.signal;await new Promise(((u,d)=>{const _=new WebSocket(s);let g=!0;const f=t=>{i.abort(),_.close(),g?(g=!1,d(new Error(`handshake failed: ${t.code} ${t.reason}`))):(this.P=null,this.dispatchEvent(n("disconnected",t)),this.L||this.I.schedule(this.$))};e&&_.addEventListener("open",(()=>_.send(e)),{once:!0,signal:h}),_.addEventListener("message",(t=>{t.data!==c&&(g&&(g=!1,this.P=_,this.dispatchEvent(n("connected")),u()),this.dispatchEvent(n("message",t.data)))}),{signal:h}),_.addEventListener("close",f,{signal:h}),_.addEventListener("error",(()=>f(o)),{signal:h}),t.addEventListener("abort",(()=>f(r)),{signal:h}),function(t){const s=new AbortController;let e=null;const i=()=>{null!==e&&(clearTimeout(e),e=null),t.send(l)},n=()=>{null!==e&&clearTimeout(e),e=setTimeout(i,a)},h=()=>{null!==e&&(clearTimeout(e),e=null),s.abort()};t.addEventListener("open",n,{once:!0,signal:s.signal}),t.addEventListener("message",n,{signal:s.signal}),t.addEventListener("close",h,{signal:s.signal}),t.addEventListener("error",h,{signal:s.signal}),global.addEventListener?.("offline",i,{signal:s.signal})}(_)})).catch((t=>{throw i.abort(),t}))}isConnected(){return null!==this.P}send=t=>{if(!this.P)throw new Error("connection lost");this.P.send(t)};close(){this.L=!0,this.I.stop(),this.P?.close()}}const o={code:0,reason:"client side error"},r={code:0,reason:"handshake timeout"},l="P",c="p",a=2e4,u=()=>!0;class d{M;A;O=[];j=function(){let t=1;return()=>t++}();constructor(t,s){this.M=t,this.A=s}D(t){this.O.push({J:void 0,N:t,F:[],G:[]})}q(t,s,e){if(s||e)if(this.O.length){const t=this.O[this.O.length-1];t.F.push(s??_),t.G.push(e??_)}else s&&Promise.resolve(t).then(s)}U(t){let s=0;for(let e=0;e<this.O.length;++e){const i=this.O[e];this.A(t,i.N,void 0!==i.J)?(i.J=void 0,this.O[e-s]=i):(i.G.forEach((t=>t("message possibly lost"))),++s)}this.O.length-=s}W(t){for(let t=0,s=0;t<=this.O.length;++t){const e=this.O[t];if(!e||void 0!==e.J||e.F.length>0||e.G.length>0){const e=t-s;if(e>1){const i=this.O[t-1],n=this.O.splice(s,e,i);i.N=this.M.combine(n.map((t=>t.N))),t-=e-1}s=t+1}}for(const s of this.O)void 0===s.J&&(s.J=this.j(),t(JSON.stringify({change:s.N,id:s.J})))}X(t){const s=void 0===t?-1:this.O.findIndex((s=>s.J===t));return-1===s?{B:null,H:!1}:{B:this.O.splice(s,1)[0],H:0===s}}K(t){if(!this.O.length)return t;const s=this.M.combine(this.O.map((({N:t})=>t)));return this.M.update(t,s)}}const _=()=>null;const g={code:0,reason:"graceful shutdown"},f=s({base:2,initialDelay:200,maxDelay:6e5,randomness:.3});exports.AT_LEAST_ONCE=u,exports.AT_MOST_ONCE=(t,s,e)=>!e,exports.OnlineScheduler=t,exports.SharedReducer=class extends i{M;P;R=!0;S={V:0,Y:[]};Z;tt=new Set;st=function(t){let s=!1;return e=>{if(s)throw new Error(t);try{return s=!0,e()}finally{s=!1}}}("Cannot dispatch recursively");constructor(s,e,{scheduler:i=new t(f,2e4),deliveryStrategy:n=u}={}){super(),this.M=s,this.Z=new d(s,n),this.P=new h(e,i),this.P.addEventListener("message",this.et),this.P.addEventListener("connected",this.it),this.P.addEventListener("disconnected",this.nt)}dispatch=function(t){return Object.assign(t,{sync:(s=[])=>new Promise(((e,i)=>t(s,e,(t=>i(new Error(t))))))})}(((t,s,e)=>{if(!t.length&&!s&&!e)return;const i={ht:t,F:s,G:e};switch(this.S.V){case-1:throw new Error("closed");case 0:this.S.Y.push(i);break;case 1:this.ot(this.rt(this.S.lt,[i])),this.ut.ct()}}));rt(t,s){return this.st((()=>{for(const{ht:i,F:n,G:h}of s){if(i.length){const{S:s,T:n}=e(this.M,t,i);t=s,this.Z.D(n)}this.Z.q(t,n,h)}return t}))}ut=function(t){let s=null;const e=()=>{null!==s&&(clearTimeout(s),s=null)},i=()=>{e(),t()};return{dt:i,ct:()=>{null===s&&(s=setTimeout(i,0))},o:e}}((()=>{this.P.isConnected()&&!this.R&&this.Z.W(this.P.send)}));_t(t){if(-1!==this.S.V){if(this.R=!1,0===this.S.V){const s=this.rt(t.init,this.S.Y);this.S={V:1,gt:t.init,lt:s},this.ot(s,!0)}else this.S.gt=t.init,this.Z.U(t.init),this.ot(this.Z.K(t.init));this.ut.dt()}else this.ft(`Ignoring init after closing: ${JSON.stringify(t)}`)}wt(t){if(1!==this.S.V)return void this.ft(`Ignoring change before init: ${JSON.stringify(t)}`);const s=this.S.gt=this.M.update(this.S.gt,t.change),{B:e,H:i}=this.Z.X(t.id);i||this.ot(this.Z.K(s)),e?.F.forEach((t=>t(s)))}vt(t){if(1!==this.S.V)return void this.ft(`Ignoring error before init: ${JSON.stringify(t)}`);const{B:s}=this.Z.X(t.id);s?(this.ft(`API rejected update: ${t.error}`),s?.G.forEach((s=>s(t.error))),this.ot(this.Z.K(this.S.gt))):this.ft(`API sent error: ${t.error}`)}bt(){this.P.send("x"),this.R?this.ft("Unexpected extra close message"):(this.R=!0,this.dispatchEvent(n("disconnected",g)))}et=t=>{if("X"===t.detail)return void this.bt();const s=JSON.parse(t.detail);"change"in s?this.wt(s):"init"in s?this._t(s):"error"in s?this.vt(s):this.ft(`Ignoring unknown API message: ${t.detail}`)};addStateListener(t){this.tt.add(t),1===this.S.V&&t(this.S.lt)}removeStateListener(t){this.tt.delete(t)}ot(t,s=!1){if(1!==this.S.V)throw new Error("invalid state");if(s||this.S.lt!==t){this.S.lt=t;for(const s of this.tt)s(t)}}getState(){return 1===this.S.V?this.S.lt:void 0}ft(t){this.dispatchEvent(n("warning",new Error(t)))}it=()=>{this.dispatchEvent(n("connected"))};nt=t=>{this.R||(this.R=!0,this.dispatchEvent(n("disconnected",t.detail)))};close(){this.R=!0,this.S={V:-1},this.P.close(),this.ut.o(),this.tt.clear(),this.P.removeEventListener("message",this.et),this.P.removeEventListener("connected",this.it),this.P.removeEventListener("disconnected",this.nt)}},exports.exponentialDelay=s;
1
+ "use strict";class t{t;i;h=null;o=null;l=null;u=0;constructor(t,s){this.t=t,this.i=s,this._=this._.bind(this)}trigger(t){this.stop(),this.l=t,this._()}schedule(t){this.l!==t&&(this.o&&this.stop(),this.l=t,null===this.h&&("hidden"===global.document?.visibilityState?global.addEventListener?.("visibilitychange",this._):(global.addEventListener?.("online",this._),this.h=setTimeout(this._,this.t(this.u))),global.addEventListener?.("pageshow",this._),global.addEventListener?.("focus",this._),++this.u))}stop(){this.l=null,this.o?.(),this.o=null,this.m()}async _(){if(this.o||!this.l)return;this.m();const t=new AbortController;let s=()=>null;this.o=()=>{s(),t.abort()};try{await Promise.race([this.l(t.signal),new Promise(((t,e)=>{const i=setTimeout(e,this.i);s=()=>clearTimeout(i)}))]),this.l=null,this.u=0}catch(s){if(!t.signal.aborted){t.abort();const s=this.l;s&&(this.l=null,this.schedule(s))}}finally{s(),this.o=null}}m(){null!==this.h&&(clearTimeout(this.h),this.h=null,global.removeEventListener?.("online",this._),global.removeEventListener?.("pageshow",this._),global.removeEventListener?.("visibilitychange",this._),global.removeEventListener?.("focus",this._))}}const s=({base:t=2,initialDelay:s,maxDelay:e,randomness:i=0})=>n=>Math.min(Math.pow(t,n)*s,e)*(1-Math.random()*i);function e(t,s,e){const i=[],n=[];function h(){if(n.length>0){const e=t.combine(n);i.push(e),s=t.update(s,e),n.length=0}}return function(t,s){let e={p:t,v:0,C:null};for(;e;)if(e.v>=e.p.length)e=e.C;else{const t=s(e.p[e.v]);++e.v,t&&t.length&&(e={p:t,v:0,C:e})}}(e,(t=>{if("function"==typeof t){h();return t(s)}return t&&n.push(t),null})),h(),{S:s,T:t.combine(i)}}class i extends EventTarget{addEventListener(t,s,e){super.addEventListener(t,s,e)}removeEventListener(t,s,e){super.removeEventListener(t,s,e)}dispatchEvent(t){return super.dispatchEvent(t)}}const n=(t,s)=>new CustomEvent(t,{detail:s});class h extends i{$;k;I=null;P=!1;constructor(t,s){super(),this.$=t,this.k=s,this.L=this.L.bind(this),this.k.trigger(this.L)}async L(t){const{url:s,token:e}=await this.$(t);t.throwIfAborted();const i=new AbortController,h=i.signal;await new Promise(((u,d)=>{const _=new WebSocket(s);let g=!0;const f=t=>{i.abort(),_.close(),g?(g=!1,this.dispatchEvent(n("connectionfailure",t)),d(new Error(`handshake failed: ${t.code} ${t.reason}`))):(this.I=null,this.dispatchEvent(n("disconnected",t)),this.P||this.k.schedule(this.L))};e&&_.addEventListener("open",(()=>_.send(e)),{once:!0,signal:h}),_.addEventListener("message",(t=>{t.data!==c&&(g&&(g=!1,this.I=_,this.dispatchEvent(n("connected")),u()),this.dispatchEvent(n("message",t.data)))}),{signal:h}),_.addEventListener("close",f,{signal:h}),_.addEventListener("error",(()=>f(o)),{signal:h}),t.addEventListener("abort",(()=>f(r)),{signal:h}),function(t){const s=new AbortController;let e=null;const i=()=>{null!==e&&(clearTimeout(e),e=null),t.send(l)},n=()=>{null!==e&&clearTimeout(e),e=setTimeout(i,a)},h=()=>{null!==e&&(clearTimeout(e),e=null),s.abort()};t.addEventListener("open",n,{once:!0,signal:s.signal}),t.addEventListener("message",n,{signal:s.signal}),t.addEventListener("close",h,{signal:s.signal}),t.addEventListener("error",h,{signal:s.signal}),global.addEventListener?.("offline",i,{signal:s.signal})}(_)})).catch((t=>{throw i.abort(),t}))}isConnected(){return null!==this.I}send=t=>{if(!this.I)throw new Error("connection lost");this.I.send(t)};close(){this.P=!0,this.k.stop(),this.I?.close()}}const o={code:0,reason:"client side error"},r={code:0,reason:"handshake timeout"},l="P",c="p",a=2e4,u=()=>!0;class d{M;A;O=[];j=function(){let t=1;return()=>t++}();constructor(t,s){this.M=t,this.A=s}D(t){this.O.push({J:void 0,N:t,F:[],G:[]})}q(t,s,e){if(s||e)if(this.O.length){const t=this.O[this.O.length-1];t.F.push(s??_),t.G.push(e??_)}else s&&Promise.resolve(t).then(s)}U(t){let s=0;for(let e=0;e<this.O.length;++e){const i=this.O[e];this.A(t,i.N,void 0!==i.J)?(i.J=void 0,this.O[e-s]=i):(i.G.forEach((t=>t("message possibly lost"))),++s)}this.O.length-=s}W(t){for(let t=0,s=0;t<=this.O.length;++t){const e=this.O[t];if(!e||void 0!==e.J||e.F.length>0||e.G.length>0){const e=t-s;if(e>1){const i=this.O[t-1],n=this.O.splice(s,e,i);i.N=this.M.combine(n.map((t=>t.N))),t-=e-1}s=t+1}}for(const s of this.O)void 0===s.J&&(s.J=this.j(),t(JSON.stringify({change:s.N,id:s.J})))}X(t){const s=void 0===t?-1:this.O.findIndex((s=>s.J===t));return-1===s?{B:null,H:!1}:{B:this.O.splice(s,1)[0],H:0===s}}K(t){if(!this.O.length)return t;const s=this.M.combine(this.O.map((({N:t})=>t)));return this.M.update(t,s)}}const _=()=>null;const g={code:0,reason:"graceful shutdown"},f=s({base:2,initialDelay:1e3,maxDelay:6e5,randomness:.3});exports.AT_LEAST_ONCE=u,exports.AT_MOST_ONCE=(t,s,e)=>!e,exports.OnlineScheduler=t,exports.SharedReducer=class extends i{M;I;R=!0;S={V:0,Y:[]};Z;tt=new Set;st=function(t){let s=!1;return e=>{if(s)throw new Error(t);try{return s=!0,e()}finally{s=!1}}}("Cannot dispatch recursively");constructor(s,e,{scheduler:i=new t(f,2e4),deliveryStrategy:n=u}={}){super(),this.M=s,this.Z=new d(s,n),this.I=new h(e,i),this.I.addEventListener("message",this.et),this.I.addEventListener("connected",this.it),this.I.addEventListener("connectionfailure",this.nt),this.I.addEventListener("disconnected",this.ht)}dispatch=function(t){return Object.assign(t,{sync:(s=[])=>new Promise(((e,i)=>t(s,e,(t=>i(new Error(t))))))})}(((t,s,e)=>{if(!t.length&&!s&&!e)return;const i={ot:t,F:s,G:e};switch(this.S.V){case-1:throw new Error("closed");case 0:this.S.Y.push(i);break;case 1:this.rt(this.lt(this.S.ct,[i])),this.dt.ut()}}));lt(t,s){return this.st((()=>{for(const{ot:i,F:n,G:h}of s){if(i.length){const{S:s,T:n}=e(this.M,t,i);t=s,this.Z.D(n)}this.Z.q(t,n,h)}return t}))}dt=function(t){let s=null;const e=()=>{null!==s&&(clearTimeout(s),s=null)},i=()=>{e(),t()};return{_t:i,ut:()=>{null===s&&(s=setTimeout(i,0))},o:e}}((()=>{this.I.isConnected()&&!this.R&&this.Z.W(this.I.send)}));gt(t){if(-1!==this.S.V){if(this.R=!1,0===this.S.V){const s=this.lt(t.init,this.S.Y);this.S={V:1,ft:t.init,ct:s},this.rt(s,!0)}else this.S.ft=t.init,this.Z.U(t.init),this.rt(this.Z.K(t.init));this.dt._t()}else this.wt(`Ignoring init after closing: ${JSON.stringify(t)}`)}vt(t){if(1!==this.S.V)return void this.wt(`Ignoring change before init: ${JSON.stringify(t)}`);const s=this.S.ft=this.M.update(this.S.ft,t.change),{B:e,H:i}=this.Z.X(t.id);i||this.rt(this.Z.K(s)),e?.F.forEach((t=>t(s)))}bt(t){if(1!==this.S.V)return void this.wt(`Ignoring error before init: ${JSON.stringify(t)}`);const{B:s}=this.Z.X(t.id);s?(this.wt(`API rejected update: ${t.error}`),s?.G.forEach((s=>s(t.error))),this.rt(this.Z.K(this.S.ft))):this.wt(`API sent error: ${t.error}`)}yt(){this.I.send("x"),this.R?this.wt("Unexpected extra close message"):(this.R=!0,this.dispatchEvent(n("disconnected",g)))}et=t=>{if("X"===t.detail)return void this.yt();const s=JSON.parse(t.detail);"change"in s?this.vt(s):"init"in s?this.gt(s):"error"in s?this.bt(s):this.wt(`Ignoring unknown API message: ${t.detail}`)};addStateListener(t){this.tt.add(t),1===this.S.V&&t(this.S.ct)}removeStateListener(t){this.tt.delete(t)}rt(t,s=!1){if(1!==this.S.V)throw new Error("invalid state");if(s||this.S.ct!==t){this.S.ct=t;for(const s of this.tt)s(t)}}getState(){return 1===this.S.V?this.S.ct:void 0}wt(t){this.dispatchEvent(n("warning",new Error(t)))}it=()=>{this.dispatchEvent(n("connected"))};nt=t=>{this.dispatchEvent(n("warning",new Error(`connection failure: ${t.detail.code} ${t.detail.reason}`)))};ht=t=>{this.R||(this.R=!0,this.dispatchEvent(n("disconnected",t.detail)))};close(){this.R=!0,this.S={V:-1},this.I.close(),this.dt.o(),this.tt.clear(),this.I.removeEventListener("message",this.et),this.I.removeEventListener("connected",this.it),this.I.removeEventListener("connectionfailure",this.nt),this.I.removeEventListener("disconnected",this.ht)}},exports.exponentialDelay=s;
@@ -1 +1 @@
1
- class t{t;i;h=null;o=null;l=null;u=0;constructor(t,s){this.t=t,this.i=s,this._=this._.bind(this)}trigger(t){this.stop(),this.l=t,this._()}schedule(t){this.l!==t&&(this.o&&this.stop(),this.l=t,null===this.h&&("hidden"===global.document?.visibilityState?global.addEventListener?.("visibilitychange",this._):(global.addEventListener?.("online",this._),this.h=setTimeout(this._,this.t(this.u))),global.addEventListener?.("pageshow",this._),global.addEventListener?.("focus",this._),++this.u))}stop(){this.l=null,this.o?.(),this.o=null,this.m()}async _(){if(this.o||!this.l)return;this.m();const t=new AbortController;let s=()=>null;this.o=()=>{s(),t.abort()};try{await Promise.race([this.l(t.signal),new Promise(((t,e)=>{const i=setTimeout(e,this.i);s=()=>clearTimeout(i)}))]),this.l=null,this.u=0}catch(s){if(!t.signal.aborted){t.abort();const s=this.l;s&&(this.l=null,this.schedule(s))}}finally{s(),this.o=null}}m(){null!==this.h&&(clearTimeout(this.h),this.h=null,global.removeEventListener?.("online",this._),global.removeEventListener?.("pageshow",this._),global.removeEventListener?.("visibilitychange",this._),global.removeEventListener?.("focus",this._))}}const s=({base:t=2,initialDelay:s,maxDelay:e,randomness:i=0})=>n=>Math.min(Math.pow(t,n)*s,e)*(1-Math.random()*i);function e(t,s,e){const i=[],n=[];function h(){if(n.length>0){const e=t.combine(n);i.push(e),s=t.update(s,e),n.length=0}}return function(t,s){let e={p:t,v:0,C:null};for(;e;)if(e.v>=e.p.length)e=e.C;else{const t=s(e.p[e.v]);++e.v,t&&t.length&&(e={p:t,v:0,C:e})}}(e,(t=>{if("function"==typeof t){h();return t(s)}return t&&n.push(t),null})),h(),{S:s,T:t.combine(i)}}class i extends EventTarget{addEventListener(t,s,e){super.addEventListener(t,s,e)}removeEventListener(t,s,e){super.removeEventListener(t,s,e)}dispatchEvent(t){return super.dispatchEvent(t)}}const n=(t,s)=>new CustomEvent(t,{detail:s});class h extends i{k;I;P=null;L=!1;constructor(t,s){super(),this.k=t,this.I=s,this.$=this.$.bind(this),this.I.trigger(this.$)}async $(t){const{url:s,token:e}=await this.k(t);t.throwIfAborted();const i=new AbortController,h=i.signal;await new Promise(((u,d)=>{const _=new WebSocket(s);let g=!0;const f=t=>{i.abort(),_.close(),g?(g=!1,d(new Error(`handshake failed: ${t.code} ${t.reason}`))):(this.P=null,this.dispatchEvent(n("disconnected",t)),this.L||this.I.schedule(this.$))};e&&_.addEventListener("open",(()=>_.send(e)),{once:!0,signal:h}),_.addEventListener("message",(t=>{t.data!==c&&(g&&(g=!1,this.P=_,this.dispatchEvent(n("connected")),u()),this.dispatchEvent(n("message",t.data)))}),{signal:h}),_.addEventListener("close",f,{signal:h}),_.addEventListener("error",(()=>f(o)),{signal:h}),t.addEventListener("abort",(()=>f(r)),{signal:h}),function(t){const s=new AbortController;let e=null;const i=()=>{null!==e&&(clearTimeout(e),e=null),t.send(l)},n=()=>{null!==e&&clearTimeout(e),e=setTimeout(i,a)},h=()=>{null!==e&&(clearTimeout(e),e=null),s.abort()};t.addEventListener("open",n,{once:!0,signal:s.signal}),t.addEventListener("message",n,{signal:s.signal}),t.addEventListener("close",h,{signal:s.signal}),t.addEventListener("error",h,{signal:s.signal}),global.addEventListener?.("offline",i,{signal:s.signal})}(_)})).catch((t=>{throw i.abort(),t}))}isConnected(){return null!==this.P}send=t=>{if(!this.P)throw new Error("connection lost");this.P.send(t)};close(){this.L=!0,this.I.stop(),this.P?.close()}}const o={code:0,reason:"client side error"},r={code:0,reason:"handshake timeout"},l="P",c="p",a=2e4,u=()=>!0,d=(t,s,e)=>!e;class _{M;A;O=[];j=function(){let t=1;return()=>t++}();constructor(t,s){this.M=t,this.A=s}D(t){this.O.push({J:void 0,N:t,F:[],G:[]})}q(t,s,e){if(s||e)if(this.O.length){const t=this.O[this.O.length-1];t.F.push(s??g),t.G.push(e??g)}else s&&Promise.resolve(t).then(s)}U(t){let s=0;for(let e=0;e<this.O.length;++e){const i=this.O[e];this.A(t,i.N,void 0!==i.J)?(i.J=void 0,this.O[e-s]=i):(i.G.forEach((t=>t("message possibly lost"))),++s)}this.O.length-=s}W(t){for(let t=0,s=0;t<=this.O.length;++t){const e=this.O[t];if(!e||void 0!==e.J||e.F.length>0||e.G.length>0){const e=t-s;if(e>1){const i=this.O[t-1],n=this.O.splice(s,e,i);i.N=this.M.combine(n.map((t=>t.N))),t-=e-1}s=t+1}}for(const s of this.O)void 0===s.J&&(s.J=this.j(),t(JSON.stringify({change:s.N,id:s.J})))}X(t){const s=void 0===t?-1:this.O.findIndex((s=>s.J===t));return-1===s?{B:null,H:!1}:{B:this.O.splice(s,1)[0],H:0===s}}K(t){if(!this.O.length)return t;const s=this.M.combine(this.O.map((({N:t})=>t)));return this.M.update(t,s)}}const g=()=>null;class f extends i{M;P;R=!0;S={V:0,Y:[]};Z;tt=new Set;st=function(t){let s=!1;return e=>{if(s)throw new Error(t);try{return s=!0,e()}finally{s=!1}}}("Cannot dispatch recursively");constructor(s,e,{scheduler:i=new t(w,2e4),deliveryStrategy:n=u}={}){super(),this.M=s,this.Z=new _(s,n),this.P=new h(e,i),this.P.addEventListener("message",this.et),this.P.addEventListener("connected",this.it),this.P.addEventListener("disconnected",this.nt)}dispatch=function(t){return Object.assign(t,{sync:(s=[])=>new Promise(((e,i)=>t(s,e,(t=>i(new Error(t))))))})}(((t,s,e)=>{if(!t.length&&!s&&!e)return;const i={ht:t,F:s,G:e};switch(this.S.V){case-1:throw new Error("closed");case 0:this.S.Y.push(i);break;case 1:this.ot(this.rt(this.S.lt,[i])),this.ut.ct()}}));rt(t,s){return this.st((()=>{for(const{ht:i,F:n,G:h}of s){if(i.length){const{S:s,T:n}=e(this.M,t,i);t=s,this.Z.D(n)}this.Z.q(t,n,h)}return t}))}ut=function(t){let s=null;const e=()=>{null!==s&&(clearTimeout(s),s=null)},i=()=>{e(),t()};return{dt:i,ct:()=>{null===s&&(s=setTimeout(i,0))},o:e}}((()=>{this.P.isConnected()&&!this.R&&this.Z.W(this.P.send)}));_t(t){if(-1!==this.S.V){if(this.R=!1,0===this.S.V){const s=this.rt(t.init,this.S.Y);this.S={V:1,gt:t.init,lt:s},this.ot(s,!0)}else this.S.gt=t.init,this.Z.U(t.init),this.ot(this.Z.K(t.init));this.ut.dt()}else this.ft(`Ignoring init after closing: ${JSON.stringify(t)}`)}wt(t){if(1!==this.S.V)return void this.ft(`Ignoring change before init: ${JSON.stringify(t)}`);const s=this.S.gt=this.M.update(this.S.gt,t.change),{B:e,H:i}=this.Z.X(t.id);i||this.ot(this.Z.K(s)),e?.F.forEach((t=>t(s)))}vt(t){if(1!==this.S.V)return void this.ft(`Ignoring error before init: ${JSON.stringify(t)}`);const{B:s}=this.Z.X(t.id);s?(this.ft(`API rejected update: ${t.error}`),s?.G.forEach((s=>s(t.error))),this.ot(this.Z.K(this.S.gt))):this.ft(`API sent error: ${t.error}`)}bt(){this.P.send("x"),this.R?this.ft("Unexpected extra close message"):(this.R=!0,this.dispatchEvent(n("disconnected",m)))}et=t=>{if("X"===t.detail)return void this.bt();const s=JSON.parse(t.detail);"change"in s?this.wt(s):"init"in s?this._t(s):"error"in s?this.vt(s):this.ft(`Ignoring unknown API message: ${t.detail}`)};addStateListener(t){this.tt.add(t),1===this.S.V&&t(this.S.lt)}removeStateListener(t){this.tt.delete(t)}ot(t,s=!1){if(1!==this.S.V)throw new Error("invalid state");if(s||this.S.lt!==t){this.S.lt=t;for(const s of this.tt)s(t)}}getState(){return 1===this.S.V?this.S.lt:void 0}ft(t){this.dispatchEvent(n("warning",new Error(t)))}it=()=>{this.dispatchEvent(n("connected"))};nt=t=>{this.R||(this.R=!0,this.dispatchEvent(n("disconnected",t.detail)))};close(){this.R=!0,this.S={V:-1},this.P.close(),this.ut.o(),this.tt.clear(),this.P.removeEventListener("message",this.et),this.P.removeEventListener("connected",this.it),this.P.removeEventListener("disconnected",this.nt)}}const m={code:0,reason:"graceful shutdown"},w=s({base:2,initialDelay:200,maxDelay:6e5,randomness:.3});export{u as AT_LEAST_ONCE,d as AT_MOST_ONCE,t as OnlineScheduler,f as SharedReducer,s as exponentialDelay};
1
+ class t{t;i;h=null;o=null;l=null;u=0;constructor(t,s){this.t=t,this.i=s,this._=this._.bind(this)}trigger(t){this.stop(),this.l=t,this._()}schedule(t){this.l!==t&&(this.o&&this.stop(),this.l=t,null===this.h&&("hidden"===global.document?.visibilityState?global.addEventListener?.("visibilitychange",this._):(global.addEventListener?.("online",this._),this.h=setTimeout(this._,this.t(this.u))),global.addEventListener?.("pageshow",this._),global.addEventListener?.("focus",this._),++this.u))}stop(){this.l=null,this.o?.(),this.o=null,this.m()}async _(){if(this.o||!this.l)return;this.m();const t=new AbortController;let s=()=>null;this.o=()=>{s(),t.abort()};try{await Promise.race([this.l(t.signal),new Promise(((t,e)=>{const i=setTimeout(e,this.i);s=()=>clearTimeout(i)}))]),this.l=null,this.u=0}catch(s){if(!t.signal.aborted){t.abort();const s=this.l;s&&(this.l=null,this.schedule(s))}}finally{s(),this.o=null}}m(){null!==this.h&&(clearTimeout(this.h),this.h=null,global.removeEventListener?.("online",this._),global.removeEventListener?.("pageshow",this._),global.removeEventListener?.("visibilitychange",this._),global.removeEventListener?.("focus",this._))}}const s=({base:t=2,initialDelay:s,maxDelay:e,randomness:i=0})=>n=>Math.min(Math.pow(t,n)*s,e)*(1-Math.random()*i);function e(t,s,e){const i=[],n=[];function h(){if(n.length>0){const e=t.combine(n);i.push(e),s=t.update(s,e),n.length=0}}return function(t,s){let e={p:t,v:0,C:null};for(;e;)if(e.v>=e.p.length)e=e.C;else{const t=s(e.p[e.v]);++e.v,t&&t.length&&(e={p:t,v:0,C:e})}}(e,(t=>{if("function"==typeof t){h();return t(s)}return t&&n.push(t),null})),h(),{S:s,T:t.combine(i)}}class i extends EventTarget{addEventListener(t,s,e){super.addEventListener(t,s,e)}removeEventListener(t,s,e){super.removeEventListener(t,s,e)}dispatchEvent(t){return super.dispatchEvent(t)}}const n=(t,s)=>new CustomEvent(t,{detail:s});class h extends i{$;k;I=null;P=!1;constructor(t,s){super(),this.$=t,this.k=s,this.L=this.L.bind(this),this.k.trigger(this.L)}async L(t){const{url:s,token:e}=await this.$(t);t.throwIfAborted();const i=new AbortController,h=i.signal;await new Promise(((u,d)=>{const _=new WebSocket(s);let g=!0;const f=t=>{i.abort(),_.close(),g?(g=!1,this.dispatchEvent(n("connectionfailure",t)),d(new Error(`handshake failed: ${t.code} ${t.reason}`))):(this.I=null,this.dispatchEvent(n("disconnected",t)),this.P||this.k.schedule(this.L))};e&&_.addEventListener("open",(()=>_.send(e)),{once:!0,signal:h}),_.addEventListener("message",(t=>{t.data!==c&&(g&&(g=!1,this.I=_,this.dispatchEvent(n("connected")),u()),this.dispatchEvent(n("message",t.data)))}),{signal:h}),_.addEventListener("close",f,{signal:h}),_.addEventListener("error",(()=>f(o)),{signal:h}),t.addEventListener("abort",(()=>f(r)),{signal:h}),function(t){const s=new AbortController;let e=null;const i=()=>{null!==e&&(clearTimeout(e),e=null),t.send(l)},n=()=>{null!==e&&clearTimeout(e),e=setTimeout(i,a)},h=()=>{null!==e&&(clearTimeout(e),e=null),s.abort()};t.addEventListener("open",n,{once:!0,signal:s.signal}),t.addEventListener("message",n,{signal:s.signal}),t.addEventListener("close",h,{signal:s.signal}),t.addEventListener("error",h,{signal:s.signal}),global.addEventListener?.("offline",i,{signal:s.signal})}(_)})).catch((t=>{throw i.abort(),t}))}isConnected(){return null!==this.I}send=t=>{if(!this.I)throw new Error("connection lost");this.I.send(t)};close(){this.P=!0,this.k.stop(),this.I?.close()}}const o={code:0,reason:"client side error"},r={code:0,reason:"handshake timeout"},l="P",c="p",a=2e4,u=()=>!0,d=(t,s,e)=>!e;class _{M;A;O=[];j=function(){let t=1;return()=>t++}();constructor(t,s){this.M=t,this.A=s}D(t){this.O.push({J:void 0,N:t,F:[],G:[]})}q(t,s,e){if(s||e)if(this.O.length){const t=this.O[this.O.length-1];t.F.push(s??g),t.G.push(e??g)}else s&&Promise.resolve(t).then(s)}U(t){let s=0;for(let e=0;e<this.O.length;++e){const i=this.O[e];this.A(t,i.N,void 0!==i.J)?(i.J=void 0,this.O[e-s]=i):(i.G.forEach((t=>t("message possibly lost"))),++s)}this.O.length-=s}W(t){for(let t=0,s=0;t<=this.O.length;++t){const e=this.O[t];if(!e||void 0!==e.J||e.F.length>0||e.G.length>0){const e=t-s;if(e>1){const i=this.O[t-1],n=this.O.splice(s,e,i);i.N=this.M.combine(n.map((t=>t.N))),t-=e-1}s=t+1}}for(const s of this.O)void 0===s.J&&(s.J=this.j(),t(JSON.stringify({change:s.N,id:s.J})))}X(t){const s=void 0===t?-1:this.O.findIndex((s=>s.J===t));return-1===s?{B:null,H:!1}:{B:this.O.splice(s,1)[0],H:0===s}}K(t){if(!this.O.length)return t;const s=this.M.combine(this.O.map((({N:t})=>t)));return this.M.update(t,s)}}const g=()=>null;class f extends i{M;I;R=!0;S={V:0,Y:[]};Z;tt=new Set;st=function(t){let s=!1;return e=>{if(s)throw new Error(t);try{return s=!0,e()}finally{s=!1}}}("Cannot dispatch recursively");constructor(s,e,{scheduler:i=new t(w,2e4),deliveryStrategy:n=u}={}){super(),this.M=s,this.Z=new _(s,n),this.I=new h(e,i),this.I.addEventListener("message",this.et),this.I.addEventListener("connected",this.it),this.I.addEventListener("connectionfailure",this.nt),this.I.addEventListener("disconnected",this.ht)}dispatch=function(t){return Object.assign(t,{sync:(s=[])=>new Promise(((e,i)=>t(s,e,(t=>i(new Error(t))))))})}(((t,s,e)=>{if(!t.length&&!s&&!e)return;const i={ot:t,F:s,G:e};switch(this.S.V){case-1:throw new Error("closed");case 0:this.S.Y.push(i);break;case 1:this.rt(this.lt(this.S.ct,[i])),this.dt.ut()}}));lt(t,s){return this.st((()=>{for(const{ot:i,F:n,G:h}of s){if(i.length){const{S:s,T:n}=e(this.M,t,i);t=s,this.Z.D(n)}this.Z.q(t,n,h)}return t}))}dt=function(t){let s=null;const e=()=>{null!==s&&(clearTimeout(s),s=null)},i=()=>{e(),t()};return{_t:i,ut:()=>{null===s&&(s=setTimeout(i,0))},o:e}}((()=>{this.I.isConnected()&&!this.R&&this.Z.W(this.I.send)}));gt(t){if(-1!==this.S.V){if(this.R=!1,0===this.S.V){const s=this.lt(t.init,this.S.Y);this.S={V:1,ft:t.init,ct:s},this.rt(s,!0)}else this.S.ft=t.init,this.Z.U(t.init),this.rt(this.Z.K(t.init));this.dt._t()}else this.wt(`Ignoring init after closing: ${JSON.stringify(t)}`)}vt(t){if(1!==this.S.V)return void this.wt(`Ignoring change before init: ${JSON.stringify(t)}`);const s=this.S.ft=this.M.update(this.S.ft,t.change),{B:e,H:i}=this.Z.X(t.id);i||this.rt(this.Z.K(s)),e?.F.forEach((t=>t(s)))}bt(t){if(1!==this.S.V)return void this.wt(`Ignoring error before init: ${JSON.stringify(t)}`);const{B:s}=this.Z.X(t.id);s?(this.wt(`API rejected update: ${t.error}`),s?.G.forEach((s=>s(t.error))),this.rt(this.Z.K(this.S.ft))):this.wt(`API sent error: ${t.error}`)}yt(){this.I.send("x"),this.R?this.wt("Unexpected extra close message"):(this.R=!0,this.dispatchEvent(n("disconnected",m)))}et=t=>{if("X"===t.detail)return void this.yt();const s=JSON.parse(t.detail);"change"in s?this.vt(s):"init"in s?this.gt(s):"error"in s?this.bt(s):this.wt(`Ignoring unknown API message: ${t.detail}`)};addStateListener(t){this.tt.add(t),1===this.S.V&&t(this.S.ct)}removeStateListener(t){this.tt.delete(t)}rt(t,s=!1){if(1!==this.S.V)throw new Error("invalid state");if(s||this.S.ct!==t){this.S.ct=t;for(const s of this.tt)s(t)}}getState(){return 1===this.S.V?this.S.ct:void 0}wt(t){this.dispatchEvent(n("warning",new Error(t)))}it=()=>{this.dispatchEvent(n("connected"))};nt=t=>{this.dispatchEvent(n("warning",new Error(`connection failure: ${t.detail.code} ${t.detail.reason}`)))};ht=t=>{this.R||(this.R=!0,this.dispatchEvent(n("disconnected",t.detail)))};close(){this.R=!0,this.S={V:-1},this.I.close(),this.dt.o(),this.tt.clear(),this.I.removeEventListener("message",this.et),this.I.removeEventListener("connected",this.it),this.I.removeEventListener("connectionfailure",this.nt),this.I.removeEventListener("disconnected",this.ht)}}const m={code:0,reason:"graceful shutdown"},w=s({base:2,initialDelay:1e3,maxDelay:6e5,randomness:.3});export{u as AT_LEAST_ONCE,d as AT_MOST_ONCE,t as OnlineScheduler,f as SharedReducer,s as exponentialDelay};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shared-reducer",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "shared state management",
5
5
  "author": "David Evans",
6
6
  "license": "MIT",