react-websocket 3.6.0 → 4.18.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 +34 -5
- package/dist/ag-ui.cjs +421 -0
- package/dist/ag-ui.cjs.map +1 -0
- package/dist/ag-ui.d.cts +493 -0
- package/dist/ag-ui.d.ts +171 -0
- package/dist/ag-ui.js +418 -0
- package/dist/ag-ui.js.map +1 -0
- package/dist/core.cjs +727 -162
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +501 -62
- package/dist/core.d.ts +501 -62
- package/dist/core.js +723 -163
- package/dist/core.js.map +1 -1
- package/dist/devtools.cjs +186 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.cts +32 -0
- package/dist/devtools.d.ts +32 -0
- package/dist/devtools.js +185 -0
- package/dist/devtools.js.map +1 -0
- package/dist/index.cjs +214 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +705 -56
- package/dist/index.d.ts +421 -30
- package/dist/index.js +211 -21
- package/dist/index.js.map +1 -1
- package/dist/outbox.cjs +222 -0
- package/dist/outbox.cjs.map +1 -0
- package/dist/outbox.d.cts +127 -0
- package/dist/outbox.d.ts +127 -0
- package/dist/outbox.js +219 -0
- package/dist/outbox.js.map +1 -0
- package/dist/replay.cjs +324 -0
- package/dist/replay.cjs.map +1 -0
- package/dist/replay.d.cts +124 -0
- package/dist/replay.d.ts +124 -0
- package/dist/replay.js +322 -0
- package/dist/replay.js.map +1 -0
- package/dist/tabs.cjs +336 -0
- package/dist/tabs.cjs.map +1 -0
- package/dist/tabs.d.cts +81 -0
- package/dist/tabs.d.ts +81 -0
- package/dist/tabs.js +335 -0
- package/dist/tabs.js.map +1 -0
- package/dist/timeline.d.cts +1 -213
- package/dist/types-BEecFqIB.d.cts +268 -0
- package/dist/webtransport.cjs +243 -0
- package/dist/webtransport.cjs.map +1 -0
- package/dist/webtransport.d.cts +67 -0
- package/dist/webtransport.d.ts +67 -0
- package/dist/webtransport.js +242 -0
- package/dist/webtransport.js.map +1 -0
- package/package.json +139 -3
package/dist/core.d.cts
CHANGED
|
@@ -20,7 +20,8 @@ type UrlSource = string | (() => string | Promise<string>);
|
|
|
20
20
|
* - `open`: connected. `send()` goes out immediately.
|
|
21
21
|
* - `reconnecting`: the connection dropped (or an attempt failed) and a retry is scheduled or in progress.
|
|
22
22
|
* - `closed`: stopped for good, by `close()` or after giving up on reconnecting.
|
|
23
|
-
* It stays closed until `connect()` or `reconnect()` is called, or every hook unmounts.
|
|
23
|
+
* It stays closed until `connect()` or `reconnect()` is called, or every hook unmounts. If
|
|
24
|
+
* nothing held it when it stopped, the next hook to mount starts it again.
|
|
24
25
|
*/
|
|
25
26
|
type Status = 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed';
|
|
26
27
|
/** The client's state as seen by React. A new object is created on every change. */
|
|
@@ -29,7 +30,10 @@ interface Snapshot<In> {
|
|
|
29
30
|
readonly status: Status;
|
|
30
31
|
/** Most recent message (after `parse`/`json` and `schema`), kept across reconnects. */
|
|
31
32
|
readonly lastMessage: In | undefined;
|
|
32
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Consecutive failed attempts since the last stable connection. A connection that closes
|
|
35
|
+
* within `reconnect.minUptime` of opening counts as failed. `0` while open.
|
|
36
|
+
*/
|
|
33
37
|
readonly attempt: number;
|
|
34
38
|
}
|
|
35
39
|
/** Details of a closed connection, from the browser's `CloseEvent`. */
|
|
@@ -51,6 +55,14 @@ interface ReconnectOptions {
|
|
|
51
55
|
* @default Infinity
|
|
52
56
|
*/
|
|
53
57
|
maxAttempts?: number;
|
|
58
|
+
/**
|
|
59
|
+
* How long a connection must stay open, in ms, before the backoff resets. One that closes
|
|
60
|
+
* sooner counts as a failed attempt, so a server that accepts and then closes right away
|
|
61
|
+
* (e.g. rejecting the credentials sent on open) is retried with a growing delay, and
|
|
62
|
+
* `maxAttempts` still applies. `0` resets on every open.
|
|
63
|
+
* @default 5000
|
|
64
|
+
*/
|
|
65
|
+
minUptime?: number;
|
|
54
66
|
/**
|
|
55
67
|
* Delay before the first retry, in ms.
|
|
56
68
|
* @default 1000
|
|
@@ -68,21 +80,67 @@ interface ReconnectOptions {
|
|
|
68
80
|
factor?: number;
|
|
69
81
|
/**
|
|
70
82
|
* Randomly shave up to this fraction (0–1) off each delay, so that many clients
|
|
71
|
-
* dropped at once don't all reconnect at the same moment.
|
|
72
|
-
*
|
|
83
|
+
* dropped at once don't all reconnect at the same moment. The default `1` picks each
|
|
84
|
+
* delay at random between 0 and the backoff ("full jitter").
|
|
85
|
+
* @default 1
|
|
73
86
|
*/
|
|
74
87
|
jitter?: number;
|
|
75
88
|
/**
|
|
76
89
|
* Called on every unexpected close. Return `false` to stop reconnecting.
|
|
77
90
|
*
|
|
91
|
+
* The default, `retryableClose`, retries everything except `1002` (protocol error), `1003`
|
|
92
|
+
* (unsupported data) and `1008` (policy violation). Your own function replaces it; call
|
|
93
|
+
* `retryableClose` from it to keep that rule, or return `true` to retry every close.
|
|
94
|
+
*
|
|
78
95
|
* @example
|
|
79
96
|
* ```ts
|
|
80
|
-
*
|
|
81
|
-
*
|
|
97
|
+
* import { retryableClose } from 'react-websocket';
|
|
98
|
+
*
|
|
99
|
+
* // Also stop when our server rejects the credentials.
|
|
100
|
+
* shouldReconnect: close => close.code !== 4001 && retryableClose(close)
|
|
82
101
|
* ```
|
|
83
|
-
* @default
|
|
102
|
+
* @default retryableClose
|
|
84
103
|
*/
|
|
85
104
|
shouldReconnect?: (close: CloseInfo) => boolean;
|
|
105
|
+
/**
|
|
106
|
+
* How long the server asked the client to wait before trying again, in ms, read from the
|
|
107
|
+
* close. Return `undefined` when it didn't ask. The client waits that long plus the usual
|
|
108
|
+
* backoff, so clients told the same delay still spread out, and the `online` and
|
|
109
|
+
* `visibilitychange` events don't cut the wait short.
|
|
110
|
+
*
|
|
111
|
+
* The default, `retryAfter`, reads a `1012` (Service Restart) or `1013` (Try Again Later)
|
|
112
|
+
* close whose reason is a number of seconds, like an HTTP `Retry-After` header. Your own
|
|
113
|
+
* function replaces it.
|
|
114
|
+
*
|
|
115
|
+
* A `1013` close that names no delay still waits longer than other closes: its backoff starts
|
|
116
|
+
* at `maxDelay`.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* import { retryAfter } from 'react-websocket';
|
|
121
|
+
*
|
|
122
|
+
* // Our server also closes with 4429 and the delay in ms as the reason.
|
|
123
|
+
* retryAfter: close => (close.code === 4429 ? Number(close.reason) : retryAfter(close))
|
|
124
|
+
* ```
|
|
125
|
+
* @default retryAfter
|
|
126
|
+
*/
|
|
127
|
+
retryAfter?: (close: CloseInfo) => number | undefined;
|
|
128
|
+
}
|
|
129
|
+
/** Options for `client.migrate()`. */
|
|
130
|
+
interface MigrateOptions {
|
|
131
|
+
/**
|
|
132
|
+
* Where to connect instead: a URL, or a function called before the connection, like the
|
|
133
|
+
* client's own `url`. Later reconnects go there too, until the client is closed, gives up or
|
|
134
|
+
* is torn down.
|
|
135
|
+
* @default the client's current URL
|
|
136
|
+
*/
|
|
137
|
+
url?: UrlSource;
|
|
138
|
+
/**
|
|
139
|
+
* Move at a random moment within this many ms, so that clients asked to move together don't
|
|
140
|
+
* all reconnect at once.
|
|
141
|
+
* @default 0
|
|
142
|
+
*/
|
|
143
|
+
within?: number;
|
|
86
144
|
}
|
|
87
145
|
/** Application-level keep-alive. Useful behind proxies that drop idle connections. */
|
|
88
146
|
interface HeartbeatOptions {
|
|
@@ -98,6 +156,10 @@ interface HeartbeatOptions {
|
|
|
98
156
|
/**
|
|
99
157
|
* If nothing at all arrives within this many ms after a ping, the connection is
|
|
100
158
|
* treated as dead: it closes with code `4000` and reconnects.
|
|
159
|
+
*
|
|
160
|
+
* A ping also goes out right away when the browser comes back online or the tab becomes
|
|
161
|
+
* visible, so a connection that died during sleep is found within `timeout`, not after up
|
|
162
|
+
* to `interval` more.
|
|
101
163
|
*/
|
|
102
164
|
timeout?: number;
|
|
103
165
|
}
|
|
@@ -142,11 +204,19 @@ interface SendOptions {
|
|
|
142
204
|
* ```
|
|
143
205
|
*/
|
|
144
206
|
maxBuffered?: number;
|
|
207
|
+
/**
|
|
208
|
+
* Drop the message instead of sending it if it has been queued for longer than this many
|
|
209
|
+
* ms, for messages that go stale during a long outage, such as "typing" or a price quote.
|
|
210
|
+
* Overrides `queue.maxAge`. Checked when the socket opens and whenever another message is
|
|
211
|
+
* queued; `onDrop` is called then.
|
|
212
|
+
*/
|
|
213
|
+
maxAge?: number;
|
|
145
214
|
/**
|
|
146
215
|
* Called if the message is dropped from the queue without being sent: replaced by a
|
|
147
|
-
* newer message with the same `key`,
|
|
148
|
-
* queue is cleared because the client closed or gave up, or when `send()`
|
|
149
|
-
* message right away (`volatile` while not open, or `maxBuffered`) and returns
|
|
216
|
+
* newer message with the same `key`, evicted by `queue.limit`, or older than `maxAge`. Not
|
|
217
|
+
* called when the queue is cleared because the client closed or gave up, or when `send()`
|
|
218
|
+
* drops the message right away (`volatile` while not open, or `maxBuffered`) and returns
|
|
219
|
+
* `false`.
|
|
150
220
|
*/
|
|
151
221
|
onDrop?: () => void;
|
|
152
222
|
}
|
|
@@ -155,9 +225,17 @@ interface QueueOptions {
|
|
|
155
225
|
/**
|
|
156
226
|
* Maximum number of messages held while not connected. When full, the oldest
|
|
157
227
|
* message is dropped, skipping `priority` messages unless nothing else is queued.
|
|
158
|
-
*
|
|
228
|
+
* `Infinity` keeps every message.
|
|
229
|
+
* @default 100
|
|
159
230
|
*/
|
|
160
231
|
limit?: number;
|
|
232
|
+
/**
|
|
233
|
+
* Drop a queued message instead of sending it once it has waited longer than this many ms,
|
|
234
|
+
* calling its `onDrop`. A message's own `maxAge` overrides it. Checked when the socket opens
|
|
235
|
+
* and whenever another message is queued.
|
|
236
|
+
* @default Infinity
|
|
237
|
+
*/
|
|
238
|
+
maxAge?: number;
|
|
161
239
|
}
|
|
162
240
|
/**
|
|
163
241
|
* The minimal [Standard Schema v1](https://standardschema.dev) interface, implemented
|
|
@@ -231,8 +309,8 @@ interface WebSocketOptions<In = unknown, Out = unknown> {
|
|
|
231
309
|
heartbeat?: HeartbeatOptions;
|
|
232
310
|
/**
|
|
233
311
|
* Buffer `send()` calls made while not connected and send them, in order, on open
|
|
234
|
-
* (right after `open` listeners run).
|
|
235
|
-
* `
|
|
312
|
+
* (right after `open` listeners run). It holds up to 100 messages, dropping the oldest;
|
|
313
|
+
* `{ limit }` changes that. `false` drops messages instead of queueing them.
|
|
236
314
|
* The queue is cleared by `close()` or when the client gives up.
|
|
237
315
|
* @default true
|
|
238
316
|
*/
|
|
@@ -356,7 +434,10 @@ interface WebSocketClient<In = unknown, Out = unknown> {
|
|
|
356
434
|
/**
|
|
357
435
|
* Close the connection and stop reconnecting. The status becomes `closed` and the
|
|
358
436
|
* send queue is cleared. Mounted hooks stay mounted; call `connect()` or `reconnect()`
|
|
359
|
-
* to start again.
|
|
437
|
+
* to start again. Closed while no hook holds it, the next hook to mount starts it again.
|
|
438
|
+
*
|
|
439
|
+
* Like `WebSocket.close()`, it throws for a code or reason the socket rejects (browsers take
|
|
440
|
+
* `1000` or `3000`–`4999`, and a reason up to 123 bytes), and then nothing has changed.
|
|
360
441
|
* @param code - Close code. @default 1000
|
|
361
442
|
*/
|
|
362
443
|
close(code?: number, reason?: string): void;
|
|
@@ -365,6 +446,28 @@ interface WebSocketClient<In = unknown, Out = unknown> {
|
|
|
365
446
|
* backoff. Also restarts a `closed` client.
|
|
366
447
|
*/
|
|
367
448
|
reconnect(): void;
|
|
449
|
+
/**
|
|
450
|
+
* Move to a new connection without a gap, for example when the server asks its clients to
|
|
451
|
+
* before a deploy. At a random moment within `options.within` ms, it opens a new connection
|
|
452
|
+
* (to `options.url`, if given) and, once that one is open, closes the current one with code
|
|
453
|
+
* `1000`. Until then the current connection keeps working, so the status stays `open` and
|
|
454
|
+
* `send()` doesn't queue.
|
|
455
|
+
*
|
|
456
|
+
* `open` listeners run for the new connection, as after any reconnect, so handshakes and
|
|
457
|
+
* topic subscriptions are sent again. Requests the old connection hadn't answered fail as on
|
|
458
|
+
* a lost connection. If the new connection fails before opening, the current one stays. If
|
|
459
|
+
* the client isn't open when the moment comes, only the URL changes, for the next attempt.
|
|
460
|
+
* A later call replaces one that hasn't moved yet.
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* ```ts
|
|
464
|
+
* // The server sends { type: 'reconnect', within: 30000 } before it shuts down.
|
|
465
|
+
* live.on('message', message => {
|
|
466
|
+
* if (message.type === 'reconnect') live.migrate({ within: message.within, url: message.url });
|
|
467
|
+
* });
|
|
468
|
+
* ```
|
|
469
|
+
*/
|
|
470
|
+
migrate(options?: MigrateOptions): void;
|
|
368
471
|
/**
|
|
369
472
|
* Hold the connection open until the returned function is called. This is what the
|
|
370
473
|
* hooks use; call it yourself to integrate with non-React code.
|
|
@@ -374,6 +477,34 @@ interface WebSocketClient<In = unknown, Out = unknown> {
|
|
|
374
477
|
}
|
|
375
478
|
//#endregion
|
|
376
479
|
//#region src/client.d.ts
|
|
480
|
+
/**
|
|
481
|
+
* The default `reconnect.shouldReconnect`: retry every close except `1002` (protocol error),
|
|
482
|
+
* `1003` (unsupported data) and `1008` (policy violation, often a rejected login), which
|
|
483
|
+
* retrying the same way can't fix. Call it from your own `shouldReconnect` to keep that rule.
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* ```ts
|
|
487
|
+
* createWebSocket(url, {
|
|
488
|
+
* // Also stop on our server's "invalid token" code.
|
|
489
|
+
* reconnect: { shouldReconnect: close => close.code !== 4001 && retryableClose(close) }
|
|
490
|
+
* });
|
|
491
|
+
* ```
|
|
492
|
+
*/
|
|
493
|
+
export declare function retryableClose(close: CloseInfo): boolean;
|
|
494
|
+
/**
|
|
495
|
+
* The default `reconnect.retryAfter`: for a `1012` (Service Restart) or `1013` (Try Again Later)
|
|
496
|
+
* close whose reason is a number of seconds, like an HTTP `Retry-After` header, that delay in ms.
|
|
497
|
+
* Call it from your own `retryAfter` to keep reading it.
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```ts
|
|
501
|
+
* createWebSocket(url, {
|
|
502
|
+
* // Our server also closes with 4429 and the delay in ms as the reason.
|
|
503
|
+
* reconnect: { retryAfter: close => (close.code === 4429 ? Number(close.reason) : retryAfter(close)) }
|
|
504
|
+
* });
|
|
505
|
+
* ```
|
|
506
|
+
*/
|
|
507
|
+
export declare function retryAfter(close: CloseInfo): number | undefined;
|
|
377
508
|
/**
|
|
378
509
|
* Create a WebSocket client, usually at module level, to share across components.
|
|
379
510
|
*
|
|
@@ -395,6 +526,131 @@ interface WebSocketClient<In = unknown, Out = unknown> {
|
|
|
395
526
|
*/
|
|
396
527
|
export declare function createWebSocket<In = unknown, Out = unknown>(url: UrlSource, options?: WebSocketOptions<In, Out>): WebSocketClient<In, Out>;
|
|
397
528
|
//#endregion
|
|
529
|
+
//#region src/topics.d.ts
|
|
530
|
+
/** @internal Option key for a callback run when nothing holds a topic anymore. Used by feeds. */
|
|
531
|
+
declare const ON_RELEASE: unique symbol;
|
|
532
|
+
/** Options for {@link createWebSocketTopics}: the messages your server expects. */
|
|
533
|
+
interface WebSocketTopicsOptions<Out> {
|
|
534
|
+
/** The message that subscribes to `topic`. */
|
|
535
|
+
subscribe: (topic: string) => Out;
|
|
536
|
+
/** The message that unsubscribes from `topic`. Omit it if your server has none. */
|
|
537
|
+
unsubscribe?: (topic: string) => Out;
|
|
538
|
+
}
|
|
539
|
+
/** Reference-counted topic subscriptions on one client. Create with {@link createWebSocketTopics}. */
|
|
540
|
+
interface WebSocketTopics {
|
|
541
|
+
/** The client the subscription messages are sent on. */
|
|
542
|
+
readonly client: WebSocketClient<any, any>;
|
|
543
|
+
/**
|
|
544
|
+
* Hold a subscription to `topic`, and the client's connection, until the returned
|
|
545
|
+
* function is called. The release function is safe to call more than once.
|
|
546
|
+
*/
|
|
547
|
+
acquire(topic: string): () => void;
|
|
548
|
+
/** The topics currently held, in the order they were first acquired. */
|
|
549
|
+
active(): string[];
|
|
550
|
+
/** Stop managing subscriptions. Only needed for managers created dynamically. */
|
|
551
|
+
dispose(): void;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Manage topic subscriptions that survive reconnects.
|
|
555
|
+
*
|
|
556
|
+
* - The first holder of a topic sends `subscribe(topic)`. Later holders share it.
|
|
557
|
+
* - A tick after the last holder releases it, `unsubscribe(topic)` is sent, so StrictMode
|
|
558
|
+
* and route transitions don't churn subscriptions.
|
|
559
|
+
* - After every reconnect, every held topic is subscribed again on the new socket, before
|
|
560
|
+
* messages queued while disconnected are sent.
|
|
561
|
+
*
|
|
562
|
+
* Subscriptions are only sent while the socket is open; nothing is queued for them.
|
|
563
|
+
*
|
|
564
|
+
* @example
|
|
565
|
+
* ```ts
|
|
566
|
+
* export const topics = createWebSocketTopics(live, {
|
|
567
|
+
* subscribe: topic => ({ type: 'subscribe', topic }),
|
|
568
|
+
* unsubscribe: topic => ({ type: 'unsubscribe', topic })
|
|
569
|
+
* });
|
|
570
|
+
*
|
|
571
|
+
* // In a component:
|
|
572
|
+
* useWebSocketTopic(topics, `prices:${symbol}`);
|
|
573
|
+
* ```
|
|
574
|
+
*/
|
|
575
|
+
export declare function createWebSocketTopics<const Out>(client: WebSocketClient<any, Out>, options: WebSocketTopicsOptions<NoInfer<Out>> & {
|
|
576
|
+
[ON_RELEASE]?: (topic: string) => void;
|
|
577
|
+
}): WebSocketTopics;
|
|
578
|
+
//#endregion
|
|
579
|
+
//#region src/feeds.d.ts
|
|
580
|
+
/** Where a numbered message belongs: its topic, and its sequence number in that topic. */
|
|
581
|
+
interface FeedPosition {
|
|
582
|
+
readonly topic: string;
|
|
583
|
+
/** One more than the message before it on the same topic. */
|
|
584
|
+
readonly seq: number;
|
|
585
|
+
}
|
|
586
|
+
/** Options for {@link createWebSocketFeeds}: the messages your server expects and sends. */
|
|
587
|
+
interface WebSocketFeedsOptions<In, Out> {
|
|
588
|
+
/**
|
|
589
|
+
* The message that subscribes to `topic`. `after` is the sequence number of the last message
|
|
590
|
+
* received on it, so the server can send the ones after it, or `undefined` the first time.
|
|
591
|
+
* Sent on every new connection too.
|
|
592
|
+
*/
|
|
593
|
+
subscribe: (topic: string, after: number | undefined) => Out;
|
|
594
|
+
/** The message that unsubscribes from `topic`. Omit it if your server has none. */
|
|
595
|
+
unsubscribe?: (topic: string) => Out;
|
|
596
|
+
/**
|
|
597
|
+
* The message that asks for what came after `after` when messages went missing on an open
|
|
598
|
+
* connection. Defaults to `subscribe(topic, after)`.
|
|
599
|
+
*/
|
|
600
|
+
resume?: (topic: string, after: number) => Out;
|
|
601
|
+
/** Recognize a numbered message: its topic and sequence number. `undefined` for any other. */
|
|
602
|
+
sequence: (message: In) => FeedPosition | null | undefined;
|
|
603
|
+
/**
|
|
604
|
+
* Recognize a snapshot: a topic's whole state as of `seq`, which the server sends when it
|
|
605
|
+
* can't replay the messages after `after`. It's always delivered, and the numbering goes on
|
|
606
|
+
* from it.
|
|
607
|
+
*/
|
|
608
|
+
snapshot?: (message: In) => FeedPosition | null | undefined;
|
|
609
|
+
/** Called when a message arrives after a gap, before the missing ones are asked for. */
|
|
610
|
+
onGap?: (topic: string, after: number, received: number) => void;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Topic subscriptions whose messages are numbered. A {@link WebSocketTopics}, so
|
|
614
|
+
* `useWebSocketTopic` takes it, and a message source for `createWebSocketState`.
|
|
615
|
+
* Create with {@link createWebSocketFeeds}.
|
|
616
|
+
*/
|
|
617
|
+
interface WebSocketFeeds<In> extends WebSocketTopics {
|
|
618
|
+
/**
|
|
619
|
+
* Listen to messages in order: numbered ones once each and without gaps, others as they
|
|
620
|
+
* arrive. Returns an unsubscribe function.
|
|
621
|
+
*/
|
|
622
|
+
on(type: 'message', listener: (message: In, event: MessageEvent) => void): () => void;
|
|
623
|
+
/** The sequence number of the last message delivered on `topic`, while it's held. */
|
|
624
|
+
position(topic: string): number | undefined;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Subscribe to topics whose messages carry a sequence number, and get each message once, in
|
|
628
|
+
* order, across reconnects:
|
|
629
|
+
*
|
|
630
|
+
* - On every new connection, each held topic is subscribed again with `after`, the last
|
|
631
|
+
* number received, so the server sends what was missed instead of everything.
|
|
632
|
+
* - A message numbered at or below the last one is a duplicate and is dropped.
|
|
633
|
+
* - A message that skips numbers is held back, and the topic asks for what's missing (`resume`)
|
|
634
|
+
* once, so nothing is delivered out of order.
|
|
635
|
+
* - A server that can't replay sends a `snapshot`, and the numbering goes on from it.
|
|
636
|
+
*
|
|
637
|
+
* Listen with `feeds.on('message', …)`, or pass `feeds` to `createWebSocketState` in place of
|
|
638
|
+
* the client. Hold topics with `useWebSocketTopic(feeds, topic)` or `feeds.acquire(topic)`.
|
|
639
|
+
*
|
|
640
|
+
* @example
|
|
641
|
+
* ```ts
|
|
642
|
+
* export const feeds = createWebSocketFeeds(live, {
|
|
643
|
+
* subscribe: (topic, after) => ({ type: 'subscribe', topic, after }),
|
|
644
|
+
* unsubscribe: topic => ({ type: 'unsubscribe', topic }),
|
|
645
|
+
* sequence: message => (message.type === 'event' ? message : undefined),
|
|
646
|
+
* snapshot: message => (message.type === 'snapshot' ? message : undefined)
|
|
647
|
+
* });
|
|
648
|
+
*
|
|
649
|
+
* export const chat = createWebSocketState(feeds, { initialState: [], reduce: addMessage });
|
|
650
|
+
* ```
|
|
651
|
+
*/
|
|
652
|
+
export declare function createWebSocketFeeds<In, const Out>(client: WebSocketClient<In, Out>, options: WebSocketFeedsOptions<NoInfer<In>, NoInfer<Out>>): WebSocketFeeds<In>;
|
|
653
|
+
//#endregion
|
|
398
654
|
//#region src/latency.d.ts
|
|
399
655
|
/** Options for {@link createWebSocketLatency}: how pings and pongs look on the wire. */
|
|
400
656
|
interface WebSocketLatencyOptions<In, Out> {
|
|
@@ -472,6 +728,91 @@ interface WebSocketLatency {
|
|
|
472
728
|
*/
|
|
473
729
|
export declare function createWebSocketLatency<In, const Out>(client: WebSocketClient<In, Out>, options: WebSocketLatencyOptions<NoInfer<In>, NoInfer<Out>>): WebSocketLatency;
|
|
474
730
|
//#endregion
|
|
731
|
+
//#region src/presence.d.ts
|
|
732
|
+
/** Another client's shared state, such as its cursor or what it's selected. */
|
|
733
|
+
interface PresencePeer<P> {
|
|
734
|
+
/** The peer's id, as the server reports it. */
|
|
735
|
+
readonly peer: string;
|
|
736
|
+
readonly state: P;
|
|
737
|
+
/** When its state last arrived, from `Date.now()`. */
|
|
738
|
+
readonly updatedAt: number;
|
|
739
|
+
}
|
|
740
|
+
/** Options for {@link createWebSocketPresence}: the messages your server relays. */
|
|
741
|
+
interface WebSocketPresenceOptions<P, In, Out> {
|
|
742
|
+
/** The message that shares this client's state with the others. Annotate `state`. */
|
|
743
|
+
send: (state: P) => Out;
|
|
744
|
+
/** The message that says this client left, sent by `leave()`. Omit it if the server has none. */
|
|
745
|
+
leave?: () => Out;
|
|
746
|
+
/**
|
|
747
|
+
* Recognize another peer's state: its id and state, or `state: null` when it left. `undefined`
|
|
748
|
+
* for any other message.
|
|
749
|
+
*/
|
|
750
|
+
receive: (message: In) => {
|
|
751
|
+
readonly peer: string;
|
|
752
|
+
readonly state: P | null;
|
|
753
|
+
} | null | undefined;
|
|
754
|
+
/**
|
|
755
|
+
* Forget a peer that hasn't sent its state for this long, in ms, as if it had left. Covers
|
|
756
|
+
* peers whose connection died without the server noticing.
|
|
757
|
+
* @default 30000
|
|
758
|
+
*/
|
|
759
|
+
timeout?: number;
|
|
760
|
+
/**
|
|
761
|
+
* Send this client's state again this often while it doesn't change, in ms, so the others
|
|
762
|
+
* don't forget it.
|
|
763
|
+
* @default timeout / 3
|
|
764
|
+
*/
|
|
765
|
+
refresh?: number;
|
|
766
|
+
/**
|
|
767
|
+
* How many times per second this client's state may be sent. Changes in between are
|
|
768
|
+
* coalesced: the newest goes out at the end of the interval.
|
|
769
|
+
* @default 20
|
|
770
|
+
*/
|
|
771
|
+
maxRate?: number;
|
|
772
|
+
}
|
|
773
|
+
/** Shared per-peer state that expires. Create with {@link createWebSocketPresence}. */
|
|
774
|
+
interface WebSocketPresence<P> {
|
|
775
|
+
/** The client the state is shared on. */
|
|
776
|
+
readonly client: WebSocketClient<any, any>;
|
|
777
|
+
/**
|
|
778
|
+
* Share this client's state, e.g. on every pointer move. It's sent at most `maxRate` times per
|
|
779
|
+
* second, the newest only, and again on every new connection and every `refresh`.
|
|
780
|
+
*/
|
|
781
|
+
set(state: P): void;
|
|
782
|
+
/** Stop sharing, and send `leave`. `set()` shares again. */
|
|
783
|
+
leave(): void;
|
|
784
|
+
/** The other peers, in the order they first appeared. A new array on every change. */
|
|
785
|
+
getSnapshot(): readonly PresencePeer<P>[];
|
|
786
|
+
/** Get notified when the peers change. Compatible with `useSyncExternalStore`. */
|
|
787
|
+
subscribe(listener: () => void): () => void;
|
|
788
|
+
/** Stop sharing and listening. */
|
|
789
|
+
dispose(): void;
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Share per-peer state that expires, for presence, live cursors and selections.
|
|
793
|
+
*
|
|
794
|
+
* - `set(state)` shares this client's state, throttled to `maxRate` sends per second with the
|
|
795
|
+
* newest winning, and sent again on every new connection and every `refresh`, so it's never
|
|
796
|
+
* queued stale while disconnected.
|
|
797
|
+
* - Other peers' states come from `receive`. A peer is forgotten when it leaves (`state: null`)
|
|
798
|
+
* or when nothing has arrived from it for `timeout`.
|
|
799
|
+
*
|
|
800
|
+
* The server relays each client's state to the others, tagged with a peer id, and says
|
|
801
|
+
* `state: null` when a client disconnects.
|
|
802
|
+
*
|
|
803
|
+
* @example
|
|
804
|
+
* ```ts
|
|
805
|
+
* export const cursors = createWebSocketPresence(room, {
|
|
806
|
+
* send: (cursor: { x: number; y: number }) => ({ type: 'cursor', cursor }),
|
|
807
|
+
* leave: () => ({ type: 'leave' }),
|
|
808
|
+
* receive: message => (message.type === 'cursor' ? { peer: message.peer, state: message.cursor } : undefined)
|
|
809
|
+
* });
|
|
810
|
+
*
|
|
811
|
+
* window.addEventListener('pointermove', event => cursors.set({ x: event.clientX, y: event.clientY }));
|
|
812
|
+
* ```
|
|
813
|
+
*/
|
|
814
|
+
export declare function createWebSocketPresence<P, In, const Out>(client: WebSocketClient<In, Out>, options: WebSocketPresenceOptions<P, NoInfer<In>, NoInfer<Out>>): WebSocketPresence<P>;
|
|
815
|
+
//#endregion
|
|
475
816
|
//#region src/requests.d.ts
|
|
476
817
|
/** Options for {@link createWebSocketRequests}: how requests and replies look on the wire. */
|
|
477
818
|
interface WebSocketRequestsOptions<Req, In, Out> {
|
|
@@ -714,6 +1055,138 @@ interface WebSocketResponder<P = unknown, R = unknown> {
|
|
|
714
1055
|
*/
|
|
715
1056
|
export declare function createWebSocketResponder<Q extends RequestFields, R, In, const Out>(client: WebSocketClient<In, Out>, options: WebSocketResponderOptions<Q, R, NoInfer<In>, NoInfer<Out>>): WebSocketResponder<ParamsOf<Q>, R>;
|
|
716
1057
|
//#endregion
|
|
1058
|
+
//#region src/runs.d.ts
|
|
1059
|
+
/**
|
|
1060
|
+
* What happens to a run when its connection drops: `keep` resumes it on the next connection,
|
|
1061
|
+
* for agents that keep working in the background; `cancel` aborts it and tells the server, for
|
|
1062
|
+
* a chat answer nobody may be waiting for.
|
|
1063
|
+
*/
|
|
1064
|
+
type RunPolicy = 'keep' | 'cancel';
|
|
1065
|
+
/** Where a run is: streaming events, or ended. */
|
|
1066
|
+
type RunStatus = 'streaming' | 'done' | 'error' | 'aborted';
|
|
1067
|
+
/** A run's state, reduced from its events. A new object on every change. */
|
|
1068
|
+
interface AgentRun<T> {
|
|
1069
|
+
readonly runId: string;
|
|
1070
|
+
readonly status: RunStatus;
|
|
1071
|
+
/** Reduced from the run's events with `reduce`, starting from `initial`. */
|
|
1072
|
+
readonly value: T;
|
|
1073
|
+
/** What `error` returned for the event that ended the run, when `status` is `'error'`. */
|
|
1074
|
+
readonly error: unknown;
|
|
1075
|
+
}
|
|
1076
|
+
/** The subset of `Storage` that runs use, so `sessionStorage`, `localStorage` or your own fits. */
|
|
1077
|
+
type RunStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
|
|
1078
|
+
/** Options for {@link createWebSocketRuns}: the messages your server expects and sends. */
|
|
1079
|
+
interface WebSocketRunsOptions<Req, T, In, Out> {
|
|
1080
|
+
/** Names the runs' entry in `storage`, `react-websocket-runs:<name>`. */
|
|
1081
|
+
name: string;
|
|
1082
|
+
/**
|
|
1083
|
+
* The message that starts a run, with its `runId`. Sent again on a new connection until the
|
|
1084
|
+
* run's first event arrives, so the server should start a run only once per `runId`.
|
|
1085
|
+
* Annotate `request`: that's what `runs.start()` accepts.
|
|
1086
|
+
*/
|
|
1087
|
+
start: (request: Req, runId: string) => Out;
|
|
1088
|
+
/**
|
|
1089
|
+
* The message that asks for a run's events after `after`, its last event received, or from
|
|
1090
|
+
* the beginning when `after` is `undefined` (after a reload, or for `attach()`). Sent on every
|
|
1091
|
+
* new connection while the run is streaming.
|
|
1092
|
+
*/
|
|
1093
|
+
resume: (runId: string, after: number | undefined) => Out;
|
|
1094
|
+
/** The message that stops a run, sent by `abort()` and for runs whose policy is `cancel`. */
|
|
1095
|
+
cancel?: (runId: string) => Out;
|
|
1096
|
+
/**
|
|
1097
|
+
* Recognize a run's event: its `runId`, and its `offset`, one more than the event before it.
|
|
1098
|
+
* `undefined` for any other message.
|
|
1099
|
+
*/
|
|
1100
|
+
event: (message: In) => {
|
|
1101
|
+
readonly runId: string;
|
|
1102
|
+
readonly offset: number;
|
|
1103
|
+
} | null | undefined;
|
|
1104
|
+
/** Whether an event is the run's last. The run is `'done'` after reducing it. */
|
|
1105
|
+
done?: (message: In) => boolean;
|
|
1106
|
+
/** Recognize an event that ends the run with an error: return it, or `undefined`. */
|
|
1107
|
+
error?: (message: In) => unknown;
|
|
1108
|
+
/** A run's value before its first event. */
|
|
1109
|
+
initial: T;
|
|
1110
|
+
/** Fold an event into a run's value. Pure. */
|
|
1111
|
+
reduce: (value: T, message: In) => T;
|
|
1112
|
+
/** @default 'keep' */
|
|
1113
|
+
policy?: RunPolicy;
|
|
1114
|
+
/**
|
|
1115
|
+
* Where to remember streaming runs, so a reload resumes them.
|
|
1116
|
+
* @default sessionStorage, where it exists. `null` remembers nothing.
|
|
1117
|
+
*/
|
|
1118
|
+
storage?: RunStorage | null;
|
|
1119
|
+
}
|
|
1120
|
+
/** Runs that stream events and survive reconnects and reloads. Create with {@link createWebSocketRuns}. */
|
|
1121
|
+
interface WebSocketRuns<Req, T> {
|
|
1122
|
+
/** The client the runs stream on. */
|
|
1123
|
+
readonly client: WebSocketClient<any, any>;
|
|
1124
|
+
/**
|
|
1125
|
+
* Start a run and return its `runId`. The start message is sent as soon as the socket is
|
|
1126
|
+
* open, and the run is remembered until it ends. `initial` overrides the `initial` option for
|
|
1127
|
+
* this run. A `runId` it already knows, streaming or ended, starts nothing: the server starts
|
|
1128
|
+
* a run only once per id. `delete()` it first to use the id again.
|
|
1129
|
+
*/
|
|
1130
|
+
start(request: Req, options?: {
|
|
1131
|
+
runId?: string;
|
|
1132
|
+
policy?: RunPolicy;
|
|
1133
|
+
initial?: T;
|
|
1134
|
+
}): string;
|
|
1135
|
+
/**
|
|
1136
|
+
* Follow a run started elsewhere, such as in another tab or before a reload, from its first
|
|
1137
|
+
* event. Does nothing for a run it already knows.
|
|
1138
|
+
*/
|
|
1139
|
+
attach(runId: string, options?: {
|
|
1140
|
+
policy?: RunPolicy;
|
|
1141
|
+
initial?: T;
|
|
1142
|
+
}): void;
|
|
1143
|
+
/** Stop a streaming run: send `cancel`, and make it `'aborted'`. */
|
|
1144
|
+
abort(runId: string): void;
|
|
1145
|
+
/** A run's state, or `undefined` for one it doesn't know. The same object until it changes. */
|
|
1146
|
+
get(runId: string): AgentRun<T> | undefined;
|
|
1147
|
+
/** Every run it knows, oldest first. A new array on every change. */
|
|
1148
|
+
list(): readonly AgentRun<T>[];
|
|
1149
|
+
/** Get notified when a run changes. Compatible with `useSyncExternalStore`. */
|
|
1150
|
+
subscribe(listener: () => void): () => void;
|
|
1151
|
+
/** Forget a run, aborting it first if it's streaming. */
|
|
1152
|
+
delete(runId: string): void;
|
|
1153
|
+
/** Stop following runs, and release the connection. Remembered runs stay remembered. */
|
|
1154
|
+
dispose(): void;
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Stream runs, such as an AI agent's answer, that survive reconnects and page reloads.
|
|
1158
|
+
*
|
|
1159
|
+
* - `start(request)` sends `start(request, runId)` and follows the run's events, folding them
|
|
1160
|
+
* into its value with `reduce`.
|
|
1161
|
+
* - Each event carries an `offset`. On a new connection, a streaming run sends
|
|
1162
|
+
* `resume(runId, after)` with the last offset received, so the server sends what was missed,
|
|
1163
|
+
* and duplicates are dropped.
|
|
1164
|
+
* - Streaming runs are remembered in `sessionStorage`. After a reload they're followed again from
|
|
1165
|
+
* their first event, rebuilding the value, so `list()` or `useAgentRun` can show them.
|
|
1166
|
+
* - A run whose policy is `cancel` is aborted when its connection drops, and `cancel` is sent
|
|
1167
|
+
* first thing on the next one.
|
|
1168
|
+
*
|
|
1169
|
+
* Built on `createWebSocketFeeds`, with each run a topic. While runs are streaming, it holds
|
|
1170
|
+
* the client's connection.
|
|
1171
|
+
*
|
|
1172
|
+
* @example
|
|
1173
|
+
* ```ts
|
|
1174
|
+
* export const runs = createWebSocketRuns(agent, {
|
|
1175
|
+
* name: 'chat',
|
|
1176
|
+
* start: (prompt: string, runId) => ({ type: 'run', runId, prompt }),
|
|
1177
|
+
* resume: (runId, after) => ({ type: 'resume', runId, after }),
|
|
1178
|
+
* cancel: runId => ({ type: 'cancel', runId }),
|
|
1179
|
+
* event: message => (message.type === 'token' || message.type === 'done' ? message : undefined),
|
|
1180
|
+
* done: message => message.type === 'done',
|
|
1181
|
+
* initial: '',
|
|
1182
|
+
* reduce: (text, message) => text + message.text
|
|
1183
|
+
* });
|
|
1184
|
+
*
|
|
1185
|
+
* const runId = runs.start('Summarize this thread');
|
|
1186
|
+
* ```
|
|
1187
|
+
*/
|
|
1188
|
+
export declare function createWebSocketRuns<Req, T, In, const Out>(client: WebSocketClient<In, Out>, options: WebSocketRunsOptions<Req, T, NoInfer<In>, NoInfer<Out>>): WebSocketRuns<Req, T>;
|
|
1189
|
+
//#endregion
|
|
717
1190
|
//#region src/sendrate.d.ts
|
|
718
1191
|
/** Options for {@link createWebSocketSendRate}. */
|
|
719
1192
|
interface WebSocketSendRateOptions {
|
|
@@ -821,6 +1294,15 @@ interface WebSocketStateOptions<S, In> {
|
|
|
821
1294
|
*
|
|
822
1295
|
* @typeParam S - The state type. Don't use a function type: `setState` treats functions as updaters.
|
|
823
1296
|
*/
|
|
1297
|
+
/**
|
|
1298
|
+
* Messages from somewhere other than the client itself, such as the in-order messages of
|
|
1299
|
+
* `createWebSocketFeeds`, for `createWebSocketState`.
|
|
1300
|
+
*/
|
|
1301
|
+
interface MessageSource<In> {
|
|
1302
|
+
/** The client the messages come from. */
|
|
1303
|
+
readonly client: WebSocketClient<any, any>;
|
|
1304
|
+
on(type: 'message', listener: (message: In, event: MessageEvent) => void): () => void;
|
|
1305
|
+
}
|
|
824
1306
|
interface WebSocketState<S> {
|
|
825
1307
|
/** The client whose messages this store reduces. Hooks reading the store keep it connected. */
|
|
826
1308
|
readonly client: WebSocketClient<any, any>;
|
|
@@ -842,6 +1324,9 @@ interface WebSocketState<S> {
|
|
|
842
1324
|
* It starts listening immediately and keeps the state across reconnects; it doesn't open
|
|
843
1325
|
* the connection by itself.
|
|
844
1326
|
*
|
|
1327
|
+
* Pass feeds from `createWebSocketFeeds` in place of the client to reduce their messages
|
|
1328
|
+
* instead: numbered ones once each and in order.
|
|
1329
|
+
*
|
|
845
1330
|
* @example
|
|
846
1331
|
* ```ts
|
|
847
1332
|
* const prices = createWebSocket<Tick>('wss://api.example.com/prices', { json: true });
|
|
@@ -855,53 +1340,7 @@ interface WebSocketState<S> {
|
|
|
855
1340
|
* const acme = useWebSocketState(priceState, state => state.ACME);
|
|
856
1341
|
* ```
|
|
857
1342
|
*/
|
|
858
|
-
export declare function createWebSocketState<S, In>(
|
|
859
|
-
//#endregion
|
|
860
|
-
//#region src/topics.d.ts
|
|
861
|
-
/** Options for {@link createWebSocketTopics}: the messages your server expects. */
|
|
862
|
-
interface WebSocketTopicsOptions<Out> {
|
|
863
|
-
/** The message that subscribes to `topic`. */
|
|
864
|
-
subscribe: (topic: string) => Out;
|
|
865
|
-
/** The message that unsubscribes from `topic`. Omit it if your server has none. */
|
|
866
|
-
unsubscribe?: (topic: string) => Out;
|
|
867
|
-
}
|
|
868
|
-
/** Reference-counted topic subscriptions on one client. Create with {@link createWebSocketTopics}. */
|
|
869
|
-
interface WebSocketTopics {
|
|
870
|
-
/** The client the subscription messages are sent on. */
|
|
871
|
-
readonly client: WebSocketClient<any, any>;
|
|
872
|
-
/**
|
|
873
|
-
* Hold a subscription to `topic`, and the client's connection, until the returned
|
|
874
|
-
* function is called. The release function is safe to call more than once.
|
|
875
|
-
*/
|
|
876
|
-
acquire(topic: string): () => void;
|
|
877
|
-
/** The topics currently held, in the order they were first acquired. */
|
|
878
|
-
active(): string[];
|
|
879
|
-
/** Stop managing subscriptions. Only needed for managers created dynamically. */
|
|
880
|
-
dispose(): void;
|
|
881
|
-
}
|
|
882
|
-
/**
|
|
883
|
-
* Manage topic subscriptions that survive reconnects.
|
|
884
|
-
*
|
|
885
|
-
* - The first holder of a topic sends `subscribe(topic)`. Later holders share it.
|
|
886
|
-
* - A tick after the last holder releases it, `unsubscribe(topic)` is sent, so StrictMode
|
|
887
|
-
* and route transitions don't churn subscriptions.
|
|
888
|
-
* - After every reconnect, every held topic is subscribed again on the new socket, before
|
|
889
|
-
* messages queued while disconnected are sent.
|
|
890
|
-
*
|
|
891
|
-
* Subscriptions are only sent while the socket is open; nothing is queued for them.
|
|
892
|
-
*
|
|
893
|
-
* @example
|
|
894
|
-
* ```ts
|
|
895
|
-
* export const topics = createWebSocketTopics(live, {
|
|
896
|
-
* subscribe: topic => ({ type: 'subscribe', topic }),
|
|
897
|
-
* unsubscribe: topic => ({ type: 'unsubscribe', topic })
|
|
898
|
-
* });
|
|
899
|
-
*
|
|
900
|
-
* // In a component:
|
|
901
|
-
* useWebSocketTopic(topics, `prices:${symbol}`);
|
|
902
|
-
* ```
|
|
903
|
-
*/
|
|
904
|
-
export declare function createWebSocketTopics<const Out>(client: WebSocketClient<any, Out>, options: WebSocketTopicsOptions<NoInfer<Out>>): WebSocketTopics;
|
|
1343
|
+
export declare function createWebSocketState<S, In>(source: WebSocketClient<In, any> | MessageSource<In>, options: WebSocketStateOptions<S, NoInfer<In>>): WebSocketState<S>;
|
|
905
1344
|
//#endregion
|
|
906
|
-
export type { CloseInfo, EventMap, HeartbeatOptions, IncomingRequest, Latency, QueueOptions, ReconnectOptions, RequestHandler, RequestOptions, SendData, SendOptions, SendRate, Snapshot, StandardSchemaV1, Status, UrlSource, WebSocketClient, WebSocketLatency, WebSocketLatencyOptions, WebSocketOptions, WebSocketRequests, WebSocketRequestsOptions, WebSocketResponder, WebSocketResponderOptions, WebSocketSendRate, WebSocketSendRateOptions, WebSocketState, WebSocketStateOptions, WebSocketTopics, WebSocketTopicsOptions };
|
|
1345
|
+
export type { AgentRun, CloseInfo, EventMap, FeedPosition, HeartbeatOptions, IncomingRequest, Latency, MessageSource, MigrateOptions, PresencePeer, QueueOptions, ReconnectOptions, RequestHandler, RequestOptions, RunPolicy, RunStatus, RunStorage, SendData, SendOptions, SendRate, Snapshot, StandardSchemaV1, Status, UrlSource, WebSocketClient, WebSocketFeeds, WebSocketFeedsOptions, WebSocketLatency, WebSocketLatencyOptions, WebSocketOptions, WebSocketPresence, WebSocketPresenceOptions, WebSocketRequests, WebSocketRequestsOptions, WebSocketResponder, WebSocketResponderOptions, WebSocketRuns, WebSocketRunsOptions, WebSocketSendRate, WebSocketSendRateOptions, WebSocketState, WebSocketStateOptions, WebSocketTopics, WebSocketTopicsOptions };
|
|
907
1346
|
//# sourceMappingURL=core.d.cts.map
|