@vielzeug/pulse 2.2.2 → 2.3.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
@@ -29,7 +29,10 @@ type Schema = {
29
29
 
30
30
  const pulse = createPulse<Schema>('wss://api.example.com/ws', {
31
31
  reconnect: true,
32
- onError: (error) => console.error(error),
32
+ });
33
+ pulse.tap((event) => {
34
+ if (event.type === 'error') console.error(event.error);
35
+ if (event.type === 'status-change') console.log('status:', event.status);
33
36
  });
34
37
 
35
38
  const chat = pulse.channel('chat');
@@ -55,7 +58,7 @@ pulse.dispose();
55
58
  - Reconnect restores channels, room memberships, and the last successfully published local presence state.
56
59
  - `send()` throws `PulseConnectionError` while disconnected; Pulse never silently drops or buffers application messages.
57
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.
58
- - `onError` receives typed transport and protocol errors.
61
+ - Call `tap()` to observe lifecycle events; Pulse reports transport and protocol errors there rather than throwing asynchronously.
59
62
 
60
63
  ## Migration
61
64
 
package/dist/errors.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=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}},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}};exports.PulseAbortError=i,exports.PulseConnectionError=t,exports.PulseDisposedError=a,exports.PulseError=e,exports.PulseProtocolError=o,exports.PulseRoomTimeoutError=r,exports.PulseTimeoutError=n;
1
+ 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}};exports.PulseAbortError=i,exports.PulseConnectionError=t,exports.PulseDisposedError=a,exports.PulseError=e,exports.PulseProtocolError=o,exports.PulseRoomTimeoutError=r,exports.PulseTimeoutError=n;
2
2
  //# sourceMappingURL=errors.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.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()` times out before the server 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 a room scope's `joined` promise times out before the server\n * confirms membership, or when an AbortSignal fires before confirmation.\n */\nexport class PulseRoomTimeoutError extends PulseError {\n readonly room: string;\n\n constructor(room: string, opts?: ErrorOptions) {\n super(`Timed out waiting for room \"${room}\"`, opts);\n this.room = room;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires,\n * or when a room scope's join is aborted before the server confirms.\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"],"mappings":"AAIA,IAAa,EAAb,MAAa,UAAmB,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CAEA,OAAO,GAAG,EAAiC,CACzC,OAAO,aAAe,CACxB,CACF,EAMa,EAAb,cAA0C,CAAW,CACnD,IAEA,YAAY,EAAiB,EAAa,EAAqB,CAC7D,MAAM,EAAS,CAAI,EACnB,KAAK,IAAM,CACb,CACF,EAKa,EAAb,cAAuC,CAAW,CAChD,MAEA,YAAY,EAAe,EAAqB,CAC9C,MAAM,0BAA0B,EAAM,GAAI,CAAI,EAC9C,KAAK,MAAQ,CACf,CACF,EAMa,EAAb,cAA2C,CAAW,CACpD,KAEA,YAAY,EAAc,EAAqB,CAC7C,MAAM,+BAA+B,EAAK,GAAI,CAAI,EAClD,KAAK,KAAO,CACd,CACF,EAMa,EAAb,cAAqC,CAAW,CAC9C,YAAY,EAAqB,CAC/B,MAAM,UAAW,CAAI,CACvB,CACF,EAKa,EAAb,cAAwC,CAAW,CACjD,YAAY,EAAS,QAAS,EAAqB,CACjD,MAAM,GAAG,EAAO,uBAAwB,CAAI,CAC9C,CACF,EAMa,EAAb,cAAwC,CAAW,CACjD,IAEA,YAAY,EAAiB,EAAe,EAAqB,CAC/D,MAAM,EAAS,CAAI,EACnB,KAAK,IAAM,CACb,CACF"}
1
+ {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.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\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()` times out before the server 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 a room scope's `joined` promise times out before the server\n * confirms membership, or when an AbortSignal fires before confirmation.\n */\nexport class PulseRoomTimeoutError extends PulseError {\n readonly room: string;\n\n constructor(room: string, opts?: ErrorOptions) {\n super(`Timed out waiting for room \"${room}\"`, opts);\n this.room = room;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires,\n * or when a room scope's join is aborted before the server confirms.\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"],"mappings":"AAIA,IAAa,EAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAMa,EAAb,cAA0C,CAAW,CACnD,IAEA,YAAY,EAAiB,EAAa,EAAqB,CAC7D,MAAM,EAAS,CAAI,EACnB,KAAK,IAAM,CACb,CACF,EAKa,EAAb,cAAuC,CAAW,CAChD,MAEA,YAAY,EAAe,EAAqB,CAC9C,MAAM,0BAA0B,EAAM,GAAI,CAAI,EAC9C,KAAK,MAAQ,CACf,CACF,EAMa,EAAb,cAA2C,CAAW,CACpD,KAEA,YAAY,EAAc,EAAqB,CAC7C,MAAM,+BAA+B,EAAK,GAAI,CAAI,EAClD,KAAK,KAAO,CACd,CACF,EAMa,EAAb,cAAqC,CAAW,CAC9C,YAAY,EAAqB,CAC/B,MAAM,UAAW,CAAI,CACvB,CACF,EAKa,EAAb,cAAwC,CAAW,CACjD,YAAY,EAAS,QAAS,EAAqB,CACjD,MAAM,GAAG,EAAO,uBAAwB,CAAI,CAC9C,CACF,EAMa,EAAb,cAAwC,CAAW,CACjD,IAEA,YAAY,EAAiB,EAAe,EAAqB,CAC/D,MAAM,EAAS,CAAI,EACnB,KAAK,IAAM,CACb,CACF"}
package/dist/errors.d.ts CHANGED
@@ -4,7 +4,6 @@
4
4
  */
5
5
  export declare class PulseError extends Error {
6
6
  constructor(message: string, opts?: ErrorOptions);
7
- static is(err: unknown): err is PulseError;
8
7
  }
