react-websocket 3.2.0 → 3.5.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 +1 -0
- package/README.md +19 -7
- package/dist/chaos.cjs +284 -0
- package/dist/chaos.cjs.map +1 -0
- package/dist/chaos.d.cts +131 -0
- package/dist/chaos.d.ts +131 -0
- package/dist/chaos.js +283 -0
- package/dist/chaos.js.map +1 -0
- package/dist/core.cjs +1157 -759
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +645 -374
- package/dist/core.d.ts +645 -374
- package/dist/core.js +1149 -727
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +217 -151
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +705 -410
- package/dist/index.d.ts +453 -524
- package/dist/index.js +204 -121
- package/dist/index.js.map +1 -1
- package/dist/timeline.cjs +162 -0
- package/dist/timeline.cjs.map +1 -0
- package/dist/timeline.d.cts +379 -0
- package/dist/timeline.d.ts +140 -0
- package/dist/timeline.js +161 -0
- package/dist/timeline.js.map +1 -0
- package/package.json +94 -38
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
|
|
2
|
+
export * from "./core.js";
|
|
3
|
+
//#region src/types.d.ts
|
|
1
4
|
/**
|
|
2
5
|
* Where to connect: a URL, or a function that returns one (sync or async).
|
|
3
6
|
*
|
|
@@ -24,105 +27,139 @@ type UrlSource = string | (() => string | Promise<string>);
|
|
|
24
27
|
type Status = 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed';
|
|
25
28
|
/** The client's state as seen by React. A new object is created on every change. */
|
|
26
29
|
interface Snapshot<In> {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
/** Connection state. */
|
|
31
|
+
readonly status: Status;
|
|
32
|
+
/** Most recent message (after `parse`/`json` and `schema`), kept across reconnects. */
|
|
33
|
+
readonly lastMessage: In | undefined;
|
|
34
|
+
/** Consecutive failed attempts since the last successful open. `0` while open. */
|
|
35
|
+
readonly attempt: number;
|
|
33
36
|
}
|
|
34
37
|
/** Details of a closed connection, from the browser's `CloseEvent`. */
|
|
35
38
|
interface CloseInfo {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
/** Close code: `1000` is a normal close, `1006` an abnormal one (e.g. network loss), `4000`–`4999` are yours. */
|
|
40
|
+
readonly code: number;
|
|
41
|
+
/** Close reason sent by the side that closed the connection. Often empty. */
|
|
42
|
+
readonly reason: string;
|
|
43
|
+
/** Whether the closing handshake completed. */
|
|
44
|
+
readonly wasClean: boolean;
|
|
42
45
|
}
|
|
43
46
|
/** Anything `WebSocket.prototype.send` accepts: a string, `Blob`, `ArrayBuffer` or typed array. */
|
|
44
47
|
type SendData = Parameters<WebSocket['send']>[0];
|
|
45
48
|
/** Reconnect timing. Delay before attempt `n` = `min(maxDelay, minDelay * factor ** (n - 1))`, minus jitter. */
|
|
46
49
|
interface ReconnectOptions {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Maximum consecutive reconnect attempts. When the last one fails too, the client
|
|
52
|
+
* gives up: the status becomes `closed` and `ready` rejects. `0` never retries.
|
|
53
|
+
* @default Infinity
|
|
54
|
+
*/
|
|
55
|
+
maxAttempts?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Delay before the first retry, in ms.
|
|
58
|
+
* @default 1000
|
|
59
|
+
*/
|
|
60
|
+
minDelay?: number;
|
|
61
|
+
/**
|
|
62
|
+
* Upper bound for the delay, in ms.
|
|
63
|
+
* @default 30000
|
|
64
|
+
*/
|
|
65
|
+
maxDelay?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Backoff multiplier applied per attempt.
|
|
68
|
+
* @default 2
|
|
69
|
+
*/
|
|
70
|
+
factor?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Randomly shave up to this fraction (0–1) off each delay, so that many clients
|
|
73
|
+
* dropped at once don't all reconnect at the same moment.
|
|
74
|
+
* @default 0.5
|
|
75
|
+
*/
|
|
76
|
+
jitter?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Called on every unexpected close. Return `false` to stop reconnecting.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* // Don't retry when the server rejects our credentials.
|
|
83
|
+
* shouldReconnect: close => close.code !== 4001
|
|
84
|
+
* ```
|
|
85
|
+
* @default () => true
|
|
86
|
+
*/
|
|
87
|
+
shouldReconnect?: (close: CloseInfo) => boolean;
|
|
85
88
|
}
|
|
86
89
|
/** Application-level keep-alive. Useful behind proxies that drop idle connections. */
|
|
87
90
|
interface HeartbeatOptions {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
91
|
+
/** Send `message` every `interval` ms while open. */
|
|
92
|
+
interval: number;
|
|
93
|
+
/**
|
|
94
|
+
* Sent as-is (it is not passed through `serialize` or `json`).
|
|
95
|
+
* @default 'ping'
|
|
96
|
+
*/
|
|
97
|
+
message?: string;
|
|
98
|
+
/** Incoming data equal to this string is swallowed instead of being delivered as a message. */
|
|
99
|
+
pong?: string;
|
|
100
|
+
/**
|
|
101
|
+
* If nothing at all arrives within this many ms after a ping, the connection is
|
|
102
|
+
* treated as dead: it closes with code `4000` and reconnects.
|
|
103
|
+
*/
|
|
104
|
+
timeout?: number;
|
|
102
105
|
}
|
|
103
106
|
/** Per-message options for `send()`. */
|
|
104
107
|
interface SendOptions {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Drop the message instead of queueing it when the socket isn't open. For data that
|
|
110
|
+
* is stale by the time a reconnect finishes, such as player input or cursor positions.
|
|
111
|
+
* @default false
|
|
112
|
+
*/
|
|
113
|
+
volatile?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Keep only the newest queued message with this key. Sending again with the same key
|
|
116
|
+
* while disconnected replaces the earlier one, which moves to the back of the queue
|
|
117
|
+
* (or of the `priority` messages, for a priority one).
|
|
118
|
+
* For state where only the latest value matters, such as "my position is now (x, y)".
|
|
119
|
+
*/
|
|
120
|
+
key?: string;
|
|
121
|
+
/**
|
|
122
|
+
* Queue this message ahead of every message queued without `priority`, after earlier
|
|
123
|
+
* priority messages. For control messages that must not wait behind a backlog, such as
|
|
124
|
+
* cancel, interrupt or a token refresh. Messages sent from `open` listeners still go first.
|
|
125
|
+
*
|
|
126
|
+
* It only matters while the socket isn't open: once a message is handed to the browser,
|
|
127
|
+
* nothing can overtake it. To keep that buffer short, send bulky streams with `maxBuffered`.
|
|
128
|
+
* @default false
|
|
129
|
+
*/
|
|
130
|
+
priority?: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Drop the message instead of sending it while more than this many bytes are already
|
|
133
|
+
* waiting in the browser's send buffer (`socket.bufferedAmount`). For high-rate streams on
|
|
134
|
+
* a slow link, such as audio frames or per-tick input, where a late message is worse than
|
|
135
|
+
* none. `send()` returns `false`, and `onDrop` isn't called.
|
|
136
|
+
*
|
|
137
|
+
* Only checked while the socket is open: a message queued while disconnected is sent on
|
|
138
|
+
* open regardless. `bufferedAmount` includes what you sent earlier in the same task, so set
|
|
139
|
+
* it well above one burst of messages.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* voice.send(frame, { volatile: true, maxBuffered: 64 * 1024 });
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
maxBuffered?: number;
|
|
147
|
+
/**
|
|
148
|
+
* Called if the message is dropped from the queue without being sent: replaced by a
|
|
149
|
+
* newer message with the same `key`, or evicted by `queue.limit`. Not called when the
|
|
150
|
+
* queue is cleared because the client closed or gave up, or when `send()` drops the
|
|
151
|
+
* message right away (`volatile` while not open, or `maxBuffered`) and returns `false`.
|
|
152
|
+
*/
|
|
153
|
+
onDrop?: () => void;
|
|
117
154
|
}
|
|
118
155
|
/** Bounds for the send queue. */
|
|
119
156
|
interface QueueOptions {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Maximum number of messages held while not connected. When full, the oldest
|
|
159
|
+
* message is dropped, skipping `priority` messages unless nothing else is queued.
|
|
160
|
+
* @default Infinity
|
|
161
|
+
*/
|
|
162
|
+
limit?: number;
|
|
126
163
|
}
|
|
127
164
|
/**
|
|
128
165
|
* The minimal [Standard Schema v1](https://standardschema.dev) interface, implemented
|
|
@@ -130,24 +167,24 @@ interface QueueOptions {
|
|
|
130
167
|
* import this; pass your schema object directly.
|
|
131
168
|
*/
|
|
132
169
|
interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
170
|
+
readonly '~standard': {
|
|
171
|
+
readonly version: 1;
|
|
172
|
+
readonly vendor: string;
|
|
173
|
+
readonly validate: (value: unknown) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
|
|
174
|
+
readonly types?: {
|
|
175
|
+
readonly input: Input;
|
|
176
|
+
readonly output: Output;
|
|
177
|
+
} | undefined;
|
|
178
|
+
};
|
|
142
179
|
}
|
|
143
180
|
/** The result of a Standard Schema `validate` call. */
|
|
144
181
|
type StandardSchemaResult<Output> = {
|
|
145
|
-
|
|
146
|
-
|
|
182
|
+
readonly value: Output;
|
|
183
|
+
readonly issues?: undefined;
|
|
147
184
|
} | {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
185
|
+
readonly issues: ReadonlyArray<{
|
|
186
|
+
readonly message: string;
|
|
187
|
+
}>;
|
|
151
188
|
};
|
|
152
189
|
/**
|
|
153
190
|
* Connection options, shared by `createWebSocket(url, options)` and `useWebSocket(url, options)`.
|
|
@@ -156,66 +193,66 @@ type StandardSchemaResult<Output> = {
|
|
|
156
193
|
* @typeParam Out - Type accepted by `send()`.
|
|
157
194
|
*/
|
|
158
195
|
interface WebSocketOptions<In = unknown, Out = unknown> {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
196
|
+
/** Sub-protocol(s) passed to the `WebSocket` constructor. */
|
|
197
|
+
protocols?: string | string[];
|
|
198
|
+
/**
|
|
199
|
+
* How binary messages are exposed.
|
|
200
|
+
* @default 'blob'
|
|
201
|
+
*/
|
|
202
|
+
binaryType?: BinaryType;
|
|
203
|
+
/**
|
|
204
|
+
* `JSON.parse` incoming and `JSON.stringify` outgoing messages. Messages that fail
|
|
205
|
+
* to parse are reported to `onError` as a {@link MessageError} and not delivered.
|
|
206
|
+
* @default false
|
|
207
|
+
*/
|
|
208
|
+
json?: boolean;
|
|
209
|
+
/**
|
|
210
|
+
* Custom decoder for incoming `event.data`. Takes precedence over `json`.
|
|
211
|
+
* Throwing reports a {@link MessageError} and drops the message.
|
|
212
|
+
*/
|
|
213
|
+
parse?: (data: unknown) => unknown;
|
|
214
|
+
/** Custom encoder for outgoing messages. Takes precedence over `json`. */
|
|
215
|
+
serialize?: (message: Out) => SendData;
|
|
216
|
+
/**
|
|
217
|
+
* Validate incoming messages (after parsing) with any Standard Schema library.
|
|
218
|
+
* Invalid messages are reported to `onError` as a {@link MessageError} and not delivered.
|
|
219
|
+
* The message type `In` is inferred from it.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* createWebSocket(url, { json: true, schema: z.object({ price: z.number() }) });
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
schema?: StandardSchemaV1<unknown, In>;
|
|
227
|
+
/**
|
|
228
|
+
* Reconnect with exponential backoff after an unexpected close. `false` disables it.
|
|
229
|
+
* @default true
|
|
230
|
+
*/
|
|
231
|
+
reconnect?: boolean | ReconnectOptions;
|
|
232
|
+
/** Send a ping on an interval and, optionally, reconnect when the server goes quiet. */
|
|
233
|
+
heartbeat?: HeartbeatOptions;
|
|
234
|
+
/**
|
|
235
|
+
* Buffer `send()` calls made while not connected and send them, in order, on open
|
|
236
|
+
* (right after `open` listeners run).
|
|
237
|
+
* `false` drops them instead. `{ limit }` caps the buffer, dropping the oldest.
|
|
238
|
+
* The queue is cleared by `close()` or when the client gives up.
|
|
239
|
+
* @default true
|
|
240
|
+
*/
|
|
241
|
+
queue?: boolean | QueueOptions;
|
|
242
|
+
/**
|
|
243
|
+
* Receives debug messages about connection attempts, retries and dropped messages.
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* logger: console.debug
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
logger?: (message: string, ...details: unknown[]) => void;
|
|
250
|
+
/**
|
|
251
|
+
* `WebSocket` implementation to use, e.g. a mock in tests or the `ws` package in Node.
|
|
252
|
+
* When omitted, the global `WebSocket` is used in browsers. During a server render
|
|
253
|
+
* (no `window`), nothing connects unless an implementation is passed.
|
|
254
|
+
*/
|
|
255
|
+
WebSocket?: typeof WebSocket;
|
|
219
256
|
}
|
|
220
257
|
/**
|
|
221
258
|
* Reported via the `error` event (and `onError`) when an incoming message can't be
|
|
@@ -229,35 +266,35 @@ interface WebSocketOptions<In = unknown, Out = unknown> {
|
|
|
229
266
|
* ```
|
|
230
267
|
*/
|
|
231
268
|
declare class MessageError extends Error {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
269
|
+
/** The raw `event.data` that failed. */
|
|
270
|
+
readonly data: unknown;
|
|
271
|
+
/** Schema validation issues, when the message failed `schema`. */
|
|
272
|
+
readonly issues: ReadonlyArray<{
|
|
273
|
+
readonly message: string;
|
|
274
|
+
}> | undefined;
|
|
275
|
+
constructor(message: string, data: unknown, options?: {
|
|
276
|
+
cause?: unknown;
|
|
277
|
+
issues?: ReadonlyArray<{
|
|
278
|
+
readonly message: string;
|
|
279
|
+
}>;
|
|
280
|
+
});
|
|
244
281
|
}
|
|
245
282
|
/** Events emitted by a client. Subscribe with `client.on(type, listener)` or `useWebSocketEvent`. */
|
|
246
283
|
interface EventMap<In> {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
284
|
+
/**
|
|
285
|
+
* The socket opened (initially or after a reconnect). Listeners run before queued
|
|
286
|
+
* messages are sent, so a handshake sent from here goes out first.
|
|
287
|
+
*/
|
|
288
|
+
open: (event: Event) => void;
|
|
289
|
+
/** A message arrived and passed `parse` and `schema`. */
|
|
290
|
+
message: (message: In, event: MessageEvent) => void;
|
|
291
|
+
/** A socket closed, whether it will be retried or not. */
|
|
292
|
+
close: (close: CloseInfo) => void;
|
|
293
|
+
/**
|
|
294
|
+
* A socket error `Event`, a {@link MessageError} for an undecodable or invalid message,
|
|
295
|
+
* or the `Error` thrown by the `WebSocket` constructor or a `url` function.
|
|
296
|
+
*/
|
|
297
|
+
error: (error: Event | MessageError | Error) => void;
|
|
261
298
|
}
|
|
262
299
|
/**
|
|
263
300
|
* A WebSocket connection that reconnects, queues, decodes and can be shared.
|
|
@@ -271,246 +308,162 @@ interface EventMap<In> {
|
|
|
271
308
|
* @typeParam Out - Type accepted by `send()`.
|
|
272
309
|
*/
|
|
273
310
|
interface WebSocketClient<In = unknown, Out = unknown> {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Create a WebSocket client, usually at module level, to share across components.
|
|
341
|
-
*
|
|
342
|
-
* Nothing connects until a hook uses it, `ready` is read, or `connect()` is called.
|
|
343
|
-
* It closes one tick after the last hook unmounts, unless `connect()` was called.
|
|
344
|
-
*
|
|
345
|
-
* @param url - A URL, or a function (sync or async) called before every connection
|
|
346
|
-
* attempt, e.g. to put a fresh auth token in the URL on each reconnect.
|
|
347
|
-
* @typeParam In - Type of incoming messages. Inferred from `options.schema` if given.
|
|
348
|
-
* @typeParam Out - Type accepted by `send()`.
|
|
349
|
-
*
|
|
350
|
-
* @example
|
|
351
|
-
* ```ts
|
|
352
|
-
* type ChatIn = { user: string; text: string };
|
|
353
|
-
* type ChatOut = { text: string };
|
|
354
|
-
*
|
|
355
|
-
* export const chat = createWebSocket<ChatIn, ChatOut>('wss://api.example.com/chat', { json: true });
|
|
356
|
-
* ```
|
|
357
|
-
*/
|
|
358
|
-
declare function createWebSocket<In = unknown, Out = unknown>(url: UrlSource, options?: WebSocketOptions<In, Out>): WebSocketClient<In, Out>;
|
|
359
|
-
|
|
360
|
-
/** Options for {@link createWebSocketLatency}: how pings and pongs look on the wire. */
|
|
361
|
-
interface WebSocketLatencyOptions<In, Out> {
|
|
362
|
-
/** The ping message, tagged with `id`. Sent with `volatile`, so pings are never queued. */
|
|
363
|
-
ping: (id: number) => Out;
|
|
364
|
-
/**
|
|
365
|
-
* Recognize a pong: return the `id` of the ping it answers, and the server's clock
|
|
366
|
-
* (`Date.now()` on the server, in ms) when it replied. Return `undefined` (or `null`)
|
|
367
|
-
* for any other message. Without `time`, only the round-trip time is measured.
|
|
368
|
-
*/
|
|
369
|
-
pong: (message: In) => {
|
|
370
|
-
id: number | string;
|
|
371
|
-
time?: number;
|
|
372
|
-
} | null | undefined;
|
|
373
|
-
/**
|
|
374
|
-
* How often to ping while open, in ms. The first ping goes out as soon as the socket opens.
|
|
375
|
-
* @default 2000
|
|
376
|
-
*/
|
|
377
|
-
interval?: number;
|
|
378
|
-
/**
|
|
379
|
-
* How many recent measurements to keep. `rtt` is their median, and `offset` comes from
|
|
380
|
-
* the one with the lowest round trip, the most accurate estimate.
|
|
381
|
-
* @default 5
|
|
382
|
-
*/
|
|
383
|
-
samples?: number;
|
|
311
|
+
/** The URL (or URL function) the client was created with. */
|
|
312
|
+
readonly url: UrlSource;
|
|
313
|
+
/**
|
|
314
|
+
* A promise that resolves once the socket has opened. Reading it starts an idle client.
|
|
315
|
+
*
|
|
316
|
+
* It stays resolved through automatic reconnects, so `use(client.ready)` shows a
|
|
317
|
+
* Suspense fallback only for the first connection, never on a network blip; use
|
|
318
|
+
* `status` to show reconnects. It rejects when the client gives up reconnecting, when
|
|
319
|
+
* `close()` is called, and during a server render. After that, `connect()` or
|
|
320
|
+
* `reconnect()` make it a new pending promise.
|
|
321
|
+
*
|
|
322
|
+
* It is safe to pass to React 19's `use()`: the same promise is returned until its state
|
|
323
|
+
* changes, and it carries `status`/`value`/`reason` so `use()` doesn't suspend when it is
|
|
324
|
+
* already settled.
|
|
325
|
+
*/
|
|
326
|
+
readonly ready: Promise<void>;
|
|
327
|
+
/** The current underlying `WebSocket`, if any. An escape hatch; prefer the client's methods. */
|
|
328
|
+
readonly socket: WebSocket | null;
|
|
329
|
+
/** Subscribe to snapshot changes. Compatible with `useSyncExternalStore`. Returns an unsubscribe function. */
|
|
330
|
+
subscribe(listener: () => void): () => void;
|
|
331
|
+
/** The current {@link Snapshot}. Returns the same object until something changes. */
|
|
332
|
+
getSnapshot(): Snapshot<In>;
|
|
333
|
+
/**
|
|
334
|
+
* Listen to an event. Returns an unsubscribe function. Listener errors are re-thrown
|
|
335
|
+
* asynchronously, so one failing listener doesn't break the others.
|
|
336
|
+
*/
|
|
337
|
+
on<K extends keyof EventMap<In>>(type: K, listener: EventMap<In>[K]): () => void;
|
|
338
|
+
/**
|
|
339
|
+
* Send a message (encoded with `serialize`/`json` if configured). If the socket isn't
|
|
340
|
+
* open, the message is queued and sent on open (see the `queue` option), unless
|
|
341
|
+
* `options.volatile` is set.
|
|
342
|
+
* @returns `true` if it was sent immediately, `false` if it was queued or dropped
|
|
343
|
+
* (`volatile` while not open, or `maxBuffered` while open).
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* ```ts
|
|
347
|
+
* game.send({ type: 'input', dx, dy }, { volatile: true }); // never replayed after a reconnect
|
|
348
|
+
* game.send({ type: 'position', x, y }, { key: 'position' }); // only the newest is queued
|
|
349
|
+
* game.send({ type: 'leave' }, { priority: true }); // ahead of everything already queued
|
|
350
|
+
* ```
|
|
351
|
+
*/
|
|
352
|
+
send(message: Out, options?: SendOptions): boolean;
|
|
353
|
+
/**
|
|
354
|
+
* Start connecting, and keep the connection open even when no hook is using the
|
|
355
|
+
* client, until `close()` is called. Does nothing if already connecting or open.
|
|
356
|
+
*/
|
|
357
|
+
connect(): void;
|
|
358
|
+
/**
|
|
359
|
+
* Close the connection and stop reconnecting. The status becomes `closed` and the
|
|
360
|
+
* send queue is cleared. Mounted hooks stay mounted; call `connect()` or `reconnect()`
|
|
361
|
+
* to start again.
|
|
362
|
+
* @param code - Close code. @default 1000
|
|
363
|
+
*/
|
|
364
|
+
close(code?: number, reason?: string): void;
|
|
365
|
+
/**
|
|
366
|
+
* Drop the current socket (if any) and connect again immediately, resetting the
|
|
367
|
+
* backoff. Also restarts a `closed` client.
|
|
368
|
+
*/
|
|
369
|
+
reconnect(): void;
|
|
370
|
+
/**
|
|
371
|
+
* Hold the connection open until the returned function is called. This is what the
|
|
372
|
+
* hooks use; call it yourself to integrate with non-React code.
|
|
373
|
+
* @returns A release function (safe to call more than once).
|
|
374
|
+
*/
|
|
375
|
+
acquire(): () => void;
|
|
384
376
|
}
|
|
377
|
+
//#endregion
|
|
378
|
+
//#region src/latency.d.ts
|
|
385
379
|
/** Latest latency measurements. A new object is created on every change. */
|
|
386
380
|
interface Latency {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
381
|
+
/** Round-trip time in ms: the median of recent pings. `undefined` until the first pong. */
|
|
382
|
+
readonly rtt: number | undefined;
|
|
383
|
+
/**
|
|
384
|
+
* Estimated server clock minus local clock, in ms. Add it to `Date.now()` for server time.
|
|
385
|
+
* `undefined` until a pong carries `time`.
|
|
386
|
+
*/
|
|
387
|
+
readonly offset: number | undefined;
|
|
394
388
|
}
|
|
395
389
|
/** Round-trip time and server clock offset for one client. Create with {@link createWebSocketLatency}. */
|
|
396
390
|
interface WebSocketLatency {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
391
|
+
/** The client being measured. Hooks reading the latency keep it connected. */
|
|
392
|
+
readonly client: WebSocketClient<any, any>;
|
|
393
|
+
/** The current {@link Latency}. Returns the same object until it changes. */
|
|
394
|
+
getSnapshot(): Latency;
|
|
395
|
+
/** Called whenever a measurement changes. Compatible with `useSyncExternalStore`. */
|
|
396
|
+
subscribe(listener: () => void): () => void;
|
|
397
|
+
/** The estimated server time: `Date.now() + offset`, or `Date.now()` before any estimate. */
|
|
398
|
+
serverNow(): number;
|
|
399
|
+
/** Ping now instead of waiting for the next interval. Does nothing unless the socket is open. */
|
|
400
|
+
ping(): void;
|
|
401
|
+
/** Stop pinging and listening. Only needed for instances created dynamically. */
|
|
402
|
+
dispose(): void;
|
|
409
403
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
/** Options for {@link createWebSocketRequests}: how requests and replies look on the wire. */
|
|
437
|
-
interface WebSocketRequestsOptions<Req, In, Out> {
|
|
438
|
-
/**
|
|
439
|
-
* Build the outgoing message for `request(message)`, tagged with `id`. Annotate `message`
|
|
440
|
-
* to type what `request()` accepts.
|
|
441
|
-
*/
|
|
442
|
-
send: (message: Req, id: number) => Out;
|
|
443
|
-
/**
|
|
444
|
-
* The id an incoming message replies to, or `undefined` (or `null`) if it isn't a reply.
|
|
445
|
-
* A string id matches the number it was sent as, so either echo works.
|
|
446
|
-
*/
|
|
447
|
-
reply: (message: In) => number | string | null | undefined;
|
|
448
|
-
/**
|
|
449
|
-
* Called on a reply. Return something other than `undefined` to reject the request with
|
|
450
|
-
* it instead of resolving, e.g. for a server-side error reply.
|
|
451
|
-
*/
|
|
452
|
-
error?: (message: In) => unknown;
|
|
453
|
-
/**
|
|
454
|
-
* Default time to wait for a reply, in ms. `Infinity` waits forever.
|
|
455
|
-
* @default 10000
|
|
456
|
-
*/
|
|
457
|
-
timeout?: number;
|
|
458
|
-
}
|
|
459
|
-
/** Per-call options for {@link WebSocketRequests.request}. */
|
|
460
|
-
interface RequestOptions extends SendOptions {
|
|
461
|
-
/** Time to wait for a reply, in ms. Overrides the manager's `timeout`. */
|
|
462
|
-
timeout?: number;
|
|
463
|
-
/** Abort the request. It rejects with `signal.reason`. A late reply is ignored. */
|
|
464
|
-
signal?: AbortSignal;
|
|
465
|
-
}
|
|
466
|
-
/** Request/response over one client. Create with {@link createWebSocketRequests}. */
|
|
467
|
-
interface WebSocketRequests<Req, In> {
|
|
468
|
-
/** The client requests are sent on. */
|
|
469
|
-
readonly client: WebSocketClient<any, any>;
|
|
470
|
-
/**
|
|
471
|
-
* Send a request and resolve with its reply. Holds the connection until it settles.
|
|
472
|
-
*
|
|
473
|
-
* Rejects with a `TimeoutError` `DOMException` after `timeout`, with `signal.reason` when
|
|
474
|
-
* aborted, and with an `Error` when the connection it was sent on closes (the reply can
|
|
475
|
-
* no longer arrive) or the client stops. A request made while disconnected is queued like
|
|
476
|
-
* any other message and waits for the connection.
|
|
477
|
-
*/
|
|
478
|
-
request(message: Req, options?: RequestOptions): Promise<In>;
|
|
479
|
-
/** Number of requests waiting for a reply. */
|
|
480
|
-
pending(): number;
|
|
481
|
-
/** Reject every pending request and stop listening. Only needed for managers created dynamically. */
|
|
482
|
-
dispose(): void;
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/responder.d.ts
|
|
406
|
+
/** A request from the server that hasn't been answered yet. */
|
|
407
|
+
interface IncomingRequest<P = unknown, R = unknown> {
|
|
408
|
+
/** The id the server sent. Replies carry it back. */
|
|
409
|
+
readonly id: number | string;
|
|
410
|
+
/** The method it was sent for. */
|
|
411
|
+
readonly method: string;
|
|
412
|
+
/** The `params` from `request`. They come from the server, so validate them. */
|
|
413
|
+
readonly params: P;
|
|
414
|
+
/**
|
|
415
|
+
* Aborts when the answer is no longer wanted: the server cancelled the request, it timed out,
|
|
416
|
+
* the connection it arrived on closed, the responder was disposed, or it was answered with
|
|
417
|
+
* `respond` or `reject` while its handler was still running.
|
|
418
|
+
*/
|
|
419
|
+
readonly signal: AbortSignal;
|
|
420
|
+
/**
|
|
421
|
+
* Reply with `result`. Only the first answer counts.
|
|
422
|
+
* @returns `true` if the reply was sent, `false` if the request was already answered or is gone.
|
|
423
|
+
*/
|
|
424
|
+
respond(result: R): boolean;
|
|
425
|
+
/**
|
|
426
|
+
* Reply with an error, built by the `error` option. Only the first answer counts.
|
|
427
|
+
* @returns `true` if the reply was sent, `false` if the request was already answered or is gone.
|
|
428
|
+
*/
|
|
429
|
+
reject(error: unknown): boolean;
|
|
483
430
|
}
|
|
484
431
|
/**
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
* Replies are ordinary messages: they also reach `onMessage`, `lastMessage` and state stores.
|
|
488
|
-
*
|
|
489
|
-
* @example
|
|
490
|
-
* ```ts
|
|
491
|
-
* type Request = { type: 'join'; room: string } | { type: 'leave' };
|
|
492
|
-
*
|
|
493
|
-
* export const rpc = createWebSocketRequests(game, {
|
|
494
|
-
* send: (message: Request, id) => ({ ...message, id }),
|
|
495
|
-
* reply: message => (message.type === 'reply' ? message.id : undefined),
|
|
496
|
-
* error: message => (message.type === 'reply' && message.error ? new Error(message.error) : undefined)
|
|
497
|
-
* });
|
|
498
|
-
*
|
|
499
|
-
* const reply = await rpc.request({ type: 'join', room: 'lobby' }, { timeout: 5000 });
|
|
500
|
-
* ```
|
|
432
|
+
* Answers a request in code. Return (or resolve) the result to reply with it; throw (or reject)
|
|
433
|
+
* to send the `error` reply instead.
|
|
501
434
|
*/
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
435
|
+
type RequestHandler<P = unknown, R = unknown> = (params: P, request: IncomingRequest<P, R>) => R | PromiseLike<R>;
|
|
436
|
+
/** Answers the server's requests on one client. Create with {@link createWebSocketResponder}. */
|
|
437
|
+
interface WebSocketResponder<P = unknown, R = unknown> {
|
|
438
|
+
/** The client requests arrive on. Hooks reading the responder keep it connected. */
|
|
439
|
+
readonly client: WebSocketClient<any, any>;
|
|
440
|
+
/**
|
|
441
|
+
* Answer requests for `method` until the returned function is called.
|
|
442
|
+
*
|
|
443
|
+
* With a `handler`, requests are answered in code. Without one, they wait in
|
|
444
|
+
* {@link getSnapshot} until something calls `respond` or `reject` on them, e.g. a dialog
|
|
445
|
+
* that asks the user. When several registrations exist for one method, the newest one takes
|
|
446
|
+
* new requests, and unregistering it hands them back to the previous one. Requests for a
|
|
447
|
+
* method with no registration get a `NotFoundError` reply.
|
|
448
|
+
*
|
|
449
|
+
* @returns An unregister function (safe to call more than once). Requests already waiting stay.
|
|
450
|
+
*/
|
|
451
|
+
onRequest(method: string, handler?: RequestHandler<P, R>): () => void;
|
|
452
|
+
/**
|
|
453
|
+
* Requests waiting for an answer from outside a handler, oldest first. Returns the same array
|
|
454
|
+
* until it changes.
|
|
455
|
+
*/
|
|
456
|
+
getSnapshot(): readonly IncomingRequest<P, R>[];
|
|
457
|
+
/** Called whenever {@link getSnapshot} changes. Compatible with `useSyncExternalStore`. */
|
|
458
|
+
subscribe(listener: () => void): () => void;
|
|
459
|
+
/**
|
|
460
|
+
* Stop answering: send an `AbortError` reply to every request in flight and stop listening.
|
|
461
|
+
* Only needed for responders created dynamically.
|
|
462
|
+
*/
|
|
463
|
+
dispose(): void;
|
|
513
464
|
}
|
|
465
|
+
//#endregion
|
|
466
|
+
//#region src/state.d.ts
|
|
514
467
|
/**
|
|
515
468
|
* State derived from a client's messages. Read it with `useWebSocketState(store, selector)`,
|
|
516
469
|
* or with `getState`/`subscribe` outside React.
|
|
@@ -518,122 +471,74 @@ interface WebSocketStateOptions<S, In> {
|
|
|
518
471
|
* @typeParam S - The state type. Don't use a function type: `setState` treats functions as updaters.
|
|
519
472
|
*/
|
|
520
473
|
interface WebSocketState<S> {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Keep state derived from a client's messages, like a reducer that runs on every message.
|
|
538
|
-
* It starts listening immediately and keeps the state across reconnects; it doesn't open
|
|
539
|
-
* the connection by itself.
|
|
540
|
-
*
|
|
541
|
-
* @example
|
|
542
|
-
* ```ts
|
|
543
|
-
* const prices = createWebSocket<Tick>('wss://api.example.com/prices', { json: true });
|
|
544
|
-
*
|
|
545
|
-
* export const priceState = createWebSocketState(prices, {
|
|
546
|
-
* initialState: {} as Record<string, number>,
|
|
547
|
-
* reduce: (state, tick) => ({ ...state, [tick.symbol]: tick.price })
|
|
548
|
-
* });
|
|
549
|
-
*
|
|
550
|
-
* // In a component: re-renders only when ACME's price changes.
|
|
551
|
-
* const acme = useWebSocketState(priceState, state => state.ACME);
|
|
552
|
-
* ```
|
|
553
|
-
*/
|
|
554
|
-
declare function createWebSocketState<S, In>(client: WebSocketClient<In, any>, options: WebSocketStateOptions<S, NoInfer<In>>): WebSocketState<S>;
|
|
555
|
-
|
|
556
|
-
/** Options for {@link createWebSocketTopics}: the messages your server expects. */
|
|
557
|
-
interface WebSocketTopicsOptions<Out> {
|
|
558
|
-
/** The message that subscribes to `topic`. */
|
|
559
|
-
subscribe: (topic: string) => Out;
|
|
560
|
-
/** The message that unsubscribes from `topic`. Omit it if your server has none. */
|
|
561
|
-
unsubscribe?: (topic: string) => Out;
|
|
474
|
+
/** The client whose messages this store reduces. Hooks reading the store keep it connected. */
|
|
475
|
+
readonly client: WebSocketClient<any, any>;
|
|
476
|
+
/** The `initialState` it was created with. Used for server rendering and hydration. */
|
|
477
|
+
readonly initialState: S;
|
|
478
|
+
/** The current state. Returns the same object until it changes. */
|
|
479
|
+
getState(): S;
|
|
480
|
+
/** Called whenever the state changes. Compatible with `useSyncExternalStore`. */
|
|
481
|
+
subscribe(listener: () => void): () => void;
|
|
482
|
+
/** Replace the state, or update it from the current one, e.g. for optimistic updates. */
|
|
483
|
+
setState(update: S | ((state: S) => S)): void;
|
|
484
|
+
/** Go back to `initialState`. */
|
|
485
|
+
reset(): void;
|
|
486
|
+
/** Stop reducing messages. Only needed for stores created dynamically rather than at module level. */
|
|
487
|
+
dispose(): void;
|
|
562
488
|
}
|
|
489
|
+
//#endregion
|
|
490
|
+
//#region src/topics.d.ts
|
|
563
491
|
/** Reference-counted topic subscriptions on one client. Create with {@link createWebSocketTopics}. */
|
|
564
492
|
interface WebSocketTopics {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
493
|
+
/** The client the subscription messages are sent on. */
|
|
494
|
+
readonly client: WebSocketClient<any, any>;
|
|
495
|
+
/**
|
|
496
|
+
* Hold a subscription to `topic`, and the client's connection, until the returned
|
|
497
|
+
* function is called. The release function is safe to call more than once.
|
|
498
|
+
*/
|
|
499
|
+
acquire(topic: string): () => void;
|
|
500
|
+
/** The topics currently held, in the order they were first acquired. */
|
|
501
|
+
active(): string[];
|
|
502
|
+
/** Stop managing subscriptions. Only needed for managers created dynamically. */
|
|
503
|
+
dispose(): void;
|
|
576
504
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
*
|
|
580
|
-
* - The first holder of a topic sends `subscribe(topic)`. Later holders share it.
|
|
581
|
-
* - A tick after the last holder releases it, `unsubscribe(topic)` is sent, so StrictMode
|
|
582
|
-
* and route transitions don't churn subscriptions.
|
|
583
|
-
* - After every reconnect, every held topic is subscribed again on the new socket, before
|
|
584
|
-
* messages queued while disconnected are sent.
|
|
585
|
-
*
|
|
586
|
-
* Subscriptions are only sent while the socket is open; nothing is queued for them.
|
|
587
|
-
*
|
|
588
|
-
* @example
|
|
589
|
-
* ```ts
|
|
590
|
-
* export const topics = createWebSocketTopics(live, {
|
|
591
|
-
* subscribe: topic => ({ type: 'subscribe', topic }),
|
|
592
|
-
* unsubscribe: topic => ({ type: 'unsubscribe', topic })
|
|
593
|
-
* });
|
|
594
|
-
*
|
|
595
|
-
* // In a component:
|
|
596
|
-
* useWebSocketTopic(topics, `prices:${symbol}`);
|
|
597
|
-
* ```
|
|
598
|
-
*/
|
|
599
|
-
declare function createWebSocketTopics<const Out>(client: WebSocketClient<any, Out>, options: WebSocketTopicsOptions<NoInfer<Out>>): WebSocketTopics;
|
|
600
|
-
|
|
505
|
+
//#endregion
|
|
506
|
+
//#region src/react.d.ts
|
|
601
507
|
/**
|
|
602
508
|
* Per-component event handlers. They always see the component's latest props and
|
|
603
509
|
* state, and changing them never reconnects.
|
|
604
510
|
*/
|
|
605
511
|
interface WebSocketHandlers<In> {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
512
|
+
/** The socket opened (initially or after a reconnect). */
|
|
513
|
+
onOpen?: (event: Event) => void;
|
|
514
|
+
/** A message arrived and passed `parse`/`json` and `schema`. */
|
|
515
|
+
onMessage?: (message: In, event: MessageEvent) => void;
|
|
516
|
+
/** A socket closed. Called whether or not it will be retried. */
|
|
517
|
+
onClose?: (close: CloseInfo) => void;
|
|
518
|
+
/** A socket error `Event`, a `MessageError`, or an `Error` from the constructor or `url` function. */
|
|
519
|
+
onError?: (error: Event | Error) => void;
|
|
520
|
+
/**
|
|
521
|
+
* `false` releases this component's hold on the connection and stops its handlers.
|
|
522
|
+
* The connection closes once nothing else holds it.
|
|
523
|
+
* @default true
|
|
524
|
+
*/
|
|
525
|
+
enabled?: boolean;
|
|
620
526
|
}
|
|
621
527
|
/** Options for `useWebSocket(url, options)`: connection options plus handlers. */
|
|
622
|
-
interface UseWebSocketOptions<In, Out> extends WebSocketOptions<In, Out>, WebSocketHandlers<In> {
|
|
623
|
-
}
|
|
528
|
+
interface UseWebSocketOptions<In, Out> extends WebSocketOptions<In, Out>, WebSocketHandlers<In> {}
|
|
624
529
|
/** What `useWebSocket` returns: the current {@link Snapshot} plus stable actions. */
|
|
625
530
|
interface UseWebSocketResult<In, Out> extends Snapshot<In> {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
531
|
+
/**
|
|
532
|
+
* Send now, or queue until open (see {@link SendOptions} for volatile and keyed sends).
|
|
533
|
+
* Returns `true` if sent immediately. Stable identity.
|
|
534
|
+
*/
|
|
535
|
+
send: (message: Out, options?: SendOptions) => boolean;
|
|
536
|
+
/** Close and stop reconnecting (for every component sharing the connection). Stable identity. */
|
|
537
|
+
close: (code?: number, reason?: string) => void;
|
|
538
|
+
/** Drop the socket and connect again right away. Stable identity. */
|
|
539
|
+
reconnect: () => void;
|
|
540
|
+
/** The underlying client, or `null` when the URL is `null`/`undefined`. */
|
|
541
|
+
client: WebSocketClient<In, Out> | null;
|
|
637
542
|
}
|
|
638
543
|
/**
|
|
639
544
|
* Connect to a WebSocket and re-render with its status and latest message.
|
|
@@ -658,8 +563,8 @@ interface UseWebSocketResult<In, Out> extends Snapshot<In> {
|
|
|
658
563
|
* });
|
|
659
564
|
* ```
|
|
660
565
|
*/
|
|
661
|
-
declare function useWebSocket<In = unknown, Out = unknown>(url: string | null | undefined, options?: UseWebSocketOptions<In, Out>): UseWebSocketResult<In, Out>;
|
|
662
|
-
declare function useWebSocket<In, Out>(client: WebSocketClient<In, Out> | null | undefined, options?: WebSocketHandlers<In>): UseWebSocketResult<In, Out>;
|
|
566
|
+
export declare function useWebSocket<In = unknown, Out = unknown>(url: string | null | undefined, options?: UseWebSocketOptions<In, Out>): UseWebSocketResult<In, Out>;
|
|
567
|
+
export declare function useWebSocket<In, Out>(client: WebSocketClient<In, Out> | null | undefined, options?: WebSocketHandlers<In>): UseWebSocketResult<In, Out>;
|
|
663
568
|
/**
|
|
664
569
|
* Listen to a client's events without re-rendering the component. The listener always
|
|
665
570
|
* sees the latest props and state. Keeps the connection open while mounted.
|
|
@@ -669,7 +574,7 @@ declare function useWebSocket<In, Out>(client: WebSocketClient<In, Out> | null |
|
|
|
669
574
|
* useWebSocketEvent(chat, 'message', message => setMessages(list => [...list, message]));
|
|
670
575
|
* ```
|
|
671
576
|
*/
|
|
672
|
-
declare function useWebSocketEvent<In, K extends keyof EventMap<In>>(client: WebSocketClient<In, any> | null | undefined, type: K, listener: EventMap<In>[K]): void;
|
|
577
|
+
export declare function useWebSocketEvent<In, K extends keyof EventMap<In>>(client: WebSocketClient<In, any> | null | undefined, type: K, listener: EventMap<In>[K]): void;
|
|
673
578
|
/**
|
|
674
579
|
* The connection status of a client. Re-renders only when the status changes, not on
|
|
675
580
|
* messages. Keeps the connection open while mounted.
|
|
@@ -679,7 +584,7 @@ declare function useWebSocketEvent<In, K extends keyof EventMap<In>>(client: Web
|
|
|
679
584
|
* const status = useWebSocketStatus(chat); // 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed'
|
|
680
585
|
* ```
|
|
681
586
|
*/
|
|
682
|
-
declare function useWebSocketStatus(client: WebSocketClient<any, any> | null | undefined): Status;
|
|
587
|
+
export declare function useWebSocketStatus(client: WebSocketClient<any, any> | null | undefined): Status;
|
|
683
588
|
/**
|
|
684
589
|
* Read state built from messages by `createWebSocketState`. Re-renders only when the
|
|
685
590
|
* selected value changes (by `isEqual`, default `Object.is`), however often messages
|
|
@@ -692,8 +597,8 @@ declare function useWebSocketStatus(client: WebSocketClient<any, any> | null | u
|
|
|
692
597
|
* const acme = useWebSocketState(priceState, state => state.ACME);
|
|
693
598
|
* ```
|
|
694
599
|
*/
|
|
695
|
-
declare function useWebSocketState<S>(store: WebSocketState<S>): S;
|
|
696
|
-
declare function useWebSocketState<S, T>(store: WebSocketState<S>, selector: (state: S) => T, isEqual?: (a: T, b: T) => boolean): T;
|
|
600
|
+
export declare function useWebSocketState<S>(store: WebSocketState<S>): S;
|
|
601
|
+
export declare function useWebSocketState<S, T>(store: WebSocketState<S>, selector: (state: S) => T, isEqual?: (a: T, b: T) => boolean): T;
|
|
697
602
|
/**
|
|
698
603
|
* Subscribe to one topic (or several) while mounted, through a `createWebSocketTopics`
|
|
699
604
|
* manager. Components holding the same topic share one subscription, and it is sent again
|
|
@@ -705,7 +610,7 @@ declare function useWebSocketState<S, T>(store: WebSocketState<S>, selector: (st
|
|
|
705
610
|
* useWebSocketTopic(topics, watchlist.map(symbol => `prices:${symbol}`));
|
|
706
611
|
* ```
|
|
707
612
|
*/
|
|
708
|
-
declare function useWebSocketTopic(topics: WebSocketTopics | null | undefined, topic: string | readonly string[] | null | undefined): void;
|
|
613
|
+
export declare function useWebSocketTopic(topics: WebSocketTopics | null | undefined, topic: string | readonly string[] | null | undefined): void;
|
|
709
614
|
/**
|
|
710
615
|
* Read round-trip time and server clock offset from a `createWebSocketLatency` instance.
|
|
711
616
|
* Re-renders when a measurement changes, about once per ping interval. Keeps the connection
|
|
@@ -717,6 +622,30 @@ declare function useWebSocketTopic(topics: WebSocketTopics | null | undefined, t
|
|
|
717
622
|
* return <span>{rtt === undefined ? '–' : `${Math.round(rtt)} ms`}</span>;
|
|
718
623
|
* ```
|
|
719
624
|
*/
|
|
720
|
-
declare function useWebSocketLatency(latency: WebSocketLatency): Latency;
|
|
721
|
-
|
|
722
|
-
|
|
625
|
+
export declare function useWebSocketLatency(latency: WebSocketLatency): Latency;
|
|
626
|
+
/**
|
|
627
|
+
* Requests for `method` that a person answers, e.g. an agent asking to run a tool. While mounted,
|
|
628
|
+
* it registers `method` on the responder without a handler, so new requests wait here instead of
|
|
629
|
+
* getting a `NotFoundError` reply. Call `respond` or `reject` on one to answer it. Re-renders
|
|
630
|
+
* when the list changes. Keeps the connection open while mounted. `null` holds nothing.
|
|
631
|
+
*
|
|
632
|
+
* Requests stay listed after unmount until they are answered, time out, are cancelled or lose
|
|
633
|
+
* their connection, so a remounted dialog shows them again. On the server the list is empty.
|
|
634
|
+
*
|
|
635
|
+
* @example
|
|
636
|
+
* ```tsx
|
|
637
|
+
* const [request] = useAgentApproval(tools, 'approve');
|
|
638
|
+
* if (!request) return null;
|
|
639
|
+
* return (
|
|
640
|
+
* <dialog open>
|
|
641
|
+
* {request.params.summary}
|
|
642
|
+
* <button onClick={() => request.respond({ approved: true })}>Approve</button>
|
|
643
|
+
* <button onClick={() => request.respond({ approved: false })}>Deny</button>
|
|
644
|
+
* </dialog>
|
|
645
|
+
* );
|
|
646
|
+
* ```
|
|
647
|
+
*/
|
|
648
|
+
export declare function useAgentApproval<P, R>(responder: WebSocketResponder<P, R> | null | undefined, method: string | null | undefined): readonly IncomingRequest<P, R>[];
|
|
649
|
+
//#endregion
|
|
650
|
+
export type { UseWebSocketOptions, UseWebSocketResult, WebSocketHandlers };
|
|
651
|
+
//# sourceMappingURL=index.d.ts.map
|