@vielzeug/pulse 2.4.0 → 26.10.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 +12 -20
- package/dist/_connection.cjs +1 -1
- package/dist/_connection.cjs.map +1 -1
- package/dist/_connection.js +1 -106
- package/dist/_connection.js.map +1 -1
- package/dist/_store.cjs +2 -0
- package/dist/_store.cjs.map +1 -0
- package/dist/_store.d.ts +6 -0
- package/dist/_store.d.ts.map +1 -0
- package/dist/_store.js +2 -0
- package/dist/_store.js.map +1 -0
- package/dist/_utils.cjs +1 -1
- package/dist/_utils.cjs.map +1 -1
- package/dist/_utils.js +1 -24
- package/dist/_utils.js.map +1 -1
- package/dist/_wait.cjs +1 -1
- package/dist/_wait.cjs.map +1 -1
- package/dist/_wait.js +1 -27
- package/dist/_wait.js.map +1 -1
- package/dist/channel.cjs +1 -1
- package/dist/channel.cjs.map +1 -1
- package/dist/channel.js +1 -56
- package/dist/channel.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js +1 -37
- package/dist/errors.js.map +1 -1
- package/dist/heartbeat.cjs +1 -1
- package/dist/heartbeat.cjs.map +1 -1
- package/dist/heartbeat.js +1 -35
- package/dist/heartbeat.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/protocol.cjs +1 -1
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.js +1 -121
- package/dist/protocol.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/dist/room.cjs +1 -1
- package/dist/room.cjs.map +1 -1
- package/dist/room.d.ts +2 -2
- package/dist/room.d.ts.map +1 -1
- package/dist/room.js +1 -255
- package/dist/room.js.map +1 -1
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/mock-websocket.cjs +2 -0
- package/dist/testing/mock-websocket.cjs.map +1 -0
- package/dist/testing/mock-websocket.d.ts +48 -0
- package/dist/testing/mock-websocket.d.ts.map +1 -0
- package/dist/testing/mock-websocket.js +2 -0
- package/dist/testing/mock-websocket.js.map +1 -0
- package/dist/testing.cjs +1 -0
- package/dist/testing.js +1 -0
- package/dist/types.d.ts +8 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -8
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# @vielzeug/pulse
|
|
2
2
|
|
|
3
|
-
Typed WebSocket
|
|
3
|
+
> Typed WebSocket client with channels, rooms, presence, reconnect
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
pnpm add @vielzeug/pulse
|
|
8
|
+
pnpm add @vielzeug/pulse
|
|
9
|
+
npm install @vielzeug/pulse
|
|
10
|
+
yarn add @vielzeug/pulse
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
## Quick Start
|
|
@@ -50,24 +52,14 @@ try {
|
|
|
50
52
|
pulse.dispose();
|
|
51
53
|
```
|
|
52
54
|
|
|
53
|
-
## Key Behavior
|
|
54
|
-
|
|
55
|
-
- Call `connect()` before sending messages or publishing presence. Room scopes can be created before connecting — joins are sent after the connection opens.
|
|
56
|
-
- Define server events, client events, channel schemas, and room schemas at `createPulse()` so named scopes are type-safe.
|
|
57
|
-
- Each `channel()` and `room()` call returns an independent disposable scope. Server subscriptions and room memberships use reference counting.
|
|
58
|
-
- Reconnect restores channels, room memberships, and the last successfully published local presence state.
|
|
59
|
-
- `send()` throws `PulseConnectionError` while disconnected; Pulse never silently drops or buffers application messages.
|
|
60
|
-
- `room()` returns a `RoomScope` with a `joined` promise. When the room definition includes `presence`, the scope also exposes reactive presence state, `updatePresence()`, and `onJoin()`/`onLeave()` handlers.
|
|
61
|
-
- Call `tap()` to observe lifecycle events; Pulse reports transport and protocol errors there rather than throwing asynchronously.
|
|
62
|
-
|
|
63
|
-
## Migration
|
|
64
|
-
|
|
65
|
-
See the [Pulse migration guide](https://vielzeug.dev/pulse/migration).
|
|
66
|
-
|
|
67
55
|
## Documentation
|
|
68
56
|
|
|
69
57
|
- [Overview](https://vielzeug.dev/pulse/)
|
|
70
|
-
- [Usage](https://vielzeug.dev/pulse/usage)
|
|
71
|
-
- [API](https://vielzeug.dev/pulse/api)
|
|
58
|
+
- [Usage Guide](https://vielzeug.dev/pulse/usage)
|
|
59
|
+
- [API Reference](https://vielzeug.dev/pulse/api)
|
|
72
60
|
- [Examples](https://vielzeug.dev/pulse/examples)
|
|
73
|
-
- [
|
|
61
|
+
- [Migration Guide](https://vielzeug.dev/pulse/migration)
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT © [Helmuth Saatkamp](https://github.com/helmuthdu) — part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
|
package/dist/_connection.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs"),t=require("./_utils.cjs");var n=5;function
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./_utils.cjs");var n=5;function createConnection(r,i,a,o){let s=a===!0?{}:a||void 0,c=s?s.maxAttempts??n:0,l=s?.delay,u=typeof l==`function`?l:typeof l==`number`?()=>l:t.defaultReconnectDelay,d=!1,f=!1,p=!1,m,h,g,_=new Set;function setStatus(e){o.onStatus(e)}function rejectWaiters(e){for(let t of _)t.reject(e);_.clear()}function resolveWaiters(){for(let e of _)e.resolve();_.clear()}function rejectOpening(e,t){g?.socket===e&&(g.reject(t),g=void 0)}function openSocket(){if(h?.readyState===WebSocket.OPEN)return Promise.resolve();if(g)return g.promise;let t=new WebSocket(r,i);h=t;let n,a,s=new Promise((e,t)=>{n=e,a=t});return g={promise:s,reject:a,resolve:n,socket:t},t.onopen=()=>{if(h!==t||!f){t.close(1e3,`stale connection`);return}g?.socket===t&&(g.resolve(),g=void 0),setStatus(`open`),o.onOpen(),resolveWaiters()},t.onmessage=e=>{h===t&&o.onMessage(e)},t.onerror=()=>{if(h!==t)return;let n=new e.PulseConnectionError(`WebSocket error`,r);o.onError(n),rejectOpening(t,n),rejectWaiters(n)},t.onclose=()=>{if(h!==t)return;h=void 0,o.onClose();let n=new e.PulseConnectionError(`Connection closed`,r);if(rejectOpening(t,n),!f||d){setStatus(`closed`);return}p||reconnectLoop()},s}async function reconnectLoop(){if(p||d||!f)return;p=!0;let n=new AbortController;m=n,setStatus(`reconnecting`);let i;for(let a=0;a<c&&f&&!d&&(await t.sleep(u(a),n.signal),!(n.signal.aborted||!f||d));a++)try{await openSocket(),p=!1;return}catch(t){i=t instanceof e.PulseConnectionError?t:new e.PulseConnectionError(`Reconnect failed`,r)}!n.signal.aborted&&f&&!d&&(f=!1,setStatus(`closed`),o.onError(i??new e.PulseConnectionError(`Reconnect budget exhausted`,r)),rejectWaiters(i??new e.PulseConnectionError(`Reconnect budget exhausted`,r))),m===n&&(m=void 0),p=!1}function cancelRetry(){m?.abort(),m=void 0,p=!1}return{connect(){if(d)return Promise.reject(new e.PulseDisposedError);if(f=!0,h?.readyState===WebSocket.OPEN)return Promise.resolve();let t=new Promise((e,t)=>{_.add({reject:t,resolve:e})});return!g&&!p&&(setStatus(`connecting`),openSocket().catch(()=>{})),t},disconnect(t=1e3,n=``){f=!1,cancelRetry(),rejectWaiters(new e.PulseConnectionError(`Connection closed`,r)),setStatus(`closed`),h?.close(t,n)},dispose(){d||(d=!0,f=!1,cancelRetry(),rejectWaiters(new e.PulseDisposedError),h?.close(1e3,`disposed`),h=void 0,g=void 0,setStatus(`closed`))},forceReconnect(e,t){f&&h?.readyState===WebSocket.OPEN&&h.close(e,t)},get open(){return h?.readyState===WebSocket.OPEN},send(t){if(h?.readyState!==WebSocket.OPEN)throw new e.PulseConnectionError(`Connection is not open`,r);h.send(t)}}}exports.createConnection=createConnection;
|
|
2
2
|
//# sourceMappingURL=_connection.cjs.map
|
package/dist/_connection.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_connection.cjs","names":[],"sources":["../src/_connection.ts"],"sourcesContent":["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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"_connection.cjs","names":[],"sources":["../src/_connection.ts"],"sourcesContent":["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"],"mappings":"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"}
|
package/dist/_connection.js
CHANGED
|
@@ -1,107 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { defaultReconnectDelay as n, sleep as r } from "./_utils.js";
|
|
3
|
-
//#region src/_connection.ts
|
|
4
|
-
var i = 5;
|
|
5
|
-
function a(a, o, s, c) {
|
|
6
|
-
let l = s === !0 ? {} : s || void 0, u = l ? l.maxAttempts ?? i : 0, d = l?.delay, f = typeof d == "function" ? d : typeof d == "number" ? () => d : n, p = !1, m = !1, h = !1, g, _, v, y = /* @__PURE__ */ new Set();
|
|
7
|
-
function b(e) {
|
|
8
|
-
c.onStatus(e);
|
|
9
|
-
}
|
|
10
|
-
function x(e) {
|
|
11
|
-
for (let t of y) t.reject(e);
|
|
12
|
-
y.clear();
|
|
13
|
-
}
|
|
14
|
-
function S() {
|
|
15
|
-
for (let e of y) e.resolve();
|
|
16
|
-
y.clear();
|
|
17
|
-
}
|
|
18
|
-
function C(e, t) {
|
|
19
|
-
v?.socket === e && (v.reject(t), v = void 0);
|
|
20
|
-
}
|
|
21
|
-
function w() {
|
|
22
|
-
if (_?.readyState === WebSocket.OPEN) return Promise.resolve();
|
|
23
|
-
if (v) return v.promise;
|
|
24
|
-
let t = new WebSocket(a, o);
|
|
25
|
-
_ = t;
|
|
26
|
-
let n, r, i = new Promise((e, t) => {
|
|
27
|
-
n = e, r = t;
|
|
28
|
-
});
|
|
29
|
-
return v = {
|
|
30
|
-
promise: i,
|
|
31
|
-
reject: r,
|
|
32
|
-
resolve: n,
|
|
33
|
-
socket: t
|
|
34
|
-
}, t.onopen = () => {
|
|
35
|
-
if (_ !== t || !m) {
|
|
36
|
-
t.close(1e3, "stale connection");
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
v?.socket === t && (v.resolve(), v = void 0), b("open"), c.onOpen(), S();
|
|
40
|
-
}, t.onmessage = (e) => {
|
|
41
|
-
_ === t && c.onMessage(e);
|
|
42
|
-
}, t.onerror = () => {
|
|
43
|
-
if (_ !== t) return;
|
|
44
|
-
let n = new e("WebSocket error", a);
|
|
45
|
-
c.onError(n), C(t, n), x(n);
|
|
46
|
-
}, t.onclose = () => {
|
|
47
|
-
if (_ !== t) return;
|
|
48
|
-
_ = void 0, c.onClose();
|
|
49
|
-
let n = new e("Connection closed", a);
|
|
50
|
-
if (C(t, n), !m || p) {
|
|
51
|
-
b("closed");
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
h || T();
|
|
55
|
-
}, i;
|
|
56
|
-
}
|
|
57
|
-
async function T() {
|
|
58
|
-
if (h || p || !m) return;
|
|
59
|
-
h = !0;
|
|
60
|
-
let t = new AbortController();
|
|
61
|
-
g = t, b("reconnecting");
|
|
62
|
-
let n;
|
|
63
|
-
for (let i = 0; i < u && m && !p && (await r(f(i), t.signal), !(t.signal.aborted || !m || p)); i++) try {
|
|
64
|
-
await w(), h = !1;
|
|
65
|
-
return;
|
|
66
|
-
} catch (t) {
|
|
67
|
-
n = t instanceof e ? t : new e("Reconnect failed", a);
|
|
68
|
-
}
|
|
69
|
-
!t.signal.aborted && m && !p && (m = !1, b("closed"), c.onError(n ?? new e("Reconnect budget exhausted", a)), x(n ?? new e("Reconnect budget exhausted", a))), g === t && (g = void 0), h = !1;
|
|
70
|
-
}
|
|
71
|
-
function E() {
|
|
72
|
-
g?.abort(), g = void 0, h = !1;
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
connect() {
|
|
76
|
-
if (p) return Promise.reject(new t());
|
|
77
|
-
if (m = !0, _?.readyState === WebSocket.OPEN) return Promise.resolve();
|
|
78
|
-
let e = new Promise((e, t) => {
|
|
79
|
-
y.add({
|
|
80
|
-
reject: t,
|
|
81
|
-
resolve: e
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
return !v && !h && (b("connecting"), w().catch(() => {})), e;
|
|
85
|
-
},
|
|
86
|
-
disconnect(t = 1e3, n = "") {
|
|
87
|
-
m = !1, E(), x(new e("Connection closed", a)), b("closed"), _?.close(t, n);
|
|
88
|
-
},
|
|
89
|
-
dispose() {
|
|
90
|
-
p || (p = !0, m = !1, E(), x(new t()), _?.close(1e3, "disposed"), _ = void 0, v = void 0, b("closed"));
|
|
91
|
-
},
|
|
92
|
-
forceReconnect(e, t) {
|
|
93
|
-
m && _?.readyState === WebSocket.OPEN && _.close(e, t);
|
|
94
|
-
},
|
|
95
|
-
get open() {
|
|
96
|
-
return _?.readyState === WebSocket.OPEN;
|
|
97
|
-
},
|
|
98
|
-
send(t) {
|
|
99
|
-
if (_?.readyState !== WebSocket.OPEN) throw new e("Connection is not open", a);
|
|
100
|
-
_.send(t);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
//#endregion
|
|
105
|
-
export { a as createConnection };
|
|
106
|
-
|
|
1
|
+
import{PulseConnectionError as e,PulseDisposedError as t}from"./errors.js";import{defaultReconnectDelay as n,sleep as r}from"./_utils.js";var i=5;function createConnection(a,o,s,c){let l=s===!0?{}:s||void 0,u=l?l.maxAttempts??i:0,d=l?.delay,f=typeof d==`function`?d:typeof d==`number`?()=>d:n,p=!1,m=!1,h=!1,g,_,v,y=new Set;function setStatus(e){c.onStatus(e)}function rejectWaiters(e){for(let t of y)t.reject(e);y.clear()}function resolveWaiters(){for(let e of y)e.resolve();y.clear()}function rejectOpening(e,t){v?.socket===e&&(v.reject(t),v=void 0)}function openSocket(){if(_?.readyState===WebSocket.OPEN)return Promise.resolve();if(v)return v.promise;let t=new WebSocket(a,o);_=t;let n,r,i=new Promise((e,t)=>{n=e,r=t});return v={promise:i,reject:r,resolve:n,socket:t},t.onopen=()=>{if(_!==t||!m){t.close(1e3,`stale connection`);return}v?.socket===t&&(v.resolve(),v=void 0),setStatus(`open`),c.onOpen(),resolveWaiters()},t.onmessage=e=>{_===t&&c.onMessage(e)},t.onerror=()=>{if(_!==t)return;let n=new e(`WebSocket error`,a);c.onError(n),rejectOpening(t,n),rejectWaiters(n)},t.onclose=()=>{if(_!==t)return;_=void 0,c.onClose();let n=new e(`Connection closed`,a);if(rejectOpening(t,n),!m||p){setStatus(`closed`);return}h||reconnectLoop()},i}async function reconnectLoop(){if(h||p||!m)return;h=!0;let t=new AbortController;g=t,setStatus(`reconnecting`);let n;for(let i=0;i<u&&m&&!p&&(await r(f(i),t.signal),!(t.signal.aborted||!m||p));i++)try{await openSocket(),h=!1;return}catch(t){n=t instanceof e?t:new e(`Reconnect failed`,a)}!t.signal.aborted&&m&&!p&&(m=!1,setStatus(`closed`),c.onError(n??new e(`Reconnect budget exhausted`,a)),rejectWaiters(n??new e(`Reconnect budget exhausted`,a))),g===t&&(g=void 0),h=!1}function cancelRetry(){g?.abort(),g=void 0,h=!1}return{connect(){if(p)return Promise.reject(new t);if(m=!0,_?.readyState===WebSocket.OPEN)return Promise.resolve();let e=new Promise((e,t)=>{y.add({reject:t,resolve:e})});return!v&&!h&&(setStatus(`connecting`),openSocket().catch(()=>{})),e},disconnect(t=1e3,n=``){m=!1,cancelRetry(),rejectWaiters(new e(`Connection closed`,a)),setStatus(`closed`),_?.close(t,n)},dispose(){p||(p=!0,m=!1,cancelRetry(),rejectWaiters(new t),_?.close(1e3,`disposed`),_=void 0,v=void 0,setStatus(`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 e(`Connection is not open`,a);_.send(t)}}}export{createConnection};
|
|
107
2
|
//# sourceMappingURL=_connection.js.map
|
package/dist/_connection.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_connection.js","names":[],"sources":["../src/_connection.ts"],"sourcesContent":["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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"_connection.js","names":[],"sources":["../src/_connection.ts"],"sourcesContent":["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"],"mappings":"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"}
|
package/dist/_store.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_store.cjs","names":[],"sources":["../src/_store.ts"],"sourcesContent":["import type { Subscribable, Unsubscribe } from './types';\n\nexport type MutableStore<T> = Subscribable<T> & {\n set(next: T): void;\n};\n\nexport function createStore<T>(initial: T, onListenerError: (error: unknown) => void): MutableStore<T> {\n let current = initial;\n const listeners = new Set<() => void>();\n\n return {\n getSnapshot: () => current,\n set(next) {\n if (Object.is(current, next)) return;\n current = next;\n\n for (const listener of [...listeners]) {\n try {\n listener();\n } catch (error) {\n onListenerError(error);\n }\n }\n },\n subscribe(listener): Unsubscribe {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n"],"mappings":"AAMA,SAAgB,YAAe,EAAY,EAA4D,CACrG,IAAI,EAAU,EACR,EAAY,IAAI,IAEtB,MAAO,CACL,gBAAmB,EACnB,IAAI,EAAM,CACJ,WAAO,GAAG,EAAS,CAAI,EAC3B,GAAU,EAEV,IAAK,IAAM,IAAY,CAAC,GAAG,CAAS,EAClC,GAAI,CACF,EAAS,CACX,OAAS,EAAO,CACd,EAAgB,CAAK,CACvB,CAPQ,CASZ,EACA,UAAU,EAAuB,CAE/B,OADA,EAAU,IAAI,CAAQ,MACT,EAAU,OAAO,CAAQ,CACxC,CACF,CACF"}
|
package/dist/_store.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Subscribable } from './types';
|
|
2
|
+
export type MutableStore<T> = Subscribable<T> & {
|
|
3
|
+
set(next: T): void;
|
|
4
|
+
};
|
|
5
|
+
export declare function createStore<T>(initial: T, onListenerError: (error: unknown) => void): MutableStore<T>;
|
|
6
|
+
//# sourceMappingURL=_store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_store.d.ts","sourceRoot":"","sources":["../src/_store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,SAAS,CAAC;AAEzD,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG;IAC9C,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;CACpB,CAAC;AAEF,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,CAuBrG"}
|
package/dist/_store.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_store.js","names":[],"sources":["../src/_store.ts"],"sourcesContent":["import type { Subscribable, Unsubscribe } from './types';\n\nexport type MutableStore<T> = Subscribable<T> & {\n set(next: T): void;\n};\n\nexport function createStore<T>(initial: T, onListenerError: (error: unknown) => void): MutableStore<T> {\n let current = initial;\n const listeners = new Set<() => void>();\n\n return {\n getSnapshot: () => current,\n set(next) {\n if (Object.is(current, next)) return;\n current = next;\n\n for (const listener of [...listeners]) {\n try {\n listener();\n } catch (error) {\n onListenerError(error);\n }\n }\n },\n subscribe(listener): Unsubscribe {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n"],"mappings":"AAMA,SAAgB,YAAe,EAAY,EAA4D,CACrG,IAAI,EAAU,EACR,EAAY,IAAI,IAEtB,MAAO,CACL,gBAAmB,EACnB,IAAI,EAAM,CACJ,WAAO,GAAG,EAAS,CAAI,EAC3B,GAAU,EAEV,IAAK,IAAM,IAAY,CAAC,GAAG,CAAS,EAClC,GAAI,CACF,EAAS,CACX,OAAS,EAAO,CACd,EAAgB,CAAK,CACvB,CAPQ,CASZ,EACA,UAAU,EAAuB,CAE/B,OADA,EAAU,IAAI,CAAQ,MACT,EAAU,OAAO,CAAQ,CACxC,CACF,CACF"}
|
package/dist/_utils.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
let e=require("@vielzeug/arsenal");function deriveAbortController(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function sleep(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 defaultReconnectDelay(t,n=3e4){return Math.random()*(0,e.backoff)(t,n)}exports.defaultReconnectDelay=defaultReconnectDelay,exports.deriveAbortController=deriveAbortController,exports.sleep=sleep;
|
|
2
2
|
//# sourceMappingURL=_utils.cjs.map
|
package/dist/_utils.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_utils.cjs","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["/**\n * Create a child `AbortController` that aborts as soon as `parent` does.\n * Handles the case where `parent` is already aborted at call time — a plain\n * `addEventListener('abort', ...)` would miss that, since the event already fired.\n *\n * @internal\n */\nexport function deriveAbortController(parent: AbortSignal): AbortController {\n const ctrl = new AbortController();\n\n if (parent.aborted) {\n ctrl.abort(parent.reason);\n } else {\n parent.addEventListener('abort', () => ctrl.abort(parent.reason), { once: true });\n }\n\n return ctrl;\n}\n\n/**\n * Sleep for `ms` milliseconds, aborting early when the signal fires.\n * Resolves (does not reject) on abort — callers check `signal.aborted`.\n *\n * @internal\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n\n return;\n }\n\n const id = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(id);\n resolve();\n },\n { once: true },\n );\n });\n}\n\n/**\n * Full-jitter exponential backoff in ms, capped at `maxMs`.\n * `attempt` is zero-based.\n *\n * @internal\n */\nexport function defaultReconnectDelay(attempt: number, maxMs = 30_000): number {\n
|
|
1
|
+
{"version":3,"file":"_utils.cjs","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["import { backoff } from '@vielzeug/arsenal';\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 return Math.random() * backoff(attempt, maxMs);\n}\n"],"mappings":"mCASA,SAAgB,sBAAsB,EAAsC,CAC1E,IAAM,EAAO,IAAI,gBAQjB,OANI,EAAO,QACT,EAAK,MAAM,EAAO,MAAM,EAExB,EAAO,iBAAiB,YAAe,EAAK,MAAM,EAAO,MAAM,EAAG,CAAE,KAAM,EAAK,CAAC,EAG3E,CACT,CAQA,SAAgB,MAAM,EAAY,EAAqC,CACrE,OAAO,IAAI,QAAS,GAAY,CAC9B,GAAI,GAAQ,QAAS,CACnB,EAAQ,EAER,MACF,CAEA,IAAM,EAAK,WAAW,EAAS,CAAE,EAEjC,GAAQ,iBACN,YACM,CACJ,aAAa,CAAE,EACf,EAAQ,CACV,EACA,CAAE,KAAM,EAAK,CACf,CACF,CAAC,CACH,CAQA,SAAgB,sBAAsB,EAAiB,EAAQ,IAAgB,CAC7E,OAAO,KAAK,OAAO,GAAA,EAAI,EAAA,QAAA,CAAQ,EAAS,CAAK,CAC/C"}
|
package/dist/_utils.js
CHANGED
|
@@ -1,25 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
function e(e) {
|
|
3
|
-
let t = new AbortController();
|
|
4
|
-
return e.aborted ? t.abort(e.reason) : e.addEventListener("abort", () => t.abort(e.reason), { once: !0 }), t;
|
|
5
|
-
}
|
|
6
|
-
function t(e, t) {
|
|
7
|
-
return new Promise((n) => {
|
|
8
|
-
if (t?.aborted) {
|
|
9
|
-
n();
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
let r = setTimeout(n, e);
|
|
13
|
-
t?.addEventListener("abort", () => {
|
|
14
|
-
clearTimeout(r), n();
|
|
15
|
-
}, { once: !0 });
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
function n(e, t = 3e4) {
|
|
19
|
-
let n = Math.min(1e3 * 2 ** e, t);
|
|
20
|
-
return Math.random() * n;
|
|
21
|
-
}
|
|
22
|
-
//#endregion
|
|
23
|
-
export { n as defaultReconnectDelay, e as deriveAbortController, t as sleep };
|
|
24
|
-
|
|
1
|
+
import{backoff as e}from"@vielzeug/arsenal";function deriveAbortController(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function sleep(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 defaultReconnectDelay(t,n=3e4){return Math.random()*e(t,n)}export{defaultReconnectDelay,deriveAbortController,sleep};
|
|
25
2
|
//# sourceMappingURL=_utils.js.map
|
package/dist/_utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_utils.js","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["/**\n * Create a child `AbortController` that aborts as soon as `parent` does.\n * Handles the case where `parent` is already aborted at call time — a plain\n * `addEventListener('abort', ...)` would miss that, since the event already fired.\n *\n * @internal\n */\nexport function deriveAbortController(parent: AbortSignal): AbortController {\n const ctrl = new AbortController();\n\n if (parent.aborted) {\n ctrl.abort(parent.reason);\n } else {\n parent.addEventListener('abort', () => ctrl.abort(parent.reason), { once: true });\n }\n\n return ctrl;\n}\n\n/**\n * Sleep for `ms` milliseconds, aborting early when the signal fires.\n * Resolves (does not reject) on abort — callers check `signal.aborted`.\n *\n * @internal\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n\n return;\n }\n\n const id = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(id);\n resolve();\n },\n { once: true },\n );\n });\n}\n\n/**\n * Full-jitter exponential backoff in ms, capped at `maxMs`.\n * `attempt` is zero-based.\n *\n * @internal\n */\nexport function defaultReconnectDelay(attempt: number, maxMs = 30_000): number {\n
|
|
1
|
+
{"version":3,"file":"_utils.js","names":[],"sources":["../src/_utils.ts"],"sourcesContent":["import { backoff } from '@vielzeug/arsenal';\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 return Math.random() * backoff(attempt, maxMs);\n}\n"],"mappings":"4CASA,SAAgB,sBAAsB,EAAsC,CAC1E,IAAM,EAAO,IAAI,gBAQjB,OANI,EAAO,QACT,EAAK,MAAM,EAAO,MAAM,EAExB,EAAO,iBAAiB,YAAe,EAAK,MAAM,EAAO,MAAM,EAAG,CAAE,KAAM,EAAK,CAAC,EAG3E,CACT,CAQA,SAAgB,MAAM,EAAY,EAAqC,CACrE,OAAO,IAAI,QAAS,GAAY,CAC9B,GAAI,GAAQ,QAAS,CACnB,EAAQ,EAER,MACF,CAEA,IAAM,EAAK,WAAW,EAAS,CAAE,EAEjC,GAAQ,iBACN,YACM,CACJ,aAAa,CAAE,EACf,EAAQ,CACV,EACA,CAAE,KAAM,EAAK,CACf,CACF,CAAC,CACH,CAQA,SAAgB,sBAAsB,EAAiB,EAAQ,IAAgB,CAC7E,OAAO,KAAK,OAAO,EAAI,EAAQ,EAAS,CAAK,CAC/C"}
|
package/dist/_wait.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs");function
|
|
1
|
+
const e=require("./errors.cjs");function createWaitPromise(t,n,r,i){return new Promise((a,o)=>{let s=[n],c;if(r?.signal&&s.push(r.signal),r?.timeout!==void 0){let n=new AbortController;c=setTimeout(()=>{n.abort(new e.PulseTimeoutError(t))},r.timeout),s.push(n.signal)}let l=AbortSignal.any(s);if(l.aborted){clearTimeout(c),o(l.reason instanceof e.PulseTimeoutError?l.reason:new e.PulseAbortError);return}let unsub=()=>{},onAbort=()=>{clearTimeout(c),unsub(),o(l.reason instanceof e.PulseTimeoutError?l.reason:new e.PulseAbortError)};l.addEventListener(`abort`,onAbort,{once:!0}),unsub=i(t,e=>{clearTimeout(c),l.removeEventListener(`abort`,onAbort),a(e)})})}exports.createWaitPromise=createWaitPromise;
|
|
2
2
|
//# sourceMappingURL=_wait.cjs.map
|
package/dist/_wait.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_wait.cjs","names":[],"sources":["../src/_wait.ts"],"sourcesContent":["import { PulseAbortError, PulseTimeoutError } from './errors';\nimport type { Unsubscribe } from './types';\n\n/**\n * Shared implementation for `pulse.wait()` and `channel.wait()`.\n * Resolves on the next event dispatched via `subscribe`, or rejects when\n * the combined signal aborts or the optional timeout elapses.\n *\n * @internal\n */\nexport function createWaitPromise<T>(\n event: string,\n baseSignal: AbortSignal,\n opts: { signal?: AbortSignal; timeout?: number } | undefined,\n subscribe: (event: string, handler: (payload: unknown) => void) => Unsubscribe,\n): Promise<T> {\n return new Promise((resolve, reject) => {\n const signals: AbortSignal[] = [baseSignal];\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if (opts?.signal) signals.push(opts.signal);\n\n if (opts?.timeout !== undefined) {\n const timeoutCtrl = new AbortController();\n\n timeoutId = setTimeout(() => {\n timeoutCtrl.abort(new PulseTimeoutError(event));\n }, opts.timeout);\n signals.push(timeoutCtrl.signal);\n }\n\n const combined = AbortSignal.any(signals);\n\n if (combined.aborted) {\n clearTimeout(timeoutId);\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n\n return;\n }\n\n let unsub: Unsubscribe = () => {};\n\n const onAbort = (): void => {\n clearTimeout(timeoutId);\n unsub();\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n };\n\n combined.addEventListener('abort', onAbort, { once: true });\n\n unsub = subscribe(event, (payload) => {\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n resolve(payload as T);\n });\n });\n}\n"],"mappings":"gCAUA,SAAgB,
|
|
1
|
+
{"version":3,"file":"_wait.cjs","names":[],"sources":["../src/_wait.ts"],"sourcesContent":["import { PulseAbortError, PulseTimeoutError } from './errors';\nimport type { Unsubscribe } from './types';\n\n/**\n * Shared implementation for `pulse.wait()` and `channel.wait()`.\n * Resolves on the next event dispatched via `subscribe`, or rejects when\n * the combined signal aborts or the optional timeout elapses.\n *\n * @internal\n */\nexport function createWaitPromise<T>(\n event: string,\n baseSignal: AbortSignal,\n opts: { signal?: AbortSignal; timeout?: number } | undefined,\n subscribe: (event: string, handler: (payload: unknown) => void) => Unsubscribe,\n): Promise<T> {\n return new Promise((resolve, reject) => {\n const signals: AbortSignal[] = [baseSignal];\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if (opts?.signal) signals.push(opts.signal);\n\n if (opts?.timeout !== undefined) {\n const timeoutCtrl = new AbortController();\n\n timeoutId = setTimeout(() => {\n timeoutCtrl.abort(new PulseTimeoutError(event));\n }, opts.timeout);\n signals.push(timeoutCtrl.signal);\n }\n\n const combined = AbortSignal.any(signals);\n\n if (combined.aborted) {\n clearTimeout(timeoutId);\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n\n return;\n }\n\n let unsub: Unsubscribe = () => {};\n\n const onAbort = (): void => {\n clearTimeout(timeoutId);\n unsub();\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n };\n\n combined.addEventListener('abort', onAbort, { once: true });\n\n unsub = subscribe(event, (payload) => {\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n resolve(payload as T);\n });\n });\n}\n"],"mappings":"gCAUA,SAAgB,kBACd,EACA,EACA,EACA,EACY,CACZ,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAAyB,CAAC,CAAU,EAEtC,EAIJ,GAFI,GAAM,QAAQ,EAAQ,KAAK,EAAK,MAAM,EAEtC,GAAM,UAAY,IAAA,GAAW,CAC/B,IAAM,EAAc,IAAI,gBAExB,EAAY,eAAiB,CAC3B,EAAY,MAAM,IAAI,EAAA,kBAAkB,CAAK,CAAC,CAChD,EAAG,EAAK,OAAO,EACf,EAAQ,KAAK,EAAY,MAAM,CACjC,CAEA,IAAM,EAAW,YAAY,IAAI,CAAO,EAExC,GAAI,EAAS,QAAS,CACpB,aAAa,CAAS,EACtB,EAAO,EAAS,kBAAkB,EAAA,kBAAoB,EAAS,OAAS,IAAI,EAAA,eAAiB,EAE7F,MACF,CAEA,IAAI,UAA2B,CAAC,EAE1B,YAAsB,CAC1B,aAAa,CAAS,EACtB,MAAM,EACN,EAAO,EAAS,kBAAkB,EAAA,kBAAoB,EAAS,OAAS,IAAI,EAAA,eAAiB,CAC/F,EAEA,EAAS,iBAAiB,QAAS,QAAS,CAAE,KAAM,EAAK,CAAC,EAE1D,MAAQ,EAAU,EAAQ,GAAY,CACpC,aAAa,CAAS,EACtB,EAAS,oBAAoB,QAAS,OAAO,EAC7C,EAAQ,CAAY,CACtB,CAAC,CACH,CAAC,CACH"}
|
package/dist/_wait.js
CHANGED
|
@@ -1,28 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
//#region src/_wait.ts
|
|
3
|
-
function n(n, r, i, a) {
|
|
4
|
-
return new Promise((o, s) => {
|
|
5
|
-
let c = [r], l;
|
|
6
|
-
if (i?.signal && c.push(i.signal), i?.timeout !== void 0) {
|
|
7
|
-
let e = new AbortController();
|
|
8
|
-
l = setTimeout(() => {
|
|
9
|
-
e.abort(new t(n));
|
|
10
|
-
}, i.timeout), c.push(e.signal);
|
|
11
|
-
}
|
|
12
|
-
let u = AbortSignal.any(c);
|
|
13
|
-
if (u.aborted) {
|
|
14
|
-
clearTimeout(l), s(u.reason instanceof t ? u.reason : new e());
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
let d = () => {}, f = () => {
|
|
18
|
-
clearTimeout(l), d(), s(u.reason instanceof t ? u.reason : new e());
|
|
19
|
-
};
|
|
20
|
-
u.addEventListener("abort", f, { once: !0 }), d = a(n, (e) => {
|
|
21
|
-
clearTimeout(l), u.removeEventListener("abort", f), o(e);
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
//#endregion
|
|
26
|
-
export { n as createWaitPromise };
|
|
27
|
-
|
|
1
|
+
import{PulseAbortError as e,PulseTimeoutError as t}from"./errors.js";function createWaitPromise(n,r,i,a){return new Promise((o,s)=>{let c=[r],l;if(i?.signal&&c.push(i.signal),i?.timeout!==void 0){let e=new AbortController;l=setTimeout(()=>{e.abort(new t(n))},i.timeout),c.push(e.signal)}let u=AbortSignal.any(c);if(u.aborted){clearTimeout(l),s(u.reason instanceof t?u.reason:new e);return}let unsub=()=>{},onAbort=()=>{clearTimeout(l),unsub(),s(u.reason instanceof t?u.reason:new e)};u.addEventListener(`abort`,onAbort,{once:!0}),unsub=a(n,e=>{clearTimeout(l),u.removeEventListener(`abort`,onAbort),o(e)})})}export{createWaitPromise};
|
|
28
2
|
//# sourceMappingURL=_wait.js.map
|
package/dist/_wait.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_wait.js","names":[],"sources":["../src/_wait.ts"],"sourcesContent":["import { PulseAbortError, PulseTimeoutError } from './errors';\nimport type { Unsubscribe } from './types';\n\n/**\n * Shared implementation for `pulse.wait()` and `channel.wait()`.\n * Resolves on the next event dispatched via `subscribe`, or rejects when\n * the combined signal aborts or the optional timeout elapses.\n *\n * @internal\n */\nexport function createWaitPromise<T>(\n event: string,\n baseSignal: AbortSignal,\n opts: { signal?: AbortSignal; timeout?: number } | undefined,\n subscribe: (event: string, handler: (payload: unknown) => void) => Unsubscribe,\n): Promise<T> {\n return new Promise((resolve, reject) => {\n const signals: AbortSignal[] = [baseSignal];\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if (opts?.signal) signals.push(opts.signal);\n\n if (opts?.timeout !== undefined) {\n const timeoutCtrl = new AbortController();\n\n timeoutId = setTimeout(() => {\n timeoutCtrl.abort(new PulseTimeoutError(event));\n }, opts.timeout);\n signals.push(timeoutCtrl.signal);\n }\n\n const combined = AbortSignal.any(signals);\n\n if (combined.aborted) {\n clearTimeout(timeoutId);\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n\n return;\n }\n\n let unsub: Unsubscribe = () => {};\n\n const onAbort = (): void => {\n clearTimeout(timeoutId);\n unsub();\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n };\n\n combined.addEventListener('abort', onAbort, { once: true });\n\n unsub = subscribe(event, (payload) => {\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n resolve(payload as T);\n });\n });\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"_wait.js","names":[],"sources":["../src/_wait.ts"],"sourcesContent":["import { PulseAbortError, PulseTimeoutError } from './errors';\nimport type { Unsubscribe } from './types';\n\n/**\n * Shared implementation for `pulse.wait()` and `channel.wait()`.\n * Resolves on the next event dispatched via `subscribe`, or rejects when\n * the combined signal aborts or the optional timeout elapses.\n *\n * @internal\n */\nexport function createWaitPromise<T>(\n event: string,\n baseSignal: AbortSignal,\n opts: { signal?: AbortSignal; timeout?: number } | undefined,\n subscribe: (event: string, handler: (payload: unknown) => void) => Unsubscribe,\n): Promise<T> {\n return new Promise((resolve, reject) => {\n const signals: AbortSignal[] = [baseSignal];\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if (opts?.signal) signals.push(opts.signal);\n\n if (opts?.timeout !== undefined) {\n const timeoutCtrl = new AbortController();\n\n timeoutId = setTimeout(() => {\n timeoutCtrl.abort(new PulseTimeoutError(event));\n }, opts.timeout);\n signals.push(timeoutCtrl.signal);\n }\n\n const combined = AbortSignal.any(signals);\n\n if (combined.aborted) {\n clearTimeout(timeoutId);\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n\n return;\n }\n\n let unsub: Unsubscribe = () => {};\n\n const onAbort = (): void => {\n clearTimeout(timeoutId);\n unsub();\n reject(combined.reason instanceof PulseTimeoutError ? combined.reason : new PulseAbortError());\n };\n\n combined.addEventListener('abort', onAbort, { once: true });\n\n unsub = subscribe(event, (payload) => {\n clearTimeout(timeoutId);\n combined.removeEventListener('abort', onAbort);\n resolve(payload as T);\n });\n });\n}\n"],"mappings":"qEAUA,SAAgB,kBACd,EACA,EACA,EACA,EACY,CACZ,OAAO,IAAI,SAAS,EAAS,IAAW,CACtC,IAAM,EAAyB,CAAC,CAAU,EAEtC,EAIJ,GAFI,GAAM,QAAQ,EAAQ,KAAK,EAAK,MAAM,EAEtC,GAAM,UAAY,IAAA,GAAW,CAC/B,IAAM,EAAc,IAAI,gBAExB,EAAY,eAAiB,CAC3B,EAAY,MAAM,IAAI,EAAkB,CAAK,CAAC,CAChD,EAAG,EAAK,OAAO,EACf,EAAQ,KAAK,EAAY,MAAM,CACjC,CAEA,IAAM,EAAW,YAAY,IAAI,CAAO,EAExC,GAAI,EAAS,QAAS,CACpB,aAAa,CAAS,EACtB,EAAO,EAAS,kBAAkB,EAAoB,EAAS,OAAS,IAAI,CAAiB,EAE7F,MACF,CAEA,IAAI,UAA2B,CAAC,EAE1B,YAAsB,CAC1B,aAAa,CAAS,EACtB,MAAM,EACN,EAAO,EAAS,kBAAkB,EAAoB,EAAS,OAAS,IAAI,CAAiB,CAC/F,EAEA,EAAS,iBAAiB,QAAS,QAAS,CAAE,KAAM,EAAK,CAAC,EAE1D,MAAQ,EAAU,EAAQ,GAAY,CACpC,aAAa,CAAS,EACtB,EAAS,oBAAoB,QAAS,OAAO,EAC7C,EAAQ,CAAY,CACtB,CAAC,CACH,CAAC,CACH"}
|
package/dist/channel.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs"),t=require("./_utils.cjs"),n=require("./_wait.cjs");function
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./_utils.cjs"),n=require("./_wait.cjs");function createChannel(r,i,a,o,s){let c=t.deriveAbortController(o),l=new Set,u=c.signal.aborted;return c.signal.addEventListener(`abort`,()=>{u=!0},{once:!0}),{get disposalSignal(){return c.signal},dispose(){if(!u){u=!0,c.abort();for(let e of l)e();l.clear(),s?.()}},get disposed(){return u},get name(){return r},on(t,n){if(u)throw new e.PulseDisposedError(`Channel "${r}"`);let i=a(r,t,n);return l.add(i),()=>{i(),l.delete(i)}},once(t,n){if(u)throw new e.PulseDisposedError(`Channel "${r}"`);let unsub=()=>{},wrapped=e=>{l.delete(unsub),unsub(),n(e)};return unsub=a(r,t,wrapped),l.add(unsub),()=>{l.delete(unsub),unsub()}},send(t,n){if(u)throw new e.PulseDisposedError(`Channel "${r}"`);i(r,t,n)},[Symbol.dispose](){this.dispose()},wait(e,t){return n.createWaitPromise(e,c.signal,t,(e,t)=>a(r,e,t))}}}exports.createChannel=createChannel;
|
|
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 { 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"],"mappings":"mFAYA,SAAgB,
|
|
1
|
+
{"version":3,"file":"channel.cjs","names":[],"sources":["../src/channel.ts"],"sourcesContent":["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"],"mappings":"mFAYA,SAAgB,cACd,EACA,EACA,EACA,EACA,EACgC,CAChC,IAAM,EAAO,EAAA,sBAAsB,CAAc,EAC3C,EAAU,IAAI,IAEhB,EAAW,EAAK,OAAO,QA8F3B,OA5FA,EAAK,OAAO,iBACV,YACM,CACJ,EAAW,EACb,EACA,CAAE,KAAM,EAAK,CACf,EAsFO,CAnFL,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,EACF,MAAM,IAAI,EAAA,mBAAmB,YAAY,EAAK,EAAE,EAGlD,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,EACF,MAAM,IAAI,EAAA,mBAAmB,YAAY,EAAK,EAAE,EAGlD,IAAI,UAA2B,CAAC,EAE1B,QAAW,GAA2B,CAC1C,EAAQ,OAAO,KAAK,EACpB,MAAM,EACN,EAAwC,CAAO,CACjD,EAKA,MAHA,OAAQ,EAAU,EAAM,EAAO,OAAO,EACtC,EAAQ,IAAI,KAAK,MAEJ,CACX,EAAQ,OAAO,KAAK,EACpB,MAAM,CACR,CACF,EAEA,KAAkC,EAAU,EAA2B,CACrE,GAAI,EACF,MAAM,IAAI,EAAA,mBAAmB,YAAY,EAAK,EAAE,EAGlD,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,57 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { deriveAbortController as t } from "./_utils.js";
|
|
3
|
-
import { createWaitPromise as n } from "./_wait.js";
|
|
4
|
-
//#region src/channel.ts
|
|
5
|
-
function r(r, i, a, o, s) {
|
|
6
|
-
let c = t(o), l = /* @__PURE__ */ new Set(), u = c.signal.aborted;
|
|
7
|
-
return c.signal.addEventListener("abort", () => {
|
|
8
|
-
u = !0;
|
|
9
|
-
}, { once: !0 }), {
|
|
10
|
-
get disposalSignal() {
|
|
11
|
-
return c.signal;
|
|
12
|
-
},
|
|
13
|
-
dispose() {
|
|
14
|
-
if (!u) {
|
|
15
|
-
u = !0, c.abort();
|
|
16
|
-
for (let e of l) e();
|
|
17
|
-
l.clear(), s?.();
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
get disposed() {
|
|
21
|
-
return u;
|
|
22
|
-
},
|
|
23
|
-
get name() {
|
|
24
|
-
return r;
|
|
25
|
-
},
|
|
26
|
-
on(t, n) {
|
|
27
|
-
if (u) throw new e(`Channel "${r}"`);
|
|
28
|
-
let i = a(r, t, n);
|
|
29
|
-
return l.add(i), () => {
|
|
30
|
-
i(), l.delete(i);
|
|
31
|
-
};
|
|
32
|
-
},
|
|
33
|
-
once(t, n) {
|
|
34
|
-
if (u) throw new e(`Channel "${r}"`);
|
|
35
|
-
let i = () => {};
|
|
36
|
-
return i = a(r, t, (e) => {
|
|
37
|
-
l.delete(i), i(), n(e);
|
|
38
|
-
}), l.add(i), () => {
|
|
39
|
-
l.delete(i), i();
|
|
40
|
-
};
|
|
41
|
-
},
|
|
42
|
-
send(t, n) {
|
|
43
|
-
if (u) throw new e(`Channel "${r}"`);
|
|
44
|
-
i(r, t, n);
|
|
45
|
-
},
|
|
46
|
-
[Symbol.dispose]() {
|
|
47
|
-
this.dispose();
|
|
48
|
-
},
|
|
49
|
-
wait(e, t) {
|
|
50
|
-
return n(e, c.signal, t, (e, t) => a(r, e, t));
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
//#endregion
|
|
55
|
-
export { r as createChannel };
|
|
56
|
-
|
|
1
|
+
import{PulseDisposedError as e}from"./errors.js";import{deriveAbortController as t}from"./_utils.js";import{createWaitPromise as n}from"./_wait.js";function createChannel(r,i,a,o,s){let c=t(o),l=new Set,u=c.signal.aborted;return c.signal.addEventListener(`abort`,()=>{u=!0},{once:!0}),{get disposalSignal(){return c.signal},dispose(){if(!u){u=!0,c.abort();for(let e of l)e();l.clear(),s?.()}},get disposed(){return u},get name(){return r},on(t,n){if(u)throw new e(`Channel "${r}"`);let i=a(r,t,n);return l.add(i),()=>{i(),l.delete(i)}},once(t,n){if(u)throw new e(`Channel "${r}"`);let unsub=()=>{},wrapped=e=>{l.delete(unsub),unsub(),n(e)};return unsub=a(r,t,wrapped),l.add(unsub),()=>{l.delete(unsub),unsub()}},send(t,n){if(u)throw new e(`Channel "${r}"`);i(r,t,n)},[Symbol.dispose](){this.dispose()},wait(e,t){return n(e,c.signal,t,(e,t)=>a(r,e,t))}}}export{createChannel};
|
|
57
2
|
//# 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 { 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"],"mappings":"
|
|
1
|
+
{"version":3,"file":"channel.js","names":[],"sources":["../src/channel.ts"],"sourcesContent":["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"],"mappings":"oJAYA,SAAgB,cACd,EACA,EACA,EACA,EACA,EACgC,CAChC,IAAM,EAAO,EAAsB,CAAc,EAC3C,EAAU,IAAI,IAEhB,EAAW,EAAK,OAAO,QA8F3B,OA5FA,EAAK,OAAO,iBACV,YACM,CACJ,EAAW,EACb,EACA,CAAE,KAAM,EAAK,CACf,EAsFO,CAnFL,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,EACF,MAAM,IAAI,EAAmB,YAAY,EAAK,EAAE,EAGlD,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,EACF,MAAM,IAAI,EAAmB,YAAY,EAAK,EAAE,EAGlD,IAAI,UAA2B,CAAC,EAE1B,QAAW,GAA2B,CAC1C,EAAQ,OAAO,KAAK,EACpB,MAAM,EACN,EAAwC,CAAO,CACjD,EAKA,MAHA,OAAQ,EAAU,EAAM,EAAO,OAAO,EACtC,EAAQ,IAAI,KAAK,MAEJ,CACX,EAAQ,OAAO,KAAK,EACpB,MAAM,CACR,CACF,EAEA,KAAkC,EAAU,EAA2B,CACrE,GAAI,EACF,MAAM,IAAI,EAAmB,YAAY,EAAK,EAAE,EAGlD,EAAK,EAAM,EAAO,CAAO,CAC3B,EAEA,CAAC,OAAO,UAAW,CACjB,KAAK,QAAQ,CACf,EAEA,KACE,EACA,EACqB,CACrB,OAAO,EAA8B,EAAiB,EAAK,OAAQ,GAAO,EAAI,IAC5E,EAAU,EAAM,EAAI,CAAO,CAC7B,CACF,CAGK,CACT"}
|
package/dist/errors.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var PulseError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},PulseConnectionError=class extends PulseError{url;constructor(e,t,n){super(e,n),this.url=t}},PulseTimeoutError=class extends PulseError{event;constructor(e,t){super(`Timed out waiting for "${e}"`,t),this.event=e}},PulseRoomTimeoutError=class extends PulseError{room;constructor(e,t){super(`Timed out waiting for room "${e}"`,t),this.room=e}},PulseAbortError=class extends PulseError{constructor(e){super(`Aborted`,e)}},PulseDisposedError=class extends PulseError{constructor(e=`Pulse`,t){super(`${e} instance is disposed`,t)}},PulseProtocolError=class extends PulseError{raw;constructor(e,t,n){super(e,n),this.raw=t}};exports.PulseAbortError=PulseAbortError,exports.PulseConnectionError=PulseConnectionError,exports.PulseDisposedError=PulseDisposedError,exports.PulseError=PulseError,exports.PulseProtocolError=PulseProtocolError,exports.PulseRoomTimeoutError=PulseRoomTimeoutError,exports.PulseTimeoutError=PulseTimeoutError;
|
|
2
2
|
//# sourceMappingURL=errors.cjs.map
|
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\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,
|
|
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,WAAb,cAAgC,KAAM,CACpC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAMa,qBAAb,cAA0C,UAAW,CACnD,IAEA,YAAY,EAAiB,EAAa,EAAqB,CAC7D,MAAM,EAAS,CAAI,EACnB,KAAK,IAAM,CACb,CACF,EAKa,kBAAb,cAAuC,UAAW,CAChD,MAEA,YAAY,EAAe,EAAqB,CAC9C,MAAM,0BAA0B,EAAM,GAAI,CAAI,EAC9C,KAAK,MAAQ,CACf,CACF,EAMa,sBAAb,cAA2C,UAAW,CACpD,KAEA,YAAY,EAAc,EAAqB,CAC7C,MAAM,+BAA+B,EAAK,GAAI,CAAI,EAClD,KAAK,KAAO,CACd,CACF,EAMa,gBAAb,cAAqC,UAAW,CAC9C,YAAY,EAAqB,CAC/B,MAAM,UAAW,CAAI,CACvB,CACF,EAKa,mBAAb,cAAwC,UAAW,CACjD,YAAY,EAAS,QAAS,EAAqB,CACjD,MAAM,GAAG,EAAO,uBAAwB,CAAI,CAC9C,CACF,EAMa,mBAAb,cAAwC,UAAW,CACjD,IAEA,YAAY,EAAiB,EAAe,EAAqB,CAC/D,MAAM,EAAS,CAAI,EACnB,KAAK,IAAM,CACb,CACF"}
|