react-websocket 4.0.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 +27 -3
- 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 +689 -153
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +459 -53
- package/dist/core.d.ts +459 -53
- package/dist/core.js +686 -154
- 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 +182 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +663 -48
- package/dist/index.d.ts +393 -22
- package/dist/index.js +178 -24
- 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 -216
- 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.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.cjs","names":["VOLATILE","VOLATILE"],"sources":["../src/types.ts","../src/client.ts","../src/latency.ts","../src/requests.ts","../src/responder.ts","../src/sendrate.ts","../src/state.ts","../src/topics.ts"],"sourcesContent":["/**\n * Where to connect: a URL, or a function that returns one (sync or async).\n *\n * A function is called before **every** connection attempt, including reconnects,\n * so it can put a fresh auth token in the URL. If it throws or rejects, that counts\n * as a failed attempt and is retried with backoff.\n *\n * @example\n * ```ts\n * createWebSocket(async () => `wss://api.example.com/live?token=${await getToken()}`);\n * ```\n */\nexport type UrlSource = string | (() => string | Promise<string>);\n\n/**\n * Connection state.\n *\n * - `idle`: not connected and not trying to be. Nobody is using the client yet, or everyone stopped.\n * - `connecting`: the first attempt is in progress.\n * - `open`: connected. `send()` goes out immediately.\n * - `reconnecting`: the connection dropped (or an attempt failed) and a retry is scheduled or in progress.\n * - `closed`: stopped for good, by `close()` or after giving up on reconnecting.\n * It stays closed until `connect()` or `reconnect()` is called, or every hook unmounts.\n */\nexport type Status = 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed';\n\n/** The client's state as seen by React. A new object is created on every change. */\nexport interface Snapshot<In> {\n /** Connection state. */\n readonly status: Status;\n /** Most recent message (after `parse`/`json` and `schema`), kept across reconnects. */\n readonly lastMessage: In | undefined;\n /**\n * Consecutive failed attempts since the last stable connection. A connection that closes\n * within `reconnect.minUptime` of opening counts as failed. `0` while open.\n */\n readonly attempt: number;\n}\n\n/** Details of a closed connection, from the browser's `CloseEvent`. */\nexport interface CloseInfo {\n /** Close code: `1000` is a normal close, `1006` an abnormal one (e.g. network loss), `4000`–`4999` are yours. */\n readonly code: number;\n /** Close reason sent by the side that closed the connection. Often empty. */\n readonly reason: string;\n /** Whether the closing handshake completed. */\n readonly wasClean: boolean;\n}\n\n/** Anything `WebSocket.prototype.send` accepts: a string, `Blob`, `ArrayBuffer` or typed array. */\nexport type SendData = Parameters<WebSocket['send']>[0];\n\n/** Reconnect timing. Delay before attempt `n` = `min(maxDelay, minDelay * factor ** (n - 1))`, minus jitter. */\nexport interface ReconnectOptions {\n /**\n * Maximum consecutive reconnect attempts. When the last one fails too, the client\n * gives up: the status becomes `closed` and `ready` rejects. `0` never retries.\n * @default Infinity\n */\n maxAttempts?: number;\n /**\n * How long a connection must stay open, in ms, before the backoff resets. One that closes\n * sooner counts as a failed attempt, so a server that accepts and then closes right away\n * (e.g. rejecting the credentials sent on open) is retried with a growing delay, and\n * `maxAttempts` still applies. `0` resets on every open.\n * @default 5000\n */\n minUptime?: number;\n /**\n * Delay before the first retry, in ms.\n * @default 1000\n */\n minDelay?: number;\n /**\n * Upper bound for the delay, in ms.\n * @default 30000\n */\n maxDelay?: number;\n /**\n * Backoff multiplier applied per attempt.\n * @default 2\n */\n factor?: number;\n /**\n * Randomly shave up to this fraction (0–1) off each delay, so that many clients\n * dropped at once don't all reconnect at the same moment. The default `1` picks each\n * delay at random between 0 and the backoff (\"full jitter\").\n * @default 1\n */\n jitter?: number;\n /**\n * Called on every unexpected close. Return `false` to stop reconnecting.\n *\n * The default, `retryableClose`, retries everything except `1002` (protocol error), `1003`\n * (unsupported data) and `1008` (policy violation). Your own function replaces it; call\n * `retryableClose` from it to keep that rule, or return `true` to retry every close.\n *\n * @example\n * ```ts\n * import { retryableClose } from 'react-websocket';\n *\n * // Also stop when our server rejects the credentials.\n * shouldReconnect: close => close.code !== 4001 && retryableClose(close)\n * ```\n * @default retryableClose\n */\n shouldReconnect?: (close: CloseInfo) => boolean;\n}\n\n/** Application-level keep-alive. Useful behind proxies that drop idle connections. */\nexport interface HeartbeatOptions {\n /** Send `message` every `interval` ms while open. */\n interval: number;\n /**\n * Sent as-is (it is not passed through `serialize` or `json`).\n * @default 'ping'\n */\n message?: string;\n /** Incoming data equal to this string is swallowed instead of being delivered as a message. */\n pong?: string;\n /**\n * If nothing at all arrives within this many ms after a ping, the connection is\n * treated as dead: it closes with code `4000` and reconnects.\n */\n timeout?: number;\n}\n\n/** Per-message options for `send()`. */\nexport interface SendOptions {\n /**\n * Drop the message instead of queueing it when the socket isn't open. For data that\n * is stale by the time a reconnect finishes, such as player input or cursor positions.\n * @default false\n */\n volatile?: boolean;\n /**\n * Keep only the newest queued message with this key. Sending again with the same key\n * while disconnected replaces the earlier one, which moves to the back of the queue\n * (or of the `priority` messages, for a priority one).\n * For state where only the latest value matters, such as \"my position is now (x, y)\".\n */\n key?: string;\n /**\n * Queue this message ahead of every message queued without `priority`, after earlier\n * priority messages. For control messages that must not wait behind a backlog, such as\n * cancel, interrupt or a token refresh. Messages sent from `open` listeners still go first.\n *\n * It only matters while the socket isn't open: once a message is handed to the browser,\n * nothing can overtake it. To keep that buffer short, send bulky streams with `maxBuffered`.\n * @default false\n */\n priority?: boolean;\n /**\n * Drop the message instead of sending it while more than this many bytes are already\n * waiting in the browser's send buffer (`socket.bufferedAmount`). For high-rate streams on\n * a slow link, such as audio frames or per-tick input, where a late message is worse than\n * none. `send()` returns `false`, and `onDrop` isn't called.\n *\n * Only checked while the socket is open: a message queued while disconnected is sent on\n * open regardless. `bufferedAmount` includes what you sent earlier in the same task, so set\n * it well above one burst of messages.\n *\n * @example\n * ```ts\n * voice.send(frame, { volatile: true, maxBuffered: 64 * 1024 });\n * ```\n */\n maxBuffered?: number;\n /**\n * Called if the message is dropped from the queue without being sent: replaced by a\n * newer message with the same `key`, or evicted by `queue.limit`. Not called when the\n * queue is cleared because the client closed or gave up, or when `send()` drops the\n * message right away (`volatile` while not open, or `maxBuffered`) and returns `false`.\n */\n onDrop?: () => void;\n}\n\n/** Bounds for the send queue. */\nexport interface QueueOptions {\n /**\n * Maximum number of messages held while not connected. When full, the oldest\n * message is dropped, skipping `priority` messages unless nothing else is queued.\n * `Infinity` keeps every message.\n * @default 100\n */\n limit?: number;\n}\n\n/**\n * The minimal [Standard Schema v1](https://standardschema.dev) interface, implemented\n * by zod (3.24+), valibot (1.0+), arktype (2.0+) and others. You don't need to\n * import this; pass your schema object directly.\n */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (\n value: unknown\n ) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;\n readonly types?: { readonly input: Input; readonly output: Output } | undefined;\n };\n}\n\n/** The result of a Standard Schema `validate` call. */\nexport type StandardSchemaResult<Output> =\n | { readonly value: Output; readonly issues?: undefined }\n | { readonly issues: ReadonlyArray<{ readonly message: string }> };\n\n/**\n * Connection options, shared by `createWebSocket(url, options)` and `useWebSocket(url, options)`.\n *\n * @typeParam In - Type of incoming messages (after `parse`/`json`/`schema`).\n * @typeParam Out - Type accepted by `send()`.\n */\nexport interface WebSocketOptions<In = unknown, Out = unknown> {\n /** Sub-protocol(s) passed to the `WebSocket` constructor. */\n protocols?: string | string[];\n /**\n * How binary messages are exposed.\n * @default 'blob'\n */\n binaryType?: BinaryType;\n /**\n * `JSON.parse` incoming and `JSON.stringify` outgoing messages. Messages that fail\n * to parse are reported to `onError` as a {@link MessageError} and not delivered.\n * @default false\n */\n json?: boolean;\n /**\n * Custom decoder for incoming `event.data`. Takes precedence over `json`.\n * Throwing reports a {@link MessageError} and drops the message.\n */\n parse?: (data: unknown) => unknown;\n /** Custom encoder for outgoing messages. Takes precedence over `json`. */\n serialize?: (message: Out) => SendData;\n /**\n * Validate incoming messages (after parsing) with any Standard Schema library.\n * Invalid messages are reported to `onError` as a {@link MessageError} and not delivered.\n * The message type `In` is inferred from it.\n *\n * @example\n * ```ts\n * createWebSocket(url, { json: true, schema: z.object({ price: z.number() }) });\n * ```\n */\n schema?: StandardSchemaV1<unknown, In>;\n /**\n * Reconnect with exponential backoff after an unexpected close. `false` disables it.\n * @default true\n */\n reconnect?: boolean | ReconnectOptions;\n /** Send a ping on an interval and, optionally, reconnect when the server goes quiet. */\n heartbeat?: HeartbeatOptions;\n /**\n * Buffer `send()` calls made while not connected and send them, in order, on open\n * (right after `open` listeners run). It holds up to 100 messages, dropping the oldest;\n * `{ limit }` changes that. `false` drops messages instead of queueing them.\n * The queue is cleared by `close()` or when the client gives up.\n * @default true\n */\n queue?: boolean | QueueOptions;\n /**\n * Receives debug messages about connection attempts, retries and dropped messages.\n * @example\n * ```ts\n * logger: console.debug\n * ```\n */\n logger?: (message: string, ...details: unknown[]) => void;\n /**\n * `WebSocket` implementation to use, e.g. a mock in tests or the `ws` package in Node.\n * When omitted, the global `WebSocket` is used in browsers. During a server render\n * (no `window`), nothing connects unless an implementation is passed.\n */\n WebSocket?: typeof WebSocket;\n}\n\n/**\n * Reported via the `error` event (and `onError`) when an incoming message can't be\n * decoded by `parse`/`json` or fails `schema` validation. The message is not delivered.\n *\n * @example\n * ```ts\n * onError(error) {\n * if (error instanceof MessageError) report(error.data, error.issues);\n * }\n * ```\n */\nexport class MessageError extends Error {\n /** The raw `event.data` that failed. */\n readonly data: unknown;\n /** Schema validation issues, when the message failed `schema`. */\n readonly issues: ReadonlyArray<{ readonly message: string }> | undefined;\n\n constructor(\n message: string,\n data: unknown,\n options?: { cause?: unknown; issues?: ReadonlyArray<{ readonly message: string }> }\n ) {\n super(message, options?.cause === undefined ? undefined : { cause: options.cause });\n this.name = 'MessageError';\n this.data = data;\n this.issues = options?.issues;\n }\n}\n\n/** Events emitted by a client. Subscribe with `client.on(type, listener)` or `useWebSocketEvent`. */\nexport interface EventMap<In> {\n /**\n * The socket opened (initially or after a reconnect). Listeners run before queued\n * messages are sent, so a handshake sent from here goes out first.\n */\n open: (event: Event) => void;\n /** A message arrived and passed `parse` and `schema`. */\n message: (message: In, event: MessageEvent) => void;\n /** A socket closed, whether it will be retried or not. */\n close: (close: CloseInfo) => void;\n /**\n * A socket error `Event`, a {@link MessageError} for an undecodable or invalid message,\n * or the `Error` thrown by the `WebSocket` constructor or a `url` function.\n */\n error: (error: Event | MessageError | Error) => void;\n}\n\n/**\n * A WebSocket connection that reconnects, queues, decodes and can be shared.\n * Create one with `createWebSocket()`.\n *\n * The connection is reference-counted: it opens when the first hook mounts (or\n * `ready` is read) and closes one tick after the last hook unmounts. Calling\n * `connect()` keeps it open regardless, until `close()`.\n *\n * @typeParam In - Type of incoming messages.\n * @typeParam Out - Type accepted by `send()`.\n */\nexport interface WebSocketClient<In = unknown, Out = unknown> {\n /** The URL (or URL function) the client was created with. */\n readonly url: UrlSource;\n /**\n * A promise that resolves once the socket has opened. Reading it starts an idle client.\n *\n * It stays resolved through automatic reconnects, so `use(client.ready)` shows a\n * Suspense fallback only for the first connection, never on a network blip; use\n * `status` to show reconnects. It rejects when the client gives up reconnecting, when\n * `close()` is called, and during a server render. After that, `connect()` or\n * `reconnect()` make it a new pending promise.\n *\n * It is safe to pass to React 19's `use()`: the same promise is returned until its state\n * changes, and it carries `status`/`value`/`reason` so `use()` doesn't suspend when it is\n * already settled.\n */\n readonly ready: Promise<void>;\n /** The current underlying `WebSocket`, if any. An escape hatch; prefer the client's methods. */\n readonly socket: WebSocket | null;\n /** Subscribe to snapshot changes. Compatible with `useSyncExternalStore`. Returns an unsubscribe function. */\n subscribe(listener: () => void): () => void;\n /** The current {@link Snapshot}. Returns the same object until something changes. */\n getSnapshot(): Snapshot<In>;\n /**\n * Listen to an event. Returns an unsubscribe function. Listener errors are re-thrown\n * asynchronously, so one failing listener doesn't break the others.\n */\n on<K extends keyof EventMap<In>>(type: K, listener: EventMap<In>[K]): () => void;\n /**\n * Send a message (encoded with `serialize`/`json` if configured). If the socket isn't\n * open, the message is queued and sent on open (see the `queue` option), unless\n * `options.volatile` is set.\n * @returns `true` if it was sent immediately, `false` if it was queued or dropped\n * (`volatile` while not open, or `maxBuffered` while open).\n *\n * @example\n * ```ts\n * game.send({ type: 'input', dx, dy }, { volatile: true }); // never replayed after a reconnect\n * game.send({ type: 'position', x, y }, { key: 'position' }); // only the newest is queued\n * game.send({ type: 'leave' }, { priority: true }); // ahead of everything already queued\n * ```\n */\n send(message: Out, options?: SendOptions): boolean;\n /**\n * Start connecting, and keep the connection open even when no hook is using the\n * client, until `close()` is called. Does nothing if already connecting or open.\n */\n connect(): void;\n /**\n * Close the connection and stop reconnecting. The status becomes `closed` and the\n * send queue is cleared. Mounted hooks stay mounted; call `connect()` or `reconnect()`\n * to start again.\n * @param code - Close code. @default 1000\n */\n close(code?: number, reason?: string): void;\n /**\n * Drop the current socket (if any) and connect again immediately, resetting the\n * backoff. Also restarts a `closed` client.\n */\n reconnect(): void;\n /**\n * Hold the connection open until the returned function is called. This is what the\n * hooks use; call it yourself to integrate with non-React code.\n * @returns A release function (safe to call more than once).\n */\n acquire(): () => void;\n}\n","import {\n MessageError,\n type CloseInfo,\n type EventMap,\n type ReconnectOptions,\n type SendData,\n type SendOptions,\n type Snapshot,\n type StandardSchemaResult,\n type UrlSource,\n type WebSocketClient,\n type WebSocketOptions\n} from './types';\n\nconst OPEN = 1;\n\nconst RECONNECT_DEFAULTS = {\n maxAttempts: Infinity,\n minDelay: 1000,\n maxDelay: 30_000,\n factor: 2,\n jitter: 1,\n minUptime: 5000\n} satisfies ReconnectOptions;\n\nconst QUEUE_LIMIT = 100;\n\ntype Timer = ReturnType<typeof setTimeout> | undefined;\n\n/**\n * The default `reconnect.shouldReconnect`: retry every close except `1002` (protocol error),\n * `1003` (unsupported data) and `1008` (policy violation, often a rejected login), which\n * retrying the same way can't fix. Call it from your own `shouldReconnect` to keep that rule.\n *\n * @example\n * ```ts\n * createWebSocket(url, {\n * // Also stop on our server's \"invalid token\" code.\n * reconnect: { shouldReconnect: close => close.code !== 4001 && retryableClose(close) }\n * });\n * ```\n */\nexport function retryableClose(close: CloseInfo): boolean {\n return close.code !== 1002 && close.code !== 1003 && close.code !== 1008;\n}\n\n// How long a connection opened only by reading `ready` waits for a holder before closing.\n// Long enough for React to retry a suspended render and commit its hooks.\nconst READY_GRACE = 5000;\n\n// React's `use()` reads `status`/`value`/`reason` off a thenable to skip suspending\n// when it is already settled.\ntype TrackedPromise = Promise<void> & {\n status: 'pending' | 'fulfilled' | 'rejected';\n value?: void;\n reason?: unknown;\n};\n\ninterface Deferred {\n readonly promise: TrackedPromise;\n settled: boolean;\n resolve(): void;\n reject(reason: unknown): void;\n}\n\nfunction createDeferred(): Deferred {\n let resolve!: () => void;\n let reject!: (reason: unknown) => void;\n const promise = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n }) as TrackedPromise;\n promise.status = 'pending';\n // Nobody may be awaiting `ready`; don't surface that as an unhandled rejection.\n promise.catch(() => {});\n const deferred: Deferred = {\n promise,\n settled: false,\n resolve() {\n if (deferred.settled) return;\n deferred.settled = true;\n promise.status = 'fulfilled';\n promise.value = undefined;\n resolve();\n },\n reject(reason) {\n if (deferred.settled) return;\n deferred.settled = true;\n promise.status = 'rejected';\n promise.reason = reason;\n reject(reason);\n }\n };\n return deferred;\n}\n\nfunction rethrowAsync(error: unknown) {\n queueMicrotask(() => {\n throw error;\n });\n}\n\nfunction isThenable<T>(value: unknown): value is PromiseLike<T> {\n return typeof (value as PromiseLike<T> | null)?.then === 'function';\n}\n\n/**\n * @internal Option key for a callback run when the client tears down, which lets the hooks'\n * URL registry forget clients nobody holds anymore. A registered symbol rather than an export,\n * because the hooks entry imports the client from the `core` entry at runtime.\n */\nconst ON_TEARDOWN = Symbol.for('react-websocket.onTeardown');\n\nfunction createClient<In = unknown, Out = unknown>(\n url: UrlSource,\n options: WebSocketOptions<In, Out> & { [ON_TEARDOWN]?: () => void } = {}\n): WebSocketClient<In, Out> {\n const onTeardown = options[ON_TEARDOWN];\n const log = options.logger ?? (() => {});\n const retry =\n options.reconnect === false\n ? null\n : {\n ...RECONNECT_DEFAULTS,\n ...(typeof options.reconnect === 'object' ? options.reconnect : {})\n };\n const queueLimit =\n options.queue === false\n ? 0\n : Math.max(\n 0,\n (typeof options.queue === 'object' ? options.queue.limit : undefined) ?? QUEUE_LIMIT\n );\n\n let snapshot: Snapshot<In> = { status: 'idle', lastMessage: undefined, attempt: 0 };\n const storeListeners = new Set<() => void>();\n const listeners: { [K in keyof EventMap<In>]: Set<EventMap<In>[K]> } = {\n open: new Set(),\n message: new Set(),\n close: new Set(),\n error: new Set()\n };\n\n let ws: WebSocket | null = null;\n let wanted = false;\n let refs = 0;\n // Set by a public `connect()`: stay connected after the last hook releases, until `close()`.\n let pinned = false;\n // Priority messages form the head of the queue, in the order they were sent.\n let pending: {\n data: SendData;\n key: string | undefined;\n priority: boolean | undefined;\n onDrop: (() => void) | undefined;\n }[] = [];\n let ready: Deferred | null = null;\n // Opened at least once since the client was last started. `ready` stays resolved through\n // automatic reconnects so `use(client.ready)` doesn't swap content for a fallback on every blip.\n let opened = false;\n let unavailable: Deferred | null = null;\n let startScheduled = false;\n let inbox: Promise<void> | null = null;\n // Bumped per connection attempt so a slow `url()` can't resurrect a cancelled one.\n let generation = 0;\n let windowBound = false;\n // Consecutive failed attempts, for the backoff. Unlike `snapshot.attempt`, which reads 0 while\n // open, it resets only once a connection stays up for `minUptime`, so a server that accepts\n // and then closes right away (e.g. rejecting the auth sent on open) still gets backed off.\n let failures = 0;\n let retryTimer: Timer;\n let teardownTimer: Timer;\n let stableTimer: Timer;\n let heartbeatTimer: ReturnType<typeof setInterval> | undefined;\n let pongTimer: Timer;\n\n function set(patch: Partial<Snapshot<In>>) {\n snapshot = { ...snapshot, ...patch };\n for (const listener of [...storeListeners]) {\n try {\n listener();\n } catch (error) {\n rethrowAsync(error);\n }\n }\n }\n\n function emit<K extends keyof EventMap<In>>(type: K, ...args: Parameters<EventMap<In>[K]>) {\n for (const listener of [...listeners[type]]) {\n try {\n (listener as (...a: Parameters<EventMap<In>[K]>) => void)(...args);\n } catch (error) {\n rethrowAsync(error);\n }\n }\n }\n\n function getImpl(): typeof WebSocket | undefined {\n if (options.WebSocket) return options.WebSocket;\n // Node has a global WebSocket too; don't connect from a server render.\n return typeof window === 'undefined' ? undefined : globalThis.WebSocket;\n }\n\n function rejectReady(reason: unknown) {\n if (!ready || ready.settled) ready = createDeferred();\n ready.reject(reason);\n }\n\n function retryNow() {\n if (retryTimer !== undefined) {\n log('network or visibility change, retrying now');\n open();\n }\n }\n\n function onVisibilityChange() {\n if (document.visibilityState === 'visible') retryNow();\n }\n\n function bindWindow() {\n if (windowBound || typeof window === 'undefined') return;\n windowBound = true;\n window.addEventListener('online', retryNow);\n document.addEventListener('visibilitychange', onVisibilityChange);\n }\n\n function unbindWindow() {\n if (!windowBound) return;\n windowBound = false;\n window.removeEventListener('online', retryNow);\n document.removeEventListener('visibilitychange', onVisibilityChange);\n }\n\n function startHeartbeat(socket: WebSocket) {\n const heartbeat = options.heartbeat;\n if (!heartbeat) return;\n heartbeatTimer = setInterval(() => {\n socket.send(heartbeat.message ?? 'ping');\n if (heartbeat.timeout && pongTimer === undefined) {\n pongTimer = setTimeout(() => {\n log('heartbeat timeout');\n // The close handshake may never finish on a dead link, so don't wait for it.\n detach(socket, false);\n socket.close(4000, 'heartbeat timeout');\n handleClose({ code: 4000, reason: 'heartbeat timeout', wasClean: false });\n }, heartbeat.timeout);\n }\n }, heartbeat.interval);\n }\n\n /** Stop the timers that belong to the current open socket. */\n function stopSocketTimers() {\n clearInterval(heartbeatTimer);\n clearTimeout(pongTimer);\n clearTimeout(stableTimer);\n heartbeatTimer = pongTimer = stableTimer = undefined;\n }\n\n /** Stop tracking `socket`. Its eventual close event is still reported unless `reportClose` is false. */\n function detach(socket: WebSocket, reportClose: boolean) {\n if (ws === socket) ws = null;\n socket.onopen = socket.onmessage = socket.onerror = null;\n socket.onclose = reportClose\n ? event => emit('close', { code: event.code, reason: event.reason, wasClean: event.wasClean })\n : null;\n }\n\n function backoff(attempt: number) {\n const { minDelay, maxDelay, factor, jitter } = retry!;\n const base = Math.min(maxDelay, minDelay * factor ** (attempt - 1));\n return Math.round(base * (1 - jitter * Math.random()));\n }\n\n function open() {\n clearTimeout(retryTimer);\n retryTimer = undefined;\n const Impl = getImpl();\n if (!Impl) return;\n const id = ++generation;\n set({ status: snapshot.attempt === 0 ? 'connecting' : 'reconnecting' });\n\n let target: string | Promise<string>;\n try {\n target = typeof url === 'string' ? url : url();\n } catch (error) {\n urlFailed(error);\n return;\n }\n if (typeof target === 'string') {\n start(Impl, target);\n return;\n }\n Promise.resolve(target).then(\n resolved => {\n if (id === generation && wanted) start(Impl, resolved);\n },\n error => {\n if (id === generation && wanted) urlFailed(error);\n }\n );\n }\n\n /** `url()` threw or rejected (e.g. a token refresh failed): count it as a failed attempt. */\n function urlFailed(error: unknown) {\n const reason = error instanceof Error ? error : new Error(String(error));\n log('url() failed', reason);\n emit('error', reason);\n retryOrFail({ code: 1006, reason: 'url() failed', wasClean: false }, reason);\n }\n\n function start(Impl: typeof WebSocket, target: string) {\n let socket: WebSocket;\n try {\n socket = new Impl(target, options.protocols);\n } catch (error) {\n // e.g. an invalid URL; retrying won't help.\n emit('error', error as Error);\n fail(error);\n return;\n }\n if (options.binaryType) socket.binaryType = options.binaryType;\n ws = socket;\n log('connecting', target);\n\n socket.onopen = event => {\n log('open');\n opened = true;\n set({ status: 'open', attempt: 0 });\n if (retry) stableTimer = setTimeout(() => (failures = 0), retry.minUptime);\n startHeartbeat(socket);\n ready?.resolve();\n // Nobody holds it (it was started by reading `ready`, or the last holder left while\n // connecting): close it unless a holder turns up soon.\n if (refs === 0 && !pinned) {\n clearTimeout(teardownTimer);\n teardownTimer = setTimeout(teardown, READY_GRACE);\n }\n // `open` listeners go first, so a handshake sent from `onOpen` (e.g. auth) precedes\n // whatever was queued while disconnected.\n emit('open', event);\n if (ws !== socket) return; // a listener closed or replaced it\n const queued = pending;\n pending = [];\n for (const { data } of queued) socket.send(data);\n };\n socket.onmessage = event => receive(event, socket);\n socket.onerror = event => emit('error', event);\n socket.onclose = event => {\n detach(socket, false);\n handleClose({ code: event.code, reason: event.reason, wasClean: event.wasClean });\n };\n }\n\n function handleClose(info: CloseInfo) {\n stopSocketTimers();\n log('closed', info);\n retryOrFail(info);\n emit('close', info);\n }\n\n function retryOrFail(info: CloseInfo, error?: Error) {\n if (\n wanted &&\n retry &&\n failures < retry.maxAttempts &&\n (retry.shouldReconnect ?? retryableClose)(info)\n ) {\n const attempt = ++failures;\n const delay = backoff(attempt);\n set({ status: 'reconnecting', attempt });\n log(`reconnecting in ${delay}ms (attempt ${attempt})`);\n retryTimer = setTimeout(open, delay);\n } else {\n fail(\n error ??\n new Error(`WebSocket closed (code ${info.code}${info.reason ? `: ${info.reason}` : ''})`)\n );\n }\n }\n\n function fail(reason: unknown) {\n wanted = false;\n opened = false;\n unbindWindow();\n pending = [];\n // `failures`, not 0: a connection that kept closing right after open was still failing.\n set({ status: 'closed', attempt: failures });\n rejectReady(reason);\n }\n\n function receive(event: MessageEvent, socket: WebSocket) {\n clearTimeout(pongTimer);\n pongTimer = undefined;\n if (options.heartbeat?.pong !== undefined && event.data === options.heartbeat.pong) return;\n\n // Async schema validation must not reorder messages.\n const run = () => decode(event, socket);\n if (inbox) {\n // One failed delivery must not stop the chain, or every later message is dropped.\n const next: Promise<void> = inbox\n .then(run)\n .catch(rethrowAsync)\n .then(() => {\n if (inbox === next) inbox = null;\n });\n inbox = next;\n return;\n }\n const result = run();\n if (result) {\n const next: Promise<void> = result.catch(rethrowAsync).then(() => {\n if (inbox === next) inbox = null;\n });\n inbox = next;\n }\n }\n\n function decode(event: MessageEvent, socket: WebSocket): void | Promise<void> {\n let value: unknown;\n try {\n value = options.parse\n ? options.parse(event.data)\n : options.json\n ? JSON.parse(event.data as string)\n : event.data;\n } catch (cause) {\n emit('error', new MessageError('Failed to parse message', event.data, { cause }));\n return;\n }\n\n const schema = options.schema;\n if (!schema) return deliver(value as In, event, socket);\n\n let result: ReturnType<(typeof schema)['~standard']['validate']>;\n try {\n result = schema['~standard'].validate(value);\n } catch (cause) {\n emit('error', new MessageError('Schema validation threw', event.data, { cause }));\n return;\n }\n if (isThenable<StandardSchemaResult<In>>(result)) {\n return Promise.resolve(result).then(\n r => validated(r, event, socket),\n cause => emit('error', new MessageError('Schema validation threw', event.data, { cause }))\n );\n }\n validated(result, event, socket);\n }\n\n function validated(result: StandardSchemaResult<In>, event: MessageEvent, socket: WebSocket) {\n if (result.issues) {\n emit(\n 'error',\n new MessageError('Message failed schema validation', event.data, { issues: result.issues })\n );\n } else {\n deliver(result.value, event, socket);\n }\n }\n\n function deliver(message: In, event: MessageEvent, socket: WebSocket) {\n if (ws !== socket) return; // closed or replaced while validating\n set({ lastMessage: message });\n emit('message', message, event);\n }\n\n function send(message: Out, sendOptions: SendOptions = {}): boolean {\n const data = options.serialize\n ? options.serialize(message)\n : options.json\n ? JSON.stringify(message)\n : (message as SendData);\n const { key, priority, maxBuffered } = sendOptions;\n if (ws && ws.readyState === OPEN) {\n // A late frame is worse than none: let a congested buffer drain instead of growing it.\n if (maxBuffered !== undefined && ws.bufferedAmount > maxBuffered) {\n log('dropped message: send buffer above maxBuffered');\n return false;\n }\n ws.send(data);\n return true;\n }\n if (queueLimit === 0 || sendOptions.volatile) {\n log('dropped message: socket is not open');\n return false;\n }\n if (!getImpl()) {\n // e.g. during a server render: this client can never connect, so don't hold on to it.\n log('dropped message: no WebSocket implementation available');\n return false;\n }\n if (key !== undefined) {\n pending = pending.filter(entry => {\n if (entry.key !== key) return true;\n dropped(entry);\n return false;\n });\n }\n let head = pending.findIndex(entry => !entry.priority);\n if (head < 0) head = pending.length;\n const entry = { data, key, priority, onDrop: sendOptions.onDrop };\n if (priority) pending.splice(head++, 0, entry);\n else pending.push(entry);\n if (pending.length > queueLimit) {\n // The oldest ordinary message goes first; priority ones only when nothing else is left.\n dropped(pending.splice(head < pending.length ? head : 0, 1)[0]!);\n log(`dropped oldest queued message: queue limit (${queueLimit}) reached`);\n }\n return false;\n }\n\n function dropped(entry: (typeof pending)[number]) {\n try {\n entry.onDrop?.();\n } catch (error) {\n rethrowAsync(error);\n }\n }\n\n function begin() {\n wanted = true;\n // Starting over after `close()` or giving up: wait for a new connection.\n if (snapshot.status === 'idle' || snapshot.status === 'closed') {\n opened = false;\n if (ready?.settled) ready = null;\n }\n failures = 0;\n snapshot = { ...snapshot, attempt: 0 }; // open() notifies\n bindWindow();\n open();\n }\n\n /** Start connecting unless already wanted. Used by holders (`acquire`, `ready`). */\n function ensureConnecting() {\n if (wanted || !getImpl()) return;\n begin();\n }\n\n function connect() {\n pinned = true;\n ensureConnecting();\n }\n\n function stop(code?: number, reason?: string) {\n wanted = false;\n generation++;\n clearTimeout(retryTimer);\n retryTimer = undefined;\n failures = 0;\n stopSocketTimers();\n unbindWindow();\n pending = [];\n if (ws) {\n const socket = ws;\n detach(socket, true);\n socket.close(code, reason);\n }\n }\n\n function close(code = 1000, reason?: string) {\n pinned = false;\n opened = false;\n stop(code, reason);\n set({ status: 'closed', attempt: 0 });\n rejectReady(new Error('WebSocket was closed'));\n }\n\n function reconnect() {\n if (!getImpl()) return;\n clearTimeout(retryTimer);\n retryTimer = undefined;\n stopSocketTimers();\n if (ws) {\n const socket = ws;\n detach(socket, true);\n socket.close(1000, 'reconnect');\n }\n begin();\n }\n\n function teardown() {\n if (refs > 0 || pinned) return;\n // Someone may be suspended on `ready`; let the attempt finish.\n if (ready && !ready.settled) return;\n stop();\n ready = null;\n opened = false;\n set({ status: 'idle', attempt: 0 });\n onTeardown?.();\n }\n\n function acquire() {\n refs++;\n clearTimeout(teardownTimer);\n if (snapshot.status === 'idle') ensureConnecting();\n let released = false;\n return () => {\n if (released) return;\n released = true;\n if (--refs > 0) return;\n // Deferred so StrictMode's unmount/remount and route transitions don't churn the socket.\n clearTimeout(teardownTimer);\n teardownTimer = setTimeout(teardown, 0);\n };\n }\n\n return {\n url,\n get ready() {\n if (!getImpl()) {\n if (!unavailable) {\n unavailable = createDeferred();\n unavailable.reject(\n new Error('react-websocket: no WebSocket available here (server render?)')\n );\n }\n return unavailable.promise;\n }\n if (snapshot.status === 'idle' && !startScheduled) {\n // `ready` is read during render; start outside of it.\n startScheduled = true;\n queueMicrotask(() => {\n startScheduled = false;\n if (snapshot.status === 'idle') ensureConnecting();\n });\n }\n if (!ready) {\n ready = createDeferred();\n if (opened) ready.resolve();\n }\n return ready.promise;\n },\n get socket() {\n return ws;\n },\n subscribe(listener) {\n storeListeners.add(listener);\n return () => {\n storeListeners.delete(listener);\n };\n },\n getSnapshot: () => snapshot,\n on(type, listener) {\n // Wrap so the same function can be subscribed twice and unsubscribed independently.\n const entry = ((...args: unknown[]) =>\n (listener as (...a: unknown[]) => void)(...args)) as typeof listener;\n listeners[type].add(entry);\n return () => {\n listeners[type].delete(entry);\n };\n },\n send,\n connect,\n close,\n reconnect,\n acquire\n };\n}\n\n/**\n * Create a WebSocket client, usually at module level, to share across components.\n *\n * Nothing connects until a hook uses it, `ready` is read, or `connect()` is called.\n * It closes one tick after the last hook unmounts, unless `connect()` was called.\n *\n * @param url - A URL, or a function (sync or async) called before every connection\n * attempt, e.g. to put a fresh auth token in the URL on each reconnect.\n * @typeParam In - Type of incoming messages. Inferred from `options.schema` if given.\n * @typeParam Out - Type accepted by `send()`.\n *\n * @example\n * ```ts\n * type ChatIn = { user: string; text: string };\n * type ChatOut = { text: string };\n *\n * export const chat = createWebSocket<ChatIn, ChatOut>('wss://api.example.com/chat', { json: true });\n * ```\n */\nexport function createWebSocket<In = unknown, Out = unknown>(\n url: UrlSource,\n options?: WebSocketOptions<In, Out>\n): WebSocketClient<In, Out> {\n return createClient(url, options);\n}\n","import type { WebSocketClient } from './types';\n\n/** Options for {@link createWebSocketLatency}: how pings and pongs look on the wire. */\nexport interface WebSocketLatencyOptions<In, Out> {\n /** The ping message, tagged with `id`. Sent with `volatile`, so pings are never queued. */\n ping: (id: number) => Out;\n /**\n * Recognize a pong: return the `id` of the ping it answers, and the server's clock\n * (`Date.now()` on the server, in ms) when it replied. Return `undefined` (or `null`)\n * for any other message. Without `time`, only the round-trip time is measured.\n */\n pong: (message: In) => { id: number | string; time?: number } | null | undefined;\n /**\n * How often to ping while open, in ms. The first ping goes out as soon as the socket opens.\n * @default 2000\n */\n interval?: number;\n /**\n * How many recent measurements to keep. `rtt` is their median, and `offset` comes from\n * the one with the lowest round trip, the most accurate estimate.\n * @default 5\n */\n samples?: number;\n}\n\n/** Latest latency measurements. A new object is created on every change. */\nexport interface Latency {\n /** Round-trip time in ms: the median of recent pings. `undefined` until the first pong. */\n readonly rtt: number | undefined;\n /**\n * Estimated server clock minus local clock, in ms. Add it to `Date.now()` for server time.\n * `undefined` until a pong carries `time`.\n */\n readonly offset: number | undefined;\n}\n\n/** Round-trip time and server clock offset for one client. Create with {@link createWebSocketLatency}. */\nexport interface WebSocketLatency {\n /** The client being measured. Hooks reading the latency keep it connected. */\n readonly client: WebSocketClient<any, any>;\n /** The current {@link Latency}. Returns the same object until it changes. */\n getSnapshot(): Latency;\n /** Called whenever a measurement changes. Compatible with `useSyncExternalStore`. */\n subscribe(listener: () => void): () => void;\n /** The estimated server time: `Date.now() + offset`, or `Date.now()` before any estimate. */\n serverNow(): number;\n /** Ping now instead of waiting for the next interval. Does nothing unless the socket is open. */\n ping(): void;\n /** Stop pinging and listening. Only needed for instances created dynamically. */\n dispose(): void;\n}\n\nconst EMPTY: Latency = Object.freeze({ rtt: undefined, offset: undefined });\n\nfunction median(values: number[]) {\n const sorted = [...values].sort((a, b) => a - b);\n const middle = sorted.length >> 1;\n return sorted.length % 2 ? sorted[middle]! : (sorted[middle - 1]! + sorted[middle]!) / 2;\n}\n\n/**\n * Measure round-trip time and estimate the server's clock, for ping displays, countdowns\n * and timestamps that must agree across players.\n *\n * Pings are sent on an interval while the socket is open, and paused otherwise. Measurements\n * from a previous connection are discarded on reconnect, but the last values stay readable\n * until new ones arrive. It doesn't open the connection by itself.\n *\n * Pongs are ordinary messages: they also reach `onMessage`, `lastMessage` and state stores.\n *\n * @example\n * ```ts\n * export const latency = createWebSocketLatency(game, {\n * ping: id => ({ type: 'ping', id }),\n * pong: message => (message.type === 'pong' ? { id: message.id, time: message.time } : undefined)\n * });\n *\n * // In a component:\n * const { rtt } = useWebSocketLatency(latency);\n *\n * // Anywhere: seconds left in a round that ends at the server's `endsAt`.\n * const left = (endsAt - latency.serverNow()) / 1000;\n * ```\n */\n// `const` + `NoInfer`: take `In` and `Out` from the client only, and keep literals such as\n// `type: 'ping'` from widening to `string` inside the callbacks.\nexport function createWebSocketLatency<In, const Out>(\n client: WebSocketClient<In, Out>,\n options: WebSocketLatencyOptions<NoInfer<In>, NoInfer<Out>>\n): WebSocketLatency {\n const interval = options.interval ?? 2000;\n const keep = Math.max(1, options.samples ?? 5);\n const listeners = new Set<() => void>();\n let snapshot = EMPTY;\n // Ping id -> `performance.now()` when sent. Pongs that never come are evicted oldest first.\n const inFlight = new Map<string, number>();\n let samples: { rtt: number; offset: number | undefined }[] = [];\n let nextId = 0;\n let timer: ReturnType<typeof setInterval> | undefined;\n\n function ping() {\n if (client.getSnapshot().status !== 'open') return;\n const id = ++nextId;\n inFlight.set(String(id), performance.now());\n if (inFlight.size > keep * 2) inFlight.delete(inFlight.keys().next().value!);\n client.send(options.ping(id), { volatile: true });\n }\n\n function start() {\n stop();\n inFlight.clear();\n samples = [];\n ping();\n timer = setInterval(ping, interval);\n }\n\n function stop() {\n clearInterval(timer);\n timer = undefined;\n }\n\n function measure(id: string, serverTime: number | undefined) {\n const sentAt = inFlight.get(id);\n if (sentAt === undefined) return;\n inFlight.delete(id);\n const rtt = performance.now() - sentAt;\n // The server read its clock about halfway through the round trip.\n const offset = serverTime === undefined ? undefined : serverTime + rtt / 2 - Date.now();\n samples.push({ rtt, offset });\n if (samples.length > keep) samples.shift();\n\n let best: (typeof samples)[number] | undefined;\n for (const sample of samples) {\n if (sample.offset !== undefined && (!best || sample.rtt < best.rtt)) best = sample;\n }\n snapshot = {\n rtt: median(samples.map(sample => sample.rtt)),\n offset: best ? best.offset : snapshot.offset\n };\n for (const listener of [...listeners]) listener();\n }\n\n const offs = [\n client.on('open', start),\n client.on('message', message => {\n const pong = options.pong(message);\n if (pong != null) measure(String(pong.id), pong.time);\n }),\n client.subscribe(() => {\n if (client.getSnapshot().status !== 'open') stop();\n })\n ];\n if (client.getSnapshot().status === 'open') start();\n\n return {\n client,\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n serverNow: () => Date.now() + (snapshot.offset ?? 0),\n ping,\n dispose() {\n stop();\n offs.forEach(off => off());\n }\n };\n}\n","import type { SendOptions, WebSocketClient } from './types';\n\n/** Options for {@link createWebSocketRequests}: how requests and replies look on the wire. */\nexport interface WebSocketRequestsOptions<Req, In, Out> {\n /**\n * Build the outgoing message for `request(message)`, tagged with `id`. Annotate `message`\n * to type what `request()` accepts.\n */\n send: (message: Req, id: number) => Out;\n /**\n * The id an incoming message replies to, or `undefined` (or `null`) if it isn't a reply.\n * A string id matches the number it was sent as, so either echo works.\n */\n reply: (message: In) => number | string | null | undefined;\n /**\n * Called on a reply. Return something other than `undefined` to reject the request with\n * it instead of resolving, e.g. for a server-side error reply. A stream throws it.\n */\n error?: (message: In) => unknown;\n /**\n * Whether a reply is the last one of a stream. `stream()` needs it. That reply is yielded\n * too, then the stream ends.\n */\n done?: (message: In) => boolean;\n /**\n * Build the message that tells the server to stop working on request `id`. Sent when this\n * side gives up: the request's `signal` aborts, it times out, a loop over a stream exits\n * early, or the manager is disposed. Not sent after the last reply, an error reply, a lost\n * connection or `close()`.\n */\n cancel?: (id: number) => Out;\n /**\n * Default time to wait for a reply, in ms. For a stream, the time to wait for each reply,\n * including the first. `Infinity` waits forever.\n * @default 10000\n */\n timeout?: number;\n}\n\n/** Per-call options for {@link WebSocketRequests.request} and {@link WebSocketRequests.stream}. */\nexport interface RequestOptions extends SendOptions {\n /**\n * Time to wait for a reply (for a stream, for each reply), in ms. Overrides the manager's\n * `timeout`.\n */\n timeout?: number;\n /** Abort the request. It rejects (a stream throws) with `signal.reason`. A late reply is ignored. */\n signal?: AbortSignal;\n}\n\n/** Request/response over one client. Create with {@link createWebSocketRequests}. */\nexport interface WebSocketRequests<Req, In> {\n /** The client requests are sent on. */\n readonly client: WebSocketClient<any, any>;\n /**\n * Send a request and resolve with its reply. Holds the connection until it settles.\n *\n * Rejects with a `TimeoutError` `DOMException` after `timeout`, with `signal.reason` when\n * aborted, and with an `Error` when the connection it was sent on closes (the reply can\n * no longer arrive) or the client stops. A request made while disconnected is queued like\n * any other message and waits for the connection; it rejects if it is dropped from the\n * queue (replaced by a newer request with the same `key`, or evicted by `queue.limit`).\n */\n request(message: Req, options?: RequestOptions): Promise<In>;\n /**\n * Send a request and iterate over its replies, up to and including the one `done` matches.\n * Holds the connection until the stream ends. Needs the `done` option.\n *\n * `timeout` is the time to wait for each reply. Replies that arrive before the loop reads\n * them are buffered. The stream is one-shot: iterate it once.\n *\n * Fails where `request()` rejects, with the same errors, after yielding the replies already\n * received. Aborting `signal` throws `signal.reason` right away and discards them. Leaving\n * the loop early, aborting and timing out send `cancel`.\n *\n * @example\n * ```ts\n * for await (const reply of rpc.stream({ type: 'ask', prompt }, { signal })) {\n * text += reply.delta;\n * }\n * ```\n */\n stream(message: Req, options?: RequestOptions): AsyncIterableIterator<In>;\n /** Number of requests and streams waiting for a reply. */\n pending(): number;\n /** Reject every pending request and stop listening. Only needed for managers created dynamically. */\n dispose(): void;\n}\n\n// Last id sent per client, shared by its managers so two of them never send the same id\n// and resolve each other's requests with one reply.\nconst lastIds = new WeakMap<WebSocketClient<any, any>, number>();\n\nconst VOLATILE = { volatile: true };\nconst noop = () => {};\n\ninterface Pending<In> {\n /** The socket the request went out on, or `null` while it waits in the client's queue. */\n socket: WebSocket | null;\n /** A reply that isn't an error. */\n receive(reply: In): void;\n /** Settle, failed with `reason` or not. `cancel` tells the server to stop. Only the first call counts. */\n end(failed: boolean, reason?: unknown, cancel?: boolean): void;\n}\n\n/**\n * Send requests and await their replies, matched by an id the library generates.\n *\n * Replies are ordinary messages: they also reach `onMessage`, `lastMessage` and state stores.\n *\n * @example\n * ```ts\n * type Request = { type: 'join'; room: string } | { type: 'leave' };\n *\n * export const rpc = createWebSocketRequests(game, {\n * send: (message: Request, id) => ({ ...message, id }),\n * reply: message => (message.type === 'reply' ? message.id : undefined),\n * error: message => (message.type === 'reply' && message.error ? new Error(message.error) : undefined)\n * });\n *\n * const reply = await rpc.request({ type: 'join', room: 'lobby' }, { timeout: 5000 });\n * ```\n */\n// `const` + `NoInfer`: `In` and `Out` come from the client only, with literals kept unwidened;\n// `Req` comes from how `send` annotates its parameter.\nexport function createWebSocketRequests<Req, In, const Out>(\n client: WebSocketClient<In, Out>,\n options: WebSocketRequestsOptions<Req, NoInfer<In>, NoInfer<Out>>\n): WebSocketRequests<Req, In> {\n const waiting = new Map<string, Pending<In>>();\n\n function endWhere(\n test: (entry: Pending<In>) => boolean,\n reason: () => unknown,\n cancel?: boolean\n ) {\n for (const entry of [...waiting.values()]) if (test(entry)) entry.end(true, reason(), cancel);\n }\n\n const offs = [\n client.on('message', message => {\n const id = options.reply(message);\n const entry = id == null ? undefined : waiting.get(String(id));\n if (!entry) return;\n const error = options.error?.(message);\n if (error === undefined) entry.receive(message);\n else entry.end(true, error);\n }),\n // `open` listeners run before the client flushes its queue: queued requests go out on this socket.\n client.on('open', () => {\n for (const entry of waiting.values()) entry.socket ??= client.socket;\n }),\n client.subscribe(() => {\n const { status } = client.getSnapshot();\n if (status === 'closed' || status === 'idle') {\n // The queue was cleared too, so nothing still waiting can be sent.\n endWhere(\n () => true,\n () => new Error('WebSocket was closed before the request was answered')\n );\n } else {\n endWhere(\n entry => entry.socket !== null && entry.socket !== client.socket,\n () => new Error('WebSocket connection was lost before the request was answered')\n );\n }\n })\n ];\n\n /**\n * Send `message` as a new request. `next` gets each reply that isn't an error and returns\n * whether it was the last; `done` runs once, when the request ends. Returns a function that\n * ends it from this side and tells the server.\n */\n function start(\n message: Req,\n requestOptions: RequestOptions,\n next: (reply: In) => boolean,\n done: (failed: boolean, reason?: unknown) => void\n ): () => void {\n const { signal, priority } = requestOptions;\n if (signal?.aborted) {\n done(true, signal.reason);\n return noop;\n }\n\n const id = (lastIds.get(client) ?? 0) + 1;\n lastIds.set(client, id);\n const key = String(id);\n const timeout = requestOptions.timeout ?? options.timeout ?? 10_000;\n const release = client.acquire();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const onAbort = () => entry.end(true, signal!.reason, true);\n\n function arm() {\n clearTimeout(timer);\n if (timeout > 0 && timeout < Infinity) {\n timer = setTimeout(\n () =>\n entry.end(\n true,\n new DOMException(`Request ${id} timed out after ${timeout}ms`, 'TimeoutError'),\n true\n ),\n timeout\n );\n }\n }\n\n const entry: Pending<In> = {\n socket: null,\n receive(reply) {\n if (next(reply)) entry.end(false);\n else arm();\n },\n end(failed, reason, cancel) {\n if (!waiting.delete(key)) return;\n clearTimeout(timer);\n signal?.removeEventListener('abort', onAbort);\n release();\n done(failed, reason);\n if (cancel && options.cancel) {\n try {\n // Still queued, it can't be taken back: queue the cancel right behind it. Once sent,\n // the cancel only means something on that connection, so drop it unless it's open.\n client.send(options.cancel(id), entry.socket ? VOLATILE : { priority });\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n };\n waiting.set(key, entry);\n signal?.addEventListener('abort', onAbort, { once: true });\n arm();\n\n const { status } = client.getSnapshot();\n if (status === 'closed' || status === 'idle') {\n // Closed until someone reconnects it, or (idle after acquiring) it can't connect here.\n entry.end(true, new Error(`WebSocket is ${status}: request not sent`));\n } else {\n try {\n const sent = client.send(options.send(message, id), {\n ...requestOptions,\n onDrop() {\n entry.end(\n true,\n new Error('Request was dropped from the send queue before it was sent')\n );\n requestOptions.onDrop?.();\n }\n });\n if (sent) entry.socket = client.socket;\n // Open but not sent: `maxBuffered` dropped it, and nothing will send it later.\n else if (client.socket?.readyState === 1)\n entry.end(true, new Error('WebSocket send buffer is full: request dropped'));\n else if (requestOptions.volatile)\n entry.end(true, new Error('WebSocket is not open: request dropped'));\n } catch (error) {\n entry.end(true, error);\n }\n }\n return () => entry.end(false, undefined, true);\n }\n\n return {\n client,\n request(message, requestOptions = {}) {\n return new Promise<In>((resolve, reject) => {\n start(\n message,\n requestOptions,\n reply => {\n resolve(reply);\n return true;\n },\n (failed, reason) => {\n if (failed) reject(reason);\n }\n );\n });\n },\n stream(message, requestOptions = {}) {\n const last = options.done;\n if (!last) throw new TypeError('stream() needs the `done` option');\n const replies: In[] = [];\n let ended: [failed: boolean, reason: unknown] | undefined;\n let wake = noop;\n const stop = start(\n message,\n requestOptions,\n reply => {\n replies.push(reply);\n wake();\n return last(reply);\n },\n (failed, reason) => {\n // Aborting stops right away; any other end comes after the replies already received.\n if (requestOptions.signal?.aborted) replies.length = 0;\n ended = [failed, reason];\n wake();\n }\n );\n\n async function* read() {\n try {\n for (;;) {\n if (replies.length) yield replies.shift()!;\n else if (ended) {\n if (ended[0]) throw ended[1];\n return;\n } else await new Promise<void>(resolve => (wake = resolve));\n }\n } finally {\n stop();\n }\n }\n\n const iterator = read();\n const close = iterator.return;\n // A generator closed before its first `next()` skips `finally`, and one closed during a\n // `next()` waits for it: stop first, so leaving a loop tells the server right away.\n iterator.return = value => {\n stop();\n return close.call(iterator, value);\n };\n return iterator;\n },\n pending: () => waiting.size,\n dispose() {\n offs.forEach(off => off());\n endWhere(\n () => true,\n () => new Error('Requests were disposed'),\n true\n );\n }\n };\n}\n","import type { WebSocketClient } from './types';\n\n/** What `request` returns for a request: any object with these fields, such as the message itself. */\ntype RequestFields = { id: number | string; method: string; params?: unknown; timeout?: number };\n\n/**\n * The type of `params` in `Q`, keeping `undefined` when they're optional and giving `undefined`\n * when there are none. (Inferring `params?: P` directly would drop the `undefined`.)\n */\ntype ParamsOf<Q> = Q extends unknown\n ? 'params' extends keyof Q\n ? Q['params' & keyof Q]\n : undefined\n : never;\n\n/**\n * Options for {@link createWebSocketResponder}: how the server's requests and your replies look\n * on the wire.\n *\n * @typeParam Q - What `request` returns. Handlers receive its `params`.\n * @typeParam R - What handlers return, from the annotation on `respond`'s `result`.\n */\nexport interface WebSocketResponderOptions<Q extends RequestFields, R, In, Out> {\n /**\n * Recognize a request from the server: its `id`, the `method` handlers are registered for,\n * and the `params` they receive. Return `undefined` (or `null`) for any other message.\n * A `timeout` (in ms) overrides the manager's for this request.\n *\n * `params` come from the server, so validate them before acting on them.\n */\n request: (message: In) => Q | null | undefined;\n /** Build the reply to request `id`. Annotate `result` to type what handlers return. */\n respond: (id: number | string, result: R) => Out;\n /**\n * Build the error reply to request `id`: what a handler threw, a `NotFoundError` `DOMException`\n * when nothing is registered for the method, a `TimeoutError` after `timeout`, or an\n * `AbortError` from `dispose()`.\n */\n error: (id: number | string, error: unknown) => Out;\n /**\n * The id of a request the server withdrew, or `undefined` (or `null`) if the message isn't a\n * cancellation. The request's `signal` aborts and nothing is sent.\n */\n cancel?: (message: In) => number | string | null | undefined;\n /**\n * Time to answer a request, in ms. When it runs out, the `error` reply (a `TimeoutError`)\n * is sent and the request's `signal` aborts.\n * @default Infinity\n */\n timeout?: number;\n}\n\n/** A request from the server that hasn't been answered yet. */\nexport interface IncomingRequest<P = unknown, R = unknown> {\n /** The id the server sent. Replies carry it back. */\n readonly id: number | string;\n /** The method it was sent for. */\n readonly method: string;\n /** The `params` from `request`. They come from the server, so validate them. */\n readonly params: P;\n /**\n * Aborts when the answer is no longer wanted: the server cancelled the request, it timed out,\n * the connection it arrived on closed, the responder was disposed, or it was answered with\n * `respond` or `reject` while its handler was still running.\n */\n readonly signal: AbortSignal;\n /**\n * Reply with `result`. Only the first answer counts.\n * @returns `true` if the reply was sent, `false` if the request was already answered or is gone.\n */\n respond(result: R): boolean;\n /**\n * Reply with an error, built by the `error` option. Only the first answer counts.\n * @returns `true` if the reply was sent, `false` if the request was already answered or is gone.\n */\n reject(error: unknown): boolean;\n}\n\n/**\n * Answers a request in code. Return (or resolve) the result to reply with it; throw (or reject)\n * to send the `error` reply instead.\n */\nexport type RequestHandler<P = unknown, R = unknown> = (\n params: P,\n request: IncomingRequest<P, R>\n) => R | PromiseLike<R>;\n\n/** Answers the server's requests on one client. Create with {@link createWebSocketResponder}. */\nexport interface WebSocketResponder<P = unknown, R = unknown> {\n /** The client requests arrive on. Hooks reading the responder keep it connected. */\n readonly client: WebSocketClient<any, any>;\n /**\n * Answer requests for `method` until the returned function is called.\n *\n * With a `handler`, requests are answered in code. Without one, they wait in\n * {@link getSnapshot} until something calls `respond` or `reject` on them, e.g. a dialog\n * that asks the user. When several registrations exist for one method, the newest one takes\n * new requests, and unregistering it hands them back to the previous one. Requests for a\n * method with no registration get a `NotFoundError` reply.\n *\n * @returns An unregister function (safe to call more than once). Requests already waiting stay.\n */\n onRequest(method: string, handler?: RequestHandler<P, R>): () => void;\n /**\n * Requests waiting for an answer from outside a handler, oldest first. Returns the same array\n * until it changes.\n */\n getSnapshot(): readonly IncomingRequest<P, R>[];\n /** Called whenever {@link getSnapshot} changes. Compatible with `useSyncExternalStore`. */\n subscribe(listener: () => void): () => void;\n /**\n * Stop answering: send an `AbortError` reply to every request in flight and stop listening.\n * Only needed for responders created dynamically.\n */\n dispose(): void;\n}\n\ninterface Entry {\n /** The socket the request arrived on. Its reply can only go back on that one. */\n readonly socket: WebSocket | null;\n /** Forget the request and abort its signal, without replying. */\n drop(reason: DOMException): void;\n /** Forget the request, abort its signal and send the `error` reply, both with `error`. */\n fail(error: DOMException): void;\n}\n\nconst VOLATILE = { volatile: true };\nconst abortError = (message: string) => new DOMException(message, 'AbortError');\n\n/**\n * Answer requests the server sends: run a handler for tools that only exist in the browser\n * (selection, clipboard, geolocation), or let a person answer, e.g. to approve an agent's action.\n * The reverse of `createWebSocketRequests`.\n *\n * Every request gets exactly one reply while its connection lives: the handler's result, an\n * `error` reply when it throws, has no registration or times out. When the connection a request\n * arrived on closes, the request is dropped and nothing is sent, since the server can't match a\n * reply on a new connection. Replies are never queued.\n *\n * Requests are ordinary messages: they also reach `onMessage`, `lastMessage` and state stores.\n *\n * @example\n * ```ts\n * export const tools = createWebSocketResponder(agent, {\n * request: message => (message.type === 'call' ? message : undefined),\n * respond: (id, result: unknown) => ({ type: 'result', id, result }),\n * error: (id, error) => ({ type: 'result', id, error: String(error) })\n * });\n *\n * tools.onRequest('get_selection', () => window.getSelection()?.toString() ?? '');\n *\n * // In a component: requests a person answers.\n * const approvals = useAgentApproval(tools, 'approve');\n * approvals[0]?.respond({ approved: true });\n * ```\n */\n// `const` + `NoInfer`: `In` and `Out` come from the client only, with literals kept unwidened;\n// `Q` comes from what `request` returns and `R` from how `respond` annotates `result`.\nexport function createWebSocketResponder<Q extends RequestFields, R, In, const Out>(\n client: WebSocketClient<In, Out>,\n options: WebSocketResponderOptions<Q, R, NoInfer<In>, NoInfer<Out>>\n): WebSocketResponder<ParamsOf<Q>, R> {\n type P = ParamsOf<Q>;\n const entries = new Map<string, Entry>();\n const registrations = new Map<string, { handler?: RequestHandler<P, R> }[]>();\n const listeners = new Set<() => void>();\n let waiting: readonly IncomingRequest<P, R>[] = [];\n\n function setWaiting(next: readonly IncomingRequest<P, R>[]) {\n waiting = next;\n for (const listener of [...listeners]) listener();\n }\n\n /** Send a reply, or an `error` reply if building or encoding it throws. */\n function reply(id: number | string, build: () => Out): boolean {\n try {\n return client.send(build(), VOLATILE);\n } catch (error) {\n return client.send(options.error(id, error), VOLATILE);\n }\n }\n\n function receive(incoming: Q) {\n const { id, method } = incoming;\n const key = String(id);\n if (entries.has(key)) return; // sent again while it is being answered\n const registration = registrations.get(method)?.at(-1);\n if (!registration) {\n const error = new DOMException(`No handler for request method \"${method}\"`, 'NotFoundError');\n reply(id, () => options.error(id, error));\n return;\n }\n\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n let done = false;\n let running = false;\n\n function settle(build: (() => Out) | null, reason?: unknown) {\n if (done) return false;\n done = true;\n entries.delete(key);\n clearTimeout(timer);\n if (waiting.includes(request)) setWaiting(waiting.filter(entry => entry !== request));\n if (reason !== undefined) controller.abort(reason);\n return build ? reply(id, build) : false;\n }\n\n // An answer from outside the handler makes the handler's work moot.\n const answeredElsewhere = () =>\n running ? abortError(`Request ${id} was answered elsewhere`) : undefined;\n const request: IncomingRequest<P, R> = {\n id,\n method,\n params: incoming.params as P,\n signal: controller.signal,\n respond: result => settle(() => options.respond(id, result), answeredElsewhere()),\n reject: error => settle(() => options.error(id, error), answeredElsewhere())\n };\n const fail = (error: DOMException) => {\n settle(() => options.error(id, error), error);\n };\n // Registered before the handler runs, so a handler that closes the socket drops its own request.\n entries.set(key, {\n socket: client.socket,\n drop: reason => {\n settle(null, reason);\n },\n fail\n });\n\n const timeout = incoming.timeout ?? options.timeout ?? Infinity;\n if (timeout > 0 && timeout < Infinity) {\n timer = setTimeout(\n () =>\n fail(\n new DOMException(`Request ${id} was not answered within ${timeout}ms`, 'TimeoutError')\n ),\n timeout\n );\n }\n\n const { handler } = registration;\n if (!handler) {\n setWaiting([...waiting, request]);\n return;\n }\n running = true;\n // Also catches a handler that throws synchronously.\n new Promise<R>(resolve => resolve(handler(request.params, request))).then(\n result => {\n running = false;\n request.respond(result);\n },\n error => {\n running = false;\n request.reject(error);\n }\n );\n }\n\n const offs = [\n client.on('message', message => {\n const incoming = options.request(message);\n if (incoming != null) {\n receive(incoming);\n return;\n }\n const id = options.cancel?.(message);\n if (id != null) {\n entries.get(String(id))?.drop(abortError(`Request ${id} was cancelled`));\n }\n }),\n // Every close, reconnect and teardown replaces `client.socket` and notifies.\n client.subscribe(() => {\n for (const entry of [...entries.values()]) {\n if (entry.socket !== client.socket) {\n entry.drop(abortError('WebSocket connection was lost before the request was answered'));\n }\n }\n })\n ];\n\n return {\n client,\n onRequest(method, handler) {\n let stack = registrations.get(method);\n if (!stack) registrations.set(method, (stack = []));\n const registration = { handler };\n stack.push(registration);\n return () => {\n const index = stack.indexOf(registration);\n if (index !== -1) stack.splice(index, 1);\n };\n },\n getSnapshot: () => waiting,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n offs.forEach(off => off());\n registrations.clear();\n for (const entry of [...entries.values()]) entry.fail(abortError('Responder was disposed'));\n }\n };\n}\n","import type { WebSocketLatency } from './latency';\nimport type { WebSocketClient } from './types';\n\ntype Timer = ReturnType<typeof setTimeout>;\n\n/** Options for {@link createWebSocketSendRate}. */\nexport interface WebSocketSendRateOptions {\n /**\n * Ticks per second while the connection keeps up.\n * @default 30\n */\n maxRate?: number;\n /**\n * The rate never drops below this, in ticks per second.\n * @default 5\n */\n minRate?: number;\n /**\n * Bytes waiting in the browser's send buffer (`socket.bufferedAmount`) above which a tick\n * sends nothing and the rate halves.\n * @default 1024\n */\n maxBuffered?: number;\n /**\n * Measurements from `createWebSocketLatency` on the same client. With them, a round-trip time\n * that rises also lowers the rate. Without them, only the send buffer is watched.\n */\n latency?: Pick<WebSocketLatency, 'getSnapshot'>;\n /**\n * How far `rtt` may rise above the lowest one measured on this connection before the rate\n * halves, in ms. A steady high ping keeps the full rate; only a rising one counts.\n * @default 100\n */\n maxRttIncrease?: number;\n}\n\n/** The current send rate. A new object is created on every change. */\nexport interface SendRate {\n /** Ticks per second, rounded. */\n readonly rate: number;\n /**\n * Whether the last tick found the link congested: the send buffer above `maxBuffered`, or\n * `rtt` risen more than `maxRttIncrease`.\n */\n readonly congested: boolean;\n}\n\n/** Keyed sends coalesced at a rate that adapts to the link. Create with {@link createWebSocketSendRate}. */\nexport interface WebSocketSendRate<Out> {\n /** The client the messages are sent on. */\n readonly client: WebSocketClient<any, Out>;\n /**\n * Send `message` at the next tick. A message with the same `key` still waiting for it is\n * replaced, so only the newest one goes out. Sent with `volatile`.\n */\n send(message: Out, key: string): void;\n /** Send everything waiting now, e.g. right before a one-shot action that must carry the latest aim. */\n flush(): void;\n /** The current {@link SendRate}. Returns the same object until it changes. */\n getSnapshot(): SendRate;\n /** Called whenever the rate or `congested` changes. Compatible with `useSyncExternalStore`. */\n subscribe(listener: () => void): () => void;\n /** Stop sending and listening. Only needed for instances created dynamically. */\n dispose(): void;\n}\n\nconst VOLATILE = { volatile: true };\n\n/**\n * Send high-rate state, such as aim, cursor or position, at a rate that adapts to the\n * connection. Messages are coalesced per key between ticks. The rate drops by half when the\n * send buffer fills up or the round trip grows, and climbs back one tick per second at a time\n * when the link keeps up, so players on a poor connection degrade smoothly instead of building\n * up lag.\n *\n * Ticks run only while the socket is open and something is waiting. Messages waiting during a\n * reconnect go out after it; `close()` drops them. It doesn't open the connection by itself.\n *\n * @example\n * ```ts\n * export const stream = createWebSocketSendRate(game, { latency, maxRate: 30 });\n *\n * // On every pointer move: at most 30 a second, fewer on a congested link.\n * stream.send({ type: 'aim', angle }, 'aim');\n * ```\n */\nexport function createWebSocketSendRate<Out>(\n client: WebSocketClient<any, Out>,\n options: WebSocketSendRateOptions = {}\n): WebSocketSendRate<Out> {\n const { maxBuffered = 1024, latency, maxRttIncrease = 100 } = options;\n const maxRate = options.maxRate ?? 30;\n const minRate = Math.min(options.minRate ?? 5, maxRate);\n const listeners = new Set<() => void>();\n // Insertion order is send order: a replaced message moves to the end.\n const pending = new Map<string, Out>();\n let rate = maxRate;\n let snapshot: SendRate = { rate: maxRate, congested: false };\n let last = -Infinity; // `performance.now()` at the last tick\n let best = Infinity; // the lowest `rtt` on this connection\n let slow = false; // `rtt` has risen too far above `best`\n let measured: unknown; // the latency snapshot already reacted to\n let timer: Timer | undefined;\n let disposed = false;\n\n function flush() {\n for (const [key, message] of pending) {\n if (!client.send(message, VOLATILE)) return; // not open: keep the rest for later\n pending.delete(key);\n }\n }\n\n function publish(congested: boolean) {\n const rounded = Math.round(rate);\n if (rounded === snapshot.rate && congested === snapshot.congested) return;\n snapshot = { rate: rounded, congested };\n for (const listener of [...listeners]) listener();\n }\n\n function schedule() {\n if (disposed || timer !== undefined || !pending.size || client.getSnapshot().status !== 'open')\n return;\n // Right away after a pause, otherwise one interval after the last tick.\n timer = setTimeout(tick, Math.max(0, last + 1000 / rate - performance.now()));\n }\n\n function stop() {\n clearTimeout(timer);\n timer = undefined;\n }\n\n // Additive increase, multiplicative decrease, as TCP does.\n function tick() {\n timer = undefined;\n last = performance.now();\n const measurement = latency?.getSnapshot();\n if (measurement !== measured) {\n // React to each measurement once.\n measured = measurement;\n const rtt = measurement?.rtt;\n if (rtt !== undefined) {\n best = Math.min(best, rtt);\n slow = rtt - best > maxRttIncrease;\n if (slow) rate /= 2;\n }\n }\n // Checked once per tick, not per message, so messages in one tick don't count against each other.\n const full = (client.socket?.bufferedAmount ?? 0) > maxBuffered;\n if (full) rate /= 2;\n else {\n flush();\n if (!slow) rate++;\n }\n rate = Math.min(maxRate, Math.max(minRate, rate));\n publish(full || slow);\n schedule();\n }\n\n const offs = [\n client.on('open', () => {\n // A new connection may take a different route: measure its best round trip afresh, and\n // ignore the last connection's measurement, which stays readable until a new one arrives.\n best = Infinity;\n slow = false;\n measured = latency?.getSnapshot();\n schedule(); // after the client's own queue, which is sent once `open` listeners return\n }),\n client.subscribe(() => {\n const { status } = client.getSnapshot();\n if (status !== 'open') stop();\n // Like the client's queue: kept through a reconnect, dropped by `close()` or giving up.\n if (status === 'closed' || status === 'idle') pending.clear();\n })\n ];\n\n return {\n client,\n send(message, key) {\n pending.delete(key);\n pending.set(key, message);\n schedule();\n },\n flush,\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n disposed = true;\n stop();\n pending.clear();\n offs.forEach(off => off());\n }\n };\n}\n","import type { WebSocketClient } from './types';\n\n/** Options for {@link createWebSocketState}. */\nexport interface WebSocketStateOptions<S, In> {\n /** The state before any message arrives, and what `reset()` returns to. */\n initialState: S;\n /**\n * Fold one incoming message into the state. Must be pure: return a new object for a\n * change, or the same `state` to ignore the message (subscribers aren't notified).\n */\n reduce: (state: S, message: In) => S;\n /**\n * `'frame'`: notify subscribers at most once per animation frame instead of once per\n * message, so a fast stream (tokens, tickers, game state) renders once per frame.\n * `getState()` is always current; only notifications wait. `setState` and `reset` still\n * notify right away.\n *\n * Browsers pause animation frames in background tabs: the state keeps up, and subscribers\n * hear about it when the tab is visible again. Without `requestAnimationFrame` (Node, some\n * workers), it waits about 16 ms instead.\n */\n batch?: 'frame';\n}\n\n/**\n * State derived from a client's messages. Read it with `useWebSocketState(store, selector)`,\n * or with `getState`/`subscribe` outside React.\n *\n * @typeParam S - The state type. Don't use a function type: `setState` treats functions as updaters.\n */\nexport interface WebSocketState<S> {\n /** The client whose messages this store reduces. Hooks reading the store keep it connected. */\n readonly client: WebSocketClient<any, any>;\n /** The `initialState` it was created with. Used for server rendering and hydration. */\n readonly initialState: S;\n /** The current state. Returns the same object until it changes. */\n getState(): S;\n /** Called whenever the state changes. Compatible with `useSyncExternalStore`. */\n subscribe(listener: () => void): () => void;\n /** Replace the state, or update it from the current one, e.g. for optimistic updates. */\n setState(update: S | ((state: S) => S)): void;\n /** Go back to `initialState`. */\n reset(): void;\n /** Stop reducing messages. Only needed for stores created dynamically rather than at module level. */\n dispose(): void;\n}\n\n/**\n * Keep state derived from a client's messages, like a reducer that runs on every message.\n * It starts listening immediately and keeps the state across reconnects; it doesn't open\n * the connection by itself.\n *\n * @example\n * ```ts\n * const prices = createWebSocket<Tick>('wss://api.example.com/prices', { json: true });\n *\n * export const priceState = createWebSocketState(prices, {\n * initialState: {} as Record<string, number>,\n * reduce: (state, tick) => ({ ...state, [tick.symbol]: tick.price })\n * });\n *\n * // In a component: re-renders only when ACME's price changes.\n * const acme = useWebSocketState(priceState, state => state.ACME);\n * ```\n */\nexport function createWebSocketState<S, In>(\n client: WebSocketClient<In, any>,\n // `In` comes from the client only, not from how `reduce` annotates its parameter.\n options: WebSocketStateOptions<S, NoInfer<In>>\n): WebSocketState<S> {\n const { initialState, reduce, batch } = options;\n let state = initialState;\n // What subscribers were last told about, so a frame that finds nothing new stays quiet.\n let notified = state;\n let frame = false;\n const listeners = new Set<() => void>();\n\n function notify() {\n if (Object.is(state, notified)) return;\n notified = state;\n for (const listener of [...listeners]) listener();\n }\n\n function flush() {\n frame = false;\n notify();\n }\n\n function update(change: S | ((state: S) => S), later?: boolean) {\n const next = typeof change === 'function' ? (change as (state: S) => S)(state) : change;\n if (Object.is(next, state)) return;\n state = next;\n if (!later) notify();\n else if (!frame) {\n frame = true;\n // Node and some workers have no animation frames: wait about one instead.\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(flush);\n else setTimeout(flush, 16);\n }\n }\n\n // A throwing reducer leaves the state unchanged; the client re-throws the error asynchronously.\n const off = client.on('message', message =>\n update(current => reduce(current, message), batch === 'frame')\n );\n\n return {\n client,\n initialState,\n getState: () => state,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n setState: change => update(change),\n reset: () => update(initialState),\n dispose: off\n };\n}\n","import type { WebSocketClient } from './types';\n\ntype Timer = ReturnType<typeof setTimeout>;\n\n/** Options for {@link createWebSocketTopics}: the messages your server expects. */\nexport interface WebSocketTopicsOptions<Out> {\n /** The message that subscribes to `topic`. */\n subscribe: (topic: string) => Out;\n /** The message that unsubscribes from `topic`. Omit it if your server has none. */\n unsubscribe?: (topic: string) => Out;\n}\n\n/** Reference-counted topic subscriptions on one client. Create with {@link createWebSocketTopics}. */\nexport interface WebSocketTopics {\n /** The client the subscription messages are sent on. */\n readonly client: WebSocketClient<any, any>;\n /**\n * Hold a subscription to `topic`, and the client's connection, until the returned\n * function is called. The release function is safe to call more than once.\n */\n acquire(topic: string): () => void;\n /** The topics currently held, in the order they were first acquired. */\n active(): string[];\n /** Stop managing subscriptions. Only needed for managers created dynamically. */\n dispose(): void;\n}\n\n/**\n * Manage topic subscriptions that survive reconnects.\n *\n * - The first holder of a topic sends `subscribe(topic)`. Later holders share it.\n * - A tick after the last holder releases it, `unsubscribe(topic)` is sent, so StrictMode\n * and route transitions don't churn subscriptions.\n * - After every reconnect, every held topic is subscribed again on the new socket, before\n * messages queued while disconnected are sent.\n *\n * Subscriptions are only sent while the socket is open; nothing is queued for them.\n *\n * @example\n * ```ts\n * export const topics = createWebSocketTopics(live, {\n * subscribe: topic => ({ type: 'subscribe', topic }),\n * unsubscribe: topic => ({ type: 'unsubscribe', topic })\n * });\n *\n * // In a component:\n * useWebSocketTopic(topics, `prices:${symbol}`);\n * ```\n */\n// `const` + `NoInfer`: take `Out` from the client only, and keep literals such as\n// `type: 'subscribe'` from widening to `string` inside the callbacks.\nexport function createWebSocketTopics<const Out>(\n client: WebSocketClient<any, Out>,\n options: WebSocketTopicsOptions<NoInfer<Out>>\n): WebSocketTopics {\n const counts = new Map<string, number>();\n const pendingRemoval = new Map<string, Timer>();\n // Topics subscribed on `sentOn`. A new socket starts with none, whatever the old one had.\n let sentOn: WebSocket | null = null;\n let sent = new Set<string>();\n\n function isOpen() {\n return client.getSnapshot().status === 'open' && client.socket !== null;\n }\n\n function subscribe(topic: string) {\n if (!isOpen()) return; // sent on `open` instead\n if (client.socket !== sentOn) {\n sentOn = client.socket;\n sent = new Set();\n }\n if (sent.has(topic)) return;\n sent.add(topic);\n client.send(options.subscribe(topic));\n }\n\n function unsubscribe(topic: string) {\n if (!isOpen() || client.socket !== sentOn || !sent.delete(topic)) return;\n if (options.unsubscribe) client.send(options.unsubscribe(topic));\n }\n\n // `open` listeners run before the client flushes its queue.\n const off = client.on('open', () => {\n sentOn = client.socket;\n sent = new Set();\n for (const topic of counts.keys()) subscribe(topic);\n });\n\n return {\n client,\n acquire(topic) {\n const releaseConnection = client.acquire();\n clearTimeout(pendingRemoval.get(topic));\n pendingRemoval.delete(topic);\n counts.set(topic, (counts.get(topic) ?? 0) + 1);\n subscribe(topic);\n\n let released = false;\n return () => {\n if (released) return;\n released = true;\n const remaining = (counts.get(topic) ?? 1) - 1;\n if (remaining > 0) {\n counts.set(topic, remaining);\n } else {\n counts.delete(topic);\n pendingRemoval.set(\n topic,\n setTimeout(() => {\n pendingRemoval.delete(topic);\n if (!counts.has(topic)) unsubscribe(topic);\n }, 0)\n );\n }\n releaseConnection();\n };\n },\n active: () => [...counts.keys()],\n dispose() {\n off();\n for (const timer of pendingRemoval.values()) clearTimeout(timer);\n pendingRemoval.clear();\n }\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core.cjs","names":["VOLATILE"],"sources":["../src/types.ts","../src/client.ts","../src/topics.ts","../src/feeds.ts","../src/latency.ts","../src/presence.ts","../src/requests.ts","../src/responder.ts","../src/runs.ts","../src/sendrate.ts","../src/state.ts"],"sourcesContent":["/**\n * Where to connect: a URL, or a function that returns one (sync or async).\n *\n * A function is called before **every** connection attempt, including reconnects,\n * so it can put a fresh auth token in the URL. If it throws or rejects, that counts\n * as a failed attempt and is retried with backoff.\n *\n * @example\n * ```ts\n * createWebSocket(async () => `wss://api.example.com/live?token=${await getToken()}`);\n * ```\n */\nexport type UrlSource = string | (() => string | Promise<string>);\n\n/**\n * Connection state.\n *\n * - `idle`: not connected and not trying to be. Nobody is using the client yet, or everyone stopped.\n * - `connecting`: the first attempt is in progress.\n * - `open`: connected. `send()` goes out immediately.\n * - `reconnecting`: the connection dropped (or an attempt failed) and a retry is scheduled or in progress.\n * - `closed`: stopped for good, by `close()` or after giving up on reconnecting.\n * It stays closed until `connect()` or `reconnect()` is called, or every hook unmounts. If\n * nothing held it when it stopped, the next hook to mount starts it again.\n */\nexport type Status = 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed';\n\n/** The client's state as seen by React. A new object is created on every change. */\nexport interface Snapshot<In> {\n /** Connection state. */\n readonly status: Status;\n /** Most recent message (after `parse`/`json` and `schema`), kept across reconnects. */\n readonly lastMessage: In | undefined;\n /**\n * Consecutive failed attempts since the last stable connection. A connection that closes\n * within `reconnect.minUptime` of opening counts as failed. `0` while open.\n */\n readonly attempt: number;\n}\n\n/** Details of a closed connection, from the browser's `CloseEvent`. */\nexport interface CloseInfo {\n /** Close code: `1000` is a normal close, `1006` an abnormal one (e.g. network loss), `4000`–`4999` are yours. */\n readonly code: number;\n /** Close reason sent by the side that closed the connection. Often empty. */\n readonly reason: string;\n /** Whether the closing handshake completed. */\n readonly wasClean: boolean;\n}\n\n/** Anything `WebSocket.prototype.send` accepts: a string, `Blob`, `ArrayBuffer` or typed array. */\nexport type SendData = Parameters<WebSocket['send']>[0];\n\n/** Reconnect timing. Delay before attempt `n` = `min(maxDelay, minDelay * factor ** (n - 1))`, minus jitter. */\nexport interface ReconnectOptions {\n /**\n * Maximum consecutive reconnect attempts. When the last one fails too, the client\n * gives up: the status becomes `closed` and `ready` rejects. `0` never retries.\n * @default Infinity\n */\n maxAttempts?: number;\n /**\n * How long a connection must stay open, in ms, before the backoff resets. One that closes\n * sooner counts as a failed attempt, so a server that accepts and then closes right away\n * (e.g. rejecting the credentials sent on open) is retried with a growing delay, and\n * `maxAttempts` still applies. `0` resets on every open.\n * @default 5000\n */\n minUptime?: number;\n /**\n * Delay before the first retry, in ms.\n * @default 1000\n */\n minDelay?: number;\n /**\n * Upper bound for the delay, in ms.\n * @default 30000\n */\n maxDelay?: number;\n /**\n * Backoff multiplier applied per attempt.\n * @default 2\n */\n factor?: number;\n /**\n * Randomly shave up to this fraction (0–1) off each delay, so that many clients\n * dropped at once don't all reconnect at the same moment. The default `1` picks each\n * delay at random between 0 and the backoff (\"full jitter\").\n * @default 1\n */\n jitter?: number;\n /**\n * Called on every unexpected close. Return `false` to stop reconnecting.\n *\n * The default, `retryableClose`, retries everything except `1002` (protocol error), `1003`\n * (unsupported data) and `1008` (policy violation). Your own function replaces it; call\n * `retryableClose` from it to keep that rule, or return `true` to retry every close.\n *\n * @example\n * ```ts\n * import { retryableClose } from 'react-websocket';\n *\n * // Also stop when our server rejects the credentials.\n * shouldReconnect: close => close.code !== 4001 && retryableClose(close)\n * ```\n * @default retryableClose\n */\n shouldReconnect?: (close: CloseInfo) => boolean;\n /**\n * How long the server asked the client to wait before trying again, in ms, read from the\n * close. Return `undefined` when it didn't ask. The client waits that long plus the usual\n * backoff, so clients told the same delay still spread out, and the `online` and\n * `visibilitychange` events don't cut the wait short.\n *\n * The default, `retryAfter`, reads a `1012` (Service Restart) or `1013` (Try Again Later)\n * close whose reason is a number of seconds, like an HTTP `Retry-After` header. Your own\n * function replaces it.\n *\n * A `1013` close that names no delay still waits longer than other closes: its backoff starts\n * at `maxDelay`.\n *\n * @example\n * ```ts\n * import { retryAfter } from 'react-websocket';\n *\n * // Our server also closes with 4429 and the delay in ms as the reason.\n * retryAfter: close => (close.code === 4429 ? Number(close.reason) : retryAfter(close))\n * ```\n * @default retryAfter\n */\n retryAfter?: (close: CloseInfo) => number | undefined;\n}\n\n/** Options for `client.migrate()`. */\nexport interface MigrateOptions {\n /**\n * Where to connect instead: a URL, or a function called before the connection, like the\n * client's own `url`. Later reconnects go there too, until the client is closed, gives up or\n * is torn down.\n * @default the client's current URL\n */\n url?: UrlSource;\n /**\n * Move at a random moment within this many ms, so that clients asked to move together don't\n * all reconnect at once.\n * @default 0\n */\n within?: number;\n}\n\n/** Application-level keep-alive. Useful behind proxies that drop idle connections. */\nexport interface HeartbeatOptions {\n /** Send `message` every `interval` ms while open. */\n interval: number;\n /**\n * Sent as-is (it is not passed through `serialize` or `json`).\n * @default 'ping'\n */\n message?: string;\n /** Incoming data equal to this string is swallowed instead of being delivered as a message. */\n pong?: string;\n /**\n * If nothing at all arrives within this many ms after a ping, the connection is\n * treated as dead: it closes with code `4000` and reconnects.\n *\n * A ping also goes out right away when the browser comes back online or the tab becomes\n * visible, so a connection that died during sleep is found within `timeout`, not after up\n * to `interval` more.\n */\n timeout?: number;\n}\n\n/** Per-message options for `send()`. */\nexport interface SendOptions {\n /**\n * Drop the message instead of queueing it when the socket isn't open. For data that\n * is stale by the time a reconnect finishes, such as player input or cursor positions.\n * @default false\n */\n volatile?: boolean;\n /**\n * Keep only the newest queued message with this key. Sending again with the same key\n * while disconnected replaces the earlier one, which moves to the back of the queue\n * (or of the `priority` messages, for a priority one).\n * For state where only the latest value matters, such as \"my position is now (x, y)\".\n */\n key?: string;\n /**\n * Queue this message ahead of every message queued without `priority`, after earlier\n * priority messages. For control messages that must not wait behind a backlog, such as\n * cancel, interrupt or a token refresh. Messages sent from `open` listeners still go first.\n *\n * It only matters while the socket isn't open: once a message is handed to the browser,\n * nothing can overtake it. To keep that buffer short, send bulky streams with `maxBuffered`.\n * @default false\n */\n priority?: boolean;\n /**\n * Drop the message instead of sending it while more than this many bytes are already\n * waiting in the browser's send buffer (`socket.bufferedAmount`). For high-rate streams on\n * a slow link, such as audio frames or per-tick input, where a late message is worse than\n * none. `send()` returns `false`, and `onDrop` isn't called.\n *\n * Only checked while the socket is open: a message queued while disconnected is sent on\n * open regardless. `bufferedAmount` includes what you sent earlier in the same task, so set\n * it well above one burst of messages.\n *\n * @example\n * ```ts\n * voice.send(frame, { volatile: true, maxBuffered: 64 * 1024 });\n * ```\n */\n maxBuffered?: number;\n /**\n * Drop the message instead of sending it if it has been queued for longer than this many\n * ms, for messages that go stale during a long outage, such as \"typing\" or a price quote.\n * Overrides `queue.maxAge`. Checked when the socket opens and whenever another message is\n * queued; `onDrop` is called then.\n */\n maxAge?: number;\n /**\n * Called if the message is dropped from the queue without being sent: replaced by a\n * newer message with the same `key`, evicted by `queue.limit`, or older than `maxAge`. Not\n * called when the queue is cleared because the client closed or gave up, or when `send()`\n * drops the message right away (`volatile` while not open, or `maxBuffered`) and returns\n * `false`.\n */\n onDrop?: () => void;\n}\n\n/** Bounds for the send queue. */\nexport interface QueueOptions {\n /**\n * Maximum number of messages held while not connected. When full, the oldest\n * message is dropped, skipping `priority` messages unless nothing else is queued.\n * `Infinity` keeps every message.\n * @default 100\n */\n limit?: number;\n /**\n * Drop a queued message instead of sending it once it has waited longer than this many ms,\n * calling its `onDrop`. A message's own `maxAge` overrides it. Checked when the socket opens\n * and whenever another message is queued.\n * @default Infinity\n */\n maxAge?: number;\n}\n\n/**\n * The minimal [Standard Schema v1](https://standardschema.dev) interface, implemented\n * by zod (3.24+), valibot (1.0+), arktype (2.0+) and others. You don't need to\n * import this; pass your schema object directly.\n */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (\n value: unknown\n ) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;\n readonly types?: { readonly input: Input; readonly output: Output } | undefined;\n };\n}\n\n/** The result of a Standard Schema `validate` call. */\nexport type StandardSchemaResult<Output> =\n | { readonly value: Output; readonly issues?: undefined }\n | { readonly issues: ReadonlyArray<{ readonly message: string }> };\n\n/**\n * Connection options, shared by `createWebSocket(url, options)` and `useWebSocket(url, options)`.\n *\n * @typeParam In - Type of incoming messages (after `parse`/`json`/`schema`).\n * @typeParam Out - Type accepted by `send()`.\n */\nexport interface WebSocketOptions<In = unknown, Out = unknown> {\n /** Sub-protocol(s) passed to the `WebSocket` constructor. */\n protocols?: string | string[];\n /**\n * How binary messages are exposed.\n * @default 'blob'\n */\n binaryType?: BinaryType;\n /**\n * `JSON.parse` incoming and `JSON.stringify` outgoing messages. Messages that fail\n * to parse are reported to `onError` as a {@link MessageError} and not delivered.\n * @default false\n */\n json?: boolean;\n /**\n * Custom decoder for incoming `event.data`. Takes precedence over `json`.\n * Throwing reports a {@link MessageError} and drops the message.\n */\n parse?: (data: unknown) => unknown;\n /** Custom encoder for outgoing messages. Takes precedence over `json`. */\n serialize?: (message: Out) => SendData;\n /**\n * Validate incoming messages (after parsing) with any Standard Schema library.\n * Invalid messages are reported to `onError` as a {@link MessageError} and not delivered.\n * The message type `In` is inferred from it.\n *\n * @example\n * ```ts\n * createWebSocket(url, { json: true, schema: z.object({ price: z.number() }) });\n * ```\n */\n schema?: StandardSchemaV1<unknown, In>;\n /**\n * Reconnect with exponential backoff after an unexpected close. `false` disables it.\n * @default true\n */\n reconnect?: boolean | ReconnectOptions;\n /** Send a ping on an interval and, optionally, reconnect when the server goes quiet. */\n heartbeat?: HeartbeatOptions;\n /**\n * Buffer `send()` calls made while not connected and send them, in order, on open\n * (right after `open` listeners run). It holds up to 100 messages, dropping the oldest;\n * `{ limit }` changes that. `false` drops messages instead of queueing them.\n * The queue is cleared by `close()` or when the client gives up.\n * @default true\n */\n queue?: boolean | QueueOptions;\n /**\n * Receives debug messages about connection attempts, retries and dropped messages.\n * @example\n * ```ts\n * logger: console.debug\n * ```\n */\n logger?: (message: string, ...details: unknown[]) => void;\n /**\n * `WebSocket` implementation to use, e.g. a mock in tests or the `ws` package in Node.\n * When omitted, the global `WebSocket` is used in browsers. During a server render\n * (no `window`), nothing connects unless an implementation is passed.\n */\n WebSocket?: typeof WebSocket;\n}\n\n/**\n * Reported via the `error` event (and `onError`) when an incoming message can't be\n * decoded by `parse`/`json` or fails `schema` validation. The message is not delivered.\n *\n * @example\n * ```ts\n * onError(error) {\n * if (error instanceof MessageError) report(error.data, error.issues);\n * }\n * ```\n */\nexport class MessageError extends Error {\n /** The raw `event.data` that failed. */\n readonly data: unknown;\n /** Schema validation issues, when the message failed `schema`. */\n readonly issues: ReadonlyArray<{ readonly message: string }> | undefined;\n\n constructor(\n message: string,\n data: unknown,\n options?: { cause?: unknown; issues?: ReadonlyArray<{ readonly message: string }> }\n ) {\n super(message, options?.cause === undefined ? undefined : { cause: options.cause });\n this.name = 'MessageError';\n this.data = data;\n this.issues = options?.issues;\n }\n}\n\n/** Events emitted by a client. Subscribe with `client.on(type, listener)` or `useWebSocketEvent`. */\nexport interface EventMap<In> {\n /**\n * The socket opened (initially or after a reconnect). Listeners run before queued\n * messages are sent, so a handshake sent from here goes out first.\n */\n open: (event: Event) => void;\n /** A message arrived and passed `parse` and `schema`. */\n message: (message: In, event: MessageEvent) => void;\n /** A socket closed, whether it will be retried or not. */\n close: (close: CloseInfo) => void;\n /**\n * A socket error `Event`, a {@link MessageError} for an undecodable or invalid message,\n * or the `Error` thrown by the `WebSocket` constructor or a `url` function.\n */\n error: (error: Event | MessageError | Error) => void;\n}\n\n/**\n * A WebSocket connection that reconnects, queues, decodes and can be shared.\n * Create one with `createWebSocket()`.\n *\n * The connection is reference-counted: it opens when the first hook mounts (or\n * `ready` is read) and closes one tick after the last hook unmounts. Calling\n * `connect()` keeps it open regardless, until `close()`.\n *\n * @typeParam In - Type of incoming messages.\n * @typeParam Out - Type accepted by `send()`.\n */\nexport interface WebSocketClient<In = unknown, Out = unknown> {\n /** The URL (or URL function) the client was created with. */\n readonly url: UrlSource;\n /**\n * A promise that resolves once the socket has opened. Reading it starts an idle client.\n *\n * It stays resolved through automatic reconnects, so `use(client.ready)` shows a\n * Suspense fallback only for the first connection, never on a network blip; use\n * `status` to show reconnects. It rejects when the client gives up reconnecting, when\n * `close()` is called, and during a server render. After that, `connect()` or\n * `reconnect()` make it a new pending promise.\n *\n * It is safe to pass to React 19's `use()`: the same promise is returned until its state\n * changes, and it carries `status`/`value`/`reason` so `use()` doesn't suspend when it is\n * already settled.\n */\n readonly ready: Promise<void>;\n /** The current underlying `WebSocket`, if any. An escape hatch; prefer the client's methods. */\n readonly socket: WebSocket | null;\n /** Subscribe to snapshot changes. Compatible with `useSyncExternalStore`. Returns an unsubscribe function. */\n subscribe(listener: () => void): () => void;\n /** The current {@link Snapshot}. Returns the same object until something changes. */\n getSnapshot(): Snapshot<In>;\n /**\n * Listen to an event. Returns an unsubscribe function. Listener errors are re-thrown\n * asynchronously, so one failing listener doesn't break the others.\n */\n on<K extends keyof EventMap<In>>(type: K, listener: EventMap<In>[K]): () => void;\n /**\n * Send a message (encoded with `serialize`/`json` if configured). If the socket isn't\n * open, the message is queued and sent on open (see the `queue` option), unless\n * `options.volatile` is set.\n * @returns `true` if it was sent immediately, `false` if it was queued or dropped\n * (`volatile` while not open, or `maxBuffered` while open).\n *\n * @example\n * ```ts\n * game.send({ type: 'input', dx, dy }, { volatile: true }); // never replayed after a reconnect\n * game.send({ type: 'position', x, y }, { key: 'position' }); // only the newest is queued\n * game.send({ type: 'leave' }, { priority: true }); // ahead of everything already queued\n * ```\n */\n send(message: Out, options?: SendOptions): boolean;\n /**\n * Start connecting, and keep the connection open even when no hook is using the\n * client, until `close()` is called. Does nothing if already connecting or open.\n */\n connect(): void;\n /**\n * Close the connection and stop reconnecting. The status becomes `closed` and the\n * send queue is cleared. Mounted hooks stay mounted; call `connect()` or `reconnect()`\n * to start again. Closed while no hook holds it, the next hook to mount starts it again.\n *\n * Like `WebSocket.close()`, it throws for a code or reason the socket rejects (browsers take\n * `1000` or `3000`–`4999`, and a reason up to 123 bytes), and then nothing has changed.\n * @param code - Close code. @default 1000\n */\n close(code?: number, reason?: string): void;\n /**\n * Drop the current socket (if any) and connect again immediately, resetting the\n * backoff. Also restarts a `closed` client.\n */\n reconnect(): void;\n /**\n * Move to a new connection without a gap, for example when the server asks its clients to\n * before a deploy. At a random moment within `options.within` ms, it opens a new connection\n * (to `options.url`, if given) and, once that one is open, closes the current one with code\n * `1000`. Until then the current connection keeps working, so the status stays `open` and\n * `send()` doesn't queue.\n *\n * `open` listeners run for the new connection, as after any reconnect, so handshakes and\n * topic subscriptions are sent again. Requests the old connection hadn't answered fail as on\n * a lost connection. If the new connection fails before opening, the current one stays. If\n * the client isn't open when the moment comes, only the URL changes, for the next attempt.\n * A later call replaces one that hasn't moved yet.\n *\n * @example\n * ```ts\n * // The server sends { type: 'reconnect', within: 30000 } before it shuts down.\n * live.on('message', message => {\n * if (message.type === 'reconnect') live.migrate({ within: message.within, url: message.url });\n * });\n * ```\n */\n migrate(options?: MigrateOptions): void;\n /**\n * Hold the connection open until the returned function is called. This is what the\n * hooks use; call it yourself to integrate with non-React code.\n * @returns A release function (safe to call more than once).\n */\n acquire(): () => void;\n}\n","import {\n MessageError,\n type CloseInfo,\n type EventMap,\n type ReconnectOptions,\n type SendData,\n type MigrateOptions,\n type SendOptions,\n type Snapshot,\n type StandardSchemaResult,\n type UrlSource,\n type WebSocketClient,\n type WebSocketOptions\n} from './types';\n\nconst OPEN = 1;\n\n// Defined in workers only, and missing from the DOM typings.\ndeclare const WorkerGlobalScope: unknown;\n\nconst RECONNECT_DEFAULTS = {\n maxAttempts: Infinity,\n minDelay: 1000,\n maxDelay: 30_000,\n factor: 2,\n jitter: 1,\n minUptime: 5000\n} satisfies ReconnectOptions;\n\nconst QUEUE_LIMIT = 100;\n\n// The longest delay `setTimeout` takes; anything longer fires at once.\nconst MAX_TIMEOUT = 2 ** 31 - 1;\n\ntype Timer = ReturnType<typeof setTimeout> | undefined;\n\n/**\n * The default `reconnect.shouldReconnect`: retry every close except `1002` (protocol error),\n * `1003` (unsupported data) and `1008` (policy violation, often a rejected login), which\n * retrying the same way can't fix. Call it from your own `shouldReconnect` to keep that rule.\n *\n * @example\n * ```ts\n * createWebSocket(url, {\n * // Also stop on our server's \"invalid token\" code.\n * reconnect: { shouldReconnect: close => close.code !== 4001 && retryableClose(close) }\n * });\n * ```\n */\nexport function retryableClose(close: CloseInfo): boolean {\n return close.code !== 1002 && close.code !== 1003 && close.code !== 1008;\n}\n\n/**\n * The default `reconnect.retryAfter`: for a `1012` (Service Restart) or `1013` (Try Again Later)\n * close whose reason is a number of seconds, like an HTTP `Retry-After` header, that delay in ms.\n * Call it from your own `retryAfter` to keep reading it.\n *\n * @example\n * ```ts\n * createWebSocket(url, {\n * // Our server also closes with 4429 and the delay in ms as the reason.\n * reconnect: { retryAfter: close => (close.code === 4429 ? Number(close.reason) : retryAfter(close)) }\n * });\n * ```\n */\nexport function retryAfter(close: CloseInfo): number | undefined {\n return (close.code === 1012 || close.code === 1013) && /^\\s*\\d+(\\.\\d+)?\\s*$/.test(close.reason)\n ? Number(close.reason) * 1000\n : undefined;\n}\n\n// How long a connection opened only by reading `ready` waits for a holder before closing.\n// Long enough for React to retry a suspended render and commit its hooks.\nconst READY_GRACE = 5000;\n\n// React's `use()` reads `status`/`value`/`reason` off a thenable to skip suspending\n// when it is already settled.\ntype TrackedPromise = Promise<void> & {\n status: 'pending' | 'fulfilled' | 'rejected';\n value?: void;\n reason?: unknown;\n};\n\ninterface Deferred {\n readonly promise: TrackedPromise;\n settled: boolean;\n resolve(): void;\n reject(reason: unknown): void;\n}\n\nfunction createDeferred(): Deferred {\n let resolve!: () => void;\n let reject!: (reason: unknown) => void;\n const promise = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n }) as TrackedPromise;\n promise.status = 'pending';\n // Nobody may be awaiting `ready`; don't surface that as an unhandled rejection.\n promise.catch(() => {});\n const deferred: Deferred = {\n promise,\n settled: false,\n resolve() {\n if (deferred.settled) return;\n deferred.settled = true;\n promise.status = 'fulfilled';\n promise.value = undefined;\n resolve();\n },\n reject(reason) {\n if (deferred.settled) return;\n deferred.settled = true;\n promise.status = 'rejected';\n promise.reason = reason;\n reject(reason);\n }\n };\n return deferred;\n}\n\nfunction rethrowAsync(error: unknown) {\n queueMicrotask(() => {\n throw error;\n });\n}\n\nfunction isThenable<T>(value: unknown): value is PromiseLike<T> {\n return typeof (value as PromiseLike<T> | null)?.then === 'function';\n}\n\n/**\n * @internal Option key for a callback run when the client tears down, which lets the hooks'\n * URL registry forget clients nobody holds anymore. A registered symbol rather than an export,\n * because the hooks entry imports the client from the `core` entry at runtime.\n */\nconst ON_TEARDOWN = Symbol.for('react-websocket.onTeardown');\n\n/** @internal Method key on a socket that sends `volatile` messages as datagrams (WebTransport). */\nconst DATAGRAM = Symbol.for('react-websocket.datagram');\n\n/** @internal Method key for what `react-websocket/devtools` shows that the snapshot doesn't. */\nconst INSPECT = Symbol.for('react-websocket.inspect');\n\nfunction createClient<In = unknown, Out = unknown>(\n url: UrlSource,\n options: WebSocketOptions<In, Out> & { [ON_TEARDOWN]?: () => void } = {}\n): WebSocketClient<In, Out> {\n const onTeardown = options[ON_TEARDOWN];\n const log = options.logger ?? (() => {});\n const retry =\n options.reconnect === false\n ? null\n : {\n ...RECONNECT_DEFAULTS,\n ...(typeof options.reconnect === 'object' ? options.reconnect : {})\n };\n const queue = typeof options.queue === 'object' ? options.queue : {};\n const queueLimit = options.queue === false ? 0 : Math.max(0, queue.limit ?? QUEUE_LIMIT);\n const queueMaxAge = queue.maxAge ?? Infinity;\n\n let snapshot: Snapshot<In> = { status: 'idle', lastMessage: undefined, attempt: 0 };\n const storeListeners = new Set<() => void>();\n const listeners: { [K in keyof EventMap<In>]: Set<EventMap<In>[K]> } = {\n open: new Set(),\n message: new Set(),\n close: new Set(),\n error: new Set()\n };\n\n let ws: WebSocket | null = null;\n // The new connection of a `migrate()`, until it opens and replaces `ws`.\n let incoming: WebSocket | null = null;\n // The connection a `migrate()` replaced: messages it received still go out once validated.\n let replaced: WebSocket | null = null;\n // Where `migrate()` moved the client, used instead of `url` until it stops.\n let moved: UrlSource | undefined;\n let wanted = false;\n let refs = 0;\n // Set by a public `connect()`: stay connected after the last hook releases, until `close()`.\n let pinned = false;\n // Stopped (`close()` or giving up) while nothing held it: the next holder starts it again,\n // as it would after the last hook unmounted.\n let unheldStop = false;\n // Priority messages form the head of the queue, in the order they were sent.\n let pending: {\n data: SendData;\n key: string | undefined;\n priority: boolean | undefined;\n onDrop: (() => void) | undefined;\n // `Date.now()`, not `performance.now()`, which can stand still while the computer sleeps.\n expires: number;\n }[] = [];\n let ready: Deferred | null = null;\n // Opened at least once since the client was last started. `ready` stays resolved through\n // automatic reconnects so `use(client.ready)` doesn't swap content for a fallback on every blip.\n let opened = false;\n let unavailable: Deferred | null = null;\n let startScheduled = false;\n let inbox: Promise<void> | null = null;\n // Bumped per connection attempt so a slow `url()` can't resurrect a cancelled one.\n let generation = 0;\n let windowBound = false;\n // Consecutive failed attempts, for the backoff. Unlike `snapshot.attempt`, which reads 0 while\n // open, it resets only once a connection stays up for `minUptime`, so a server that accepts\n // and then closes right away (e.g. rejecting the auth sent on open) still gets backed off.\n let failures = 0;\n // The pending retry waits because the server asked it to (1013 or `retryAfter`), so coming\n // back online or to the tab doesn't cut it short.\n let serverWait = false;\n let retryTimer: Timer;\n let migrateTimer: Timer;\n let teardownTimer: Timer;\n let stableTimer: Timer;\n let heartbeatTimer: ReturnType<typeof setInterval> | undefined;\n let pongTimer: Timer;\n\n function set(patch: Partial<Snapshot<In>>) {\n snapshot = { ...snapshot, ...patch };\n for (const listener of [...storeListeners]) {\n try {\n listener();\n } catch (error) {\n rethrowAsync(error);\n }\n }\n }\n\n function emit<K extends keyof EventMap<In>>(type: K, ...args: Parameters<EventMap<In>[K]>) {\n for (const listener of [...listeners[type]]) {\n try {\n (listener as (...a: Parameters<EventMap<In>[K]>) => void)(...args);\n } catch (error) {\n rethrowAsync(error);\n }\n }\n }\n\n function getImpl(): typeof WebSocket | undefined {\n if (options.WebSocket) return options.WebSocket;\n // Node has a global WebSocket too; don't connect from a server render. Workers have no\n // `window`, but are browsers.\n return typeof window === 'undefined' && typeof WorkerGlobalScope === 'undefined'\n ? undefined\n : globalThis.WebSocket;\n }\n\n function rejectReady(reason: unknown) {\n if (!ready || ready.settled) ready = createDeferred();\n ready.reject(reason);\n }\n\n /** The network or the tab came back: retry now, or check that an open connection still works. */\n function wake() {\n if (retryTimer !== undefined) {\n if (serverWait) return;\n log('network or visibility change, retrying now');\n open();\n } else if (heartbeatTimer !== undefined && pongTimer === undefined) {\n // After sleep or a network change, a socket can be dead while still reading OPEN.\n log('network or visibility change, checking the connection');\n ping(ws!);\n }\n }\n\n function onVisibilityChange() {\n if (document.visibilityState === 'visible') wake();\n }\n\n // `self` in a worker, which has `online` too but no document.\n function bindWindow() {\n if (windowBound || typeof addEventListener !== 'function') return;\n windowBound = true;\n addEventListener('online', wake);\n if (typeof document !== 'undefined') {\n document.addEventListener('visibilitychange', onVisibilityChange);\n }\n }\n\n function unbindWindow() {\n if (!windowBound) return;\n windowBound = false;\n removeEventListener('online', wake);\n if (typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', onVisibilityChange);\n }\n }\n\n /** Send a heartbeat ping and, with a `timeout`, close the socket if nothing comes back. */\n function ping(socket: WebSocket) {\n const heartbeat = options.heartbeat!;\n socket.send(heartbeat.message ?? 'ping');\n if (heartbeat.timeout && pongTimer === undefined) {\n pongTimer = setTimeout(() => {\n log('heartbeat timeout');\n // The close handshake may never finish on a dead link, so don't wait for it.\n detach(socket, false);\n socket.close(4000, 'heartbeat timeout');\n handleClose({ code: 4000, reason: 'heartbeat timeout', wasClean: false });\n }, heartbeat.timeout);\n }\n }\n\n function startHeartbeat(socket: WebSocket) {\n if (options.heartbeat) heartbeatTimer = setInterval(ping, options.heartbeat.interval, socket);\n }\n\n /** Stop the timers that belong to the current open socket. */\n function stopSocketTimers() {\n clearInterval(heartbeatTimer);\n clearTimeout(pongTimer);\n clearTimeout(stableTimer);\n heartbeatTimer = pongTimer = stableTimer = undefined;\n }\n\n /** Stop tracking `socket`. Its eventual close event is still reported unless `reportClose` is false. */\n function detach(socket: WebSocket, reportClose: boolean) {\n if (ws === socket) ws = null;\n socket.onopen = socket.onmessage = socket.onerror = null;\n socket.onclose = reportClose\n ? event => emit('close', { code: event.code, reason: event.reason, wasClean: event.wasClean })\n : null;\n }\n\n /** The delay before attempt `n`; `slow` starts at `maxDelay`, for a server shedding load. */\n function backoff(attempt: number, slow: boolean) {\n const { minDelay, maxDelay, factor, jitter } = retry!;\n const base = slow ? maxDelay : Math.min(maxDelay, minDelay * factor ** (attempt - 1));\n return Math.round(base * (1 - jitter * Math.random()));\n }\n\n /** Stop a `migrate()` that hasn't switched yet. */\n function dropIncoming() {\n if (incoming) {\n detach(incoming, false);\n incoming.close();\n incoming = null;\n }\n }\n\n /**\n * Start a connection attempt. A `handoff` (from `migrate()`) opens beside the current\n * connection, which keeps working until the new one opens.\n */\n function open(handoff?: boolean) {\n if (!handoff) {\n clearTimeout(retryTimer);\n retryTimer = undefined;\n serverWait = false;\n dropIncoming();\n }\n const Impl = getImpl();\n if (!Impl) return;\n const id = ++generation;\n if (!handoff) set({ status: snapshot.attempt === 0 ? 'connecting' : 'reconnecting' });\n\n const source = moved ?? url;\n let target: string | Promise<string>;\n try {\n target = typeof source === 'string' ? source : source();\n } catch (error) {\n urlFailed(error, handoff);\n return;\n }\n if (typeof target === 'string') {\n start(Impl, target, handoff);\n return;\n }\n Promise.resolve(target).then(\n resolved => {\n if (id === generation && wanted) start(Impl, resolved, handoff);\n },\n error => {\n if (id === generation && wanted) urlFailed(error, handoff);\n }\n );\n }\n\n /**\n * `url()` threw or rejected (e.g. a token refresh failed): count it as a failed attempt,\n * unless it was for a handoff, which leaves the current connection in place.\n */\n function urlFailed(error: unknown, handoff?: boolean) {\n const reason = error instanceof Error ? error : new Error(String(error));\n log('url() failed', reason);\n emit('error', reason);\n if (!handoff) retryOrFail({ code: 1006, reason: 'url() failed', wasClean: false }, reason);\n }\n\n function start(Impl: typeof WebSocket, target: string, handoff?: boolean) {\n let socket: WebSocket;\n try {\n socket = new Impl(target, options.protocols);\n } catch (error) {\n // e.g. an invalid URL; retrying won't help.\n emit('error', error as Error);\n if (!handoff) fail(error);\n return;\n }\n if (options.binaryType) socket.binaryType = options.binaryType;\n if (handoff) incoming = socket;\n else ws = socket;\n log('connecting', target);\n\n socket.onopen = event => {\n if (handoff) {\n // The switch: the new connection takes over, then the old one closes. If the old one\n // dropped meanwhile, this one replaces the pending retry.\n incoming = null;\n clearTimeout(retryTimer);\n retryTimer = undefined;\n stopSocketTimers();\n if (ws) {\n const old = (replaced = ws);\n detach(old, true);\n old.close(1000, 'migrated');\n }\n ws = socket;\n }\n log('open');\n opened = true;\n set({ status: 'open', attempt: 0 });\n if (retry) stableTimer = setTimeout(() => (failures = 0), retry.minUptime);\n startHeartbeat(socket);\n ready?.resolve();\n // Nobody holds it (it was started by reading `ready`, or the last holder left while\n // connecting): close it unless a holder turns up soon.\n if (refs === 0 && !pinned) {\n clearTimeout(teardownTimer);\n teardownTimer = setTimeout(teardown, READY_GRACE);\n }\n // `open` listeners go first, so a handshake sent from `onOpen` (e.g. auth) precedes\n // whatever was queued while disconnected.\n emit('open', event);\n if (ws !== socket) return; // a listener closed or replaced it\n const queued = pending;\n pending = [];\n const now = Date.now();\n for (const entry of queued) if (entry.expires >= now) socket.send(entry.data);\n // After the rest is sent, so an `onDrop` that sends again doesn't jump ahead of it.\n for (const entry of queued) if (entry.expires < now) expired(entry);\n };\n socket.onmessage = event => receive(event, socket);\n socket.onerror = event => {\n if (ws === socket) emit('error', event);\n };\n socket.onclose = event => {\n if (ws !== socket) {\n // A handoff that never opened: the current connection stays.\n incoming = null;\n log('migrate() failed', event.code);\n return;\n }\n detach(socket, false);\n handleClose({ code: event.code, reason: event.reason, wasClean: event.wasClean });\n };\n }\n\n function handleClose(info: CloseInfo) {\n replaced = null;\n stopSocketTimers();\n log('closed', info);\n retryOrFail(info);\n emit('close', info);\n }\n\n function retryOrFail(info: CloseInfo, error?: Error) {\n if (\n wanted &&\n retry &&\n failures < retry.maxAttempts &&\n (retry.shouldReconnect ?? retryableClose)(info)\n ) {\n const attempt = ++failures;\n const asked = (retry.retryAfter ?? retryAfter)(info);\n const named = asked! >= 0; // not for undefined, NaN or a negative delay\n // A server that names a delay gets it, plus the usual backoff so clients told the same\n // delay don't all return at once. An overloaded one (1013) gets the longest backoff.\n const delay = Math.min(\n MAX_TIMEOUT,\n named ? asked! + backoff(attempt, false) : backoff(attempt, info.code === 1013)\n );\n serverWait = named || info.code === 1013;\n set({ status: 'reconnecting', attempt });\n log(`reconnecting in ${delay}ms (attempt ${attempt})`);\n retryTimer = setTimeout(open, delay);\n } else {\n fail(\n error ??\n new Error(`WebSocket closed (code ${info.code}${info.reason ? `: ${info.reason}` : ''})`)\n );\n }\n }\n\n /** Forget `migrate()`: its timer, its connection and the URL it moved to. */\n function stopMigrating() {\n clearTimeout(migrateTimer);\n migrateTimer = undefined;\n dropIncoming();\n moved = undefined;\n }\n\n function migrate({ url: to, within }: MigrateOptions = {}) {\n clearTimeout(migrateTimer);\n dropIncoming();\n migrateTimer = setTimeout(\n () => {\n migrateTimer = undefined;\n if (!wanted) return;\n if (to !== undefined) moved = to;\n // Not connected: the next attempt goes to the new URL.\n if (snapshot.status === 'open') open(true);\n },\n // Each client picks its own moment, so a server's request doesn't move everyone at once.\n Math.min(MAX_TIMEOUT, Math.random() * (within ?? 0))\n );\n }\n\n function fail(reason: unknown) {\n wanted = false;\n opened = false;\n unheldStop = refs === 0;\n unbindWindow();\n stopMigrating();\n pending = [];\n // `failures`, not 0: a connection that kept closing right after open was still failing.\n set({ status: 'closed', attempt: failures });\n rejectReady(reason);\n }\n\n function receive(event: MessageEvent, socket: WebSocket) {\n clearTimeout(pongTimer);\n pongTimer = undefined;\n if (options.heartbeat?.pong !== undefined && event.data === options.heartbeat.pong) return;\n\n // Async schema validation must not reorder messages.\n const run = () => decode(event, socket);\n if (inbox) {\n // One failed delivery must not stop the chain, or every later message is dropped.\n const next: Promise<void> = inbox\n .then(run)\n .catch(rethrowAsync)\n .then(() => {\n if (inbox === next) inbox = null;\n });\n inbox = next;\n return;\n }\n const result = run();\n if (result) {\n const next: Promise<void> = result.catch(rethrowAsync).then(() => {\n if (inbox === next) inbox = null;\n });\n inbox = next;\n }\n }\n\n function decode(event: MessageEvent, socket: WebSocket): void | Promise<void> {\n let value: unknown;\n try {\n value = options.parse\n ? options.parse(event.data)\n : options.json\n ? JSON.parse(event.data as string)\n : event.data;\n } catch (cause) {\n emit('error', new MessageError('Failed to parse message', event.data, { cause }));\n return;\n }\n\n const schema = options.schema;\n if (!schema) return deliver(value as In, event, socket);\n\n let result: ReturnType<(typeof schema)['~standard']['validate']>;\n try {\n result = schema['~standard'].validate(value);\n } catch (cause) {\n emit('error', new MessageError('Schema validation threw', event.data, { cause }));\n return;\n }\n if (isThenable<StandardSchemaResult<In>>(result)) {\n return Promise.resolve(result).then(\n r => validated(r, event, socket),\n cause => emit('error', new MessageError('Schema validation threw', event.data, { cause }))\n );\n }\n validated(result, event, socket);\n }\n\n function validated(result: StandardSchemaResult<In>, event: MessageEvent, socket: WebSocket) {\n if (result.issues) {\n emit(\n 'error',\n new MessageError('Message failed schema validation', event.data, { issues: result.issues })\n );\n } else {\n deliver(result.value, event, socket);\n }\n }\n\n function deliver(message: In, event: MessageEvent, socket: WebSocket) {\n // Closed while validating. One that `migrate()` replaced received it before the switch.\n if (ws !== socket && replaced !== socket) return;\n set({ lastMessage: message });\n emit('message', message, event);\n }\n\n function send(message: Out, sendOptions: SendOptions = {}): boolean {\n const data = options.serialize\n ? options.serialize(message)\n : options.json\n ? JSON.stringify(message)\n : (message as SendData);\n const { key, priority, maxBuffered } = sendOptions;\n if (ws && ws.readyState === OPEN) {\n // A late frame is worse than none: let a congested buffer drain instead of growing it.\n if (maxBuffered !== undefined && ws.bufferedAmount > maxBuffered) {\n log('dropped message: send buffer above maxBuffered');\n return false;\n }\n const datagram = sendOptions.volatile\n ? (ws as unknown as Record<symbol, ((data: SendData) => void) | undefined>)[DATAGRAM]\n : undefined;\n if (datagram) datagram.call(ws, data);\n else ws.send(data);\n return true;\n }\n if (queueLimit === 0 || sendOptions.volatile) {\n log('dropped message: socket is not open');\n return false;\n }\n if (!getImpl()) {\n // e.g. during a server render: this client can never connect, so don't hold on to it.\n log('dropped message: no WebSocket implementation available');\n return false;\n }\n const now = Date.now();\n // Drop what's too old to send, and an older message with the same key. Their `onDrop`s run\n // once the queue is updated, so one that sends again doesn't lose its message.\n const before = pending;\n pending = before.filter(\n entry => entry.expires >= now && (key === undefined || entry.key !== key)\n );\n for (const entry of before) {\n if (!pending.includes(entry)) (entry.expires < now ? expired : dropped)(entry);\n }\n let head = pending.findIndex(entry => !entry.priority);\n if (head < 0) head = pending.length;\n const entry = {\n data,\n key,\n priority,\n onDrop: sendOptions.onDrop,\n expires: now + (sendOptions.maxAge ?? queueMaxAge)\n };\n if (priority) pending.splice(head++, 0, entry);\n else pending.push(entry);\n if (pending.length > queueLimit) {\n // The oldest ordinary message goes first; priority ones only when nothing else is left.\n dropped(pending.splice(head < pending.length ? head : 0, 1)[0]!);\n log(`dropped oldest queued message: queue limit (${queueLimit}) reached`);\n }\n return false;\n }\n\n function expired(entry: (typeof pending)[number]) {\n log('dropped queued message: older than maxAge');\n dropped(entry);\n }\n\n function dropped(entry: (typeof pending)[number]) {\n try {\n entry.onDrop?.();\n } catch (error) {\n rethrowAsync(error);\n }\n }\n\n function begin() {\n wanted = true;\n unheldStop = false;\n // Starting over after `close()` or giving up: wait for a new connection.\n if (snapshot.status === 'idle' || snapshot.status === 'closed') {\n opened = false;\n if (ready?.settled) ready = null;\n }\n failures = 0;\n snapshot = { ...snapshot, attempt: 0 }; // open() notifies\n bindWindow();\n open();\n }\n\n /** Start connecting unless already wanted. Used by holders (`acquire`, `ready`). */\n function ensureConnecting() {\n if (wanted || !getImpl()) return;\n begin();\n }\n\n function connect() {\n pinned = true;\n ensureConnecting();\n }\n\n function stop(code?: number, reason?: string) {\n const socket = ws;\n // First, so that a code or reason the socket rejects throws before anything has changed.\n socket?.close(code, reason);\n wanted = false;\n generation++;\n clearTimeout(retryTimer);\n retryTimer = undefined;\n failures = 0;\n stopSocketTimers();\n unbindWindow();\n stopMigrating();\n pending = [];\n replaced = null;\n if (socket) detach(socket, true);\n }\n\n function close(code = 1000, reason?: string) {\n stop(code, reason);\n pinned = false;\n opened = false;\n unheldStop = refs === 0;\n set({ status: 'closed', attempt: 0 });\n rejectReady(new Error('WebSocket was closed'));\n }\n\n function reconnect() {\n if (!getImpl()) return;\n clearTimeout(retryTimer);\n retryTimer = undefined;\n stopSocketTimers();\n if (ws) {\n const socket = ws;\n detach(socket, true);\n socket.close(1000, 'reconnect');\n }\n begin();\n }\n\n function teardown() {\n if (refs > 0 || pinned) return;\n // Someone may be suspended on `ready`; let the attempt finish.\n if (ready && !ready.settled) return;\n stop();\n ready = null;\n opened = false;\n set({ status: 'idle', attempt: 0 });\n onTeardown?.();\n }\n\n function acquire() {\n refs++;\n clearTimeout(teardownTimer);\n if (snapshot.status === 'idle' || (snapshot.status === 'closed' && unheldStop)) {\n ensureConnecting();\n }\n let released = false;\n return () => {\n if (released) return;\n released = true;\n if (--refs > 0) return;\n // Deferred so StrictMode's unmount/remount and route transitions don't churn the socket.\n clearTimeout(teardownTimer);\n teardownTimer = setTimeout(teardown, 0);\n };\n }\n\n return {\n url,\n get ready() {\n if (!getImpl()) {\n if (!unavailable) {\n unavailable = createDeferred();\n unavailable.reject(\n new Error('react-websocket: no WebSocket available here (server render?)')\n );\n }\n return unavailable.promise;\n }\n if (snapshot.status === 'idle' && !startScheduled) {\n // `ready` is read during render; start outside of it.\n startScheduled = true;\n queueMicrotask(() => {\n startScheduled = false;\n if (snapshot.status === 'idle') ensureConnecting();\n });\n }\n if (!ready) {\n ready = createDeferred();\n if (opened) ready.resolve();\n }\n return ready.promise;\n },\n get socket() {\n return ws;\n },\n subscribe(listener) {\n storeListeners.add(listener);\n return () => {\n storeListeners.delete(listener);\n };\n },\n getSnapshot: () => snapshot,\n on(type, listener) {\n // Wrap so the same function can be subscribed twice and unsubscribed independently.\n const entry = ((...args: unknown[]) =>\n (listener as (...a: unknown[]) => void)(...args)) as typeof listener;\n listeners[type].add(entry);\n return () => {\n listeners[type].delete(entry);\n };\n },\n send,\n connect,\n close,\n reconnect,\n migrate,\n acquire,\n [INSPECT]: () => ({ queued: pending.length })\n } as WebSocketClient<In, Out>;\n}\n\n/**\n * Create a WebSocket client, usually at module level, to share across components.\n *\n * Nothing connects until a hook uses it, `ready` is read, or `connect()` is called.\n * It closes one tick after the last hook unmounts, unless `connect()` was called.\n *\n * @param url - A URL, or a function (sync or async) called before every connection\n * attempt, e.g. to put a fresh auth token in the URL on each reconnect.\n * @typeParam In - Type of incoming messages. Inferred from `options.schema` if given.\n * @typeParam Out - Type accepted by `send()`.\n *\n * @example\n * ```ts\n * type ChatIn = { user: string; text: string };\n * type ChatOut = { text: string };\n *\n * export const chat = createWebSocket<ChatIn, ChatOut>('wss://api.example.com/chat', { json: true });\n * ```\n */\nexport function createWebSocket<In = unknown, Out = unknown>(\n url: UrlSource,\n options?: WebSocketOptions<In, Out>\n): WebSocketClient<In, Out> {\n return createClient(url, options);\n}\n","import type { WebSocketClient } from './types';\n\ntype Timer = ReturnType<typeof setTimeout>;\n\n/** @internal Option key for a callback run when nothing holds a topic anymore. Used by feeds. */\nexport const ON_RELEASE = Symbol('react-websocket.onRelease');\n\n/** Options for {@link createWebSocketTopics}: the messages your server expects. */\nexport interface WebSocketTopicsOptions<Out> {\n /** The message that subscribes to `topic`. */\n subscribe: (topic: string) => Out;\n /** The message that unsubscribes from `topic`. Omit it if your server has none. */\n unsubscribe?: (topic: string) => Out;\n}\n\n/** Reference-counted topic subscriptions on one client. Create with {@link createWebSocketTopics}. */\nexport interface WebSocketTopics {\n /** The client the subscription messages are sent on. */\n readonly client: WebSocketClient<any, any>;\n /**\n * Hold a subscription to `topic`, and the client's connection, until the returned\n * function is called. The release function is safe to call more than once.\n */\n acquire(topic: string): () => void;\n /** The topics currently held, in the order they were first acquired. */\n active(): string[];\n /** Stop managing subscriptions. Only needed for managers created dynamically. */\n dispose(): void;\n}\n\n/**\n * Manage topic subscriptions that survive reconnects.\n *\n * - The first holder of a topic sends `subscribe(topic)`. Later holders share it.\n * - A tick after the last holder releases it, `unsubscribe(topic)` is sent, so StrictMode\n * and route transitions don't churn subscriptions.\n * - After every reconnect, every held topic is subscribed again on the new socket, before\n * messages queued while disconnected are sent.\n *\n * Subscriptions are only sent while the socket is open; nothing is queued for them.\n *\n * @example\n * ```ts\n * export const topics = createWebSocketTopics(live, {\n * subscribe: topic => ({ type: 'subscribe', topic }),\n * unsubscribe: topic => ({ type: 'unsubscribe', topic })\n * });\n *\n * // In a component:\n * useWebSocketTopic(topics, `prices:${symbol}`);\n * ```\n */\n// `const` + `NoInfer`: take `Out` from the client only, and keep literals such as\n// `type: 'subscribe'` from widening to `string` inside the callbacks.\nexport function createWebSocketTopics<const Out>(\n client: WebSocketClient<any, Out>,\n options: WebSocketTopicsOptions<NoInfer<Out>> & { [ON_RELEASE]?: (topic: string) => void }\n): WebSocketTopics {\n const counts = new Map<string, number>();\n const pendingRemoval = new Map<string, Timer>();\n // Topics subscribed on `sentOn`. A new socket starts with none, whatever the old one had.\n let sentOn: WebSocket | null = null;\n let sent = new Set<string>();\n\n // The socket, not the status: one the server is closing still reads `open` until its close\n // event, and what's sent on it would be queued and sent after the new subscriptions.\n function isOpen() {\n return client.socket?.readyState === 1;\n }\n\n function subscribe(topic: string) {\n if (!isOpen()) return; // sent on `open` instead\n if (client.socket !== sentOn) {\n sentOn = client.socket;\n sent = new Set();\n }\n if (sent.has(topic)) return;\n sent.add(topic);\n client.send(options.subscribe(topic));\n }\n\n function unsubscribe(topic: string) {\n if (!isOpen() || client.socket !== sentOn || !sent.delete(topic)) return;\n if (options.unsubscribe) client.send(options.unsubscribe(topic));\n }\n\n // `open` listeners run before the client flushes its queue.\n const off = client.on('open', () => {\n sentOn = client.socket;\n sent = new Set();\n for (const topic of counts.keys()) subscribe(topic);\n });\n\n return {\n client,\n acquire(topic) {\n const releaseConnection = client.acquire();\n clearTimeout(pendingRemoval.get(topic));\n pendingRemoval.delete(topic);\n counts.set(topic, (counts.get(topic) ?? 0) + 1);\n subscribe(topic);\n\n let released = false;\n return () => {\n if (released) return;\n released = true;\n const remaining = (counts.get(topic) ?? 1) - 1;\n if (remaining > 0) {\n counts.set(topic, remaining);\n } else {\n counts.delete(topic);\n pendingRemoval.set(\n topic,\n setTimeout(() => {\n pendingRemoval.delete(topic);\n if (!counts.has(topic)) {\n unsubscribe(topic);\n options[ON_RELEASE]?.(topic);\n }\n }, 0)\n );\n }\n releaseConnection();\n };\n },\n active: () => [...counts.keys()],\n dispose() {\n off();\n for (const timer of pendingRemoval.values()) clearTimeout(timer);\n pendingRemoval.clear();\n }\n };\n}\n","import { createWebSocketTopics, ON_RELEASE, type WebSocketTopics } from './topics';\nimport type { WebSocketClient } from './types';\n\n/** Where a numbered message belongs: its topic, and its sequence number in that topic. */\nexport interface FeedPosition {\n readonly topic: string;\n /** One more than the message before it on the same topic. */\n readonly seq: number;\n}\n\n/** Options for {@link createWebSocketFeeds}: the messages your server expects and sends. */\nexport interface WebSocketFeedsOptions<In, Out> {\n /**\n * The message that subscribes to `topic`. `after` is the sequence number of the last message\n * received on it, so the server can send the ones after it, or `undefined` the first time.\n * Sent on every new connection too.\n */\n subscribe: (topic: string, after: number | undefined) => Out;\n /** The message that unsubscribes from `topic`. Omit it if your server has none. */\n unsubscribe?: (topic: string) => Out;\n /**\n * The message that asks for what came after `after` when messages went missing on an open\n * connection. Defaults to `subscribe(topic, after)`.\n */\n resume?: (topic: string, after: number) => Out;\n /** Recognize a numbered message: its topic and sequence number. `undefined` for any other. */\n sequence: (message: In) => FeedPosition | null | undefined;\n /**\n * Recognize a snapshot: a topic's whole state as of `seq`, which the server sends when it\n * can't replay the messages after `after`. It's always delivered, and the numbering goes on\n * from it.\n */\n snapshot?: (message: In) => FeedPosition | null | undefined;\n /** Called when a message arrives after a gap, before the missing ones are asked for. */\n onGap?: (topic: string, after: number, received: number) => void;\n}\n\n/**\n * Topic subscriptions whose messages are numbered. A {@link WebSocketTopics}, so\n * `useWebSocketTopic` takes it, and a message source for `createWebSocketState`.\n * Create with {@link createWebSocketFeeds}.\n */\nexport interface WebSocketFeeds<In> extends WebSocketTopics {\n /**\n * Listen to messages in order: numbered ones once each and without gaps, others as they\n * arrive. Returns an unsubscribe function.\n */\n on(type: 'message', listener: (message: In, event: MessageEvent) => void): () => void;\n /** The sequence number of the last message delivered on `topic`, while it's held. */\n position(topic: string): number | undefined;\n}\n\n/**\n * Subscribe to topics whose messages carry a sequence number, and get each message once, in\n * order, across reconnects:\n *\n * - On every new connection, each held topic is subscribed again with `after`, the last\n * number received, so the server sends what was missed instead of everything.\n * - A message numbered at or below the last one is a duplicate and is dropped.\n * - A message that skips numbers is held back, and the topic asks for what's missing (`resume`)\n * once, so nothing is delivered out of order.\n * - A server that can't replay sends a `snapshot`, and the numbering goes on from it.\n *\n * Listen with `feeds.on('message', …)`, or pass `feeds` to `createWebSocketState` in place of\n * the client. Hold topics with `useWebSocketTopic(feeds, topic)` or `feeds.acquire(topic)`.\n *\n * @example\n * ```ts\n * export const feeds = createWebSocketFeeds(live, {\n * subscribe: (topic, after) => ({ type: 'subscribe', topic, after }),\n * unsubscribe: topic => ({ type: 'unsubscribe', topic }),\n * sequence: message => (message.type === 'event' ? message : undefined),\n * snapshot: message => (message.type === 'snapshot' ? message : undefined)\n * });\n *\n * export const chat = createWebSocketState(feeds, { initialState: [], reduce: addMessage });\n * ```\n */\n// `NoInfer`: take `In` and `Out` from the client only.\nexport function createWebSocketFeeds<In, const Out>(\n client: WebSocketClient<In, Out>,\n options: WebSocketFeedsOptions<NoInfer<In>, NoInfer<Out>>\n): WebSocketFeeds<In> {\n // The last number delivered per topic, from its first message until nothing holds it.\n const last = new Map<string, number>();\n // Topics that asked for missing messages, so a burst after a gap asks only once.\n const resuming = new Set<string>();\n const listeners = new Set<(message: In, event: MessageEvent) => void>();\n const topics = createWebSocketTopics(client, {\n subscribe: topic => options.subscribe(topic, last.get(topic)),\n unsubscribe: options.unsubscribe,\n [ON_RELEASE]: topic => {\n last.delete(topic);\n resuming.delete(topic);\n }\n });\n\n const offs = [\n // The new connection subscribes again with `after`, which asks for what's missing.\n client.on('open', () => resuming.clear()),\n client.on('message', (message, event) => {\n const snapshot = options.snapshot?.(message);\n const position = snapshot ?? options.sequence(message);\n if (position) {\n const { topic, seq } = position;\n const prev = last.get(topic);\n if (prev !== undefined && !snapshot && seq !== prev + 1) {\n // Not the next one: a duplicate, or one after a gap, which waits for those before it.\n if (seq > prev && !resuming.has(topic)) {\n resuming.add(topic);\n options.onGap?.(topic, prev, seq);\n // Now or never, like a subscription: the next connection subscribes with `after`.\n // Not `volatile`, which can be a datagram that never arrives.\n if (client.socket?.readyState === 1) {\n client.send((options.resume ?? options.subscribe)(topic, prev));\n }\n }\n return;\n }\n // Count from the first message on a topic this holds, not on one it doesn't.\n if (prev !== undefined || topics.active().includes(topic)) last.set(topic, seq);\n resuming.delete(topic);\n }\n for (const listener of [...listeners]) {\n try {\n listener(message, event);\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n })\n ];\n\n return {\n ...topics,\n on(_type, listener) {\n // Wrapped, so the same function can be added twice and removed independently.\n const entry = (message: In, event: MessageEvent) => listener(message, event);\n listeners.add(entry);\n return () => {\n listeners.delete(entry);\n };\n },\n position: topic => last.get(topic),\n dispose() {\n topics.dispose();\n for (const off of offs) off();\n }\n };\n}\n","import type { WebSocketClient } from './types';\n\n/** Options for {@link createWebSocketLatency}: how pings and pongs look on the wire. */\nexport interface WebSocketLatencyOptions<In, Out> {\n /** The ping message, tagged with `id`. Sent with `volatile`, so pings are never queued. */\n ping: (id: number) => Out;\n /**\n * Recognize a pong: return the `id` of the ping it answers, and the server's clock\n * (`Date.now()` on the server, in ms) when it replied. Return `undefined` (or `null`)\n * for any other message. Without `time`, only the round-trip time is measured.\n */\n pong: (message: In) => { id: number | string; time?: number } | null | undefined;\n /**\n * How often to ping while open, in ms. The first ping goes out as soon as the socket opens.\n * @default 2000\n */\n interval?: number;\n /**\n * How many recent measurements to keep. `rtt` is their median, and `offset` comes from\n * the one with the lowest round trip, the most accurate estimate.\n * @default 5\n */\n samples?: number;\n}\n\n/** Latest latency measurements. A new object is created on every change. */\nexport interface Latency {\n /** Round-trip time in ms: the median of recent pings. `undefined` until the first pong. */\n readonly rtt: number | undefined;\n /**\n * Estimated server clock minus local clock, in ms. Add it to `Date.now()` for server time.\n * `undefined` until a pong carries `time`.\n */\n readonly offset: number | undefined;\n}\n\n/** Round-trip time and server clock offset for one client. Create with {@link createWebSocketLatency}. */\nexport interface WebSocketLatency {\n /** The client being measured. Hooks reading the latency keep it connected. */\n readonly client: WebSocketClient<any, any>;\n /** The current {@link Latency}. Returns the same object until it changes. */\n getSnapshot(): Latency;\n /** Called whenever a measurement changes. Compatible with `useSyncExternalStore`. */\n subscribe(listener: () => void): () => void;\n /** The estimated server time: `Date.now() + offset`, or `Date.now()` before any estimate. */\n serverNow(): number;\n /** Ping now instead of waiting for the next interval. Does nothing unless the socket is open. */\n ping(): void;\n /** Stop pinging and listening. Only needed for instances created dynamically. */\n dispose(): void;\n}\n\nconst EMPTY: Latency = Object.freeze({ rtt: undefined, offset: undefined });\n\nfunction median(values: number[]) {\n const sorted = [...values].sort((a, b) => a - b);\n const middle = sorted.length >> 1;\n return sorted.length % 2 ? sorted[middle]! : (sorted[middle - 1]! + sorted[middle]!) / 2;\n}\n\n/**\n * Measure round-trip time and estimate the server's clock, for ping displays, countdowns\n * and timestamps that must agree across players.\n *\n * Pings are sent on an interval while the socket is open, and paused otherwise. Measurements\n * from a previous connection are discarded on reconnect, but the last values stay readable\n * until new ones arrive. It doesn't open the connection by itself.\n *\n * Pongs are ordinary messages: they also reach `onMessage`, `lastMessage` and state stores.\n *\n * @example\n * ```ts\n * export const latency = createWebSocketLatency(game, {\n * ping: id => ({ type: 'ping', id }),\n * pong: message => (message.type === 'pong' ? { id: message.id, time: message.time } : undefined)\n * });\n *\n * // In a component:\n * const { rtt } = useWebSocketLatency(latency);\n *\n * // Anywhere: seconds left in a round that ends at the server's `endsAt`.\n * const left = (endsAt - latency.serverNow()) / 1000;\n * ```\n */\n// `const` + `NoInfer`: take `In` and `Out` from the client only, and keep literals such as\n// `type: 'ping'` from widening to `string` inside the callbacks.\nexport function createWebSocketLatency<In, const Out>(\n client: WebSocketClient<In, Out>,\n options: WebSocketLatencyOptions<NoInfer<In>, NoInfer<Out>>\n): WebSocketLatency {\n const interval = options.interval ?? 2000;\n const keep = Math.max(1, options.samples ?? 5);\n const listeners = new Set<() => void>();\n let snapshot = EMPTY;\n // Ping id -> `performance.now()` when sent. Pongs that never come are evicted oldest first.\n const inFlight = new Map<string, number>();\n let samples: { rtt: number; offset: number | undefined }[] = [];\n let nextId = 0;\n let timer: ReturnType<typeof setInterval> | undefined;\n\n function ping() {\n if (client.getSnapshot().status !== 'open') return;\n const id = ++nextId;\n inFlight.set(String(id), performance.now());\n if (inFlight.size > keep * 2) inFlight.delete(inFlight.keys().next().value!);\n client.send(options.ping(id), { volatile: true });\n }\n\n function start() {\n stop();\n inFlight.clear();\n samples = [];\n ping();\n timer = setInterval(ping, interval);\n }\n\n function stop() {\n clearInterval(timer);\n timer = undefined;\n }\n\n function measure(id: string, serverTime: number | undefined) {\n const sentAt = inFlight.get(id);\n if (sentAt === undefined) return;\n inFlight.delete(id);\n const rtt = performance.now() - sentAt;\n // The server read its clock about halfway through the round trip.\n const offset = serverTime === undefined ? undefined : serverTime + rtt / 2 - Date.now();\n samples.push({ rtt, offset });\n if (samples.length > keep) samples.shift();\n\n let best: (typeof samples)[number] | undefined;\n for (const sample of samples) {\n if (sample.offset !== undefined && (!best || sample.rtt < best.rtt)) best = sample;\n }\n snapshot = {\n rtt: median(samples.map(sample => sample.rtt)),\n offset: best ? best.offset : snapshot.offset\n };\n for (const listener of [...listeners]) listener();\n }\n\n const offs = [\n client.on('open', start),\n client.on('message', message => {\n const pong = options.pong(message);\n if (pong != null) measure(String(pong.id), pong.time);\n }),\n client.subscribe(() => {\n if (client.getSnapshot().status !== 'open') stop();\n })\n ];\n if (client.getSnapshot().status === 'open') start();\n\n return {\n client,\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n serverNow: () => Date.now() + (snapshot.offset ?? 0),\n ping,\n dispose() {\n stop();\n offs.forEach(off => off());\n }\n };\n}\n","import type { WebSocketClient } from './types';\n\ntype Timer = ReturnType<typeof setTimeout> | undefined;\n\n/** Another client's shared state, such as its cursor or what it's selected. */\nexport interface PresencePeer<P> {\n /** The peer's id, as the server reports it. */\n readonly peer: string;\n readonly state: P;\n /** When its state last arrived, from `Date.now()`. */\n readonly updatedAt: number;\n}\n\n/** Options for {@link createWebSocketPresence}: the messages your server relays. */\nexport interface WebSocketPresenceOptions<P, In, Out> {\n /** The message that shares this client's state with the others. Annotate `state`. */\n send: (state: P) => Out;\n /** The message that says this client left, sent by `leave()`. Omit it if the server has none. */\n leave?: () => Out;\n /**\n * Recognize another peer's state: its id and state, or `state: null` when it left. `undefined`\n * for any other message.\n */\n receive: (message: In) => { readonly peer: string; readonly state: P | null } | null | undefined;\n /**\n * Forget a peer that hasn't sent its state for this long, in ms, as if it had left. Covers\n * peers whose connection died without the server noticing.\n * @default 30000\n */\n timeout?: number;\n /**\n * Send this client's state again this often while it doesn't change, in ms, so the others\n * don't forget it.\n * @default timeout / 3\n */\n refresh?: number;\n /**\n * How many times per second this client's state may be sent. Changes in between are\n * coalesced: the newest goes out at the end of the interval.\n * @default 20\n */\n maxRate?: number;\n}\n\n/** Shared per-peer state that expires. Create with {@link createWebSocketPresence}. */\nexport interface WebSocketPresence<P> {\n /** The client the state is shared on. */\n readonly client: WebSocketClient<any, any>;\n /**\n * Share this client's state, e.g. on every pointer move. It's sent at most `maxRate` times per\n * second, the newest only, and again on every new connection and every `refresh`.\n */\n set(state: P): void;\n /** Stop sharing, and send `leave`. `set()` shares again. */\n leave(): void;\n /** The other peers, in the order they first appeared. A new array on every change. */\n getSnapshot(): readonly PresencePeer<P>[];\n /** Get notified when the peers change. Compatible with `useSyncExternalStore`. */\n subscribe(listener: () => void): () => void;\n /** Stop sharing and listening. */\n dispose(): void;\n}\n\nconst VOLATILE = { volatile: true };\n\n/**\n * Share per-peer state that expires, for presence, live cursors and selections.\n *\n * - `set(state)` shares this client's state, throttled to `maxRate` sends per second with the\n * newest winning, and sent again on every new connection and every `refresh`, so it's never\n * queued stale while disconnected.\n * - Other peers' states come from `receive`. A peer is forgotten when it leaves (`state: null`)\n * or when nothing has arrived from it for `timeout`.\n *\n * The server relays each client's state to the others, tagged with a peer id, and says\n * `state: null` when a client disconnects.\n *\n * @example\n * ```ts\n * export const cursors = createWebSocketPresence(room, {\n * send: (cursor: { x: number; y: number }) => ({ type: 'cursor', cursor }),\n * leave: () => ({ type: 'leave' }),\n * receive: message => (message.type === 'cursor' ? { peer: message.peer, state: message.cursor } : undefined)\n * });\n *\n * window.addEventListener('pointermove', event => cursors.set({ x: event.clientX, y: event.clientY }));\n * ```\n */\n// `NoInfer`: take `In` and `Out` from the client only; `P` comes from how `send` annotates `state`.\nexport function createWebSocketPresence<P, In, const Out>(\n client: WebSocketClient<In, Out>,\n options: WebSocketPresenceOptions<P, NoInfer<In>, NoInfer<Out>>\n): WebSocketPresence<P> {\n const timeout = options.timeout ?? 30_000;\n const interval = 1000 / (options.maxRate ?? 20);\n const peers = new Map<string, PresencePeer<P>>();\n const listeners = new Set<() => void>();\n let snapshot: readonly PresencePeer<P>[] = [];\n // This client's state while it's shared, and when it was last sent.\n let own: { state: P } | undefined;\n let lastSent = -Infinity;\n let sendTimer: Timer;\n let refreshTimer: ReturnType<typeof setInterval> | undefined;\n let expiryTimer: Timer;\n\n function changed() {\n snapshot = [...peers.values()];\n for (const listener of [...listeners]) listener();\n }\n\n function sendNow() {\n clearTimeout(sendTimer);\n sendTimer = undefined;\n if (!own) return;\n lastSent = Date.now();\n // Volatile: a new connection gets the newest state from the `open` listener instead.\n client.send(options.send(own.state), VOLATILE);\n }\n\n function expire() {\n expiryTimer = undefined;\n const now = Date.now();\n let next = Infinity;\n let removed = false;\n for (const [peer, entry] of peers) {\n const at = entry.updatedAt + timeout;\n if (at <= now) removed = peers.delete(peer);\n else next = Math.min(next, at);\n }\n if (next < Infinity) expiryTimer = setTimeout(expire, next - now);\n if (removed) changed();\n }\n\n const offs = [\n client.on('open', sendNow),\n client.on('message', message => {\n const update = options.receive(message);\n if (!update) return;\n if (update.state === null) {\n if (!peers.delete(update.peer)) return;\n } else {\n peers.set(update.peer, { peer: update.peer, state: update.state, updatedAt: Date.now() });\n expiryTimer ??= setTimeout(expire, timeout);\n }\n changed();\n })\n ];\n\n return {\n client,\n set(state) {\n own = { state };\n refreshTimer ??= setInterval(sendNow, options.refresh ?? timeout / 3);\n if (sendTimer !== undefined) return; // the newest goes out when it fires\n const wait = lastSent + interval - Date.now();\n if (wait <= 0) sendNow();\n else sendTimer = setTimeout(sendNow, wait);\n },\n leave() {\n own = undefined;\n clearTimeout(sendTimer);\n sendTimer = undefined;\n clearInterval(refreshTimer);\n refreshTimer = undefined;\n if (options.leave) client.send(options.leave(), VOLATILE);\n },\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n own = undefined;\n clearTimeout(sendTimer);\n clearInterval(refreshTimer);\n clearTimeout(expiryTimer);\n for (const off of offs) off();\n }\n };\n}\n","import type { SendOptions, WebSocketClient } from './types';\n\n/** Options for {@link createWebSocketRequests}: how requests and replies look on the wire. */\nexport interface WebSocketRequestsOptions<Req, In, Out> {\n /**\n * Build the outgoing message for `request(message)`, tagged with `id`. Annotate `message`\n * to type what `request()` accepts.\n */\n send: (message: Req, id: number) => Out;\n /**\n * The id an incoming message replies to, or `undefined` (or `null`) if it isn't a reply.\n * A string id matches the number it was sent as, so either echo works.\n */\n reply: (message: In) => number | string | null | undefined;\n /**\n * Called on a reply. Return something other than `undefined` to reject the request with\n * it instead of resolving, e.g. for a server-side error reply. A stream throws it.\n */\n error?: (message: In) => unknown;\n /**\n * Whether a reply is the last one of a stream. `stream()` needs it. That reply is yielded\n * too, then the stream ends.\n */\n done?: (message: In) => boolean;\n /**\n * Build the message that tells the server to stop working on request `id`. Sent when this\n * side gives up: the request's `signal` aborts, it times out, a loop over a stream exits\n * early, or the manager is disposed. Not sent after the last reply, an error reply, a lost\n * connection or `close()`.\n */\n cancel?: (id: number) => Out;\n /**\n * Default time to wait for a reply, in ms. For a stream, the time to wait for each reply,\n * including the first. `Infinity` waits forever.\n * @default 10000\n */\n timeout?: number;\n}\n\n/** Per-call options for {@link WebSocketRequests.request} and {@link WebSocketRequests.stream}. */\nexport interface RequestOptions extends SendOptions {\n /**\n * Time to wait for a reply (for a stream, for each reply), in ms. Overrides the manager's\n * `timeout`.\n */\n timeout?: number;\n /** Abort the request. It rejects (a stream throws) with `signal.reason`. A late reply is ignored. */\n signal?: AbortSignal;\n}\n\n/** Request/response over one client. Create with {@link createWebSocketRequests}. */\nexport interface WebSocketRequests<Req, In> {\n /** The client requests are sent on. */\n readonly client: WebSocketClient<any, any>;\n /**\n * Send a request and resolve with its reply. Holds the connection until it settles.\n *\n * Rejects with a `TimeoutError` `DOMException` after `timeout`, with `signal.reason` when\n * aborted, and with an `Error` when the connection it was sent on closes (the reply can\n * no longer arrive) or the client stops. A request made while disconnected is queued like\n * any other message and waits for the connection; it rejects if it is dropped from the\n * queue (replaced by a newer request with the same `key`, or evicted by `queue.limit`).\n */\n request(message: Req, options?: RequestOptions): Promise<In>;\n /**\n * Send a request and iterate over its replies, up to and including the one `done` matches.\n * Holds the connection until the stream ends. Needs the `done` option.\n *\n * `timeout` is the time to wait for each reply. Replies that arrive before the loop reads\n * them are buffered. The stream is one-shot: iterate it once.\n *\n * Fails where `request()` rejects, with the same errors, after yielding the replies already\n * received. Aborting `signal` throws `signal.reason` right away and discards them. Leaving\n * the loop early, aborting and timing out send `cancel`.\n *\n * @example\n * ```ts\n * for await (const reply of rpc.stream({ type: 'ask', prompt }, { signal })) {\n * text += reply.delta;\n * }\n * ```\n */\n stream(message: Req, options?: RequestOptions): AsyncIterableIterator<In>;\n /** Number of requests and streams waiting for a reply. */\n pending(): number;\n /** Reject every pending request and stop listening. Only needed for managers created dynamically. */\n dispose(): void;\n}\n\n// Last id sent per client, shared by its managers so two of them never send the same id\n// and resolve each other's requests with one reply.\nconst lastIds = new WeakMap<WebSocketClient<any, any>, number>();\n\nconst noop = () => {};\n\ninterface Pending<In> {\n /** The socket the request went out on, or `null` while it waits in the client's queue. */\n socket: WebSocket | null;\n /** A reply that isn't an error. */\n receive(reply: In): void;\n /** Settle, failed with `reason` or not. `cancel` tells the server to stop. Only the first call counts. */\n end(failed: boolean, reason?: unknown, cancel?: boolean): void;\n}\n\n/**\n * Send requests and await their replies, matched by an id the library generates.\n *\n * Replies are ordinary messages: they also reach `onMessage`, `lastMessage` and state stores.\n *\n * @example\n * ```ts\n * type Request = { type: 'join'; room: string } | { type: 'leave' };\n *\n * export const rpc = createWebSocketRequests(game, {\n * send: (message: Request, id) => ({ ...message, id }),\n * reply: message => (message.type === 'reply' ? message.id : undefined),\n * error: message => (message.type === 'reply' && message.error ? new Error(message.error) : undefined)\n * });\n *\n * const reply = await rpc.request({ type: 'join', room: 'lobby' }, { timeout: 5000 });\n * ```\n */\n// `const` + `NoInfer`: `In` and `Out` come from the client only, with literals kept unwidened;\n// `Req` comes from how `send` annotates its parameter.\nexport function createWebSocketRequests<Req, In, const Out>(\n client: WebSocketClient<In, Out>,\n options: WebSocketRequestsOptions<Req, NoInfer<In>, NoInfer<Out>>\n): WebSocketRequests<Req, In> {\n const waiting = new Map<string, Pending<In>>();\n\n function endWhere(\n test: (entry: Pending<In>) => boolean,\n reason: () => unknown,\n cancel?: boolean\n ) {\n for (const entry of [...waiting.values()]) if (test(entry)) entry.end(true, reason(), cancel);\n }\n\n const offs = [\n client.on('message', message => {\n const id = options.reply(message);\n const entry = id == null ? undefined : waiting.get(String(id));\n if (!entry) return;\n const error = options.error?.(message);\n if (error === undefined) entry.receive(message);\n else entry.end(true, error);\n }),\n // `open` listeners run before the client flushes its queue: queued requests go out on this socket.\n client.on('open', () => {\n for (const entry of waiting.values()) entry.socket ??= client.socket;\n }),\n client.subscribe(() => {\n const { status } = client.getSnapshot();\n if (status === 'closed' || status === 'idle') {\n // The queue was cleared too, so nothing still waiting can be sent.\n endWhere(\n () => true,\n () => new Error('WebSocket was closed before the request was answered')\n );\n } else {\n endWhere(\n entry => entry.socket !== null && entry.socket !== client.socket,\n () => new Error('WebSocket connection was lost before the request was answered')\n );\n }\n })\n ];\n\n /**\n * Send `message` as a new request. `next` gets each reply that isn't an error and returns\n * whether it was the last; `done` runs once, when the request ends. Returns a function that\n * ends it from this side and tells the server.\n */\n function start(\n message: Req,\n requestOptions: RequestOptions,\n next: (reply: In) => boolean,\n done: (failed: boolean, reason?: unknown) => void\n ): () => void {\n const { signal, priority } = requestOptions;\n if (signal?.aborted) {\n done(true, signal.reason);\n return noop;\n }\n\n if (client.getSnapshot().status === 'closed') {\n // Closed until someone reconnects it. Holding it would start it again, or leave it idle.\n done(true, new Error('WebSocket is closed: request not sent'));\n return noop;\n }\n const id = (lastIds.get(client) ?? 0) + 1;\n lastIds.set(client, id);\n const key = String(id);\n const timeout = requestOptions.timeout ?? options.timeout ?? 10_000;\n const release = client.acquire();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const onAbort = () => entry.end(true, signal!.reason, true);\n\n function arm() {\n clearTimeout(timer);\n if (timeout > 0 && timeout < Infinity) {\n timer = setTimeout(\n () =>\n entry.end(\n true,\n new DOMException(`Request ${id} timed out after ${timeout}ms`, 'TimeoutError'),\n true\n ),\n timeout\n );\n }\n }\n\n const entry: Pending<In> = {\n socket: null,\n receive(reply) {\n if (next(reply)) entry.end(false);\n else arm();\n },\n end(failed, reason, cancel) {\n if (!waiting.delete(key)) return;\n clearTimeout(timer);\n signal?.removeEventListener('abort', onAbort);\n release();\n done(failed, reason);\n if (cancel && options.cancel) {\n try {\n // Still queued, it can't be taken back: queue the cancel right behind it. Once sent,\n // the cancel only means something on that connection, so drop it unless that one is\n // still open. Not `volatile`, which can be a datagram that never arrives.\n if (!entry.socket) client.send(options.cancel(id), { priority });\n else if (entry.socket === client.socket && entry.socket.readyState === 1) {\n client.send(options.cancel(id));\n }\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n }\n }\n };\n waiting.set(key, entry);\n signal?.addEventListener('abort', onAbort, { once: true });\n arm();\n\n if (client.getSnapshot().status === 'idle') {\n // Idle after acquiring: it can't connect here (a server render).\n entry.end(true, new Error('WebSocket is idle: request not sent'));\n } else {\n try {\n const sent = client.send(options.send(message, id), {\n ...requestOptions,\n onDrop() {\n entry.end(\n true,\n new Error('Request was dropped from the send queue before it was sent')\n );\n requestOptions.onDrop?.();\n }\n });\n if (sent) entry.socket = client.socket;\n // Open but not sent: `maxBuffered` dropped it, and nothing will send it later.\n else if (client.socket?.readyState === 1)\n entry.end(true, new Error('WebSocket send buffer is full: request dropped'));\n else if (requestOptions.volatile)\n entry.end(true, new Error('WebSocket is not open: request dropped'));\n } catch (error) {\n entry.end(true, error);\n }\n }\n return () => entry.end(false, undefined, true);\n }\n\n return {\n client,\n request(message, requestOptions = {}) {\n return new Promise<In>((resolve, reject) => {\n start(\n message,\n requestOptions,\n reply => {\n resolve(reply);\n return true;\n },\n (failed, reason) => {\n if (failed) reject(reason);\n }\n );\n });\n },\n stream(message, requestOptions = {}) {\n const last = options.done;\n if (!last) throw new TypeError('stream() needs the `done` option');\n const replies: In[] = [];\n let ended: [failed: boolean, reason: unknown] | undefined;\n let wake = noop;\n const stop = start(\n message,\n requestOptions,\n reply => {\n replies.push(reply);\n wake();\n return last(reply);\n },\n (failed, reason) => {\n // Aborting stops right away; any other end comes after the replies already received.\n if (requestOptions.signal?.aborted) replies.length = 0;\n ended = [failed, reason];\n wake();\n }\n );\n\n async function* read() {\n try {\n for (;;) {\n if (replies.length) yield replies.shift()!;\n else if (ended) {\n if (ended[0]) throw ended[1];\n return;\n } else await new Promise<void>(resolve => (wake = resolve));\n }\n } finally {\n stop();\n }\n }\n\n const iterator = read();\n const close = iterator.return;\n // A generator closed before its first `next()` skips `finally`, and one closed during a\n // `next()` waits for it: stop first, so leaving a loop tells the server right away.\n iterator.return = value => {\n stop();\n return close.call(iterator, value);\n };\n return iterator;\n },\n pending: () => waiting.size,\n dispose() {\n offs.forEach(off => off());\n endWhere(\n () => true,\n () => new Error('Requests were disposed'),\n true\n );\n }\n };\n}\n","import type { WebSocketClient } from './types';\n\n/** What `request` returns for a request: any object with these fields, such as the message itself. */\ntype RequestFields = { id: number | string; method: string; params?: unknown; timeout?: number };\n\n/**\n * The type of `params` in `Q`, keeping `undefined` when they're optional and giving `undefined`\n * when there are none. (Inferring `params?: P` directly would drop the `undefined`.)\n */\ntype ParamsOf<Q> = Q extends unknown\n ? 'params' extends keyof Q\n ? Q['params' & keyof Q]\n : undefined\n : never;\n\n/**\n * Options for {@link createWebSocketResponder}: how the server's requests and your replies look\n * on the wire.\n *\n * @typeParam Q - What `request` returns. Handlers receive its `params`.\n * @typeParam R - What handlers return, from the annotation on `respond`'s `result`.\n */\nexport interface WebSocketResponderOptions<Q extends RequestFields, R, In, Out> {\n /**\n * Recognize a request from the server: its `id`, the `method` handlers are registered for,\n * and the `params` they receive. Return `undefined` (or `null`) for any other message.\n * A `timeout` (in ms) overrides the manager's for this request.\n *\n * `params` come from the server, so validate them before acting on them.\n */\n request: (message: In) => Q | null | undefined;\n /** Build the reply to request `id`. Annotate `result` to type what handlers return. */\n respond: (id: number | string, result: R) => Out;\n /**\n * Build the error reply to request `id`: what a handler threw, a `NotFoundError` `DOMException`\n * when nothing is registered for the method, a `TimeoutError` after `timeout`, or an\n * `AbortError` from `dispose()`.\n */\n error: (id: number | string, error: unknown) => Out;\n /**\n * The id of a request the server withdrew, or `undefined` (or `null`) if the message isn't a\n * cancellation. The request's `signal` aborts and nothing is sent.\n */\n cancel?: (message: In) => number | string | null | undefined;\n /**\n * Time to answer a request, in ms. When it runs out, the `error` reply (a `TimeoutError`)\n * is sent and the request's `signal` aborts.\n * @default Infinity\n */\n timeout?: number;\n}\n\n/** A request from the server that hasn't been answered yet. */\nexport interface IncomingRequest<P = unknown, R = unknown> {\n /** The id the server sent. Replies carry it back. */\n readonly id: number | string;\n /** The method it was sent for. */\n readonly method: string;\n /** The `params` from `request`. They come from the server, so validate them. */\n readonly params: P;\n /**\n * Aborts when the answer is no longer wanted: the server cancelled the request, it timed out,\n * the connection it arrived on closed, the responder was disposed, or it was answered with\n * `respond` or `reject` while its handler was still running.\n */\n readonly signal: AbortSignal;\n /**\n * Reply with `result`. Only the first answer counts.\n * @returns `true` if the reply was sent, `false` if the request was already answered or is gone.\n */\n respond(result: R): boolean;\n /**\n * Reply with an error, built by the `error` option. Only the first answer counts.\n * @returns `true` if the reply was sent, `false` if the request was already answered or is gone.\n */\n reject(error: unknown): boolean;\n}\n\n/**\n * Answers a request in code. Return (or resolve) the result to reply with it; throw (or reject)\n * to send the `error` reply instead.\n */\nexport type RequestHandler<P = unknown, R = unknown> = (\n params: P,\n request: IncomingRequest<P, R>\n) => R | PromiseLike<R>;\n\n/** Answers the server's requests on one client. Create with {@link createWebSocketResponder}. */\nexport interface WebSocketResponder<P = unknown, R = unknown> {\n /** The client requests arrive on. Hooks reading the responder keep it connected. */\n readonly client: WebSocketClient<any, any>;\n /**\n * Answer requests for `method` until the returned function is called.\n *\n * With a `handler`, requests are answered in code. Without one, they wait in\n * {@link getSnapshot} until something calls `respond` or `reject` on them, e.g. a dialog\n * that asks the user. When several registrations exist for one method, the newest one takes\n * new requests, and unregistering it hands them back to the previous one. Requests for a\n * method with no registration get a `NotFoundError` reply.\n *\n * @returns An unregister function (safe to call more than once). Requests already waiting stay.\n */\n onRequest(method: string, handler?: RequestHandler<P, R>): () => void;\n /**\n * Requests waiting for an answer from outside a handler, oldest first. Returns the same array\n * until it changes.\n */\n getSnapshot(): readonly IncomingRequest<P, R>[];\n /** Called whenever {@link getSnapshot} changes. Compatible with `useSyncExternalStore`. */\n subscribe(listener: () => void): () => void;\n /**\n * Stop answering: send an `AbortError` reply to every request in flight and stop listening.\n * Only needed for responders created dynamically.\n */\n dispose(): void;\n}\n\ninterface Entry {\n /** The socket the request arrived on. Its reply can only go back on that one. */\n readonly socket: WebSocket | null;\n /** Forget the request and abort its signal, without replying. */\n drop(reason: DOMException): void;\n /** Forget the request, abort its signal and send the `error` reply, both with `error`. */\n fail(error: DOMException): void;\n}\n\nconst abortError = (message: string) => new DOMException(message, 'AbortError');\n\n/**\n * Answer requests the server sends: run a handler for tools that only exist in the browser\n * (selection, clipboard, geolocation), or let a person answer, e.g. to approve an agent's action.\n * The reverse of `createWebSocketRequests`.\n *\n * Every request gets exactly one reply while its connection lives: the handler's result, an\n * `error` reply when it throws, has no registration or times out. When the connection a request\n * arrived on closes, the request is dropped and nothing is sent, since the server can't match a\n * reply on a new connection. Replies are never queued.\n *\n * Requests are ordinary messages: they also reach `onMessage`, `lastMessage` and state stores.\n *\n * @example\n * ```ts\n * export const tools = createWebSocketResponder(agent, {\n * request: message => (message.type === 'call' ? message : undefined),\n * respond: (id, result: unknown) => ({ type: 'result', id, result }),\n * error: (id, error) => ({ type: 'result', id, error: String(error) })\n * });\n *\n * tools.onRequest('get_selection', () => window.getSelection()?.toString() ?? '');\n *\n * // In a component: requests a person answers.\n * const approvals = useAgentApproval(tools, 'approve');\n * approvals[0]?.respond({ approved: true });\n * ```\n */\n// `const` + `NoInfer`: `In` and `Out` come from the client only, with literals kept unwidened;\n// `Q` comes from what `request` returns and `R` from how `respond` annotates `result`.\nexport function createWebSocketResponder<Q extends RequestFields, R, In, const Out>(\n client: WebSocketClient<In, Out>,\n options: WebSocketResponderOptions<Q, R, NoInfer<In>, NoInfer<Out>>\n): WebSocketResponder<ParamsOf<Q>, R> {\n type P = ParamsOf<Q>;\n const entries = new Map<string, Entry>();\n const registrations = new Map<string, { handler?: RequestHandler<P, R> }[]>();\n const listeners = new Set<() => void>();\n let waiting: readonly IncomingRequest<P, R>[] = [];\n\n function setWaiting(next: readonly IncomingRequest<P, R>[]) {\n waiting = next;\n for (const listener of [...listeners]) listener();\n }\n\n /**\n * Send a reply, or an `error` reply if building or encoding it throws. Only while open: never\n * queued, and not `volatile`, which can be a datagram that never arrives.\n */\n function reply(id: number | string, build: () => Out): boolean {\n if (client.socket?.readyState !== 1) return false;\n try {\n return client.send(build());\n } catch (error) {\n return client.send(options.error(id, error));\n }\n }\n\n function receive(incoming: Q) {\n const { id, method } = incoming;\n const key = String(id);\n if (entries.has(key)) return; // sent again while it is being answered\n const registration = registrations.get(method)?.at(-1);\n if (!registration) {\n const error = new DOMException(`No handler for request method \"${method}\"`, 'NotFoundError');\n reply(id, () => options.error(id, error));\n return;\n }\n\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n let done = false;\n let running = false;\n\n function settle(build: (() => Out) | null, reason?: unknown) {\n if (done) return false;\n done = true;\n entries.delete(key);\n clearTimeout(timer);\n if (waiting.includes(request)) setWaiting(waiting.filter(entry => entry !== request));\n if (reason !== undefined) controller.abort(reason);\n return build ? reply(id, build) : false;\n }\n\n // An answer from outside the handler makes the handler's work moot.\n const answeredElsewhere = () =>\n running ? abortError(`Request ${id} was answered elsewhere`) : undefined;\n const request: IncomingRequest<P, R> = {\n id,\n method,\n params: incoming.params as P,\n signal: controller.signal,\n respond: result => settle(() => options.respond(id, result), answeredElsewhere()),\n reject: error => settle(() => options.error(id, error), answeredElsewhere())\n };\n const fail = (error: DOMException) => {\n settle(() => options.error(id, error), error);\n };\n // Registered before the handler runs, so a handler that closes the socket drops its own request.\n entries.set(key, {\n socket: client.socket,\n drop: reason => {\n settle(null, reason);\n },\n fail\n });\n\n const timeout = incoming.timeout ?? options.timeout ?? Infinity;\n if (timeout > 0 && timeout < Infinity) {\n timer = setTimeout(\n () =>\n fail(\n new DOMException(`Request ${id} was not answered within ${timeout}ms`, 'TimeoutError')\n ),\n timeout\n );\n }\n\n const { handler } = registration;\n if (!handler) {\n setWaiting([...waiting, request]);\n return;\n }\n running = true;\n // Also catches a handler that throws synchronously.\n new Promise<R>(resolve => resolve(handler(request.params, request))).then(\n result => {\n running = false;\n request.respond(result);\n },\n error => {\n running = false;\n request.reject(error);\n }\n );\n }\n\n const offs = [\n client.on('message', message => {\n const incoming = options.request(message);\n if (incoming != null) {\n receive(incoming);\n return;\n }\n const id = options.cancel?.(message);\n if (id != null) {\n entries.get(String(id))?.drop(abortError(`Request ${id} was cancelled`));\n }\n }),\n // Every close, reconnect and teardown replaces `client.socket` and notifies.\n client.subscribe(() => {\n for (const entry of [...entries.values()]) {\n if (entry.socket !== client.socket) {\n entry.drop(abortError('WebSocket connection was lost before the request was answered'));\n }\n }\n })\n ];\n\n return {\n client,\n onRequest(method, handler) {\n let stack = registrations.get(method);\n if (!stack) registrations.set(method, (stack = []));\n const registration = { handler };\n stack.push(registration);\n return () => {\n const index = stack.indexOf(registration);\n if (index !== -1) stack.splice(index, 1);\n };\n },\n getSnapshot: () => waiting,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n offs.forEach(off => off());\n registrations.clear();\n for (const entry of [...entries.values()]) entry.fail(abortError('Responder was disposed'));\n }\n };\n}\n","import { createWebSocketFeeds } from './feeds';\nimport type { WebSocketClient } from './types';\n\n/**\n * What happens to a run when its connection drops: `keep` resumes it on the next connection,\n * for agents that keep working in the background; `cancel` aborts it and tells the server, for\n * a chat answer nobody may be waiting for.\n */\nexport type RunPolicy = 'keep' | 'cancel';\n\n/** Where a run is: streaming events, or ended. */\nexport type RunStatus = 'streaming' | 'done' | 'error' | 'aborted';\n\n/** A run's state, reduced from its events. A new object on every change. */\nexport interface AgentRun<T> {\n readonly runId: string;\n readonly status: RunStatus;\n /** Reduced from the run's events with `reduce`, starting from `initial`. */\n readonly value: T;\n /** What `error` returned for the event that ended the run, when `status` is `'error'`. */\n readonly error: unknown;\n}\n\n/** The subset of `Storage` that runs use, so `sessionStorage`, `localStorage` or your own fits. */\nexport type RunStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;\n\n/** Options for {@link createWebSocketRuns}: the messages your server expects and sends. */\nexport interface WebSocketRunsOptions<Req, T, In, Out> {\n /** Names the runs' entry in `storage`, `react-websocket-runs:<name>`. */\n name: string;\n /**\n * The message that starts a run, with its `runId`. Sent again on a new connection until the\n * run's first event arrives, so the server should start a run only once per `runId`.\n * Annotate `request`: that's what `runs.start()` accepts.\n */\n start: (request: Req, runId: string) => Out;\n /**\n * The message that asks for a run's events after `after`, its last event received, or from\n * the beginning when `after` is `undefined` (after a reload, or for `attach()`). Sent on every\n * new connection while the run is streaming.\n */\n resume: (runId: string, after: number | undefined) => Out;\n /** The message that stops a run, sent by `abort()` and for runs whose policy is `cancel`. */\n cancel?: (runId: string) => Out;\n /**\n * Recognize a run's event: its `runId`, and its `offset`, one more than the event before it.\n * `undefined` for any other message.\n */\n event: (message: In) => { readonly runId: string; readonly offset: number } | null | undefined;\n /** Whether an event is the run's last. The run is `'done'` after reducing it. */\n done?: (message: In) => boolean;\n /** Recognize an event that ends the run with an error: return it, or `undefined`. */\n error?: (message: In) => unknown;\n /** A run's value before its first event. */\n initial: T;\n /** Fold an event into a run's value. Pure. */\n reduce: (value: T, message: In) => T;\n /** @default 'keep' */\n policy?: RunPolicy;\n /**\n * Where to remember streaming runs, so a reload resumes them.\n * @default sessionStorage, where it exists. `null` remembers nothing.\n */\n storage?: RunStorage | null;\n}\n\n/** Runs that stream events and survive reconnects and reloads. Create with {@link createWebSocketRuns}. */\nexport interface WebSocketRuns<Req, T> {\n /** The client the runs stream on. */\n readonly client: WebSocketClient<any, any>;\n /**\n * Start a run and return its `runId`. The start message is sent as soon as the socket is\n * open, and the run is remembered until it ends. `initial` overrides the `initial` option for\n * this run. A `runId` it already knows, streaming or ended, starts nothing: the server starts\n * a run only once per id. `delete()` it first to use the id again.\n */\n start(request: Req, options?: { runId?: string; policy?: RunPolicy; initial?: T }): string;\n /**\n * Follow a run started elsewhere, such as in another tab or before a reload, from its first\n * event. Does nothing for a run it already knows.\n */\n attach(runId: string, options?: { policy?: RunPolicy; initial?: T }): void;\n /** Stop a streaming run: send `cancel`, and make it `'aborted'`. */\n abort(runId: string): void;\n /** A run's state, or `undefined` for one it doesn't know. The same object until it changes. */\n get(runId: string): AgentRun<T> | undefined;\n /** Every run it knows, oldest first. A new array on every change. */\n list(): readonly AgentRun<T>[];\n /** Get notified when a run changes. Compatible with `useSyncExternalStore`. */\n subscribe(listener: () => void): () => void;\n /** Forget a run, aborting it first if it's streaming. */\n delete(runId: string): void;\n /** Stop following runs, and release the connection. Remembered runs stay remembered. */\n dispose(): void;\n}\n\nfunction randomId() {\n return (\n globalThis.crypto?.randomUUID?.() ??\n `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`\n );\n}\n\n// How long a page waits for another to say it's following a run found in storage, before\n// cancelling it. A duplicated tab, or one opened with `window.open`, gets a copy of\n// `sessionStorage` while the page that started the run is still following it.\nconst CLAIM_WAIT = 200;\n\nfunction defaultStorage(): RunStorage | null {\n try {\n return typeof sessionStorage === 'undefined' ? null : sessionStorage;\n } catch {\n // Reading `sessionStorage` throws where storage is blocked, e.g. some sandboxed iframes.\n return null;\n }\n}\n\n/**\n * Stream runs, such as an AI agent's answer, that survive reconnects and page reloads.\n *\n * - `start(request)` sends `start(request, runId)` and follows the run's events, folding them\n * into its value with `reduce`.\n * - Each event carries an `offset`. On a new connection, a streaming run sends\n * `resume(runId, after)` with the last offset received, so the server sends what was missed,\n * and duplicates are dropped.\n * - Streaming runs are remembered in `sessionStorage`. After a reload they're followed again from\n * their first event, rebuilding the value, so `list()` or `useAgentRun` can show them.\n * - A run whose policy is `cancel` is aborted when its connection drops, and `cancel` is sent\n * first thing on the next one.\n *\n * Built on `createWebSocketFeeds`, with each run a topic. While runs are streaming, it holds\n * the client's connection.\n *\n * @example\n * ```ts\n * export const runs = createWebSocketRuns(agent, {\n * name: 'chat',\n * start: (prompt: string, runId) => ({ type: 'run', runId, prompt }),\n * resume: (runId, after) => ({ type: 'resume', runId, after }),\n * cancel: runId => ({ type: 'cancel', runId }),\n * event: message => (message.type === 'token' || message.type === 'done' ? message : undefined),\n * done: message => message.type === 'done',\n * initial: '',\n * reduce: (text, message) => text + message.text\n * });\n *\n * const runId = runs.start('Summarize this thread');\n * ```\n */\n// `NoInfer`: take `In` and `Out` from the client only; `Req` comes from how `start` annotates\n// its parameter, and `T` from `initial`.\nexport function createWebSocketRuns<Req, T, In, const Out>(\n client: WebSocketClient<In, Out>,\n options: WebSocketRunsOptions<Req, T, NoInfer<In>, NoInfer<Out>>\n): WebSocketRuns<Req, T> {\n const storage = options.storage === undefined ? defaultStorage() : options.storage;\n const key = `react-websocket-runs:${options.name}`;\n const runs = new Map<string, AgentRun<T>>();\n // Start requests not yet answered by an event, sent again on a new connection.\n const starting = new Map<string, Req>();\n const policies = new Map<string, RunPolicy>();\n const releases = new Map<string, () => void>();\n const listeners = new Set<() => void>();\n let list: readonly AgentRun<T>[] = [];\n\n const feeds = createWebSocketFeeds(client, {\n subscribe: (runId, after) =>\n after === undefined && starting.has(runId)\n ? options.start(starting.get(runId)!, runId)\n : options.resume(runId, after),\n sequence: message => {\n const event = options.event(message);\n return event && { topic: event.runId, seq: event.offset };\n }\n });\n\n function read(): Record<string, RunPolicy> {\n try {\n return JSON.parse(storage?.getItem(key) ?? '{}') as Record<string, RunPolicy>;\n } catch {\n return {};\n }\n }\n\n function remember(runId: string, policy: RunPolicy | undefined) {\n if (!storage) return;\n const stored = read();\n if (policy) stored[runId] = policy;\n else delete stored[runId];\n try {\n if (Object.keys(stored).length) storage.setItem(key, JSON.stringify(stored));\n else storage.removeItem(key);\n } catch {\n // Full or blocked: the run still streams, but won't survive a reload.\n }\n }\n\n function set(runId: string, patch: Partial<AgentRun<T>>) {\n const run = runs.get(runId);\n runs.set(runId, {\n ...(run ?? { runId, status: 'streaming', value: options.initial, error: undefined }),\n ...patch\n });\n list = [...runs.values()];\n for (const listener of [...listeners]) listener();\n }\n\n /** Stop following a run that ended. */\n function end(runId: string, patch: Partial<AgentRun<T>>) {\n releases.get(runId)?.();\n releases.delete(runId);\n starting.delete(runId);\n policies.delete(runId);\n remember(runId, undefined);\n set(runId, patch);\n }\n\n function follow(runId: string, policy: RunPolicy, value = options.initial) {\n policies.set(runId, policy);\n remember(runId, policy);\n set(runId, { value });\n releases.set(runId, feeds.acquire(runId));\n }\n\n function cancel(runId: string) {\n const message = options.cancel;\n if (!message) return;\n if (client.socket?.readyState === 1) return void client.send(message(runId));\n // Not through the queue, which `queue: false` turns off: hold the connection, and send it\n // from an `open` listener, ahead of anything queued.\n const release = client.acquire();\n const off = client.on('open', () => {\n off();\n client.send(message(runId));\n release();\n });\n }\n\n function abort(runId: string) {\n if (runs.get(runId)?.status !== 'streaming') return;\n cancel(runId);\n end(runId, { status: 'aborted' });\n }\n\n let status = client.getSnapshot().status;\n const offs = [\n feeds.on('message', message => {\n const event = options.event(message);\n if (!event || runs.get(event.runId)?.status !== 'streaming') return;\n const { runId } = event;\n starting.delete(runId);\n const error = options.error?.(message);\n if (error !== undefined) return end(runId, { status: 'error', error });\n const value = options.reduce(runs.get(runId)!.value, message);\n if (options.done?.(message)) end(runId, { status: 'done', value });\n else set(runId, { value });\n }),\n client.subscribe(() => {\n const previous = status;\n status = client.getSnapshot().status;\n if (previous !== 'open' || status === 'open') return;\n // The connection dropped: runs that shouldn't outlive it stop here.\n for (const [runId, policy] of policies) if (policy === 'cancel') abort(runId);\n }),\n feeds.dispose\n ];\n\n // Pages using the same storage say which runs they follow, so a copy of it doesn't cancel them.\n // Not on a server, where an open channel would keep the process alive.\n const channel =\n storage && typeof window !== 'undefined' && typeof BroadcastChannel === 'function'\n ? new BroadcastChannel(key)\n : undefined;\n const claimed = new Set<string>();\n if (channel) {\n channel.onmessage = ({ data }: MessageEvent<{ who?: string; mine?: string } | null>) => {\n if (data?.mine) claimed.add(data.mine);\n else if (data?.who && policies.has(data.who)) channel.postMessage({ mine: data.who });\n };\n offs.push(() => channel.close());\n }\n\n // Runs that were streaming before a reload: follow them again, or stop those that shouldn't\n // outlive their page, unless another page still follows them.\n const stale: string[] = [];\n for (const [runId, policy] of Object.entries(read())) {\n if (policy !== 'cancel') follow(runId, policy);\n else if (channel) {\n stale.push(runId);\n channel.postMessage({ who: runId });\n } else {\n cancel(runId);\n remember(runId, undefined);\n }\n }\n if (stale.length) {\n const timer = setTimeout(() => {\n for (const runId of stale) {\n if (claimed.has(runId) || policies.has(runId)) continue;\n cancel(runId);\n remember(runId, undefined);\n }\n }, CLAIM_WAIT);\n offs.push(() => clearTimeout(timer));\n }\n\n return {\n client,\n start(request, { runId = randomId(), policy = options.policy ?? 'keep', initial } = {}) {\n if (runs.has(runId)) return runId;\n starting.set(runId, request);\n follow(runId, policy, initial);\n return runId;\n },\n attach(runId, { policy = options.policy ?? 'keep', initial } = {}) {\n if (!runs.has(runId)) follow(runId, policy, initial);\n },\n abort,\n get: runId => runs.get(runId),\n list: () => list,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n delete(runId) {\n abort(runId);\n if (runs.delete(runId)) {\n list = [...runs.values()];\n for (const listener of [...listeners]) listener();\n }\n },\n dispose() {\n for (const release of releases.values()) release();\n releases.clear();\n for (const off of offs) off();\n }\n };\n}\n","import type { WebSocketLatency } from './latency';\nimport type { WebSocketClient } from './types';\n\ntype Timer = ReturnType<typeof setTimeout>;\n\n/** Options for {@link createWebSocketSendRate}. */\nexport interface WebSocketSendRateOptions {\n /**\n * Ticks per second while the connection keeps up.\n * @default 30\n */\n maxRate?: number;\n /**\n * The rate never drops below this, in ticks per second.\n * @default 5\n */\n minRate?: number;\n /**\n * Bytes waiting in the browser's send buffer (`socket.bufferedAmount`) above which a tick\n * sends nothing and the rate halves.\n * @default 1024\n */\n maxBuffered?: number;\n /**\n * Measurements from `createWebSocketLatency` on the same client. With them, a round-trip time\n * that rises also lowers the rate. Without them, only the send buffer is watched.\n */\n latency?: Pick<WebSocketLatency, 'getSnapshot'>;\n /**\n * How far `rtt` may rise above the lowest one measured on this connection before the rate\n * halves, in ms. A steady high ping keeps the full rate; only a rising one counts.\n * @default 100\n */\n maxRttIncrease?: number;\n}\n\n/** The current send rate. A new object is created on every change. */\nexport interface SendRate {\n /** Ticks per second, rounded. */\n readonly rate: number;\n /**\n * Whether the last tick found the link congested: the send buffer above `maxBuffered`, or\n * `rtt` risen more than `maxRttIncrease`.\n */\n readonly congested: boolean;\n}\n\n/** Keyed sends coalesced at a rate that adapts to the link. Create with {@link createWebSocketSendRate}. */\nexport interface WebSocketSendRate<Out> {\n /** The client the messages are sent on. */\n readonly client: WebSocketClient<any, Out>;\n /**\n * Send `message` at the next tick. A message with the same `key` still waiting for it is\n * replaced, so only the newest one goes out. Sent with `volatile`.\n */\n send(message: Out, key: string): void;\n /** Send everything waiting now, e.g. right before a one-shot action that must carry the latest aim. */\n flush(): void;\n /** The current {@link SendRate}. Returns the same object until it changes. */\n getSnapshot(): SendRate;\n /** Called whenever the rate or `congested` changes. Compatible with `useSyncExternalStore`. */\n subscribe(listener: () => void): () => void;\n /** Stop sending and listening. Only needed for instances created dynamically. */\n dispose(): void;\n}\n\nconst VOLATILE = { volatile: true };\n\n/**\n * Send high-rate state, such as aim, cursor or position, at a rate that adapts to the\n * connection. Messages are coalesced per key between ticks. The rate drops by half when the\n * send buffer fills up or the round trip grows, and climbs back one tick per second at a time\n * when the link keeps up, so players on a poor connection degrade smoothly instead of building\n * up lag.\n *\n * Ticks run only while the socket is open and something is waiting. Messages waiting during a\n * reconnect go out after it; `close()` drops them. It doesn't open the connection by itself.\n *\n * @example\n * ```ts\n * export const stream = createWebSocketSendRate(game, { latency, maxRate: 30 });\n *\n * // On every pointer move: at most 30 a second, fewer on a congested link.\n * stream.send({ type: 'aim', angle }, 'aim');\n * ```\n */\nexport function createWebSocketSendRate<Out>(\n client: WebSocketClient<any, Out>,\n options: WebSocketSendRateOptions = {}\n): WebSocketSendRate<Out> {\n const { maxBuffered = 1024, latency, maxRttIncrease = 100 } = options;\n const maxRate = options.maxRate ?? 30;\n const minRate = Math.min(options.minRate ?? 5, maxRate);\n const listeners = new Set<() => void>();\n // Insertion order is send order: a replaced message moves to the end.\n const pending = new Map<string, Out>();\n let rate = maxRate;\n let snapshot: SendRate = { rate: maxRate, congested: false };\n let last = -Infinity; // `performance.now()` at the last tick\n let best = Infinity; // the lowest `rtt` on this connection\n let slow = false; // `rtt` has risen too far above `best`\n let measured: unknown; // the latency snapshot already reacted to\n let timer: Timer | undefined;\n let disposed = false;\n\n function flush() {\n for (const [key, message] of pending) {\n if (!client.send(message, VOLATILE)) return; // not open: keep the rest for later\n pending.delete(key);\n }\n }\n\n function publish(congested: boolean) {\n const rounded = Math.round(rate);\n if (rounded === snapshot.rate && congested === snapshot.congested) return;\n snapshot = { rate: rounded, congested };\n for (const listener of [...listeners]) listener();\n }\n\n function schedule() {\n if (disposed || timer !== undefined || !pending.size || client.getSnapshot().status !== 'open')\n return;\n // Right away after a pause, otherwise one interval after the last tick.\n timer = setTimeout(tick, Math.max(0, last + 1000 / rate - performance.now()));\n }\n\n function stop() {\n clearTimeout(timer);\n timer = undefined;\n }\n\n // Additive increase, multiplicative decrease, as TCP does.\n function tick() {\n timer = undefined;\n last = performance.now();\n const measurement = latency?.getSnapshot();\n if (measurement !== measured) {\n // React to each measurement once.\n measured = measurement;\n const rtt = measurement?.rtt;\n if (rtt !== undefined) {\n best = Math.min(best, rtt);\n slow = rtt - best > maxRttIncrease;\n if (slow) rate /= 2;\n }\n }\n // Checked once per tick, not per message, so messages in one tick don't count against each other.\n const full = (client.socket?.bufferedAmount ?? 0) > maxBuffered;\n if (full) rate /= 2;\n else {\n flush();\n if (!slow) rate++;\n }\n rate = Math.min(maxRate, Math.max(minRate, rate));\n publish(full || slow);\n schedule();\n }\n\n const offs = [\n client.on('open', () => {\n // A new connection may take a different route: measure its best round trip afresh, and\n // ignore the last connection's measurement, which stays readable until a new one arrives.\n best = Infinity;\n slow = false;\n measured = latency?.getSnapshot();\n schedule(); // after the client's own queue, which is sent once `open` listeners return\n }),\n client.subscribe(() => {\n const { status } = client.getSnapshot();\n if (status !== 'open') stop();\n // Like the client's queue: kept through a reconnect, dropped by `close()` or giving up.\n if (status === 'closed' || status === 'idle') pending.clear();\n })\n ];\n\n return {\n client,\n send(message, key) {\n pending.delete(key);\n pending.set(key, message);\n schedule();\n },\n flush,\n getSnapshot: () => snapshot,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n disposed = true;\n stop();\n pending.clear();\n offs.forEach(off => off());\n }\n };\n}\n","import type { WebSocketClient } from './types';\n\n/** Options for {@link createWebSocketState}. */\nexport interface WebSocketStateOptions<S, In> {\n /** The state before any message arrives, and what `reset()` returns to. */\n initialState: S;\n /**\n * Fold one incoming message into the state. Must be pure: return a new object for a\n * change, or the same `state` to ignore the message (subscribers aren't notified).\n */\n reduce: (state: S, message: In) => S;\n /**\n * `'frame'`: notify subscribers at most once per animation frame instead of once per\n * message, so a fast stream (tokens, tickers, game state) renders once per frame.\n * `getState()` is always current; only notifications wait. `setState` and `reset` still\n * notify right away.\n *\n * Browsers pause animation frames in background tabs: the state keeps up, and subscribers\n * hear about it when the tab is visible again. Without `requestAnimationFrame` (Node, some\n * workers), it waits about 16 ms instead.\n */\n batch?: 'frame';\n}\n\n/**\n * State derived from a client's messages. Read it with `useWebSocketState(store, selector)`,\n * or with `getState`/`subscribe` outside React.\n *\n * @typeParam S - The state type. Don't use a function type: `setState` treats functions as updaters.\n */\n/**\n * Messages from somewhere other than the client itself, such as the in-order messages of\n * `createWebSocketFeeds`, for `createWebSocketState`.\n */\nexport interface MessageSource<In> {\n /** The client the messages come from. */\n readonly client: WebSocketClient<any, any>;\n on(type: 'message', listener: (message: In, event: MessageEvent) => void): () => void;\n}\n\nexport interface WebSocketState<S> {\n /** The client whose messages this store reduces. Hooks reading the store keep it connected. */\n readonly client: WebSocketClient<any, any>;\n /** The `initialState` it was created with. Used for server rendering and hydration. */\n readonly initialState: S;\n /** The current state. Returns the same object until it changes. */\n getState(): S;\n /** Called whenever the state changes. Compatible with `useSyncExternalStore`. */\n subscribe(listener: () => void): () => void;\n /** Replace the state, or update it from the current one, e.g. for optimistic updates. */\n setState(update: S | ((state: S) => S)): void;\n /** Go back to `initialState`. */\n reset(): void;\n /** Stop reducing messages. Only needed for stores created dynamically rather than at module level. */\n dispose(): void;\n}\n\n/**\n * Keep state derived from a client's messages, like a reducer that runs on every message.\n * It starts listening immediately and keeps the state across reconnects; it doesn't open\n * the connection by itself.\n *\n * Pass feeds from `createWebSocketFeeds` in place of the client to reduce their messages\n * instead: numbered ones once each and in order.\n *\n * @example\n * ```ts\n * const prices = createWebSocket<Tick>('wss://api.example.com/prices', { json: true });\n *\n * export const priceState = createWebSocketState(prices, {\n * initialState: {} as Record<string, number>,\n * reduce: (state, tick) => ({ ...state, [tick.symbol]: tick.price })\n * });\n *\n * // In a component: re-renders only when ACME's price changes.\n * const acme = useWebSocketState(priceState, state => state.ACME);\n * ```\n */\nexport function createWebSocketState<S, In>(\n source: WebSocketClient<In, any> | MessageSource<In>,\n // `In` comes from the source only, not from how `reduce` annotates its parameter.\n options: WebSocketStateOptions<S, NoInfer<In>>\n): WebSocketState<S> {\n const client = 'client' in source ? source.client : source;\n const { initialState, reduce, batch } = options;\n let state = initialState;\n // What subscribers were last told about, so a frame that finds nothing new stays quiet.\n let notified = state;\n let frame = false;\n const listeners = new Set<() => void>();\n\n function notify() {\n if (Object.is(state, notified)) return;\n notified = state;\n for (const listener of [...listeners]) listener();\n }\n\n function flush() {\n frame = false;\n notify();\n }\n\n function update(change: S | ((state: S) => S), later?: boolean) {\n const next = typeof change === 'function' ? (change as (state: S) => S)(state) : change;\n if (Object.is(next, state)) return;\n state = next;\n if (!later) notify();\n else if (!frame) {\n frame = true;\n // Node and some workers have no animation frames: wait about one instead.\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(flush);\n else setTimeout(flush, 16);\n }\n }\n\n // A throwing reducer leaves the state unchanged; the client re-throws the error asynchronously.\n const off = (source as MessageSource<In>).on('message', message =>\n update(current => reduce(current, message), batch === 'frame')\n );\n\n return {\n client,\n initialState,\n getState: () => state,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n setState: change => update(change),\n reset: () => update(initialState),\n dispose: off\n };\n}\n"],"mappings":"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"}
|