@wooksjs/ws-client 0.7.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 wooksjs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # @wooksjs/ws-client
2
+
3
+ <p align="center">
4
+ <img src="../../wooks-logo.png" width="450px"><br>
5
+ <a href="https://github.com/wooksjs/wooksjs/blob/main/LICENSE">
6
+ <img src="https://img.shields.io/badge/License-MIT-green?style=for-the-badge" />
7
+ </a>
8
+ </p>
9
+
10
+ WebSocket client for Wooks with RPC, subscriptions, reconnection, push listeners, and message queuing. Works in browsers and Node.js.
11
+
12
+ ## Installation
13
+
14
+ ```sh
15
+ npm install @wooksjs/ws-client
16
+ ```
17
+
18
+ ## Documentation
19
+
20
+ For full documentation, visit [wooks.moost.org](https://wooks.moost.org/wsapp/).
21
+
22
+ ## AI Agent Skills
23
+
24
+ This package ships with structured skill files for AI coding agents (Claude Code, Cursor, Windsurf, Codex, etc.).
25
+
26
+ ```bash
27
+ # Project-local (recommended — version-locked, commits with your repo)
28
+ npx wooksjs-ws-client-skill
29
+
30
+ # Global (available across all your projects)
31
+ npx wooksjs-ws-client-skill --global
32
+ ```
33
+
34
+ To keep skills automatically up-to-date, add a postinstall script to your `package.json`:
35
+
36
+ ```json
37
+ {
38
+ "scripts": {
39
+ "postinstall": "wooksjs-ws-client-skill --postinstall"
40
+ }
41
+ }
42
+ ```
43
+
44
+ ## License
45
+
46
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,409 @@
1
+
2
+ //#region packages/ws-client/src/ws-client-error.ts
3
+ /** Error from the WebSocket client (server error reply, timeout, or disconnect). */
4
+ var WsClientError = class extends Error {
5
+ constructor(code, message) {
6
+ super(message ?? `WsClientError ${code}`);
7
+ this.code = code;
8
+ this.name = "WsClientError";
9
+ }
10
+ };
11
+
12
+ //#endregion
13
+ //#region packages/ws-client/src/rpc-tracker.ts
14
+ /** Tracks pending RPC calls by correlation ID. */
15
+ var RpcTracker = class {
16
+ nextId = 1;
17
+ pending = /* @__PURE__ */ new Map();
18
+ /** Generate a new unique message ID. */
19
+ generateId() {
20
+ return this.nextId++;
21
+ }
22
+ /** Track a new pending RPC. Returns a promise that resolves/rejects based on the reply. */
23
+ track(id, timeout) {
24
+ return new Promise((resolve, reject) => {
25
+ const timer = setTimeout(() => {
26
+ this.pending.delete(id);
27
+ reject(new WsClientError(408, "RPC timeout"));
28
+ }, timeout);
29
+ this.pending.set(id, {
30
+ resolve,
31
+ reject,
32
+ timer
33
+ });
34
+ });
35
+ }
36
+ /** Resolve a pending RPC with a server reply. Returns true if an RPC was found. */
37
+ resolve(reply) {
38
+ const id = typeof reply.id === "number" ? reply.id : Number(reply.id);
39
+ const entry = this.pending.get(id);
40
+ if (!entry) return false;
41
+ clearTimeout(entry.timer);
42
+ this.pending.delete(id);
43
+ if (reply.error) entry.reject(new WsClientError(reply.error.code, reply.error.message));
44
+ else entry.resolve(reply.data);
45
+ return true;
46
+ }
47
+ /** Reject all pending RPCs (on disconnect or close). */
48
+ rejectAll(code, message) {
49
+ for (const [, entry] of this.pending) {
50
+ clearTimeout(entry.timer);
51
+ entry.reject(new WsClientError(code, message));
52
+ }
53
+ this.pending.clear();
54
+ }
55
+ /** Number of pending RPCs. */
56
+ get size() {
57
+ return this.pending.size;
58
+ }
59
+ };
60
+
61
+ //#endregion
62
+ //#region packages/ws-client/src/push-dispatcher.ts
63
+ /** Dispatches server push messages to registered client-side listeners. */
64
+ var PushDispatcher = class {
65
+ /** Exact match: key = "event:path", value = set of handlers. */
66
+ exact = /* @__PURE__ */ new Map();
67
+ /** Wildcard listeners (path ends with "*"). */
68
+ wildcards = [];
69
+ /**
70
+ * Register a push listener. Returns an unregister function.
71
+ *
72
+ * - Exact path: `"/chat/rooms/lobby"` — O(1) Map lookup.
73
+ * - Wildcard: `"/chat/rooms/*"` — `startsWith` prefix check.
74
+ */
75
+ on(event, pathPattern, handler) {
76
+ const h = handler;
77
+ if (pathPattern.endsWith("*")) {
78
+ const prefix = pathPattern.slice(0, -1);
79
+ const entry = {
80
+ event,
81
+ prefix,
82
+ handler: h
83
+ };
84
+ this.wildcards.push(entry);
85
+ return () => {
86
+ const idx = this.wildcards.indexOf(entry);
87
+ if (idx !== -1) this.wildcards.splice(idx, 1);
88
+ };
89
+ }
90
+ const key = `${event}:${pathPattern}`;
91
+ let set = this.exact.get(key);
92
+ if (!set) {
93
+ set = /* @__PURE__ */ new Set();
94
+ this.exact.set(key, set);
95
+ }
96
+ set.add(h);
97
+ return () => {
98
+ set.delete(h);
99
+ if (set.size === 0) this.exact.delete(key);
100
+ };
101
+ }
102
+ /** Dispatch a server push message to matching listeners. */
103
+ dispatch(msg) {
104
+ const pushEvent = {
105
+ event: msg.event,
106
+ path: msg.path,
107
+ params: msg.params ?? {},
108
+ data: msg.data
109
+ };
110
+ const key = `${msg.event}:${msg.path}`;
111
+ const exactSet = this.exact.get(key);
112
+ if (exactSet) for (const handler of exactSet) handler(pushEvent);
113
+ for (const { event, prefix, handler } of this.wildcards) if (msg.event === event && msg.path.startsWith(prefix)) handler(pushEvent);
114
+ }
115
+ /** Remove all listeners. */
116
+ clear() {
117
+ this.exact.clear();
118
+ this.wildcards.length = 0;
119
+ }
120
+ };
121
+
122
+ //#endregion
123
+ //#region packages/ws-client/src/message-queue.ts
124
+ /** Queue for outbound messages that accumulate while disconnected. */
125
+ var MessageQueue = class {
126
+ queue = [];
127
+ /** Enqueue a serialized message. */
128
+ enqueue(serialized) {
129
+ this.queue.push(serialized);
130
+ }
131
+ /** Flush all queued messages via the provided send function. Returns number flushed. */
132
+ flush(send) {
133
+ const count = this.queue.length;
134
+ for (const msg of this.queue) send(msg);
135
+ this.queue.length = 0;
136
+ return count;
137
+ }
138
+ /** Discard all queued messages. */
139
+ clear() {
140
+ this.queue.length = 0;
141
+ }
142
+ /** Number of queued messages. */
143
+ get size() {
144
+ return this.queue.length;
145
+ }
146
+ };
147
+
148
+ //#endregion
149
+ //#region packages/ws-client/src/reconnect.ts
150
+ /** Normalize the user-facing reconnect option into a full config. */
151
+ function normalizeReconnectConfig(opt) {
152
+ if (opt === true) return {
153
+ enabled: true,
154
+ maxRetries: Infinity,
155
+ baseDelay: 1e3,
156
+ maxDelay: 3e4,
157
+ backoff: "exponential"
158
+ };
159
+ if (opt === false || opt === void 0) return {
160
+ enabled: false,
161
+ maxRetries: 0,
162
+ baseDelay: 1e3,
163
+ maxDelay: 3e4,
164
+ backoff: "exponential"
165
+ };
166
+ return {
167
+ enabled: opt.enabled,
168
+ maxRetries: opt.maxRetries ?? Infinity,
169
+ baseDelay: opt.baseDelay ?? 1e3,
170
+ maxDelay: opt.maxDelay ?? 3e4,
171
+ backoff: opt.backoff ?? "exponential"
172
+ };
173
+ }
174
+ /** Manages reconnection state and backoff calculation. */
175
+ var ReconnectController = class {
176
+ attempt = 0;
177
+ timer;
178
+ stopped = false;
179
+ constructor(config) {
180
+ this.config = config;
181
+ }
182
+ /** Whether reconnect is enabled and not manually stopped. */
183
+ get enabled() {
184
+ return this.config.enabled && !this.stopped;
185
+ }
186
+ /** Current attempt number. */
187
+ get currentAttempt() {
188
+ return this.attempt;
189
+ }
190
+ /** Schedule a reconnection attempt. Returns false if max retries exceeded or stopped. */
191
+ schedule(onReconnect) {
192
+ if (this.stopped) return false;
193
+ if (this.attempt >= this.config.maxRetries) return false;
194
+ const delay = this.getDelay();
195
+ this.attempt++;
196
+ this.timer = setTimeout(onReconnect, delay);
197
+ return true;
198
+ }
199
+ /** Reset attempt counter (called on successful connection). */
200
+ reset() {
201
+ this.attempt = 0;
202
+ this.cancelPending();
203
+ }
204
+ /** Permanently stop reconnection (called on explicit close()). */
205
+ stop() {
206
+ this.stopped = true;
207
+ this.cancelPending();
208
+ }
209
+ getDelay() {
210
+ const { baseDelay, maxDelay, backoff } = this.config;
211
+ const delay = backoff === "exponential" ? baseDelay * 2 ** this.attempt : baseDelay * (this.attempt + 1);
212
+ return Math.min(delay, maxDelay);
213
+ }
214
+ cancelPending() {
215
+ if (this.timer !== void 0) {
216
+ clearTimeout(this.timer);
217
+ this.timer = void 0;
218
+ }
219
+ }
220
+ };
221
+
222
+ //#endregion
223
+ //#region packages/ws-client/src/ws-client.ts
224
+ const DEFAULT_RPC_TIMEOUT = 1e4;
225
+ function getWebSocketImpl(override) {
226
+ if (override) return override;
227
+ if (typeof WebSocket !== "undefined") return WebSocket;
228
+ throw new TypeError("@wooksjs/ws-client: No WebSocket implementation found. Install the \"ws\" package for Node.js < 22 or use a polyfill.");
229
+ }
230
+ /** WebSocket client with RPC, subscriptions, reconnection, and push listeners. */
231
+ var WsClient = class {
232
+ ws = null;
233
+ url;
234
+ protocols;
235
+ rpcTimeout;
236
+ serializer;
237
+ parser;
238
+ WsImpl;
239
+ rpc;
240
+ dispatcher;
241
+ queue;
242
+ reconnector;
243
+ /** Active subscriptions for auto-resubscribe: path → data. */
244
+ subscriptions = /* @__PURE__ */ new Map();
245
+ openHandlers = [];
246
+ closeHandlers = [];
247
+ errorHandlers = [];
248
+ reconnectHandlers = [];
249
+ closed = false;
250
+ constructor(url, options) {
251
+ this.url = url;
252
+ this.protocols = options?.protocols;
253
+ this.rpcTimeout = options?.rpcTimeout ?? DEFAULT_RPC_TIMEOUT;
254
+ this.serializer = options?.messageSerializer ?? JSON.stringify;
255
+ this.parser = options?.messageParser ?? JSON.parse;
256
+ this.WsImpl = getWebSocketImpl(options?._WebSocket);
257
+ this.rpc = new RpcTracker();
258
+ this.dispatcher = new PushDispatcher();
259
+ this.queue = new MessageQueue();
260
+ this.reconnector = new ReconnectController(normalizeReconnectConfig(options?.reconnect));
261
+ this.connect();
262
+ }
263
+ /** Fire-and-forget. Queued when disconnected with reconnect enabled. */
264
+ send(event, path, data) {
265
+ const msg = {
266
+ event,
267
+ path
268
+ };
269
+ if (data !== void 0) msg.data = data;
270
+ const serialized = this.serializer(msg);
271
+ if (this.isOpen()) this.ws.send(serialized);
272
+ else if (this.reconnector.enabled) this.queue.enqueue(serialized);
273
+ }
274
+ /** RPC with auto-generated correlation ID. Rejects when not connected. */
275
+ call(event, path, data) {
276
+ if (!this.isOpen()) return Promise.reject(new WsClientError(503, "Not connected"));
277
+ const id = this.rpc.generateId();
278
+ const msg = {
279
+ event,
280
+ path,
281
+ id
282
+ };
283
+ if (data !== void 0) msg.data = data;
284
+ this.ws.send(this.serializer(msg));
285
+ return this.rpc.track(id, this.rpcTimeout);
286
+ }
287
+ /** Subscribe to a path. Returns an unsubscribe function. Auto-resubscribes on reconnect. */
288
+ async subscribe(path, data) {
289
+ await this.call("subscribe", path, data);
290
+ this.subscriptions.set(path, data);
291
+ return () => {
292
+ this.subscriptions.delete(path);
293
+ if (this.isOpen()) this.send("unsubscribe", path);
294
+ };
295
+ }
296
+ /** Register a client-side push listener. Returns an unregister function. */
297
+ on(event, pathPattern, handler) {
298
+ return this.dispatcher.on(event, pathPattern, handler);
299
+ }
300
+ /** Close the connection. Disables reconnect. Rejects pending RPCs. */
301
+ close() {
302
+ this.closed = true;
303
+ this.reconnector.stop();
304
+ this.rpc.rejectAll(503, "Connection closed");
305
+ this.queue.clear();
306
+ if (this.ws) {
307
+ this.ws.close(1e3, "Client closed");
308
+ this.ws = null;
309
+ }
310
+ }
311
+ /** Register a handler called when the WebSocket connection opens. Returns an unregister function. */
312
+ onOpen(handler) {
313
+ this.openHandlers.push(handler);
314
+ return () => {
315
+ const idx = this.openHandlers.indexOf(handler);
316
+ if (idx !== -1) this.openHandlers.splice(idx, 1);
317
+ };
318
+ }
319
+ /** Register a handler called when the WebSocket connection closes. Returns an unregister function. */
320
+ onClose(handler) {
321
+ this.closeHandlers.push(handler);
322
+ return () => {
323
+ const idx = this.closeHandlers.indexOf(handler);
324
+ if (idx !== -1) this.closeHandlers.splice(idx, 1);
325
+ };
326
+ }
327
+ /** Register a handler called on WebSocket errors. Returns an unregister function. */
328
+ onError(handler) {
329
+ this.errorHandlers.push(handler);
330
+ return () => {
331
+ const idx = this.errorHandlers.indexOf(handler);
332
+ if (idx !== -1) this.errorHandlers.splice(idx, 1);
333
+ };
334
+ }
335
+ /** Register a handler called before each reconnection attempt. Returns an unregister function. */
336
+ onReconnect(handler) {
337
+ this.reconnectHandlers.push(handler);
338
+ return () => {
339
+ const idx = this.reconnectHandlers.indexOf(handler);
340
+ if (idx !== -1) this.reconnectHandlers.splice(idx, 1);
341
+ };
342
+ }
343
+ isOpen() {
344
+ return this.ws !== null && this.ws.readyState === 1;
345
+ }
346
+ connect() {
347
+ if (this.closed) return;
348
+ const ws = new this.WsImpl(this.url, this.protocols);
349
+ this.ws = ws;
350
+ ws.addEventListener("open", () => {
351
+ this.reconnector.reset();
352
+ this.queue.flush((data) => ws.send(data));
353
+ this.resubscribe();
354
+ for (const h of this.openHandlers) h();
355
+ });
356
+ ws.addEventListener("close", (ev) => {
357
+ this.rpc.rejectAll(503, "Connection lost");
358
+ for (const h of this.closeHandlers) h(ev.code, ev.reason ?? "");
359
+ if (!this.closed && this.reconnector.enabled) this.reconnector.schedule(() => {
360
+ for (const h of this.reconnectHandlers) h(this.reconnector.currentAttempt);
361
+ this.connect();
362
+ });
363
+ });
364
+ ws.addEventListener("error", (ev) => {
365
+ for (const h of this.errorHandlers) h(ev);
366
+ });
367
+ ws.addEventListener("message", (ev) => {
368
+ this.handleMessage(ev.data);
369
+ });
370
+ }
371
+ handleMessage(raw) {
372
+ let msg;
373
+ try {
374
+ msg = this.parser(raw);
375
+ } catch {
376
+ return;
377
+ }
378
+ if ("id" in msg && msg.id !== void 0) {
379
+ this.rpc.resolve(msg);
380
+ return;
381
+ }
382
+ if ("event" in msg && "path" in msg) this.dispatcher.dispatch(msg);
383
+ }
384
+ resubscribe() {
385
+ for (const [path, data] of this.subscriptions) this.call("subscribe", path, data).catch(() => {});
386
+ }
387
+ };
388
+ /**
389
+ * Creates a new WebSocket client.
390
+ *
391
+ * @example
392
+ * ```ts
393
+ * const client = createWsClient('wss://api.example.com', { reconnect: true })
394
+ *
395
+ * client.on('message', '/chat/*', ({ data }) => console.log(data))
396
+ * await client.subscribe('/chat/rooms/lobby')
397
+ * client.send('message', '/chat/rooms/lobby', { text: 'hello' })
398
+ *
399
+ * const me = await client.call('rpc', '/users/me')
400
+ * ```
401
+ */
402
+ function createWsClient(url, options) {
403
+ return new WsClient(url, options);
404
+ }
405
+
406
+ //#endregion
407
+ exports.WsClient = WsClient;
408
+ exports.WsClientError = WsClientError;
409
+ exports.createWsClient = createWsClient;
@@ -0,0 +1,144 @@
1
+ /** Client-to-server message. */
2
+ interface WsClientMessage {
3
+ /** Event type — used as the router method on the server. */
4
+ event: string;
5
+ /** Route path (always concrete, never a pattern). */
6
+ path: string;
7
+ /** Payload. */
8
+ data?: unknown;
9
+ /** Correlation ID. When present, the server sends a reply with the same ID. */
10
+ id?: number;
11
+ }
12
+ /** Server-to-client reply (sent only when the client message included an `id`). */
13
+ interface WsReplyMessage {
14
+ /** Correlation ID matching the client's request. */
15
+ id: string | number;
16
+ /** Response payload. */
17
+ data?: unknown;
18
+ /** Error (mutually exclusive with data). */
19
+ error?: {
20
+ code: number;
21
+ message: string;
22
+ };
23
+ }
24
+ /** Server-to-client push (broadcast, direct send, subscription notification). */
25
+ interface WsPushMessage {
26
+ /** Event type. */
27
+ event: string;
28
+ /** The concrete path this message relates to. */
29
+ path: string;
30
+ /** Route params extracted by the server router. */
31
+ params?: Record<string, string>;
32
+ /** Payload. */
33
+ data?: unknown;
34
+ }
35
+ /** Reconnection behaviour configuration for the WebSocket client. */
36
+ interface WsClientReconnectOptions {
37
+ /** Whether reconnection is enabled. */
38
+ enabled: boolean;
39
+ /** Max reconnection attempts (default: Infinity). */
40
+ maxRetries?: number;
41
+ /** Base delay in ms (default: 1000). */
42
+ baseDelay?: number;
43
+ /** Max delay in ms (default: 30000). */
44
+ maxDelay?: number;
45
+ /** Backoff strategy (default: "exponential"). */
46
+ backoff?: 'linear' | 'exponential';
47
+ }
48
+ /** Options for {@link WsClient} / {@link createWsClient}. */
49
+ interface WsClientOptions {
50
+ /** Protocols passed to the WebSocket constructor. */
51
+ protocols?: string | string[];
52
+ /** Reconnection config. */
53
+ reconnect?: boolean | WsClientReconnectOptions;
54
+ /** Timeout for RPC calls in ms (default: 10000). */
55
+ rpcTimeout?: number;
56
+ /** Custom message parser. Default: JSON.parse. */
57
+ messageParser?: (raw: string) => WsReplyMessage | WsPushMessage;
58
+ /** Custom message serializer. Default: JSON.stringify. */
59
+ messageSerializer?: (msg: WsClientMessage) => string;
60
+ /** @internal For testing only — override the WebSocket constructor. */
61
+ _WebSocket?: new (url: string, protocols?: string | string[]) => WebSocket;
62
+ }
63
+ /** Payload shape received by `client.on()` handlers. */
64
+ interface WsClientPushEvent<T = unknown> {
65
+ /** Event type from server. */
66
+ event: string;
67
+ /** Concrete path from server. */
68
+ path: string;
69
+ /** Route params extracted by the server. */
70
+ params: Record<string, string>;
71
+ /** Payload. */
72
+ data: T;
73
+ }
74
+ /** Push message handler. */
75
+ type WsPushHandler<T = unknown> = (ev: WsClientPushEvent<T>) => void;
76
+
77
+ /** WebSocket client with RPC, subscriptions, reconnection, and push listeners. */
78
+ declare class WsClient {
79
+ private ws;
80
+ private readonly url;
81
+ private readonly protocols;
82
+ private readonly rpcTimeout;
83
+ private readonly serializer;
84
+ private readonly parser;
85
+ private readonly WsImpl;
86
+ private readonly rpc;
87
+ private readonly dispatcher;
88
+ private readonly queue;
89
+ private readonly reconnector;
90
+ /** Active subscriptions for auto-resubscribe: path → data. */
91
+ private readonly subscriptions;
92
+ private readonly openHandlers;
93
+ private readonly closeHandlers;
94
+ private readonly errorHandlers;
95
+ private readonly reconnectHandlers;
96
+ private closed;
97
+ constructor(url: string, options?: WsClientOptions);
98
+ /** Fire-and-forget. Queued when disconnected with reconnect enabled. */
99
+ send(event: string, path: string, data?: unknown): void;
100
+ /** RPC with auto-generated correlation ID. Rejects when not connected. */
101
+ call<T = unknown>(event: string, path: string, data?: unknown): Promise<T>;
102
+ /** Subscribe to a path. Returns an unsubscribe function. Auto-resubscribes on reconnect. */
103
+ subscribe(path: string, data?: unknown): Promise<() => void>;
104
+ /** Register a client-side push listener. Returns an unregister function. */
105
+ on<T = unknown>(event: string, pathPattern: string, handler: WsPushHandler<T>): () => void;
106
+ /** Close the connection. Disables reconnect. Rejects pending RPCs. */
107
+ close(): void;
108
+ /** Register a handler called when the WebSocket connection opens. Returns an unregister function. */
109
+ onOpen(handler: () => void): () => void;
110
+ /** Register a handler called when the WebSocket connection closes. Returns an unregister function. */
111
+ onClose(handler: (code: number, reason: string) => void): () => void;
112
+ /** Register a handler called on WebSocket errors. Returns an unregister function. */
113
+ onError(handler: (error: Event) => void): () => void;
114
+ /** Register a handler called before each reconnection attempt. Returns an unregister function. */
115
+ onReconnect(handler: (attempt: number) => void): () => void;
116
+ private isOpen;
117
+ private connect;
118
+ private handleMessage;
119
+ private resubscribe;
120
+ }
121
+ /**
122
+ * Creates a new WebSocket client.
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * const client = createWsClient('wss://api.example.com', { reconnect: true })
127
+ *
128
+ * client.on('message', '/chat/*', ({ data }) => console.log(data))
129
+ * await client.subscribe('/chat/rooms/lobby')
130
+ * client.send('message', '/chat/rooms/lobby', { text: 'hello' })
131
+ *
132
+ * const me = await client.call('rpc', '/users/me')
133
+ * ```
134
+ */
135
+ declare function createWsClient(url: string, options?: WsClientOptions): WsClient;
136
+
137
+ /** Error from the WebSocket client (server error reply, timeout, or disconnect). */
138
+ declare class WsClientError extends Error {
139
+ readonly code: number;
140
+ constructor(code: number, message?: string);
141
+ }
142
+
143
+ export { WsClient, WsClientError, createWsClient };
144
+ export type { WsClientMessage, WsClientOptions, WsClientPushEvent, WsClientReconnectOptions, WsPushHandler, WsPushMessage, WsReplyMessage };