@vielzeug/pulse 2.0.1 → 2.1.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.
Files changed (46) hide show
  1. package/README.md +37 -42
  2. package/dist/_connection.cjs +2 -0
  3. package/dist/_connection.cjs.map +1 -0
  4. package/dist/_connection.d.ts +9 -0
  5. package/dist/_connection.d.ts.map +1 -0
  6. package/dist/_connection.js +107 -0
  7. package/dist/_connection.js.map +1 -0
  8. package/dist/channel.cjs +1 -1
  9. package/dist/channel.cjs.map +1 -1
  10. package/dist/channel.js +32 -35
  11. package/dist/channel.js.map +1 -1
  12. package/dist/heartbeat.cjs +1 -1
  13. package/dist/heartbeat.cjs.map +1 -1
  14. package/dist/heartbeat.d.ts.map +1 -1
  15. package/dist/heartbeat.js +1 -2
  16. package/dist/heartbeat.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/presence.cjs +1 -1
  20. package/dist/presence.cjs.map +1 -1
  21. package/dist/presence.d.ts.map +1 -1
  22. package/dist/presence.js +34 -35
  23. package/dist/presence.js.map +1 -1
  24. package/dist/pulse.cjs +1 -1
  25. package/dist/pulse.cjs.map +1 -1
  26. package/dist/pulse.d.ts +4 -13
  27. package/dist/pulse.d.ts.map +1 -1
  28. package/dist/pulse.iife.js +1 -1
  29. package/dist/pulse.iife.js.map +1 -1
  30. package/dist/pulse.js +1 -1
  31. package/dist/pulse.js.map +1 -1
  32. package/dist/types.d.ts +40 -69
  33. package/dist/types.d.ts.map +1 -1
  34. package/package.json +3 -3
  35. package/dist/_dev.cjs +0 -2
  36. package/dist/_dev.cjs.map +0 -1
  37. package/dist/_dev.d.ts +0 -2
  38. package/dist/_dev.d.ts.map +0 -1
  39. package/dist/_dev.js +0 -6
  40. package/dist/_dev.js.map +0 -1
  41. package/dist/reconnect.cjs +0 -2
  42. package/dist/reconnect.cjs.map +0 -1
  43. package/dist/reconnect.d.ts +0 -13
  44. package/dist/reconnect.d.ts.map +0 -1
  45. package/dist/reconnect.js +0 -32
  46. package/dist/reconnect.js.map +0 -1
package/dist/presence.js CHANGED
@@ -1,71 +1,70 @@
1
- import "./_dev.js";
2
- import { deriveAbortController as e } from "./_utils.js";
3
- import { encode as t } from "./protocol.js";
1
+ import { PulseDisposedError as e } from "./errors.js";
2
+ import { deriveAbortController as t } from "./_utils.js";
4
3
  import { signal as n } from "@vielzeug/ripple";
5
4
  //#region src/presence.ts
