@vielzeug/pulse 1.0.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_dev.cjs +1 -1
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.js +2 -5
- package/dist/_dev.js.map +1 -1
- package/dist/channel.cjs +1 -1
- package/dist/channel.cjs.map +1 -1
- package/dist/channel.js +33 -33
- package/dist/channel.js.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/heartbeat.cjs +1 -1
- package/dist/heartbeat.cjs.map +1 -1
- package/dist/heartbeat.js +17 -17
- package/dist/heartbeat.js.map +1 -1
- package/dist/presence.cjs +1 -1
- package/dist/presence.cjs.map +1 -1
- package/dist/presence.js +39 -39
- package/dist/presence.js.map +1 -1
- package/dist/pulse.cjs +1 -1
- package/dist/pulse.cjs.map +1 -1
- package/dist/pulse.d.ts.map +1 -1
- package/dist/pulse.iife.js +1 -1
- package/dist/pulse.iife.js.map +1 -1
- package/dist/pulse.js +1 -1
- package/dist/pulse.js.map +1 -1
- package/package.json +4 -4
package/dist/_dev.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
function e(e){}exports.warn=e;
|
|
2
2
|
//# sourceMappingURL=_dev.cjs.map
|
package/dist/_dev.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __PULSE_PROD__?: boolean }).__PULSE_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/pulse] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __PULSE_PROD__?: boolean }).__PULSE_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/pulse] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":"AAGA,SAAgB,EAAK,EAAmB,CAExC"}
|
package/dist/_dev.js
CHANGED
package/dist/_dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __PULSE_PROD__?: boolean }).__PULSE_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/pulse] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __PULSE_PROD__?: boolean }).__PULSE_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/pulse] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":";AAGA,SAAgB,EAAK,GAAmB,CAExC"}
|
package/dist/channel.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
require("./_dev.cjs");const e=require("./_utils.cjs"),t=require("./_wait.cjs");function n(n,r,i,a,o){let s=e.deriveAbortController(a),c=new Set,l=s.signal.aborted;return s.signal.addEventListener(`abort`,()=>{l=!0},{once:!0}),{get disposalSignal(){return s.signal},dispose(){if(!l){l=!0,s.abort();for(let e of c)e();c.clear(),o?.()}},get disposed(){return l},get name(){return n},on(e,t){if(l)return`${String(e)}${n}`,()=>{};let r=i(n,e,t);return c.add(r),()=>{r(),c.delete(r)}},once(e,t){if(l)return`${String(e)}${n}`,()=>{};let r=()=>{};return r=i(n,e,e=>{c.delete(r),r(),t(e)}),c.add(r),()=>{c.delete(r),r()}},send(e,t){if(l){`${String(e)}${n}`;return}r(n,e,t)},[Symbol.dispose](){this.dispose()},wait(e,r){return t.createWaitPromise(e,s.signal,r,(e,t)=>i(n,e,t))}}}exports.createChannel=n;
|
|
2
2
|
//# sourceMappingURL=channel.cjs.map
|
package/dist/channel.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.cjs","names":[],"sources":["../src/channel.ts"],"sourcesContent":["import type { EventKey, MessageMap, PulseChannel, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { deriveAbortController } from './_utils';\nimport { createWaitPromise } from './_wait';\n\ntype SendFn = (channel: string, event: string, payload: unknown) => void;\ntype SubscribeFn = (channel: string, event: string, handler: (payload: unknown) => void) => () => void;\n\n/**\n * Create a typed channel object that scopes messaging to a named namespace.\n * @internal\n */\nexport function createChannel<TServer extends MessageMap, TClient extends MessageMap>(\n name: string,\n send: SendFn,\n subscribe: SubscribeFn,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PulseChannel<TServer, TClient> {\n const ctrl = deriveAbortController(disposalSignal);\n const tracked = new Set<() => void>();\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const channel: PulseChannel<TServer, TClient> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n ctrl.abort();\n\n for (const unsub of tracked) unsub();\n\n tracked.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n get name() {\n return name;\n },\n\n on<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`on('${String(event)}') called on a disposed channel '${name}' — listener ignored`);\n\n return () => {};\n }\n\n const unsub = subscribe(name, event, handler as (payload: unknown) => void);\n\n tracked.add(unsub);\n\n return () => {\n unsub();\n tracked.delete(unsub);\n };\n },\n\n once<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`once('${String(event)}') called on a disposed channel '${name}' — listener ignored`);\n\n return () => {};\n }\n\n let unsub: Unsubscribe = () => {};\n\n const wrapped = (payload: unknown): void => {\n tracked.delete(unsub);\n unsub();\n (handler as (payload: unknown) => void)(payload);\n };\n\n unsub = subscribe(name, event, wrapped);\n tracked.add(unsub);\n\n return () => {\n tracked.delete(unsub);\n unsub();\n };\n },\n\n send<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void {\n if (disposed) {\n warn(`send('${String(event)}') called on a disposed channel '${name}' — message dropped`);\n\n return;\n }\n\n send(name, event, payload);\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n wait<K extends EventKey<TServer>>(\n event: K,\n opts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<TServer[K]> {\n return createWaitPromise<TServer[K]>(event as string, ctrl.signal, opts, (ev, handler) =>\n subscribe(name, ev, handler),\n );\n },\n };\n\n return channel;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"channel.cjs","names":[],"sources":["../src/channel.ts"],"sourcesContent":["import type { EventKey, MessageMap, PulseChannel, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { deriveAbortController } from './_utils';\nimport { createWaitPromise } from './_wait';\n\ntype SendFn = (channel: string, event: string, payload: unknown) => void;\ntype SubscribeFn = (channel: string, event: string, handler: (payload: unknown) => void) => () => void;\n\n/**\n * Create a typed channel object that scopes messaging to a named namespace.\n * @internal\n */\nexport function createChannel<TServer extends MessageMap, TClient extends MessageMap>(\n name: string,\n send: SendFn,\n subscribe: SubscribeFn,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PulseChannel<TServer, TClient> {\n const ctrl = deriveAbortController(disposalSignal);\n const tracked = new Set<() => void>();\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const channel: PulseChannel<TServer, TClient> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n ctrl.abort();\n\n for (const unsub of tracked) unsub();\n\n tracked.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n get name() {\n return name;\n },\n\n on<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`on('${String(event)}') called on a disposed channel '${name}' — listener ignored`);\n\n return () => {};\n }\n\n const unsub = subscribe(name, event, handler as (payload: unknown) => void);\n\n tracked.add(unsub);\n\n return () => {\n unsub();\n tracked.delete(unsub);\n };\n },\n\n once<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`once('${String(event)}') called on a disposed channel '${name}' — listener ignored`);\n\n return () => {};\n }\n\n let unsub: Unsubscribe = () => {};\n\n const wrapped = (payload: unknown): void => {\n tracked.delete(unsub);\n unsub();\n (handler as (payload: unknown) => void)(payload);\n };\n\n unsub = subscribe(name, event, wrapped);\n tracked.add(unsub);\n\n return () => {\n tracked.delete(unsub);\n unsub();\n };\n },\n\n send<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void {\n if (disposed) {\n warn(`send('${String(event)}') called on a disposed channel '${name}' — message dropped`);\n\n return;\n }\n\n send(name, event, payload);\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n wait<K extends EventKey<TServer>>(\n event: K,\n opts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<TServer[K]> {\n return createWaitPromise<TServer[K]>(event as string, ctrl.signal, opts, (ev, handler) =>\n subscribe(name, ev, handler),\n );\n },\n };\n\n return channel;\n}\n"],"mappings":"+EAaA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACgC,CAChC,IAAM,EAAO,EAAA,sBAAsB,CAAc,EAC3C,EAAU,IAAI,IAEhB,EAAW,EAAK,OAAO,QAoG3B,OAlGA,EAAK,OAAO,iBACV,YACM,CACJ,EAAW,EACb,EACA,CAAE,KAAM,EAAK,CACf,EA4FO,CAzFL,IAAI,gBAAiB,CACnB,OAAO,EAAK,MACd,EAEA,SAAU,CACJ,MAGJ,CADA,EAAW,GACX,EAAK,MAAM,EAEX,IAAK,IAAM,KAAS,EAAS,EAAM,EAEnC,EAAQ,MAAM,EACd,IAAY,CALD,CAMb,EAEA,IAAI,UAAW,CACb,OAAO,CACT,EAEA,IAAI,MAAO,CACT,OAAO,CACT,EAEA,GAAgC,EAAU,EAAqD,CAC7F,GAAI,EAGF,MAFK,GAAO,OAAO,CAAK,EAAnB,EAAwD,EAAxD,MAEQ,CAAC,EAGhB,IAAM,EAAQ,EAAU,EAAM,EAAO,CAAqC,EAI1E,OAFA,EAAQ,IAAI,CAAK,MAEJ,CACX,EAAM,EACN,EAAQ,OAAO,CAAK,CACtB,CACF,EAEA,KAAkC,EAAU,EAAqD,CAC/F,GAAI,EAGF,MAFK,GAAS,OAAO,CAAK,EAArB,EAA0D,EAA1D,MAEQ,CAAC,EAGhB,IAAI,MAA2B,CAAC,EAWhC,MAHA,GAAQ,EAAU,EAAM,EANP,GAA2B,CAC1C,EAAQ,OAAO,CAAK,EACpB,EAAM,EACN,EAAwC,CAAO,CACjD,CAEsC,EACtC,EAAQ,IAAI,CAAK,MAEJ,CACX,EAAQ,OAAO,CAAK,EACpB,EAAM,CACR,CACF,EAEA,KAAkC,EAAU,EAA2B,CACrE,GAAI,EAAU,CACP,GAAS,OAAO,CAAK,EAArB,EAA0D,EAA1D,EAEL,MACF,CAEA,EAAK,EAAM,EAAO,CAAO,CAC3B,EAEA,CAAC,OAAO,UAAW,CACjB,KAAK,QAAQ,CACf,EAEA,KACE,EACA,EACqB,CACrB,OAAO,EAAA,kBAA8B,EAAiB,EAAK,OAAQ,GAAO,EAAI,IAC5E,EAAU,EAAM,EAAI,CAAO,CAC7B,CACF,CAGK,CACT"}
|
package/dist/channel.js
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { deriveAbortController as
|
|
3
|
-
import { createWaitPromise as
|
|
1
|
+
import "./_dev.js";
|
|
2
|
+
import { deriveAbortController as e } from "./_utils.js";
|
|
3
|
+
import { createWaitPromise as t } from "./_wait.js";
|
|
4
4
|
//#region src/channel.ts
|
|
5
|
-
function
|
|
6
|
-
let
|
|
7
|
-
return
|
|
8
|
-
|
|
5
|
+
function n(n, r, i, a, o) {
|
|
6
|
+
let s = e(a), c = /* @__PURE__ */ new Set(), l = s.signal.aborted;
|
|
7
|
+
return s.signal.addEventListener("abort", () => {
|
|
8
|
+
l = !0;
|
|
9
9
|
}, { once: !0 }), {
|
|
10
10
|
get disposalSignal() {
|
|
11
|
-
return
|
|
11
|
+
return s.signal;
|
|
12
12
|
},
|
|
13
13
|
dispose() {
|
|
14
|
-
if (!
|
|
15
|
-
|
|
16
|
-
for (let e of
|
|
17
|
-
|
|
14
|
+
if (!l) {
|
|
15
|
+
l = !0, s.abort();
|
|
16
|
+
for (let e of c) e();
|
|
17
|
+
c.clear(), o?.();
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
get disposed() {
|
|
21
|
-
return
|
|
21
|
+
return l;
|
|
22
22
|
},
|
|
23
23
|
get name() {
|
|
24
|
-
return
|
|
24
|
+
return n;
|
|
25
25
|
},
|
|
26
|
-
on(
|
|
27
|
-
if (
|
|
28
|
-
let
|
|
29
|
-
return
|
|
30
|
-
|
|
26
|
+
on(e, t) {
|
|
27
|
+
if (l) return `${String(e)}${n}`, () => {};
|
|
28
|
+
let r = i(n, e, t);
|
|
29
|
+
return c.add(r), () => {
|
|
30
|
+
r(), c.delete(r);
|
|
31
31
|
};
|
|
32
32
|
},
|
|
33
|
-
once(
|
|
34
|
-
if (
|
|
35
|
-
let
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
}),
|
|
39
|
-
|
|
33
|
+
once(e, t) {
|
|
34
|
+
if (l) return `${String(e)}${n}`, () => {};
|
|
35
|
+
let r = () => {};
|
|
36
|
+
return r = i(n, e, (e) => {
|
|
37
|
+
c.delete(r), r(), t(e);
|
|
38
|
+
}), c.add(r), () => {
|
|
39
|
+
c.delete(r), r();
|
|
40
40
|
};
|
|
41
41
|
},
|
|
42
|
-
send(
|
|
43
|
-
if (
|
|
44
|
-
|
|
42
|
+
send(e, t) {
|
|
43
|
+
if (l) {
|
|
44
|
+
`${String(e)}${n}`;
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
r(n, e, t);
|
|
48
48
|
},
|
|
49
49
|
[Symbol.dispose]() {
|
|
50
50
|
this.dispose();
|
|
51
51
|
},
|
|
52
|
-
wait(e,
|
|
53
|
-
return
|
|
52
|
+
wait(e, r) {
|
|
53
|
+
return t(e, s.signal, r, (e, t) => i(n, e, t));
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
//#endregion
|
|
58
|
-
export {
|
|
58
|
+
export { n as createChannel };
|
|
59
59
|
|
|
60
60
|
//# sourceMappingURL=channel.js.map
|
package/dist/channel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.js","names":[],"sources":["../src/channel.ts"],"sourcesContent":["import type { EventKey, MessageMap, PulseChannel, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { deriveAbortController } from './_utils';\nimport { createWaitPromise } from './_wait';\n\ntype SendFn = (channel: string, event: string, payload: unknown) => void;\ntype SubscribeFn = (channel: string, event: string, handler: (payload: unknown) => void) => () => void;\n\n/**\n * Create a typed channel object that scopes messaging to a named namespace.\n * @internal\n */\nexport function createChannel<TServer extends MessageMap, TClient extends MessageMap>(\n name: string,\n send: SendFn,\n subscribe: SubscribeFn,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PulseChannel<TServer, TClient> {\n const ctrl = deriveAbortController(disposalSignal);\n const tracked = new Set<() => void>();\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const channel: PulseChannel<TServer, TClient> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n ctrl.abort();\n\n for (const unsub of tracked) unsub();\n\n tracked.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n get name() {\n return name;\n },\n\n on<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`on('${String(event)}') called on a disposed channel '${name}' — listener ignored`);\n\n return () => {};\n }\n\n const unsub = subscribe(name, event, handler as (payload: unknown) => void);\n\n tracked.add(unsub);\n\n return () => {\n unsub();\n tracked.delete(unsub);\n };\n },\n\n once<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`once('${String(event)}') called on a disposed channel '${name}' — listener ignored`);\n\n return () => {};\n }\n\n let unsub: Unsubscribe = () => {};\n\n const wrapped = (payload: unknown): void => {\n tracked.delete(unsub);\n unsub();\n (handler as (payload: unknown) => void)(payload);\n };\n\n unsub = subscribe(name, event, wrapped);\n tracked.add(unsub);\n\n return () => {\n tracked.delete(unsub);\n unsub();\n };\n },\n\n send<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void {\n if (disposed) {\n warn(`send('${String(event)}') called on a disposed channel '${name}' — message dropped`);\n\n return;\n }\n\n send(name, event, payload);\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n wait<K extends EventKey<TServer>>(\n event: K,\n opts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<TServer[K]> {\n return createWaitPromise<TServer[K]>(event as string, ctrl.signal, opts, (ev, handler) =>\n subscribe(name, ev, handler),\n );\n },\n };\n\n return channel;\n}\n"],"mappings":";;;;AAaA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACgC;CAChC,IAAM,IAAO,EAAsB,CAAc,GAC3C,oBAAU,IAAI,IAAgB,GAEhC,IAAW,EAAK,OAAO;CAoG3B,OAlGA,EAAK,OAAO,iBACV,eACM;EACJ,IAAW;CACb,GACA,EAAE,MAAM,GAAK,CACf,GA4FO;EAzFL,IAAI,iBAAiB;GACnB,OAAO,EAAK;EACd;EAEA,UAAU;GACJ,QAGJ;IADA,IAAW,IACX,EAAK,MAAM;IAEX,KAAK,IAAM,KAAS,GAAS,EAAM;IAGnC,AADA,EAAQ,MAAM,GACd,IAAY;GALD;EAMb;EAEA,IAAI,WAAW;GACb,OAAO;EACT;EAEA,IAAI,OAAO;GACT,OAAO;EACT;EAEA,GAAgC,GAAU,GAAqD;GAC7F,IAAI,GAGF,
|
|
1
|
+
{"version":3,"file":"channel.js","names":[],"sources":["../src/channel.ts"],"sourcesContent":["import type { EventKey, MessageMap, PulseChannel, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { deriveAbortController } from './_utils';\nimport { createWaitPromise } from './_wait';\n\ntype SendFn = (channel: string, event: string, payload: unknown) => void;\ntype SubscribeFn = (channel: string, event: string, handler: (payload: unknown) => void) => () => void;\n\n/**\n * Create a typed channel object that scopes messaging to a named namespace.\n * @internal\n */\nexport function createChannel<TServer extends MessageMap, TClient extends MessageMap>(\n name: string,\n send: SendFn,\n subscribe: SubscribeFn,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PulseChannel<TServer, TClient> {\n const ctrl = deriveAbortController(disposalSignal);\n const tracked = new Set<() => void>();\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const channel: PulseChannel<TServer, TClient> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n ctrl.abort();\n\n for (const unsub of tracked) unsub();\n\n tracked.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n get name() {\n return name;\n },\n\n on<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`on('${String(event)}') called on a disposed channel '${name}' — listener ignored`);\n\n return () => {};\n }\n\n const unsub = subscribe(name, event, handler as (payload: unknown) => void);\n\n tracked.add(unsub);\n\n return () => {\n unsub();\n tracked.delete(unsub);\n };\n },\n\n once<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe {\n if (disposed) {\n warn(`once('${String(event)}') called on a disposed channel '${name}' — listener ignored`);\n\n return () => {};\n }\n\n let unsub: Unsubscribe = () => {};\n\n const wrapped = (payload: unknown): void => {\n tracked.delete(unsub);\n unsub();\n (handler as (payload: unknown) => void)(payload);\n };\n\n unsub = subscribe(name, event, wrapped);\n tracked.add(unsub);\n\n return () => {\n tracked.delete(unsub);\n unsub();\n };\n },\n\n send<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void {\n if (disposed) {\n warn(`send('${String(event)}') called on a disposed channel '${name}' — message dropped`);\n\n return;\n }\n\n send(name, event, payload);\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n wait<K extends EventKey<TServer>>(\n event: K,\n opts?: { signal?: AbortSignal; timeout?: number },\n ): Promise<TServer[K]> {\n return createWaitPromise<TServer[K]>(event as string, ctrl.signal, opts, (ev, handler) =>\n subscribe(name, ev, handler),\n );\n },\n };\n\n return channel;\n}\n"],"mappings":";;;;AAaA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACgC;CAChC,IAAM,IAAO,EAAsB,CAAc,GAC3C,oBAAU,IAAI,IAAgB,GAEhC,IAAW,EAAK,OAAO;CAoG3B,OAlGA,EAAK,OAAO,iBACV,eACM;EACJ,IAAW;CACb,GACA,EAAE,MAAM,GAAK,CACf,GA4FO;EAzFL,IAAI,iBAAiB;GACnB,OAAO,EAAK;EACd;EAEA,UAAU;GACJ,QAGJ;IADA,IAAW,IACX,EAAK,MAAM;IAEX,KAAK,IAAM,KAAS,GAAS,EAAM;IAGnC,AADA,EAAQ,MAAM,GACd,IAAY;GALD;EAMb;EAEA,IAAI,WAAW;GACb,OAAO;EACT;EAEA,IAAI,OAAO;GACT,OAAO;EACT;EAEA,GAAgC,GAAU,GAAqD;GAC7F,IAAI,GAGF,OAFK,GAAO,OAAO,CAAK,EAAnB,EAAwD,EAAxD,SAEQ,CAAC;GAGhB,IAAM,IAAQ,EAAU,GAAM,GAAO,CAAqC;GAI1E,OAFA,EAAQ,IAAI,CAAK,SAEJ;IAEX,AADA,EAAM,GACN,EAAQ,OAAO,CAAK;GACtB;EACF;EAEA,KAAkC,GAAU,GAAqD;GAC/F,IAAI,GAGF,OAFK,GAAS,OAAO,CAAK,EAArB,EAA0D,EAA1D,SAEQ,CAAC;GAGhB,IAAI,UAA2B,CAAC;GAWhC,OAHA,IAAQ,EAAU,GAAM,IANP,MAA2B;IAG1C,AAFA,EAAQ,OAAO,CAAK,GACpB,EAAM,GACN,EAAwC,CAAO;GACjD,CAEsC,GACtC,EAAQ,IAAI,CAAK,SAEJ;IAEX,AADA,EAAQ,OAAO,CAAK,GACpB,EAAM;GACR;EACF;EAEA,KAAkC,GAAU,GAA2B;GACrE,IAAI,GAAU;IACZ,AAAK,GAAS,OAAO,CAAK,EAArB,EAA0D,EAA1D;IAEL;GACF;GAEA,EAAK,GAAM,GAAO,CAAO;EAC3B;EAEA,CAAC,OAAO,WAAW;GACjB,KAAK,QAAQ;EACf;EAEA,KACE,GACA,GACqB;GACrB,OAAO,EAA8B,GAAiB,EAAK,QAAQ,IAAO,GAAI,MAC5E,EAAU,GAAM,GAAI,CAAO,CAC7B;EACF;CAGK;AACT"}
|
package/dist/errors.cjs.map
CHANGED
|
@@ -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()` or `join()`/`leave()` times out before receiving a\n * server response, or when an AbortSignal fires before the event arrives.\n */\nexport class PulseTimeoutError extends PulseError {\n readonly event: string;\n\n constructor(event: string, opts?: ErrorOptions) {\n super(`Timed out waiting for \"${event}\"`, opts);\n this.event = event;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires.\n */\nexport class PulseAbortError extends PulseError {\n constructor(opts?: ErrorOptions) {\n super('Aborted', opts);\n }\n}\n\n/**\n * Thrown when a method is called on a disposed Pulse instance or channel.\n */\nexport class PulseDisposedError extends PulseError {\n constructor(target = 'Pulse', opts?: ErrorOptions) {\n super(`${target} instance is disposed`, opts);\n }\n}\n\n/**\n * Thrown when the server sends a frame that cannot be parsed or violates\n * the wire protocol schema.\n */\nexport class PulseProtocolError extends PulseError {\n readonly raw: unknown;\n\n constructor(message: string, raw?: unknown, opts?: ErrorOptions) {\n super(message, opts);\n this.raw = raw;\n }\n}\n"],"mappings":"AAIA,IAAa,EAAb,MAAa,UAAmB,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,
|
|
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()` or `join()`/`leave()` times out before receiving a\n * server response, or when an AbortSignal fires before the event arrives.\n */\nexport class PulseTimeoutError extends PulseError {\n readonly event: string;\n\n constructor(event: string, opts?: ErrorOptions) {\n super(`Timed out waiting for \"${event}\"`, opts);\n this.event = event;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires.\n */\nexport class PulseAbortError extends PulseError {\n constructor(opts?: ErrorOptions) {\n super('Aborted', opts);\n }\n}\n\n/**\n * Thrown when a method is called on a disposed Pulse instance or channel.\n */\nexport class PulseDisposedError extends PulseError {\n constructor(target = 'Pulse', opts?: ErrorOptions) {\n super(`${target} instance is disposed`, opts);\n }\n}\n\n/**\n * Thrown when the server sends a frame that cannot be parsed or violates\n * the wire protocol schema.\n */\nexport class PulseProtocolError extends PulseError {\n readonly raw: unknown;\n\n constructor(message: string, raw?: unknown, opts?: ErrorOptions) {\n super(message, opts);\n this.raw = raw;\n }\n}\n"],"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,EAMa,EAAb,cAAuC,CAAW,CAChD,MAEA,YAAY,EAAe,EAAqB,CAC9C,MAAM,0BAA0B,EAAM,GAAI,CAAI,EAC9C,KAAK,MAAQ,CACf,CACF,EAKa,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.js.map
CHANGED
|
@@ -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()` or `join()`/`leave()` times out before receiving a\n * server response, or when an AbortSignal fires before the event arrives.\n */\nexport class PulseTimeoutError extends PulseError {\n readonly event: string;\n\n constructor(event: string, opts?: ErrorOptions) {\n super(`Timed out waiting for \"${event}\"`, opts);\n this.event = event;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires.\n */\nexport class PulseAbortError extends PulseError {\n constructor(opts?: ErrorOptions) {\n super('Aborted', opts);\n }\n}\n\n/**\n * Thrown when a method is called on a disposed Pulse instance or channel.\n */\nexport class PulseDisposedError extends PulseError {\n constructor(target = 'Pulse', opts?: ErrorOptions) {\n super(`${target} instance is disposed`, opts);\n }\n}\n\n/**\n * Thrown when the server sends a frame that cannot be parsed or violates\n * the wire protocol schema.\n */\nexport class PulseProtocolError extends PulseError {\n readonly raw: unknown;\n\n constructor(message: string, raw?: unknown, opts?: ErrorOptions) {\n super(message, opts);\n this.raw = raw;\n }\n}\n"],"mappings":";AAIA,IAAa,IAAb,MAAa,UAAmB,MAAM;CACpC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,
|
|
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()` or `join()`/`leave()` times out before receiving a\n * server response, or when an AbortSignal fires before the event arrives.\n */\nexport class PulseTimeoutError extends PulseError {\n readonly event: string;\n\n constructor(event: string, opts?: ErrorOptions) {\n super(`Timed out waiting for \"${event}\"`, opts);\n this.event = event;\n }\n}\n\n/**\n * Thrown when `wait()` is aborted via an AbortSignal before the event fires.\n */\nexport class PulseAbortError extends PulseError {\n constructor(opts?: ErrorOptions) {\n super('Aborted', opts);\n }\n}\n\n/**\n * Thrown when a method is called on a disposed Pulse instance or channel.\n */\nexport class PulseDisposedError extends PulseError {\n constructor(target = 'Pulse', opts?: ErrorOptions) {\n super(`${target} instance is disposed`, opts);\n }\n}\n\n/**\n * Thrown when the server sends a frame that cannot be parsed or violates\n * the wire protocol schema.\n */\nexport class PulseProtocolError extends PulseError {\n readonly raw: unknown;\n\n constructor(message: string, raw?: unknown, opts?: ErrorOptions) {\n super(message, opts);\n this.raw = raw;\n }\n}\n"],"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,GAMa,IAAb,cAAuC,EAAW;CAChD;CAEA,YAAY,GAAe,GAAqB;EAE9C,AADA,MAAM,0BAA0B,EAAM,IAAI,CAAI,GAC9C,KAAK,QAAQ;CACf;AACF,GAKa,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/heartbeat.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
require("./_dev.cjs");const e=require("./protocol.cjs");var t=3e4,n=5e3;function r(r,i,a){if(!r)return{onPong(){},start(){},stop(){}};let o=r===!0?{}:r,s=o.interval??t,c=o.timeout??n,l,u,d=!1;function f(){l=setTimeout(()=>{i(e.encode({ts:Date.now(),type:`ping`})),u=setTimeout(()=>{`${c}`,a()},c)},s)}return{onPong(){clearTimeout(u),u=void 0,d&&f()},start(){d=!0,clearTimeout(l),clearTimeout(u),u=void 0,f()},stop(){d=!1,clearTimeout(l),clearTimeout(u),l=void 0,u=void 0}}}exports.createHeartbeat=r;
|
|
2
2
|
//# sourceMappingURL=heartbeat.cjs.map
|
package/dist/heartbeat.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heartbeat.cjs","names":[],"sources":["../src/heartbeat.ts"],"sourcesContent":["import type { HeartbeatOptions } from './types';\n\nimport { warn } from './_dev';\nimport { encode } from './protocol';\n\nconst DEFAULT_INTERVAL = 30_000;\nconst DEFAULT_TIMEOUT = 5_000;\n\nexport type HeartbeatHandle = {\n /** Called when a `pong` frame is received. Resets the timeout timer. */\n onPong(): void;\n /** Start the heartbeat loop. */\n start(): void;\n /** Stop the heartbeat loop and cancel any pending timers. */\n stop(): void;\n};\n\n/**\n * Create a heartbeat manager that sends periodic pings and expects pong replies.\n * Calls `onDead` when a pong is not received within the timeout window.\n * @internal\n */\nexport function createHeartbeat(\n opts: boolean | HeartbeatOptions | undefined,\n send: (frame: string) => void,\n onDead: () => void,\n): HeartbeatHandle {\n if (!opts) {\n return { onPong() {}, start() {}, stop() {} };\n }\n\n const options: HeartbeatOptions = opts === true ? {} : opts;\n const interval = options.interval ?? DEFAULT_INTERVAL;\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n\n let pingTimer: ReturnType<typeof setTimeout> | undefined;\n let pongTimer: ReturnType<typeof setTimeout> | undefined;\n let running = false;\n\n function schedulePing(): void {\n pingTimer = setTimeout(() => {\n const ts = Date.now();\n\n send(encode({ ts, type: 'ping' }));\n\n pongTimer = setTimeout(() => {\n warn(`Heartbeat timed out after ${timeout}ms — treating connection as dead`);\n onDead();\n }, timeout);\n }, interval);\n }\n\n return {\n onPong() {\n clearTimeout(pongTimer);\n pongTimer = undefined;\n\n if (running) schedulePing();\n },\n\n start() {\n running = true;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pongTimer = undefined;\n schedulePing();\n },\n\n stop() {\n running = false;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pingTimer = undefined;\n pongTimer = undefined;\n },\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"heartbeat.cjs","names":[],"sources":["../src/heartbeat.ts"],"sourcesContent":["import type { HeartbeatOptions } from './types';\n\nimport { warn } from './_dev';\nimport { encode } from './protocol';\n\nconst DEFAULT_INTERVAL = 30_000;\nconst DEFAULT_TIMEOUT = 5_000;\n\nexport type HeartbeatHandle = {\n /** Called when a `pong` frame is received. Resets the timeout timer. */\n onPong(): void;\n /** Start the heartbeat loop. */\n start(): void;\n /** Stop the heartbeat loop and cancel any pending timers. */\n stop(): void;\n};\n\n/**\n * Create a heartbeat manager that sends periodic pings and expects pong replies.\n * Calls `onDead` when a pong is not received within the timeout window.\n * @internal\n */\nexport function createHeartbeat(\n opts: boolean | HeartbeatOptions | undefined,\n send: (frame: string) => void,\n onDead: () => void,\n): HeartbeatHandle {\n if (!opts) {\n return { onPong() {}, start() {}, stop() {} };\n }\n\n const options: HeartbeatOptions = opts === true ? {} : opts;\n const interval = options.interval ?? DEFAULT_INTERVAL;\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n\n let pingTimer: ReturnType<typeof setTimeout> | undefined;\n let pongTimer: ReturnType<typeof setTimeout> | undefined;\n let running = false;\n\n function schedulePing(): void {\n pingTimer = setTimeout(() => {\n const ts = Date.now();\n\n send(encode({ ts, type: 'ping' }));\n\n pongTimer = setTimeout(() => {\n warn(`Heartbeat timed out after ${timeout}ms — treating connection as dead`);\n onDead();\n }, timeout);\n }, interval);\n }\n\n return {\n onPong() {\n clearTimeout(pongTimer);\n pongTimer = undefined;\n\n if (running) schedulePing();\n },\n\n start() {\n running = true;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pongTimer = undefined;\n schedulePing();\n },\n\n stop() {\n running = false;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pingTimer = undefined;\n pongTimer = undefined;\n },\n };\n}\n"],"mappings":"wDAKA,IAAM,EAAmB,IACnB,EAAkB,IAgBxB,SAAgB,EACd,EACA,EACA,EACiB,CACjB,GAAI,CAAC,EACH,MAAO,CAAE,QAAS,CAAC,EAAG,OAAQ,CAAC,EAAG,MAAO,CAAC,CAAE,EAG9C,IAAM,EAA4B,IAAS,GAAO,CAAC,EAAI,EACjD,EAAW,EAAQ,UAAY,EAC/B,EAAU,EAAQ,SAAW,EAE/B,EACA,EACA,EAAU,GAEd,SAAS,GAAqB,CAC5B,EAAY,eAAiB,CAG3B,EAAK,EAAA,OAAO,CAAE,GAFH,KAAK,IAEF,EAAI,KAAM,MAAO,CAAC,CAAC,EAEjC,EAAY,eAAiB,CACtB,GAA6B,EAA7B,EACL,EAAO,CACT,EAAG,CAAO,CACZ,EAAG,CAAQ,CACb,CAEA,MAAO,CACL,QAAS,CACP,aAAa,CAAS,EACtB,EAAY,IAAA,GAER,GAAS,EAAa,CAC5B,EAEA,OAAQ,CACN,EAAU,GACV,aAAa,CAAS,EACtB,aAAa,CAAS,EACtB,EAAY,IAAA,GACZ,EAAa,CACf,EAEA,MAAO,CACL,EAAU,GACV,aAAa,CAAS,EACtB,aAAa,CAAS,EACtB,EAAY,IAAA,GACZ,EAAY,IAAA,EACd,CACF,CACF"}
|
package/dist/heartbeat.js
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { encode as
|
|
1
|
+
import "./_dev.js";
|
|
2
|
+
import { encode as e } from "./protocol.js";
|
|
3
3
|
//#region src/heartbeat.ts
|
|
4
|
-
var
|
|
5
|
-
function
|
|
6
|
-
if (!
|
|
4
|
+
var t = 3e4, n = 5e3;
|
|
5
|
+
function r(r, i, a) {
|
|
6
|
+
if (!r) return {
|
|
7
7
|
onPong() {},
|
|
8
8
|
start() {},
|
|
9
9
|
stop() {}
|
|
10
10
|
};
|
|
11
|
-
let
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
let o = r === !0 ? {} : r, s = o.interval ?? t, c = o.timeout ?? n, l, u, d = !1;
|
|
12
|
+
function f() {
|
|
13
|
+
l = setTimeout(() => {
|
|
14
|
+
i(e({
|
|
15
15
|
ts: Date.now(),
|
|
16
16
|
type: "ping"
|
|
17
|
-
})),
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
},
|
|
17
|
+
})), u = setTimeout(() => {
|
|
18
|
+
`${c}`, a();
|
|
19
|
+
}, c);
|
|
20
|
+
}, s);
|
|
21
21
|
}
|
|
22
22
|
return {
|
|
23
23
|
onPong() {
|
|
24
|
-
clearTimeout(
|
|
24
|
+
clearTimeout(u), u = void 0, d && f();
|
|
25
25
|
},
|
|
26
26
|
start() {
|
|
27
|
-
|
|
27
|
+
d = !0, clearTimeout(l), clearTimeout(u), u = void 0, f();
|
|
28
28
|
},
|
|
29
29
|
stop() {
|
|
30
|
-
|
|
30
|
+
d = !1, clearTimeout(l), clearTimeout(u), l = void 0, u = void 0;
|
|
31
31
|
}
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
//#endregion
|
|
35
|
-
export {
|
|
35
|
+
export { r as createHeartbeat };
|
|
36
36
|
|
|
37
37
|
//# sourceMappingURL=heartbeat.js.map
|
package/dist/heartbeat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heartbeat.js","names":[],"sources":["../src/heartbeat.ts"],"sourcesContent":["import type { HeartbeatOptions } from './types';\n\nimport { warn } from './_dev';\nimport { encode } from './protocol';\n\nconst DEFAULT_INTERVAL = 30_000;\nconst DEFAULT_TIMEOUT = 5_000;\n\nexport type HeartbeatHandle = {\n /** Called when a `pong` frame is received. Resets the timeout timer. */\n onPong(): void;\n /** Start the heartbeat loop. */\n start(): void;\n /** Stop the heartbeat loop and cancel any pending timers. */\n stop(): void;\n};\n\n/**\n * Create a heartbeat manager that sends periodic pings and expects pong replies.\n * Calls `onDead` when a pong is not received within the timeout window.\n * @internal\n */\nexport function createHeartbeat(\n opts: boolean | HeartbeatOptions | undefined,\n send: (frame: string) => void,\n onDead: () => void,\n): HeartbeatHandle {\n if (!opts) {\n return { onPong() {}, start() {}, stop() {} };\n }\n\n const options: HeartbeatOptions = opts === true ? {} : opts;\n const interval = options.interval ?? DEFAULT_INTERVAL;\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n\n let pingTimer: ReturnType<typeof setTimeout> | undefined;\n let pongTimer: ReturnType<typeof setTimeout> | undefined;\n let running = false;\n\n function schedulePing(): void {\n pingTimer = setTimeout(() => {\n const ts = Date.now();\n\n send(encode({ ts, type: 'ping' }));\n\n pongTimer = setTimeout(() => {\n warn(`Heartbeat timed out after ${timeout}ms — treating connection as dead`);\n onDead();\n }, timeout);\n }, interval);\n }\n\n return {\n onPong() {\n clearTimeout(pongTimer);\n pongTimer = undefined;\n\n if (running) schedulePing();\n },\n\n start() {\n running = true;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pongTimer = undefined;\n schedulePing();\n },\n\n stop() {\n running = false;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pingTimer = undefined;\n pongTimer = undefined;\n },\n };\n}\n"],"mappings":";;;AAKA,IAAM,IAAmB,KACnB,IAAkB;AAgBxB,SAAgB,EACd,GACA,GACA,GACiB;CACjB,IAAI,CAAC,GACH,OAAO;EAAE,SAAS,CAAC;EAAG,QAAQ,CAAC;EAAG,OAAO,CAAC;CAAE;CAG9C,IAAM,IAA4B,MAAS,KAAO,CAAC,IAAI,GACjD,IAAW,EAAQ,YAAY,GAC/B,IAAU,EAAQ,WAAW,GAE/B,GACA,GACA,IAAU;CAEd,SAAS,IAAqB;EAC5B,IAAY,iBAAiB;GAK3B,AAFA,EAAK,EAAO;IAAE,IAFH,KAAK,IAEF;IAAI,MAAM;GAAO,CAAC,CAAC,GAEjC,IAAY,iBAAiB;IAE3B,
|
|
1
|
+
{"version":3,"file":"heartbeat.js","names":[],"sources":["../src/heartbeat.ts"],"sourcesContent":["import type { HeartbeatOptions } from './types';\n\nimport { warn } from './_dev';\nimport { encode } from './protocol';\n\nconst DEFAULT_INTERVAL = 30_000;\nconst DEFAULT_TIMEOUT = 5_000;\n\nexport type HeartbeatHandle = {\n /** Called when a `pong` frame is received. Resets the timeout timer. */\n onPong(): void;\n /** Start the heartbeat loop. */\n start(): void;\n /** Stop the heartbeat loop and cancel any pending timers. */\n stop(): void;\n};\n\n/**\n * Create a heartbeat manager that sends periodic pings and expects pong replies.\n * Calls `onDead` when a pong is not received within the timeout window.\n * @internal\n */\nexport function createHeartbeat(\n opts: boolean | HeartbeatOptions | undefined,\n send: (frame: string) => void,\n onDead: () => void,\n): HeartbeatHandle {\n if (!opts) {\n return { onPong() {}, start() {}, stop() {} };\n }\n\n const options: HeartbeatOptions = opts === true ? {} : opts;\n const interval = options.interval ?? DEFAULT_INTERVAL;\n const timeout = options.timeout ?? DEFAULT_TIMEOUT;\n\n let pingTimer: ReturnType<typeof setTimeout> | undefined;\n let pongTimer: ReturnType<typeof setTimeout> | undefined;\n let running = false;\n\n function schedulePing(): void {\n pingTimer = setTimeout(() => {\n const ts = Date.now();\n\n send(encode({ ts, type: 'ping' }));\n\n pongTimer = setTimeout(() => {\n warn(`Heartbeat timed out after ${timeout}ms — treating connection as dead`);\n onDead();\n }, timeout);\n }, interval);\n }\n\n return {\n onPong() {\n clearTimeout(pongTimer);\n pongTimer = undefined;\n\n if (running) schedulePing();\n },\n\n start() {\n running = true;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pongTimer = undefined;\n schedulePing();\n },\n\n stop() {\n running = false;\n clearTimeout(pingTimer);\n clearTimeout(pongTimer);\n pingTimer = undefined;\n pongTimer = undefined;\n },\n };\n}\n"],"mappings":";;;AAKA,IAAM,IAAmB,KACnB,IAAkB;AAgBxB,SAAgB,EACd,GACA,GACA,GACiB;CACjB,IAAI,CAAC,GACH,OAAO;EAAE,SAAS,CAAC;EAAG,QAAQ,CAAC;EAAG,OAAO,CAAC;CAAE;CAG9C,IAAM,IAA4B,MAAS,KAAO,CAAC,IAAI,GACjD,IAAW,EAAQ,YAAY,GAC/B,IAAU,EAAQ,WAAW,GAE/B,GACA,GACA,IAAU;CAEd,SAAS,IAAqB;EAC5B,IAAY,iBAAiB;GAK3B,AAFA,EAAK,EAAO;IAAE,IAFH,KAAK,IAEF;IAAI,MAAM;GAAO,CAAC,CAAC,GAEjC,IAAY,iBAAiB;IAE3B,AADK,GAA6B,EAA7B,GACL,EAAO;GACT,GAAG,CAAO;EACZ,GAAG,CAAQ;CACb;CAEA,OAAO;EACL,SAAS;GAIP,AAHA,aAAa,CAAS,GACtB,IAAY,KAAA,GAER,KAAS,EAAa;EAC5B;EAEA,QAAQ;GAKN,AAJA,IAAU,IACV,aAAa,CAAS,GACtB,aAAa,CAAS,GACtB,IAAY,KAAA,GACZ,EAAa;EACf;EAEA,OAAO;GAKL,AAJA,IAAU,IACV,aAAa,CAAS,GACtB,aAAa,CAAS,GACtB,IAAY,KAAA,GACZ,IAAY,KAAA;EACd;CACF;AACF"}
|
package/dist/presence.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
require("./_dev.cjs");const e=require("./_utils.cjs"),t=require("./protocol.cjs");let n=require("@vielzeug/ripple");function r(r,i,a,o,s){let c=e.deriveAbortController(o),l=c.signal.aborted;c.signal.addEventListener(`abort`,()=>{l=!0},{once:!0});let u=(0,n.signal)(new Map),d=new Set,f=new Set,p=[a.onState(e=>{if(e.room!==r)return;let t=new Map;for(let[n,r]of Object.entries(e.members))t.set(n,r);u.value=t}),a.onJoin(e=>{if(e.room!==r)return;let t=new Map(u.value);t.set(e.id,e.state),u.value=t;for(let t of d)t(e.id,e.state)}),a.onLeave(e=>{if(e.room!==r)return;let t=new Map(u.value);t.delete(e.id),u.value=t;for(let t of f)t(e.id)})];return{get disposalSignal(){return c.signal},dispose(){if(!l){l=!0,i(t.encode({room:r,type:`leave`})),c.abort();for(let e of p)e();p.length=0,d.clear(),f.clear(),s?.()}},get disposed(){return l},onJoin(e){return l?(`${r}`,()=>{}):(d.add(e),()=>d.delete(e))},onLeave(e){return l?(`${r}`,()=>{}):(f.add(e),()=>f.delete(e))},get room(){return r},get state(){return u},[Symbol.dispose](){this.dispose()},update(e){l||i(t.encode({room:r,state:e,type:`presence`}))}}}exports.createPresence=r;
|
|
2
2
|
//# sourceMappingURL=presence.cjs.map
|
package/dist/presence.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presence.cjs","names":[],"sources":["../src/presence.ts"],"sourcesContent":["import { type Signal, signal } from '@vielzeug/ripple';\n\nimport type { PresenceChannel, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { deriveAbortController } from './_utils';\nimport { type InPresenceJoinFrame, type InPresenceLeaveFrame, type InPresenceStateFrame, encode } from './protocol';\n\ntype RawSendFn = (frame: string) => void;\n\nexport type PresenceEvents = {\n onJoin(handler: (frame: InPresenceJoinFrame) => void): Unsubscribe;\n onLeave(handler: (frame: InPresenceLeaveFrame) => void): Unsubscribe;\n onState(handler: (frame: InPresenceStateFrame) => void): Unsubscribe;\n};\n\n/**\n * Create a presence channel that reactively tracks room member states.\n * @internal\n */\nexport function createPresence<T>(\n room: string,\n rawSend: RawSendFn,\n events: PresenceEvents,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PresenceChannel<T> {\n const ctrl = deriveAbortController(disposalSignal);\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const members: Signal<Map<string, T>> = signal(new Map<string, T>());\n const joinHandlers = new Set<(memberId: string, state: T) => void>();\n const leaveHandlers = new Set<(memberId: string) => void>();\n\n const unsubs: Array<() => void> = [\n events.onState((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map<string, T>();\n\n for (const [id, state] of Object.entries(frame.members)) {\n next.set(id, state as T);\n }\n\n members.value = next;\n }),\n events.onJoin((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.set(frame.id, frame.state as T);\n members.value = next;\n\n for (const handler of joinHandlers) {\n handler(frame.id, frame.state as T);\n }\n }),\n events.onLeave((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.delete(frame.id);\n members.value = next;\n\n for (const handler of leaveHandlers) {\n handler(frame.id);\n }\n }),\n ];\n\n const presence: PresenceChannel<T> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n rawSend(encode({ room, type: 'leave' }));\n ctrl.abort();\n\n for (const unsub of unsubs) unsub();\n\n unsubs.length = 0;\n joinHandlers.clear();\n leaveHandlers.clear();\n
|
|
1
|
+
{"version":3,"file":"presence.cjs","names":[],"sources":["../src/presence.ts"],"sourcesContent":["import { type Signal, signal } from '@vielzeug/ripple';\n\nimport type { PresenceChannel, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { deriveAbortController } from './_utils';\nimport { type InPresenceJoinFrame, type InPresenceLeaveFrame, type InPresenceStateFrame, encode } from './protocol';\n\ntype RawSendFn = (frame: string) => void;\n\nexport type PresenceEvents = {\n onJoin(handler: (frame: InPresenceJoinFrame) => void): Unsubscribe;\n onLeave(handler: (frame: InPresenceLeaveFrame) => void): Unsubscribe;\n onState(handler: (frame: InPresenceStateFrame) => void): Unsubscribe;\n};\n\n/**\n * Create a presence channel that reactively tracks room member states.\n * @internal\n */\nexport function createPresence<T>(\n room: string,\n rawSend: RawSendFn,\n events: PresenceEvents,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PresenceChannel<T> {\n const ctrl = deriveAbortController(disposalSignal);\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const members: Signal<Map<string, T>> = signal(new Map<string, T>());\n const joinHandlers = new Set<(memberId: string, state: T) => void>();\n const leaveHandlers = new Set<(memberId: string) => void>();\n\n const unsubs: Array<() => void> = [\n events.onState((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map<string, T>();\n\n for (const [id, state] of Object.entries(frame.members)) {\n next.set(id, state as T);\n }\n\n members.value = next;\n }),\n events.onJoin((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.set(frame.id, frame.state as T);\n members.value = next;\n\n for (const handler of joinHandlers) {\n handler(frame.id, frame.state as T);\n }\n }),\n events.onLeave((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.delete(frame.id);\n members.value = next;\n\n for (const handler of leaveHandlers) {\n handler(frame.id);\n }\n }),\n ];\n\n const presence: PresenceChannel<T> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n rawSend(encode({ room, type: 'leave' }));\n ctrl.abort();\n\n for (const unsub of unsubs) unsub();\n\n unsubs.length = 0;\n joinHandlers.clear();\n leaveHandlers.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n onJoin(handler: (memberId: string, state: T) => void): Unsubscribe {\n if (disposed) {\n warn(`onJoin() called on a disposed presence channel '${room}' — listener ignored`);\n\n return () => {};\n }\n\n joinHandlers.add(handler);\n\n return () => joinHandlers.delete(handler);\n },\n\n onLeave(handler: (memberId: string) => void): Unsubscribe {\n if (disposed) {\n warn(`onLeave() called on a disposed presence channel '${room}' — listener ignored`);\n\n return () => {};\n }\n\n leaveHandlers.add(handler);\n\n return () => leaveHandlers.delete(handler);\n },\n\n get room() {\n return room;\n },\n\n get state() {\n return members;\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n update(state: T): void {\n if (disposed) return;\n\n rawSend(encode({ room, state, type: 'presence' }));\n },\n };\n\n return presence;\n}\n"],"mappings":"oHAoBA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACoB,CACpB,IAAM,EAAO,EAAA,sBAAsB,CAAc,EAE7C,EAAW,EAAK,OAAO,QAE3B,EAAK,OAAO,iBACV,YACM,CACJ,EAAW,EACb,EACA,CAAE,KAAM,EAAK,CACf,EAEA,IAAM,GAAA,EAAkC,EAAA,OAAA,CAAO,IAAI,GAAgB,EAC7D,EAAe,IAAI,IACnB,EAAgB,IAAI,IAEpB,EAA4B,CAChC,EAAO,QAAS,GAAU,CACxB,GAAI,EAAM,OAAS,EAAM,OAEzB,IAAM,EAAO,IAAI,IAEjB,IAAK,GAAM,CAAC,EAAI,KAAU,OAAO,QAAQ,EAAM,OAAO,EACpD,EAAK,IAAI,EAAI,CAAU,EAGzB,EAAQ,MAAQ,CAClB,CAAC,EACD,EAAO,OAAQ,GAAU,CACvB,GAAI,EAAM,OAAS,EAAM,OAEzB,IAAM,EAAO,IAAI,IAAI,EAAQ,KAAK,EAElC,EAAK,IAAI,EAAM,GAAI,EAAM,KAAU,EACnC,EAAQ,MAAQ,EAEhB,IAAK,IAAM,KAAW,EACpB,EAAQ,EAAM,GAAI,EAAM,KAAU,CAEtC,CAAC,EACD,EAAO,QAAS,GAAU,CACxB,GAAI,EAAM,OAAS,EAAM,OAEzB,IAAM,EAAO,IAAI,IAAI,EAAQ,KAAK,EAElC,EAAK,OAAO,EAAM,EAAE,EACpB,EAAQ,MAAQ,EAEhB,IAAK,IAAM,KAAW,EACpB,EAAQ,EAAM,EAAE,CAEpB,CAAC,CACH,EAqEA,MAAO,CAlEL,IAAI,gBAAiB,CACnB,OAAO,EAAK,MACd,EAEA,SAAU,CACJ,MAIJ,CAFA,EAAW,GACX,EAAQ,EAAA,OAAO,CAAE,OAAM,KAAM,OAAQ,CAAC,CAAC,EACvC,EAAK,MAAM,EAEX,IAAK,IAAM,KAAS,EAAQ,EAAM,EAElC,EAAO,OAAS,EAChB,EAAa,MAAM,EACnB,EAAc,MAAM,EACpB,IAAY,CAPD,CAQb,EAEA,IAAI,UAAW,CACb,OAAO,CACT,EAEA,OAAO,EAA4D,CASjE,OARI,GACG,GAAmD,EAAnD,MAEQ,CAAC,IAGhB,EAAa,IAAI,CAAO,MAEX,EAAa,OAAO,CAAO,EAC1C,EAEA,QAAQ,EAAkD,CASxD,OARI,GACG,GAAoD,EAApD,MAEQ,CAAC,IAGhB,EAAc,IAAI,CAAO,MAEZ,EAAc,OAAO,CAAO,EAC3C,EAEA,IAAI,MAAO,CACT,OAAO,CACT,EAEA,IAAI,OAAQ,CACV,OAAO,CACT,EAEA,CAAC,OAAO,UAAW,CACjB,KAAK,QAAQ,CACf,EAEA,OAAO,EAAgB,CACjB,GAEJ,EAAQ,EAAA,OAAO,CAAE,OAAM,QAAO,KAAM,UAAW,CAAC,CAAC,CACnD,CAGK,CACT"}
|
package/dist/presence.js
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { deriveAbortController as
|
|
3
|
-
import { encode as
|
|
4
|
-
import { signal as
|
|
1
|
+
import "./_dev.js";
|
|
2
|
+
import { deriveAbortController as e } from "./_utils.js";
|
|
3
|
+
import { encode as t } from "./protocol.js";
|
|
4
|
+
import { signal as n } from "@vielzeug/ripple";
|
|
5
5
|
//#region src/presence.ts
|
|
6
|
-
function
|
|
7
|
-
let
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
function r(r, i, a, o, s) {
|
|
7
|
+
let c = e(o), l = c.signal.aborted;
|
|
8
|
+
c.signal.addEventListener("abort", () => {
|
|
9
|
+
l = !0;
|
|
10
10
|
}, { once: !0 });
|
|
11
|
-
let
|
|
12
|
-
|
|
13
|
-
if (e.room !==
|
|
11
|
+
let u = n(/* @__PURE__ */ new Map()), d = /* @__PURE__ */ new Set(), f = /* @__PURE__ */ new Set(), p = [
|
|
12
|
+
a.onState((e) => {
|
|
13
|
+
if (e.room !== r) return;
|
|
14
14
|
let t = /* @__PURE__ */ new Map();
|
|
15
15
|
for (let [n, r] of Object.entries(e.members)) t.set(n, r);
|
|
16
|
-
|
|
16
|
+
u.value = t;
|
|
17
17
|
}),
|
|
18
|
-
|
|
19
|
-
if (e.room !==
|
|
20
|
-
let t = new Map(
|
|
21
|
-
t.set(e.id, e.state),
|
|
22
|
-
for (let t of
|
|
18
|
+
a.onJoin((e) => {
|
|
19
|
+
if (e.room !== r) return;
|
|
20
|
+
let t = new Map(u.value);
|
|
21
|
+
t.set(e.id, e.state), u.value = t;
|
|
22
|
+
for (let t of d) t(e.id, e.state);
|
|
23
23
|
}),
|
|
24
|
-
|
|
25
|
-
if (e.room !==
|
|
26
|
-
let t = new Map(
|
|
27
|
-
t.delete(e.id),
|
|
28
|
-
for (let t of
|
|
24
|
+
a.onLeave((e) => {
|
|
25
|
+
if (e.room !== r) return;
|
|
26
|
+
let t = new Map(u.value);
|
|
27
|
+
t.delete(e.id), u.value = t;
|
|
28
|
+
for (let t of f) t(e.id);
|
|
29
29
|
})
|
|
30
30
|
];
|
|
31
31
|
return {
|
|
32
32
|
get disposalSignal() {
|
|
33
|
-
return
|
|
33
|
+
return c.signal;
|
|
34
34
|
},
|
|
35
35
|
dispose() {
|
|
36
|
-
if (!
|
|
37
|
-
|
|
38
|
-
room:
|
|
36
|
+
if (!l) {
|
|
37
|
+
l = !0, i(t({
|
|
38
|
+
room: r,
|
|
39
39
|
type: "leave"
|
|
40
|
-
})),
|
|
41
|
-
for (let e of
|
|
42
|
-
|
|
40
|
+
})), c.abort();
|
|
41
|
+
for (let e of p) e();
|
|
42
|
+
p.length = 0, d.clear(), f.clear(), s?.();
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
get disposed() {
|
|
46
|
-
return
|
|
46
|
+
return l;
|
|
47
47
|
},
|
|
48
|
-
onJoin(
|
|
49
|
-
return
|
|
48
|
+
onJoin(e) {
|
|
49
|
+
return l ? (`${r}`, () => {}) : (d.add(e), () => d.delete(e));
|
|
50
50
|
},
|
|
51
|
-
onLeave(
|
|
52
|
-
return
|
|
51
|
+
onLeave(e) {
|
|
52
|
+
return l ? (`${r}`, () => {}) : (f.add(e), () => f.delete(e));
|
|
53
53
|
},
|
|
54
54
|
get room() {
|
|
55
|
-
return
|
|
55
|
+
return r;
|
|
56
56
|
},
|
|
57
57
|
get state() {
|
|
58
|
-
return
|
|
58
|
+
return u;
|
|
59
59
|
},
|
|
60
60
|
[Symbol.dispose]() {
|
|
61
61
|
this.dispose();
|
|
62
62
|
},
|
|
63
63
|
update(e) {
|
|
64
|
-
|
|
65
|
-
room:
|
|
64
|
+
l || i(t({
|
|
65
|
+
room: r,
|
|
66
66
|
state: e,
|
|
67
67
|
type: "presence"
|
|
68
68
|
}));
|
|
@@ -70,6 +70,6 @@ function i(i, a, o, s, c) {
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
//#endregion
|
|
73
|
-
export {
|
|
73
|
+
export { r as createPresence };
|
|
74
74
|
|
|
75
75
|
//# sourceMappingURL=presence.js.map
|
package/dist/presence.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presence.js","names":[],"sources":["../src/presence.ts"],"sourcesContent":["import { type Signal, signal } from '@vielzeug/ripple';\n\nimport type { PresenceChannel, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { deriveAbortController } from './_utils';\nimport { type InPresenceJoinFrame, type InPresenceLeaveFrame, type InPresenceStateFrame, encode } from './protocol';\n\ntype RawSendFn = (frame: string) => void;\n\nexport type PresenceEvents = {\n onJoin(handler: (frame: InPresenceJoinFrame) => void): Unsubscribe;\n onLeave(handler: (frame: InPresenceLeaveFrame) => void): Unsubscribe;\n onState(handler: (frame: InPresenceStateFrame) => void): Unsubscribe;\n};\n\n/**\n * Create a presence channel that reactively tracks room member states.\n * @internal\n */\nexport function createPresence<T>(\n room: string,\n rawSend: RawSendFn,\n events: PresenceEvents,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PresenceChannel<T> {\n const ctrl = deriveAbortController(disposalSignal);\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const members: Signal<Map<string, T>> = signal(new Map<string, T>());\n const joinHandlers = new Set<(memberId: string, state: T) => void>();\n const leaveHandlers = new Set<(memberId: string) => void>();\n\n const unsubs: Array<() => void> = [\n events.onState((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map<string, T>();\n\n for (const [id, state] of Object.entries(frame.members)) {\n next.set(id, state as T);\n }\n\n members.value = next;\n }),\n events.onJoin((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.set(frame.id, frame.state as T);\n members.value = next;\n\n for (const handler of joinHandlers) {\n handler(frame.id, frame.state as T);\n }\n }),\n events.onLeave((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.delete(frame.id);\n members.value = next;\n\n for (const handler of leaveHandlers) {\n handler(frame.id);\n }\n }),\n ];\n\n const presence: PresenceChannel<T> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n rawSend(encode({ room, type: 'leave' }));\n ctrl.abort();\n\n for (const unsub of unsubs) unsub();\n\n unsubs.length = 0;\n joinHandlers.clear();\n leaveHandlers.clear();\n
|
|
1
|
+
{"version":3,"file":"presence.js","names":[],"sources":["../src/presence.ts"],"sourcesContent":["import { type Signal, signal } from '@vielzeug/ripple';\n\nimport type { PresenceChannel, Unsubscribe } from './types';\n\nimport { warn } from './_dev';\nimport { deriveAbortController } from './_utils';\nimport { type InPresenceJoinFrame, type InPresenceLeaveFrame, type InPresenceStateFrame, encode } from './protocol';\n\ntype RawSendFn = (frame: string) => void;\n\nexport type PresenceEvents = {\n onJoin(handler: (frame: InPresenceJoinFrame) => void): Unsubscribe;\n onLeave(handler: (frame: InPresenceLeaveFrame) => void): Unsubscribe;\n onState(handler: (frame: InPresenceStateFrame) => void): Unsubscribe;\n};\n\n/**\n * Create a presence channel that reactively tracks room member states.\n * @internal\n */\nexport function createPresence<T>(\n room: string,\n rawSend: RawSendFn,\n events: PresenceEvents,\n disposalSignal: AbortSignal,\n onDispose?: () => void,\n): PresenceChannel<T> {\n const ctrl = deriveAbortController(disposalSignal);\n\n let disposed = ctrl.signal.aborted;\n\n ctrl.signal.addEventListener(\n 'abort',\n () => {\n disposed = true;\n },\n { once: true },\n );\n\n const members: Signal<Map<string, T>> = signal(new Map<string, T>());\n const joinHandlers = new Set<(memberId: string, state: T) => void>();\n const leaveHandlers = new Set<(memberId: string) => void>();\n\n const unsubs: Array<() => void> = [\n events.onState((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map<string, T>();\n\n for (const [id, state] of Object.entries(frame.members)) {\n next.set(id, state as T);\n }\n\n members.value = next;\n }),\n events.onJoin((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.set(frame.id, frame.state as T);\n members.value = next;\n\n for (const handler of joinHandlers) {\n handler(frame.id, frame.state as T);\n }\n }),\n events.onLeave((frame) => {\n if (frame.room !== room) return;\n\n const next = new Map(members.value);\n\n next.delete(frame.id);\n members.value = next;\n\n for (const handler of leaveHandlers) {\n handler(frame.id);\n }\n }),\n ];\n\n const presence: PresenceChannel<T> = {\n get disposalSignal() {\n return ctrl.signal;\n },\n\n dispose() {\n if (disposed) return;\n\n disposed = true;\n rawSend(encode({ room, type: 'leave' }));\n ctrl.abort();\n\n for (const unsub of unsubs) unsub();\n\n unsubs.length = 0;\n joinHandlers.clear();\n leaveHandlers.clear();\n onDispose?.();\n },\n\n get disposed() {\n return disposed;\n },\n\n onJoin(handler: (memberId: string, state: T) => void): Unsubscribe {\n if (disposed) {\n warn(`onJoin() called on a disposed presence channel '${room}' — listener ignored`);\n\n return () => {};\n }\n\n joinHandlers.add(handler);\n\n return () => joinHandlers.delete(handler);\n },\n\n onLeave(handler: (memberId: string) => void): Unsubscribe {\n if (disposed) {\n warn(`onLeave() called on a disposed presence channel '${room}' — listener ignored`);\n\n return () => {};\n }\n\n leaveHandlers.add(handler);\n\n return () => leaveHandlers.delete(handler);\n },\n\n get room() {\n return room;\n },\n\n get state() {\n return members;\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n\n update(state: T): void {\n if (disposed) return;\n\n rawSend(encode({ room, state, type: 'presence' }));\n },\n };\n\n return presence;\n}\n"],"mappings":";;;;;AAoBA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACoB;CACpB,IAAM,IAAO,EAAsB,CAAc,GAE7C,IAAW,EAAK,OAAO;CAE3B,EAAK,OAAO,iBACV,eACM;EACJ,IAAW;CACb,GACA,EAAE,MAAM,GAAK,CACf;CAEA,IAAM,IAAkC,kBAAO,IAAI,IAAe,CAAC,GAC7D,oBAAe,IAAI,IAA0C,GAC7D,oBAAgB,IAAI,IAAgC,GAEpD,IAA4B;EAChC,EAAO,SAAS,MAAU;GACxB,IAAI,EAAM,SAAS,GAAM;GAEzB,IAAM,oBAAO,IAAI,IAAe;GAEhC,KAAK,IAAM,CAAC,GAAI,MAAU,OAAO,QAAQ,EAAM,OAAO,GACpD,EAAK,IAAI,GAAI,CAAU;GAGzB,EAAQ,QAAQ;EAClB,CAAC;EACD,EAAO,QAAQ,MAAU;GACvB,IAAI,EAAM,SAAS,GAAM;GAEzB,IAAM,IAAO,IAAI,IAAI,EAAQ,KAAK;GAGlC,AADA,EAAK,IAAI,EAAM,IAAI,EAAM,KAAU,GACnC,EAAQ,QAAQ;GAEhB,KAAK,IAAM,KAAW,GACpB,EAAQ,EAAM,IAAI,EAAM,KAAU;EAEtC,CAAC;EACD,EAAO,SAAS,MAAU;GACxB,IAAI,EAAM,SAAS,GAAM;GAEzB,IAAM,IAAO,IAAI,IAAI,EAAQ,KAAK;GAGlC,AADA,EAAK,OAAO,EAAM,EAAE,GACpB,EAAQ,QAAQ;GAEhB,KAAK,IAAM,KAAW,GACpB,EAAQ,EAAM,EAAE;EAEpB,CAAC;CACH;CAqEA,OAAO;EAlEL,IAAI,iBAAiB;GACnB,OAAO,EAAK;EACd;EAEA,UAAU;GACJ,QAIJ;IAFA,IAAW,IACX,EAAQ,EAAO;KAAE;KAAM,MAAM;IAAQ,CAAC,CAAC,GACvC,EAAK,MAAM;IAEX,KAAK,IAAM,KAAS,GAAQ,EAAM;IAKlC,AAHA,EAAO,SAAS,GAChB,EAAa,MAAM,GACnB,EAAc,MAAM,GACpB,IAAY;GAPD;EAQb;EAEA,IAAI,WAAW;GACb,OAAO;EACT;EAEA,OAAO,GAA4D;GASjE,OARI,KACG,GAAmD,EAAnD,SAEQ,CAAC,MAGhB,EAAa,IAAI,CAAO,SAEX,EAAa,OAAO,CAAO;EAC1C;EAEA,QAAQ,GAAkD;GASxD,OARI,KACG,GAAoD,EAApD,SAEQ,CAAC,MAGhB,EAAc,IAAI,CAAO,SAEZ,EAAc,OAAO,CAAO;EAC3C;EAEA,IAAI,OAAO;GACT,OAAO;EACT;EAEA,IAAI,QAAQ;GACV,OAAO;EACT;EAEA,CAAC,OAAO,WAAW;GACjB,KAAK,QAAQ;EACf;EAEA,OAAO,GAAgB;GACjB,KAEJ,EAAQ,EAAO;IAAE;IAAM;IAAO,MAAM;GAAW,CAAC,CAAC;EACnD;CAGK;AACT"}
|
package/dist/pulse.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/ripple");var t=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},n=class extends t{url;constructor(e,t,n){super(e,n),this.url=t}},r=class extends t{event;constructor(e,t){super(`Timed out waiting for "${e}"`,t),this.event=e}},i=class extends t{constructor(e){super(`Aborted`,e)}},a=class extends t{constructor(e=`Pulse`,t){super(`${e} instance is disposed`,t)}},o=class extends t{raw;constructor(e,t,n){super(e,n),this.raw=t}},s=!globalThis.__PULSE_PROD__;function c(e){s&&console.warn(`[@vielzeug/pulse] ${e}`)}function l(e,...t){return t.length===0?e:t.reduce(u,e)}function u(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 d(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function f(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 p(e,t=3e4){let n=Math.min(1e3*2**e,t);return Math.random()*n}function m(e,t,n,a){return new Promise((o,s)=>{let c=[t],u;if(n?.signal&&c.push(n.signal),n?.timeout!==void 0){let t=new AbortController;u=setTimeout(()=>{t.abort(new r(e))},n.timeout),c.push(t.signal)}let d=c.length===1?c[0]:l(c[0],...c.slice(1));if(d.aborted){clearTimeout(u),s(d.reason instanceof r?d.reason:new i);return}let f=()=>{},p=()=>{clearTimeout(u),f(),s(d.reason instanceof r?d.reason:new i)};d.addEventListener(`abort`,p,{once:!0}),f=a(e,e=>{clearTimeout(u),d.removeEventListener(`abort`,p),o(e)})})}function h(e,t,n,r,i){let a=d(r),o=new Set,s=a.signal.aborted;return a.signal.addEventListener(`abort`,()=>{s=!0},{once:!0}),{get disposalSignal(){return a.signal},dispose(){if(!s){s=!0,a.abort();for(let e of o)e();o.clear(),i?.()}},get disposed(){return s},get name(){return e},on(t,r){if(s)return c(`on('${String(t)}') called on a disposed channel '${e}' — listener ignored`),()=>{};let i=n(e,t,r);return o.add(i),()=>{i(),o.delete(i)}},once(t,r){if(s)return c(`once('${String(t)}') called on a disposed channel '${e}' — listener ignored`),()=>{};let i=()=>{};return i=n(e,t,e=>{o.delete(i),i(),r(e)}),o.add(i),()=>{o.delete(i),i()}},send(n,r){if(s){c(`send('${String(n)}') called on a disposed channel '${e}' — message dropped`);return}t(e,n,r)},[Symbol.dispose](){this.dispose()},wait(t,r){return m(t,a.signal,r,(t,r)=>n(e,t,r))}}}function g(e){return JSON.stringify(e)}function _(e){if(typeof e!=`string`)throw new o(`Expected string message data`,e);let t;try{t=JSON.parse(e)}catch{throw new o(`JSON parse error`,e)}if(typeof t!=`object`||!t||!(`type`in t)||typeof t.type!=`string`)throw new o(`Missing or invalid "type" field`,t);return t}function v(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function y(e,t){if(!v(e))return null;let n=e[t];return typeof n==`string`?n:null}function b(e){return y(e,`code`)!==null&&y(e,`message`)!==null}function x(e){return y(e,`room`)!==null}function S(e){return y(e,`room`)!==null}function C(e){return!v(e)||y(e,`event`)===null||!(`payload`in e)?!1:e.channel===void 0||typeof e.channel==`string`}function w(e){return v(e)&&typeof e.ts==`number`}function T(e){return v(e)&&y(e,`id`)!==null&&y(e,`room`)!==null&&`state`in e}function E(e){return y(e,`id`)!==null&&y(e,`room`)!==null}function D(e){return v(e)&&y(e,`room`)!==null&&v(e.members)}function O(e){return y(e,`channel`)!==null}function k(e){return y(e,`channel`)!==null}function A(e){let t=_(e);switch(t.type){case`error`:if(b(t))return{frame:t,kind:`known`};break;case`joined`:if(x(t))return{frame:t,kind:`known`};break;case`left`:if(S(t))return{frame:t,kind:`known`};break;case`message`:if(C(t))return{frame:t,kind:`known`};break;case`pong`:if(w(t))return{frame:t,kind:`known`};break;case`presence_join`:if(T(t))return{frame:t,kind:`known`};break;case`presence_leave`:if(E(t))return{frame:t,kind:`known`};break;case`presence_state`:if(D(t))return{frame:t,kind:`known`};break;case`subscribed`:if(O(t))return{frame:t,kind:`known`};break;case`unsubscribed`:if(k(t))return{frame:t,kind:`known`};break;default:return{kind:`unknown`,type:t.type}}throw new o(`Malformed "${t.type}" frame`,t)}var j=3e4,M=5e3;function N(e,t,n){if(!e)return{onPong(){},start(){},stop(){}};let r=e===!0?{}:e,i=r.interval??j,a=r.timeout??M,o,s,l=!1;function u(){o=setTimeout(()=>{t(g({ts:Date.now(),type:`ping`})),s=setTimeout(()=>{c(`Heartbeat timed out after ${a}ms — treating connection as dead`),n()},a)},i)}return{onPong(){clearTimeout(s),s=void 0,l&&u()},start(){l=!0,clearTimeout(o),clearTimeout(s),s=void 0,u()},stop(){l=!1,clearTimeout(o),clearTimeout(s),o=void 0,s=void 0}}}function P(t,n,r,i,a){let o=d(i),s=o.signal.aborted;o.signal.addEventListener(`abort`,()=>{s=!0},{once:!0});let l=(0,e.signal)(new Map),u=new Set,f=new Set,p=[r.onState(e=>{if(e.room!==t)return;let n=new Map;for(let[t,r]of Object.entries(e.members))n.set(t,r);l.value=n}),r.onJoin(e=>{if(e.room!==t)return;let n=new Map(l.value);n.set(e.id,e.state),l.value=n;for(let t of u)t(e.id,e.state)}),r.onLeave(e=>{if(e.room!==t)return;let n=new Map(l.value);n.delete(e.id),l.value=n;for(let t of f)t(e.id)})];return{get disposalSignal(){return o.signal},dispose(){if(!s){s=!0,n(g({room:t,type:`leave`})),o.abort();for(let e of p)e();p.length=0,u.clear(),f.clear(),l.dispose(),a?.()}},get disposed(){return s},onJoin(e){return s?(c(`onJoin() called on a disposed presence channel '${t}' — listener ignored`),()=>{}):(u.add(e),()=>u.delete(e))},onLeave(e){return s?(c(`onLeave() called on a disposed presence channel '${t}' — listener ignored`),()=>{}):(f.add(e),()=>f.delete(e))},get room(){return t},get state(){return l},[Symbol.dispose](){this.dispose()},update(e){s||n(g({room:t,state:e,type:`presence`}))}}}var F=5;function I(e){if(!e)return{attempt:async()=>({ok:!1}),reset(){}};let t=e===!0?{}:e,n=t.maxAttempts??F,r=typeof t.delay==`function`?t.delay:typeof t.delay==`number`?()=>t.delay:p,i=0;return{async attempt(e,t,a){if(i>=n)return{ok:!1};let o=r(i);if(i++,a?.(i),await f(o,t),t.aborted)return{ok:!1};try{return await e(),i=0,{ok:!0}}catch(e){return{error:e,ok:!1}}},reset(){i=0}}}var L=class{map=new Map;add(e,t,n){let r=this.map.get(e);r||(r=new Map,this.map.set(e,r));let i=r.get(t);return i||(i=new Set,r.set(t,i)),i.add(n),()=>{i.delete(n),i.size===0&&r.delete(t),r.size===0&&this.map.delete(e)}}dispatch(e,t,n){this.map.get(e)?.get(t)?.forEach(e=>e(n))}clear(){this.map.clear()}};function R(t,o={}){let s=new AbortController,u=(0,e.signal)(`connecting`),d=(0,e.signal)(new Set),f=new L,p=o.middleware??[],_=new Map,v=new Map,y=null,b=!1,x=!1,S=[],C=new Map,w=new Map,T=o.buffer,E=T?[]:null,D=typeof T==`object`&&T?T.maxSize??50:50,O=I(o.reconnect),k=N(o.heartbeat,e=>{y?.readyState===WebSocket.OPEN&&y.send(e)},()=>{c(`Heartbeat dead — closing socket for reconnect`),y?.close(4e3,`heartbeat timeout`)});function j(e){y?.readyState===WebSocket.OPEN?y.send(e):E===null?c(`send() called while connection is ${u.value} — message dropped`):(E.length>=D&&E.shift(),E.push(e))}function M(e,t,n){let r=0;function i(){if(r>=p.length){n();return}let a=p[r++];a(e,t,i)}i()}function F(){b||(y=new WebSocket(t,o.protocols),y.onopen=()=>{x=!1,u.value=`open`,O.reset(),k.start(),o.onOpen?.();let e=S.splice(0);for(let t of e)t.resolve();if(E&&E.length>0){let e=E.splice(0);for(let t of e)y.send(t)}for(let e of C.keys())y.send(g({channel:e,type:`subscribe`}))},y.onmessage=e=>{o.onMessage?.(e);let t;try{t=A(e.data)}catch(e){c(`Protocol error: ${String(e)}`);return}try{z(t)}catch(e){c(`Frame handling error: ${String(e)}`)}},y.onerror=()=>{o.onError?o.onError(new n(`WebSocket error`,t)):c(`WebSocket error — pass onError to observe it (see onClose for recovery logic)`);let e=S.splice(0);for(let r of e)r.reject(new n(`WebSocket error`,t))},y.onclose=e=>{k.stop(),o.onClose?.(e.code,e.reason);let r=S.splice(0);for(let e of r)e.reject(new n(`Connection closed before open`,t));if(x||b){u.value=`closed`;return}R()})}async function R(){u.value=`reconnecting`;let e=await O.attempt(()=>new Promise((e,t)=>{S.push({reject:t,resolve:e}),F()}),s.signal,o.onReconnect);!e.ok&&!b&&(e.error&&c(`Reconnect attempt failed: ${String(e.error)}`),c(`Reconnect budget exhausted — connection permanently closed`),u.value=`closed`)}function z(e){if(e.kind===`unknown`){c(`Received frame with an unrecognized type: ${e.type}`);return}let t=e.frame;switch(t.type){case`error`:c(`Server error [${t.code}]: ${t.message}`);break;case`joined`:{let e=new Set(d.value);e.add(t.room),d.value=e;let n=_.get(t.room);if(n){_.delete(t.room);for(let e of n)e()}break}case`left`:{let e=new Set(d.value);e.delete(t.room),d.value=e;let n=v.get(t.room);if(n){v.delete(t.room);for(let e of n)e()}break}case`message`:f.dispatch(t.channel??null,t.event,t.payload);break;case`pong`:k.onPong();break;case`presence_join`:f.dispatch(null,t.type,t);break;case`presence_leave`:f.dispatch(null,t.type,t);break;case`presence_state`:f.dispatch(null,t.type,t);break;case`subscribed`:case`unsubscribed`:break}}function B(e,t,n,o){return b?Promise.reject(new a):new Promise((a,c)=>{let u=[];o?.signal&&u.push(o.signal);let d;if(o?.timeout!==void 0){let e=new AbortController;d=setTimeout(()=>e.abort(new r(n)),o.timeout),u.push(e.signal)}let f=u.length===0?s.signal:l(s.signal,...u);if(f.aborted){clearTimeout(d),c(f.reason instanceof r?f.reason:new i);return}let p=()=>{},m=()=>{clearTimeout(d),f.removeEventListener(`abort`,p),a()};p=()=>{clearTimeout(d);let n=e.get(t);n?.delete(m),n?.size===0&&e.delete(t),c(f.reason instanceof r?f.reason:new i)};let h=e.get(t),_=!h;h||(h=new Set,e.set(t,h)),h.add(m),f.addEventListener(`abort`,p,{once:!0});let v=()=>j(g({room:t,type:n}));_&&(y?.readyState===WebSocket.OPEN?v():V.connect().then(v).catch(n=>{clearTimeout(d),f.removeEventListener(`abort`,p);let r=e.get(t);r?.delete(m),r?.size===0&&e.delete(t),c(n)}))})}let V={channel(e){let t=C.get(e);if(t)return t;b&&c(`channel('${e}') called on a disposed Pulse — returning an already-disposed channel`);let n=h(e,(e,t,n)=>{M(t,n,()=>{j(g({channel:e,event:t,payload:n,type:`message`}))})},(e,t,n)=>f.add(e,t,n),s.signal,()=>{C.delete(e),j(g({channel:e,type:`unsubscribe`}))});return b||C.set(e,n),y?.readyState===WebSocket.OPEN&&y.send(g({channel:e,type:`subscribe`})),n},connect(){return b?Promise.reject(new a):y?.readyState===WebSocket.OPEN?Promise.resolve():new Promise((e,t)=>{S.push({reject:t,resolve:e}),(!y||y.readyState>WebSocket.OPEN)&&F()})},disconnect(e=1e3,t=``){x=!0,y?.close(e,t)},get disposalSignal(){return s.signal},dispose(){if(b)return;b=!0,x=!0,k.stop(),y?.close(1e3,`disposed`),y=null;let e=S.splice(0);for(let t of e)t.reject(new a);C.clear(),w.clear(),_.clear(),v.clear(),u.dispose(),d.dispose(),f.clear(),s.abort()},get disposed(){return b},join(e,t){return B(_,e,`join`,t)},leave(e,t){return B(v,e,`leave`,t)},on(e,t){return b?(c(`on('${String(e)}') called on a disposed Pulse — listener ignored`),()=>{}):f.add(null,e,t)},once(e,t){if(b)return c(`once('${String(e)}') called on a disposed Pulse — listener ignored`),()=>{};let n=()=>{};return n=f.add(null,e,e=>{n(),t(e)}),n},presence(e){let t=w.get(e);if(t)return t;let n=P(e,j,{onJoin:e=>f.add(null,`presence_join`,t=>e(t)),onLeave:e=>f.add(null,`presence_leave`,t=>e(t)),onState:e=>f.add(null,`presence_state`,t=>e(t))},s.signal,()=>{w.delete(e)});return b||w.set(e,n),b?c(`presence('${e}') called on a disposed Pulse — returning an already-disposed presence channel`):V.join(e).catch(t=>{t instanceof i||t instanceof a||c(`presence() join failed for room '${e}': ${String(t)}`)}),n},get rooms(){return d},send(e,t){if(b){c(`send('${String(e)}') called on a disposed Pulse — message dropped`);return}M(e,t,()=>{j(g({event:e,payload:t,type:`message`}))})},get status(){return u},[Symbol.dispose](){this.dispose()},wait(e,t){return m(e,s.signal,t,(e,t)=>f.add(null,e,t))}};return o.lazy||F(),V}exports.PulseAbortError=i,exports.PulseConnectionError=n,exports.PulseDisposedError=a,exports.PulseError=t,exports.PulseProtocolError=o,exports.PulseTimeoutError=r,exports.createPulse=R;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/ripple");var t=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},n=class extends t{url;constructor(e,t,n){super(e,n),this.url=t}},r=class extends t{event;constructor(e,t){super(`Timed out waiting for "${e}"`,t),this.event=e}},i=class extends t{constructor(e){super(`Aborted`,e)}},a=class extends t{constructor(e=`Pulse`,t){super(`${e} instance is disposed`,t)}},o=class extends t{raw;constructor(e,t,n){super(e,n),this.raw=t}};function s(e,...t){return t.length===0?e:t.reduce(c,e)}function c(e,t){if(e.aborted)return e;if(t.aborted)return t;let n=new AbortController,r=()=>n.abort(e.reason),i=()=>n.abort(t.reason);return e.addEventListener(`abort`,r,{once:!0}),t.addEventListener(`abort`,i,{once:!0}),n.signal.addEventListener(`abort`,()=>{e.removeEventListener(`abort`,r),t.removeEventListener(`abort`,i)},{once:!0}),n.signal}function l(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function u(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=setTimeout(n,e);t?.addEventListener(`abort`,()=>{clearTimeout(r),n()},{once:!0})})}function d(e,t=3e4){let n=Math.min(1e3*2**e,t);return Math.random()*n}function f(e,t,n,a){return new Promise((o,c)=>{let l=[t],u;if(n?.signal&&l.push(n.signal),n?.timeout!==void 0){let t=new AbortController;u=setTimeout(()=>{t.abort(new r(e))},n.timeout),l.push(t.signal)}let d=l.length===1?l[0]:s(l[0],...l.slice(1));if(d.aborted){clearTimeout(u),c(d.reason instanceof r?d.reason:new i);return}let f=()=>{},p=()=>{clearTimeout(u),f(),c(d.reason instanceof r?d.reason:new i)};d.addEventListener(`abort`,p,{once:!0}),f=a(e,e=>{clearTimeout(u),d.removeEventListener(`abort`,p),o(e)})})}function p(e,t,n,r,i){let a=l(r),o=new Set,s=a.signal.aborted;return a.signal.addEventListener(`abort`,()=>{s=!0},{once:!0}),{get disposalSignal(){return a.signal},dispose(){if(!s){s=!0,a.abort();for(let e of o)e();o.clear(),i?.()}},get disposed(){return s},get name(){return e},on(t,r){if(s)return`${String(t)}${e}`,()=>{};let i=n(e,t,r);return o.add(i),()=>{i(),o.delete(i)}},once(t,r){if(s)return`${String(t)}${e}`,()=>{};let i=()=>{};return i=n(e,t,e=>{o.delete(i),i(),r(e)}),o.add(i),()=>{o.delete(i),i()}},send(n,r){if(s){`${String(n)}${e}`;return}t(e,n,r)},[Symbol.dispose](){this.dispose()},wait(t,r){return f(t,a.signal,r,(t,r)=>n(e,t,r))}}}function m(e){return JSON.stringify(e)}function h(e){if(typeof e!=`string`)throw new o(`Expected string message data`,e);let t;try{t=JSON.parse(e)}catch{throw new o(`JSON parse error`,e)}if(typeof t!=`object`||!t||!(`type`in t)||typeof t.type!=`string`)throw new o(`Missing or invalid "type" field`,t);return t}function g(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function _(e,t){if(!g(e))return null;let n=e[t];return typeof n==`string`?n:null}function v(e){return _(e,`code`)!==null&&_(e,`message`)!==null}function y(e){return _(e,`room`)!==null}function b(e){return _(e,`room`)!==null}function x(e){return!g(e)||_(e,`event`)===null||!(`payload`in e)?!1:e.channel===void 0||typeof e.channel==`string`}function S(e){return g(e)&&typeof e.ts==`number`}function C(e){return g(e)&&_(e,`id`)!==null&&_(e,`room`)!==null&&`state`in e}function w(e){return _(e,`id`)!==null&&_(e,`room`)!==null}function T(e){return g(e)&&_(e,`room`)!==null&&g(e.members)}function E(e){return _(e,`channel`)!==null}function D(e){return _(e,`channel`)!==null}function O(e){let t=h(e);switch(t.type){case`error`:if(v(t))return{frame:t,kind:`known`};break;case`joined`:if(y(t))return{frame:t,kind:`known`};break;case`left`:if(b(t))return{frame:t,kind:`known`};break;case`message`:if(x(t))return{frame:t,kind:`known`};break;case`pong`:if(S(t))return{frame:t,kind:`known`};break;case`presence_join`:if(C(t))return{frame:t,kind:`known`};break;case`presence_leave`:if(w(t))return{frame:t,kind:`known`};break;case`presence_state`:if(T(t))return{frame:t,kind:`known`};break;case`subscribed`:if(E(t))return{frame:t,kind:`known`};break;case`unsubscribed`:if(D(t))return{frame:t,kind:`known`};break;default:return{kind:`unknown`,type:t.type}}throw new o(`Malformed "${t.type}" frame`,t)}var k=3e4,A=5e3;function j(e,t,n){if(!e)return{onPong(){},start(){},stop(){}};let r=e===!0?{}:e,i=r.interval??k,a=r.timeout??A,o,s,c=!1;function l(){o=setTimeout(()=>{t(m({ts:Date.now(),type:`ping`})),s=setTimeout(()=>{`${a}`,n()},a)},i)}return{onPong(){clearTimeout(s),s=void 0,c&&l()},start(){c=!0,clearTimeout(o),clearTimeout(s),s=void 0,l()},stop(){c=!1,clearTimeout(o),clearTimeout(s),o=void 0,s=void 0}}}function M(t,n,r,i,a){let o=l(i),s=o.signal.aborted;o.signal.addEventListener(`abort`,()=>{s=!0},{once:!0});let c=(0,e.signal)(new Map),u=new Set,d=new Set,f=[r.onState(e=>{if(e.room!==t)return;let n=new Map;for(let[t,r]of Object.entries(e.members))n.set(t,r);c.value=n}),r.onJoin(e=>{if(e.room!==t)return;let n=new Map(c.value);n.set(e.id,e.state),c.value=n;for(let t of u)t(e.id,e.state)}),r.onLeave(e=>{if(e.room!==t)return;let n=new Map(c.value);n.delete(e.id),c.value=n;for(let t of d)t(e.id)})];return{get disposalSignal(){return o.signal},dispose(){if(!s){s=!0,n(m({room:t,type:`leave`})),o.abort();for(let e of f)e();f.length=0,u.clear(),d.clear(),a?.()}},get disposed(){return s},onJoin(e){return s?(`${t}`,()=>{}):(u.add(e),()=>u.delete(e))},onLeave(e){return s?(`${t}`,()=>{}):(d.add(e),()=>d.delete(e))},get room(){return t},get state(){return c},[Symbol.dispose](){this.dispose()},update(e){s||n(m({room:t,state:e,type:`presence`}))}}}var N=5;function P(e){if(!e)return{attempt:async()=>({ok:!1}),reset(){}};let t=e===!0?{}:e,n=t.maxAttempts??N,r=typeof t.delay==`function`?t.delay:typeof t.delay==`number`?()=>t.delay:d,i=0;return{async attempt(e,t,a){if(i>=n)return{ok:!1};let o=r(i);if(i++,a?.(i),await u(o,t),t.aborted)return{ok:!1};try{return await e(),i=0,{ok:!0}}catch(e){return{error:e,ok:!1}}},reset(){i=0}}}var F=class{map=new Map;add(e,t,n){let r=this.map.get(e);r||(r=new Map,this.map.set(e,r));let i=r.get(t);return i||(i=new Set,r.set(t,i)),i.add(n),()=>{i.delete(n),i.size===0&&r.delete(t),r.size===0&&this.map.delete(e)}}dispatch(e,t,n){this.map.get(e)?.get(t)?.forEach(e=>e(n))}clear(){this.map.clear()}};function I(t,o={}){let c=new AbortController,l=(0,e.signal)(`connecting`),u=(0,e.signal)(new Set),d=new F,h=o.middleware??[],g=new Map,_=new Map,v=null,y=!1,b=!1,x=[],S=new Map,C=new Map,w=o.buffer,T=w?[]:null,E=typeof w==`object`&&w?w.maxSize??50:50,D=P(o.reconnect),k=j(o.heartbeat,e=>{v?.readyState===WebSocket.OPEN&&v.send(e)},()=>{v?.close(4e3,`heartbeat timeout`)});function A(e){v?.readyState===WebSocket.OPEN?v.send(e):T===null?`${l.value}`:(T.length>=E&&T.shift(),T.push(e))}function N(e,t,n){let r=0;function i(){if(r>=h.length){n();return}let a=h[r++];a(e,t,i)}i()}function I(){y||(v=new WebSocket(t,o.protocols),v.onopen=()=>{b=!1,l.value=`open`,D.reset(),k.start(),o.onOpen?.();let e=x.splice(0);for(let t of e)t.resolve();if(T&&T.length>0){let e=T.splice(0);for(let t of e)v.send(t)}for(let e of S.keys())v.send(m({channel:e,type:`subscribe`}))},v.onmessage=e=>{o.onMessage?.(e);let t;try{t=O(e.data)}catch(e){`${String(e)}`;return}try{R(t)}catch(e){`${String(e)}`}},v.onerror=()=>{o.onError&&o.onError(new n(`WebSocket error`,t));let e=x.splice(0);for(let r of e)r.reject(new n(`WebSocket error`,t))},v.onclose=e=>{k.stop(),o.onClose?.(e.code,e.reason);let r=x.splice(0);for(let e of r)e.reject(new n(`Connection closed before open`,t));if(b||y){l.value=`closed`;return}L()})}async function L(){l.value=`reconnecting`;let e=await D.attempt(()=>new Promise((e,t)=>{x.push({reject:t,resolve:e}),I()}),c.signal,o.onReconnect);!e.ok&&!y&&(e.error&&`${String(e.error)}`,l.value=`closed`)}function R(e){if(e.kind===`unknown`){`${e.type}`;return}let t=e.frame;switch(t.type){case`error`:`${t.code}${t.message}`;break;case`joined`:{let e=new Set(u.value);e.add(t.room),u.value=e;let n=g.get(t.room);if(n){g.delete(t.room);for(let e of n)e()}break}case`left`:{let e=new Set(u.value);e.delete(t.room),u.value=e;let n=_.get(t.room);if(n){_.delete(t.room);for(let e of n)e()}break}case`message`:d.dispatch(t.channel??null,t.event,t.payload);break;case`pong`:k.onPong();break;case`presence_join`:d.dispatch(null,t.type,t);break;case`presence_leave`:d.dispatch(null,t.type,t);break;case`presence_state`:d.dispatch(null,t.type,t)}}function z(e,t,n,o){return y?Promise.reject(new a):new Promise((a,l)=>{let u=[];o?.signal&&u.push(o.signal);let d;if(o?.timeout!==void 0){let e=new AbortController;d=setTimeout(()=>e.abort(new r(n)),o.timeout),u.push(e.signal)}let f=u.length===0?c.signal:s(c.signal,...u);if(f.aborted){clearTimeout(d),l(f.reason instanceof r?f.reason:new i);return}let p=()=>{},h=()=>{clearTimeout(d),f.removeEventListener(`abort`,p),a()};p=()=>{clearTimeout(d);let n=e.get(t);n?.delete(h),n?.size===0&&e.delete(t),l(f.reason instanceof r?f.reason:new i)};let g=e.get(t),_=!g;g||(g=new Set,e.set(t,g)),g.add(h),f.addEventListener(`abort`,p,{once:!0});let y=()=>A(m({room:t,type:n}));_&&(v?.readyState===WebSocket.OPEN?y():B.connect().then(y).catch(n=>{clearTimeout(d),f.removeEventListener(`abort`,p);let r=e.get(t);r?.delete(h),r?.size===0&&e.delete(t),l(n)}))})}let B={channel(e){let t=S.get(e);if(t)return t;y&&`${e}`;let n=p(e,(e,t,n)=>{N(t,n,()=>{A(m({channel:e,event:t,payload:n,type:`message`}))})},(e,t,n)=>d.add(e,t,n),c.signal,()=>{S.delete(e),A(m({channel:e,type:`unsubscribe`}))});return y||S.set(e,n),v?.readyState===WebSocket.OPEN&&v.send(m({channel:e,type:`subscribe`})),n},connect(){return y?Promise.reject(new a):v?.readyState===WebSocket.OPEN?Promise.resolve():new Promise((e,t)=>{x.push({reject:t,resolve:e}),(!v||v.readyState>WebSocket.OPEN)&&I()})},disconnect(e=1e3,t=``){b=!0,v?.close(e,t)},get disposalSignal(){return c.signal},dispose(){if(y)return;y=!0,b=!0,k.stop(),v?.close(1e3,`disposed`),v=null;let e=x.splice(0);for(let t of e)t.reject(new a);S.clear(),C.clear(),g.clear(),_.clear(),d.clear(),c.abort()},get disposed(){return y},join(e,t){return z(g,e,`join`,t)},leave(e,t){return z(_,e,`leave`,t)},on(e,t){return y?(`${String(e)}`,()=>{}):d.add(null,e,t)},once(e,t){if(y)return`${String(e)}`,()=>{};let n=()=>{};return n=d.add(null,e,e=>{n(),t(e)}),n},presence(e){let t=C.get(e);if(t)return t;let n=M(e,A,{onJoin:e=>d.add(null,`presence_join`,t=>e(t)),onLeave:e=>d.add(null,`presence_leave`,t=>e(t)),onState:e=>d.add(null,`presence_state`,t=>e(t))},c.signal,()=>{C.delete(e)});return y||C.set(e,n),y?`${e}`:B.join(e).catch(t=>{t instanceof i||t instanceof a||`${e}${String(t)}`}),n},get rooms(){return u},send(e,t){if(y){`${String(e)}`;return}N(e,t,()=>{A(m({event:e,payload:t,type:`message`}))})},get status(){return l},[Symbol.dispose](){this.dispose()},wait(e,t){return f(e,c.signal,t,(e,t)=>d.add(null,e,t))}};return o.lazy||I(),B}exports.PulseAbortError=i,exports.PulseConnectionError=n,exports.PulseDisposedError=a,exports.PulseError=t,exports.PulseProtocolError=o,exports.PulseTimeoutError=r,exports.createPulse=I;
|
|
2
2
|
//# sourceMappingURL=pulse.cjs.map
|