@vielzeug/pulse 2.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # @vielzeug/pulse
2
2
 
3
- Typed WebSocket sessions with explicit connection ownership, scoped channels, ref-counted rooms with reactive presence, reconnect restoration, and heartbeat support.
3
+ > Typed WebSocket client with channels, rooms, presence, reconnect
4
4
 
5
- ## Install
5
+ ## Installation
6
6
 
7
7
  ```sh
8
- pnpm add @vielzeug/pulse @vielzeug/ripple
8
+ pnpm add @vielzeug/pulse
9
+ npm install @vielzeug/pulse
10
+ yarn add @vielzeug/pulse
9
11
  ```
10
12
 
11
13
  ## Quick Start
@@ -50,24 +52,14 @@ try {
50
52
  pulse.dispose();
51
53
  ```
52
54
 
53
- ## Key Behavior
54
-
55
- - Call `connect()` before sending messages or publishing presence. Room scopes can be created before connecting — joins are sent after the connection opens.
56
- - Define server events, client events, channel schemas, and room schemas at `createPulse()` so named scopes are type-safe.
57
- - Each `channel()` and `room()` call returns an independent disposable scope. Server subscriptions and room memberships use reference counting.
58
- - Reconnect restores channels, room memberships, and the last successfully published local presence state.
59
- - `send()` throws `PulseConnectionError` while disconnected; Pulse never silently drops or buffers application messages.
60
- - `room()` returns a `RoomScope` with a `joined` promise. When the room definition includes `presence`, the scope also exposes reactive presence state, `updatePresence()`, and `onJoin()`/`onLeave()` handlers.
61
- - Call `tap()` to observe lifecycle events; Pulse reports transport and protocol errors there rather than throwing asynchronously.
62
-
63
- ## Migration
64
-
65
- See the [Pulse migration guide](https://vielzeug.dev/pulse/migration).
66
-
67
55
  ## Documentation
68
56
 
69
57
  - [Overview](https://vielzeug.dev/pulse/)
70
- - [Usage](https://vielzeug.dev/pulse/usage)
71
- - [API](https://vielzeug.dev/pulse/api)
58
+ - [Usage Guide](https://vielzeug.dev/pulse/usage)
59
+ - [API Reference](https://vielzeug.dev/pulse/api)
72
60
  - [Examples](https://vielzeug.dev/pulse/examples)
73
- - [Pulse Migration](https://vielzeug.dev/pulse/migration)
61
+ - [Migration Guide](https://vielzeug.dev/pulse/migration)
62
+
63
+ ## License
64
+
65
+ MIT © [Helmuth Saatkamp](https://github.com/helmuthdu) — part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
@@ -0,0 +1,2 @@
1
+ function e(e,t){let n=e,r=new Set;return{getSnapshot:()=>n,set(e){if(!Object.is(n,e)){n=e;for(let e of[...r])try{e()}catch(e){t(e)}}},subscribe(e){return r.add(e),()=>r.delete(e)}}}exports.createStore=e;
2
+ //# sourceMappingURL=_store.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_store.cjs","names":[],"sources":["../src/_store.ts"],"sourcesContent":["import type { ExternalStore, Unsubscribe } from './types';\n\nexport type MutableStore<T> = ExternalStore<T> & {\n set(next: T): void;\n};\n\nexport function createStore<T>(initial: T, onListenerError: (error: unknown) => void): MutableStore<T> {\n let current = initial;\n const listeners = new Set<() => void>();\n\n return {\n getSnapshot: () => current,\n set(next) {\n if (Object.is(current, next)) return;\n current = next;\n\n for (const listener of [...listeners]) {\n try {\n listener();\n } catch (error) {\n onListenerError(error);\n }\n }\n },\n subscribe(listener): Unsubscribe {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n"],"mappings":"AAMA,SAAgB,EAAe,EAAY,EAA4D,CACrG,IAAI,EAAU,EACR,EAAY,IAAI,IAEtB,MAAO,CACL,gBAAmB,EACnB,IAAI,EAAM,CACJ,WAAO,GAAG,EAAS,CAAI,EAC3B,GAAU,EAEV,IAAK,IAAM,IAAY,CAAC,GAAG,CAAS,EAClC,GAAI,CACF,EAAS,CACX,OAAS,EAAO,CACd,EAAgB,CAAK,CACvB,CAPQ,CASZ,EACA,UAAU,EAAuB,CAE/B,OADA,EAAU,IAAI,CAAQ,MACT,EAAU,OAAO,CAAQ,CACxC,CACF,CACF"}
@@ -0,0 +1,6 @@
1
+ import type { ExternalStore } from './types';
2
+ export type MutableStore<T> = ExternalStore<T> & {
3
+ set(next: T): void;
4
+ };
5
+ export declare function createStore<T>(initial: T, onListenerError: (error: unknown) => void): MutableStore<T>;
6
+ //# sourceMappingURL=_store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_store.d.ts","sourceRoot":"","sources":["../src/_store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,SAAS,CAAC;AAE1D,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG;IAC/C,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;CACpB,CAAC;AAEF,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,CAuBrG"}
package/dist/_store.js ADDED
@@ -0,0 +1,24 @@
1
+ //#region src/_store.ts
2
+ function e(e, t) {
3
+ let n = e, r = /* @__PURE__ */ new Set();
4
+ return {
5
+ getSnapshot: () => n,
6
+ set(e) {
7
+ if (!Object.is(n, e)) {
8
+ n = e;
9
+ for (let e of [...r]) try {
10
+ e();
11
+ } catch (e) {
12
+ t(e);
13
+ }
14
+ }
15
+ },
16
+ subscribe(e) {
17
+ return r.add(e), () => r.delete(e);
18
+ }
19
+ };
20
+ }
21
+ //#endregion
22
+ export { e as createStore };
23
+
24
+ //# sourceMappingURL=_store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_store.js","names":[],"sources":["../src/_store.ts"],"sourcesContent":["import type { ExternalStore, Unsubscribe } from './types';\n\nexport type MutableStore<T> = ExternalStore<T> & {\n set(next: T): void;\n};\n\nexport function createStore<T>(initial: T, onListenerError: (error: unknown) => void): MutableStore<T> {\n let current = initial;\n const listeners = new Set<() => void>();\n\n return {\n getSnapshot: () => current,\n set(next) {\n if (Object.is(current, next)) return;\n current = next;\n\n for (const listener of [...listeners]) {\n try {\n listener();\n } catch (error) {\n onListenerError(error);\n }\n }\n },\n subscribe(listener): Unsubscribe {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n"],"mappings":";AAMA,SAAgB,EAAe,GAAY,GAA4D;CACrG,IAAI,IAAU,GACR,oBAAY,IAAI,IAAgB;CAEtC,OAAO;EACL,mBAAmB;EACnB,IAAI,GAAM;GACJ,YAAO,GAAG,GAAS,CAAI,GAC3B;QAAU;IAEV,KAAK,IAAM,KAAY,CAAC,GAAG,CAAS,GAClC,IAAI;KACF,EAAS;IACX,SAAS,GAAO;KACd,EAAgB,CAAK;IACvB;GAPQ;EASZ;EACA,UAAU,GAAuB;GAE/B,OADA,EAAU,IAAI,CAAQ,SACT,EAAU,OAAO,CAAQ;EACxC;CACF;AACF"}
package/dist/_utils.cjs CHANGED
@@ -1,2 +1,2 @@
1
- function e(e,...n){return n.length===0?e:n.reduce(t,e)}function t(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 n(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function r(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 i(e,t=3e4){let n=Math.min(1e3*2**e,t);return Math.random()*n}exports.combineSignals=e,exports.defaultReconnectDelay=i,exports.deriveAbortController=n,exports.sleep=r;
1
+ function e(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function t(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 n(e,t=3e4){let n=Math.min(1e3*2**e,t);return Math.random()*n}exports.defaultReconnectDelay=n,exports.deriveAbortController=e,exports.sleep=t;
2
2
  //# sourceMappingURL=_utils.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_utils.cjs","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["/**\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 * 2 ** attempt, maxMs);\n\n return Math.random() * base;\n}\n"],"mappings":"AAOA,SAAgB,EAAe,EAAoB,GAAG,EAAkC,CAGtF,OAFI,EAAK,SAAW,EAAU,EAEvB,EAAK,OAAO,EAAU,CAAK,CACpC,CAEA,SAAS,EAAS,EAAgB,EAA6B,CAC7D,GAAI,EAAE,QAAS,OAAO,EAEtB,GAAI,EAAE,QAAS,OAAO,EAEtB,IAAM,EAAO,IAAI,gBACX,MAAkB,EAAK,MAAM,EAAE,MAAM,EACrC,MAAkB,EAAK,MAAM,EAAE,MAAM,EAa3C,OAXA,EAAE,iBAAiB,QAAS,EAAK,CAAE,KAAM,EAAK,CAAC,EAC/C,EAAE,iBAAiB,QAAS,EAAK,CAAE,KAAM,EAAK,CAAC,EAC/C,EAAK,OAAO,iBACV,YACM,CACJ,EAAE,oBAAoB,QAAS,CAAG,EAClC,EAAE,oBAAoB,QAAS,CAAG,CACpC,EACA,CAAE,KAAM,EAAK,CACf,EAEO,EAAK,MACd,CASA,SAAgB,EAAsB,EAAsC,CAC1E,IAAM,EAAO,IAAI,gBAQjB,OANI,EAAO,QACT,EAAK,MAAM,EAAO,MAAM,EAExB,EAAO,iBAAiB,YAAe,EAAK,MAAM,EAAO,MAAM,EAAG,CAAE,KAAM,EAAK,CAAC,EAG3E,CACT,CAQA,SAAgB,EAAM,EAAY,EAAqC,CACrE,OAAO,IAAI,QAAS,GAAY,CAC9B,GAAI,GAAQ,QAAS,CACnB,EAAQ,EAER,MACF,CAEA,IAAM,EAAK,WAAW,EAAS,CAAE,EAEjC,GAAQ,iBACN,YACM,CACJ,aAAa,CAAE,EACf,EAAQ,CACV,EACA,CAAE,KAAM,EAAK,CACf,CACF,CAAC,CACH,CAQA,SAAgB,EAAsB,EAAiB,EAAQ,IAAgB,CAC7E,IAAM,EAAO,KAAK,IAAI,IAAO,GAAK,EAAS,CAAK,EAEhD,OAAO,KAAK,OAAO,EAAI,CACzB"}
1
+ {"version":3,"file":"_utils.cjs","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["/**\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 * 2 ** attempt, maxMs);\n\n return Math.random() * base;\n}\n"],"mappings":"AAOA,SAAgB,EAAsB,EAAsC,CAC1E,IAAM,EAAO,IAAI,gBAQjB,OANI,EAAO,QACT,EAAK,MAAM,EAAO,MAAM,EAExB,EAAO,iBAAiB,YAAe,EAAK,MAAM,EAAO,MAAM,EAAG,CAAE,KAAM,EAAK,CAAC,EAG3E,CACT,CAQA,SAAgB,EAAM,EAAY,EAAqC,CACrE,OAAO,IAAI,QAAS,GAAY,CAC9B,GAAI,GAAQ,QAAS,CACnB,EAAQ,EAER,MACF,CAEA,IAAM,EAAK,WAAW,EAAS,CAAE,EAEjC,GAAQ,iBACN,YACM,CACJ,aAAa,CAAE,EACf,EAAQ,CACV,EACA,CAAE,KAAM,EAAK,CACf,CACF,CAAC,CACH,CAQA,SAAgB,EAAsB,EAAiB,EAAQ,IAAgB,CAC7E,IAAM,EAAO,KAAK,IAAI,IAAO,GAAK,EAAS,CAAK,EAEhD,OAAO,KAAK,OAAO,EAAI,CACzB"}
package/dist/_utils.js CHANGED
@@ -1,20 +1,9 @@
1
1
  //#region src/_utils.ts
2
- function e(e, ...n) {
3
- return n.length === 0 ? e : n.reduce(t, e);
4
- }
5
- function t(e, t) {
6
- if (e.aborted) return e;
7
- if (t.aborted) return t;
8
- let n = new AbortController(), r = () => n.abort(e.reason), i = () => n.abort(t.reason);
9
- return e.addEventListener("abort", r, { once: !0 }), t.addEventListener("abort", i, { once: !0 }), n.signal.addEventListener("abort", () => {
10
- e.removeEventListener("abort", r), t.removeEventListener("abort", i);
11
- }, { once: !0 }), n.signal;
12
- }
13
- function n(e) {
2
+ function e(e) {
14
3
  let t = new AbortController();
15
4
  return e.aborted ? t.abort(e.reason) : e.addEventListener("abort", () => t.abort(e.reason), { once: !0 }), t;
16
5
  }
17
- function r(e, t) {
6
+ function t(e, t) {
18
7
  return new Promise((n) => {
19
8
  if (t?.aborted) {
20
9
  n();
@@ -26,11 +15,11 @@ function r(e, t) {
26
15
  }, { once: !0 });
27
16
  });
28
17
  }
29
- function i(e, t = 3e4) {
18
+ function n(e, t = 3e4) {
30
19
  let n = Math.min(1e3 * 2 ** e, t);
31
20
  return Math.random() * n;
32
21
  }
33
22
  //#endregion
34
- export { e as combineSignals, i as defaultReconnectDelay, n as deriveAbortController, r as sleep };
23
+ export { n as defaultReconnectDelay, e as deriveAbortController, t as sleep };
35
24
 
36
25
  //# sourceMappingURL=_utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_utils.js","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["/**\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 * 2 ** attempt, maxMs);\n\n return Math.random() * base;\n}\n"],"mappings":";AAOA,SAAgB,EAAe,GAAoB,GAAG,GAAkC;CAGtF,OAFI,EAAK,WAAW,IAAU,IAEvB,EAAK,OAAO,GAAU,CAAK;AACpC;AAEA,SAAS,EAAS,GAAgB,GAA6B;CAC7D,IAAI,EAAE,SAAS,OAAO;CAEtB,IAAI,EAAE,SAAS,OAAO;CAEtB,IAAM,IAAO,IAAI,gBAAgB,GAC3B,UAAkB,EAAK,MAAM,EAAE,MAAM,GACrC,UAAkB,EAAK,MAAM,EAAE,MAAM;CAa3C,OAXA,EAAE,iBAAiB,SAAS,GAAK,EAAE,MAAM,GAAK,CAAC,GAC/C,EAAE,iBAAiB,SAAS,GAAK,EAAE,MAAM,GAAK,CAAC,GAC/C,EAAK,OAAO,iBACV,eACM;EAEJ,AADA,EAAE,oBAAoB,SAAS,CAAG,GAClC,EAAE,oBAAoB,SAAS,CAAG;CACpC,GACA,EAAE,MAAM,GAAK,CACf,GAEO,EAAK;AACd;AASA,SAAgB,EAAsB,GAAsC;CAC1E,IAAM,IAAO,IAAI,gBAAgB;CAQjC,OANI,EAAO,UACT,EAAK,MAAM,EAAO,MAAM,IAExB,EAAO,iBAAiB,eAAe,EAAK,MAAM,EAAO,MAAM,GAAG,EAAE,MAAM,GAAK,CAAC,GAG3E;AACT;AAQA,SAAgB,EAAM,GAAY,GAAqC;CACrE,OAAO,IAAI,SAAS,MAAY;EAC9B,IAAI,GAAQ,SAAS;GACnB,EAAQ;GAER;EACF;EAEA,IAAM,IAAK,WAAW,GAAS,CAAE;EAEjC,GAAQ,iBACN,eACM;GAEJ,AADA,aAAa,CAAE,GACf,EAAQ;EACV,GACA,EAAE,MAAM,GAAK,CACf;CACF,CAAC;AACH;AAQA,SAAgB,EAAsB,GAAiB,IAAQ,KAAgB;CAC7E,IAAM,IAAO,KAAK,IAAI,MAAO,KAAK,GAAS,CAAK;CAEhD,OAAO,KAAK,OAAO,IAAI;AACzB"}
1
+ {"version":3,"file":"_utils.js","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["/**\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 * 2 ** attempt, maxMs);\n\n return Math.random() * base;\n}\n"],"mappings":";AAOA,SAAgB,EAAsB,GAAsC;CAC1E,IAAM,IAAO,IAAI,gBAAgB;CAQjC,OANI,EAAO,UACT,EAAK,MAAM,EAAO,MAAM,IAExB,EAAO,iBAAiB,eAAe,EAAK,MAAM,EAAO,MAAM,GAAG,EAAE,MAAM,GAAK,CAAC,GAG3E;AACT;AAQA,SAAgB,EAAM,GAAY,GAAqC;CACrE,OAAO,IAAI,SAAS,MAAY;EAC9B,IAAI,GAAQ,SAAS;GACnB,EAAQ;GAER;EACF;EAEA,IAAM,IAAK,WAAW,GAAS,CAAE;EAEjC,GAAQ,iBACN,eACM;GAEJ,AADA,aAAa,CAAE,GACf,EAAQ;EACV,GACA,EAAE,MAAM,GAAK,CACf;CACF,CAAC;AACH;AAQA,SAAgB,EAAsB,GAAiB,IAAQ,KAAgB;CAC7E,IAAM,IAAO,KAAK,IAAI,MAAO,KAAK,GAAS,CAAK;CAEhD,OAAO,KAAK,OAAO,IAAI;AACzB"}
package/dist/_wait.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./_utils.cjs");function n(n,r,i,a){return new Promise((o,s)=>{let c=[r],l;if(i?.signal&&c.push(i.signal),i?.timeout!==void 0){let t=new AbortController;l=setTimeout(()=>{t.abort(new e.PulseTimeoutError(n))},i.timeout),c.push(t.signal)}let u=c.length===1?c[0]:t.combineSignals(c[0],...c.slice(1));if(u.aborted){clearTimeout(l),s(u.reason instanceof e.PulseTimeoutError?u.reason:new e.PulseAbortError);return}let d=()=>{},f=()=>{clearTimeout(l),d(),s(u.reason instanceof e.PulseTimeoutError?u.reason:new e.PulseAbortError)};u.addEventListener(`abort`,f,{once:!0}),d=a(n,e=>{clearTimeout(l),u.removeEventListener(`abort`,f),o(e)})})}exports.createWaitPromise=n;
1
+ const e=require("./errors.cjs");function t(t,n,r,i){return new Promise((a,o)=>{let s=[n],c;if(r?.signal&&s.push(r.signal),r?.timeout!==void 0){let n=new AbortController;c=setTimeout(()=>{n.abort(new e.PulseTimeoutError(t))},r.timeout),s.push(n.signal)}let l=AbortSignal.any(s);if(l.aborted){clearTimeout(c),o(l.reason instanceof e.PulseTimeoutError?l.reason:new e.PulseAbortError);return}let u=()=>{},d=()=>{clearTimeout(c),u(),o(l.reason instanceof e.PulseTimeoutError?l.reason:new e.PulseAbortError)};l.addEventListener(`abort`,d,{once:!0}),u=i(t,e=>{clearTimeout(c),l.removeEventListener(`abort`,d),a(e)})})}exports.createWaitPromise=t;
2
2
  //# sourceMappingURL=_wait.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_wait.cjs","names":[],"sources":["../src/_wait.ts"],"sourcesContent":["import { combineSignals } from './_utils';\nimport { PulseAbortError, PulseTimeoutError } from './errors';\nimport type { Unsubscribe } from './types';\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"],"mappings":"0DAWA,SAAgB,EACd,EACA,EACA,EACA,EACY,CACZ,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAAyB,CAAC,CAAU,EAEtC,EAIJ,GAFI,GAAM,QAAQ,EAAQ,KAAK,EAAK,MAAM,EAEtC,GAAM,UAAY,IAAA,GAAW,CAC/B,IAAM,EAAc,IAAI,gBAExB,EAAY,eAAiB,CAC3B,EAAY,MAAM,IAAI,EAAA,kBAAkB,CAAK,CAAC,CAChD,EAAG,EAAK,OAAO,EACf,EAAQ,KAAK,EAAY,MAAM,CACjC,CAEA,IAAM,EAAW,EAAQ,SAAW,EAAI,EAAQ,GAAM,EAAA,eAAe,EAAQ,GAAK,GAAG,EAAQ,MAAM,CAAC,CAAC,EAErG,GAAI,EAAS,QAAS,CACpB,aAAa,CAAS,EACtB,EAAO,EAAS,kBAAkB,EAAA,kBAAoB,EAAS,OAAS,IAAI,EAAA,eAAiB,EAE7F,MACF,CAEA,IAAI,MAA2B,CAAC,EAE1B,MAAsB,CAC1B,aAAa,CAAS,EACtB,EAAM,EACN,EAAO,EAAS,kBAAkB,EAAA,kBAAoB,EAAS,OAAS,IAAI,EAAA,eAAiB,CAC/F,EAEA,EAAS,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAE1D,EAAQ,EAAU,EAAQ,GAAY,CACpC,aAAa,CAAS,EACtB,EAAS,oBAAoB,QAAS,CAAO,EAC7C,EAAQ,CAAY,CACtB,CAAC,CACH,CAAC,CACH"}
1
+ {"version":3,"file":"_wait.cjs","names":[],"sources":["../src/_wait.ts"],"sourcesContent":["import { PulseAbortError, PulseTimeoutError } from './errors';\nimport type { Unsubscribe } from './types';\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 = AbortSignal.any(signals);\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"],"mappings":"gCAUA,SAAgB,EACd,EACA,EACA,EACA,EACY,CACZ,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAAyB,CAAC,CAAU,EAEtC,EAIJ,GAFI,GAAM,QAAQ,EAAQ,KAAK,EAAK,MAAM,EAEtC,GAAM,UAAY,IAAA,GAAW,CAC/B,IAAM,EAAc,IAAI,gBAExB,EAAY,eAAiB,CAC3B,EAAY,MAAM,IAAI,EAAA,kBAAkB,CAAK,CAAC,CAChD,EAAG,EAAK,OAAO,EACf,EAAQ,KAAK,EAAY,MAAM,CACjC,CAEA,IAAM,EAAW,YAAY,IAAI,CAAO,EAExC,GAAI,EAAS,QAAS,CACpB,aAAa,CAAS,EACtB,EAAO,EAAS,kBAAkB,EAAA,kBAAoB,EAAS,OAAS,IAAI,EAAA,eAAiB,EAE7F,MACF,CAEA,IAAI,MAA2B,CAAC,EAE1B,MAAsB,CAC1B,aAAa,CAAS,EACtB,EAAM,EACN,EAAO,EAAS,kBAAkB,EAAA,kBAAoB,EAAS,OAAS,IAAI,EAAA,eAAiB,CAC/F,EAEA,EAAS,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EAE1D,EAAQ,EAAU,EAAQ,GAAY,CACpC,aAAa,CAAS,EACtB,EAAS,oBAAoB,QAAS,CAAO,EAC7C,EAAQ,CAAY,CACtB,CAAC,CACH,CAAC,CACH"}
package/dist/_wait.js CHANGED
@@ -1,29 +1,28 @@
1
1
  import { PulseAbortError as e, PulseTimeoutError as t } from "./errors.js";
2
- import { combineSignals as n } from "./_utils.js";
3
2
  //#region src/_wait.ts
4
- function r(r, i, a, o) {
5
- return new Promise((s, c) => {
6
- let l = [i], u;
7
- if (a?.signal && l.push(a.signal), a?.timeout !== void 0) {
3
+ function n(n, r, i, a) {
4
+ return new Promise((o, s) => {
5
+ let c = [r], l;
6
+ if (i?.signal && c.push(i.signal), i?.timeout !== void 0) {
8
7
  let e = new AbortController();
9
- u = setTimeout(() => {
10
- e.abort(new t(r));
11
- }, a.timeout), l.push(e.signal);
8
+ l = setTimeout(() => {
9
+ e.abort(new t(n));
10
+ }, i.timeout), c.push(e.signal);
12
11
  }
13
- let d = l.length === 1 ? l[0] : n(l[0], ...l.slice(1));
14
- if (d.aborted) {
15
- clearTimeout(u), c(d.reason instanceof t ? d.reason : new e());
12
+ let u = AbortSignal.any(c);
13
+ if (u.aborted) {
14
+ clearTimeout(l), s(u.reason instanceof t ? u.reason : new e());
16
15
  return;
17
16
  }
18
- let f = () => {}, p = () => {
19
- clearTimeout(u), f(), c(d.reason instanceof t ? d.reason : new e());
17
+ let d = () => {}, f = () => {
18
+ clearTimeout(l), d(), s(u.reason instanceof t ? u.reason : new e());
20
19
  };
21
- d.addEventListener("abort", p, { once: !0 }), f = o(r, (e) => {
22
- clearTimeout(u), d.removeEventListener("abort", p), s(e);
20
+ u.addEventListener("abort", f, { once: !0 }), d = a(n, (e) => {
21
+ clearTimeout(l), u.removeEventListener("abort", f), o(e);
23
22
  });
24
23
  });
25
24
  }
26
25
  //#endregion
27
- export { r as createWaitPromise };
26
+ export { n as createWaitPromise };
28
27
 
29
28
  //# sourceMappingURL=_wait.js.map
package/dist/_wait.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_wait.js","names":[],"sources":["../src/_wait.ts"],"sourcesContent":["import { combineSignals } from './_utils';\nimport { PulseAbortError, PulseTimeoutError } from './errors';\nimport type { Unsubscribe } from './types';\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"],"mappings":";;;AAWA,SAAgB,EACd,GACA,GACA,GACA,GACY;CACZ,OAAO,IAAI,SAAS,GAAS,MAAW;EACtC,IAAM,IAAyB,CAAC,CAAU,GAEtC;EAIJ,IAFI,GAAM,UAAQ,EAAQ,KAAK,EAAK,MAAM,GAEtC,GAAM,YAAY,KAAA,GAAW;GAC/B,IAAM,IAAc,IAAI,gBAAgB;GAKxC,AAHA,IAAY,iBAAiB;IAC3B,EAAY,MAAM,IAAI,EAAkB,CAAK,CAAC;GAChD,GAAG,EAAK,OAAO,GACf,EAAQ,KAAK,EAAY,MAAM;EACjC;EAEA,IAAM,IAAW,EAAQ,WAAW,IAAI,EAAQ,KAAM,EAAe,EAAQ,IAAK,GAAG,EAAQ,MAAM,CAAC,CAAC;EAErG,IAAI,EAAS,SAAS;GAEpB,AADA,aAAa,CAAS,GACtB,EAAO,EAAS,kBAAkB,IAAoB,EAAS,SAAS,IAAI,EAAgB,CAAC;GAE7F;EACF;EAEA,IAAI,UAA2B,CAAC,GAE1B,UAAsB;GAG1B,AAFA,aAAa,CAAS,GACtB,EAAM,GACN,EAAO,EAAS,kBAAkB,IAAoB,EAAS,SAAS,IAAI,EAAgB,CAAC;EAC/F;EAIA,AAFA,EAAS,iBAAiB,SAAS,GAAS,EAAE,MAAM,GAAK,CAAC,GAE1D,IAAQ,EAAU,IAAQ,MAAY;GAGpC,AAFA,aAAa,CAAS,GACtB,EAAS,oBAAoB,SAAS,CAAO,GAC7C,EAAQ,CAAY;EACtB,CAAC;CACH,CAAC;AACH"}
1
+ {"version":3,"file":"_wait.js","names":[],"sources":["../src/_wait.ts"],"sourcesContent":["import { PulseAbortError, PulseTimeoutError } from './errors';\nimport type { Unsubscribe } from './types';\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 = AbortSignal.any(signals);\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"],"mappings":";;AAUA,SAAgB,EACd,GACA,GACA,GACA,GACY;CACZ,OAAO,IAAI,SAAS,GAAS,MAAW;EACtC,IAAM,IAAyB,CAAC,CAAU,GAEtC;EAIJ,IAFI,GAAM,UAAQ,EAAQ,KAAK,EAAK,MAAM,GAEtC,GAAM,YAAY,KAAA,GAAW;GAC/B,IAAM,IAAc,IAAI,gBAAgB;GAKxC,AAHA,IAAY,iBAAiB;IAC3B,EAAY,MAAM,IAAI,EAAkB,CAAK,CAAC;GAChD,GAAG,EAAK,OAAO,GACf,EAAQ,KAAK,EAAY,MAAM;EACjC;EAEA,IAAM,IAAW,YAAY,IAAI,CAAO;EAExC,IAAI,EAAS,SAAS;GAEpB,AADA,aAAa,CAAS,GACtB,EAAO,EAAS,kBAAkB,IAAoB,EAAS,SAAS,IAAI,EAAgB,CAAC;GAE7F;EACF;EAEA,IAAI,UAA2B,CAAC,GAE1B,UAAsB;GAG1B,AAFA,aAAa,CAAS,GACtB,EAAM,GACN,EAAO,EAAS,kBAAkB,IAAoB,EAAS,SAAS,IAAI,EAAgB,CAAC;EAC/F;EAIA,AAFA,EAAS,iBAAiB,SAAS,GAAS,EAAE,MAAM,GAAK,CAAC,GAE1D,IAAQ,EAAU,IAAQ,MAAY;GAGpC,AAFA,aAAa,CAAS,GACtB,EAAS,oBAAoB,SAAS,CAAO,GAC7C,EAAQ,CAAY;EACtB,CAAC;CACH,CAAC;AACH"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { PulseAbortError, PulseConnectionError, PulseDisposedError, PulseError, PulseProtocolError, PulseRoomTimeoutError, PulseTimeoutError, } from './errors';
2
2
  export { createPulse } from './pulse';
3
- export type { ChannelDefinition, ChannelDefinitions, ClientEvents, EventKey, HeartbeatOptions, MessageMap, OutgoingMessage, OutgoingTransform, PresenceRoomScope, Pulse, PulseChannel, PulseEvent, PulseOptions, PulseSchema, PulseStatus, ReconnectOptions, RoomDefinition, RoomDefinitions, RoomMap, RoomOptions, RoomScope, RoomScopeBase, ServerEvents, Unsubscribe, } from './types';
3
+ export type { ChannelDefinition, ChannelDefinitions, ClientEvents, EventKey, ExternalStore, HeartbeatOptions, MessageMap, OutgoingMessage, OutgoingTransform, PresenceRoomScope, Pulse, PulseChannel, PulseEvent, PulseOptions, PulseSchema, PulseStatus, ReconnectOptions, RoomDefinition, RoomDefinitions, RoomMap, RoomOptions, RoomScope, RoomScopeBase, ServerEvents, Unsubscribe, } from './types';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,UAAU,EACV,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,QAAQ,EACR,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,OAAO,EACP,WAAW,EACX,SAAS,EACT,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,UAAU,EACV,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,OAAO,EACP,WAAW,EACX,SAAS,EACT,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,SAAS,CAAC"}
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 extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},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{room;constructor(e,t){super(`Timed out waiting for room "${e}"`,t),this.room=e}},a=class extends t{constructor(e){super(`Aborted`,e)}},o=class extends t{constructor(e=`Pulse`,t){super(`${e} instance is disposed`,t)}},s=class extends t{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,r,i){let a=r===!0?{}:r||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 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||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 r;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){r=t instanceof n?t:new n(`Reconnect failed`,e)}!t.signal.aborted&&m&&!u&&(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(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,r=``){m=!1,E(),x(new n(`Connection closed`,e)),b(`closed`),_?.close(t,r)},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 n(`Connection is not open`,e);_.send(t)}}}function h(e,t,n,i){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 r(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 r?d.reason:new a);return}let f=()=>{},p=()=>{clearTimeout(u),f(),s(d.reason instanceof r?d.reason:new a)};d.addEventListener(`abort`,p,{once:!0}),f=i(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,`room`)!==null}function S(e){return b(e,`code`)!==null&&b(e,`message`)!==null}function C(e){return!y(e)||b(e,`event`)===null||!(`payload`in e)?!1:e.channel===void 0||typeof e.channel==`string`}function w(e){return y(e)&&typeof e.ts==`number`}function T(e){return y(e)&&b(e,`id`)!==null&&b(e,`room`)!==null&&`state`in e}function E(e){return b(e,`id`)!==null&&b(e,`room`)!==null}function D(e){return y(e)&&b(e,`room`)!==null&&y(e.members)}function O(e){return b(e,`channel`)!==null}function k(e){let t=v(e);switch(t.type){case`error`:if(S(t))return{frame:t,kind:`known`};break;case`joined`:if(x(t))return{frame:t,kind:`known`};break;case`left`:if(x(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(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(_({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 N(r){let s=(0,e.signal)(new Set),l=new Map;function d(e){let t=l.get(e);return t||(t={confirmed:!1,joinSent:!1,leaveAfterJoin:!1,localPresence:void 0,refs:0,scopes:new Set},l.set(e,t)),t}function f(e,t){let n=new Set(s.value);t?n.add(e):n.delete(e),s.value=n}function p(e){r.send(_({room:e,type:`join`}))}function m(e){r.send(_({room:e,type:`leave`}))}function h(e,t){let i=l.get(e);if(!i)throw new o(`Room "${e}"`);if(!r.isOpen())throw new n(`Connection is not open`,e);r.send(_({room:e,state:t,type:`presence`})),i.localPresence=t}function g(e,t){let n=l.get(e);!n||!n.scopes.has(t)||(n.scopes.delete(t),n.refs--,n.refs===0&&(n.confirmed?(n.confirmed=!1,n.localPresence=void 0,f(e,!1),r.isOpen()&&m(e),l.delete(e)):n.joinSent?n.leaveAfterJoin=!0:l.delete(e)))}return{createScope(s,l,f){if(r.disposalSignal.aborted)return F(s,l);let m=d(s),_=u(r.disposalSignal),v,y,b=!1,x=new Promise((e,t)=>{v=()=>{b||(b=!0,clearTimeout(S),w.removeEventListener(`abort`,A),e())},y=e=>{b||(b=!0,clearTimeout(S),w.removeEventListener(`abort`,A),t(e))}});x.catch(()=>{});let S,C=[_.signal];if(f?.signal&&C.push(f.signal),f?.timeout!==void 0){let e=new AbortController;S=setTimeout(()=>e.abort(new i(s)),f.timeout),C.push(e.signal)}let w=C.length===1?C[0]:c(C[0],...C.slice(1)),T,E,D=new Set,O=new Set;l&&(T=(0,e.signal)(new Map),E={onJoin(e,t){let n=new Map(T.value);n.set(e,t),T.value=n;for(let n of D)n(e,t)},onLeave(e){let t=new Map(T.value);t.delete(e),T.value=t;for(let t of O)t(e)},onState(e){let t=new Map;for(let[n,r]of Object.entries(e))t.set(n,r);T.value=t},reset(){T.value=new Map}});let k={isSettled:()=>b,joined:x,name:s,presenceHandlers:E,rejectJoined:y,resolveJoined:v},A=()=>{clearTimeout(S),_.abort();let e=w.reason,n=e instanceof t?e:new a;k.rejectJoined(n),g(s,k)};if(m.refs++,m.scopes.add(k),w.aborted)return A(),P(k,_.signal,T,D,O,s,h);if(w.addEventListener(`abort`,A,{once:!0}),m.confirmed)k.resolveJoined();else if(!m.joinSent&&r.isOpen()){m.joinSent=!0;try{p(s)}catch(e){m.joinSent=!1,k.rejectJoined(e instanceof t?e:new n(`Failed to send join`,s))}}return P(k,_.signal,T,D,O,s,h,()=>{_.signal.aborted||(clearTimeout(S),w.removeEventListener(`abort`,A),_.abort(),b||k.rejectJoined(new o(`Room "${s}"`)),g(s,k))})},dispose(){s.value=new Set,l.clear()},handleJoined(e){let t=l.get(e);if(t){if(t.confirmed=!0,t.joinSent=!1,f(e,!0),t.leaveAfterJoin){t.leaveAfterJoin=!1,t.confirmed=!1,f(e,!1),r.isOpen()&&m(e),l.delete(e);return}for(let e of t.scopes)e.isSettled()||e.resolveJoined()}},handleLeft(e){let t=l.get(e);t&&(t.confirmed=!1,t.joinSent=!1,f(e,!1))},handlePresenceJoin(e,t,n){let r=l.get(e);if(r)for(let e of r.scopes)e.presenceHandlers?.onJoin(t,n)},handlePresenceLeave(e,t){let n=l.get(e);if(n)for(let e of n.scopes)e.presenceHandlers?.onLeave(t)},handlePresenceState(e,t){let n=l.get(e);if(n)for(let e of n.scopes)e.presenceHandlers?.onState(t)},rejectAll(e){for(let t of l.values())for(let n of t.scopes)n.isSettled()||n.rejectJoined(e)},reset(){s.value=new Set;for(let[e,t]of l){if(t.refs===0){l.delete(e);continue}t.confirmed=!1,t.joinSent=!1;for(let e of t.scopes)e.presenceHandlers?.reset()}},restore(){for(let[e,t]of l)if(t.refs!==0){t.confirmed=!1,t.joinSent=!0;try{p(e)}catch{t.joinSent=!1}if(t.localPresence!==void 0)try{r.send(_({room:e,state:t.localPresence,type:`presence`}))}catch{}}},rooms:s}}function P(e,t,n,r,i,a,s,c){let l=c??(()=>{});return n?{get disposalSignal(){return t},dispose:l,get disposed(){return t.aborted},get joined(){return e.joined},get name(){return e.name},onJoin(e){if(t.aborted)throw new o(`Room "${a}"`);return r.add(e),()=>r.delete(e)},onLeave(e){if(t.aborted)throw new o(`Room "${a}"`);return i.add(e),()=>i.delete(e)},get presence(){return n},updatePresence(e){s(a,e)},[Symbol.dispose]:l}:{get disposalSignal(){return t},dispose:l,get disposed(){return t.aborted},get joined(){return e.joined},get name(){return e.name},[Symbol.dispose]:l}}function F(t,n){let r=new AbortController;r.abort(new o);let i=Promise.reject(new o(`Room "${t}"`));return i.catch(()=>{}),n?{disposalSignal:r.signal,dispose(){},disposed:!0,joined:i,name:t,onJoin(){throw new o(`Room "${t}"`)},onLeave(){throw new o(`Room "${t}"`)},presence:(0,e.signal)(new Map),updatePresence(){throw new o(`Room "${t}"`)},[Symbol.dispose](){}}:{disposalSignal:r.signal,dispose(){},disposed:!0,joined:i,name:t,[Symbol.dispose](){}}}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(r,i={}){let a=new AbortController,c=(0,e.signal)(`closed`),l=new I,u=new Map,d=new Set,f=!1,p=!0,v=`closed`;function y(e){if(d.size!==0)for(let t of d)try{t(e)}catch{}}function b(e){y({error:e,type:`error`})}function x(e){E.send(e)}let S=N({disposalSignal:a.signal,isOpen:()=>E.open,send:x});function C(){for(let e of u.keys())x(_({channel:e,type:`subscribe`}));S.restore()}function w(){S.reset()}function T(){p||(p=!0,D.stop(),w(),S.rejectAll(new n(`Connection closed before room confirmation`,r)))}let E=m(r,i.protocols,i.reconnect,{onClose(){T()},onError:b,onMessage(e){let t;try{t=k(e.data)}catch(t){b(t instanceof s?t:new s(`Failed to decode frame`,e.data));return}if(t.kind===`unknown`){b(new s(`Unknown frame type "${t.type}"`,e.data));return}try{O(t)}catch(e){b(new s(`Frame handler threw`,t.frame,{cause:e}))}},onOpen(){p=!1;try{C(),D.start()}catch(e){b(e instanceof t?e:new n(`Session restore failed`,r))}},onStatus(e){c.value=e,e!==v&&(v=e,y({status:e,type:`status-change`}))}}),D=M(i.heartbeat,e=>{E.open&&x(e)},()=>E.forceReconnect(4e3,`heartbeat timeout`));function O(e){let t=e.frame;switch(t.type){case`error`:b(new s(`Server error [${t.code}]: ${t.message}`,t));break;case`joined`:S.handleJoined(t.room);break;case`left`:S.handleLeft(t.room);break;case`message`:l.dispatch(t.channel??null,t.event,t.payload);break;case`pong`:D.onPong();break;case`presence_join`:S.handlePresenceJoin(t.room,t.id,t.state);break;case`presence_leave`:S.handlePresenceLeave(t.room,t.id);break;case`presence_state`:S.handlePresenceState(t.room,t.members)}}function A(e){let t=u.get(e)??0;u.set(e,t+1),t===0&&E.open&&x(_({channel:e,type:`subscribe`}))}function j(e){let t=u.get(e);if(t){if(t>1){u.set(e,t-1);return}u.delete(e),E.open&&x(_({channel:e,type:`unsubscribe`}))}}return{channel(e){if(f)throw new o;return A(e),g(e,(e,t,n)=>{let r=i.transform?i.transform({channel:e,event:t,payload:n}):{channel:e,event:t,payload:n};r!==null&&x(_({...r,type:`message`}))},(e,t,n)=>l.add(e,t,n),a.signal,()=>j(e))},connect(){return f?Promise.reject(new o):E.connect()},disconnect(e=1e3,t=``){f||(T(),E.disconnect(e,t))},get disposalSignal(){return a.signal},dispose(){f||(f=!0,D.stop(),p=!0,S.reset(),S.rejectAll(new o),E.dispose(),u.clear(),S.dispose(),l.clear(),y({type:`dispose`}),d.clear(),a.abort())},get disposed(){return f},on(e,t){if(f)throw new o;return l.add(null,e,t)},once(e,t){if(f)throw new o;let n=()=>{};return n=l.add(null,e,e=>{n(),t(e)}),n},room(e,t){if(f)throw new o;return S.createScope(e,!0,t)},get rooms(){return S.rooms},send(e,t){if(f)throw new o;let n=i.transform?i.transform({event:e,payload:t}):{event:e,payload:t};n!==null&&x(_({...n,type:`message`}))},get status(){return c},tap(e,t){if(f)throw new o;if(d.add(e),t?.signal){if(t.signal.aborted)return d.delete(e),()=>{};let n=()=>d.delete(e);return t.signal.addEventListener(`abort`,n,{once:!0}),()=>{d.delete(e),t.signal?.removeEventListener(`abort`,n)}}return()=>d.delete(e)},[Symbol.dispose](){this.dispose()},wait(e,t){return h(e,a.signal,t,(e,t)=>l.add(null,e,t))}}}exports.PulseAbortError=a,exports.PulseConnectionError=n,exports.PulseDisposedError=o,exports.PulseError=t,exports.PulseProtocolError=s,exports.PulseRoomTimeoutError=i,exports.PulseTimeoutError=r,exports.createPulse=L;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{url;constructor(e,t,n){super(e,n),this.url=t}},n=class extends e{event;constructor(e,t){super(`Timed out waiting for "${e}"`,t),this.event=e}},r=class extends e{room;constructor(e,t){super(`Timed out waiting for room "${e}"`,t),this.room=e}},i=class extends e{constructor(e){super(`Aborted`,e)}},a=class extends e{constructor(e=`Pulse`,t){super(`${e} instance is disposed`,t)}},o=class extends e{raw;constructor(e,t,n){super(e,n),this.raw=t}};function s(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function c(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 l(e,t=3e4){let n=Math.min(1e3*2**e,t);return Math.random()*n}var u=5;function d(e,n,r,i){let o=r===!0?{}:r||void 0,s=o?o.maxAttempts??u:0,d=o?.delay,f=typeof d==`function`?d:typeof d==`number`?()=>d:l,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,n);_=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 n=new t(`WebSocket error`,e);i.onError(n),C(r,n),x(n)},r.onclose=()=>{if(_!==r)return;_=void 0,i.onClose();let n=new t(`Connection closed`,e);if(C(r,n),!m||p){b(`closed`);return}h||T()},s}async function T(){if(h||p||!m)return;h=!0;let n=new AbortController;g=n,b(`reconnecting`);let r;for(let i=0;i<s&&m&&!p&&(await c(f(i),n.signal),!(n.signal.aborted||!m||p));i++)try{await w(),h=!1;return}catch(n){r=n instanceof t?n:new t(`Reconnect failed`,e)}!n.signal.aborted&&m&&!p&&(m=!1,b(`closed`),i.onError(r??new t(`Reconnect budget exhausted`,e)),x(r??new t(`Reconnect budget exhausted`,e))),g===n&&(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(n=1e3,r=``){m=!1,E(),x(new t(`Connection closed`,e)),b(`closed`),_?.close(n,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(n){if(_?.readyState!==WebSocket.OPEN)throw new t(`Connection is not open`,e);_.send(n)}}}function f(e,t){let n=e,r=new Set;return{getSnapshot:()=>n,set(e){if(!Object.is(n,e)){n=e;for(let e of[...r])try{e()}catch(e){t(e)}}},subscribe(e){return r.add(e),()=>r.delete(e)}}}function p(e,t,r,a){return new Promise((o,s)=>{let c=[t],l;if(r?.signal&&c.push(r.signal),r?.timeout!==void 0){let t=new AbortController;l=setTimeout(()=>{t.abort(new n(e))},r.timeout),c.push(t.signal)}let u=AbortSignal.any(c);if(u.aborted){clearTimeout(l),s(u.reason instanceof n?u.reason:new i);return}let d=()=>{},f=()=>{clearTimeout(l),d(),s(u.reason instanceof n?u.reason:new i)};u.addEventListener(`abort`,f,{once:!0}),d=a(e,e=>{clearTimeout(l),u.removeEventListener(`abort`,f),o(e)})})}function m(e,t,n,r,i){let o=s(r),c=new Set,l=o.signal.aborted;return o.signal.addEventListener(`abort`,()=>{l=!0},{once:!0}),{get disposalSignal(){return o.signal},dispose(){if(!l){l=!0,o.abort();for(let e of c)e();c.clear(),i?.()}},get disposed(){return l},get name(){return e},on(t,r){if(l)throw new a(`Channel "${e}"`);let i=n(e,t,r);return c.add(i),()=>{i(),c.delete(i)}},once(t,r){if(l)throw new a(`Channel "${e}"`);let i=()=>{};return i=n(e,t,e=>{c.delete(i),i(),r(e)}),c.add(i),()=>{c.delete(i),i()}},send(n,r){if(l)throw new a(`Channel "${e}"`);t(e,n,r)},[Symbol.dispose](){this.dispose()},wait(t,r){return p(t,o.signal,r,(t,r)=>n(e,t,r))}}}function h(e){return JSON.stringify(e)}function g(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 _(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,`room`)!==null}function b(e){return v(e,`code`)!==null&&v(e,`message`)!==null}function x(e){return!_(e)||v(e,`event`)===null||!(`payload`in e)?!1:e.channel===void 0||typeof e.channel==`string`}function S(e){return _(e)&&typeof e.ts==`number`}function C(e){return _(e)&&v(e,`id`)!==null&&v(e,`room`)!==null&&`state`in e}function w(e){return v(e,`id`)!==null&&v(e,`room`)!==null}function T(e){return _(e)&&v(e,`room`)!==null&&_(e.members)}function E(e){return v(e,`channel`)!==null}function D(e){let t=g(e);switch(t.type){case`error`:if(b(t))return{frame:t,kind:`known`};break;case`joined`:if(y(t))return{frame:t,kind:`known`};break;case`left`:if(y(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(E(t))return{frame:t,kind:`known`};break;default:return{kind:`unknown`,type:t.type}}throw new o(`Malformed "${t.type}" frame`,t)}var O=3e4,k=5e3;function A(e,t,n){if(!e)return{onPong(){},start(){},stop(){}};let r=e===!0?{}:e,i=r.interval??O,a=r.timeout??k,o,s,c=!1;function l(){o=setTimeout(()=>{t(h({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 j(n){let o=f(new Set,n.onStoreError),c=new Map;function l(e){let t=c.get(e);return t||(t={confirmed:!1,joinSent:!1,leaveAfterJoin:!1,localPresence:void 0,refs:0,scopes:new Set},c.set(e,t)),t}function u(e,t){let n=new Set(o.getSnapshot());t?n.add(e):n.delete(e),o.set(n)}function d(e){n.send(h({room:e,type:`join`}))}function p(e){n.send(h({room:e,type:`leave`}))}function m(e,r){let i=c.get(e);if(!i)throw new a(`Room "${e}"`);if(!n.isOpen())throw new t(`Connection is not open`,e);n.send(h({room:e,state:r,type:`presence`})),i.localPresence=r}function g(e,t){let r=c.get(e);r&&r.scopes.has(t)&&(r.scopes.delete(t),r.refs--,r.refs===0&&(r.confirmed?(r.confirmed=!1,r.localPresence=void 0,u(e,!1),n.isOpen()&&p(e),c.delete(e)):r.joinSent?r.leaveAfterJoin=!0:c.delete(e)))}return{createScope(o,c,u){if(n.disposalSignal.aborted)return N(o,c);let p=l(o),h=s(n.disposalSignal),_,v,y=!1,b=new Promise((e,t)=>{_=()=>{y||(y=!0,clearTimeout(x),C.removeEventListener(`abort`,k),e())},v=e=>{y||(y=!0,clearTimeout(x),C.removeEventListener(`abort`,k),t(e))}});b.catch(()=>{});let x,S=[h.signal];if(u?.signal&&S.push(u.signal),u?.timeout!==void 0){let e=new AbortController;x=setTimeout(()=>e.abort(new r(o)),u.timeout),S.push(e.signal)}let C=AbortSignal.any(S),w,T,E=new Set,D=new Set;c&&(w=f(new Map,n.onStoreError),T={onJoin(e,t){let n=new Map(w.getSnapshot());n.set(e,t),w.set(n);for(let n of E)n(e,t)},onLeave(e){let t=new Map(w.getSnapshot());t.delete(e),w.set(t);for(let t of D)t(e)},onState(e){let t=new Map;for(let[n,r]of Object.entries(e))t.set(n,r);w.set(t)},reset(){w.set(new Map)}});let O={isSettled:()=>y,joined:b,name:o,presenceHandlers:T,rejectJoined:v,resolveJoined:_},k=()=>{clearTimeout(x),h.abort();let t=C.reason,n=t instanceof e?t:new i;O.rejectJoined(n),g(o,O)};if(p.refs++,p.scopes.add(O),C.aborted)return k(),M(O,h.signal,w,E,D,o,m);if(C.addEventListener(`abort`,k,{once:!0}),p.confirmed)O.resolveJoined();else if(!p.joinSent&&n.isOpen()){p.joinSent=!0;try{d(o)}catch(n){p.joinSent=!1,O.rejectJoined(n instanceof e?n:new t(`Failed to send join`,o))}}return M(O,h.signal,w,E,D,o,m,()=>{h.signal.aborted||(clearTimeout(x),C.removeEventListener(`abort`,k),h.abort(),y||O.rejectJoined(new a(`Room "${o}"`)),g(o,O))})},dispose(){o.set(new Set),c.clear()},handleJoined(e){let t=c.get(e);if(t){if(t.confirmed=!0,t.joinSent=!1,u(e,!0),t.leaveAfterJoin){t.leaveAfterJoin=!1,t.confirmed=!1,u(e,!1),n.isOpen()&&p(e),c.delete(e);return}for(let e of t.scopes)e.isSettled()||e.resolveJoined()}},handleLeft(e){let t=c.get(e);t&&(t.confirmed=!1,t.joinSent=!1,u(e,!1))},handlePresenceJoin(e,t,n){let r=c.get(e);if(r)for(let e of r.scopes)e.presenceHandlers?.onJoin(t,n)},handlePresenceLeave(e,t){let n=c.get(e);if(n)for(let e of n.scopes)e.presenceHandlers?.onLeave(t)},handlePresenceState(e,t){let n=c.get(e);if(n)for(let e of n.scopes)e.presenceHandlers?.onState(t)},rejectAll(e){for(let t of c.values())for(let n of t.scopes)n.isSettled()||n.rejectJoined(e)},reset(){o.set(new Set);for(let[e,t]of c){if(t.refs===0){c.delete(e);continue}t.confirmed=!1,t.joinSent=!1;for(let e of t.scopes)e.presenceHandlers?.reset()}},restore(){for(let[e,t]of c)if(t.refs!==0){t.confirmed=!1,t.joinSent=!0;try{d(e)}catch{t.joinSent=!1}if(t.localPresence!==void 0)try{n.send(h({room:e,state:t.localPresence,type:`presence`}))}catch{}}},rooms:o}}function M(e,t,n,r,i,o,s,c){let l=c??(()=>{});return n?{get disposalSignal(){return t},dispose:l,get disposed(){return t.aborted},get joined(){return e.joined},get name(){return e.name},onJoin(e){if(t.aborted)throw new a(`Room "${o}"`);return r.add(e),()=>r.delete(e)},onLeave(e){if(t.aborted)throw new a(`Room "${o}"`);return i.add(e),()=>i.delete(e)},get presence(){return n},updatePresence(e){s(o,e)},[Symbol.dispose]:l}:{get disposalSignal(){return t},dispose:l,get disposed(){return t.aborted},get joined(){return e.joined},get name(){return e.name},[Symbol.dispose]:l}}function N(e,t){let n=new AbortController;n.abort(new a);let r=Promise.reject(new a(`Room "${e}"`));return r.catch(()=>{}),t?{disposalSignal:n.signal,dispose(){},disposed:!0,joined:r,name:e,onJoin(){throw new a(`Room "${e}"`)},onLeave(){throw new a(`Room "${e}"`)},presence:f(new Map,()=>{}),updatePresence(){throw new a(`Room "${e}"`)},[Symbol.dispose](){}}:{disposalSignal:n.signal,dispose(){},disposed:!0,joined:r,name:e,[Symbol.dispose](){}}}var P=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 F(n,r={}){let i=new AbortController,s=new P,c=new Map,l=new Set,u=!1,g=!0,_=`closed`;function v(e){if(l.size!==0)for(let t of l)try{t(e)}catch{}}function y(e){v({error:e,type:`error`})}let b=t=>y(new e(`External store subscriber threw`,{cause:t})),x=f(`closed`,b);function S(e){O.send(e)}let C=j({disposalSignal:i.signal,isOpen:()=>O.open,onStoreError:b,send:S});function w(){for(let e of c.keys())S(h({channel:e,type:`subscribe`}));C.restore()}function T(){C.reset()}function E(){g||(g=!0,k.stop(),T(),C.rejectAll(new t(`Connection closed before room confirmation`,n)))}let O=d(n,r.protocols,r.reconnect,{onClose(){E()},onError:y,onMessage(e){let t;try{t=D(e.data)}catch(t){y(t instanceof o?t:new o(`Failed to decode frame`,e.data));return}if(t.kind===`unknown`){y(new o(`Unknown frame type "${t.type}"`,e.data));return}try{M(t)}catch(e){y(new o(`Frame handler threw`,t.frame,{cause:e}))}},onOpen(){g=!1;try{w(),k.start()}catch(r){y(r instanceof e?r:new t(`Session restore failed`,n))}},onStatus(e){x.set(e),e!==_&&(_=e,v({status:e,type:`status-change`}))}}),k=A(r.heartbeat,e=>{O.open&&S(e)},()=>O.forceReconnect(4e3,`heartbeat timeout`));function M(e){let t=e.frame;switch(t.type){case`error`:y(new o(`Server error [${t.code}]: ${t.message}`,t));break;case`joined`:C.handleJoined(t.room);break;case`left`:C.handleLeft(t.room);break;case`message`:s.dispatch(t.channel??null,t.event,t.payload);break;case`pong`:k.onPong();break;case`presence_join`:C.handlePresenceJoin(t.room,t.id,t.state);break;case`presence_leave`:C.handlePresenceLeave(t.room,t.id);break;case`presence_state`:C.handlePresenceState(t.room,t.members)}}function N(e){let t=c.get(e)??0;c.set(e,t+1),t===0&&O.open&&S(h({channel:e,type:`subscribe`}))}function F(e){let t=c.get(e);if(t){if(t>1){c.set(e,t-1);return}c.delete(e),O.open&&S(h({channel:e,type:`unsubscribe`}))}}return{channel(e){if(u)throw new a;return N(e),m(e,(e,t,n)=>{let i=r.transform?r.transform({channel:e,event:t,payload:n}):{channel:e,event:t,payload:n};i!==null&&S(h({...i,type:`message`}))},(e,t,n)=>s.add(e,t,n),i.signal,()=>F(e))},connect(){return u?Promise.reject(new a):O.connect()},disconnect(e=1e3,t=``){u||(E(),O.disconnect(e,t))},get disposalSignal(){return i.signal},dispose(){u||(u=!0,k.stop(),g=!0,C.reset(),C.rejectAll(new a),O.dispose(),c.clear(),C.dispose(),s.clear(),v({type:`dispose`}),l.clear(),i.abort())},get disposed(){return u},on(e,t){if(u)throw new a;return s.add(null,e,t)},once(e,t){if(u)throw new a;let n=()=>{};return n=s.add(null,e,e=>{n(),t(e)}),n},room(e,t){if(u)throw new a;return C.createScope(e,!0,t)},get rooms(){return C.rooms},send(e,t){if(u)throw new a;let n=r.transform?r.transform({event:e,payload:t}):{event:e,payload:t};n!==null&&S(h({...n,type:`message`}))},get status(){return x},tap(e,t){if(u)throw new a;if(l.add(e),t?.signal){if(t.signal.aborted)return l.delete(e),()=>{};let n=()=>l.delete(e);return t.signal.addEventListener(`abort`,n,{once:!0}),()=>{l.delete(e),t.signal?.removeEventListener(`abort`,n)}}return()=>l.delete(e)},[Symbol.dispose](){this.dispose()},wait(e,t){return p(e,i.signal,t,(e,t)=>s.add(null,e,t))}}}exports.PulseAbortError=i,exports.PulseConnectionError=t,exports.PulseDisposedError=a,exports.PulseError=e,exports.PulseProtocolError=o,exports.PulseRoomTimeoutError=r,exports.PulseTimeoutError=n,exports.createPulse=F;
2
2
  //# sourceMappingURL=pulse.cjs.map