shared-reducer 6.2.0 → 6.3.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/backend/index.d.ts +11 -2
- package/backend/index.js +1 -1
- package/backend/index.mjs +1 -1
- package/frontend/index.d.ts +15 -6
- package/frontend/index.js +1 -1
- package/frontend/index.mjs +1 -1
- package/package.json +1 -1
package/backend/index.d.ts
CHANGED
|
@@ -27,11 +27,14 @@ interface TopicMap<K, T> {
|
|
|
27
27
|
broadcast(key: K, message: T): MaybePromise<void>;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
type ChangeEvent = [string, ...unknown[]];
|
|
31
|
+
|
|
30
32
|
declare class PermissionError extends Error {
|
|
31
33
|
}
|
|
32
34
|
interface Permission<T, SpecT> {
|
|
33
35
|
validateWriteSpec?(spec: SpecT): void;
|
|
34
36
|
validateWrite(newValue: T, oldValue: T): void;
|
|
37
|
+
validateEvent(event: ChangeEvent): void;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
interface Model<ID, T> {
|
|
@@ -42,17 +45,19 @@ interface Model<ID, T> {
|
|
|
42
45
|
|
|
43
46
|
interface Context<T, SpecT> {
|
|
44
47
|
update: (input: T, spec: SpecT) => T;
|
|
48
|
+
isNoOp?: (spec: SpecT) => boolean;
|
|
45
49
|
}
|
|
46
50
|
type Listener<SpecT, MetaT> = (message: ChangeInfo<SpecT>, meta: MetaT | undefined) => void;
|
|
47
51
|
interface Subscription<T, SpecT, MetaT> {
|
|
48
52
|
getInitialData(): Readonly<T>;
|
|
49
53
|
listen(onChange: Listener<SpecT, MetaT>): void;
|
|
50
|
-
send(change: SpecT, meta?: MetaT): Promise<void>;
|
|
54
|
+
send(change: SpecT, events?: ChangeEvent[] | undefined, meta?: MetaT): Promise<void>;
|
|
51
55
|
close(): Promise<void>;
|
|
52
56
|
}
|
|
53
57
|
type Identifier = string | null;
|
|
54
58
|
type ChangeInfo<SpecT> = {
|
|
55
59
|
change: SpecT;
|
|
60
|
+
events: Readonly<Readonly<ChangeEvent>[]> | undefined;
|
|
56
61
|
error?: undefined;
|
|
57
62
|
} | {
|
|
58
63
|
change?: undefined;
|
|
@@ -76,7 +81,10 @@ declare class Broadcaster<T, SpecT> {
|
|
|
76
81
|
idProvider?: () => MaybePromise<string>;
|
|
77
82
|
});
|
|
78
83
|
subscribe<MetaT = void>(id: ID, permission?: Permission<T, SpecT>): Promise<Subscription<T, SpecT, MetaT> | null>;
|
|
79
|
-
update(id: ID, change: SpecT,
|
|
84
|
+
update(id: ID, change: SpecT, { events, permission, }?: {
|
|
85
|
+
events?: ChangeEvent[] | undefined;
|
|
86
|
+
permission?: Permission<T, SpecT>;
|
|
87
|
+
}): Promise<void>;
|
|
80
88
|
private _internalApplyChange;
|
|
81
89
|
private _internalQueueChange;
|
|
82
90
|
}
|
|
@@ -161,6 +169,7 @@ declare class ReadWriteStruct<T extends object> implements Permission<T, unknown
|
|
|
161
169
|
private readonly _readOnlyFields;
|
|
162
170
|
constructor(_readOnlyFields?: (keyof T)[]);
|
|
163
171
|
validateWrite(newValue: T, oldValue: T): void;
|
|
172
|
+
validateEvent(): void;
|
|
164
173
|
}
|
|
165
174
|
|
|
166
175
|
declare class AsyncTaskQueue extends EventTarget implements TaskQueue {
|
package/backend/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var t=require("node:crypto");const e=()=>{const e=t.randomUUID().substring(0,8);let r=0;return()=>{const t=r++;return`${e}-${t}`}};class r extends EventTarget{t=[];i=!1;push(t){return new Promise((e,r)=>{this.t.push(async()=>{try{e(await t())}catch(t){r(t)}}),this.i||this.o()})}async o(){for(this.i=!0;this.t.length>0;)await this.t.shift()();this.i=!1,this.dispatchEvent(new CustomEvent("drain"))}active(){return this.i}}class s{h;
|
|
1
|
+
"use strict";var t=require("node:crypto");const e=()=>{const e=t.randomUUID().substring(0,8);let r=0;return()=>{const t=r++;return`${e}-${t}`}};class r extends EventTarget{t=[];i=!1;push(t){return new Promise((e,r)=>{this.t.push(async()=>{try{e(await t())}catch(t){r(t)}}),this.i||this.o()})}async o(){for(this.i=!0;this.t.length>0;)await this.t.shift()();this.i=!1,this.dispatchEvent(new CustomEvent("drain"))}active(){return this.i}}class s{h;l=new Map;constructor(t=()=>new r){this.h=t}push(t,e){let r=this.l.get(t);if(!r){const e=this.h(),s=()=>{e.active()||(this.l.delete(t),e.removeEventListener("drain",s))};e.addEventListener("drain",s),this.l.set(t,e),r=e}return r.push(e)}}class n{u;p=new Map;constructor(t){this.u=t}async add(t,e){let r=this.p.get(t);r||(r=this.u(t),this.p.set(t,r)),await r.add(e)}async remove(t,e){const r=this.p.get(t);if(r){await r.remove(e)||this.p.delete(t)}}async broadcast(t,e){const r=this.p.get(t);r&&await r.broadcast(e)}}class i{v=new Set;add(t){this.v.add(t)}remove(t){return this.v.delete(t),this.v.size>0}broadcast(t){this.v.forEach(e=>e(t))}}const a={validateWrite(){},validateEvent(){}};class o extends Error{}class c extends Error{}const h=0,l=1,u=2,d=3,w=new Error("not found"),f=(t,e,r)=>console.warn(`shared-reducer: ${r}`,e),p=t=>t;const y="Cannot modify data",v={validateWriteSpec(){throw new o(y)},validateWrite(){throw new o(y)},validateEvent(){throw new o(y)}};exports.AsyncTaskQueue=r,exports.Broadcaster=class{m;_;v;S;O;constructor(t,r,a={}){this.m=t,this._=r,this.v=a.subscribers??new n(()=>new i),this.S=a.taskQueues??new s,this.O=a.idProvider??e()}async subscribe(t,e=a){let r={C:0},s="";const n=t=>{2===r.C?t.source===s?r.I(t.message,t.meta):t.message.change&&r.I(t.message,void 0):1===r.C&&r.t.push(t)};try{if(await this.S.push(t,async()=>{const e=await this.m.read(t);null!=e&&(r={C:1,J:e,t:[]},await this.v.add(t,n))}),0===r.C)return null;s=await this.O()}catch(e){throw await this.v.remove(t,n),e}return{getInitialData(){if(1!==r.C)throw new Error("Already started");return r.J},listen(t){if(1!==r.C)throw new Error("Already started");const e=r.t;r={C:2,I:t},e.forEach(n)},send:(r,n,i)=>this.N(t,r,n,e,s,i),close:async()=>{await this.v.remove(t,n)}}}update(t,e,{events:r,permission:s=a}={}){return this.N(t,e,r,s,null,void 0)}async T(t,e,r,s,n,i){if(r?.length)for(const t of r)s.validateEvent(t);try{if(s.validateWriteSpec?.(e),!this._.isNoOp?.(e)){const r=await this.m.read(t);if(!r)throw new Error("Deleted");const n=this._.update(r,e),i=this.m.validate(n);s.validateWrite(i,r),await this.m.write(t,i,r)}}catch(e){return void this.v.broadcast(t,{message:{error:e instanceof Error?e.message:"Internal error"},source:n,meta:i})}0===r?.length&&(r=void 0),this.v.broadcast(t,{message:{change:e,events:r},source:n,meta:i})}async N(t,e,r,s,n,i){return this.S.push(t,()=>this.T(t,e,r,s,n,i))}},exports.CLOSE="X",exports.CLOSE_ACK="x",exports.CollectionStorageModel=class{j;A;validate;D;$;constructor(t,e,r,s={}){this.j=t,this.A=e,this.validate=r,this.D=s.readErrorIntercept??p,this.$=s.writeErrorIntercept??p}async read(t){try{return await this.j.where(this.A,t).get()}catch(t){throw this.D(t)}}async write(t,e,r){const s={};Object.entries(e).forEach(([t,e])=>{const n=t;e!==(Object.prototype.hasOwnProperty.call(r,n)?r[n]:void 0)&&(s[n]?Object.defineProperty(s,n,{value:e,configurable:!0,enumerable:!0,writable:!0}):s[n]=e)});try{await this.j.where(this.A,t).update(s)}catch(t){throw this.$(t)}}},exports.InMemoryModel=class{read=this.get;validate;q=new Map;constructor(t=t=>t){this.validate=t}set(t,e){this.q.set(t,e)}get(t){return this.q.get(t)}delete(t){this.q.delete(t)}write(t,e,r){if(r!==this.q.get(t))throw new Error("Unexpected previous value");this.q.set(t,e)}},exports.InMemoryTopic=i,exports.PING="P",exports.PONG="p",exports.PermissionError=o,exports.ReadOnly=v,exports.ReadWrite=a,exports.ReadWriteStruct=class{P;constructor(t=[]){this.P=t}validateWrite(t,e){for(const r of this.P){const s=Object.prototype.hasOwnProperty.call(e,r),n=Object.prototype.hasOwnProperty.call(t,r);if(s!==n)throw new o(s?`Cannot remove field ${String(r)}`:`Cannot add field ${String(r)}`);if(n&&e[r]!==t[r])throw new o(`Cannot edit field ${String(r)}`)}}validateEvent(){}},exports.TaskQueueMap=s,exports.TrackingTopicMap=n,exports.UniqueIdProvider=e,exports.WebsocketHandlerFactory=class{broadcaster;constructor(t){this.broadcaster=t}handler({accessGetter:t,acceptWebSocket:e,pingInterval:r=25e3,pongTimeout:s=3e4,notFoundError:n=w,setSoftCloseHandler:i,onConnect:a,onDisconnect:p,onError:y=f}){return async(...w)=>{const f=[];let v;try{const{id:m,permission:g}=await t(...w),_=await this.broadcaster.subscribe(m,g);if(!_)throw n;f.push(()=>_.close());let x,b=h,E="connection failed";const S=new Promise(t=>{x=e=>{x=()=>{},E=e,t()}}),O=await e(...w);w.length=1,i?.(w[0],()=>(b===h&&(b=l,O.send("X"),v||(v=setTimeout(I,s))),S));const C=Date.now();a?.(w[0],m,g),f.push(()=>p?.(w[0],E,Date.now()-C)),O.on("close",()=>{clearTimeout(v),b=u,x("client disconnect")});const I=()=>{O.terminate(),b=d,x("connection lost")},J=()=>{O.ping(),clearTimeout(v),v=setTimeout(I,s)},N=()=>{clearTimeout(v),v=setTimeout(J,r)};O.on("pong",N),O.on("message",async(t,e)=>{if(N(),e)return O.send(JSON.stringify({error:"Binary messages are not supported"}));const r=String(t);if("P"===r)return O.send("p");if("x"===r)return b!==l&&b!==d?O.send(JSON.stringify({error:"Unexpected close ack message"})):(b=u,x("clean shutdown"),O.close());if(b===u)return O.send(JSON.stringify({error:"Unexpected message after close ack"}));try{const t=function(t){let e;try{e=JSON.parse(t)}catch{throw new c("Invalid JSON")}if("object"!=typeof e||!e||Array.isArray(e)||!("change"in e))throw new c("Must specify change and optional id");const r={change:e.change};if("events"in e){if(!Array.isArray(e.events)||e.events.some(t=>!Array.isArray(t)||"string"!=typeof t[0]))throw new c("If specified, events must be an array of events");r.events=e.events}if("id"in e){if("number"!=typeof e.id)throw new c("If specified, id must be a number");r.id=e.id}return r}(r);await _.send(t.change,t.events,t.id)}catch(t){t instanceof o||t instanceof c?O.send(JSON.stringify({error:t.message})):(y(w[0],t,"message"),O.send(JSON.stringify({error:"Internal error"})))}}),b===h&&(O.send(JSON.stringify({init:_.getInitialData()})),_.listen((t,e)=>O.send(JSON.stringify(void 0!==e?{id:e,...t}:t))),N()),await S}finally{clearTimeout(v);for(const t of f.reverse())try{await t()}catch(t){y(w[0],t,"teardown")}}}}};
|
package/backend/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as t}from"node:crypto";const e=()=>{const e=t().substring(0,8);let r=0;return()=>{const t=r++;return`${e}-${t}`}};class r extends EventTarget{t=[];i=!1;push(t){return new Promise((e,r)=>{this.t.push(async()=>{try{e(await t())}catch(t){r(t)}}),this.i||this.o()})}async o(){for(this.i=!0;this.t.length>0;)await this.t.shift()();this.i=!1,this.dispatchEvent(new CustomEvent("drain"))}active(){return this.i}}class s{h;
|
|
1
|
+
import{randomUUID as t}from"node:crypto";const e=()=>{const e=t().substring(0,8);let r=0;return()=>{const t=r++;return`${e}-${t}`}};class r extends EventTarget{t=[];i=!1;push(t){return new Promise((e,r)=>{this.t.push(async()=>{try{e(await t())}catch(t){r(t)}}),this.i||this.o()})}async o(){for(this.i=!0;this.t.length>0;)await this.t.shift()();this.i=!1,this.dispatchEvent(new CustomEvent("drain"))}active(){return this.i}}class s{h;l=new Map;constructor(t=()=>new r){this.h=t}push(t,e){let r=this.l.get(t);if(!r){const e=this.h(),s=()=>{e.active()||(this.l.delete(t),e.removeEventListener("drain",s))};e.addEventListener("drain",s),this.l.set(t,e),r=e}return r.push(e)}}class n{u;m=new Map;constructor(t){this.u=t}async add(t,e){let r=this.m.get(t);r||(r=this.u(t),this.m.set(t,r)),await r.add(e)}async remove(t,e){const r=this.m.get(t);if(r){await r.remove(e)||this.m.delete(t)}}async broadcast(t,e){const r=this.m.get(t);r&&await r.broadcast(e)}}class i{p=new Set;add(t){this.p.add(t)}remove(t){return this.p.delete(t),this.p.size>0}broadcast(t){this.p.forEach(e=>e(t))}}const a={validateWrite(){},validateEvent(){}};class o{v;_;p;S;O;constructor(t,r,a={}){this.v=t,this._=r,this.p=a.subscribers??new n(()=>new i),this.S=a.taskQueues??new s,this.O=a.idProvider??e()}async subscribe(t,e=a){let r={C:0},s="";const n=t=>{2===r.C?t.source===s?r.I(t.message,t.meta):t.message.change&&r.I(t.message,void 0):1===r.C&&r.t.push(t)};try{if(await this.S.push(t,async()=>{const e=await this.v.read(t);null!=e&&(r={C:1,J:e,t:[]},await this.p.add(t,n))}),0===r.C)return null;s=await this.O()}catch(e){throw await this.p.remove(t,n),e}return{getInitialData(){if(1!==r.C)throw new Error("Already started");return r.J},listen(t){if(1!==r.C)throw new Error("Already started");const e=r.t;r={C:2,I:t},e.forEach(n)},send:(r,n,i)=>this.N(t,r,n,e,s,i),close:async()=>{await this.p.remove(t,n)}}}update(t,e,{events:r,permission:s=a}={}){return this.N(t,e,r,s,null,void 0)}async T(t,e,r,s,n,i){if(r?.length)for(const t of r)s.validateEvent(t);try{if(s.validateWriteSpec?.(e),!this._.isNoOp?.(e)){const r=await this.v.read(t);if(!r)throw new Error("Deleted");const n=this._.update(r,e),i=this.v.validate(n);s.validateWrite(i,r),await this.v.write(t,i,r)}}catch(e){return void this.p.broadcast(t,{message:{error:e instanceof Error?e.message:"Internal error"},source:n,meta:i})}0===r?.length&&(r=void 0),this.p.broadcast(t,{message:{change:e,events:r},source:n,meta:i})}async N(t,e,r,s,n,i){return this.S.push(t,()=>this.T(t,e,r,s,n,i))}}class c extends Error{}class h extends Error{}const l="P",u="p",d="X",w="x";class f{broadcaster;constructor(t){this.broadcaster=t}handler({accessGetter:t,acceptWebSocket:e,pingInterval:r=25e3,pongTimeout:s=3e4,notFoundError:n=g,setSoftCloseHandler:i,onConnect:a,onDisconnect:o,onError:l=_}){return async(...u)=>{const d=[];let w;try{const{id:f,permission:g}=await t(...u),_=await this.broadcaster.subscribe(f,g);if(!_)throw n;d.push(()=>_.close());let b,E=y,S="connection failed";const O=new Promise(t=>{b=e=>{b=()=>{},S=e,t()}}),C=await e(...u);u.length=1,i?.(u[0],()=>(E===y&&(E=m,C.send("X"),w||(w=setTimeout(I,s))),O));const x=Date.now();a?.(u[0],f,g),d.push(()=>o?.(u[0],S,Date.now()-x)),C.on("close",()=>{clearTimeout(w),E=p,b("client disconnect")});const I=()=>{C.terminate(),E=v,b("connection lost")},J=()=>{C.ping(),clearTimeout(w),w=setTimeout(I,s)},N=()=>{clearTimeout(w),w=setTimeout(J,r)};C.on("pong",N),C.on("message",async(t,e)=>{if(N(),e)return C.send(JSON.stringify({error:"Binary messages are not supported"}));const r=String(t);if("P"===r)return C.send("p");if("x"===r)return E!==m&&E!==v?C.send(JSON.stringify({error:"Unexpected close ack message"})):(E=p,b("clean shutdown"),C.close());if(E===p)return C.send(JSON.stringify({error:"Unexpected message after close ack"}));try{const t=function(t){let e;try{e=JSON.parse(t)}catch{throw new h("Invalid JSON")}if("object"!=typeof e||!e||Array.isArray(e)||!("change"in e))throw new h("Must specify change and optional id");const r={change:e.change};if("events"in e){if(!Array.isArray(e.events)||e.events.some(t=>!Array.isArray(t)||"string"!=typeof t[0]))throw new h("If specified, events must be an array of events");r.events=e.events}if("id"in e){if("number"!=typeof e.id)throw new h("If specified, id must be a number");r.id=e.id}return r}(r);await _.send(t.change,t.events,t.id)}catch(t){t instanceof c||t instanceof h?C.send(JSON.stringify({error:t.message})):(l(u[0],t,"message"),C.send(JSON.stringify({error:"Internal error"})))}}),E===y&&(C.send(JSON.stringify({init:_.getInitialData()})),_.listen((t,e)=>C.send(JSON.stringify(void 0!==e?{id:e,...t}:t))),N()),await O}finally{clearTimeout(w);for(const t of d.reverse())try{await t()}catch(t){l(u[0],t,"teardown")}}}}}const y=0,m=1,p=2,v=3,g=new Error("not found"),_=(t,e,r)=>console.warn(`shared-reducer: ${r}`,e),b=t=>t;class E{j;A;validate;D;$;constructor(t,e,r,s={}){this.j=t,this.A=e,this.validate=r,this.D=s.readErrorIntercept??b,this.$=s.writeErrorIntercept??b}async read(t){try{return await this.j.where(this.A,t).get()}catch(t){throw this.D(t)}}async write(t,e,r){const s={};Object.entries(e).forEach(([t,e])=>{const n=t;e!==(Object.prototype.hasOwnProperty.call(r,n)?r[n]:void 0)&&(s[n]?Object.defineProperty(s,n,{value:e,configurable:!0,enumerable:!0,writable:!0}):s[n]=e)});try{await this.j.where(this.A,t).update(s)}catch(t){throw this.$(t)}}}class S{read=this.get;validate;P=new Map;constructor(t=t=>t){this.validate=t}set(t,e){this.P.set(t,e)}get(t){return this.P.get(t)}delete(t){this.P.delete(t)}write(t,e,r){if(r!==this.P.get(t))throw new Error("Unexpected previous value");this.P.set(t,e)}}const O="Cannot modify data",C={validateWriteSpec(){throw new c(O)},validateWrite(){throw new c(O)},validateEvent(){throw new c(O)}};class x{W;constructor(t=[]){this.W=t}validateWrite(t,e){for(const r of this.W){const s=Object.prototype.hasOwnProperty.call(e,r),n=Object.prototype.hasOwnProperty.call(t,r);if(s!==n)throw new c(s?`Cannot remove field ${String(r)}`:`Cannot add field ${String(r)}`);if(n&&e[r]!==t[r])throw new c(`Cannot edit field ${String(r)}`)}}validateEvent(){}}export{r as AsyncTaskQueue,o as Broadcaster,d as CLOSE,w as CLOSE_ACK,E as CollectionStorageModel,S as InMemoryModel,i as InMemoryTopic,l as PING,u as PONG,c as PermissionError,C as ReadOnly,a as ReadWrite,x as ReadWriteStruct,s as TaskQueueMap,n as TrackingTopicMap,e as UniqueIdProvider,f as WebsocketHandlerFactory};
|
package/frontend/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
type ChangeEvent = [string, ...unknown[]];
|
|
2
|
+
|
|
1
3
|
interface Context<T, SpecT> {
|
|
2
4
|
update: (input: T, spec: SpecT) => T;
|
|
3
5
|
combine: (specs: SpecT[]) => SpecT;
|
|
@@ -5,9 +7,15 @@ interface Context<T, SpecT> {
|
|
|
5
7
|
type SpecGenerator<T, SpecT> = (state: T) => SpecSource<T, SpecT>[];
|
|
6
8
|
type SpecSource<T, SpecT> = SpecT | SpecGenerator<T, SpecT> | null;
|
|
7
9
|
type DispatchSpec<T, SpecT> = SpecSource<T, SpecT>[];
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
type DispatchFn<T, SpecT> = (specs: DispatchSpec<T, SpecT>, options?: {
|
|
11
|
+
events?: ChangeEvent[] | undefined;
|
|
12
|
+
syncedCallback?: ((state: T) => void) | undefined;
|
|
13
|
+
errorCallback?: ((error: string) => void) | undefined;
|
|
14
|
+
}) => void;
|
|
15
|
+
interface Dispatch<T, SpecT> extends DispatchFn<T, SpecT> {
|
|
16
|
+
sync(specs?: DispatchSpec<T, SpecT>, options?: {
|
|
17
|
+
events?: ChangeEvent[] | undefined;
|
|
18
|
+
}): Promise<T>;
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
type MaybePromise<T> = Promise<T> | T;
|
|
@@ -79,6 +87,7 @@ type SharedReducerEvents = {
|
|
|
79
87
|
rejected: CustomEvent<DisconnectDetail>;
|
|
80
88
|
warning: CustomEvent<Error>;
|
|
81
89
|
};
|
|
90
|
+
type StateListener<T> = (state: Readonly<T>, events: Readonly<Readonly<ChangeEvent>[]>) => void;
|
|
82
91
|
declare class SharedReducer<T, SpecT> extends TypedEventTarget<SharedReducerEvents> {
|
|
83
92
|
private readonly _context;
|
|
84
93
|
private readonly _ws;
|
|
@@ -97,8 +106,8 @@ declare class SharedReducer<T, SpecT> extends TypedEventTarget<SharedReducerEven
|
|
|
97
106
|
private _handleErrorMessage;
|
|
98
107
|
private _handleGracefulClose;
|
|
99
108
|
private readonly _handleMessage;
|
|
100
|
-
addStateListener(listener:
|
|
101
|
-
removeStateListener(listener:
|
|
109
|
+
addStateListener(listener: StateListener<T>): void;
|
|
110
|
+
removeStateListener(listener: StateListener<T>): void;
|
|
102
111
|
private _setLocalState;
|
|
103
112
|
getState(): Readonly<T> | undefined;
|
|
104
113
|
private _warn;
|
|
@@ -110,4 +119,4 @@ declare class SharedReducer<T, SpecT> extends TypedEventTarget<SharedReducerEven
|
|
|
110
119
|
}
|
|
111
120
|
|
|
112
121
|
export { AT_LEAST_ONCE, AT_MOST_ONCE, OnlineScheduler, SharedReducer, exponentialDelay };
|
|
113
|
-
export type { ConnectionInfo, Context, DeliveryStrategy, DisconnectDetail, Dispatch, DispatchSpec, Scheduler, SharedReducerOptions };
|
|
122
|
+
export type { ChangeEvent, ConnectionInfo, Context, DeliveryStrategy, DisconnectDetail, Dispatch, DispatchSpec, Scheduler, SharedReducerOptions, StateListener };
|
package/frontend/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";class t{t;i;h=null;o=null;l=null;u=()=>null;_=0;constructor(t,s){this.t=t,this.i=s,this.m=this.m.bind(this)}trigger(t,s){this.stop(),this.l=t,this.u=s,this.m()}schedule(t,s,e=!0){e&&(this._=0),this.l!==t&&(this.o&&this.stop(),this.l=t,this.u=s,null===this.h&&("hidden"===globalThis.document?.visibilityState?globalThis.addEventListener?.("visibilitychange",this.m):(globalThis.addEventListener?.("online",this.m),this.h=setTimeout(this.m,this.t(this._))),globalThis.addEventListener?.("pageshow",this.m),globalThis.addEventListener?.("focus",this.m),++this._))}stop(){this.l=null,this.o?.(),this.o=null,this.p()}async m(){if(this.o||!this.l)return;this.p();const t=new AbortController,s=(t=>{const s={stop:()=>{}};return s.promise=new Promise((e,i)=>{const n=setTimeout(()=>i(new Error(`Timed out after ${t}ms`)),t);s.stop=()=>clearTimeout(n)}),s})(this.i);this.o=()=>{s.stop(),t.abort()};const e=this.l;this.l=null;try{await Promise.race([e(t.signal),s.promise])}catch(s){if(!t.signal.aborted){t.abort();try{this.u(s)}catch(t){console.error("Error handler failed",s,t)}e&&this.schedule(e,this.u,!1)}}finally{s.stop(),this.o=null}}p(){null!==this.h&&(clearTimeout(this.h),this.h=null,globalThis.removeEventListener?.("online",this.m),globalThis.removeEventListener?.("pageshow",this.m),globalThis.removeEventListener?.("visibilitychange",this.m),globalThis.removeEventListener?.("focus",this.m))}}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={v:t,T:0,C:null};for(;e;)if(e.T>=e.v.length)e=e.C;else{const t=s(e.v[e.T]);++e.T,t&&t.length&&(e={v:t,T:0,C:e})}}(e,t=>{if("function"==typeof t){h();return t(s)}return t&&n.push(t),null}),h(),{S:s
|
|
1
|
+
"use strict";class t{t;i;h=null;o=null;l=null;u=()=>null;_=0;constructor(t,s){this.t=t,this.i=s,this.m=this.m.bind(this)}trigger(t,s){this.stop(),this.l=t,this.u=s,this.m()}schedule(t,s,e=!0){e&&(this._=0),this.l!==t&&(this.o&&this.stop(),this.l=t,this.u=s,null===this.h&&("hidden"===globalThis.document?.visibilityState?globalThis.addEventListener?.("visibilitychange",this.m):(globalThis.addEventListener?.("online",this.m),this.h=setTimeout(this.m,this.t(this._))),globalThis.addEventListener?.("pageshow",this.m),globalThis.addEventListener?.("focus",this.m),++this._))}stop(){this.l=null,this.o?.(),this.o=null,this.p()}async m(){if(this.o||!this.l)return;this.p();const t=new AbortController,s=(t=>{const s={stop:()=>{}};return s.promise=new Promise((e,i)=>{const n=setTimeout(()=>i(new Error(`Timed out after ${t}ms`)),t);s.stop=()=>clearTimeout(n)}),s})(this.i);this.o=()=>{s.stop(),t.abort()};const e=this.l;this.l=null;try{await Promise.race([e(t.signal),s.promise])}catch(s){if(!t.signal.aborted){t.abort();try{this.u(s)}catch(t){console.error("Error handler failed",s,t)}e&&this.schedule(e,this.u,!1)}}finally{s.stop(),this.o=null}}p(){null!==this.h&&(clearTimeout(this.h),this.h=null,globalThis.removeEventListener?.("online",this.m),globalThis.removeEventListener?.("pageshow",this.m),globalThis.removeEventListener?.("visibilitychange",this.m),globalThis.removeEventListener?.("focus",this.m))}}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={v:t,T:0,C:null};for(;e;)if(e.T>=e.v.length)e=e.C;else{const t=s(e.v[e.T]);++e.T,t&&t.length&&(e={v:t,T:0,C:e})}}(e,t=>{if("function"==typeof t){h();return t(s)}return t&&n.push(t),null}),h(),{S:s,k: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,s);class h extends i{$;j;I=null;P=!1;constructor(t,s){super(),this.$=t,this.j=s,this.L=this.L.bind(this),this.M=this.M.bind(this),this.j.trigger(this.L,this.M)}reconnect(t){t&&(this.$=t),this.I?this.I.close():this.P||this.j.schedule(this.L,this.M)}M(t){const s=t instanceof Error?t:new Error(`unknown connection error ${t}`);this.dispatchEvent(n("connectionfailure",{detail:s}))}async L(t){t.throwIfAborted();const s=new AbortController,e=s.signal,{url:i,token:h}=this.$;await new Promise((c,a)=>{const u=new WebSocket(i);let d=!0;const _=t=>{s.abort(),u.close();const e=d;d=!1,e||(this.I=null,this.dispatchEvent(n("disconnected",{detail:t}))),this.P||this.dispatchEvent(n("rejected",{detail:t,cancelable:!0}))?e?a(new Error(`Connection closed ${t.code} ${t.reason}`)):this.P||this.j.schedule(this.L,this.M):e&&c()};h&&u.addEventListener("open",()=>u.send(h),{once:!0,signal:e}),u.addEventListener("message",t=>{t.data!==r&&(d&&(d=!1,this.I=u,this.dispatchEvent(n("connected")),c()),this.dispatchEvent(n("message",{detail:t.data})))},{signal:e}),u.addEventListener("close",_,{signal:e}),u.addEventListener("error",t=>{let s="unknown";if("error"in t){const e=t.error;s=e instanceof Error?e.stack??String(e):String(e)}_({code:0,reason:`client side error: ${s}`})},{signal:e}),t.addEventListener("abort",()=>{s.abort(),u.close(),d=!1,a(t.reason)},{signal:e}),function(t){const s=new AbortController;let e=null;const i=()=>{null!==e&&(clearTimeout(e),e=null),t.send(o)},n=()=>{null!==e&&clearTimeout(e),e=setTimeout(i,l)},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}),globalThis.addEventListener?.("offline",i,{signal:s.signal})}(u)}).catch(t=>{throw s.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.j.stop(),this.I?.close()}}const o="P",r="p",l=2e4,c=()=>!0;class a{A;O;D=[];J=function(){let t=1;return()=>t++}();constructor(t,s){this.A=t,this.O=s}N(t,s){this.D.push({F:void 0,q:t,G:s,H:[],R:[]})}U(t,s,e){if(s||e)if(this.D.length){const t=this.D[this.D.length-1];t.H.push(s??d),t.R.push(e??d)}else s&&Promise.resolve(t).then(s)}W(t){let s=0;for(let e=0;e<this.D.length;++e){const i=this.D[e];this.O(t,i.q,void 0!==i.F)?(i.F=void 0,this.D[e-s]=i):(i.R.forEach(t=>t("message possibly lost")),++s)}this.D.length-=s}X(t){for(let t=0,s=0;t<=this.D.length;++t){const e=this.D[t];if(!e||void 0!==e.F||e.H.length>0||e.R.length>0){const e=t-s;if(e>1){const i=this.D[t-1],n=this.D.splice(s,e,i);i.q=this.A.combine(n.map(t=>t.q)),i.G=u(n.map(t=>t.G)),t-=e-1}s=t+1}}for(const s of this.D)void 0===s.F&&(s.F=this.J(),t(JSON.stringify({change:s.q,events:s.G,id:s.F})))}B(t){const s=void 0===t?-1:this.D.findIndex(s=>s.F===t);return-1===s?{K:null,V:!1}:{K:this.D.splice(s,1)[0],V:0===s}}Y(t){if(!this.D.length)return t;const s=this.A.combine(this.D.map(({q:t})=>t));return this.A.update(t,s)}}function u(t){const[s,...e]=t.filter(t=>void 0!==t);if(!s)return;if(!e.length)return s;const i=[...s];for(const t of e)for(const s of t){const t=i.findIndex(t=>t[0]===s[0]);-1!==t&&i.splice(t,1),i.push(s)}return i}const d=()=>null;const _=[],g={code:0,reason:"graceful shutdown"},f=s({base:2,initialDelay:1e3,maxDelay:6e5,randomness:.3});exports.AT_LEAST_ONCE=c,exports.AT_MOST_ONCE=(t,s,e)=>!e,exports.OnlineScheduler=t,exports.SharedReducer=class extends i{A;I;Z=!0;S={tt:0,st:[]};et;it=new Set;nt=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=c}={}){super(),this.A=s,this.et=new a(s,n),this.I=new h(e,i),this.I.addEventListener("message",this.ht),this.I.addEventListener("connected",this.ot),this.I.addEventListener("connectionfailure",this.rt),this.I.addEventListener("rejected",this.lt),this.I.addEventListener("disconnected",this.ct)}reconnect(t){this.I.reconnect(t)}dispatch=function(t){return Object.assign(t,{sync:(s=[],e={})=>new Promise((i,n)=>t(s,{...e,syncedCallback:i,errorCallback:t=>n(new Error(t))}))})}((t,s={})=>{if(!(t.length||s.events?.length||s.syncedCallback||s.errorCallback))return;const e={ut:t,G:s.events,H:s.syncedCallback,R:s.errorCallback};switch(this.S.tt){case-1:throw new Error("closed");case 0:this.S.st.push(e);break;case 1:this.dt(this._t(this.S.gt,[e]),s.events),this.wt.ft()}});_t(t,s){return this.nt(()=>{for(const{ut:i,G:n,H:h,R:o}of s){if(i.length){const{S:s,k:h}=e(this.A,t,i);t=s,this.et.N(h,n)}else n?.length&&this.et.N(this.A.combine([]),n);this.et.U(t,h,o)}return t})}wt=function(t){let s=null;const e=()=>{null!==s&&(clearTimeout(s),s=null)},i=()=>{e(),t()};return{vt:i,ft:()=>{null===s&&(s=setTimeout(i,0))},o:e}}(()=>{this.I.isConnected()&&!this.Z&&this.et.X(this.I.send)});bt(t){if(-1!==this.S.tt){if(this.Z=!1,0===this.S.tt){const s=this._t(t.init,this.S.st);this.S={tt:1,yt:t.init,gt:s},this.dt(s,_,!0)}else this.S.yt=t.init,this.et.W(t.init),this.dt(this.et.Y(t.init));this.wt.vt()}else this.Tt(`Ignoring init after closing: ${JSON.stringify(t)}`)}Ct(t){if(1!==this.S.tt)return void this.Tt(`Ignoring change before init: ${JSON.stringify(t)}`);const s=this.S.yt=this.A.update(this.S.yt,t.change),{K:e,V:i}=this.et.B(t.id);i||this.dt(this.et.Y(s),e?_:t.events),e?.H.forEach(t=>t(s))}Et(t){if(1!==this.S.tt)return void this.Tt(`Ignoring error before init: ${JSON.stringify(t)}`);const{K:s}=this.et.B(t.id);s?(this.Tt(`API rejected update: ${t.error}`),s?.R.forEach(s=>s(t.error)),this.dt(this.et.Y(this.S.yt))):this.Tt(`API sent error: ${t.error}`)}xt(){this.I.send("x"),this.Z?this.Tt("Unexpected extra close message"):(this.Z=!0,this.dispatchEvent(n("disconnected",{detail:g})))}ht=t=>{if("X"===t.detail)return void this.xt();const s=JSON.parse(t.detail);"change"in s?this.Ct(s):"init"in s?this.bt(s):"error"in s?this.Et(s):this.Tt(`Ignoring unknown API message: ${t.detail}`)};addStateListener(t){this.it.add(t),1===this.S.tt&&t(this.S.gt,[])}removeStateListener(t){this.it.delete(t)}dt(t,s=_,e=!1){if(1!==this.S.tt)throw new Error("invalid state");if(e||this.S.gt!==t||s.length){this.S.gt=t;for(const e of this.it)e(t,s)}}getState(){return 1===this.S.tt?this.S.gt:void 0}Tt(t){this.dispatchEvent(n("warning",{detail:new Error(t)}))}ot=()=>{this.dispatchEvent(n("connected"))};rt=t=>{this.dispatchEvent(n("warning",{detail:t.detail}))};lt=t=>{this.dispatchEvent(n("rejected",{detail:t.detail,cancelable:!0}))||t.preventDefault()};ct=t=>{this.Z||(this.Z=!0,this.dispatchEvent(n("disconnected",{detail:t.detail})))};close(){this.Z=!0,this.S={tt:-1},this.I.close(),this.wt.o(),this.it.clear(),this.I.removeEventListener("message",this.ht),this.I.removeEventListener("connected",this.ot),this.I.removeEventListener("connectionfailure",this.rt),this.I.removeEventListener("rejected",this.lt),this.I.removeEventListener("disconnected",this.ct)}},exports.exponentialDelay=s;
|
package/frontend/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class t{t;i;h=null;o=null;l=null;u=()=>null;_=0;constructor(t,s){this.t=t,this.i=s,this.m=this.m.bind(this)}trigger(t,s){this.stop(),this.l=t,this.u=s,this.m()}schedule(t,s,e=!0){e&&(this._=0),this.l!==t&&(this.o&&this.stop(),this.l=t,this.u=s,null===this.h&&("hidden"===globalThis.document?.visibilityState?globalThis.addEventListener?.("visibilitychange",this.m):(globalThis.addEventListener?.("online",this.m),this.h=setTimeout(this.m,this.t(this._))),globalThis.addEventListener?.("pageshow",this.m),globalThis.addEventListener?.("focus",this.m),++this._))}stop(){this.l=null,this.o?.(),this.o=null,this.
|
|
1
|
+
class t{t;i;h=null;o=null;l=null;u=()=>null;_=0;constructor(t,s){this.t=t,this.i=s,this.m=this.m.bind(this)}trigger(t,s){this.stop(),this.l=t,this.u=s,this.m()}schedule(t,s,e=!0){e&&(this._=0),this.l!==t&&(this.o&&this.stop(),this.l=t,this.u=s,null===this.h&&("hidden"===globalThis.document?.visibilityState?globalThis.addEventListener?.("visibilitychange",this.m):(globalThis.addEventListener?.("online",this.m),this.h=setTimeout(this.m,this.t(this._))),globalThis.addEventListener?.("pageshow",this.m),globalThis.addEventListener?.("focus",this.m),++this._))}stop(){this.l=null,this.o?.(),this.o=null,this.v()}async m(){if(this.o||!this.l)return;this.v();const t=new AbortController,s=(t=>{const s={stop:()=>{}};return s.promise=new Promise((e,i)=>{const n=setTimeout(()=>i(new Error(`Timed out after ${t}ms`)),t);s.stop=()=>clearTimeout(n)}),s})(this.i);this.o=()=>{s.stop(),t.abort()};const e=this.l;this.l=null;try{await Promise.race([e(t.signal),s.promise])}catch(s){if(!t.signal.aborted){t.abort();try{this.u(s)}catch(t){console.error("Error handler failed",s,t)}e&&this.schedule(e,this.u,!1)}}finally{s.stop(),this.o=null}}v(){null!==this.h&&(clearTimeout(this.h),this.h=null,globalThis.removeEventListener?.("online",this.m),globalThis.removeEventListener?.("pageshow",this.m),globalThis.removeEventListener?.("visibilitychange",this.m),globalThis.removeEventListener?.("focus",this.m))}}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,T:0,C:null};for(;e;)if(e.T>=e.p.length)e=e.C;else{const t=s(e.p[e.T]);++e.T,t&&t.length&&(e={p:t,T:0,C:e})}}(e,t=>{if("function"==typeof t){h();return t(s)}return t&&n.push(t),null}),h(),{S:s,k: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,s);class h extends i{$;j;I=null;P=!1;constructor(t,s){super(),this.$=t,this.j=s,this.L=this.L.bind(this),this.M=this.M.bind(this),this.j.trigger(this.L,this.M)}reconnect(t){t&&(this.$=t),this.I?this.I.close():this.P||this.j.schedule(this.L,this.M)}M(t){const s=t instanceof Error?t:new Error(`unknown connection error ${t}`);this.dispatchEvent(n("connectionfailure",{detail:s}))}async L(t){t.throwIfAborted();const s=new AbortController,e=s.signal,{url:i,token:h}=this.$;await new Promise((c,a)=>{const u=new WebSocket(i);let d=!0;const _=t=>{s.abort(),u.close();const e=d;d=!1,e||(this.I=null,this.dispatchEvent(n("disconnected",{detail:t}))),this.P||this.dispatchEvent(n("rejected",{detail:t,cancelable:!0}))?e?a(new Error(`Connection closed ${t.code} ${t.reason}`)):this.P||this.j.schedule(this.L,this.M):e&&c()};h&&u.addEventListener("open",()=>u.send(h),{once:!0,signal:e}),u.addEventListener("message",t=>{t.data!==r&&(d&&(d=!1,this.I=u,this.dispatchEvent(n("connected")),c()),this.dispatchEvent(n("message",{detail:t.data})))},{signal:e}),u.addEventListener("close",_,{signal:e}),u.addEventListener("error",t=>{let s="unknown";if("error"in t){const e=t.error;s=e instanceof Error?e.stack??String(e):String(e)}_({code:0,reason:`client side error: ${s}`})},{signal:e}),t.addEventListener("abort",()=>{s.abort(),u.close(),d=!1,a(t.reason)},{signal:e}),function(t){const s=new AbortController;let e=null;const i=()=>{null!==e&&(clearTimeout(e),e=null),t.send(o)},n=()=>{null!==e&&clearTimeout(e),e=setTimeout(i,l)},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}),globalThis.addEventListener?.("offline",i,{signal:s.signal})}(u)}).catch(t=>{throw s.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.j.stop(),this.I?.close()}}const o="P",r="p",l=2e4,c=()=>!0,a=(t,s,e)=>!e;class u{A;O;D=[];J=function(){let t=1;return()=>t++}();constructor(t,s){this.A=t,this.O=s}N(t,s){this.D.push({F:void 0,q:t,G:s,H:[],R:[]})}U(t,s,e){if(s||e)if(this.D.length){const t=this.D[this.D.length-1];t.H.push(s??_),t.R.push(e??_)}else s&&Promise.resolve(t).then(s)}W(t){let s=0;for(let e=0;e<this.D.length;++e){const i=this.D[e];this.O(t,i.q,void 0!==i.F)?(i.F=void 0,this.D[e-s]=i):(i.R.forEach(t=>t("message possibly lost")),++s)}this.D.length-=s}X(t){for(let t=0,s=0;t<=this.D.length;++t){const e=this.D[t];if(!e||void 0!==e.F||e.H.length>0||e.R.length>0){const e=t-s;if(e>1){const i=this.D[t-1],n=this.D.splice(s,e,i);i.q=this.A.combine(n.map(t=>t.q)),i.G=d(n.map(t=>t.G)),t-=e-1}s=t+1}}for(const s of this.D)void 0===s.F&&(s.F=this.J(),t(JSON.stringify({change:s.q,events:s.G,id:s.F})))}B(t){const s=void 0===t?-1:this.D.findIndex(s=>s.F===t);return-1===s?{K:null,V:!1}:{K:this.D.splice(s,1)[0],V:0===s}}Y(t){if(!this.D.length)return t;const s=this.A.combine(this.D.map(({q:t})=>t));return this.A.update(t,s)}}function d(t){const[s,...e]=t.filter(t=>void 0!==t);if(!s)return;if(!e.length)return s;const i=[...s];for(const t of e)for(const s of t){const t=i.findIndex(t=>t[0]===s[0]);-1!==t&&i.splice(t,1),i.push(s)}return i}const _=()=>null;class g extends i{A;I;Z=!0;S={tt:0,st:[]};et;it=new Set;nt=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=c}={}){super(),this.A=s,this.et=new u(s,n),this.I=new h(e,i),this.I.addEventListener("message",this.ht),this.I.addEventListener("connected",this.ot),this.I.addEventListener("connectionfailure",this.rt),this.I.addEventListener("rejected",this.lt),this.I.addEventListener("disconnected",this.ct)}reconnect(t){this.I.reconnect(t)}dispatch=function(t){return Object.assign(t,{sync:(s=[],e={})=>new Promise((i,n)=>t(s,{...e,syncedCallback:i,errorCallback:t=>n(new Error(t))}))})}((t,s={})=>{if(!(t.length||s.events?.length||s.syncedCallback||s.errorCallback))return;const e={ut:t,G:s.events,H:s.syncedCallback,R:s.errorCallback};switch(this.S.tt){case-1:throw new Error("closed");case 0:this.S.st.push(e);break;case 1:this.dt(this._t(this.S.gt,[e]),s.events),this.wt.ft()}});_t(t,s){return this.nt(()=>{for(const{ut:i,G:n,H:h,R:o}of s){if(i.length){const{S:s,k:h}=e(this.A,t,i);t=s,this.et.N(h,n)}else n?.length&&this.et.N(this.A.combine([]),n);this.et.U(t,h,o)}return t})}wt=function(t){let s=null;const e=()=>{null!==s&&(clearTimeout(s),s=null)},i=()=>{e(),t()};return{vt:i,ft:()=>{null===s&&(s=setTimeout(i,0))},o:e}}(()=>{this.I.isConnected()&&!this.Z&&this.et.X(this.I.send)});bt(t){if(-1!==this.S.tt){if(this.Z=!1,0===this.S.tt){const s=this._t(t.init,this.S.st);this.S={tt:1,yt:t.init,gt:s},this.dt(s,f,!0)}else this.S.yt=t.init,this.et.W(t.init),this.dt(this.et.Y(t.init));this.wt.vt()}else this.Tt(`Ignoring init after closing: ${JSON.stringify(t)}`)}Ct(t){if(1!==this.S.tt)return void this.Tt(`Ignoring change before init: ${JSON.stringify(t)}`);const s=this.S.yt=this.A.update(this.S.yt,t.change),{K:e,V:i}=this.et.B(t.id);i||this.dt(this.et.Y(s),e?f:t.events),e?.H.forEach(t=>t(s))}Et(t){if(1!==this.S.tt)return void this.Tt(`Ignoring error before init: ${JSON.stringify(t)}`);const{K:s}=this.et.B(t.id);s?(this.Tt(`API rejected update: ${t.error}`),s?.R.forEach(s=>s(t.error)),this.dt(this.et.Y(this.S.yt))):this.Tt(`API sent error: ${t.error}`)}St(){this.I.send("x"),this.Z?this.Tt("Unexpected extra close message"):(this.Z=!0,this.dispatchEvent(n("disconnected",{detail:m})))}ht=t=>{if("X"===t.detail)return void this.St();const s=JSON.parse(t.detail);"change"in s?this.Ct(s):"init"in s?this.bt(s):"error"in s?this.Et(s):this.Tt(`Ignoring unknown API message: ${t.detail}`)};addStateListener(t){this.it.add(t),1===this.S.tt&&t(this.S.gt,[])}removeStateListener(t){this.it.delete(t)}dt(t,s=f,e=!1){if(1!==this.S.tt)throw new Error("invalid state");if(e||this.S.gt!==t||s.length){this.S.gt=t;for(const e of this.it)e(t,s)}}getState(){return 1===this.S.tt?this.S.gt:void 0}Tt(t){this.dispatchEvent(n("warning",{detail:new Error(t)}))}ot=()=>{this.dispatchEvent(n("connected"))};rt=t=>{this.dispatchEvent(n("warning",{detail:t.detail}))};lt=t=>{this.dispatchEvent(n("rejected",{detail:t.detail,cancelable:!0}))||t.preventDefault()};ct=t=>{this.Z||(this.Z=!0,this.dispatchEvent(n("disconnected",{detail:t.detail})))};close(){this.Z=!0,this.S={tt:-1},this.I.close(),this.wt.o(),this.it.clear(),this.I.removeEventListener("message",this.ht),this.I.removeEventListener("connected",this.ot),this.I.removeEventListener("connectionfailure",this.rt),this.I.removeEventListener("rejected",this.lt),this.I.removeEventListener("disconnected",this.ct)}}const f=[],m={code:0,reason:"graceful shutdown"},w=s({base:2,initialDelay:1e3,maxDelay:6e5,randomness:.3});export{c as AT_LEAST_ONCE,a as AT_MOST_ONCE,t as OnlineScheduler,g as SharedReducer,s as exponentialDelay};
|