9
8
  /**
10
9
  * Thrown when a WebSocket connection cannot be established or is lost
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;IAMhD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU;CAG3C;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;gBAET,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAI9D;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAI/C;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,UAAU;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAI9C;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,UAAU;gBACjC,IAAI,CAAC,EAAE,YAAY;CAGhC;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;gBACpC,MAAM,SAAU,EAAE,IAAI,CAAC,EAAE,YAAY;CAGlD;AAED;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;IAChD,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;gBAEV,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY;CAIhE"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAKjD;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;gBAET,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAI9D;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAI/C;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,UAAU;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAI9C;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,UAAU;gBACjC,IAAI,CAAC,EAAE,YAAY;CAGhC;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;gBACpC,MAAM,SAAU,EAAE,IAAI,CAAC,EAAE,YAAY;CAGlD;AAED;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;IAChD,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;gBAEV,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY;CAIhE"}
package/dist/errors.js CHANGED
@@ -1,11 +1,8 @@
1
1
  //#region src/errors.ts
2
- var e = class e extends Error {
2
+ var e = class extends Error {
3
3
  constructor(e, t) {
4
4
  super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
5
5
  }
6
- static is(t) {
7
- return t instanceof e;
8
- }
9
6
  }, t = class extends e {
10
7
  url;
11
8
  constructor(e, t, n) {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.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()` times out before the server 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 a room scope's `joined` promise times out before the server\n * confirms membership, or when an AbortSignal fires before confirmation.\n */\nexport class PulseRoomTimeoutError extends PulseError {\n readonly room: string;\n\n constructor(room: string, opts?: ErrorOptions) {\n super(`Timed out waiting for room \"${room}\"`, opts);\n this.room = room;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires,\n * or when a room scope's join is aborted before the server confirms.\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"],"mappings":";AAIA,IAAa,IAAb,MAAa,UAAmB,MAAM;CACpC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;CAEA,OAAO,GAAG,GAAiC;EACzC,OAAO,aAAe;CACxB;AACF,GAMa,IAAb,cAA0C,EAAW;CACnD;CAEA,YAAY,GAAiB,GAAa,GAAqB;EAE7D,AADA,MAAM,GAAS,CAAI,GACnB,KAAK,MAAM;CACb;AACF,GAKa,IAAb,cAAuC,EAAW;CAChD;CAEA,YAAY,GAAe,GAAqB;EAE9C,AADA,MAAM,0BAA0B,EAAM,IAAI,CAAI,GAC9C,KAAK,QAAQ;CACf;AACF,GAMa,IAAb,cAA2C,EAAW;CACpD;CAEA,YAAY,GAAc,GAAqB;EAE7C,AADA,MAAM,+BAA+B,EAAK,IAAI,CAAI,GAClD,KAAK,OAAO;CACd;AACF,GAMa,IAAb,cAAqC,EAAW;CAC9C,YAAY,GAAqB;EAC/B,MAAM,WAAW,CAAI;CACvB;AACF,GAKa,IAAb,cAAwC,EAAW;CACjD,YAAY,IAAS,SAAS,GAAqB;EACjD,MAAM,GAAG,EAAO,wBAAwB,CAAI;CAC9C;AACF,GAMa,IAAb,cAAwC,EAAW;CACjD;CAEA,YAAY,GAAiB,GAAe,GAAqB;EAE/D,AADA,MAAM,GAAS,CAAI,GACnB,KAAK,MAAM;CACb;AACF"}
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.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\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()` times out before the server 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 a room scope's `joined` promise times out before the server\n * confirms membership, or when an AbortSignal fires before confirmation.\n */\nexport class PulseRoomTimeoutError extends PulseError {\n readonly room: string;\n\n constructor(room: string, opts?: ErrorOptions) {\n super(`Timed out waiting for room \"${room}\"`, opts);\n this.room = room;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires,\n * or when a room scope's join is aborted before the server confirms.\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"],"mappings":";AAIA,IAAa,IAAb,cAAgC,MAAM;CACpC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF,GAMa,IAAb,cAA0C,EAAW;CACnD;CAEA,YAAY,GAAiB,GAAa,GAAqB;EAE7D,AADA,MAAM,GAAS,CAAI,GACnB,KAAK,MAAM;CACb;AACF,GAKa,IAAb,cAAuC,EAAW;CAChD;CAEA,YAAY,GAAe,GAAqB;EAE9C,AADA,MAAM,0BAA0B,EAAM,IAAI,CAAI,GAC9C,KAAK,QAAQ;CACf;AACF,GAMa,IAAb,cAA2C,EAAW;CACpD;CAEA,YAAY,GAAc,GAAqB;EAE7C,AADA,MAAM,+BAA+B,EAAK,IAAI,CAAI,GAClD,KAAK,OAAO;CACd;AACF,GAMa,IAAb,cAAqC,EAAW;CAC9C,YAAY,GAAqB;EAC/B,MAAM,WAAW,CAAI;CACvB;AACF,GAKa,IAAb,cAAwC,EAAW;CACjD,YAAY,IAAS,SAAS,GAAqB;EACjD,MAAM,GAAG,EAAO,wBAAwB,CAAI;CAC9C;AACF,GAMa,IAAb,cAAwC,EAAW;CACjD;CAEA,YAAY,GAAiB,GAAe,GAAqB;EAE/D,AADA,MAAM,GAAS,CAAI,GACnB,KAAK,MAAM;CACb;AACF"}
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, PulseOptions, PulseSchema, PulseStatus, ReconnectOptions, RoomDefinition, RoomDefinitions, RoomMap, RoomOptions, RoomScope, RoomScopeBase, ServerEvents, Unsubscribe, } from './types';
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';
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,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,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 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{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=!1,f=!0;function p(e){i.onError?.(e)}function v(e){C.send(e)}let y=N({disposalSignal:a.signal,isOpen:()=>C.open,send:v});function b(){for(let e of u.keys())v(_({channel:e,type:`subscribe`}));y.restore()}function x(){y.reset()}function S(){f||(f=!0,w.stop(),x(),y.rejectAll(new n(`Connection closed before room confirmation`,r)))}let C=m(r,i.protocols,i.reconnect,{onClose(){S()},onError:p,onMessage(e){let t;try{t=k(e.data)}catch(t){p(t instanceof s?t:new s(`Failed to decode frame`,e.data));return}if(t.kind===`unknown`){p(new s(`Unknown frame type "${t.type}"`,e.data));return}try{T(t)}catch(e){p(new s(`Frame handler threw`,t.frame,{cause:e}))}},onOpen(){f=!1;try{b(),w.start()}catch(e){p(e instanceof t?e:new n(`Session restore failed`,r))}},onStatus(e){c.value=e}}),w=M(i.heartbeat,e=>{C.open&&v(e)},()=>C.forceReconnect(4e3,`heartbeat timeout`));function T(e){let t=e.frame;switch(t.type){case`error`:p(new s(`Server error [${t.code}]: ${t.message}`,t));break;case`joined`:y.handleJoined(t.room);break;case`left`:y.handleLeft(t.room);break;case`message`:l.dispatch(t.channel??null,t.event,t.payload);break;case`pong`:w.onPong();break;case`presence_join`:y.handlePresenceJoin(t.room,t.id,t.state);break;case`presence_leave`:y.handlePresenceLeave(t.room,t.id);break;case`presence_state`:y.handlePresenceState(t.room,t.members)}}function E(e){let t=u.get(e)??0;u.set(e,t+1),t===0&&C.open&&v(_({channel:e,type:`subscribe`}))}function D(e){let t=u.get(e);if(t){if(t>1){u.set(e,t-1);return}u.delete(e),C.open&&v(_({channel:e,type:`unsubscribe`}))}}return{channel(e){if(d)throw new o;return E(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&&v(_({...r,type:`message`}))},(e,t,n)=>l.add(e,t,n),a.signal,()=>D(e))},connect(){return d?Promise.reject(new o):C.connect()},disconnect(e=1e3,t=``){d||(S(),C.disconnect(e,t))},get disposalSignal(){return a.signal},dispose(){d||(d=!0,w.stop(),f=!0,y.reset(),y.rejectAll(new o),C.dispose(),u.clear(),y.dispose(),l.clear(),a.abort())},get disposed(){return d},on(e,t){if(d)throw new o;return l.add(null,e,t)},once(e,t){if(d)throw new o;let n=()=>{};return n=l.add(null,e,e=>{n(),t(e)}),n},room(e,t){if(d)throw new o;return y.createScope(e,!0,t)},get rooms(){return y.rooms},send(e,t){if(d)throw new o;let n=i.transform?i.transform({event:e,payload:t}):{event:e,payload:t};n!==null&&v(_({...n,type:`message`}))},get status(){return c},[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`});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;
2
2
  //# sourceMappingURL=pulse.cjs.map
@@ -1 +1 @@
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/room.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()` times out before the server 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 a room scope's `joined` promise times out before the server\n * confirms membership, or when an AbortSignal fires before confirmation.\n */\nexport class PulseRoomTimeoutError extends PulseError {\n readonly room: string;\n\n constructor(room: string, opts?: ErrorOptions) {\n super(`Timed out waiting for room \"${room}\"`, opts);\n this.room = room;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires,\n * or when a room scope's join is aborted before the server confirms.\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 * 2 ** attempt, maxMs);\n\n return Math.random() * base;\n}\n","import { defaultReconnectDelay, sleep } from './_utils';\nimport { PulseConnectionError, PulseDisposedError } from './errors';\nimport type { PulseStatus, ReconnectOptions } from './types';\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 { 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","import { deriveAbortController } from './_utils';\nimport { createWaitPromise } from './_wait';\nimport { PulseDisposedError } from './errors';\nimport type { EventKey, MessageMap, PulseChannel, Unsubscribe } from './types';\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\nfunction hasRoom(frame: unknown): frame is { room: string } {\n return getStringProp(frame, 'room') !== 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 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 hasChannel(frame: unknown): frame is { channel: string } {\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 (hasRoom(frame)) return { frame: frame as InJoinedFrame, kind: 'known' };\n\n break;\n case 'left':\n if (hasRoom(frame)) return { frame: frame as InLeftFrame, 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 (hasChannel(frame)) return { frame: frame as InSubscribedFrame, kind: 'known' };\n\n break;\n case 'unsubscribed':\n if (hasChannel(frame)) return { frame: frame as InUnsubscribedFrame, 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 { encode } from './protocol';\nimport type { HeartbeatOptions } from './types';\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';\nimport { combineSignals, deriveAbortController } from './_utils';\nimport { PulseAbortError, PulseConnectionError, PulseDisposedError, PulseError, PulseRoomTimeoutError } from './errors';\nimport { encode } from './protocol';\nimport type { PresenceRoomScope, RoomOptions, RoomScopeBase, Unsubscribe } from './types';\n\ntype PresenceHandlers = {\n onState(members: Record<string, unknown>): void;\n onJoin(id: string, state: unknown): void;\n onLeave(id: string): void;\n reset(): void;\n};\n\ntype RoomState = {\n refs: number;\n confirmed: boolean;\n joinSent: boolean;\n leaveAfterJoin: boolean;\n localPresence: unknown | undefined;\n scopes: Set<ScopeInternal>;\n};\n\ntype ScopeInternal = {\n readonly name: string;\n readonly joined: Promise<void>;\n resolveJoined(): void;\n rejectJoined(error: PulseError): void;\n isSettled(): boolean;\n presenceHandlers?: PresenceHandlers;\n};\n\ntype RegistryOptions = {\n isOpen: () => boolean;\n send: (frame: string) => void;\n disposalSignal: AbortSignal;\n};\n\nexport type RoomRegistry = {\n readonly rooms: Signal<ReadonlySet<string>>;\n createScope(name: string, hasPresence: boolean, opts: RoomOptions | undefined): RoomScopeBase | PresenceRoomScope;\n handleJoined(room: string): void;\n handleLeft(room: string): void;\n handlePresenceState(room: string, members: Record<string, unknown>): void;\n handlePresenceJoin(room: string, id: string, state: unknown): void;\n handlePresenceLeave(room: string, id: string): void;\n restore(): void;\n reset(): void;\n rejectAll(error: PulseError): void;\n dispose(): void;\n};\n\n/**\n * Manages ref-counted room memberships, pending join tracking, and presence state.\n * @internal\n */\nexport function createRoomRegistry(opts: RegistryOptions): RoomRegistry {\n const rooms = signal<ReadonlySet<string>>(new Set());\n const states = new Map<string, RoomState>();\n\n function getOrCreate(name: string): RoomState {\n let state = states.get(name);\n\n if (!state) {\n state = {\n confirmed: false,\n joinSent: false,\n leaveAfterJoin: false,\n localPresence: undefined,\n refs: 0,\n scopes: new Set(),\n };\n states.set(name, state);\n }\n\n return state;\n }\n\n function setRoom(name: string, joined: boolean): void {\n const next = new Set(rooms.value);\n if (joined) next.add(name);\n else next.delete(name);\n rooms.value = next;\n }\n\n function sendJoin(name: string): void {\n opts.send(encode({ room: name, type: 'join' }));\n }\n\n function sendLeave(name: string): void {\n opts.send(encode({ room: name, type: 'leave' }));\n }\n\n function updatePresence(name: string, state: unknown): void {\n const roomState = states.get(name);\n if (!roomState) throw new PulseDisposedError(`Room \"${name}\"`);\n if (!opts.isOpen()) throw new PulseConnectionError('Connection is not open', name);\n opts.send(encode({ room: name, state, type: 'presence' }));\n roomState.localPresence = state;\n }\n\n function releaseScope(name: string, scope: ScopeInternal): void {\n const state = states.get(name);\n if (!state || !state.scopes.has(scope)) return;\n\n state.scopes.delete(scope);\n state.refs--;\n\n if (state.refs === 0) {\n if (state.confirmed) {\n state.confirmed = false;\n state.localPresence = undefined;\n setRoom(name, false);\n if (opts.isOpen()) sendLeave(name);\n states.delete(name);\n } else if (state.joinSent) {\n state.leaveAfterJoin = true;\n // Keep state alive until handleJoined processes the compensating leave\n } else {\n states.delete(name);\n }\n }\n }\n\n return {\n createScope(name, hasPresence, joinOpts) {\n if (opts.disposalSignal.aborted) {\n return createDeadScope(name, hasPresence);\n }\n\n const state = getOrCreate(name);\n const ctrl = deriveAbortController(opts.disposalSignal);\n\n let resolveJoined!: () => void;\n let rejectJoined!: (error: PulseError) => void;\n let settled = false;\n\n const joined = new Promise<void>((resolve, reject) => {\n resolveJoined = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n resolve();\n };\n rejectJoined = (error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n reject(error);\n };\n });\n // Suppress unhandled rejection when joined is never awaited\n joined.catch(() => {});\n\n // Timeout + abort handling\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const signals: AbortSignal[] = [ctrl.signal];\n\n if (joinOpts?.signal) signals.push(joinOpts.signal);\n\n if (joinOpts?.timeout !== undefined) {\n const timeoutCtrl = new AbortController();\n timeoutId = setTimeout(() => timeoutCtrl.abort(new PulseRoomTimeoutError(name)), joinOpts.timeout);\n signals.push(timeoutCtrl.signal);\n }\n\n const combined = signals.length === 1 ? signals[0]! : combineSignals(signals[0]!, ...signals.slice(1));\n\n // Presence state\n let presenceSignal: Signal<Map<string, unknown>> | undefined;\n let presenceHandlers: PresenceHandlers | undefined;\n const joinHandlers = new Set<(memberId: string, state: unknown) => void>();\n const leaveHandlers = new Set<(memberId: string) => void>();\n\n if (hasPresence) {\n presenceSignal = signal(new Map<string, unknown>());\n presenceHandlers = {\n onJoin(id, memberState) {\n const next = new Map(presenceSignal!.value);\n next.set(id, memberState);\n presenceSignal!.value = next;\n for (const handler of joinHandlers) handler(id, memberState);\n },\n onLeave(id) {\n const next = new Map(presenceSignal!.value);\n next.delete(id);\n presenceSignal!.value = next;\n for (const handler of leaveHandlers) handler(id);\n },\n onState(members) {\n const next = new Map<string, unknown>();\n for (const [id, memberState] of Object.entries(members)) next.set(id, memberState);\n presenceSignal!.value = next;\n },\n reset() {\n presenceSignal!.value = new Map();\n },\n };\n }\n\n const scope: ScopeInternal = {\n isSettled: () => settled,\n joined,\n name,\n presenceHandlers,\n rejectJoined,\n resolveJoined,\n };\n\n // Abort handler — rejects joined and auto-disposes\n const onAbort = (): void => {\n clearTimeout(timeoutId);\n ctrl.abort();\n const reason = combined.reason;\n const error = reason instanceof PulseError ? reason : new PulseAbortError();\n scope.rejectJoined(error);\n releaseScope(name, scope);\n };\n\n // Register scope BEFORE setting up abort, so releaseScope is safe\n state.refs++;\n state.scopes.add(scope);\n\n if (combined.aborted) {\n onAbort();\n return buildPublicScope(scope, ctrl.signal, presenceSignal, joinHandlers, leaveHandlers, name, updatePresence);\n }\n\n combined.addEventListener('abort', onAbort, { once: true });\n\n // Start join\n if (state.confirmed) {\n scope.resolveJoined();\n } else if (!state.joinSent && opts.isOpen()) {\n state.joinSent = true;\n try {\n sendJoin(name);\n } catch (error) {\n state.joinSent = false;\n scope.rejectJoined(\n error instanceof PulseError ? error : new PulseConnectionError('Failed to send join', name),\n );\n }\n }\n\n // Build dispose function\n const disposeScope = (): void => {\n if (ctrl.signal.aborted) return;\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n ctrl.abort();\n if (!settled) scope.rejectJoined(new PulseDisposedError(`Room \"${name}\"`));\n releaseScope(name, scope);\n };\n\n return buildPublicScope(\n scope,\n ctrl.signal,\n presenceSignal,\n joinHandlers,\n leaveHandlers,\n name,\n updatePresence,\n disposeScope,\n );\n },\n\n dispose() {\n rooms.value = new Set();\n states.clear();\n },\n\n handleJoined(room) {\n const state = states.get(room);\n if (!state) return;\n\n state.confirmed = true;\n state.joinSent = false;\n setRoom(room, true);\n\n if (state.leaveAfterJoin) {\n state.leaveAfterJoin = false;\n state.confirmed = false;\n setRoom(room, false);\n if (opts.isOpen()) sendLeave(room);\n states.delete(room);\n return;\n }\n\n for (const scope of state.scopes) {\n if (!scope.isSettled()) scope.resolveJoined();\n }\n },\n\n handleLeft(room) {\n const state = states.get(room);\n if (!state) return;\n\n state.confirmed = false;\n state.joinSent = false;\n setRoom(room, false);\n },\n\n handlePresenceJoin(room, id, memberState) {\n const state = states.get(room);\n if (!state) return;\n for (const scope of state.scopes) scope.presenceHandlers?.onJoin(id, memberState);\n },\n\n handlePresenceLeave(room, id) {\n const state = states.get(room);\n if (!state) return;\n for (const scope of state.scopes) scope.presenceHandlers?.onLeave(id);\n },\n\n handlePresenceState(room, members) {\n const state = states.get(room);\n if (!state) return;\n for (const scope of state.scopes) scope.presenceHandlers?.onState(members);\n },\n\n rejectAll(error) {\n for (const state of states.values()) {\n for (const scope of state.scopes) {\n if (!scope.isSettled()) scope.rejectJoined(error);\n }\n }\n },\n\n reset() {\n rooms.value = new Set();\n for (const [name, state] of states) {\n if (state.refs === 0) {\n states.delete(name);\n continue;\n }\n state.confirmed = false;\n state.joinSent = false;\n for (const scope of state.scopes) scope.presenceHandlers?.reset();\n }\n },\n\n restore() {\n for (const [name, state] of states) {\n if (state.refs === 0) continue;\n\n state.confirmed = false;\n state.joinSent = true;\n\n try {\n sendJoin(name);\n } catch {\n state.joinSent = false;\n }\n\n if (state.localPresence !== undefined) {\n try {\n opts.send(encode({ room: name, state: state.localPresence, type: 'presence' }));\n } catch {\n // non-fatal; next updatePresence retries\n }\n }\n }\n },\n rooms,\n };\n}\n\nfunction buildPublicScope(\n scope: ScopeInternal,\n disposalSignal: AbortSignal,\n presenceSignal: Signal<Map<string, unknown>> | undefined,\n joinHandlers: Set<(memberId: string, state: unknown) => void>,\n leaveHandlers: Set<(memberId: string) => void>,\n name: string,\n updatePresence: (name: string, state: unknown) => void,\n disposeFn?: () => void,\n): RoomScopeBase | PresenceRoomScope {\n const dispose = disposeFn ?? (() => {});\n\n if (presenceSignal) {\n const publicScope: PresenceRoomScope = {\n get disposalSignal() {\n return disposalSignal;\n },\n dispose,\n get disposed() {\n return disposalSignal.aborted;\n },\n get joined() {\n return scope.joined;\n },\n get name() {\n return scope.name;\n },\n onJoin(handler: (memberId: string, state: unknown) => void): Unsubscribe {\n if (disposalSignal.aborted) throw new PulseDisposedError(`Room \"${name}\"`);\n joinHandlers.add(handler);\n return () => joinHandlers.delete(handler);\n },\n onLeave(handler: (memberId: string) => void): Unsubscribe {\n if (disposalSignal.aborted) throw new PulseDisposedError(`Room \"${name}\"`);\n leaveHandlers.add(handler);\n return () => leaveHandlers.delete(handler);\n },\n get presence() {\n return presenceSignal;\n },\n updatePresence(state: unknown) {\n updatePresence(name, state);\n },\n [Symbol.dispose]: dispose,\n };\n return publicScope;\n }\n\n const publicScope: RoomScopeBase = {\n get disposalSignal() {\n return disposalSignal;\n },\n dispose,\n get disposed() {\n return disposalSignal.aborted;\n },\n get joined() {\n return scope.joined;\n },\n get name() {\n return scope.name;\n },\n [Symbol.dispose]: dispose,\n };\n return publicScope;\n}\n\nfunction createDeadScope(name: string, hasPresence: boolean): RoomScopeBase | PresenceRoomScope {\n const disposalCtrl = new AbortController();\n disposalCtrl.abort(new PulseDisposedError());\n\n const joined = Promise.reject(new PulseDisposedError(`Room \"${name}\"`));\n joined.catch(() => {});\n\n if (hasPresence) {\n return {\n disposalSignal: disposalCtrl.signal,\n dispose() {},\n disposed: true,\n joined,\n name,\n onJoin() {\n throw new PulseDisposedError(`Room \"${name}\"`);\n },\n onLeave() {\n throw new PulseDisposedError(`Room \"${name}\"`);\n },\n presence: signal(new Map()),\n updatePresence() {\n throw new PulseDisposedError(`Room \"${name}\"`);\n },\n [Symbol.dispose]() {},\n };\n }\n\n return {\n disposalSignal: disposalCtrl.signal,\n dispose() {},\n disposed: true,\n joined,\n name,\n [Symbol.dispose]() {},\n };\n}\n","import { signal } from '@vielzeug/ripple';\nimport { createConnection } from './_connection';\nimport { createWaitPromise } from './_wait';\nimport { createChannel } from './channel';\nimport { PulseConnectionError, PulseDisposedError, PulseError, PulseProtocolError } from './errors';\nimport { createHeartbeat, type HeartbeatHandle } from './heartbeat';\nimport { type DecodedInFrame, decodeValidated, encode } from './protocol';\nimport { createRoomRegistry, type RoomRegistry } from './room';\nimport type {\n ChannelMap,\n ClientEvents,\n EventKey,\n Pulse,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n RoomMap,\n RoomOptions,\n RoomScope,\n ServerEvents,\n Unsubscribe,\n} from './types';\n\ntype Handler = (payload: unknown) => void;\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 if (!events) {\n events = new Map();\n this.map.set(channel, events);\n }\n\n let handlers = events.get(event);\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 if (handlers.size === 0) events.delete(event);\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<S extends PulseSchema = PulseSchema>(url: string, options: PulseOptions = {}): Pulse<S> {\n const disposalCtrl = new AbortController();\n const status = signal<PulseStatus>('closed');\n const listeners = new ListenerMap();\n const channelReferences = new Map<string, number>();\n\n let disposed = false;\n let transportClosed = true;\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 const rooms: RoomRegistry = createRoomRegistry({\n disposalSignal: disposalCtrl.signal,\n isOpen: () => connection.open,\n send: sendInternal,\n });\n\n function restoreSession(): void {\n for (const channel of channelReferences.keys()) {\n sendInternal(encode({ channel, type: 'subscribe' }));\n }\n rooms.restore();\n }\n\n function resetRemoteSession(): void {\n rooms.reset();\n }\n\n function handleTransportClose(): void {\n if (transportClosed) return;\n transportClosed = true;\n heartbeat.stop();\n resetRemoteSession();\n rooms.rejectAll(new PulseConnectionError('Connection closed before room confirmation', url));\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 return;\n }\n\n if (decoded.kind === 'unknown') {\n report(new PulseProtocolError(`Unknown frame type \"${decoded.type}\"`, event.data));\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 transportClosed = false;\n try {\n restoreSession();\n heartbeat.start();\n } catch (error) {\n report(error instanceof PulseError ? error : new PulseConnectionError('Session restore failed', url));\n }\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 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 break;\n case 'joined':\n rooms.handleJoined(frame.room);\n break;\n case 'left':\n rooms.handleLeft(frame.room);\n break;\n case 'message':\n listeners.dispatch(frame.channel ?? null, frame.event, frame.payload);\n break;\n case 'pong':\n heartbeat.onPong();\n break;\n case 'presence_join':\n rooms.handlePresenceJoin(frame.room, frame.id, frame.state);\n break;\n case 'presence_leave':\n rooms.handlePresenceLeave(frame.room, frame.id);\n break;\n case 'presence_state':\n rooms.handlePresenceState(frame.room, frame.members);\n break;\n case 'subscribed':\n case 'unsubscribed':\n break;\n }\n }\n\n function acquireChannel(name: string): void {\n const references = channelReferences.get(name) ?? 0;\n channelReferences.set(name, references + 1);\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 if (!references) return;\n\n if (references > 1) {\n channelReferences.set(name, references - 1);\n return;\n }\n\n channelReferences.delete(name);\n if (connection.open) sendInternal(encode({ channel: name, type: 'unsubscribe' }));\n }\n\n const pulse: Pulse<S> = {\n channel<K extends keyof ChannelMap<S> & string>(name: K) {\n if (disposed) throw new PulseDisposedError();\n\n acquireChannel(name);\n\n return createChannel(\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 return connection.connect();\n },\n\n disconnect(code = 1000, reason = ''): void {\n if (disposed) return;\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 transportClosed = true;\n rooms.reset();\n rooms.rejectAll(new PulseDisposedError());\n connection.dispose();\n channelReferences.clear();\n rooms.dispose();\n listeners.clear();\n disposalCtrl.abort();\n },\n\n get disposed() {\n return disposed;\n },\n\n on<K extends EventKey<ServerEvents<S>>>(event: K, handler: (payload: ServerEvents<S>[K]) => void): Unsubscribe {\n if (disposed) throw new PulseDisposedError();\n return listeners.add(null, event, handler as Handler);\n },\n\n once<K extends EventKey<ServerEvents<S>>>(event: K, handler: (payload: ServerEvents<S>[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 room<K extends keyof RoomMap<S> & string>(name: K, opts?: RoomOptions): RoomScope<RoomMap<S>[K]> {\n if (disposed) throw new PulseDisposedError();\n // Always create with presence machinery — the type-level RoomScope<R> narrows\n // the public surface so plain rooms don't expose presence members.\n return rooms.createScope(name, true, opts) as RoomScope<RoomMap<S>[K]>;\n },\n\n get rooms() {\n return rooms.rooms;\n },\n\n send<K extends EventKey<ClientEvents<S>>>(event: K, payload: ClientEvents<S>[K]): void {\n if (disposed) throw new PulseDisposedError();\n\n const transformed = options.transform ? options.transform({ event, payload }) : { event, payload };\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<ServerEvents<S>>>(\n event: K,\n opts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<ServerEvents<S>[K]> {\n return createWaitPromise<ServerEvents<S>[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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"}
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/room.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\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()` times out before the server 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 a room scope's `joined` promise times out before the server\n * confirms membership, or when an AbortSignal fires before confirmation.\n */\nexport class PulseRoomTimeoutError extends PulseError {\n readonly room: string;\n\n constructor(room: string, opts?: ErrorOptions) {\n super(`Timed out waiting for room \"${room}\"`, opts);\n this.room = room;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires,\n * or when a room scope's join is aborted before the server confirms.\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 * 2 ** attempt, maxMs);\n\n return Math.random() * base;\n}\n","import { defaultReconnectDelay, sleep } from './_utils';\nimport { PulseConnectionError, PulseDisposedError } from './errors';\nimport type { PulseStatus, ReconnectOptions } from './types';\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 { 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","import { deriveAbortController } from './_utils';\nimport { createWaitPromise } from './_wait';\nimport { PulseDisposedError } from './errors';\nimport type { EventKey, MessageMap, PulseChannel, Unsubscribe } from './types';\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\nfunction hasRoom(frame: unknown): frame is { room: string } {\n return getStringProp(frame, 'room') !== 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 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 hasChannel(frame: unknown): frame is { channel: string } {\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 (hasRoom(frame)) return { frame: frame as InJoinedFrame, kind: 'known' };\n\n break;\n case 'left':\n if (hasRoom(frame)) return { frame: frame as InLeftFrame, 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 (hasChannel(frame)) return { frame: frame as InSubscribedFrame, kind: 'known' };\n\n break;\n case 'unsubscribed':\n if (hasChannel(frame)) return { frame: frame as InUnsubscribedFrame, 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 { encode } from './protocol';\nimport type { HeartbeatOptions } from './types';\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';\nimport { combineSignals, deriveAbortController } from './_utils';\nimport { PulseAbortError, PulseConnectionError, PulseDisposedError, PulseError, PulseRoomTimeoutError } from './errors';\nimport { encode } from './protocol';\nimport type { PresenceRoomScope, RoomOptions, RoomScopeBase, Unsubscribe } from './types';\n\ntype PresenceHandlers = {\n onState(members: Record<string, unknown>): void;\n onJoin(id: string, state: unknown): void;\n onLeave(id: string): void;\n reset(): void;\n};\n\ntype RoomState = {\n refs: number;\n confirmed: boolean;\n joinSent: boolean;\n leaveAfterJoin: boolean;\n localPresence: unknown | undefined;\n scopes: Set<ScopeInternal>;\n};\n\ntype ScopeInternal = {\n readonly name: string;\n readonly joined: Promise<void>;\n resolveJoined(): void;\n rejectJoined(error: PulseError): void;\n isSettled(): boolean;\n presenceHandlers?: PresenceHandlers;\n};\n\ntype RegistryOptions = {\n isOpen: () => boolean;\n send: (frame: string) => void;\n disposalSignal: AbortSignal;\n};\n\nexport type RoomRegistry = {\n readonly rooms: Signal<ReadonlySet<string>>;\n createScope(name: string, hasPresence: boolean, opts: RoomOptions | undefined): RoomScopeBase | PresenceRoomScope;\n handleJoined(room: string): void;\n handleLeft(room: string): void;\n handlePresenceState(room: string, members: Record<string, unknown>): void;\n handlePresenceJoin(room: string, id: string, state: unknown): void;\n handlePresenceLeave(room: string, id: string): void;\n restore(): void;\n reset(): void;\n rejectAll(error: PulseError): void;\n dispose(): void;\n};\n\n/**\n * Manages ref-counted room memberships, pending join tracking, and presence state.\n * @internal\n */\nexport function createRoomRegistry(opts: RegistryOptions): RoomRegistry {\n const rooms = signal<ReadonlySet<string>>(new Set());\n const states = new Map<string, RoomState>();\n\n function getOrCreate(name: string): RoomState {\n let state = states.get(name);\n\n if (!state) {\n state = {\n confirmed: false,\n joinSent: false,\n leaveAfterJoin: false,\n localPresence: undefined,\n refs: 0,\n scopes: new Set(),\n };\n states.set(name, state);\n }\n\n return state;\n }\n\n function setRoom(name: string, joined: boolean): void {\n const next = new Set(rooms.value);\n if (joined) next.add(name);\n else next.delete(name);\n rooms.value = next;\n }\n\n function sendJoin(name: string): void {\n opts.send(encode({ room: name, type: 'join' }));\n }\n\n function sendLeave(name: string): void {\n opts.send(encode({ room: name, type: 'leave' }));\n }\n\n function updatePresence(name: string, state: unknown): void {\n const roomState = states.get(name);\n if (!roomState) throw new PulseDisposedError(`Room \"${name}\"`);\n if (!opts.isOpen()) throw new PulseConnectionError('Connection is not open', name);\n opts.send(encode({ room: name, state, type: 'presence' }));\n roomState.localPresence = state;\n }\n\n function releaseScope(name: string, scope: ScopeInternal): void {\n const state = states.get(name);\n if (!state || !state.scopes.has(scope)) return;\n\n state.scopes.delete(scope);\n state.refs--;\n\n if (state.refs === 0) {\n if (state.confirmed) {\n state.confirmed = false;\n state.localPresence = undefined;\n setRoom(name, false);\n if (opts.isOpen()) sendLeave(name);\n states.delete(name);\n } else if (state.joinSent) {\n state.leaveAfterJoin = true;\n // Keep state alive until handleJoined processes the compensating leave\n } else {\n states.delete(name);\n }\n }\n }\n\n return {\n createScope(name, hasPresence, joinOpts) {\n if (opts.disposalSignal.aborted) {\n return createDeadScope(name, hasPresence);\n }\n\n const state = getOrCreate(name);\n const ctrl = deriveAbortController(opts.disposalSignal);\n\n let resolveJoined!: () => void;\n let rejectJoined!: (error: PulseError) => void;\n let settled = false;\n\n const joined = new Promise<void>((resolve, reject) => {\n resolveJoined = () => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n resolve();\n };\n rejectJoined = (error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n reject(error);\n };\n });\n // Suppress unhandled rejection when joined is never awaited\n joined.catch(() => {});\n\n // Timeout + abort handling\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n const signals: AbortSignal[] = [ctrl.signal];\n\n if (joinOpts?.signal) signals.push(joinOpts.signal);\n\n if (joinOpts?.timeout !== undefined) {\n const timeoutCtrl = new AbortController();\n timeoutId = setTimeout(() => timeoutCtrl.abort(new PulseRoomTimeoutError(name)), joinOpts.timeout);\n signals.push(timeoutCtrl.signal);\n }\n\n const combined = signals.length === 1 ? signals[0]! : combineSignals(signals[0]!, ...signals.slice(1));\n\n // Presence state\n let presenceSignal: Signal<Map<string, unknown>> | undefined;\n let presenceHandlers: PresenceHandlers | undefined;\n const joinHandlers = new Set<(memberId: string, state: unknown) => void>();\n const leaveHandlers = new Set<(memberId: string) => void>();\n\n if (hasPresence) {\n presenceSignal = signal(new Map<string, unknown>());\n presenceHandlers = {\n onJoin(id, memberState) {\n const next = new Map(presenceSignal!.value);\n next.set(id, memberState);\n presenceSignal!.value = next;\n for (const handler of joinHandlers) handler(id, memberState);\n },\n onLeave(id) {\n const next = new Map(presenceSignal!.value);\n next.delete(id);\n presenceSignal!.value = next;\n for (const handler of leaveHandlers) handler(id);\n },\n onState(members) {\n const next = new Map<string, unknown>();\n for (const [id, memberState] of Object.entries(members)) next.set(id, memberState);\n presenceSignal!.value = next;\n },\n reset() {\n presenceSignal!.value = new Map();\n },\n };\n }\n\n const scope: ScopeInternal = {\n isSettled: () => settled,\n joined,\n name,\n presenceHandlers,\n rejectJoined,\n resolveJoined,\n };\n\n // Abort handler — rejects joined and auto-disposes\n const onAbort = (): void => {\n clearTimeout(timeoutId);\n ctrl.abort();\n const reason = combined.reason;\n const error = reason instanceof PulseError ? reason : new PulseAbortError();\n scope.rejectJoined(error);\n releaseScope(name, scope);\n };\n\n // Register scope BEFORE setting up abort, so releaseScope is safe\n state.refs++;\n state.scopes.add(scope);\n\n if (combined.aborted) {\n onAbort();\n return buildPublicScope(scope, ctrl.signal, presenceSignal, joinHandlers, leaveHandlers, name, updatePresence);\n }\n\n combined.addEventListener('abort', onAbort, { once: true });\n\n // Start join\n if (state.confirmed) {\n scope.resolveJoined();\n } else if (!state.joinSent && opts.isOpen()) {\n state.joinSent = true;\n try {\n sendJoin(name);\n } catch (error) {\n state.joinSent = false;\n scope.rejectJoined(\n error instanceof PulseError ? error : new PulseConnectionError('Failed to send join', name),\n );\n }\n }\n\n // Build dispose function\n const disposeScope = (): void => {\n if (ctrl.signal.aborted) return;\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n ctrl.abort();\n if (!settled) scope.rejectJoined(new PulseDisposedError(`Room \"${name}\"`));\n releaseScope(name, scope);\n };\n\n return buildPublicScope(\n scope,\n ctrl.signal,\n presenceSignal,\n joinHandlers,\n leaveHandlers,\n name,\n updatePresence,\n disposeScope,\n );\n },\n\n dispose() {\n rooms.value = new Set();\n states.clear();\n },\n\n handleJoined(room) {\n const state = states.get(room);\n if (!state) return;\n\n state.confirmed = true;\n state.joinSent = false;\n setRoom(room, true);\n\n if (state.leaveAfterJoin) {\n state.leaveAfterJoin = false;\n state.confirmed = false;\n setRoom(room, false);\n if (opts.isOpen()) sendLeave(room);\n states.delete(room);\n return;\n }\n\n for (const scope of state.scopes) {\n if (!scope.isSettled()) scope.resolveJoined();\n }\n },\n\n handleLeft(room) {\n const state = states.get(room);\n if (!state) return;\n\n state.confirmed = false;\n state.joinSent = false;\n setRoom(room, false);\n },\n\n handlePresenceJoin(room, id, memberState) {\n const state = states.get(room);\n if (!state) return;\n for (const scope of state.scopes) scope.presenceHandlers?.onJoin(id, memberState);\n },\n\n handlePresenceLeave(room, id) {\n const state = states.get(room);\n if (!state) return;\n for (const scope of state.scopes) scope.presenceHandlers?.onLeave(id);\n },\n\n handlePresenceState(room, members) {\n const state = states.get(room);\n if (!state) return;\n for (const scope of state.scopes) scope.presenceHandlers?.onState(members);\n },\n\n rejectAll(error) {\n for (const state of states.values()) {\n for (const scope of state.scopes) {\n if (!scope.isSettled()) scope.rejectJoined(error);\n }\n }\n },\n\n reset() {\n rooms.value = new Set();\n for (const [name, state] of states) {\n if (state.refs === 0) {\n states.delete(name);\n continue;\n }\n state.confirmed = false;\n state.joinSent = false;\n for (const scope of state.scopes) scope.presenceHandlers?.reset();\n }\n },\n\n restore() {\n for (const [name, state] of states) {\n if (state.refs === 0) continue;\n\n state.confirmed = false;\n state.joinSent = true;\n\n try {\n sendJoin(name);\n } catch {\n state.joinSent = false;\n }\n\n if (state.localPresence !== undefined) {\n try {\n opts.send(encode({ room: name, state: state.localPresence, type: 'presence' }));\n } catch {\n // non-fatal; next updatePresence retries\n }\n }\n }\n },\n rooms,\n };\n}\n\nfunction buildPublicScope(\n scope: ScopeInternal,\n disposalSignal: AbortSignal,\n presenceSignal: Signal<Map<string, unknown>> | undefined,\n joinHandlers: Set<(memberId: string, state: unknown) => void>,\n leaveHandlers: Set<(memberId: string) => void>,\n name: string,\n updatePresence: (name: string, state: unknown) => void,\n disposeFn?: () => void,\n): RoomScopeBase | PresenceRoomScope {\n const dispose = disposeFn ?? (() => {});\n\n if (presenceSignal) {\n const publicScope: PresenceRoomScope = {\n get disposalSignal() {\n return disposalSignal;\n },\n dispose,\n get disposed() {\n return disposalSignal.aborted;\n },\n get joined() {\n return scope.joined;\n },\n get name() {\n return scope.name;\n },\n onJoin(handler: (memberId: string, state: unknown) => void): Unsubscribe {\n if (disposalSignal.aborted) throw new PulseDisposedError(`Room \"${name}\"`);\n joinHandlers.add(handler);\n return () => joinHandlers.delete(handler);\n },\n onLeave(handler: (memberId: string) => void): Unsubscribe {\n if (disposalSignal.aborted) throw new PulseDisposedError(`Room \"${name}\"`);\n leaveHandlers.add(handler);\n return () => leaveHandlers.delete(handler);\n },\n get presence() {\n return presenceSignal;\n },\n updatePresence(state: unknown) {\n updatePresence(name, state);\n },\n [Symbol.dispose]: dispose,\n };\n return publicScope;\n }\n\n const publicScope: RoomScopeBase = {\n get disposalSignal() {\n return disposalSignal;\n },\n dispose,\n get disposed() {\n return disposalSignal.aborted;\n },\n get joined() {\n return scope.joined;\n },\n get name() {\n return scope.name;\n },\n [Symbol.dispose]: dispose,\n };\n return publicScope;\n}\n\nfunction createDeadScope(name: string, hasPresence: boolean): RoomScopeBase | PresenceRoomScope {\n const disposalCtrl = new AbortController();\n disposalCtrl.abort(new PulseDisposedError());\n\n const joined = Promise.reject(new PulseDisposedError(`Room \"${name}\"`));\n joined.catch(() => {});\n\n if (hasPresence) {\n return {\n disposalSignal: disposalCtrl.signal,\n dispose() {},\n disposed: true,\n joined,\n name,\n onJoin() {\n throw new PulseDisposedError(`Room \"${name}\"`);\n },\n onLeave() {\n throw new PulseDisposedError(`Room \"${name}\"`);\n },\n presence: signal(new Map()),\n updatePresence() {\n throw new PulseDisposedError(`Room \"${name}\"`);\n },\n [Symbol.dispose]() {},\n };\n }\n\n return {\n disposalSignal: disposalCtrl.signal,\n dispose() {},\n disposed: true,\n joined,\n name,\n [Symbol.dispose]() {},\n };\n}\n","import { signal } from '@vielzeug/ripple';\nimport { createConnection } from './_connection';\nimport { createWaitPromise } from './_wait';\nimport { createChannel } from './channel';\nimport { PulseConnectionError, PulseDisposedError, PulseError, PulseProtocolError } from './errors';\nimport { createHeartbeat, type HeartbeatHandle } from './heartbeat';\nimport { type DecodedInFrame, decodeValidated, encode } from './protocol';\nimport { createRoomRegistry, type RoomRegistry } from './room';\nimport type {\n ChannelMap,\n ClientEvents,\n EventKey,\n Pulse,\n PulseEvent,\n PulseOptions,\n PulseSchema,\n PulseStatus,\n RoomMap,\n RoomOptions,\n RoomScope,\n ServerEvents,\n Unsubscribe,\n} from './types';\n\ntype Handler = (payload: unknown) => void;\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 if (!events) {\n events = new Map();\n this.map.set(channel, events);\n }\n\n let handlers = events.get(event);\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 if (handlers.size === 0) events.delete(event);\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<S extends PulseSchema = PulseSchema>(url: string, options: PulseOptions = {}): Pulse<S> {\n const disposalCtrl = new AbortController();\n const status = signal<PulseStatus>('closed');\n const listeners = new ListenerMap();\n const channelReferences = new Map<string, number>();\n const tappers = new Set<(event: PulseEvent) => void>();\n\n let disposed = false;\n let transportClosed = true;\n let lastStatus: PulseStatus = 'closed';\n\n function emitTap(event: PulseEvent): void {\n if (tappers.size === 0) return;\n for (const tapper of tappers) {\n try {\n tapper(event);\n } catch {\n // Observability must not affect pulse behavior.\n }\n }\n }\n\n function report(error: PulseError): void {\n emitTap({ error, type: 'error' });\n }\n\n function sendInternal(frame: string): void {\n connection.send(frame);\n }\n\n const rooms: RoomRegistry = createRoomRegistry({\n disposalSignal: disposalCtrl.signal,\n isOpen: () => connection.open,\n send: sendInternal,\n });\n\n function restoreSession(): void {\n for (const channel of channelReferences.keys()) {\n sendInternal(encode({ channel, type: 'subscribe' }));\n }\n rooms.restore();\n }\n\n function resetRemoteSession(): void {\n rooms.reset();\n }\n\n function handleTransportClose(): void {\n if (transportClosed) return;\n transportClosed = true;\n heartbeat.stop();\n resetRemoteSession();\n rooms.rejectAll(new PulseConnectionError('Connection closed before room confirmation', url));\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 return;\n }\n\n if (decoded.kind === 'unknown') {\n report(new PulseProtocolError(`Unknown frame type \"${decoded.type}\"`, event.data));\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 transportClosed = false;\n try {\n restoreSession();\n heartbeat.start();\n } catch (error) {\n report(error instanceof PulseError ? error : new PulseConnectionError('Session restore failed', url));\n }\n },\n onStatus(nextStatus) {\n status.value = nextStatus;\n if (nextStatus !== lastStatus) {\n lastStatus = nextStatus;\n emitTap({ status: nextStatus, type: 'status-change' });\n }\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 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 break;\n case 'joined':\n rooms.handleJoined(frame.room);\n break;\n case 'left':\n rooms.handleLeft(frame.room);\n break;\n case 'message':\n listeners.dispatch(frame.channel ?? null, frame.event, frame.payload);\n break;\n case 'pong':\n heartbeat.onPong();\n break;\n case 'presence_join':\n rooms.handlePresenceJoin(frame.room, frame.id, frame.state);\n break;\n case 'presence_leave':\n rooms.handlePresenceLeave(frame.room, frame.id);\n break;\n case 'presence_state':\n rooms.handlePresenceState(frame.room, frame.members);\n break;\n case 'subscribed':\n case 'unsubscribed':\n break;\n }\n }\n\n function acquireChannel(name: string): void {\n const references = channelReferences.get(name) ?? 0;\n channelReferences.set(name, references + 1);\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 if (!references) return;\n\n if (references > 1) {\n channelReferences.set(name, references - 1);\n return;\n }\n\n channelReferences.delete(name);\n if (connection.open) sendInternal(encode({ channel: name, type: 'unsubscribe' }));\n }\n\n const pulse: Pulse<S> = {\n channel<K extends keyof ChannelMap<S> & string>(name: K) {\n if (disposed) throw new PulseDisposedError();\n\n acquireChannel(name);\n\n return createChannel(\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 return connection.connect();\n },\n\n disconnect(code = 1000, reason = ''): void {\n if (disposed) return;\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 transportClosed = true;\n rooms.reset();\n rooms.rejectAll(new PulseDisposedError());\n connection.dispose();\n channelReferences.clear();\n rooms.dispose();\n listeners.clear();\n emitTap({ type: 'dispose' });\n tappers.clear();\n disposalCtrl.abort();\n },\n\n get disposed() {\n return disposed;\n },\n\n on<K extends EventKey<ServerEvents<S>>>(event: K, handler: (payload: ServerEvents<S>[K]) => void): Unsubscribe {\n if (disposed) throw new PulseDisposedError();\n return listeners.add(null, event, handler as Handler);\n },\n\n once<K extends EventKey<ServerEvents<S>>>(event: K, handler: (payload: ServerEvents<S>[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 room<K extends keyof RoomMap<S> & string>(name: K, opts?: RoomOptions): RoomScope<RoomMap<S>[K]> {\n if (disposed) throw new PulseDisposedError();\n // Always create with presence machinery — the type-level RoomScope<R> narrows\n // the public surface so plain rooms don't expose presence members.\n return rooms.createScope(name, true, opts) as RoomScope<RoomMap<S>[K]>;\n },\n\n get rooms() {\n return rooms.rooms;\n },\n\n send<K extends EventKey<ClientEvents<S>>>(event: K, payload: ClientEvents<S>[K]): void {\n if (disposed) throw new PulseDisposedError();\n\n const transformed = options.transform ? options.transform({ event, payload }) : { event, payload };\n if (transformed === null) return;\n\n sendInternal(encode({ ...transformed, type: 'message' }));\n },\n\n get status() {\n return status;\n },\n\n tap(handler: (event: PulseEvent) => void, opts?: { signal?: AbortSignal }): Unsubscribe {\n if (disposed) throw new PulseDisposedError();\n tappers.add(handler);\n\n if (opts?.signal) {\n if (opts.signal.aborted) {\n tappers.delete(handler);\n return () => {};\n }\n const onAbort = () => tappers.delete(handler);\n opts.signal.addEventListener('abort', onAbort, { once: true });\n return () => {\n tappers.delete(handler);\n opts.signal?.removeEventListener('abort', onAbort);\n };\n }\n\n return () => tappers.delete(handler);\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n wait<K extends EventKey<ServerEvents<S>>>(\n event: K,\n opts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<ServerEvents<S>[K]> {\n return createWaitPromise<ServerEvents<S>[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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pulse.d.ts","sourceRoot":"","sources":["../src/pulse.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAIV,KAAK,EACL,YAAY,EACZ,WAAW,EAOZ,MAAM,SAAS,CAAC;AAsCjB;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,KAAK,CAAC,CAAC,CAAC,CAmQlH"}
1
+ {"version":3,"file":"pulse.d.ts","sourceRoot":"","sources":["../src/pulse.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAIV,KAAK,EAEL,YAAY,EACZ,WAAW,EAOZ,MAAM,SAAS,CAAC;AAsCjB;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,KAAK,CAAC,CAAC,CAAC,CA0SlH"}