6
- function r(r, i, a, o, s) {
7
- let c = e(o), l = c.signal.aborted;
8
- c.signal.addEventListener("abort", () => {
9
- l = !0;
5
+ function r(r, i, a, o, s, c) {
6
+ let l = t(o), u = l.signal.aborted;
7
+ l.signal.addEventListener("abort", () => {
8
+ u = !0;
10
9
  }, { once: !0 });
11
- let u = n(/* @__PURE__ */ new Map()), d = /* @__PURE__ */ new Set(), f = /* @__PURE__ */ new Set(), p = [
10
+ let d = n(/* @__PURE__ */ new Map());
11
+ s?.(() => {
12
+ d.value = /* @__PURE__ */ new Map();
13
+ });
14
+ let f = /* @__PURE__ */ new Set(), p = /* @__PURE__ */ new Set(), m = [
12
15
  a.onState((e) => {
13
16
  if (e.room !== r) return;
14
17
  let t = /* @__PURE__ */ new Map();
15
18
  for (let [n, r] of Object.entries(e.members)) t.set(n, r);
16
- u.value = t;
19
+ d.value = t;
17
20
  }),
18
21
  a.onJoin((e) => {
19
22
  if (e.room !== r) return;
20
- let t = new Map(u.value);
21
- t.set(e.id, e.state), u.value = t;
22
- for (let t of d) t(e.id, e.state);
23
+ let t = new Map(d.value);
24
+ t.set(e.id, e.state), d.value = t;
25
+ for (let t of f) t(e.id, e.state);
23
26
  }),
24
27
  a.onLeave((e) => {
25
28
  if (e.room !== r) return;
26
- let t = new Map(u.value);
27
- t.delete(e.id), u.value = t;
28
- for (let t of f) t(e.id);
29
+ let t = new Map(d.value);
30
+ t.delete(e.id), d.value = t;
31
+ for (let t of p) t(e.id);
29
32
  })
30
33
  ];
31
34
  return {
32
35
  get disposalSignal() {
33
- return c.signal;
36
+ return l.signal;
34
37
  },
35
38
  dispose() {
36
- if (!l) {
37
- l = !0, i(t({
38
- room: r,
39
- type: "leave"
40
- })), c.abort();
41
- for (let e of p) e();
42
- p.length = 0, d.clear(), f.clear(), s?.();
39
+ if (!u) {
40
+ u = !0, l.abort();
41
+ for (let e of m) e();
42
+ m.length = 0, f.clear(), p.clear(), c?.();
43
43
  }
44
44
  },
45
45
  get disposed() {
46
- return l;
46
+ return u;
47
47
  },
48
- onJoin(e) {
49
- return l ? (`${r}`, () => {}) : (d.add(e), () => d.delete(e));
48
+ onJoin(t) {
49
+ if (u) throw new e(`Presence "${r}"`);
50
+ return f.add(t), () => f.delete(t);
50
51
  },
51
- onLeave(e) {
52
- return l ? (`${r}`, () => {}) : (f.add(e), () => f.delete(e));
52
+ onLeave(t) {
53
+ if (u) throw new e(`Presence "${r}"`);
54
+ return p.add(t), () => p.delete(t);
53
55
  },
54
56
  get room() {
55
57
  return r;
56
58
  },
57
59
  get state() {
58
- return u;
60
+ return d;
59
61
  },
60
62
  [Symbol.dispose]() {
61
63
  this.dispose();
62
64
  },
63
- update(e) {
64
- l || i(t({
65
- room: r,
66
- state: e,
67
- type: "presence"
68
- }));
65
+ update(t) {
66
+ if (u) throw new e(`Presence "${r}"`);
67
+ i(r, t);
69
68
  }
70
69
  };
71
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"presence.js","names":[],"sources":["../src/presence.ts"],"sourcesContent":["import { type Signal, signal } from '@vielzeug/ripple';\n\nimport type { PresenceChannel, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { deriveAbortController } from './_utils';\nimport { type InPresenceJoinFrame, type InPresenceLeaveFrame, type InPresenceStateFrame, encode } from './protocol';\n\ntype RawSendFn = (frame: string) => void;\n\nexport type PresenceEvents = {\n onJoin(handler: (frame: InPresenceJoinFrame) => void): Unsubscribe;\n onLeave(handler: (frame: InPresenceLeaveFrame) => void): Unsubscribe;\n onState(handler: (frame: InPresenceStateFrame) => void): Unsubscribe;\n};\n\n/**\n * Create a presence channel that reactively tracks room member states.\n * @internal\n */\nexport function createPresence<T>(\n room: string,\n rawSend: RawSendFn,\n events: PresenceEvents,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PresenceChannel<T> {\n const ctrl = deriveAbortController(disposalSignal);\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const members: Signal<Map<string, T>> = signal(new Map<string, T>());\n const joinHandlers = new Set<(memberId: string, state: T) => void>();\n const leaveHandlers = new Set<(memberId: string) => void>();\n\n const unsubs: Array<() => void> = [\n events.onState((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map<string, T>();\n\n for (const [id, state] of Object.entries(frame.members)) {\n next.set(id, state as T);\n }\n\n members.value = next;\n }),\n events.onJoin((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.set(frame.id, frame.state as T);\n members.value = next;\n\n for (const handler of joinHandlers) {\n handler(frame.id, frame.state as T);\n }\n }),\n events.onLeave((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.delete(frame.id);\n members.value = next;\n\n for (const handler of leaveHandlers) {\n handler(frame.id);\n }\n }),\n ];\n\n const presence: PresenceChannel<T> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n rawSend(encode({ room, type: 'leave' }));\n ctrl.abort();\n\n for (const unsub of unsubs) unsub();\n\n unsubs.length = 0;\n joinHandlers.clear();\n leaveHandlers.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n onJoin(handler: (memberId: string, state: T) => void): Unsubscribe {\n if (disposed) {\n warn(`onJoin() called on a disposed presence channel '${room}' — listener ignored`);\n\n return () => {};\n }\n\n joinHandlers.add(handler);\n\n return () => joinHandlers.delete(handler);\n },\n\n onLeave(handler: (memberId: string) => void): Unsubscribe {\n if (disposed) {\n warn(`onLeave() called on a disposed presence channel '${room}' — listener ignored`);\n\n return () => {};\n }\n\n leaveHandlers.add(handler);\n\n return () => leaveHandlers.delete(handler);\n },\n\n get room() {\n return room;\n },\n\n get state() {\n return members;\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n update(state: T): void {\n if (disposed) return;\n\n rawSend(encode({ room, state, type: 'presence' }));\n },\n };\n\n return presence;\n}\n"],"mappings":";;;;;AAoBA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACoB;CACpB,IAAM,IAAO,EAAsB,CAAc,GAE7C,IAAW,EAAK,OAAO;CAE3B,EAAK,OAAO,iBACV,eACM;EACJ,IAAW;CACb,GACA,EAAE,MAAM,GAAK,CACf;CAEA,IAAM,IAAkC,kBAAO,IAAI,IAAe,CAAC,GAC7D,oBAAe,IAAI,IAA0C,GAC7D,oBAAgB,IAAI,IAAgC,GAEpD,IAA4B;EAChC,EAAO,SAAS,MAAU;GACxB,IAAI,EAAM,SAAS,GAAM;GAEzB,IAAM,oBAAO,IAAI,IAAe;GAEhC,KAAK,IAAM,CAAC,GAAI,MAAU,OAAO,QAAQ,EAAM,OAAO,GACpD,EAAK,IAAI,GAAI,CAAU;GAGzB,EAAQ,QAAQ;EAClB,CAAC;EACD,EAAO,QAAQ,MAAU;GACvB,IAAI,EAAM,SAAS,GAAM;GAEzB,IAAM,IAAO,IAAI,IAAI,EAAQ,KAAK;GAGlC,AADA,EAAK,IAAI,EAAM,IAAI,EAAM,KAAU,GACnC,EAAQ,QAAQ;GAEhB,KAAK,IAAM,KAAW,GACpB,EAAQ,EAAM,IAAI,EAAM,KAAU;EAEtC,CAAC;EACD,EAAO,SAAS,MAAU;GACxB,IAAI,EAAM,SAAS,GAAM;GAEzB,IAAM,IAAO,IAAI,IAAI,EAAQ,KAAK;GAGlC,AADA,EAAK,OAAO,EAAM,EAAE,GACpB,EAAQ,QAAQ;GAEhB,KAAK,IAAM,KAAW,GACpB,EAAQ,EAAM,EAAE;EAEpB,CAAC;CACH;CAqEA,OAAO;EAlEL,IAAI,iBAAiB;GACnB,OAAO,EAAK;EACd;EAEA,UAAU;GACJ,QAIJ;IAFA,IAAW,IACX,EAAQ,EAAO;KAAE;KAAM,MAAM;IAAQ,CAAC,CAAC,GACvC,EAAK,MAAM;IAEX,KAAK,IAAM,KAAS,GAAQ,EAAM;IAKlC,AAHA,EAAO,SAAS,GAChB,EAAa,MAAM,GACnB,EAAc,MAAM,GACpB,IAAY;GAPD;EAQb;EAEA,IAAI,WAAW;GACb,OAAO;EACT;EAEA,OAAO,GAA4D;GASjE,OARI,KACG,GAAmD,EAAnD,SAEQ,CAAC,MAGhB,EAAa,IAAI,CAAO,SAEX,EAAa,OAAO,CAAO;EAC1C;EAEA,QAAQ,GAAkD;GASxD,OARI,KACG,GAAoD,EAApD,SAEQ,CAAC,MAGhB,EAAc,IAAI,CAAO,SAEZ,EAAc,OAAO,CAAO;EAC3C;EAEA,IAAI,OAAO;GACT,OAAO;EACT;EAEA,IAAI,QAAQ;GACV,OAAO;EACT;EAEA,CAAC,OAAO,WAAW;GACjB,KAAK,QAAQ;EACf;EAEA,OAAO,GAAgB;GACjB,KAEJ,EAAQ,EAAO;IAAE;IAAM;IAAO,MAAM;GAAW,CAAC,CAAC;EACnD;CAGK;AACT"}
1
+ {"version":3,"file":"presence.js","names":[],"sources":["../src/presence.ts"],"sourcesContent":["import { type Signal, signal } from '@vielzeug/ripple';\n\nimport type { PresenceChannel, Unsubscribe } from './types';\n\nimport { deriveAbortController } from './_utils';\nimport { PulseDisposedError } from './errors';\nimport { type InPresenceJoinFrame, type InPresenceLeaveFrame, type InPresenceStateFrame } from './protocol';\n\ntype UpdateFn<T> = (room: string, state: T) => void;\n\nexport type PresenceEvents = {\n onJoin(handler: (frame: InPresenceJoinFrame) => void): Unsubscribe;\n onLeave(handler: (frame: InPresenceLeaveFrame) => void): Unsubscribe;\n onState(handler: (frame: InPresenceStateFrame) => void): Unsubscribe;\n};\n\n/**\n * Create a presence channel that reactively tracks room member states.\n * @internal\n */\nexport function createPresence<T>(\n room: string,\n update: UpdateFn<T>,\n events: PresenceEvents,\n disposalSignal: AbortSignal,\n registerReset?: (reset: () => void) => void,\n onDispose?: () => void,\n): PresenceChannel<T> {\n const ctrl = deriveAbortController(disposalSignal);\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const members: Signal<Map<string, T>> = signal(new Map<string, T>());\n\n registerReset?.(() => {\n members.value = new Map<string, T>();\n });\n\n const joinHandlers = new Set<(memberId: string, state: T) => void>();\n const leaveHandlers = new Set<(memberId: string) => void>();\n\n const unsubs: Array<() => void> = [\n events.onState((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map<string, T>();\n\n for (const [id, state] of Object.entries(frame.members)) {\n next.set(id, state as T);\n }\n\n members.value = next;\n }),\n events.onJoin((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.set(frame.id, frame.state as T);\n members.value = next;\n\n for (const handler of joinHandlers) {\n handler(frame.id, frame.state as T);\n }\n }),\n events.onLeave((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.delete(frame.id);\n members.value = next;\n\n for (const handler of leaveHandlers) {\n handler(frame.id);\n }\n }),\n ];\n\n const presence: PresenceChannel<T> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n ctrl.abort();\n\n for (const unsub of unsubs) unsub();\n\n unsubs.length = 0;\n joinHandlers.clear();\n leaveHandlers.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n onJoin(handler: (memberId: string, state: T) => void): Unsubscribe {\n if (disposed) {\n throw new PulseDisposedError(`Presence \"${room}\"`);\n }\n\n joinHandlers.add(handler);\n\n return () => joinHandlers.delete(handler);\n },\n\n onLeave(handler: (memberId: string) => void): Unsubscribe {\n if (disposed) {\n throw new PulseDisposedError(`Presence \"${room}\"`);\n }\n\n leaveHandlers.add(handler);\n\n return () => leaveHandlers.delete(handler);\n },\n\n get room() {\n return room;\n },\n\n get state() {\n return members;\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n update(state: T): void {\n if (disposed) throw new PulseDisposedError(`Presence \"${room}\"`);\n\n update(room, state);\n },\n };\n\n return presence;\n}\n"],"mappings":";;;;AAoBA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACA,GACoB;CACpB,IAAM,IAAO,EAAsB,CAAc,GAE7C,IAAW,EAAK,OAAO;CAE3B,EAAK,OAAO,iBACV,eACM;EACJ,IAAW;CACb,GACA,EAAE,MAAM,GAAK,CACf;CAEA,IAAM,IAAkC,kBAAO,IAAI,IAAe,CAAC;CAEnE,UAAsB;EACpB,EAAQ,wBAAQ,IAAI,IAAe;CACrC,CAAC;CAED,IAAM,oBAAe,IAAI,IAA0C,GAC7D,oBAAgB,IAAI,IAAgC,GAEpD,IAA4B;EAChC,EAAO,SAAS,MAAU;GACxB,IAAI,EAAM,SAAS,GAAM;GAEzB,IAAM,oBAAO,IAAI,IAAe;GAEhC,KAAK,IAAM,CAAC,GAAI,MAAU,OAAO,QAAQ,EAAM,OAAO,GACpD,EAAK,IAAI,GAAI,CAAU;GAGzB,EAAQ,QAAQ;EAClB,CAAC;EACD,EAAO,QAAQ,MAAU;GACvB,IAAI,EAAM,SAAS,GAAM;GAEzB,IAAM,IAAO,IAAI,IAAI,EAAQ,KAAK;GAGlC,AADA,EAAK,IAAI,EAAM,IAAI,EAAM,KAAU,GACnC,EAAQ,QAAQ;GAEhB,KAAK,IAAM,KAAW,GACpB,EAAQ,EAAM,IAAI,EAAM,KAAU;EAEtC,CAAC;EACD,EAAO,SAAS,MAAU;GACxB,IAAI,EAAM,SAAS,GAAM;GAEzB,IAAM,IAAO,IAAI,IAAI,EAAQ,KAAK;GAGlC,AADA,EAAK,OAAO,EAAM,EAAE,GACpB,EAAQ,QAAQ;GAEhB,KAAK,IAAM,KAAW,GACpB,EAAQ,EAAM,EAAE;EAEpB,CAAC;CACH;CAgEA,OAAO;EA7DL,IAAI,iBAAiB;GACnB,OAAO,EAAK;EACd;EAEA,UAAU;GACJ,QAGJ;IADA,IAAW,IACX,EAAK,MAAM;IAEX,KAAK,IAAM,KAAS,GAAQ,EAAM;IAKlC,AAHA,EAAO,SAAS,GAChB,EAAa,MAAM,GACnB,EAAc,MAAM,GACpB,IAAY;GAPD;EAQb;EAEA,IAAI,WAAW;GACb,OAAO;EACT;EAEA,OAAO,GAA4D;GACjE,IAAI,GACF,MAAM,IAAI,EAAmB,aAAa,EAAK,EAAE;GAKnD,OAFA,EAAa,IAAI,CAAO,SAEX,EAAa,OAAO,CAAO;EAC1C;EAEA,QAAQ,GAAkD;GACxD,IAAI,GACF,MAAM,IAAI,EAAmB,aAAa,EAAK,EAAE;GAKnD,OAFA,EAAc,IAAI,CAAO,SAEZ,EAAc,OAAO,CAAO;EAC3C;EAEA,IAAI,OAAO;GACT,OAAO;EACT;EAEA,IAAI,QAAQ;GACV,OAAO;EACT;EAEA,CAAC,OAAO,WAAW;GACjB,KAAK,QAAQ;EACf;EAEA,OAAO,GAAgB;GACrB,IAAI,GAAU,MAAM,IAAI,EAAmB,aAAa,EAAK,EAAE;GAE/D,EAAO,GAAM,CAAK;EACpB;CAGK;AACT"}
package/dist/pulse.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/ripple");var t=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},n=class extends t{url;constructor(e,t,n){super(e,n),this.url=t}},r=class extends t{event;constructor(e,t){super(`Timed out waiting for "${e}"`,t),this.event=e}},i=class extends t{constructor(e){super(`Aborted`,e)}},a=class extends t{constructor(e=`Pulse`,t){super(`${e} instance is disposed`,t)}},o=class extends t{raw;constructor(e,t,n){super(e,n),this.raw=t}};function s(e,...t){return t.length===0?e:t.reduce(c,e)}function c(e,t){if(e.aborted)return e;if(t.aborted)return t;let n=new AbortController,r=()=>n.abort(e.reason),i=()=>n.abort(t.reason);return e.addEventListener(`abort`,r,{once:!0}),t.addEventListener(`abort`,i,{once:!0}),n.signal.addEventListener(`abort`,()=>{e.removeEventListener(`abort`,r),t.removeEventListener(`abort`,i)},{once:!0}),n.signal}function l(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function u(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=setTimeout(n,e);t?.addEventListener(`abort`,()=>{clearTimeout(r),n()},{once:!0})})}function d(e,t=3e4){let n=Math.min(1e3*2**e,t);return Math.random()*n}function f(e,t,n,a){return new Promise((o,c)=>{let l=[t],u;if(n?.signal&&l.push(n.signal),n?.timeout!==void 0){let t=new AbortController;u=setTimeout(()=>{t.abort(new r(e))},n.timeout),l.push(t.signal)}let d=l.length===1?l[0]:s(l[0],...l.slice(1));if(d.aborted){clearTimeout(u),c(d.reason instanceof r?d.reason:new i);return}let f=()=>{},p=()=>{clearTimeout(u),f(),c(d.reason instanceof r?d.reason:new i)};d.addEventListener(`abort`,p,{once:!0}),f=a(e,e=>{clearTimeout(u),d.removeEventListener(`abort`,p),o(e)})})}function p(e,t,n,r,i){let a=l(r),o=new Set,s=a.signal.aborted;return a.signal.addEventListener(`abort`,()=>{s=!0},{once:!0}),{get disposalSignal(){return a.signal},dispose(){if(!s){s=!0,a.abort();for(let e of o)e();o.clear(),i?.()}},get disposed(){return s},get name(){return e},on(t,r){if(s)return`${String(t)}${e}`,()=>{};let i=n(e,t,r);return o.add(i),()=>{i(),o.delete(i)}},once(t,r){if(s)return`${String(t)}${e}`,()=>{};let i=()=>{};return i=n(e,t,e=>{o.delete(i),i(),r(e)}),o.add(i),()=>{o.delete(i),i()}},send(n,r){if(s){`${String(n)}${e}`;return}t(e,n,r)},[Symbol.dispose](){this.dispose()},wait(t,r){return f(t,a.signal,r,(t,r)=>n(e,t,r))}}}function m(e){return JSON.stringify(e)}function h(e){if(typeof e!=`string`)throw new o(`Expected string message data`,e);let t;try{t=JSON.parse(e)}catch{throw new o(`JSON parse error`,e)}if(typeof t!=`object`||!t||!(`type`in t)||typeof t.type!=`string`)throw new o(`Missing or invalid "type" field`,t);return t}function g(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function _(e,t){if(!g(e))return null;let n=e[t];return typeof n==`string`?n:null}function v(e){return _(e,`code`)!==null&&_(e,`message`)!==null}function y(e){return _(e,`room`)!==null}function b(e){return _(e,`room`)!==null}function x(e){return!g(e)||_(e,`event`)===null||!(`payload`in e)?!1:e.channel===void 0||typeof e.channel==`string`}function S(e){return g(e)&&typeof e.ts==`number`}function C(e){return g(e)&&_(e,`id`)!==null&&_(e,`room`)!==null&&`state`in e}function w(e){return _(e,`id`)!==null&&_(e,`room`)!==null}function T(e){return g(e)&&_(e,`room`)!==null&&g(e.members)}function E(e){return _(e,`channel`)!==null}function D(e){return _(e,`channel`)!==null}function O(e){let t=h(e);switch(t.type){case`error`:if(v(t))return{frame:t,kind:`known`};break;case`joined`:if(y(t))return{frame:t,kind:`known`};break;case`left`:if(b(t))return{frame:t,kind:`known`};break;case`message`:if(x(t))return{frame:t,kind:`known`};break;case`pong`:if(S(t))return{frame:t,kind:`known`};break;case`presence_join`:if(C(t))return{frame:t,kind:`known`};break;case`presence_leave`:if(w(t))return{frame:t,kind:`known`};break;case`presence_state`:if(T(t))return{frame:t,kind:`known`};break;case`subscribed`:if(E(t))return{frame:t,kind:`known`};break;case`unsubscribed`:if(D(t))return{frame:t,kind:`known`};break;default:return{kind:`unknown`,type:t.type}}throw new o(`Malformed "${t.type}" frame`,t)}var k=3e4,A=5e3;function j(e,t,n){if(!e)return{onPong(){},start(){},stop(){}};let r=e===!0?{}:e,i=r.interval??k,a=r.timeout??A,o,s,c=!1;function l(){o=setTimeout(()=>{t(m({ts:Date.now(),type:`ping`})),s=setTimeout(()=>{`${a}`,n()},a)},i)}return{onPong(){clearTimeout(s),s=void 0,c&&l()},start(){c=!0,clearTimeout(o),clearTimeout(s),s=void 0,l()},stop(){c=!1,clearTimeout(o),clearTimeout(s),o=void 0,s=void 0}}}function M(t,n,r,i,a){let o=l(i),s=o.signal.aborted;o.signal.addEventListener(`abort`,()=>{s=!0},{once:!0});let c=(0,e.signal)(new Map),u=new Set,d=new Set,f=[r.onState(e=>{if(e.room!==t)return;let n=new Map;for(let[t,r]of Object.entries(e.members))n.set(t,r);c.value=n}),r.onJoin(e=>{if(e.room!==t)return;let n=new Map(c.value);n.set(e.id,e.state),c.value=n;for(let t of u)t(e.id,e.state)}),r.onLeave(e=>{if(e.room!==t)return;let n=new Map(c.value);n.delete(e.id),c.value=n;for(let t of d)t(e.id)})];return{get disposalSignal(){return o.signal},dispose(){if(!s){s=!0,n(m({room:t,type:`leave`})),o.abort();for(let e of f)e();f.length=0,u.clear(),d.clear(),a?.()}},get disposed(){return s},onJoin(e){return s?(`${t}`,()=>{}):(u.add(e),()=>u.delete(e))},onLeave(e){return s?(`${t}`,()=>{}):(d.add(e),()=>d.delete(e))},get room(){return t},get state(){return c},[Symbol.dispose](){this.dispose()},update(e){s||n(m({room:t,state:e,type:`presence`}))}}}var N=5;function P(e){if(!e)return{attempt:async()=>({ok:!1}),reset(){}};let t=e===!0?{}:e,n=t.maxAttempts??N,r=typeof t.delay==`function`?t.delay:typeof t.delay==`number`?()=>t.delay:d,i=0;return{async attempt(e,t,a){if(i>=n)return{ok:!1};let o=r(i);if(i++,a?.(i),await u(o,t),t.aborted)return{ok:!1};try{return await e(),i=0,{ok:!0}}catch(e){return{error:e,ok:!1}}},reset(){i=0}}}var F=class{map=new Map;add(e,t,n){let r=this.map.get(e);r||(r=new Map,this.map.set(e,r));let i=r.get(t);return i||(i=new Set,r.set(t,i)),i.add(n),()=>{i.delete(n),i.size===0&&r.delete(t),r.size===0&&this.map.delete(e)}}dispatch(e,t,n){this.map.get(e)?.get(t)?.forEach(e=>e(n))}clear(){this.map.clear()}};function I(t,o={}){let c=new AbortController,l=(0,e.signal)(`connecting`),u=(0,e.signal)(new Set),d=new F,h=o.middleware??[],g=new Map,_=new Map,v=null,y=!1,b=!1,x=[],S=new Map,C=new Map,w=o.buffer,T=w?[]:null,E=typeof w==`object`&&w?w.maxSize??50:50,D=P(o.reconnect),k=j(o.heartbeat,e=>{v?.readyState===WebSocket.OPEN&&v.send(e)},()=>{v?.close(4e3,`heartbeat timeout`)});function A(e){v?.readyState===WebSocket.OPEN?v.send(e):T===null?`${l.value}`:(T.length>=E&&T.shift(),T.push(e))}function N(e,t,n){let r=0;function i(){if(r>=h.length){n();return}let a=h[r++];a(e,t,i)}i()}function I(){y||(v=new WebSocket(t,o.protocols),v.onopen=()=>{b=!1,l.value=`open`,D.reset(),k.start(),o.onOpen?.();let e=x.splice(0);for(let t of e)t.resolve();if(T&&T.length>0){let e=T.splice(0);for(let t of e)v.send(t)}for(let e of S.keys())v.send(m({channel:e,type:`subscribe`}))},v.onmessage=e=>{o.onMessage?.(e);let t;try{t=O(e.data)}catch(e){`${String(e)}`;return}try{R(t)}catch(e){`${String(e)}`}},v.onerror=()=>{o.onError&&o.onError(new n(`WebSocket error`,t));let e=x.splice(0);for(let r of e)r.reject(new n(`WebSocket error`,t))},v.onclose=e=>{k.stop(),o.onClose?.(e.code,e.reason);let r=x.splice(0);for(let e of r)e.reject(new n(`Connection closed before open`,t));if(b||y){l.value=`closed`;return}L()})}async function L(){l.value=`reconnecting`;let e=await D.attempt(()=>new Promise((e,t)=>{x.push({reject:t,resolve:e}),I()}),c.signal,o.onReconnect);!e.ok&&!y&&(e.error&&`${String(e.error)}`,l.value=`closed`)}function R(e){if(e.kind===`unknown`){`${e.type}`;return}let t=e.frame;switch(t.type){case`error`:`${t.code}${t.message}`;break;case`joined`:{let e=new Set(u.value);e.add(t.room),u.value=e;let n=g.get(t.room);if(n){g.delete(t.room);for(let e of n)e()}break}case`left`:{let e=new Set(u.value);e.delete(t.room),u.value=e;let n=_.get(t.room);if(n){_.delete(t.room);for(let e of n)e()}break}case`message`:d.dispatch(t.channel??null,t.event,t.payload);break;case`pong`:k.onPong();break;case`presence_join`:d.dispatch(null,t.type,t);break;case`presence_leave`:d.dispatch(null,t.type,t);break;case`presence_state`:d.dispatch(null,t.type,t)}}function z(e,t,n,o){return y?Promise.reject(new a):new Promise((a,l)=>{let u=[];o?.signal&&u.push(o.signal);let d;if(o?.timeout!==void 0){let e=new AbortController;d=setTimeout(()=>e.abort(new r(n)),o.timeout),u.push(e.signal)}let f=u.length===0?c.signal:s(c.signal,...u);if(f.aborted){clearTimeout(d),l(f.reason instanceof r?f.reason:new i);return}let p=()=>{},h=()=>{clearTimeout(d),f.removeEventListener(`abort`,p),a()};p=()=>{clearTimeout(d);let n=e.get(t);n?.delete(h),n?.size===0&&e.delete(t),l(f.reason instanceof r?f.reason:new i)};let g=e.get(t),_=!g;g||(g=new Set,e.set(t,g)),g.add(h),f.addEventListener(`abort`,p,{once:!0});let y=()=>A(m({room:t,type:n}));_&&(v?.readyState===WebSocket.OPEN?y():B.connect().then(y).catch(n=>{clearTimeout(d),f.removeEventListener(`abort`,p);let r=e.get(t);r?.delete(h),r?.size===0&&e.delete(t),l(n)}))})}let B={channel(e){let t=S.get(e);if(t)return t;y&&`${e}`;let n=p(e,(e,t,n)=>{N(t,n,()=>{A(m({channel:e,event:t,payload:n,type:`message`}))})},(e,t,n)=>d.add(e,t,n),c.signal,()=>{S.delete(e),A(m({channel:e,type:`unsubscribe`}))});return y||S.set(e,n),v?.readyState===WebSocket.OPEN&&v.send(m({channel:e,type:`subscribe`})),n},connect(){return y?Promise.reject(new a):v?.readyState===WebSocket.OPEN?Promise.resolve():new Promise((e,t)=>{x.push({reject:t,resolve:e}),(!v||v.readyState>WebSocket.OPEN)&&I()})},disconnect(e=1e3,t=``){b=!0,v?.close(e,t)},get disposalSignal(){return c.signal},dispose(){if(y)return;y=!0,b=!0,k.stop(),v?.close(1e3,`disposed`),v=null;let e=x.splice(0);for(let t of e)t.reject(new a);S.clear(),C.clear(),g.clear(),_.clear(),d.clear(),c.abort()},get disposed(){return y},join(e,t){return z(g,e,`join`,t)},leave(e,t){return z(_,e,`leave`,t)},on(e,t){return y?(`${String(e)}`,()=>{}):d.add(null,e,t)},once(e,t){if(y)return`${String(e)}`,()=>{};let n=()=>{};return n=d.add(null,e,e=>{n(),t(e)}),n},presence(e){let t=C.get(e);if(t)return t;let n=M(e,A,{onJoin:e=>d.add(null,`presence_join`,t=>e(t)),onLeave:e=>d.add(null,`presence_leave`,t=>e(t)),onState:e=>d.add(null,`presence_state`,t=>e(t))},c.signal,()=>{C.delete(e)});return y||C.set(e,n),y?`${e}`:B.join(e).catch(t=>{t instanceof i||t instanceof a||`${e}${String(t)}`}),n},get rooms(){return u},send(e,t){if(y){`${String(e)}`;return}N(e,t,()=>{A(m({event:e,payload:t,type:`message`}))})},get status(){return l},[Symbol.dispose](){this.dispose()},wait(e,t){return f(e,c.signal,t,(e,t)=>d.add(null,e,t))}};return o.lazy||I(),B}exports.PulseAbortError=i,exports.PulseConnectionError=n,exports.PulseDisposedError=a,exports.PulseError=t,exports.PulseProtocolError=o,exports.PulseTimeoutError=r,exports.createPulse=I;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/ripple");var t=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},n=class extends t{url;constructor(e,t,n){super(e,n),this.url=t}},r=class extends t{event;constructor(e,t){super(`Timed out waiting for "${e}"`,t),this.event=e}},i=class extends t{constructor(e){super(`Aborted`,e)}},a=class extends t{constructor(e=`Pulse`,t){super(`${e} instance is disposed`,t)}},o=class extends t{raw;constructor(e,t,n){super(e,n),this.raw=t}};function s(e,...t){return t.length===0?e:t.reduce(c,e)}function c(e,t){if(e.aborted)return e;if(t.aborted)return t;let n=new AbortController,r=()=>n.abort(e.reason),i=()=>n.abort(t.reason);return e.addEventListener(`abort`,r,{once:!0}),t.addEventListener(`abort`,i,{once:!0}),n.signal.addEventListener(`abort`,()=>{e.removeEventListener(`abort`,r),t.removeEventListener(`abort`,i)},{once:!0}),n.signal}function l(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function u(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=setTimeout(n,e);t?.addEventListener(`abort`,()=>{clearTimeout(r),n()},{once:!0})})}function d(e,t=3e4){let n=Math.min(1e3*2**e,t);return Math.random()*n}var f=5;function p(e,t,r,i){let o=r===!0?{}:r||void 0,s=o?o.maxAttempts??f:0,c=o?.delay,l=typeof c==`function`?c:typeof c==`number`?()=>c:d,p=!1,m=!1,h=!1,g,_,v,y=new Set;function b(e){i.onStatus(e)}function x(e){for(let t of y)t.reject(e);y.clear()}function S(){for(let e of y)e.resolve();y.clear()}function C(e,t){v?.socket===e&&(v.reject(t),v=void 0)}function w(){if(_?.readyState===WebSocket.OPEN)return Promise.resolve();if(v)return v.promise;let r=new WebSocket(e,t);_=r;let a,o,s=new Promise((e,t)=>{a=e,o=t});return v={promise:s,reject:o,resolve:a,socket:r},r.onopen=()=>{if(_!==r||!m){r.close(1e3,`stale connection`);return}v?.socket===r&&(v.resolve(),v=void 0),b(`open`),i.onOpen(),S()},r.onmessage=e=>{_===r&&i.onMessage(e)},r.onerror=()=>{if(_!==r)return;let t=new n(`WebSocket error`,e);i.onError(t),C(r,t),x(t)},r.onclose=()=>{if(_!==r)return;_=void 0,i.onClose();let t=new n(`Connection closed`,e);if(C(r,t),!m||p){b(`closed`);return}h||T()},s}async function T(){if(h||p||!m)return;h=!0;let t=new AbortController;g=t,b(`reconnecting`);let r;for(let i=0;i<s&&m&&!p&&(await u(l(i),t.signal),!(t.signal.aborted||!m||p));i++)try{await w(),h=!1;return}catch(t){r=t instanceof n?t:new n(`Reconnect failed`,e)}!t.signal.aborted&&m&&!p&&(m=!1,b(`closed`),i.onError(r??new n(`Reconnect budget exhausted`,e)),x(r??new n(`Reconnect budget exhausted`,e))),g===t&&(g=void 0),h=!1}function E(){g?.abort(),g=void 0,h=!1}return{connect(){if(p)return Promise.reject(new a);if(m=!0,_?.readyState===WebSocket.OPEN)return Promise.resolve();let e=new Promise((e,t)=>{y.add({reject:t,resolve:e})});return!v&&!h&&(b(`connecting`),w().catch(()=>{})),e},disconnect(t=1e3,r=``){m=!1,E(),x(new n(`Connection closed`,e)),b(`closed`),_?.close(t,r)},dispose(){p||(p=!0,m=!1,E(),x(new a),_?.close(1e3,`disposed`),_=void 0,v=void 0,b(`closed`))},forceReconnect(e,t){m&&_?.readyState===WebSocket.OPEN&&_.close(e,t)},get open(){return _?.readyState===WebSocket.OPEN},send(t){if(_?.readyState!==WebSocket.OPEN)throw new n(`Connection is not open`,e);_.send(t)}}}function m(e,t,n,a){return new Promise((o,c)=>{let l=[t],u;if(n?.signal&&l.push(n.signal),n?.timeout!==void 0){let t=new AbortController;u=setTimeout(()=>{t.abort(new r(e))},n.timeout),l.push(t.signal)}let d=l.length===1?l[0]:s(l[0],...l.slice(1));if(d.aborted){clearTimeout(u),c(d.reason instanceof r?d.reason:new i);return}let f=()=>{},p=()=>{clearTimeout(u),f(),c(d.reason instanceof r?d.reason:new i)};d.addEventListener(`abort`,p,{once:!0}),f=a(e,e=>{clearTimeout(u),d.removeEventListener(`abort`,p),o(e)})})}function h(e,t,n,r,i){let o=l(r),s=new Set,c=o.signal.aborted;return o.signal.addEventListener(`abort`,()=>{c=!0},{once:!0}),{get disposalSignal(){return o.signal},dispose(){if(!c){c=!0,o.abort();for(let e of s)e();s.clear(),i?.()}},get disposed(){return c},get name(){return e},on(t,r){if(c)throw new a(`Channel "${e}"`);let i=n(e,t,r);return s.add(i),()=>{i(),s.delete(i)}},once(t,r){if(c)throw new a(`Channel "${e}"`);let i=()=>{};return i=n(e,t,e=>{s.delete(i),i(),r(e)}),s.add(i),()=>{s.delete(i),i()}},send(n,r){if(c)throw new a(`Channel "${e}"`);t(e,n,r)},[Symbol.dispose](){this.dispose()},wait(t,r){return m(t,o.signal,r,(t,r)=>n(e,t,r))}}}function g(e){return JSON.stringify(e)}function _(e){if(typeof e!=`string`)throw new o(`Expected string message data`,e);let t;try{t=JSON.parse(e)}catch{throw new o(`JSON parse error`,e)}if(typeof t!=`object`||!t||!(`type`in t)||typeof t.type!=`string`)throw new o(`Missing or invalid "type" field`,t);return t}function v(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function y(e,t){if(!v(e))return null;let n=e[t];return typeof n==`string`?n:null}function b(e){return y(e,`code`)!==null&&y(e,`message`)!==null}function x(e){return y(e,`room`)!==null}function S(e){return y(e,`room`)!==null}function C(e){return!v(e)||y(e,`event`)===null||!(`payload`in e)?!1:e.channel===void 0||typeof e.channel==`string`}function w(e){return v(e)&&typeof e.ts==`number`}function T(e){return v(e)&&y(e,`id`)!==null&&y(e,`room`)!==null&&`state`in e}function E(e){return y(e,`id`)!==null&&y(e,`room`)!==null}function D(e){return v(e)&&y(e,`room`)!==null&&v(e.members)}function O(e){return y(e,`channel`)!==null}function k(e){return y(e,`channel`)!==null}function A(e){let t=_(e);switch(t.type){case`error`:if(b(t))return{frame:t,kind:`known`};break;case`joined`:if(x(t))return{frame:t,kind:`known`};break;case`left`:if(S(t))return{frame:t,kind:`known`};break;case`message`:if(C(t))return{frame:t,kind:`known`};break;case`pong`:if(w(t))return{frame:t,kind:`known`};break;case`presence_join`:if(T(t))return{frame:t,kind:`known`};break;case`presence_leave`:if(E(t))return{frame:t,kind:`known`};break;case`presence_state`:if(D(t))return{frame:t,kind:`known`};break;case`subscribed`:if(O(t))return{frame:t,kind:`known`};break;case`unsubscribed`:if(k(t))return{frame:t,kind:`known`};break;default:return{kind:`unknown`,type:t.type}}throw new o(`Malformed "${t.type}" frame`,t)}var j=3e4,M=5e3;function N(e,t,n){if(!e)return{onPong(){},start(){},stop(){}};let r=e===!0?{}:e,i=r.interval??j,a=r.timeout??M,o,s,c=!1;function l(){o=setTimeout(()=>{t(g({ts:Date.now(),type:`ping`})),s=setTimeout(()=>{n()},a)},i)}return{onPong(){clearTimeout(s),s=void 0,c&&l()},start(){c=!0,clearTimeout(o),clearTimeout(s),s=void 0,l()},stop(){c=!1,clearTimeout(o),clearTimeout(s),o=void 0,s=void 0}}}function P(t,n,r,i,o,s){let c=l(i),u=c.signal.aborted;c.signal.addEventListener(`abort`,()=>{u=!0},{once:!0});let d=(0,e.signal)(new Map);o?.(()=>{d.value=new Map});let f=new Set,p=new Set,m=[r.onState(e=>{if(e.room!==t)return;let n=new Map;for(let[t,r]of Object.entries(e.members))n.set(t,r);d.value=n}),r.onJoin(e=>{if(e.room!==t)return;let n=new Map(d.value);n.set(e.id,e.state),d.value=n;for(let t of f)t(e.id,e.state)}),r.onLeave(e=>{if(e.room!==t)return;let n=new Map(d.value);n.delete(e.id),d.value=n;for(let t of p)t(e.id)})];return{get disposalSignal(){return c.signal},dispose(){if(!u){u=!0,c.abort();for(let e of m)e();m.length=0,f.clear(),p.clear(),s?.()}},get disposed(){return u},onJoin(e){if(u)throw new a(`Presence "${t}"`);return f.add(e),()=>f.delete(e)},onLeave(e){if(u)throw new a(`Presence "${t}"`);return p.add(e),()=>p.delete(e)},get room(){return t},get state(){return d},[Symbol.dispose](){this.dispose()},update(e){if(u)throw new a(`Presence "${t}"`);n(t,e)}}}var F=class{map=new Map;add(e,t,n){let r=this.map.get(e);r||(r=new Map,this.map.set(e,r));let i=r.get(t);return i||(i=new Set,r.set(t,i)),i.add(n),()=>{i.delete(n),i.size===0&&r.delete(t),r.size===0&&this.map.delete(e)}}clear(){this.map.clear()}dispatch(e,t,n){for(let r of this.map.get(e)?.get(t)??[])r(n)}};function I(s,c={}){let l=new AbortController,u=(0,e.signal)(`closed`),d=(0,e.signal)(new Set),f=new F,_=new Map,v=new Map,y=new Set,b=new Map,x=new Map,S=new Map,C=!1;function w(e){c.onError?.(e)}function T(e){I.send(e)}function E(e){return y.has(e)||(v.get(e)??0)>0}function D(e,t){let n=new Set(d.value);t?n.add(e):n.delete(e),d.value=n}function O(){d.value=new Set;for(let e of x.values())for(let t of e)t()}function k(e,t=!0){let n=!1;for(let[t,r]of S){let i=r;for(;i;){i.operation===`join`&&y.delete(t);for(let t of i.callbacks)t.reject(e),n=!0;i=i.next}}S.clear(),n&&t&&w(e)}function j(){L.stop(),O(),k(new n(`Connection closed before room confirmation`,s))}function M(){for(let e of _.keys())T(g({channel:e,type:`subscribe`}));for(let e of new Set([...y,...v.keys()]))E(e)&&T(g({room:e,type:`join`}));for(let[e,t]of b)E(e)&&T(g({room:e,state:t,type:`presence`}))}let I=p(s,c.protocols,c.reconnect,{onClose(){j()},onError:w,onMessage(e){let t;try{t=A(e.data)}catch(t){w(t instanceof o?t:new o(`Failed to decode frame`,e.data));return}if(t.kind===`unknown`){w(new o(`Unknown frame type "${t.type}"`,e.data));return}try{z(t)}catch(e){w(new o(`Frame handler threw`,t.frame,{cause:e}))}},onOpen(){M(),L.start()},onStatus(e){u.value=e}}),L=N(c.heartbeat,e=>{I.open&&T(e)},()=>I.forceReconnect(4e3,`heartbeat timeout`));function R(e,r){let i=S.get(e);if(!i||i.operation!==r)return;for(let e of i.callbacks)e.resolve();let a=i.next;if(!a){S.delete(e);let i=r===`join`&&!E(e)?`leave`:r===`leave`&&E(e)?`join`:void 0;if(i){let r={callbacks:new Set,operation:i};S.set(e,r);try{T(g({room:e,type:i}))}catch(r){S.delete(e),i===`join`&&y.delete(e),w(r instanceof t?r:new n(`Failed to send room request`,s))}}return}S.set(e,a);try{T(g({room:e,type:a.operation}))}catch(r){S.delete(e),a.operation===`join`&&y.delete(e);for(let e of a.callbacks)e.reject(r instanceof t?r:new n(`Failed to send room request`,s))}}function z(e){let t=e.frame;switch(t.type){case`error`:w(new o(`Server error [${t.code}]: ${t.message}`,t));break;case`joined`:E(t.room)&&D(t.room,!0),R(t.room,`join`);break;case`left`:D(t.room,!1),R(t.room,`leave`);break;case`message`:f.dispatch(t.channel??null,t.event,t.payload);break;case`pong`:L.onPong();break;case`presence_join`:case`presence_leave`:case`presence_state`:f.dispatch(null,t.type,t)}}function B(e,o,c){if(C)return o===`join`&&y.delete(e),Promise.reject(new a);if(!I.open)return o===`join`&&y.delete(e),Promise.reject(new n(`Connection is not open`,s));let l=S.get(e);if(o===`join`&&d.value.has(e)&&!l||o===`leave`&&!d.value.has(e)&&!l)return Promise.resolve();let u=l;if(u){for(;u.next;)u=u.next;if(u.operation!==o){let e={callbacks:new Set,operation:o};u.next=e,u=e}}else u={callbacks:new Set,operation:o};return new Promise((a,d)=>{let f=c?.timeout===void 0?void 0:new AbortController,p=c?.signal&&f?AbortSignal.any([c.signal,f.signal]):c?.signal??f?.signal,m=c?.timeout===void 0?void 0:setTimeout(()=>f?.abort(),c.timeout),h={reject(e){clearTimeout(m),p?.removeEventListener(`abort`,_),d(e)},resolve(){clearTimeout(m),p?.removeEventListener(`abort`,_),a()}},_=()=>{u.callbacks.delete(h),u.callbacks.size===0&&o===`join`&&y.delete(e),h.reject(f?.signal.aborted?new r(o):new i)};if(p?.aborted){_();return}if(u.callbacks.add(h),p?.addEventListener(`abort`,_,{once:!0}),!l){S.set(e,u);try{T(g({room:e,type:o}))}catch(r){u.callbacks.delete(h),u.callbacks.size===0&&S.delete(e),h.reject(r instanceof t?r:new n(`Failed to send room request`,s))}}})}function V(e){let t=_.get(e)??0;_.set(e,t+1),t===0&&I.open&&T(g({channel:e,type:`subscribe`}))}function H(e){let t=_.get(e);if(t){if(t>1){_.set(e,t-1);return}_.delete(e),I.open&&T(g({channel:e,type:`unsubscribe`}))}}function U(e,t){let n=v.get(e)??0,r=x.get(e)??new Set;r.add(t),x.set(e,r),v.set(e,n+1),n===0&&I.open&&T(g({room:e,type:`join`}))}function W(e,t){x.get(e)?.delete(t),x.get(e)?.size===0&&x.delete(e);let n=v.get(e);if(n){if(n>1){v.set(e,n-1);return}v.delete(e),b.delete(e),!y.has(e)&&I.open&&T(g({room:e,type:`leave`}))}}return{channel(e){if(C)throw new a;return V(e),h(e,(e,t,n)=>{let r=c.transform?c.transform({channel:e,event:t,payload:n}):{channel:e,event:t,payload:n};r!==null&&T(g({...r,type:`message`}))},(e,t,n)=>f.add(e,t,n),l.signal,()=>H(e))},connect(){return C?Promise.reject(new a):I.connect()},disconnect(e=1e3,t=``){C||(j(),I.disconnect(e,t))},get disposalSignal(){return l.signal},dispose(){C||(C=!0,L.stop(),O(),k(new a,!1),I.dispose(),_.clear(),v.clear(),y.clear(),b.clear(),x.clear(),S.clear(),f.clear(),l.abort())},get disposed(){return C},join(e,t){return y.add(e),B(e,`join`,t)},leave(e,t){return y.delete(e),E(e)?Promise.resolve():B(e,`leave`,t)},on(e,t){if(C)throw new a;return f.add(null,e,t)},once(e,t){if(C)throw new a;let n=()=>{};return n=f.add(null,e,e=>{n(),t(e)}),n},presence(e){if(C)throw new a;let t,n=P(e,(e,t)=>{T(g({room:e,state:t,type:`presence`})),b.set(e,t)},{onJoin:e=>f.add(null,`presence_join`,t=>e(t)),onLeave:e=>f.add(null,`presence_leave`,t=>e(t)),onState:e=>f.add(null,`presence_state`,t=>e(t))},l.signal,e=>{t=e},()=>i()),r=t;if(!r)throw Error(`Presence reset callback was not initialized`);let i=()=>W(e,r);return U(e,r),n},get rooms(){return d},send(e,t){if(C)throw new a;let n=c.transform?c.transform({event:e,payload:t}):{event:e,payload:t};n!==null&&T(g({...n,type:`message`}))},get status(){return u},[Symbol.dispose](){this.dispose()},wait(e,t){return m(e,l.signal,t,(e,t)=>f.add(null,e,t))}}}exports.PulseAbortError=i,exports.PulseConnectionError=n,exports.PulseDisposedError=a,exports.PulseError=t,exports.PulseProtocolError=o,exports.PulseTimeoutError=r,exports.createPulse=I;
2
2
  //# sourceMappingURL=pulse.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pulse.cjs","names":[],"sources":["../src/errors.ts","../src/_dev.ts","../src/_utils.ts","../src/_wait.ts","../src/channel.ts","../src/protocol.ts","../src/heartbeat.ts","../src/presence.ts","../src/reconnect.ts","../src/pulse.ts"],"sourcesContent":["/**\n * Base class for all pulse errors.\n * Use `instanceof PulseError` to catch any pulse-originated error in one branch.\n */\nexport class PulseError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is PulseError {\n return err instanceof PulseError;\n }\n}\n\n/**\n * Thrown when a WebSocket connection cannot be established or is lost\n * without triggering auto-reconnect (budget exhausted, or reconnect disabled).\n */\nexport class PulseConnectionError extends PulseError {\n readonly url: string;\n\n constructor(message: string, url: string, opts?: ErrorOptions) {\n super(message, opts);\n this.url = url;\n }\n}\n\n/**\n * Thrown when `wait()` or `join()`/`leave()` times out before receiving a\n * server response, or when an AbortSignal fires before the event arrives.\n */\nexport class PulseTimeoutError extends PulseError {\n readonly event: string;\n\n constructor(event: string, opts?: ErrorOptions) {\n super(`Timed out waiting for \"${event}\"`, opts);\n this.event = event;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires.\n */\nexport class PulseAbortError extends PulseError {\n constructor(opts?: ErrorOptions) {\n super('Aborted', opts);\n }\n}\n\n/**\n * Thrown when a method is called on a disposed Pulse instance or channel.\n */\nexport class PulseDisposedError extends PulseError {\n constructor(target = 'Pulse', opts?: ErrorOptions) {\n super(`${target} instance is disposed`, opts);\n }\n}\n\n/**\n * Thrown when the server sends a frame that cannot be parsed or violates\n * the wire protocol schema.\n */\nexport class PulseProtocolError extends PulseError {\n readonly raw: unknown;\n\n constructor(message: string, raw?: unknown, opts?: ErrorOptions) {\n super(message, opts);\n this.raw = raw;\n }\n}\n","const isDev = !(globalThis as { __PULSE_PROD__?: boolean }).__PULSE_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/pulse] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n","/**\n * Returns a signal that aborts as soon as any of the provided signals abort.\n * With a single argument, returns it directly (no allocation).\n * Cleanup listeners are registered to prevent leaks.\n *\n * @internal\n */\nexport function combineSignals(first: AbortSignal, ...rest: AbortSignal[]): AbortSignal {\n if (rest.length === 0) return first;\n\n return rest.reduce(mergeTwo, first);\n}\n\nfunction mergeTwo(a: AbortSignal, b: AbortSignal): AbortSignal {\n if (a.aborted) return a;\n\n if (b.aborted) return b;\n\n const ctrl = new AbortController();\n const onA = (): void => ctrl.abort(a.reason);\n const onB = (): void => ctrl.abort(b.reason);\n\n a.addEventListener('abort', onA, { once: true });\n b.addEventListener('abort', onB, { once: true });\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n a.removeEventListener('abort', onA);\n b.removeEventListener('abort', onB);\n },\n { once: true },\n );\n\n return ctrl.signal;\n}\n\n/**\n * Create a child `AbortController` that aborts as soon as `parent` does.\n * Handles the case where `parent` is already aborted at call time — a plain\n * `addEventListener('abort', ...)` would miss that, since the event already fired.\n *\n * @internal\n */\nexport function deriveAbortController(parent: AbortSignal): AbortController {\n const ctrl = new AbortController();\n\n if (parent.aborted) {\n ctrl.abort(parent.reason);\n } else {\n parent.addEventListener('abort', () => ctrl.abort(parent.reason), { once: true });\n }\n\n return ctrl;\n}\n\n/**\n * Sleep for `ms` milliseconds, aborting early when the signal fires.\n * Resolves (does not reject) on abort — callers check `signal.aborted`.\n *\n * @internal\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n\n return;\n }\n\n const id = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(id);\n resolve();\n },\n { once: true },\n );\n });\n}\n\n/**\n * Full-jitter exponential backoff in ms, capped at `maxMs`.\n * `attempt` is zero-based.\n *\n * @internal\n */\nexport function defaultReconnectDelay(attempt: number, maxMs = 30_000): number {\n const base = Math.min(1000 * Math.pow(2, attempt), maxMs);\n\n return Math.random() * base;\n}\n","import type { Unsubscribe } from './types';\n\nimport { combineSignals } from './_utils';\nimport { PulseAbortError, PulseTimeoutError } from './errors';\n\n/**\n * Shared implementation for `pulse.wait()` and `channel.wait()`.\n * Resolves on the next event dispatched via `subscribe`, or rejects when\n * the combined signal aborts or the optional timeout elapses.\n *\n * @internal\n */\nexport function createWaitPromise<T>(\n event: string,\n baseSignal: AbortSignal,\n opts: { signal?: AbortSignal; timeout?: number } | undefined,\n subscribe: (event: string, handler: (payload: unknown) => void) => Unsubscribe,\n): Promise<T> {\n return new Promise((resolve, reject) => {\n const signals: AbortSignal[] = [baseSignal];\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if (opts?.signal) signals.push(opts.signal);\n\n if (opts?.timeout !== undefined) {\n const timeoutCtrl = new AbortController();\n\n timeoutId = setTimeout(() => {\n timeoutCtrl.abort(new PulseTimeoutError(event));\n }, opts.timeout);\n signals.push(timeoutCtrl.signal);\n }\n\n const combined = signals.length === 1 ? signals[0]! : combineSignals(signals[0]!, ...signals.slice(1));\n\n if (combined.aborted) {\n clearTimeout(timeoutId);\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n\n return;\n }\n\n let unsub: Unsubscribe = () => {};\n\n const onAbort = (): void => {\n clearTimeout(timeoutId);\n unsub();\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n };\n\n combined.addEventListener('abort', onAbort, { once: true });\n\n unsub = subscribe(event, (payload) => {\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n resolve(payload as T);\n });\n });\n}\n","import type { EventKey, MessageMap, PulseChannel, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { deriveAbortController } from './_utils';\nimport { createWaitPromise } from './_wait';\n\ntype SendFn = (channel: string, event: string, payload: unknown) => void;\ntype SubscribeFn = (channel: string, event: string, handler: (payload: unknown) => void) => () => void;\n\n/**\n * Create a typed channel object that scopes messaging to a named namespace.\n * @internal\n */\nexport function createChannel<TServer extends MessageMap, TClient extends MessageMap>(\n name: string,\n send: SendFn,\n subscribe: SubscribeFn,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PulseChannel<TServer, TClient> {\n const ctrl = deriveAbortController(disposalSignal);\n const tracked = new Set<() => void>();\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const channel: PulseChannel<TServer, TClient> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n ctrl.abort();\n\n for (const unsub of tracked) unsub();\n\n tracked.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n get name() {\n return name;\n },\n\n on<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`on('${String(event)}') called on a disposed channel '${name}' — listener ignored`);\n\n return () => {};\n }\n\n const unsub = subscribe(name, event, handler as (payload: unknown) => void);\n\n tracked.add(unsub);\n\n return () => {\n unsub();\n tracked.delete(unsub);\n };\n },\n\n once<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`once('${String(event)}') called on a disposed channel '${name}' — listener ignored`);\n\n return () => {};\n }\n\n let unsub: Unsubscribe = () => {};\n\n const wrapped = (payload: unknown): void => {\n tracked.delete(unsub);\n unsub();\n (handler as (payload: unknown) => void)(payload);\n };\n\n unsub = subscribe(name, event, wrapped);\n tracked.add(unsub);\n\n return () => {\n tracked.delete(unsub);\n unsub();\n };\n },\n\n send<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void {\n if (disposed) {\n warn(`send('${String(event)}') called on a disposed channel '${name}' — message dropped`);\n\n return;\n }\n\n send(name, event, payload);\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n wait<K extends EventKey<TServer>>(\n event: K,\n opts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<TServer[K]> {\n return createWaitPromise<TServer[K]>(event as string, ctrl.signal, opts, (ev, handler) =>\n subscribe(name, ev, handler),\n );\n },\n };\n\n return channel;\n}\n","import { PulseProtocolError } from './errors';\n\n// ─── Outgoing frames (client → server) ────────────────────────────────────────\n\nexport type OutMessageFrame = { channel?: string; event: string; payload: unknown; type: 'message' };\nexport type OutSubscribeFrame = { channel: string; type: 'subscribe' };\nexport type OutUnsubscribeFrame = { channel: string; type: 'unsubscribe' };\nexport type OutJoinFrame = { room: string; type: 'join' };\nexport type OutLeaveFrame = { room: string; type: 'leave' };\nexport type OutPresenceFrame = { room: string; state: unknown; type: 'presence' };\nexport type OutPingFrame = { ts: number; type: 'ping' };\n\nexport type OutFrame =\n | OutJoinFrame\n | OutLeaveFrame\n | OutMessageFrame\n | OutPingFrame\n | OutPresenceFrame\n | OutSubscribeFrame\n | OutUnsubscribeFrame;\n\n// ─── Incoming frames (server → client) ────────────────────────────────────────\n\nexport type InMessageFrame = { channel?: string; event: string; payload: unknown; type: 'message' };\nexport type InSubscribedFrame = { channel: string; type: 'subscribed' };\nexport type InUnsubscribedFrame = { channel: string; type: 'unsubscribed' };\nexport type InJoinedFrame = { room: string; type: 'joined' };\nexport type InLeftFrame = { room: string; type: 'left' };\nexport type InPresenceStateFrame = { members: Record<string, unknown>; room: string; type: 'presence_state' };\nexport type InPresenceJoinFrame = { id: string; room: string; state: unknown; type: 'presence_join' };\nexport type InPresenceLeaveFrame = { id: string; room: string; type: 'presence_leave' };\nexport type InPongFrame = { ts: number; type: 'pong' };\nexport type InErrorFrame = { code: string; message: string; type: 'error' };\n\nexport type InFrame =\n | InErrorFrame\n | InJoinedFrame\n | InLeftFrame\n | InMessageFrame\n | InPongFrame\n | InPresenceJoinFrame\n | InPresenceLeaveFrame\n | InPresenceStateFrame\n | InSubscribedFrame\n | InUnsubscribedFrame;\n\ntype ParsedFrame = { type: string } & Record<string, unknown>;\nexport type DecodedInFrame = { frame: InFrame; kind: 'known' } | { kind: 'unknown'; type: string };\n\n// ─── Codec ────────────────────────────────────────────────────────────────────\n\n/**\n * Encode an outgoing frame to a JSON string.\n * @internal\n */\nexport function encode(frame: OutFrame): string {\n return JSON.stringify(frame);\n}\n\n/**\n * Parse a raw MessageEvent data string and ensure it has a string `type` field.\n * Throws `PulseProtocolError` if the data is not valid JSON or missing the `type` field.\n * @internal\n */\nexport function decode(raw: unknown): ParsedFrame {\n if (typeof raw !== 'string') {\n throw new PulseProtocolError('Expected string message data', raw);\n }\n\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(raw);\n } catch (cause) {\n throw new PulseProtocolError('JSON parse error', raw);\n }\n\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n !('type' in parsed) ||\n typeof (parsed as { type: unknown }).type !== 'string'\n ) {\n throw new PulseProtocolError('Missing or invalid \"type\" field', parsed);\n }\n\n return parsed as ParsedFrame;\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction getStringProp(frame: unknown, key: string): string | null {\n if (!isObjectRecord(frame)) return null;\n\n const value = frame[key];\n\n return typeof value === 'string' ? value : null;\n}\n\nexport function isErrorFrame(frame: unknown): frame is InErrorFrame {\n return getStringProp(frame, 'code') !== null && getStringProp(frame, 'message') !== null;\n}\n\nexport function isJoinedFrame(frame: unknown): frame is InJoinedFrame {\n return getStringProp(frame, 'room') !== null;\n}\n\nexport function isLeftFrame(frame: unknown): frame is InLeftFrame {\n return getStringProp(frame, 'room') !== null;\n}\n\nexport function isMessageFrame(frame: unknown): frame is InMessageFrame {\n if (!isObjectRecord(frame) || getStringProp(frame, 'event') === null || !('payload' in frame)) return false;\n\n return frame.channel === undefined || typeof frame.channel === 'string';\n}\n\nexport function isPongFrame(frame: unknown): frame is InPongFrame {\n return isObjectRecord(frame) && typeof frame.ts === 'number';\n}\n\nexport function isPresenceJoinFrame(frame: unknown): frame is InPresenceJoinFrame {\n return (\n isObjectRecord(frame) &&\n getStringProp(frame, 'id') !== null &&\n getStringProp(frame, 'room') !== null &&\n 'state' in frame\n );\n}\n\nexport function isPresenceLeaveFrame(frame: unknown): frame is InPresenceLeaveFrame {\n return getStringProp(frame, 'id') !== null && getStringProp(frame, 'room') !== null;\n}\n\nexport function isPresenceStateFrame(frame: unknown): frame is InPresenceStateFrame {\n return isObjectRecord(frame) && getStringProp(frame, 'room') !== null && isObjectRecord(frame.members);\n}\n\nfunction isSubscribedFrame(frame: unknown): frame is InSubscribedFrame {\n return getStringProp(frame, 'channel') !== null;\n}\n\nfunction isUnsubscribedFrame(frame: unknown): frame is InUnsubscribedFrame {\n return getStringProp(frame, 'channel') !== null;\n}\n\n/**\n * Parse and validate known incoming frame shapes.\n * Unknown frame `type` values are passed through so callers can warn/drop them.\n * @internal\n */\nexport function decodeValidated(raw: unknown): DecodedInFrame {\n const frame = decode(raw) as ParsedFrame;\n\n switch (frame.type) {\n case 'error':\n if (isErrorFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'joined':\n if (isJoinedFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'left':\n if (isLeftFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'message':\n if (isMessageFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'pong':\n if (isPongFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'presence_join':\n if (isPresenceJoinFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'presence_leave':\n if (isPresenceLeaveFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'presence_state':\n if (isPresenceStateFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'subscribed':\n if (isSubscribedFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'unsubscribed':\n if (isUnsubscribedFrame(frame)) return { frame, kind: 'known' };\n\n break;\n default:\n return { kind: 'unknown', type: frame.type };\n }\n\n throw new PulseProtocolError(`Malformed \"${frame.type}\" frame`, frame);\n}\n","import type { HeartbeatOptions } from './types';\n\nimport { warn } from './_dev';\nimport { encode } from './protocol';\n\nconst DEFAULT_INTERVAL = 30_000;\nconst DEFAULT_TIMEOUT = 5_000;\n\nexport type HeartbeatHandle = {\n /** Called when a `pong` frame is received. Resets the timeout timer. */\n onPong(): void;\n /** Start the heartbeat loop. */\n start(): void;\n /** Stop the heartbeat loop and cancel any pending timers. */\n stop(): void;\n};\n\n/**\n * Create a heartbeat manager that sends periodic pings and expects pong replies.\n * Calls `onDead` when a pong is not received within the timeout window.\n * @internal\n */\nexport function createHeartbeat(\n opts: boolean | HeartbeatOptions | undefined,\n send: (frame: string) => void,\n onDead: () => void,\n): HeartbeatHandle {\n if (!opts) {\n return { onPong() {}, start() {}, stop() {} };\n }\n\n const options: HeartbeatOptions = opts === true ? {} : opts;\n const interval = options.interval ?? DEFAULT_INTERVAL;\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n\n let pingTimer: ReturnType<typeof setTimeout> | undefined;\n let pongTimer: ReturnType<typeof setTimeout> | undefined;\n let running = false;\n\n function schedulePing(): void {\n pingTimer = setTimeout(() => {\n const ts = Date.now();\n\n send(encode({ ts, type: 'ping' }));\n\n pongTimer = setTimeout(() => {\n warn(`Heartbeat timed out after ${timeout}ms — treating connection as dead`);\n onDead();\n }, timeout);\n }, interval);\n }\n\n return {\n onPong() {\n clearTimeout(pongTimer);\n pongTimer = undefined;\n\n if (running) schedulePing();\n },\n\n start() {\n running = true;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pongTimer = undefined;\n schedulePing();\n },\n\n stop() {\n running = false;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pingTimer = undefined;\n pongTimer = undefined;\n },\n };\n}\n","import { type Signal, signal } from '@vielzeug/ripple';\n\nimport type { PresenceChannel, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { deriveAbortController } from './_utils';\nimport { type InPresenceJoinFrame, type InPresenceLeaveFrame, type InPresenceStateFrame, encode } from './protocol';\n\ntype RawSendFn = (frame: string) => void;\n\nexport type PresenceEvents = {\n onJoin(handler: (frame: InPresenceJoinFrame) => void): Unsubscribe;\n onLeave(handler: (frame: InPresenceLeaveFrame) => void): Unsubscribe;\n onState(handler: (frame: InPresenceStateFrame) => void): Unsubscribe;\n};\n\n/**\n * Create a presence channel that reactively tracks room member states.\n * @internal\n */\nexport function createPresence<T>(\n room: string,\n rawSend: RawSendFn,\n events: PresenceEvents,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PresenceChannel<T> {\n const ctrl = deriveAbortController(disposalSignal);\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const members: Signal<Map<string, T>> = signal(new Map<string, T>());\n const joinHandlers = new Set<(memberId: string, state: T) => void>();\n const leaveHandlers = new Set<(memberId: string) => void>();\n\n const unsubs: Array<() => void> = [\n events.onState((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map<string, T>();\n\n for (const [id, state] of Object.entries(frame.members)) {\n next.set(id, state as T);\n }\n\n members.value = next;\n }),\n events.onJoin((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.set(frame.id, frame.state as T);\n members.value = next;\n\n for (const handler of joinHandlers) {\n handler(frame.id, frame.state as T);\n }\n }),\n events.onLeave((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.delete(frame.id);\n members.value = next;\n\n for (const handler of leaveHandlers) {\n handler(frame.id);\n }\n }),\n ];\n\n const presence: PresenceChannel<T> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n rawSend(encode({ room, type: 'leave' }));\n ctrl.abort();\n\n for (const unsub of unsubs) unsub();\n\n unsubs.length = 0;\n joinHandlers.clear();\n leaveHandlers.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n onJoin(handler: (memberId: string, state: T) => void): Unsubscribe {\n if (disposed) {\n warn(`onJoin() called on a disposed presence channel '${room}' — listener ignored`);\n\n return () => {};\n }\n\n joinHandlers.add(handler);\n\n return () => joinHandlers.delete(handler);\n },\n\n onLeave(handler: (memberId: string) => void): Unsubscribe {\n if (disposed) {\n warn(`onLeave() called on a disposed presence channel '${room}' — listener ignored`);\n\n return () => {};\n }\n\n leaveHandlers.add(handler);\n\n return () => leaveHandlers.delete(handler);\n },\n\n get room() {\n return room;\n },\n\n get state() {\n return members;\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n update(state: T): void {\n if (disposed) return;\n\n rawSend(encode({ room, state, type: 'presence' }));\n },\n };\n\n return presence;\n}\n","import type { ReconnectOptions } from './types';\n\nimport { defaultReconnectDelay, sleep } from './_utils';\n\nconst DEFAULT_MAX_ATTEMPTS = 5;\n\nexport type ReconnectHandle = {\n /**\n * Attempt a reconnect. Waits for the computed delay then calls `connect`.\n * Returns outcome details including success and, when available, the failure reason.\n */\n attempt(\n connect: () => Promise<void>,\n signal: AbortSignal,\n onReconnect?: (attempt: number) => void,\n ): Promise<{ error?: unknown; ok: boolean }>;\n /** Cancel any pending reconnect attempt and reset the counter. */\n reset(): void;\n};\n\n/**\n * Create a stateful reconnect manager.\n * @internal\n */\nexport function createReconnect(opts: boolean | ReconnectOptions | undefined): ReconnectHandle {\n if (!opts) {\n return {\n attempt: async () => ({ ok: false }),\n reset() {},\n };\n }\n\n const options: ReconnectOptions = opts === true ? {} : opts;\n const maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const delayFn: (attempt: number) => number =\n typeof options.delay === 'function'\n ? options.delay\n : typeof options.delay === 'number'\n ? () => options.delay as number\n : defaultReconnectDelay;\n\n let attempts = 0;\n\n return {\n async attempt(\n connect: () => Promise<void>,\n signal: AbortSignal,\n onReconnect?: (attempt: number) => void,\n ): Promise<{ error?: unknown; ok: boolean }> {\n if (attempts >= maxAttempts) return { ok: false };\n\n const delay = delayFn(attempts);\n\n attempts++;\n onReconnect?.(attempts);\n\n await sleep(delay, signal);\n\n if (signal.aborted) return { ok: false };\n\n try {\n await connect();\n\n attempts = 0;\n\n return { ok: true };\n } catch (error) {\n return { error, ok: false };\n }\n },\n\n reset() {\n attempts = 0;\n },\n };\n}\n","import { signal } from '@vielzeug/ripple';\n\nimport type {\n EventKey,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n Unsubscribe,\n} from './types';\n\nimport { warn } from './_dev';\nimport { combineSignals } from './_utils';\nimport { createWaitPromise } from './_wait';\nimport { createChannel } from './channel';\nimport { PulseAbortError, PulseConnectionError, PulseDisposedError, PulseTimeoutError } from './errors';\nimport { createHeartbeat } from './heartbeat';\nimport { createPresence } from './presence';\nimport {\n type DecodedInFrame,\n type InPresenceJoinFrame,\n type InPresenceLeaveFrame,\n type InPresenceStateFrame,\n decodeValidated,\n encode,\n} from './protocol';\nimport { createReconnect } from './reconnect';\n\n// ─── Internal routing map ─────────────────────────────────────────────────────\n\ntype Handler = (payload: unknown) => void;\n\n/**\n * A two-level listener map: channel (null = root) → event → Set<handler>.\n * @internal\n */\nclass ListenerMap {\n private readonly map = new Map<string | null, Map<string, Set<Handler>>>();\n\n add(channel: string | null, event: string, handler: Handler): () => void {\n let events = this.map.get(channel);\n\n if (!events) {\n events = new Map();\n this.map.set(channel, events);\n }\n\n let handlers = events.get(event);\n\n if (!handlers) {\n handlers = new Set();\n events.set(event, handlers);\n }\n\n handlers.add(handler);\n\n return () => {\n handlers.delete(handler);\n\n if (handlers.size === 0) events.delete(event);\n\n if (events.size === 0) this.map.delete(channel);\n };\n }\n\n dispatch(channel: string | null, event: string, payload: unknown): void {\n this.map\n .get(channel)\n ?.get(event)\n ?.forEach((h) => h(payload));\n }\n\n clear(): void {\n this.map.clear();\n }\n}\n\n// ─── createPulse ──────────────────────────────────────────────────────────────\n\n/**\n * Create a managed WebSocket connection with typed messaging, channels,\n * rooms, and presence.\n *\n * @example\n * ```ts\n * const pulse = createPulse<ServerEvents, ClientEvents>('wss://api.example.com/ws', {\n * reconnect: true,\n * heartbeat: true,\n * });\n * pulse.on('message', (data) => console.log(data));\n * ```\n */\nexport function createPulse<TServer extends MessageMap = MessageMap, TClient extends MessageMap = MessageMap>(\n url: string,\n opts: PulseOptions = {},\n): Pulse<TServer, TClient> {\n // ── State ──────────────────────────────────────────────────────────────────\n\n const disposalCtrl = new AbortController();\n const status = signal<PulseStatus>('connecting');\n const rooms = signal<ReadonlySet<string>>(new Set());\n const listeners = new ListenerMap();\n const middleware: readonly Middleware[] = opts.middleware ?? [];\n\n const pendingJoins = new Map<string, Set<() => void>>();\n const pendingLeaves = new Map<string, Set<() => void>>();\n\n let ws: WebSocket | null = null;\n let disposed = false;\n let intentionalClose = false;\n\n const pendingOpens: Array<{ reject: (e: Error) => void; resolve: () => void }> = [];\n\n // Memoized channel cache — same name returns the same object\n const channelCache = new Map<string, PulseChannel<MessageMap, MessageMap>>();\n // Memoized presence cache — same room returns the same object\n const presenceCache = new Map<string, PresenceChannel<unknown>>();\n\n // Message buffer (null = disabled)\n const bufferCfg = opts.buffer;\n const buffer: string[] | null = bufferCfg ? [] : null;\n const bufferMaxSize: number = typeof bufferCfg === 'object' && bufferCfg !== null ? (bufferCfg.maxSize ?? 50) : 50;\n\n // ── Reconnect / Heartbeat ──────────────────────────────────────────────────\n\n const reconnect = createReconnect(opts.reconnect);\n\n const heartbeat = createHeartbeat(\n opts.heartbeat,\n (frame) => {\n if (ws?.readyState === WebSocket.OPEN) ws.send(frame);\n },\n () => {\n warn('Heartbeat dead — closing socket for reconnect');\n ws?.close(4000, 'heartbeat timeout');\n },\n );\n\n // ── Raw send helper ────────────────────────────────────────────────────────\n\n function rawSend(frame: string): void {\n if (ws?.readyState === WebSocket.OPEN) {\n ws.send(frame);\n } else if (buffer !== null) {\n if (buffer.length >= bufferMaxSize) buffer.shift();\n\n buffer.push(frame);\n } else {\n warn(`send() called while connection is ${status.value} — message dropped`);\n }\n }\n\n // ── Middleware pipeline ────────────────────────────────────────────────────\n\n function runMiddleware(event: string, payload: unknown, final: () => void): void {\n let idx = 0;\n\n function next(): void {\n if (idx >= middleware.length) {\n final();\n\n return;\n }\n\n const mw = middleware[idx++]!;\n\n mw(event, payload, next);\n }\n\n next();\n }\n\n // ── WebSocket lifecycle ────────────────────────────────────────────────────\n\n function openSocket(): void {\n if (disposed) return;\n\n ws = new WebSocket(url, opts.protocols);\n\n ws.onopen = (): void => {\n intentionalClose = false;\n status.value = 'open';\n reconnect.reset();\n heartbeat.start();\n opts.onOpen?.();\n\n const pending = pendingOpens.splice(0);\n\n for (const p of pending) p.resolve();\n\n if (buffer && buffer.length > 0) {\n const queued = buffer.splice(0);\n\n for (const f of queued) ws!.send(f);\n }\n\n for (const name of channelCache.keys()) {\n ws!.send(encode({ channel: name, type: 'subscribe' }));\n }\n };\n\n ws.onmessage = (ev: MessageEvent): void => {\n opts.onMessage?.(ev);\n\n let frame: DecodedInFrame;\n\n try {\n frame = decodeValidated(ev.data);\n } catch (err) {\n warn(`Protocol error: ${String(err)}`);\n\n return;\n }\n\n try {\n handleFrame(frame);\n } catch (err) {\n warn(`Frame handling error: ${String(err)}`);\n }\n };\n\n ws.onerror = (): void => {\n if (opts.onError) {\n opts.onError(new PulseConnectionError('WebSocket error', url));\n } else {\n warn('WebSocket error — pass onError to observe it (see onClose for recovery logic)');\n }\n\n const pending = pendingOpens.splice(0);\n\n for (const p of pending) p.reject(new PulseConnectionError('WebSocket error', url));\n };\n\n ws.onclose = (ev: CloseEvent): void => {\n heartbeat.stop();\n opts.onClose?.(ev.code, ev.reason);\n\n const pending = pendingOpens.splice(0);\n\n for (const p of pending) p.reject(new PulseConnectionError('Connection closed before open', url));\n\n if (intentionalClose || disposed) {\n status.value = 'closed';\n\n return;\n }\n\n handleUnexpectedClose();\n };\n }\n\n async function handleUnexpectedClose(): Promise<void> {\n status.value = 'reconnecting';\n\n const result = await reconnect.attempt(\n () =>\n new Promise<void>((resolve, reject) => {\n pendingOpens.push({ reject, resolve });\n openSocket();\n }),\n disposalCtrl.signal,\n opts.onReconnect,\n );\n\n if (!result.ok && !disposed) {\n if (result.error) {\n warn(`Reconnect attempt failed: ${String(result.error)}`);\n }\n\n warn('Reconnect budget exhausted — connection permanently closed');\n status.value = 'closed';\n }\n }\n\n // ── Frame routing ──────────────────────────────────────────────────────────\n\n function handleFrame(decoded: DecodedInFrame): void {\n if (decoded.kind === 'unknown') {\n warn(`Received frame with an unrecognized type: ${decoded.type}`);\n\n return;\n }\n\n const frame = decoded.frame;\n\n switch (frame.type) {\n case 'error':\n warn(`Server error [${frame.code}]: ${frame.message}`);\n\n break;\n\n case 'joined': {\n const next = new Set(rooms.value);\n\n next.add(frame.room);\n rooms.value = next;\n\n const joinCbs = pendingJoins.get(frame.room);\n\n if (joinCbs) {\n pendingJoins.delete(frame.room);\n\n for (const cb of joinCbs) cb();\n }\n\n break;\n }\n\n case 'left': {\n const next = new Set(rooms.value);\n\n next.delete(frame.room);\n rooms.value = next;\n\n const leaveCbs = pendingLeaves.get(frame.room);\n\n if (leaveCbs) {\n pendingLeaves.delete(frame.room);\n\n for (const cb of leaveCbs) cb();\n }\n\n break;\n }\n\n case 'message':\n listeners.dispatch(frame.channel ?? null, frame.event, frame.payload);\n\n break;\n\n case 'pong':\n heartbeat.onPong();\n\n break;\n\n case 'presence_join':\n listeners.dispatch(null, frame.type, frame);\n\n break;\n\n case 'presence_leave':\n listeners.dispatch(null, frame.type, frame);\n\n break;\n\n case 'presence_state':\n listeners.dispatch(null, frame.type, frame);\n\n break;\n\n case 'subscribed':\n case 'unsubscribed':\n break;\n }\n }\n\n // ── Shared room confirmation helper ───────────────────────────────────────\n\n function awaitRoomConfirmation(\n pending: Map<string, Set<() => void>>,\n room: string,\n frameType: 'join' | 'leave',\n roomOpts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<void> {\n if (disposed) return Promise.reject(new PulseDisposedError());\n\n return new Promise((resolve, reject) => {\n const extraSignals: AbortSignal[] = [];\n\n if (roomOpts?.signal) extraSignals.push(roomOpts.signal);\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if (roomOpts?.timeout !== undefined) {\n const tc = new AbortController();\n\n timeoutId = setTimeout(() => tc.abort(new PulseTimeoutError(frameType)), roomOpts.timeout);\n extraSignals.push(tc.signal);\n }\n\n const sig =\n extraSignals.length === 0 ? disposalCtrl.signal : combineSignals(disposalCtrl.signal, ...extraSignals);\n\n if (sig.aborted) {\n clearTimeout(timeoutId);\n reject(sig.reason instanceof PulseTimeoutError ? sig.reason : new PulseAbortError());\n\n return;\n }\n\n let onAbort: () => void = () => {};\n\n const onConfirm = (): void => {\n clearTimeout(timeoutId);\n sig.removeEventListener('abort', onAbort);\n resolve();\n };\n\n onAbort = (): void => {\n clearTimeout(timeoutId);\n\n const set = pending.get(room);\n\n set?.delete(onConfirm);\n\n if (set?.size === 0) pending.delete(room);\n\n reject(sig.reason instanceof PulseTimeoutError ? sig.reason : new PulseAbortError());\n };\n\n let pendingSet = pending.get(room);\n const shouldSendFrame = !pendingSet;\n\n if (!pendingSet) {\n pendingSet = new Set();\n pending.set(room, pendingSet);\n }\n\n pendingSet.add(onConfirm);\n sig.addEventListener('abort', onAbort, { once: true });\n\n const sendFrame = (): void => rawSend(encode({ room, type: frameType }));\n\n if (!shouldSendFrame) return;\n\n if (ws?.readyState === WebSocket.OPEN) {\n sendFrame();\n } else {\n pulse\n .connect()\n .then(sendFrame)\n .catch((err: unknown) => {\n clearTimeout(timeoutId);\n sig.removeEventListener('abort', onAbort);\n\n const set = pending.get(room);\n\n set?.delete(onConfirm);\n\n if (set?.size === 0) pending.delete(room);\n\n reject(err);\n });\n }\n });\n }\n\n // ── Public API ─────────────────────────────────────────────────────────────\n\n const pulse: Pulse<TServer, TClient> = {\n channel<TChServer extends MessageMap = TServer, TChClient extends MessageMap = TClient>(\n name: string,\n ): PulseChannel<TChServer, TChClient> {\n const cached = channelCache.get(name);\n\n if (cached) return cached as PulseChannel<TChServer, TChClient>;\n\n if (disposed) {\n warn(`channel('${name}') called on a disposed Pulse — returning an already-disposed channel`);\n }\n\n const ch = createChannel<TChServer, TChClient>(\n name,\n (chan, event, payload) => {\n runMiddleware(event, payload, () => {\n rawSend(encode({ channel: chan, event, payload, type: 'message' }));\n });\n },\n (chan, event, handler) => listeners.add(chan, event, handler),\n disposalCtrl.signal,\n () => {\n channelCache.delete(name);\n rawSend(encode({ channel: name, type: 'unsubscribe' }));\n },\n );\n\n if (!disposed) channelCache.set(name, ch as PulseChannel<MessageMap, MessageMap>);\n\n if (ws?.readyState === WebSocket.OPEN) {\n ws.send(encode({ channel: name, type: 'subscribe' }));\n }\n\n return ch;\n },\n\n connect(): Promise<void> {\n if (disposed) return Promise.reject(new PulseDisposedError());\n\n if (ws?.readyState === WebSocket.OPEN) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n pendingOpens.push({ reject, resolve });\n\n if (!ws || ws.readyState > WebSocket.OPEN) openSocket();\n });\n },\n\n disconnect(code = 1000, reason = ''): void {\n intentionalClose = true;\n ws?.close(code, reason);\n },\n\n get disposalSignal() {\n return disposalCtrl.signal;\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n intentionalClose = true;\n heartbeat.stop();\n ws?.close(1000, 'disposed');\n ws = null;\n\n const pending = pendingOpens.splice(0);\n\n for (const p of pending) p.reject(new PulseDisposedError());\n\n channelCache.clear();\n presenceCache.clear();\n pendingJoins.clear();\n pendingLeaves.clear();\n listeners.clear();\n disposalCtrl.abort();\n },\n\n get disposed() {\n return disposed;\n },\n\n join(room: string, roomOpts?: { signal?: AbortSignal; timeout?: number }): Promise<void> {\n return awaitRoomConfirmation(pendingJoins, room, 'join', roomOpts);\n },\n\n leave(room: string, roomOpts?: { signal?: AbortSignal; timeout?: number }): Promise<void> {\n return awaitRoomConfirmation(pendingLeaves, room, 'leave', roomOpts);\n },\n\n on<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`on('${String(event)}') called on a disposed Pulse — listener ignored`);\n\n return () => {};\n }\n\n return listeners.add(null, event, handler as Handler);\n },\n\n once<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`once('${String(event)}') called on a disposed Pulse — listener ignored`);\n\n return () => {};\n }\n\n let unsub: Unsubscribe = () => {};\n\n const wrapped: Handler = (payload) => {\n unsub();\n (handler as Handler)(payload);\n };\n\n unsub = listeners.add(null, event, wrapped);\n\n return unsub;\n },\n\n presence<T>(room: string): PresenceChannel<T> {\n const cached = presenceCache.get(room);\n\n if (cached) return cached as PresenceChannel<T>;\n\n const ch = createPresence<T>(\n room,\n rawSend,\n {\n onJoin: (handler) => listeners.add(null, 'presence_join', (p) => handler(p as InPresenceJoinFrame)),\n onLeave: (handler) => listeners.add(null, 'presence_leave', (p) => handler(p as InPresenceLeaveFrame)),\n onState: (handler) => listeners.add(null, 'presence_state', (p) => handler(p as InPresenceStateFrame)),\n },\n disposalCtrl.signal,\n () => {\n presenceCache.delete(room);\n },\n );\n\n if (!disposed) {\n presenceCache.set(room, ch as PresenceChannel<unknown>);\n }\n\n if (disposed) {\n warn(`presence('${room}') called on a disposed Pulse — returning an already-disposed presence channel`);\n } else {\n pulse.join(room).catch((err: unknown) => {\n if (err instanceof PulseAbortError || err instanceof PulseDisposedError) return;\n\n warn(`presence() join failed for room '${room}': ${String(err)}`);\n });\n }\n\n return ch;\n },\n\n get rooms() {\n return rooms;\n },\n\n send<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void {\n if (disposed) {\n warn(`send('${String(event)}') called on a disposed Pulse — message dropped`);\n\n return;\n }\n\n runMiddleware(event, payload, () => {\n rawSend(encode({ event, payload, type: 'message' }));\n });\n },\n\n get status() {\n return status;\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n wait<K extends EventKey<TServer>>(\n event: K,\n waitOpts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<TServer[K]> {\n return createWaitPromise<TServer[K]>(event as string, disposalCtrl.signal, waitOpts, (ev, handler) =>\n listeners.add(null, ev, handler),\n );\n },\n };\n\n if (!opts.lazy) openSocket();\n\n return pulse;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pulse.cjs","names":[],"sources":["../src/errors.ts","../src/_utils.ts","../src/_connection.ts","../src/_wait.ts","../src/channel.ts","../src/protocol.ts","../src/heartbeat.ts","../src/presence.ts","../src/pulse.ts"],"sourcesContent":["/**\n * Base class for all pulse errors.\n * Use `instanceof PulseError` to catch any pulse-originated error in one branch.\n */\nexport class PulseError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is PulseError {\n return err instanceof PulseError;\n }\n}\n\n/**\n * Thrown when a WebSocket connection cannot be established or is lost\n * without triggering auto-reconnect (budget exhausted, or reconnect disabled).\n */\nexport class PulseConnectionError extends PulseError {\n readonly url: string;\n\n constructor(message: string, url: string, opts?: ErrorOptions) {\n super(message, opts);\n this.url = url;\n }\n}\n\n/**\n * Thrown when `wait()` or `join()`/`leave()` times out before receiving a\n * server response, or when an AbortSignal fires before the event arrives.\n */\nexport class PulseTimeoutError extends PulseError {\n readonly event: string;\n\n constructor(event: string, opts?: ErrorOptions) {\n super(`Timed out waiting for \"${event}\"`, opts);\n this.event = event;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires.\n */\nexport class PulseAbortError extends PulseError {\n constructor(opts?: ErrorOptions) {\n super('Aborted', opts);\n }\n}\n\n/**\n * Thrown when a method is called on a disposed Pulse instance or channel.\n */\nexport class PulseDisposedError extends PulseError {\n constructor(target = 'Pulse', opts?: ErrorOptions) {\n super(`${target} instance is disposed`, opts);\n }\n}\n\n/**\n * Thrown when the server sends a frame that cannot be parsed or violates\n * the wire protocol schema.\n */\nexport class PulseProtocolError extends PulseError {\n readonly raw: unknown;\n\n constructor(message: string, raw?: unknown, opts?: ErrorOptions) {\n super(message, opts);\n this.raw = raw;\n }\n}\n","/**\n * Returns a signal that aborts as soon as any of the provided signals abort.\n * With a single argument, returns it directly (no allocation).\n * Cleanup listeners are registered to prevent leaks.\n *\n * @internal\n */\nexport function combineSignals(first: AbortSignal, ...rest: AbortSignal[]): AbortSignal {\n if (rest.length === 0) return first;\n\n return rest.reduce(mergeTwo, first);\n}\n\nfunction mergeTwo(a: AbortSignal, b: AbortSignal): AbortSignal {\n if (a.aborted) return a;\n\n if (b.aborted) return b;\n\n const ctrl = new AbortController();\n const onA = (): void => ctrl.abort(a.reason);\n const onB = (): void => ctrl.abort(b.reason);\n\n a.addEventListener('abort', onA, { once: true });\n b.addEventListener('abort', onB, { once: true });\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n a.removeEventListener('abort', onA);\n b.removeEventListener('abort', onB);\n },\n { once: true },\n );\n\n return ctrl.signal;\n}\n\n/**\n * Create a child `AbortController` that aborts as soon as `parent` does.\n * Handles the case where `parent` is already aborted at call time — a plain\n * `addEventListener('abort', ...)` would miss that, since the event already fired.\n *\n * @internal\n */\nexport function deriveAbortController(parent: AbortSignal): AbortController {\n const ctrl = new AbortController();\n\n if (parent.aborted) {\n ctrl.abort(parent.reason);\n } else {\n parent.addEventListener('abort', () => ctrl.abort(parent.reason), { once: true });\n }\n\n return ctrl;\n}\n\n/**\n * Sleep for `ms` milliseconds, aborting early when the signal fires.\n * Resolves (does not reject) on abort — callers check `signal.aborted`.\n *\n * @internal\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n\n return;\n }\n\n const id = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(id);\n resolve();\n },\n { once: true },\n );\n });\n}\n\n/**\n * Full-jitter exponential backoff in ms, capped at `maxMs`.\n * `attempt` is zero-based.\n *\n * @internal\n */\nexport function defaultReconnectDelay(attempt: number, maxMs = 30_000): number {\n const base = Math.min(1000 * Math.pow(2, attempt), maxMs);\n\n return Math.random() * base;\n}\n","import type { PulseStatus, ReconnectOptions } from './types';\n\nimport { defaultReconnectDelay, sleep } from './_utils';\nimport { PulseConnectionError, PulseDisposedError } from './errors';\n\ntype ConnectionCallbacks = {\n onClose(): void;\n onError(error: PulseConnectionError): void;\n onMessage(event: MessageEvent): void;\n onOpen(): void;\n onStatus(status: PulseStatus): void;\n};\n\nexport type ConnectionController = {\n connect(): Promise<void>;\n disconnect(code?: number, reason?: string): void;\n dispose(): void;\n forceReconnect(code: number, reason: string): void;\n readonly open: boolean;\n send(frame: string): void;\n};\n\ntype Opening = {\n promise: Promise<void>;\n reject(error: PulseConnectionError): void;\n resolve(): void;\n socket: WebSocket;\n};\n\nconst DEFAULT_MAX_ATTEMPTS = 5;\n\n/** Owns the one active socket and its retry loop. @internal */\nexport function createConnection(\n url: string,\n protocols: string | string[] | undefined,\n reconnect: boolean | ReconnectOptions | undefined,\n callbacks: ConnectionCallbacks,\n): ConnectionController {\n const options: ReconnectOptions | undefined = reconnect === true ? {} : reconnect || undefined;\n const maxAttempts = options ? (options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS) : 0;\n const configuredDelay = options?.delay;\n const delay: (attempt: number) => number =\n typeof configuredDelay === 'function'\n ? configuredDelay\n : typeof configuredDelay === 'number'\n ? () => configuredDelay\n : defaultReconnectDelay;\n\n let disposed = false;\n let desired = false;\n let retrying = false;\n let retryCtrl: AbortController | undefined;\n let socket: WebSocket | undefined;\n let opening: Opening | undefined;\n const waiters = new Set<{ reject(error: Error): void; resolve(): void }>();\n\n function setStatus(status: PulseStatus): void {\n callbacks.onStatus(status);\n }\n\n function rejectWaiters(error: Error): void {\n for (const waiter of waiters) waiter.reject(error);\n waiters.clear();\n }\n\n function resolveWaiters(): void {\n for (const waiter of waiters) waiter.resolve();\n waiters.clear();\n }\n\n function rejectOpening(socketToReject: WebSocket, error: PulseConnectionError): void {\n if (opening?.socket !== socketToReject) return;\n\n opening.reject(error);\n opening = undefined;\n }\n\n function openSocket(): Promise<void> {\n if (socket?.readyState === WebSocket.OPEN) return Promise.resolve();\n\n if (opening) return opening.promise;\n\n const nextSocket = new WebSocket(url, protocols);\n\n socket = nextSocket;\n\n let resolveOpening: () => void;\n let rejectOpeningPromise: (error: PulseConnectionError) => void;\n const promise = new Promise<void>((resolve, reject) => {\n resolveOpening = resolve;\n rejectOpeningPromise = reject;\n });\n\n opening = { promise, reject: rejectOpeningPromise!, resolve: resolveOpening!, socket: nextSocket };\n\n nextSocket.onopen = (): void => {\n if (socket !== nextSocket || !desired) {\n nextSocket.close(1000, 'stale connection');\n\n return;\n }\n\n if (opening?.socket === nextSocket) {\n opening.resolve();\n opening = undefined;\n }\n\n setStatus('open');\n callbacks.onOpen();\n resolveWaiters();\n };\n\n nextSocket.onmessage = (event: MessageEvent): void => {\n if (socket === nextSocket) callbacks.onMessage(event);\n };\n\n nextSocket.onerror = (): void => {\n if (socket !== nextSocket) return;\n\n const error = new PulseConnectionError('WebSocket error', url);\n\n callbacks.onError(error);\n rejectOpening(nextSocket, error);\n rejectWaiters(error);\n };\n\n nextSocket.onclose = (): void => {\n if (socket !== nextSocket) return;\n\n socket = undefined;\n callbacks.onClose();\n\n const error = new PulseConnectionError('Connection closed', url);\n\n rejectOpening(nextSocket, error);\n\n if (!desired || disposed) {\n setStatus('closed');\n\n return;\n }\n\n if (!retrying) void reconnectLoop();\n };\n\n return promise;\n }\n\n async function reconnectLoop(): Promise<void> {\n if (retrying || disposed || !desired) return;\n\n retrying = true;\n\n const controller = new AbortController();\n\n retryCtrl = controller;\n setStatus('reconnecting');\n\n let lastError: PulseConnectionError | undefined;\n\n for (let attempt = 0; attempt < maxAttempts && desired && !disposed; attempt++) {\n await sleep(delay(attempt), controller.signal);\n\n if (controller.signal.aborted || !desired || disposed) break;\n\n try {\n await openSocket();\n retrying = false;\n\n return;\n } catch (error) {\n lastError = error instanceof PulseConnectionError ? error : new PulseConnectionError('Reconnect failed', url);\n }\n }\n\n if (!controller.signal.aborted && desired && !disposed) {\n desired = false;\n setStatus('closed');\n callbacks.onError(lastError ?? new PulseConnectionError('Reconnect budget exhausted', url));\n rejectWaiters(lastError ?? new PulseConnectionError('Reconnect budget exhausted', url));\n }\n\n if (retryCtrl === controller) retryCtrl = undefined;\n\n retrying = false;\n }\n\n function cancelRetry(): void {\n retryCtrl?.abort();\n retryCtrl = undefined;\n retrying = false;\n }\n\n return {\n connect(): Promise<void> {\n if (disposed) return Promise.reject(new PulseDisposedError());\n\n desired = true;\n\n if (socket?.readyState === WebSocket.OPEN) return Promise.resolve();\n\n const connected = new Promise<void>((resolve, reject) => {\n waiters.add({ reject, resolve });\n });\n\n if (!opening && !retrying) {\n setStatus('connecting');\n void openSocket().catch(() => {});\n }\n\n return connected;\n },\n\n disconnect(code = 1000, reason = ''): void {\n desired = false;\n cancelRetry();\n rejectWaiters(new PulseConnectionError('Connection closed', url));\n setStatus('closed');\n socket?.close(code, reason);\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n desired = false;\n cancelRetry();\n rejectWaiters(new PulseDisposedError());\n socket?.close(1000, 'disposed');\n socket = undefined;\n opening = undefined;\n setStatus('closed');\n },\n\n forceReconnect(code: number, reason: string): void {\n if (desired && socket?.readyState === WebSocket.OPEN) socket.close(code, reason);\n },\n\n get open() {\n return socket?.readyState === WebSocket.OPEN;\n },\n\n send(frame: string): void {\n if (socket?.readyState !== WebSocket.OPEN) {\n throw new PulseConnectionError('Connection is not open', url);\n }\n\n socket.send(frame);\n },\n };\n}\n","import type { Unsubscribe } from './types';\n\nimport { combineSignals } from './_utils';\nimport { PulseAbortError, PulseTimeoutError } from './errors';\n\n/**\n * Shared implementation for `pulse.wait()` and `channel.wait()`.\n * Resolves on the next event dispatched via `subscribe`, or rejects when\n * the combined signal aborts or the optional timeout elapses.\n *\n * @internal\n */\nexport function createWaitPromise<T>(\n event: string,\n baseSignal: AbortSignal,\n opts: { signal?: AbortSignal; timeout?: number } | undefined,\n subscribe: (event: string, handler: (payload: unknown) => void) => Unsubscribe,\n): Promise<T> {\n return new Promise((resolve, reject) => {\n const signals: AbortSignal[] = [baseSignal];\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if (opts?.signal) signals.push(opts.signal);\n\n if (opts?.timeout !== undefined) {\n const timeoutCtrl = new AbortController();\n\n timeoutId = setTimeout(() => {\n timeoutCtrl.abort(new PulseTimeoutError(event));\n }, opts.timeout);\n signals.push(timeoutCtrl.signal);\n }\n\n const combined = signals.length === 1 ? signals[0]! : combineSignals(signals[0]!, ...signals.slice(1));\n\n if (combined.aborted) {\n clearTimeout(timeoutId);\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n\n return;\n }\n\n let unsub: Unsubscribe = () => {};\n\n const onAbort = (): void => {\n clearTimeout(timeoutId);\n unsub();\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n };\n\n combined.addEventListener('abort', onAbort, { once: true });\n\n unsub = subscribe(event, (payload) => {\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n resolve(payload as T);\n });\n });\n}\n","import type { EventKey, MessageMap, PulseChannel, Unsubscribe } from './types';\n\nimport { deriveAbortController } from './_utils';\nimport { createWaitPromise } from './_wait';\nimport { PulseDisposedError } from './errors';\n\ntype SendFn = (channel: string, event: string, payload: unknown) => void;\ntype SubscribeFn = (channel: string, event: string, handler: (payload: unknown) => void) => () => void;\n\n/**\n * Create a typed channel object that scopes messaging to a named namespace.\n * @internal\n */\nexport function createChannel<TServer extends MessageMap, TClient extends MessageMap>(\n name: string,\n send: SendFn,\n subscribe: SubscribeFn,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PulseChannel<TServer, TClient> {\n const ctrl = deriveAbortController(disposalSignal);\n const tracked = new Set<() => void>();\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const channel: PulseChannel<TServer, TClient> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n ctrl.abort();\n\n for (const unsub of tracked) unsub();\n\n tracked.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n get name() {\n return name;\n },\n\n on<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n throw new PulseDisposedError(`Channel \"${name}\"`);\n }\n\n const unsub = subscribe(name, event, handler as (payload: unknown) => void);\n\n tracked.add(unsub);\n\n return () => {\n unsub();\n tracked.delete(unsub);\n };\n },\n\n once<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n throw new PulseDisposedError(`Channel \"${name}\"`);\n }\n\n let unsub: Unsubscribe = () => {};\n\n const wrapped = (payload: unknown): void => {\n tracked.delete(unsub);\n unsub();\n (handler as (payload: unknown) => void)(payload);\n };\n\n unsub = subscribe(name, event, wrapped);\n tracked.add(unsub);\n\n return () => {\n tracked.delete(unsub);\n unsub();\n };\n },\n\n send<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void {\n if (disposed) {\n throw new PulseDisposedError(`Channel \"${name}\"`);\n }\n\n send(name, event, payload);\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n wait<K extends EventKey<TServer>>(\n event: K,\n opts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<TServer[K]> {\n return createWaitPromise<TServer[K]>(event as string, ctrl.signal, opts, (ev, handler) =>\n subscribe(name, ev, handler),\n );\n },\n };\n\n return channel;\n}\n","import { PulseProtocolError } from './errors';\n\n// ─── Outgoing frames (client → server) ────────────────────────────────────────\n\nexport type OutMessageFrame = { channel?: string; event: string; payload: unknown; type: 'message' };\nexport type OutSubscribeFrame = { channel: string; type: 'subscribe' };\nexport type OutUnsubscribeFrame = { channel: string; type: 'unsubscribe' };\nexport type OutJoinFrame = { room: string; type: 'join' };\nexport type OutLeaveFrame = { room: string; type: 'leave' };\nexport type OutPresenceFrame = { room: string; state: unknown; type: 'presence' };\nexport type OutPingFrame = { ts: number; type: 'ping' };\n\nexport type OutFrame =\n | OutJoinFrame\n | OutLeaveFrame\n | OutMessageFrame\n | OutPingFrame\n | OutPresenceFrame\n | OutSubscribeFrame\n | OutUnsubscribeFrame;\n\n// ─── Incoming frames (server → client) ────────────────────────────────────────\n\nexport type InMessageFrame = { channel?: string; event: string; payload: unknown; type: 'message' };\nexport type InSubscribedFrame = { channel: string; type: 'subscribed' };\nexport type InUnsubscribedFrame = { channel: string; type: 'unsubscribed' };\nexport type InJoinedFrame = { room: string; type: 'joined' };\nexport type InLeftFrame = { room: string; type: 'left' };\nexport type InPresenceStateFrame = { members: Record<string, unknown>; room: string; type: 'presence_state' };\nexport type InPresenceJoinFrame = { id: string; room: string; state: unknown; type: 'presence_join' };\nexport type InPresenceLeaveFrame = { id: string; room: string; type: 'presence_leave' };\nexport type InPongFrame = { ts: number; type: 'pong' };\nexport type InErrorFrame = { code: string; message: string; type: 'error' };\n\nexport type InFrame =\n | InErrorFrame\n | InJoinedFrame\n | InLeftFrame\n | InMessageFrame\n | InPongFrame\n | InPresenceJoinFrame\n | InPresenceLeaveFrame\n | InPresenceStateFrame\n | InSubscribedFrame\n | InUnsubscribedFrame;\n\ntype ParsedFrame = { type: string } & Record<string, unknown>;\nexport type DecodedInFrame = { frame: InFrame; kind: 'known' } | { kind: 'unknown'; type: string };\n\n// ─── Codec ────────────────────────────────────────────────────────────────────\n\n/**\n * Encode an outgoing frame to a JSON string.\n * @internal\n */\nexport function encode(frame: OutFrame): string {\n return JSON.stringify(frame);\n}\n\n/**\n * Parse a raw MessageEvent data string and ensure it has a string `type` field.\n * Throws `PulseProtocolError` if the data is not valid JSON or missing the `type` field.\n * @internal\n */\nexport function decode(raw: unknown): ParsedFrame {\n if (typeof raw !== 'string') {\n throw new PulseProtocolError('Expected string message data', raw);\n }\n\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(raw);\n } catch (cause) {\n throw new PulseProtocolError('JSON parse error', raw);\n }\n\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n !('type' in parsed) ||\n typeof (parsed as { type: unknown }).type !== 'string'\n ) {\n throw new PulseProtocolError('Missing or invalid \"type\" field', parsed);\n }\n\n return parsed as ParsedFrame;\n}\n\nfunction isObjectRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction getStringProp(frame: unknown, key: string): string | null {\n if (!isObjectRecord(frame)) return null;\n\n const value = frame[key];\n\n return typeof value === 'string' ? value : null;\n}\n\nexport function isErrorFrame(frame: unknown): frame is InErrorFrame {\n return getStringProp(frame, 'code') !== null && getStringProp(frame, 'message') !== null;\n}\n\nexport function isJoinedFrame(frame: unknown): frame is InJoinedFrame {\n return getStringProp(frame, 'room') !== null;\n}\n\nexport function isLeftFrame(frame: unknown): frame is InLeftFrame {\n return getStringProp(frame, 'room') !== null;\n}\n\nexport function isMessageFrame(frame: unknown): frame is InMessageFrame {\n if (!isObjectRecord(frame) || getStringProp(frame, 'event') === null || !('payload' in frame)) return false;\n\n return frame.channel === undefined || typeof frame.channel === 'string';\n}\n\nexport function isPongFrame(frame: unknown): frame is InPongFrame {\n return isObjectRecord(frame) && typeof frame.ts === 'number';\n}\n\nexport function isPresenceJoinFrame(frame: unknown): frame is InPresenceJoinFrame {\n return (\n isObjectRecord(frame) &&\n getStringProp(frame, 'id') !== null &&\n getStringProp(frame, 'room') !== null &&\n 'state' in frame\n );\n}\n\nexport function isPresenceLeaveFrame(frame: unknown): frame is InPresenceLeaveFrame {\n return getStringProp(frame, 'id') !== null && getStringProp(frame, 'room') !== null;\n}\n\nexport function isPresenceStateFrame(frame: unknown): frame is InPresenceStateFrame {\n return isObjectRecord(frame) && getStringProp(frame, 'room') !== null && isObjectRecord(frame.members);\n}\n\nfunction isSubscribedFrame(frame: unknown): frame is InSubscribedFrame {\n return getStringProp(frame, 'channel') !== null;\n}\n\nfunction isUnsubscribedFrame(frame: unknown): frame is InUnsubscribedFrame {\n return getStringProp(frame, 'channel') !== null;\n}\n\n/**\n * Parse and validate known incoming frame shapes.\n * Unknown frame `type` values are passed through so callers can warn/drop them.\n * @internal\n */\nexport function decodeValidated(raw: unknown): DecodedInFrame {\n const frame = decode(raw) as ParsedFrame;\n\n switch (frame.type) {\n case 'error':\n if (isErrorFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'joined':\n if (isJoinedFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'left':\n if (isLeftFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'message':\n if (isMessageFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'pong':\n if (isPongFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'presence_join':\n if (isPresenceJoinFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'presence_leave':\n if (isPresenceLeaveFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'presence_state':\n if (isPresenceStateFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'subscribed':\n if (isSubscribedFrame(frame)) return { frame, kind: 'known' };\n\n break;\n case 'unsubscribed':\n if (isUnsubscribedFrame(frame)) return { frame, kind: 'known' };\n\n break;\n default:\n return { kind: 'unknown', type: frame.type };\n }\n\n throw new PulseProtocolError(`Malformed \"${frame.type}\" frame`, frame);\n}\n","import type { HeartbeatOptions } from './types';\n\nimport { encode } from './protocol';\n\nconst DEFAULT_INTERVAL = 30_000;\nconst DEFAULT_TIMEOUT = 5_000;\n\nexport type HeartbeatHandle = {\n /** Called when a `pong` frame is received. Resets the timeout timer. */\n onPong(): void;\n /** Start the heartbeat loop. */\n start(): void;\n /** Stop the heartbeat loop and cancel any pending timers. */\n stop(): void;\n};\n\n/**\n * Create a heartbeat manager that sends periodic pings and expects pong replies.\n * Calls `onDead` when a pong is not received within the timeout window.\n * @internal\n */\nexport function createHeartbeat(\n opts: boolean | HeartbeatOptions | undefined,\n send: (frame: string) => void,\n onDead: () => void,\n): HeartbeatHandle {\n if (!opts) {\n return { onPong() {}, start() {}, stop() {} };\n }\n\n const options: HeartbeatOptions = opts === true ? {} : opts;\n const interval = options.interval ?? DEFAULT_INTERVAL;\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n\n let pingTimer: ReturnType<typeof setTimeout> | undefined;\n let pongTimer: ReturnType<typeof setTimeout> | undefined;\n let running = false;\n\n function schedulePing(): void {\n pingTimer = setTimeout(() => {\n const ts = Date.now();\n\n send(encode({ ts, type: 'ping' }));\n\n pongTimer = setTimeout(() => {\n onDead();\n }, timeout);\n }, interval);\n }\n\n return {\n onPong() {\n clearTimeout(pongTimer);\n pongTimer = undefined;\n\n if (running) schedulePing();\n },\n\n start() {\n running = true;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pongTimer = undefined;\n schedulePing();\n },\n\n stop() {\n running = false;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pingTimer = undefined;\n pongTimer = undefined;\n },\n };\n}\n","import { type Signal, signal } from '@vielzeug/ripple';\n\nimport type { PresenceChannel, Unsubscribe } from './types';\n\nimport { deriveAbortController } from './_utils';\nimport { PulseDisposedError } from './errors';\nimport { type InPresenceJoinFrame, type InPresenceLeaveFrame, type InPresenceStateFrame } from './protocol';\n\ntype UpdateFn<T> = (room: string, state: T) => void;\n\nexport type PresenceEvents = {\n onJoin(handler: (frame: InPresenceJoinFrame) => void): Unsubscribe;\n onLeave(handler: (frame: InPresenceLeaveFrame) => void): Unsubscribe;\n onState(handler: (frame: InPresenceStateFrame) => void): Unsubscribe;\n};\n\n/**\n * Create a presence channel that reactively tracks room member states.\n * @internal\n */\nexport function createPresence<T>(\n room: string,\n update: UpdateFn<T>,\n events: PresenceEvents,\n disposalSignal: AbortSignal,\n registerReset?: (reset: () => void) => void,\n onDispose?: () => void,\n): PresenceChannel<T> {\n const ctrl = deriveAbortController(disposalSignal);\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const members: Signal<Map<string, T>> = signal(new Map<string, T>());\n\n registerReset?.(() => {\n members.value = new Map<string, T>();\n });\n\n const joinHandlers = new Set<(memberId: string, state: T) => void>();\n const leaveHandlers = new Set<(memberId: string) => void>();\n\n const unsubs: Array<() => void> = [\n events.onState((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map<string, T>();\n\n for (const [id, state] of Object.entries(frame.members)) {\n next.set(id, state as T);\n }\n\n members.value = next;\n }),\n events.onJoin((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.set(frame.id, frame.state as T);\n members.value = next;\n\n for (const handler of joinHandlers) {\n handler(frame.id, frame.state as T);\n }\n }),\n events.onLeave((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.delete(frame.id);\n members.value = next;\n\n for (const handler of leaveHandlers) {\n handler(frame.id);\n }\n }),\n ];\n\n const presence: PresenceChannel<T> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n ctrl.abort();\n\n for (const unsub of unsubs) unsub();\n\n unsubs.length = 0;\n joinHandlers.clear();\n leaveHandlers.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n onJoin(handler: (memberId: string, state: T) => void): Unsubscribe {\n if (disposed) {\n throw new PulseDisposedError(`Presence \"${room}\"`);\n }\n\n joinHandlers.add(handler);\n\n return () => joinHandlers.delete(handler);\n },\n\n onLeave(handler: (memberId: string) => void): Unsubscribe {\n if (disposed) {\n throw new PulseDisposedError(`Presence \"${room}\"`);\n }\n\n leaveHandlers.add(handler);\n\n return () => leaveHandlers.delete(handler);\n },\n\n get room() {\n return room;\n },\n\n get state() {\n return members;\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n update(state: T): void {\n if (disposed) throw new PulseDisposedError(`Presence \"${room}\"`);\n\n update(room, state);\n },\n };\n\n return presence;\n}\n","import { signal } from '@vielzeug/ripple';\n\nimport type {\n ChannelDefinitions,\n EventKey,\n MessageMap,\n PresenceChannel,\n PresenceDefinitions,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n Unsubscribe,\n} from './types';\n\nimport { createConnection } from './_connection';\nimport { createWaitPromise } from './_wait';\nimport { createChannel } from './channel';\nimport {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseTimeoutError,\n} from './errors';\nimport { createHeartbeat, type HeartbeatHandle } from './heartbeat';\nimport { createPresence } from './presence';\nimport {\n type DecodedInFrame,\n type InPresenceJoinFrame,\n type InPresenceLeaveFrame,\n type InPresenceStateFrame,\n decodeValidated,\n encode,\n} from './protocol';\n\ntype Handler = (payload: unknown) => void;\ntype RoomOperation = 'join' | 'leave';\ntype RoomWaiter = { reject(error: PulseError): void; resolve(): void };\ntype PendingRoom = { callbacks: Set<RoomWaiter>; next?: PendingRoom; operation: RoomOperation };\n\nclass ListenerMap {\n private readonly map = new Map<string | null, Map<string, Set<Handler>>>();\n\n add(channel: string | null, event: string, handler: Handler): Unsubscribe {\n let events = this.map.get(channel);\n\n if (!events) {\n events = new Map();\n this.map.set(channel, events);\n }\n\n let handlers = events.get(event);\n\n if (!handlers) {\n handlers = new Set();\n events.set(event, handlers);\n }\n\n handlers.add(handler);\n\n return () => {\n handlers.delete(handler);\n\n if (handlers.size === 0) events.delete(event);\n\n if (events.size === 0) this.map.delete(channel);\n };\n }\n\n clear(): void {\n this.map.clear();\n }\n\n dispatch(channel: string | null, event: string, payload: unknown): void {\n for (const handler of this.map.get(channel)?.get(event) ?? []) handler(payload);\n }\n}\n\n/**\n * Create a typed, explicitly connected WebSocket client with scoped channels,\n * rooms, presence, reconnect, and heartbeat support.\n */\nexport function createPulse<\n TServer extends MessageMap = MessageMap,\n TClient extends MessageMap = MessageMap,\n TChannels extends ChannelDefinitions = ChannelDefinitions,\n TPresence extends PresenceDefinitions = PresenceDefinitions,\n>(url: string, options: PulseOptions = {}): Pulse<TServer, TClient, TChannels, TPresence> {\n const disposalCtrl = new AbortController();\n const status = signal<PulseStatus>('closed');\n const rooms = signal<ReadonlySet<string>>(new Set());\n const listeners = new ListenerMap();\n const channelReferences = new Map<string, number>();\n const presenceReferences = new Map<string, number>();\n const manualRooms = new Set<string>();\n const localPresence = new Map<string, unknown>();\n const presenceResetters = new Map<string, Set<() => void>>();\n const pendingRooms = new Map<string, PendingRoom>();\n\n let disposed = false;\n\n function report(error: PulseError): void {\n options.onError?.(error);\n }\n\n function sendInternal(frame: string): void {\n connection.send(frame);\n }\n\n function desiredRoom(room: string): boolean {\n return manualRooms.has(room) || (presenceReferences.get(room) ?? 0) > 0;\n }\n\n function setRoom(room: string, joined: boolean): void {\n const next = new Set(rooms.value);\n\n if (joined) next.add(room);\n else next.delete(room);\n\n rooms.value = next;\n }\n\n function resetRemoteSession(): void {\n rooms.value = new Set();\n\n for (const resetters of presenceResetters.values()) {\n for (const reset of resetters) reset();\n }\n }\n\n function rejectPendingRooms(error: PulseError, reportFailure = true): void {\n let rejected = false;\n\n for (const [room, firstPending] of pendingRooms) {\n let pending: PendingRoom | undefined = firstPending;\n\n while (pending) {\n if (pending.operation === 'join') manualRooms.delete(room);\n\n for (const waiter of pending.callbacks) {\n waiter.reject(error);\n rejected = true;\n }\n\n pending = pending.next;\n }\n }\n\n pendingRooms.clear();\n\n if (rejected && reportFailure) report(error);\n }\n\n function handleTransportClose(): void {\n heartbeat.stop();\n resetRemoteSession();\n rejectPendingRooms(new PulseConnectionError('Connection closed before room confirmation', url));\n }\n\n function restoreSession(): void {\n for (const channel of channelReferences.keys()) {\n sendInternal(encode({ channel, type: 'subscribe' }));\n }\n\n for (const room of new Set([...manualRooms, ...presenceReferences.keys()])) {\n if (desiredRoom(room)) sendInternal(encode({ room, type: 'join' }));\n }\n\n for (const [room, state] of localPresence) {\n if (desiredRoom(room)) sendInternal(encode({ room, state, type: 'presence' }));\n }\n }\n\n const connection = createConnection(url, options.protocols, options.reconnect, {\n onClose() {\n handleTransportClose();\n },\n onError: report,\n onMessage(event) {\n let decoded: DecodedInFrame;\n\n try {\n decoded = decodeValidated(event.data);\n } catch (error) {\n report(\n error instanceof PulseProtocolError ? error : new PulseProtocolError('Failed to decode frame', event.data),\n );\n\n return;\n }\n\n if (decoded.kind === 'unknown') {\n report(new PulseProtocolError(`Unknown frame type \"${decoded.type}\"`, event.data));\n\n return;\n }\n\n try {\n handleFrame(decoded);\n } catch (error) {\n report(new PulseProtocolError('Frame handler threw', decoded.frame, { cause: error }));\n }\n },\n onOpen() {\n restoreSession();\n heartbeat.start();\n },\n onStatus(nextStatus) {\n status.value = nextStatus;\n },\n });\n\n const heartbeat: HeartbeatHandle = createHeartbeat(\n options.heartbeat,\n (frame) => {\n if (connection.open) sendInternal(frame);\n },\n () => connection.forceReconnect(4000, 'heartbeat timeout'),\n );\n\n function resolvePendingRoom(room: string, operation: RoomOperation): void {\n const pending = pendingRooms.get(room);\n\n if (!pending || pending.operation !== operation) return;\n\n for (const waiter of pending.callbacks) waiter.resolve();\n\n const nextPending = pending.next;\n\n if (!nextPending) {\n pendingRooms.delete(room);\n\n const followUpOperation =\n operation === 'join' && !desiredRoom(room)\n ? 'leave'\n : operation === 'leave' && desiredRoom(room)\n ? 'join'\n : undefined;\n\n if (followUpOperation) {\n const followUpPending: PendingRoom = { callbacks: new Set<RoomWaiter>(), operation: followUpOperation };\n\n pendingRooms.set(room, followUpPending);\n\n try {\n sendInternal(encode({ room, type: followUpOperation }));\n } catch (error) {\n pendingRooms.delete(room);\n\n if (followUpOperation === 'join') manualRooms.delete(room);\n\n report(error instanceof PulseError ? error : new PulseConnectionError('Failed to send room request', url));\n }\n }\n\n return;\n }\n\n pendingRooms.set(room, nextPending);\n\n try {\n sendInternal(encode({ room, type: nextPending.operation }));\n } catch (error) {\n pendingRooms.delete(room);\n\n if (nextPending.operation === 'join') manualRooms.delete(room);\n\n for (const waiter of nextPending.callbacks) {\n waiter.reject(\n error instanceof PulseError ? error : new PulseConnectionError('Failed to send room request', url),\n );\n }\n }\n }\n\n function handleFrame(decoded: Extract<DecodedInFrame, { kind: 'known' }>): void {\n const frame = decoded.frame;\n\n switch (frame.type) {\n case 'error':\n report(new PulseProtocolError(`Server error [${frame.code}]: ${frame.message}`, frame));\n\n break;\n case 'joined':\n if (desiredRoom(frame.room)) setRoom(frame.room, true);\n\n resolvePendingRoom(frame.room, 'join');\n\n break;\n case 'left':\n setRoom(frame.room, false);\n resolvePendingRoom(frame.room, 'leave');\n\n break;\n case 'message':\n listeners.dispatch(frame.channel ?? null, frame.event, frame.payload);\n\n break;\n case 'pong':\n heartbeat.onPong();\n\n break;\n case 'presence_join':\n case 'presence_leave':\n case 'presence_state':\n listeners.dispatch(null, frame.type, frame);\n\n break;\n case 'subscribed':\n case 'unsubscribed':\n break;\n }\n }\n\n function awaitRoom(\n room: string,\n operation: RoomOperation,\n opts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<void> {\n if (disposed) {\n if (operation === 'join') manualRooms.delete(room);\n\n return Promise.reject(new PulseDisposedError());\n }\n\n if (!connection.open) {\n if (operation === 'join') manualRooms.delete(room);\n\n return Promise.reject(new PulseConnectionError('Connection is not open', url));\n }\n\n const existing = pendingRooms.get(room);\n\n if (operation === 'join' && rooms.value.has(room) && !existing) return Promise.resolve();\n\n if (operation === 'leave' && !rooms.value.has(room) && !existing) return Promise.resolve();\n\n let pending = existing;\n\n if (pending) {\n while (pending.next) pending = pending.next;\n\n if (pending.operation !== operation) {\n const nextPending: PendingRoom = { callbacks: new Set<RoomWaiter>(), operation };\n\n pending.next = nextPending;\n pending = nextPending;\n }\n } else {\n pending = { callbacks: new Set<RoomWaiter>(), operation };\n }\n\n return new Promise((resolve, reject) => {\n const timeoutCtrl = opts?.timeout === undefined ? undefined : new AbortController();\n const signal =\n opts?.signal && timeoutCtrl\n ? AbortSignal.any([opts.signal, timeoutCtrl.signal])\n : (opts?.signal ?? timeoutCtrl?.signal);\n const timeoutId = opts?.timeout === undefined ? undefined : setTimeout(() => timeoutCtrl?.abort(), opts.timeout);\n const waiter: RoomWaiter = {\n reject(error) {\n clearTimeout(timeoutId);\n signal?.removeEventListener('abort', rejectRoom);\n reject(error);\n },\n resolve() {\n clearTimeout(timeoutId);\n signal?.removeEventListener('abort', rejectRoom);\n resolve();\n },\n };\n const rejectRoom = (): void => {\n pending.callbacks.delete(waiter);\n\n if (pending.callbacks.size === 0 && operation === 'join') manualRooms.delete(room);\n\n waiter.reject(timeoutCtrl?.signal.aborted ? new PulseTimeoutError(operation) : new PulseAbortError());\n };\n\n if (signal?.aborted) {\n rejectRoom();\n\n return;\n }\n\n pending.callbacks.add(waiter);\n signal?.addEventListener('abort', rejectRoom, { once: true });\n\n if (existing) return;\n\n pendingRooms.set(room, pending);\n\n try {\n sendInternal(encode({ room, type: operation }));\n } catch (error) {\n pending.callbacks.delete(waiter);\n\n if (pending.callbacks.size === 0) pendingRooms.delete(room);\n\n waiter.reject(\n error instanceof PulseError ? error : new PulseConnectionError('Failed to send room request', url),\n );\n }\n });\n }\n\n function acquireChannel(name: string): void {\n const references = channelReferences.get(name) ?? 0;\n\n channelReferences.set(name, references + 1);\n\n if (references === 0 && connection.open) sendInternal(encode({ channel: name, type: 'subscribe' }));\n }\n\n function releaseChannel(name: string): void {\n const references = channelReferences.get(name);\n\n if (!references) return;\n\n if (references > 1) {\n channelReferences.set(name, references - 1);\n\n return;\n }\n\n channelReferences.delete(name);\n\n if (connection.open) sendInternal(encode({ channel: name, type: 'unsubscribe' }));\n }\n\n function acquirePresence(room: string, reset: () => void): void {\n const references = presenceReferences.get(room) ?? 0;\n const resetters = presenceResetters.get(room) ?? new Set<() => void>();\n\n resetters.add(reset);\n presenceResetters.set(room, resetters);\n presenceReferences.set(room, references + 1);\n\n if (references === 0 && connection.open) sendInternal(encode({ room, type: 'join' }));\n }\n\n function releasePresence(room: string, reset: () => void): void {\n presenceResetters.get(room)?.delete(reset);\n\n if (presenceResetters.get(room)?.size === 0) presenceResetters.delete(room);\n\n const references = presenceReferences.get(room);\n\n if (!references) return;\n\n if (references > 1) {\n presenceReferences.set(room, references - 1);\n\n return;\n }\n\n presenceReferences.delete(room);\n localPresence.delete(room);\n\n if (!manualRooms.has(room) && connection.open) sendInternal(encode({ room, type: 'leave' }));\n }\n\n const pulse: Pulse<TServer, TClient, TChannels, TPresence> = {\n channel<K extends keyof TChannels & string>(name: K): PulseChannel<TChannels[K]['server'], TChannels[K]['client']> {\n if (disposed) throw new PulseDisposedError();\n\n acquireChannel(name);\n\n return createChannel<TChannels[K]['server'], TChannels[K]['client']>(\n name,\n (channel, event, payload) => {\n const transformed = options.transform\n ? options.transform({ channel, event, payload })\n : { channel, event, payload };\n\n if (transformed === null) return;\n\n sendInternal(encode({ ...transformed, type: 'message' }));\n },\n (channel, event, handler) => listeners.add(channel, event, handler),\n disposalCtrl.signal,\n () => releaseChannel(name),\n );\n },\n\n connect(): Promise<void> {\n if (disposed) return Promise.reject(new PulseDisposedError());\n\n return connection.connect();\n },\n\n disconnect(code = 1000, reason = ''): void {\n if (disposed) return;\n\n handleTransportClose();\n connection.disconnect(code, reason);\n },\n\n get disposalSignal() {\n return disposalCtrl.signal;\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n heartbeat.stop();\n resetRemoteSession();\n rejectPendingRooms(new PulseDisposedError(), false);\n connection.dispose();\n channelReferences.clear();\n presenceReferences.clear();\n manualRooms.clear();\n localPresence.clear();\n presenceResetters.clear();\n pendingRooms.clear();\n listeners.clear();\n disposalCtrl.abort();\n },\n\n get disposed() {\n return disposed;\n },\n\n join(room: string, opts?: { signal?: AbortSignal; timeout?: number }): Promise<void> {\n manualRooms.add(room);\n\n return awaitRoom(room, 'join', opts);\n },\n\n leave(room: string, opts?: { signal?: AbortSignal; timeout?: number }): Promise<void> {\n manualRooms.delete(room);\n\n if (desiredRoom(room)) return Promise.resolve();\n\n return awaitRoom(room, 'leave', opts);\n },\n\n on<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) throw new PulseDisposedError();\n\n return listeners.add(null, event, handler as Handler);\n },\n\n once<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) throw new PulseDisposedError();\n\n let unsubscribe: Unsubscribe = () => {};\n\n unsubscribe = listeners.add(null, event, (payload) => {\n unsubscribe();\n (handler as Handler)(payload);\n });\n\n return unsubscribe;\n },\n\n presence<K extends keyof TPresence & string>(room: K): PresenceChannel<TPresence[K]> {\n if (disposed) throw new PulseDisposedError();\n\n let reset: (() => void) | undefined;\n\n const presence = createPresence<TPresence[K]>(\n room,\n (presenceRoom, state) => {\n sendInternal(encode({ room: presenceRoom, state, type: 'presence' }));\n localPresence.set(presenceRoom, state);\n },\n {\n onJoin: (handler) => listeners.add(null, 'presence_join', (value) => handler(value as InPresenceJoinFrame)),\n onLeave: (handler) =>\n listeners.add(null, 'presence_leave', (value) => handler(value as InPresenceLeaveFrame)),\n onState: (handler) =>\n listeners.add(null, 'presence_state', (value) => handler(value as InPresenceStateFrame)),\n },\n disposalCtrl.signal,\n (createdReset) => {\n reset = createdReset;\n },\n () => release(),\n );\n\n const resetPresence = reset;\n\n if (!resetPresence) throw new Error('Presence reset callback was not initialized');\n\n const release = () => releasePresence(room, resetPresence);\n\n acquirePresence(room, resetPresence);\n\n return presence;\n },\n\n get rooms() {\n return rooms;\n },\n\n send<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void {\n if (disposed) throw new PulseDisposedError();\n\n const transformed = options.transform ? options.transform({ event, payload }) : { event, payload };\n\n if (transformed === null) return;\n\n sendInternal(encode({ ...transformed, type: 'message' }));\n },\n\n get status() {\n return status;\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n wait<K extends EventKey<TServer>>(\n event: K,\n opts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<TServer[K]> {\n return createWaitPromise<TServer[K]>(event, disposalCtrl.signal, opts, (waitEvent, handler) =>\n listeners.add(null, waitEvent, handler),\n );\n },\n };\n\n return pulse;\n}\n"],"mappings":"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"}
package/dist/pulse.d.ts CHANGED
@@ -1,16 +1,7 @@
1
- import type { MessageMap, Pulse, PulseOptions } from './types';
1
+ import type { ChannelDefinitions, MessageMap, PresenceDefinitions, Pulse, PulseOptions } from './types';
2
2
  /**
3
- * Create a managed WebSocket connection with typed messaging, channels,
4
- * rooms, and presence.
5
- *
6
- * @example
7
- * ```ts
8
- * const pulse = createPulse<ServerEvents, ClientEvents>('wss://api.example.com/ws', {
9
- * reconnect: true,
10
- * heartbeat: true,
11
- * });
12
- * pulse.on('message', (data) => console.log(data));
13
- * ```
3
+ * Create a typed, explicitly connected WebSocket client with scoped channels,
4
+ * rooms, presence, reconnect, and heartbeat support.
14
5
  */
15
- export declare function createPulse<TServer extends MessageMap = MessageMap, TClient extends MessageMap = MessageMap>(url: string, opts?: PulseOptions): Pulse<TServer, TClient>;
6
+ export declare function createPulse<TServer extends MessageMap = MessageMap, TClient extends MessageMap = MessageMap, TChannels extends ChannelDefinitions = ChannelDefinitions, TPresence extends PresenceDefinitions = PresenceDefinitions>(url: string, options?: PulseOptions): Pulse<TServer, TClient, TChannels, TPresence>;
16
7
  //# sourceMappingURL=pulse.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pulse.d.ts","sourceRoot":"","sources":["../src/pulse.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,UAAU,EAGV,KAAK,EAEL,YAAY,EAGb,MAAM,SAAS,CAAC;AAsEjB;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,OAAO,SAAS,UAAU,GAAG,UAAU,EAAE,OAAO,SAAS,UAAU,GAAG,UAAU,EAC1G,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,YAAiB,GACtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAkiBzB"}
1
+ {"version":3,"file":"pulse.d.ts","sourceRoot":"","sources":["../src/pulse.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,kBAAkB,EAElB,UAAU,EAEV,mBAAmB,EACnB,KAAK,EAEL,YAAY,EAGb,MAAM,SAAS,CAAC;AAmEjB;;;GAGG;AACH,wBAAgB,WAAW,CACzB,OAAO,SAAS,UAAU,GAAG,UAAU,EACvC,OAAO,SAAS,UAAU,GAAG,UAAU,EACvC,SAAS,SAAS,kBAAkB,GAAG,kBAAkB,EACzD,SAAS,SAAS,mBAAmB,GAAG,mBAAmB,EAC3D,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CA2hBxF"}
@@ -1,2 +1,2 @@
1
- var Pulse=(function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},r=class extends n{url;constructor(e,t,n){super(e,n),this.url=t}},i=class extends n{event;constructor(e,t){super(`Timed out waiting for "${e}"`,t),this.event=e}},a=class extends n{constructor(e){super(`Aborted`,e)}},o=class extends n{constructor(e=`Pulse`,t){super(`${e} instance is disposed`,t)}},s=class extends n{raw;constructor(e,t,n){super(e,n),this.raw=t}};function c(e,...t){return t.length===0?e:t.reduce(l,e)}function l(e,t){if(e.aborted)return e;if(t.aborted)return t;let n=new AbortController,r=()=>n.abort(e.reason),i=()=>n.abort(t.reason);return e.addEventListener(`abort`,r,{once:!0}),t.addEventListener(`abort`,i,{once:!0}),n.signal.addEventListener(`abort`,()=>{e.removeEventListener(`abort`,r),t.removeEventListener(`abort`,i)},{once:!0}),n.signal}function u(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function d(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=setTimeout(n,e);t?.addEventListener(`abort`,()=>{clearTimeout(r),n()},{once:!0})})}function f(e,t=3e4){let n=Math.min(1e3*2**e,t);return Math.random()*n}function p(e,t,n,r){return new Promise((o,s)=>{let l=[t],u;if(n?.signal&&l.push(n.signal),n?.timeout!==void 0){let t=new AbortController;u=setTimeout(()=>{t.abort(new i(e))},n.timeout),l.push(t.signal)}let d=l.length===1?l[0]:c(l[0],...l.slice(1));if(d.aborted){clearTimeout(u),s(d.reason instanceof i?d.reason:new a);return}let f=()=>{},p=()=>{clearTimeout(u),f(),s(d.reason instanceof i?d.reason:new a)};d.addEventListener(`abort`,p,{once:!0}),f=r(e,e=>{clearTimeout(u),d.removeEventListener(`abort`,p),o(e)})})}function m(e,t,n,r,i){let a=u(r),o=new Set,s=a.signal.aborted;return a.signal.addEventListener(`abort`,()=>{s=!0},{once:!0}),{get disposalSignal(){return a.signal},dispose(){if(!s){s=!0,a.abort();for(let e of o)e();o.clear(),i?.()}},get disposed(){return s},get name(){return e},on(t,r){if(s)return`${String(t)}${e}`,()=>{};let i=n(e,t,r);return o.add(i),()=>{i(),o.delete(i)}},once(t,r){if(s)return`${String(t)}${e}`,()=>{};let i=()=>{};return i=n(e,t,e=>{o.delete(i),i(),r(e)}),o.add(i),()=>{o.delete(i),i()}},send(n,r){if(s){`${String(n)}${e}`;return}t(e,n,r)},[Symbol.dispose](){this.dispose()},wait(t,r){return p(t,a.signal,r,(t,r)=>n(e,t,r))}}}function h(e){return JSON.stringify(e)}function g(e){if(typeof e!=`string`)throw new s(`Expected string message data`,e);let t;try{t=JSON.parse(e)}catch{throw new s(`JSON parse error`,e)}if(typeof t!=`object`||!t||!(`type`in t)||typeof t.type!=`string`)throw new s(`Missing or invalid "type" field`,t);return t}function _(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function v(e,t){if(!_(e))return null;let n=e[t];return typeof n==`string`?n:null}function y(e){return v(e,`code`)!==null&&v(e,`message`)!==null}function b(e){return v(e,`room`)!==null}function x(e){return v(e,`room`)!==null}function S(e){return!_(e)||v(e,`event`)===null||!(`payload`in e)?!1:e.channel===void 0||typeof e.channel==`string`}function C(e){return _(e)&&typeof e.ts==`number`}function w(e){return _(e)&&v(e,`id`)!==null&&v(e,`room`)!==null&&`state`in e}function T(e){return v(e,`id`)!==null&&v(e,`room`)!==null}function E(e){return _(e)&&v(e,`room`)!==null&&_(e.members)}function D(e){return v(e,`channel`)!==null}function O(e){return v(e,`channel`)!==null}function k(e){let t=g(e);switch(t.type){case`error`:if(y(t))return{frame:t,kind:`known`};break;case`joined`:if(b(t))return{frame:t,kind:`known`};break;case`left`:if(x(t))return{frame:t,kind:`known`};break;case`message`:if(S(t))return{frame:t,kind:`known`};break;case`pong`:if(C(t))return{frame:t,kind:`known`};break;case`presence_join`:if(w(t))return{frame:t,kind:`known`};break;case`presence_leave`:if(T(t))return{frame:t,kind:`known`};break;case`presence_state`:if(E(t))return{frame:t,kind:`known`};break;case`subscribed`:if(D(t))return{frame:t,kind:`known`};break;case`unsubscribed`:if(O(t))return{frame:t,kind:`known`};break;default:return{kind:`unknown`,type:t.type}}throw new s(`Malformed "${t.type}" frame`,t)}var A=3e4,j=5e3;function M(e,t,n){if(!e)return{onPong(){},start(){},stop(){}};let r=e===!0?{}:e,i=r.interval??A,a=r.timeout??j,o,s,c=!1;function l(){o=setTimeout(()=>{t(h({ts:Date.now(),type:`ping`})),s=setTimeout(()=>{`${a}`,n()},a)},i)}return{onPong(){clearTimeout(s),s=void 0,c&&l()},start(){c=!0,clearTimeout(o),clearTimeout(s),s=void 0,l()},stop(){c=!1,clearTimeout(o),clearTimeout(s),o=void 0,s=void 0}}}function N(e,n,r,i,a){let o=u(i),s=o.signal.aborted;o.signal.addEventListener(`abort`,()=>{s=!0},{once:!0});let c=(0,t.signal)(new Map),l=new Set,d=new Set,f=[r.onState(t=>{if(t.room!==e)return;let n=new Map;for(let[e,r]of Object.entries(t.members))n.set(e,r);c.value=n}),r.onJoin(t=>{if(t.room!==e)return;let n=new Map(c.value);n.set(t.id,t.state),c.value=n;for(let e of l)e(t.id,t.state)}),r.onLeave(t=>{if(t.room!==e)return;let n=new Map(c.value);n.delete(t.id),c.value=n;for(let e of d)e(t.id)})];return{get disposalSignal(){return o.signal},dispose(){if(!s){s=!0,n(h({room:e,type:`leave`})),o.abort();for(let e of f)e();f.length=0,l.clear(),d.clear(),a?.()}},get disposed(){return s},onJoin(t){return s?(`${e}`,()=>{}):(l.add(t),()=>l.delete(t))},onLeave(t){return s?(`${e}`,()=>{}):(d.add(t),()=>d.delete(t))},get room(){return e},get state(){return c},[Symbol.dispose](){this.dispose()},update(t){s||n(h({room:e,state:t,type:`presence`}))}}}var P=5;function F(e){if(!e)return{attempt:async()=>({ok:!1}),reset(){}};let t=e===!0?{}:e,n=t.maxAttempts??P,r=typeof t.delay==`function`?t.delay:typeof t.delay==`number`?()=>t.delay:f,i=0;return{async attempt(e,t,a){if(i>=n)return{ok:!1};let o=r(i);if(i++,a?.(i),await d(o,t),t.aborted)return{ok:!1};try{return await e(),i=0,{ok:!0}}catch(e){return{error:e,ok:!1}}},reset(){i=0}}}var I=class{map=new Map;add(e,t,n){let r=this.map.get(e);r||(r=new Map,this.map.set(e,r));let i=r.get(t);return i||(i=new Set,r.set(t,i)),i.add(n),()=>{i.delete(n),i.size===0&&r.delete(t),r.size===0&&this.map.delete(e)}}dispatch(e,t,n){this.map.get(e)?.get(t)?.forEach(e=>e(n))}clear(){this.map.clear()}};function L(e,n={}){let s=new AbortController,l=(0,t.signal)(`connecting`),u=(0,t.signal)(new Set),d=new I,f=n.middleware??[],g=new Map,_=new Map,v=null,y=!1,b=!1,x=[],S=new Map,C=new Map,w=n.buffer,T=w?[]:null,E=typeof w==`object`&&w?w.maxSize??50:50,D=F(n.reconnect),O=M(n.heartbeat,e=>{v?.readyState===WebSocket.OPEN&&v.send(e)},()=>{v?.close(4e3,`heartbeat timeout`)});function A(e){v?.readyState===WebSocket.OPEN?v.send(e):T===null?`${l.value}`:(T.length>=E&&T.shift(),T.push(e))}function j(e,t,n){let r=0;function i(){if(r>=f.length){n();return}let a=f[r++];a(e,t,i)}i()}function P(){y||(v=new WebSocket(e,n.protocols),v.onopen=()=>{b=!1,l.value=`open`,D.reset(),O.start(),n.onOpen?.();let e=x.splice(0);for(let t of e)t.resolve();if(T&&T.length>0){let e=T.splice(0);for(let t of e)v.send(t)}for(let e of S.keys())v.send(h({channel:e,type:`subscribe`}))},v.onmessage=e=>{n.onMessage?.(e);let t;try{t=k(e.data)}catch(e){`${String(e)}`;return}try{R(t)}catch(e){`${String(e)}`}},v.onerror=()=>{n.onError&&n.onError(new r(`WebSocket error`,e));let t=x.splice(0);for(let n of t)n.reject(new r(`WebSocket error`,e))},v.onclose=t=>{O.stop(),n.onClose?.(t.code,t.reason);let i=x.splice(0);for(let t of i)t.reject(new r(`Connection closed before open`,e));if(b||y){l.value=`closed`;return}L()})}async function L(){l.value=`reconnecting`;let e=await D.attempt(()=>new Promise((e,t)=>{x.push({reject:t,resolve:e}),P()}),s.signal,n.onReconnect);!e.ok&&!y&&(e.error&&`${String(e.error)}`,l.value=`closed`)}function R(e){if(e.kind===`unknown`){`${e.type}`;return}let t=e.frame;switch(t.type){case`error`:`${t.code}${t.message}`;break;case`joined`:{let e=new Set(u.value);e.add(t.room),u.value=e;let n=g.get(t.room);if(n){g.delete(t.room);for(let e of n)e()}break}case`left`:{let e=new Set(u.value);e.delete(t.room),u.value=e;let n=_.get(t.room);if(n){_.delete(t.room);for(let e of n)e()}break}case`message`:d.dispatch(t.channel??null,t.event,t.payload);break;case`pong`:O.onPong();break;case`presence_join`:d.dispatch(null,t.type,t);break;case`presence_leave`:d.dispatch(null,t.type,t);break;case`presence_state`:d.dispatch(null,t.type,t)}}function z(e,t,n,r){return y?Promise.reject(new o):new Promise((o,l)=>{let u=[];r?.signal&&u.push(r.signal);let d;if(r?.timeout!==void 0){let e=new AbortController;d=setTimeout(()=>e.abort(new i(n)),r.timeout),u.push(e.signal)}let f=u.length===0?s.signal:c(s.signal,...u);if(f.aborted){clearTimeout(d),l(f.reason instanceof i?f.reason:new a);return}let p=()=>{},m=()=>{clearTimeout(d),f.removeEventListener(`abort`,p),o()};p=()=>{clearTimeout(d);let n=e.get(t);n?.delete(m),n?.size===0&&e.delete(t),l(f.reason instanceof i?f.reason:new a)};let g=e.get(t),_=!g;g||(g=new Set,e.set(t,g)),g.add(m),f.addEventListener(`abort`,p,{once:!0});let y=()=>A(h({room:t,type:n}));_&&(v?.readyState===WebSocket.OPEN?y():B.connect().then(y).catch(n=>{clearTimeout(d),f.removeEventListener(`abort`,p);let r=e.get(t);r?.delete(m),r?.size===0&&e.delete(t),l(n)}))})}let B={channel(e){let t=S.get(e);if(t)return t;y&&`${e}`;let n=m(e,(e,t,n)=>{j(t,n,()=>{A(h({channel:e,event:t,payload:n,type:`message`}))})},(e,t,n)=>d.add(e,t,n),s.signal,()=>{S.delete(e),A(h({channel:e,type:`unsubscribe`}))});return y||S.set(e,n),v?.readyState===WebSocket.OPEN&&v.send(h({channel:e,type:`subscribe`})),n},connect(){return y?Promise.reject(new o):v?.readyState===WebSocket.OPEN?Promise.resolve():new Promise((e,t)=>{x.push({reject:t,resolve:e}),(!v||v.readyState>WebSocket.OPEN)&&P()})},disconnect(e=1e3,t=``){b=!0,v?.close(e,t)},get disposalSignal(){return s.signal},dispose(){if(y)return;y=!0,b=!0,O.stop(),v?.close(1e3,`disposed`),v=null;let e=x.splice(0);for(let t of e)t.reject(new o);S.clear(),C.clear(),g.clear(),_.clear(),d.clear(),s.abort()},get disposed(){return y},join(e,t){return z(g,e,`join`,t)},leave(e,t){return z(_,e,`leave`,t)},on(e,t){return y?(`${String(e)}`,()=>{}):d.add(null,e,t)},once(e,t){if(y)return`${String(e)}`,()=>{};let n=()=>{};return n=d.add(null,e,e=>{n(),t(e)}),n},presence(e){let t=C.get(e);if(t)return t;let n=N(e,A,{onJoin:e=>d.add(null,`presence_join`,t=>e(t)),onLeave:e=>d.add(null,`presence_leave`,t=>e(t)),onState:e=>d.add(null,`presence_state`,t=>e(t))},s.signal,()=>{C.delete(e)});return y||C.set(e,n),y?`${e}`:B.join(e).catch(t=>{t instanceof a||t instanceof o||`${e}${String(t)}`}),n},get rooms(){return u},send(e,t){if(y){`${String(e)}`;return}j(e,t,()=>{A(h({event:e,payload:t,type:`message`}))})},get status(){return l},[Symbol.dispose](){this.dispose()},wait(e,t){return p(e,s.signal,t,(e,t)=>d.add(null,e,t))}};return n.lazy||P(),B}return e.PulseAbortError=a,e.PulseConnectionError=r,e.PulseDisposedError=o,e.PulseError=n,e.PulseProtocolError=s,e.PulseTimeoutError=i,e.createPulse=L,e})({},Ripple);
1
+ var Pulse=(function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},r=class extends n{url;constructor(e,t,n){super(e,n),this.url=t}},i=class extends n{event;constructor(e,t){super(`Timed out waiting for "${e}"`,t),this.event=e}},a=class extends n{constructor(e){super(`Aborted`,e)}},o=class extends n{constructor(e=`Pulse`,t){super(`${e} instance is disposed`,t)}},s=class extends n{raw;constructor(e,t,n){super(e,n),this.raw=t}};function c(e,...t){return t.length===0?e:t.reduce(l,e)}function l(e,t){if(e.aborted)return e;if(t.aborted)return t;let n=new AbortController,r=()=>n.abort(e.reason),i=()=>n.abort(t.reason);return e.addEventListener(`abort`,r,{once:!0}),t.addEventListener(`abort`,i,{once:!0}),n.signal.addEventListener(`abort`,()=>{e.removeEventListener(`abort`,r),t.removeEventListener(`abort`,i)},{once:!0}),n.signal}function u(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function d(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=setTimeout(n,e);t?.addEventListener(`abort`,()=>{clearTimeout(r),n()},{once:!0})})}function f(e,t=3e4){let n=Math.min(1e3*2**e,t);return Math.random()*n}var p=5;function m(e,t,n,i){let a=n===!0?{}:n||void 0,s=a?a.maxAttempts??p:0,c=a?.delay,l=typeof c==`function`?c:typeof c==`number`?()=>c:f,u=!1,m=!1,h=!1,g,_,v,y=new Set;function b(e){i.onStatus(e)}function x(e){for(let t of y)t.reject(e);y.clear()}function S(){for(let e of y)e.resolve();y.clear()}function C(e,t){v?.socket===e&&(v.reject(t),v=void 0)}function w(){if(_?.readyState===WebSocket.OPEN)return Promise.resolve();if(v)return v.promise;let n=new WebSocket(e,t);_=n;let a,o,s=new Promise((e,t)=>{a=e,o=t});return v={promise:s,reject:o,resolve:a,socket:n},n.onopen=()=>{if(_!==n||!m){n.close(1e3,`stale connection`);return}v?.socket===n&&(v.resolve(),v=void 0),b(`open`),i.onOpen(),S()},n.onmessage=e=>{_===n&&i.onMessage(e)},n.onerror=()=>{if(_!==n)return;let t=new r(`WebSocket error`,e);i.onError(t),C(n,t),x(t)},n.onclose=()=>{if(_!==n)return;_=void 0,i.onClose();let t=new r(`Connection closed`,e);if(C(n,t),!m||u){b(`closed`);return}h||T()},s}async function T(){if(h||u||!m)return;h=!0;let t=new AbortController;g=t,b(`reconnecting`);let n;for(let i=0;i<s&&m&&!u&&(await d(l(i),t.signal),!(t.signal.aborted||!m||u));i++)try{await w(),h=!1;return}catch(t){n=t instanceof r?t:new r(`Reconnect failed`,e)}!t.signal.aborted&&m&&!u&&(m=!1,b(`closed`),i.onError(n??new r(`Reconnect budget exhausted`,e)),x(n??new r(`Reconnect budget exhausted`,e))),g===t&&(g=void 0),h=!1}function E(){g?.abort(),g=void 0,h=!1}return{connect(){if(u)return Promise.reject(new o);if(m=!0,_?.readyState===WebSocket.OPEN)return Promise.resolve();let e=new Promise((e,t)=>{y.add({reject:t,resolve:e})});return!v&&!h&&(b(`connecting`),w().catch(()=>{})),e},disconnect(t=1e3,n=``){m=!1,E(),x(new r(`Connection closed`,e)),b(`closed`),_?.close(t,n)},dispose(){u||(u=!0,m=!1,E(),x(new o),_?.close(1e3,`disposed`),_=void 0,v=void 0,b(`closed`))},forceReconnect(e,t){m&&_?.readyState===WebSocket.OPEN&&_.close(e,t)},get open(){return _?.readyState===WebSocket.OPEN},send(t){if(_?.readyState!==WebSocket.OPEN)throw new r(`Connection is not open`,e);_.send(t)}}}function h(e,t,n,r){return new Promise((o,s)=>{let l=[t],u;if(n?.signal&&l.push(n.signal),n?.timeout!==void 0){let t=new AbortController;u=setTimeout(()=>{t.abort(new i(e))},n.timeout),l.push(t.signal)}let d=l.length===1?l[0]:c(l[0],...l.slice(1));if(d.aborted){clearTimeout(u),s(d.reason instanceof i?d.reason:new a);return}let f=()=>{},p=()=>{clearTimeout(u),f(),s(d.reason instanceof i?d.reason:new a)};d.addEventListener(`abort`,p,{once:!0}),f=r(e,e=>{clearTimeout(u),d.removeEventListener(`abort`,p),o(e)})})}function g(e,t,n,r,i){let a=u(r),s=new Set,c=a.signal.aborted;return a.signal.addEventListener(`abort`,()=>{c=!0},{once:!0}),{get disposalSignal(){return a.signal},dispose(){if(!c){c=!0,a.abort();for(let e of s)e();s.clear(),i?.()}},get disposed(){return c},get name(){return e},on(t,r){if(c)throw new o(`Channel "${e}"`);let i=n(e,t,r);return s.add(i),()=>{i(),s.delete(i)}},once(t,r){if(c)throw new o(`Channel "${e}"`);let i=()=>{};return i=n(e,t,e=>{s.delete(i),i(),r(e)}),s.add(i),()=>{s.delete(i),i()}},send(n,r){if(c)throw new o(`Channel "${e}"`);t(e,n,r)},[Symbol.dispose](){this.dispose()},wait(t,r){return h(t,a.signal,r,(t,r)=>n(e,t,r))}}}function _(e){return JSON.stringify(e)}function v(e){if(typeof e!=`string`)throw new s(`Expected string message data`,e);let t;try{t=JSON.parse(e)}catch{throw new s(`JSON parse error`,e)}if(typeof t!=`object`||!t||!(`type`in t)||typeof t.type!=`string`)throw new s(`Missing or invalid "type" field`,t);return t}function y(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function b(e,t){if(!y(e))return null;let n=e[t];return typeof n==`string`?n:null}function x(e){return b(e,`code`)!==null&&b(e,`message`)!==null}function S(e){return b(e,`room`)!==null}function C(e){return b(e,`room`)!==null}function w(e){return!y(e)||b(e,`event`)===null||!(`payload`in e)?!1:e.channel===void 0||typeof e.channel==`string`}function T(e){return y(e)&&typeof e.ts==`number`}function E(e){return y(e)&&b(e,`id`)!==null&&b(e,`room`)!==null&&`state`in e}function D(e){return b(e,`id`)!==null&&b(e,`room`)!==null}function O(e){return y(e)&&b(e,`room`)!==null&&y(e.members)}function k(e){return b(e,`channel`)!==null}function A(e){return b(e,`channel`)!==null}function j(e){let t=v(e);switch(t.type){case`error`:if(x(t))return{frame:t,kind:`known`};break;case`joined`:if(S(t))return{frame:t,kind:`known`};break;case`left`:if(C(t))return{frame:t,kind:`known`};break;case`message`:if(w(t))return{frame:t,kind:`known`};break;case`pong`:if(T(t))return{frame:t,kind:`known`};break;case`presence_join`:if(E(t))return{frame:t,kind:`known`};break;case`presence_leave`:if(D(t))return{frame:t,kind:`known`};break;case`presence_state`:if(O(t))return{frame:t,kind:`known`};break;case`subscribed`:if(k(t))return{frame:t,kind:`known`};break;case`unsubscribed`:if(A(t))return{frame:t,kind:`known`};break;default:return{kind:`unknown`,type:t.type}}throw new s(`Malformed "${t.type}" frame`,t)}var M=3e4,N=5e3;function P(e,t,n){if(!e)return{onPong(){},start(){},stop(){}};let r=e===!0?{}:e,i=r.interval??M,a=r.timeout??N,o,s,c=!1;function l(){o=setTimeout(()=>{t(_({ts:Date.now(),type:`ping`})),s=setTimeout(()=>{n()},a)},i)}return{onPong(){clearTimeout(s),s=void 0,c&&l()},start(){c=!0,clearTimeout(o),clearTimeout(s),s=void 0,l()},stop(){c=!1,clearTimeout(o),clearTimeout(s),o=void 0,s=void 0}}}function F(e,n,r,i,a,s){let c=u(i),l=c.signal.aborted;c.signal.addEventListener(`abort`,()=>{l=!0},{once:!0});let d=(0,t.signal)(new Map);a?.(()=>{d.value=new Map});let f=new Set,p=new Set,m=[r.onState(t=>{if(t.room!==e)return;let n=new Map;for(let[e,r]of Object.entries(t.members))n.set(e,r);d.value=n}),r.onJoin(t=>{if(t.room!==e)return;let n=new Map(d.value);n.set(t.id,t.state),d.value=n;for(let e of f)e(t.id,t.state)}),r.onLeave(t=>{if(t.room!==e)return;let n=new Map(d.value);n.delete(t.id),d.value=n;for(let e of p)e(t.id)})];return{get disposalSignal(){return c.signal},dispose(){if(!l){l=!0,c.abort();for(let e of m)e();m.length=0,f.clear(),p.clear(),s?.()}},get disposed(){return l},onJoin(t){if(l)throw new o(`Presence "${e}"`);return f.add(t),()=>f.delete(t)},onLeave(t){if(l)throw new o(`Presence "${e}"`);return p.add(t),()=>p.delete(t)},get room(){return e},get state(){return d},[Symbol.dispose](){this.dispose()},update(t){if(l)throw new o(`Presence "${e}"`);n(e,t)}}}var I=class{map=new Map;add(e,t,n){let r=this.map.get(e);r||(r=new Map,this.map.set(e,r));let i=r.get(t);return i||(i=new Set,r.set(t,i)),i.add(n),()=>{i.delete(n),i.size===0&&r.delete(t),r.size===0&&this.map.delete(e)}}clear(){this.map.clear()}dispatch(e,t,n){for(let r of this.map.get(e)?.get(t)??[])r(n)}};function L(e,c={}){let l=new AbortController,u=(0,t.signal)(`closed`),d=(0,t.signal)(new Set),f=new I,p=new Map,v=new Map,y=new Set,b=new Map,x=new Map,S=new Map,C=!1;function w(e){c.onError?.(e)}function T(e){N.send(e)}function E(e){return y.has(e)||(v.get(e)??0)>0}function D(e,t){let n=new Set(d.value);t?n.add(e):n.delete(e),d.value=n}function O(){d.value=new Set;for(let e of x.values())for(let t of e)t()}function k(e,t=!0){let n=!1;for(let[t,r]of S){let i=r;for(;i;){i.operation===`join`&&y.delete(t);for(let t of i.callbacks)t.reject(e),n=!0;i=i.next}}S.clear(),n&&t&&w(e)}function A(){L.stop(),O(),k(new r(`Connection closed before room confirmation`,e))}function M(){for(let e of p.keys())T(_({channel:e,type:`subscribe`}));for(let e of new Set([...y,...v.keys()]))E(e)&&T(_({room:e,type:`join`}));for(let[e,t]of b)E(e)&&T(_({room:e,state:t,type:`presence`}))}let N=m(e,c.protocols,c.reconnect,{onClose(){A()},onError:w,onMessage(e){let t;try{t=j(e.data)}catch(t){w(t instanceof s?t:new s(`Failed to decode frame`,e.data));return}if(t.kind===`unknown`){w(new s(`Unknown frame type "${t.type}"`,e.data));return}try{z(t)}catch(e){w(new s(`Frame handler threw`,t.frame,{cause:e}))}},onOpen(){M(),L.start()},onStatus(e){u.value=e}}),L=P(c.heartbeat,e=>{N.open&&T(e)},()=>N.forceReconnect(4e3,`heartbeat timeout`));function R(t,i){let a=S.get(t);if(!a||a.operation!==i)return;for(let e of a.callbacks)e.resolve();let o=a.next;if(!o){S.delete(t);let a=i===`join`&&!E(t)?`leave`:i===`leave`&&E(t)?`join`:void 0;if(a){let i={callbacks:new Set,operation:a};S.set(t,i);try{T(_({room:t,type:a}))}catch(i){S.delete(t),a===`join`&&y.delete(t),w(i instanceof n?i:new r(`Failed to send room request`,e))}}return}S.set(t,o);try{T(_({room:t,type:o.operation}))}catch(i){S.delete(t),o.operation===`join`&&y.delete(t);for(let t of o.callbacks)t.reject(i instanceof n?i:new r(`Failed to send room request`,e))}}function z(e){let t=e.frame;switch(t.type){case`error`:w(new s(`Server error [${t.code}]: ${t.message}`,t));break;case`joined`:E(t.room)&&D(t.room,!0),R(t.room,`join`);break;case`left`:D(t.room,!1),R(t.room,`leave`);break;case`message`:f.dispatch(t.channel??null,t.event,t.payload);break;case`pong`:L.onPong();break;case`presence_join`:case`presence_leave`:case`presence_state`:f.dispatch(null,t.type,t)}}function B(t,s,c){if(C)return s===`join`&&y.delete(t),Promise.reject(new o);if(!N.open)return s===`join`&&y.delete(t),Promise.reject(new r(`Connection is not open`,e));let l=S.get(t);if(s===`join`&&d.value.has(t)&&!l||s===`leave`&&!d.value.has(t)&&!l)return Promise.resolve();let u=l;if(u){for(;u.next;)u=u.next;if(u.operation!==s){let e={callbacks:new Set,operation:s};u.next=e,u=e}}else u={callbacks:new Set,operation:s};return new Promise((o,d)=>{let f=c?.timeout===void 0?void 0:new AbortController,p=c?.signal&&f?AbortSignal.any([c.signal,f.signal]):c?.signal??f?.signal,m=c?.timeout===void 0?void 0:setTimeout(()=>f?.abort(),c.timeout),h={reject(e){clearTimeout(m),p?.removeEventListener(`abort`,g),d(e)},resolve(){clearTimeout(m),p?.removeEventListener(`abort`,g),o()}},g=()=>{u.callbacks.delete(h),u.callbacks.size===0&&s===`join`&&y.delete(t),h.reject(f?.signal.aborted?new i(s):new a)};if(p?.aborted){g();return}if(u.callbacks.add(h),p?.addEventListener(`abort`,g,{once:!0}),!l){S.set(t,u);try{T(_({room:t,type:s}))}catch(i){u.callbacks.delete(h),u.callbacks.size===0&&S.delete(t),h.reject(i instanceof n?i:new r(`Failed to send room request`,e))}}})}function V(e){let t=p.get(e)??0;p.set(e,t+1),t===0&&N.open&&T(_({channel:e,type:`subscribe`}))}function H(e){let t=p.get(e);if(t){if(t>1){p.set(e,t-1);return}p.delete(e),N.open&&T(_({channel:e,type:`unsubscribe`}))}}function U(e,t){let n=v.get(e)??0,r=x.get(e)??new Set;r.add(t),x.set(e,r),v.set(e,n+1),n===0&&N.open&&T(_({room:e,type:`join`}))}function W(e,t){x.get(e)?.delete(t),x.get(e)?.size===0&&x.delete(e);let n=v.get(e);if(n){if(n>1){v.set(e,n-1);return}v.delete(e),b.delete(e),!y.has(e)&&N.open&&T(_({room:e,type:`leave`}))}}return{channel(e){if(C)throw new o;return V(e),g(e,(e,t,n)=>{let r=c.transform?c.transform({channel:e,event:t,payload:n}):{channel:e,event:t,payload:n};r!==null&&T(_({...r,type:`message`}))},(e,t,n)=>f.add(e,t,n),l.signal,()=>H(e))},connect(){return C?Promise.reject(new o):N.connect()},disconnect(e=1e3,t=``){C||(A(),N.disconnect(e,t))},get disposalSignal(){return l.signal},dispose(){C||(C=!0,L.stop(),O(),k(new o,!1),N.dispose(),p.clear(),v.clear(),y.clear(),b.clear(),x.clear(),S.clear(),f.clear(),l.abort())},get disposed(){return C},join(e,t){return y.add(e),B(e,`join`,t)},leave(e,t){return y.delete(e),E(e)?Promise.resolve():B(e,`leave`,t)},on(e,t){if(C)throw new o;return f.add(null,e,t)},once(e,t){if(C)throw new o;let n=()=>{};return n=f.add(null,e,e=>{n(),t(e)}),n},presence(e){if(C)throw new o;let t,n=F(e,(e,t)=>{T(_({room:e,state:t,type:`presence`})),b.set(e,t)},{onJoin:e=>f.add(null,`presence_join`,t=>e(t)),onLeave:e=>f.add(null,`presence_leave`,t=>e(t)),onState:e=>f.add(null,`presence_state`,t=>e(t))},l.signal,e=>{t=e},()=>i()),r=t;if(!r)throw Error(`Presence reset callback was not initialized`);let i=()=>W(e,r);return U(e,r),n},get rooms(){return d},send(e,t){if(C)throw new o;let n=c.transform?c.transform({event:e,payload:t}):{event:e,payload:t};n!==null&&T(_({...n,type:`message`}))},get status(){return u},[Symbol.dispose](){this.dispose()},wait(e,t){return h(e,l.signal,t,(e,t)=>f.add(null,e,t))}}}return e.PulseAbortError=a,e.PulseConnectionError=r,e.PulseDisposedError=o,e.PulseError=n,e.PulseProtocolError=s,e.PulseTimeoutError=i,e.createPulse=L,e})({},Ripple);
2
2
  //# sourceMappingURL=pulse.iife.js.map