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/index.cjs
CHANGED
|
@@ -22,6 +22,18 @@ const noopSend = () => false;
|
|
|
22
22
|
const noop = () => {};
|
|
23
23
|
const ON_TEARDOWN = Symbol.for("react-websocket.onTeardown");
|
|
24
24
|
const registry = /* @__PURE__ */ new Map();
|
|
25
|
+
if (process.env.NODE_ENV !== "production") globalThis[Symbol.for("react-websocket.registry")] = registry;
|
|
26
|
+
const registryListeners = /* @__PURE__ */ new Set();
|
|
27
|
+
const started = /* @__PURE__ */ new Map();
|
|
28
|
+
function subscribeRegistry(listener) {
|
|
29
|
+
registryListeners.add(listener);
|
|
30
|
+
return () => {
|
|
31
|
+
registryListeners.delete(listener);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function registryChanged() {
|
|
35
|
+
for (const listener of [...registryListeners]) listener();
|
|
36
|
+
}
|
|
25
37
|
const origins = /* @__PURE__ */ new WeakMap();
|
|
26
38
|
const warned = /* @__PURE__ */ new Set();
|
|
27
39
|
const CONNECTION_OPTIONS = {
|
|
@@ -53,25 +65,32 @@ function warnOnConflict(url, key, client, options, id) {
|
|
|
53
65
|
warned.add(`${key}\u0000${id}`);
|
|
54
66
|
console.warn(`react-websocket: useWebSocket("${url}") was given different ${differing.join(", ")} than the component that opened this shared connection. They are ignored: every component using the URL gets the first one's options. Pass the same options, or define the connection once with createWebSocket().`);
|
|
55
67
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
/** The client shared for `key`, or `own` until a component using it has committed. */
|
|
69
|
+
const shared_ = (key, own) => key === null ? null : registry.get(key) ?? own;
|
|
70
|
+
/** A component's own client for a URL. It becomes the shared one if the component commits first. */
|
|
71
|
+
function ownClient(url, key, options, id) {
|
|
72
|
+
const reused = started.get(key);
|
|
73
|
+
if (reused) return reused;
|
|
74
|
+
const created = (0, __core_cjs.createWebSocket)(url, {
|
|
75
|
+
...options,
|
|
76
|
+
[ON_TEARDOWN]: () => {
|
|
77
|
+
if (started.get(key) === created) started.delete(key);
|
|
78
|
+
if (registry.get(key) === created) {
|
|
79
|
+
registry.delete(key);
|
|
80
|
+
registryChanged();
|
|
65
81
|
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
const off = created.subscribe(() => {
|
|
85
|
+
if (created.getSnapshot().status !== "connecting") return;
|
|
86
|
+
off();
|
|
87
|
+
if (registry.get(key) !== created && !started.has(key)) started.set(key, created);
|
|
88
|
+
});
|
|
89
|
+
if (process.env.NODE_ENV !== "production") origins.set(created, {
|
|
90
|
+
options,
|
|
91
|
+
id
|
|
92
|
+
});
|
|
93
|
+
return created;
|
|
75
94
|
}
|
|
76
95
|
/** Always-current value for use inside effects and event listeners (a `useEffectEvent` stand-in). */
|
|
77
96
|
function useLatest(value) {
|
|
@@ -88,8 +107,23 @@ function useHold(client, enabled) {
|
|
|
88
107
|
}
|
|
89
108
|
function useWebSocket(target, options = {}) {
|
|
90
109
|
const id = (0, react.useId)();
|
|
91
|
-
const
|
|
110
|
+
const url = typeof target === "string" ? target : null;
|
|
111
|
+
const key = url === null ? null : `${url}\u0000${JSON.stringify(options.protocols ?? null)}`;
|
|
112
|
+
const make = () => ({
|
|
113
|
+
key,
|
|
114
|
+
client: key === null ? null : ownClient(url, key, options, id)
|
|
115
|
+
});
|
|
116
|
+
const [made, setMade] = (0, react.useState)(make);
|
|
117
|
+
let own = made.client;
|
|
118
|
+
if (made.key !== key) {
|
|
119
|
+
const next = make();
|
|
120
|
+
own = next.client;
|
|
121
|
+
setMade(next);
|
|
122
|
+
}
|
|
123
|
+
const shared = (0, react.useSyncExternalStore)(subscribeRegistry, () => shared_(key, own), () => own);
|
|
124
|
+
const client = key === null ? target ?? null : shared;
|
|
92
125
|
const enabled = options.enabled ?? true;
|
|
126
|
+
if (process.env.NODE_ENV !== "production" && key !== null) warnOnConflict(url, key, client, options, id);
|
|
93
127
|
const snapshot = (0, react.useSyncExternalStore)(client ? client.subscribe : noopSubscribe, client ? client.getSnapshot : idleSnapshot, idleSnapshot);
|
|
94
128
|
const handlers = useLatest(options);
|
|
95
129
|
(0, react.useEffect)(() => {
|
|
@@ -106,12 +140,31 @@ function useWebSocket(target, options = {}) {
|
|
|
106
140
|
enabled,
|
|
107
141
|
handlers
|
|
108
142
|
]);
|
|
109
|
-
|
|
143
|
+
(0, react.useEffect)(() => {
|
|
144
|
+
if (key === null || !enabled) return;
|
|
145
|
+
if (!registry.has(key)) {
|
|
146
|
+
registry.set(key, own);
|
|
147
|
+
if (started.get(key) === own) started.delete(key);
|
|
148
|
+
registryChanged();
|
|
149
|
+
}
|
|
150
|
+
return registry.get(key).acquire();
|
|
151
|
+
}, [
|
|
152
|
+
key,
|
|
153
|
+
own,
|
|
154
|
+
client,
|
|
155
|
+
enabled
|
|
156
|
+
]);
|
|
157
|
+
useHold(key === null ? client : null, enabled);
|
|
158
|
+
const actions = (0, react.useMemo)(() => key === null ? null : {
|
|
159
|
+
send: (message, sendOptions) => shared_(key, own).send(message, sendOptions),
|
|
160
|
+
close: (code, reason) => shared_(key, own).close(code, reason),
|
|
161
|
+
reconnect: () => shared_(key, own).reconnect()
|
|
162
|
+
}, [key, own]);
|
|
110
163
|
return {
|
|
111
164
|
...snapshot,
|
|
112
|
-
send: client ? client.send : noopSend,
|
|
113
|
-
close: client ? client.close : noop,
|
|
114
|
-
reconnect: client ? client.reconnect : noop,
|
|
165
|
+
send: actions ? actions.send : client ? client.send : noopSend,
|
|
166
|
+
close: actions ? actions.close : client ? client.close : noop,
|
|
167
|
+
reconnect: actions ? actions.reconnect : client ? client.reconnect : noop,
|
|
115
168
|
client
|
|
116
169
|
};
|
|
117
170
|
}
|
|
@@ -223,6 +276,107 @@ function useWebSocketLatency(latency) {
|
|
|
223
276
|
return snapshot;
|
|
224
277
|
}
|
|
225
278
|
/**
|
|
279
|
+
* The messages waiting in an outbox from `react-websocket/outbox`, oldest first, for a "3 orders
|
|
280
|
+
* waiting to sync" banner. Re-renders when they change. `null` lists nothing. On the server the
|
|
281
|
+
* list is empty.
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* ```tsx
|
|
285
|
+
* const waiting = useWebSocketOutbox(orders);
|
|
286
|
+
* if (!waiting.length) return null;
|
|
287
|
+
* return <p role="status">{waiting.length} orders waiting to sync</p>;
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
function useWebSocketOutbox(outbox) {
|
|
291
|
+
return (0, react.useSyncExternalStore)(outbox?.subscribe ?? noopSubscribe, outbox?.getSnapshot ?? noRequests, noRequests);
|
|
292
|
+
}
|
|
293
|
+
const noRun = () => void 0;
|
|
294
|
+
/**
|
|
295
|
+
* Whether an element is on screen, from an `IntersectionObserver`, for dashboards that stream
|
|
296
|
+
* full detail only for what's visible: pick a topic with it and pass it to `useWebSocketTopic`,
|
|
297
|
+
* which switches subscriptions when it changes. `initial` until the first observation, on the
|
|
298
|
+
* server, and where `IntersectionObserver` doesn't exist.
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```tsx
|
|
302
|
+
* const ref = useRef<HTMLElement>(null);
|
|
303
|
+
* useWebSocketTopic(feeds, useInView(ref) ? `run:${id}:tokens` : `run:${id}:status`);
|
|
304
|
+
* return <article ref={ref}>…</article>;
|
|
305
|
+
* ```
|
|
306
|
+
*/
|
|
307
|
+
function useInView(ref, options = {}) {
|
|
308
|
+
const { rootMargin, threshold } = options;
|
|
309
|
+
const [inView, setInView] = (0, react.useState)(options.initial ?? false);
|
|
310
|
+
(0, react.useEffect)(() => {
|
|
311
|
+
const element = ref.current;
|
|
312
|
+
if (!element || typeof IntersectionObserver === "undefined") return;
|
|
313
|
+
const observer = new IntersectionObserver((entries) => setInView(entries.at(-1).isIntersecting), {
|
|
314
|
+
rootMargin,
|
|
315
|
+
threshold
|
|
316
|
+
});
|
|
317
|
+
observer.observe(element);
|
|
318
|
+
return () => observer.disconnect();
|
|
319
|
+
}, [
|
|
320
|
+
ref,
|
|
321
|
+
rootMargin,
|
|
322
|
+
threshold
|
|
323
|
+
]);
|
|
324
|
+
return inView;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* The other peers' shared state from `createWebSocketPresence`, such as their cursors.
|
|
328
|
+
* Re-renders when a peer's state arrives, it leaves, or it times out. Keeps the connection open
|
|
329
|
+
* while mounted. On the server the list is empty.
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```tsx
|
|
333
|
+
* const peers = useWebSocketPresence(cursors);
|
|
334
|
+
* return peers.map(({ peer, state }) => <Cursor key={peer} x={state.x} y={state.y} />);
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
function useWebSocketPresence(presence) {
|
|
338
|
+
const peers = (0, react.useSyncExternalStore)(presence?.subscribe ?? noopSubscribe, presence?.getSnapshot ?? noRequests, noRequests);
|
|
339
|
+
useHold(presence?.client ?? null, true);
|
|
340
|
+
return peers;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* The thread of an AG-UI agent from `react-websocket/ag-ui`: its messages, shared state and
|
|
344
|
+
* status, re-rendering on every event.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```tsx
|
|
348
|
+
* const thread = useAgUiAgent(agent);
|
|
349
|
+
* return thread.messages.map(message => <Message key={message.id} message={message} />);
|
|
350
|
+
* ```
|
|
351
|
+
*/
|
|
352
|
+
function useAgUiAgent(agent) {
|
|
353
|
+
return (0, react.useSyncExternalStore)(agent.subscribe, agent.getSnapshot, agent.getSnapshot);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* A run from `createWebSocketRuns`, such as an agent's answer: its `value` reduced from the
|
|
357
|
+
* events so far, and its `status`. Re-renders on every event. A run it doesn't know, such as one
|
|
358
|
+
* started in another tab, is attached and followed from its first event. `undefined` while the
|
|
359
|
+
* run is unknown, for a `null` id, and on the server.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```tsx
|
|
363
|
+
* const run = useAgentRun(runs, runId);
|
|
364
|
+
* return (
|
|
365
|
+
* <>
|
|
366
|
+
* <Markdown>{run?.value ?? ''}</Markdown>
|
|
367
|
+
* {run?.status === 'streaming' && <button onClick={() => runs.abort(runId)}>Stop</button>}
|
|
368
|
+
* </>
|
|
369
|
+
* );
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
function useAgentRun(runs, runId) {
|
|
373
|
+
const run = (0, react.useSyncExternalStore)(runs?.subscribe ?? noopSubscribe, () => runs && runId ? runs.get(runId) : void 0, noRun);
|
|
374
|
+
(0, react.useEffect)(() => {
|
|
375
|
+
if (runs && runId) runs.attach(runId);
|
|
376
|
+
}, [runs, runId]);
|
|
377
|
+
return run;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
226
380
|
* Requests for `method` that a person answers, e.g. an agent asking to run a tool. While mounted,
|
|
227
381
|
* it registers `method` on the responder without a handler, so new requests wait here instead of
|
|
228
382
|
* getting a `NotFoundError` reply. Call `respond` or `reject` on one to answer it. Re-renders
|
|
@@ -319,10 +473,15 @@ function useWebSocketStream(rpc, options = {}) {
|
|
|
319
473
|
};
|
|
320
474
|
}
|
|
321
475
|
//#endregion
|
|
476
|
+
exports.useAgUiAgent = useAgUiAgent;
|
|
322
477
|
exports.useAgentApproval = useAgentApproval;
|
|
478
|
+
exports.useAgentRun = useAgentRun;
|
|
479
|
+
exports.useInView = useInView;
|
|
323
480
|
exports.useWebSocket = useWebSocket;
|
|
324
481
|
exports.useWebSocketEvent = useWebSocketEvent;
|
|
325
482
|
exports.useWebSocketLatency = useWebSocketLatency;
|
|
483
|
+
exports.useWebSocketOutbox = useWebSocketOutbox;
|
|
484
|
+
exports.useWebSocketPresence = useWebSocketPresence;
|
|
326
485
|
exports.useWebSocketState = useWebSocketState;
|
|
327
486
|
exports.useWebSocketStatus = useWebSocketStatus;
|
|
328
487
|
exports.useWebSocketStream = useWebSocketStream;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["createWebSocket","useRef","useId","useSyncExternalStore","useMemo","useState"],"sources":["../src/react.ts"],"sourcesContent":["import {\n useEffect,\n useId,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore\n} from 'react';\n// Import the client through the `core` entry: the build keeps it external, so both entries\n// share one copy of it (and of `MessageError`).\nimport { createWebSocket } from './core';\nimport type { Latency, WebSocketLatency } from './latency';\n// Types only: a value import from anything but `./core` would bundle a second copy into this entry.\nimport type { RequestOptions, WebSocketRequests } from './requests';\nimport type { IncomingRequest, WebSocketResponder } from './responder';\nimport type { WebSocketState } from './state';\nimport type { WebSocketTopics } from './topics';\nimport type {\n CloseInfo,\n EventMap,\n SendOptions,\n Snapshot,\n Status,\n WebSocketClient,\n WebSocketOptions\n} from './types';\n\n/**\n * Per-component event handlers. They always see the component's latest props and\n * state, and changing them never reconnects.\n */\nexport interface WebSocketHandlers<In> {\n /** The socket opened (initially or after a reconnect). */\n onOpen?: (event: Event) => void;\n /** A message arrived and passed `parse`/`json` and `schema`. */\n onMessage?: (message: In, event: MessageEvent) => void;\n /** A socket closed. Called whether or not it will be retried. */\n onClose?: (close: CloseInfo) => void;\n /** A socket error `Event`, a `MessageError`, or an `Error` from the constructor or `url` function. */\n onError?: (error: Event | Error) => void;\n /**\n * `false` releases this component's hold on the connection and stops its handlers.\n * The connection closes once nothing else holds it.\n * @default true\n */\n enabled?: boolean;\n}\n\n/** Options for `useWebSocket(url, options)`: connection options plus handlers. */\nexport interface UseWebSocketOptions<In, Out>\n extends WebSocketOptions<In, Out>, WebSocketHandlers<In> {}\n\n/** What `useWebSocket` returns: the current {@link Snapshot} plus stable actions. */\nexport interface UseWebSocketResult<In, Out> extends Snapshot<In> {\n /**\n * Send now, or queue until open (see {@link SendOptions} for volatile and keyed sends).\n * Returns `true` if sent immediately. Stable identity.\n */\n send: (message: Out, options?: SendOptions) => boolean;\n /** Close and stop reconnecting (for every component sharing the connection). Stable identity. */\n close: (code?: number, reason?: string) => void;\n /** Drop the socket and connect again right away. Stable identity. */\n reconnect: () => void;\n /** The underlying client, or `null` when the URL is `null`/`undefined`. */\n client: WebSocketClient<In, Out> | null;\n}\n\nconst IDLE: Snapshot<never> = Object.freeze({ status: 'idle', lastMessage: undefined, attempt: 0 });\nconst idleSnapshot = () => IDLE;\nconst idleStatus = (): Status => 'idle';\nconst NO_LATENCY: Latency = Object.freeze({ rtt: undefined, offset: undefined });\nconst noLatency = () => NO_LATENCY;\n// Not frozen: a call would keep it in bundles that never use the hook.\nconst NO_REQUESTS: readonly never[] = [];\nconst noRequests = () => NO_REQUESTS;\nconst noopSubscribe = () => () => {};\nconst noopSend = () => false;\nconst noop = () => {};\n\n// Same key as `ON_TEARDOWN` in ./client, which this entry must not import directly.\nconst ON_TEARDOWN = Symbol.for('react-websocket.onTeardown');\n\n// Hooks given the same URL (and protocols) share one connection.\nconst registry = new Map<string, WebSocketClient<any, any>>();\n\n// Development only: the options (and `useId` of the component) each URL client was created with,\n// to warn when another component passes different ones for the same URL.\nconst origins = new WeakMap<\n WebSocketClient<any, any>,\n { options: WebSocketOptions<any, any>; id: string }\n>();\nconst warned = new Set<string>();\n\n// The options that shape a shared connection, with what leaving one out means.\nconst CONNECTION_OPTIONS: { [K in keyof WebSocketOptions]-?: unknown } = {\n protocols: undefined, // part of the registry key\n binaryType: 'blob',\n json: false,\n parse: undefined,\n serialize: undefined,\n schema: undefined,\n reconnect: true,\n heartbeat: undefined,\n queue: true,\n logger: undefined, // debugging only\n WebSocket: undefined\n};\nconst IGNORED_OPTIONS = new Set(['protocols', 'logger']);\n\n// Inline functions and objects are new on every render: compare them by source and by field.\nconst sameValue = (a: unknown, b: unknown) =>\n Object.is(a, b) ||\n (typeof a === 'function' && typeof b === 'function' && String(a) === String(b));\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && Object.getPrototypeOf(value) === Object.prototype;\n\nfunction sameOption(a: unknown, b: unknown) {\n if (sameValue(a, b)) return true;\n if (!isPlainObject(a) || !isPlainObject(b)) return false;\n return [...Object.keys(a), ...Object.keys(b)].every(key => sameValue(a[key], b[key]));\n}\n\nfunction warnOnConflict(\n url: string,\n key: string,\n client: WebSocketClient<any, any>,\n options: WebSocketOptions<any, any>,\n id: string\n) {\n const origin = origins.get(client);\n if (!origin || origin.id === id || warned.has(`${key}\\u0000${id}`)) return;\n const differing = (Object.keys(CONNECTION_OPTIONS) as (keyof WebSocketOptions)[]).filter(\n name =>\n !IGNORED_OPTIONS.has(name) &&\n !sameOption(\n origin.options[name] ?? CONNECTION_OPTIONS[name],\n options[name] ?? CONNECTION_OPTIONS[name]\n )\n );\n if (!differing.length) return;\n warned.add(`${key}\\u0000${id}`);\n console.warn(\n `react-websocket: useWebSocket(\"${url}\") was given different ${differing.join(', ')} than ` +\n `the component that opened this shared connection. They are ignored: every component ` +\n `using the URL gets the first one's options. Pass the same options, or define the ` +\n `connection once with createWebSocket().`\n );\n}\n\nfunction sharedClient<In, Out>(url: string, options: WebSocketOptions<In, Out>, id: string) {\n // Don't let server renders accumulate clients that can never be torn down.\n if (typeof window === 'undefined') return createWebSocket(url, options);\n const key = `${url}\\u0000${JSON.stringify(options.protocols ?? null)}`;\n let client = registry.get(key) as WebSocketClient<In, Out> | undefined;\n if (!client) {\n const created: WebSocketClient<In, Out> = createWebSocket(url, {\n ...options,\n [ON_TEARDOWN]: () => {\n if (registry.get(key) === created) registry.delete(key);\n }\n } as WebSocketOptions<In, Out>);\n registry.set(key, created);\n client = created;\n if (process.env.NODE_ENV !== 'production') origins.set(created, { options, id });\n } else if (process.env.NODE_ENV !== 'production') {\n warnOnConflict(url, key, client, options, id);\n }\n return client;\n}\n\n/** Always-current value for use inside effects and event listeners (a `useEffectEvent` stand-in). */\nfunction useLatest<T>(value: T) {\n const ref = useRef(value);\n useInsertionEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n\nfunction useHold(client: WebSocketClient<any, any> | null, enabled: boolean) {\n useEffect(() => {\n if (client && enabled) return client.acquire();\n }, [client, enabled]);\n}\n\n/**\n * Connect to a WebSocket and re-render with its status and latest message.\n *\n * - With a URL, every component using the same URL (and `protocols`) shares one\n * connection. Its options come from whichever component created it first; in\n * development, a component passing different ones gets a console warning.\n * - With a client from `createWebSocket()`, options are the client's; only handlers\n * and `enabled` apply here.\n * - With `null` or `undefined`, nothing connects and the status stays `idle`.\n *\n * The connection opens on mount and closes one tick after the last component using it\n * unmounts, so StrictMode and `<Activity>` don't open duplicate sockets.\n *\n * Re-renders on status changes and on every message. For high-frequency streams use\n * `useWebSocketEvent` and `useWebSocketStatus` instead.\n *\n * @example\n * ```tsx\n * const { status, lastMessage, send } = useWebSocket<Price, Order>('wss://api.example.com/prices', {\n * json: true,\n * onMessage: price => console.log(price)\n * });\n * ```\n */\nexport function useWebSocket<In = unknown, Out = unknown>(\n url: string | null | undefined,\n options?: UseWebSocketOptions<In, Out>\n): UseWebSocketResult<In, Out>;\nexport function useWebSocket<In, Out>(\n client: WebSocketClient<In, Out> | null | undefined,\n options?: WebSocketHandlers<In>\n): UseWebSocketResult<In, Out>;\nexport function useWebSocket<In, Out>(\n target: string | WebSocketClient<In, Out> | null | undefined,\n options: UseWebSocketOptions<In, Out> = {}\n): UseWebSocketResult<In, Out> {\n const id = useId();\n const client =\n target == null ? null : typeof target === 'string' ? sharedClient(target, options, id) : target;\n const enabled = options.enabled ?? true;\n\n const snapshot = useSyncExternalStore<Snapshot<In>>(\n client ? client.subscribe : noopSubscribe,\n client ? client.getSnapshot : idleSnapshot,\n idleSnapshot\n );\n\n const handlers = useLatest(options);\n useEffect(() => {\n if (!client || !enabled) return;\n const offs = [\n client.on('open', event => handlers.current.onOpen?.(event)),\n client.on('message', (message, event) => handlers.current.onMessage?.(message, event)),\n client.on('close', info => handlers.current.onClose?.(info)),\n client.on('error', error => handlers.current.onError?.(error))\n ];\n return () => offs.forEach(off => off());\n }, [client, enabled, handlers]);\n useHold(client, enabled);\n\n return {\n ...snapshot,\n send: client ? client.send : noopSend,\n close: client ? client.close : noop,\n reconnect: client ? client.reconnect : noop,\n client\n };\n}\n\n/**\n * Listen to a client's events without re-rendering the component. The listener always\n * sees the latest props and state. Keeps the connection open while mounted.\n *\n * @example\n * ```tsx\n * useWebSocketEvent(chat, 'message', message => setMessages(list => [...list, message]));\n * ```\n */\nexport function useWebSocketEvent<In, K extends keyof EventMap<In>>(\n client: WebSocketClient<In, any> | null | undefined,\n type: K,\n listener: EventMap<In>[K]\n): void {\n const latest = useLatest(listener);\n useEffect(() => {\n if (!client) return;\n const off = client.on(type, ((...args: unknown[]) =>\n (latest.current as (...a: unknown[]) => void)(...args)) as EventMap<In>[K]);\n const release = client.acquire();\n return () => {\n off();\n release();\n };\n }, [client, type, latest]);\n}\n\n/**\n * The connection status of a client. Re-renders only when the status changes, not on\n * messages. Keeps the connection open while mounted.\n *\n * @example\n * ```tsx\n * const status = useWebSocketStatus(chat); // 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed'\n * ```\n */\nexport function useWebSocketStatus(client: WebSocketClient<any, any> | null | undefined): Status {\n const status = useSyncExternalStore(\n client ? client.subscribe : noopSubscribe,\n client ? () => client.getSnapshot().status : idleStatus,\n idleStatus\n );\n useHold(client ?? null, true);\n return status;\n}\n\nconst identity = <T>(value: T) => value;\n\n/**\n * Read state built from messages by `createWebSocketState`. Re-renders only when the\n * selected value changes (by `isEqual`, default `Object.is`), however often messages\n * arrive. Keeps the connection open while mounted.\n *\n * On the server, and during hydration, it reads from `initialState`.\n *\n * @example\n * ```tsx\n * const acme = useWebSocketState(priceState, state => state.ACME);\n * ```\n */\nexport function useWebSocketState<S>(store: WebSocketState<S>): S;\nexport function useWebSocketState<S, T>(\n store: WebSocketState<S>,\n selector: (state: S) => T,\n isEqual?: (a: T, b: T) => boolean\n): T;\nexport function useWebSocketState<S, T>(\n store: WebSocketState<S>,\n selector: (state: S) => T = identity as (state: S) => T,\n isEqual: (a: T, b: T) => boolean = Object.is\n): T {\n const [getSelection, getServerSelection] = useMemo(() => {\n // Return the previous selection while the state is unchanged or the result is equal,\n // so inline selectors that build objects don't re-render (or loop) on every message.\n const memoized = (read: () => S) => {\n let memo: { state: S; selection: T } | undefined;\n return () => {\n const state = read();\n if (memo && Object.is(memo.state, state)) return memo.selection;\n const selection = selector(state);\n if (memo && isEqual(memo.selection, selection)) {\n memo = { state, selection: memo.selection };\n return memo.selection;\n }\n memo = { state, selection };\n return selection;\n };\n };\n return [memoized(store.getState), memoized(() => store.initialState)] as const;\n }, [store, selector, isEqual]);\n\n const selection = useSyncExternalStore(store.subscribe, getSelection, getServerSelection);\n useHold(store.client, true);\n return selection;\n}\n\n/**\n * Subscribe to one topic (or several) while mounted, through a `createWebSocketTopics`\n * manager. Components holding the same topic share one subscription, and it is sent again\n * after every reconnect. Keeps the connection open while mounted. `null` holds nothing.\n *\n * @example\n * ```tsx\n * useWebSocketTopic(topics, `prices:${symbol}`);\n * useWebSocketTopic(topics, watchlist.map(symbol => `prices:${symbol}`));\n * ```\n */\nexport function useWebSocketTopic(\n topics: WebSocketTopics | null | undefined,\n topic: string | readonly string[] | null | undefined\n): void {\n // A stable key, so a new array with the same topics doesn't resubscribe.\n const key = topic == null ? null : JSON.stringify(typeof topic === 'string' ? [topic] : topic);\n useEffect(() => {\n if (!topics || key === null) return;\n const releases = (JSON.parse(key) as string[]).map(t => topics.acquire(t));\n return () => releases.forEach(release => release());\n }, [topics, key]);\n}\n\n/**\n * Read round-trip time and server clock offset from a `createWebSocketLatency` instance.\n * Re-renders when a measurement changes, about once per ping interval. Keeps the connection\n * open while mounted. On the server, and during hydration, both values are `undefined`.\n *\n * @example\n * ```tsx\n * const { rtt } = useWebSocketLatency(latency);\n * return <span>{rtt === undefined ? '–' : `${Math.round(rtt)} ms`}</span>;\n * ```\n */\nexport function useWebSocketLatency(latency: WebSocketLatency): Latency {\n const snapshot = useSyncExternalStore(latency.subscribe, latency.getSnapshot, noLatency);\n useHold(latency.client, true);\n return snapshot;\n}\n\n/**\n * Requests for `method` that a person answers, e.g. an agent asking to run a tool. While mounted,\n * it registers `method` on the responder without a handler, so new requests wait here instead of\n * getting a `NotFoundError` reply. Call `respond` or `reject` on one to answer it. Re-renders\n * when the list changes. Keeps the connection open while mounted. `null` holds nothing.\n *\n * Requests stay listed after unmount until they are answered, time out, are cancelled or lose\n * their connection, so a remounted dialog shows them again. On the server the list is empty.\n *\n * @example\n * ```tsx\n * const [request] = useAgentApproval(tools, 'approve');\n * if (!request) return null;\n * return (\n * <dialog open>\n * {request.params.summary}\n * <button onClick={() => request.respond({ approved: true })}>Approve</button>\n * <button onClick={() => request.respond({ approved: false })}>Deny</button>\n * </dialog>\n * );\n * ```\n */\nexport function useAgentApproval<P, R>(\n responder: WebSocketResponder<P, R> | null | undefined,\n method: string | null | undefined\n): readonly IncomingRequest<P, R>[] {\n const waiting = useSyncExternalStore(\n responder ? responder.subscribe : noopSubscribe,\n responder ? responder.getSnapshot : noRequests,\n noRequests\n );\n useEffect(() => {\n if (responder && method != null) return responder.onRequest(method);\n }, [responder, method]);\n useHold(responder?.client ?? null, method != null);\n return useMemo(() => {\n const mine = waiting.filter(request => request.method === method);\n return mine.length ? mine : NO_REQUESTS;\n }, [waiting, method]);\n}\n\n/** Options for `useWebSocketStream` that fold replies into one value, e.g. tokens into text. */\nexport interface UseWebSocketStreamOptions<In, T> {\n /** The value before the first reply. `start()` resets to it. */\n initial: T;\n /** Fold a reply into the value. Return a new value instead of changing the old one. */\n reduce: (value: T, reply: In) => T;\n /**\n * `'frame'`: re-render at most once per animation frame instead of once per reply. Browsers\n * pause frames in background tabs; the value catches up when the tab is visible again.\n */\n batch?: 'frame';\n}\n\n/** What `useWebSocketStream` returns: the stream's progress plus stable actions. */\nexport interface UseWebSocketStreamResult<Req, T> {\n /** The replies so far, folded with `reduce`, or as an array without it. */\n readonly value: T;\n /**\n * `idle` until `start()`. `done` after the last reply, `error` when the stream failed (see\n * `error`), `aborted` after `abort()`. The value stays as it was when the stream ended.\n */\n readonly status: 'idle' | 'streaming' | 'done' | 'error' | 'aborted';\n /** Why the stream failed, while `status` is `error`. */\n readonly error: unknown;\n /** Reset the value and stream the replies to `message`, aborting the stream before. Stable identity. */\n start: (message: Req, options?: Omit<RequestOptions, 'signal'>) => void;\n /** Stop the stream and send the manager's `cancel`. Stable identity. */\n abort: () => void;\n}\n\nconst append = <T>(list: readonly T[], reply: T): readonly T[] => [...list, reply];\n\n// Node and some workers have no animation frames: wait about one instead.\nfunction nextFrame(callback: () => void) {\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(callback);\n else setTimeout(callback, 16);\n}\n\n/**\n * Stream the replies to a request into a value, e.g. a model's tokens into its answer.\n * `start(message)` sends it with `rpc.stream()` and folds each reply in with `reduce`, or\n * collects the replies without it. Starting again aborts the stream before; so do `abort()`,\n * unmounting and a new `rpc`, and each sends the manager's `cancel`. Re-renders on every\n * reply, or once per frame with `batch: 'frame'`. Keeps the connection open while mounted.\n *\n * @example\n * ```tsx\n * const answer = useWebSocketStream(ask, {\n * initial: '',\n * reduce: (text, reply) => text + reply.delta,\n * batch: 'frame'\n * });\n * return (\n * <>\n * <button onClick={() => answer.start({ prompt })}>Ask</button>\n * <button onClick={answer.abort} disabled={answer.status !== 'streaming'}>Stop</button>\n * <p>{answer.value}</p>\n * </>\n * );\n * ```\n */\nexport function useWebSocketStream<Req, In, T>(\n rpc: WebSocketRequests<Req, In> | null | undefined,\n options: UseWebSocketStreamOptions<NoInfer<In>, T>\n): UseWebSocketStreamResult<Req, T>;\nexport function useWebSocketStream<Req, In>(\n rpc: WebSocketRequests<Req, In> | null | undefined,\n options?: { batch?: 'frame' }\n): UseWebSocketStreamResult<Req, readonly In[]>;\nexport function useWebSocketStream<Req, In>(\n rpc: WebSocketRequests<Req, In> | null | undefined,\n options: Partial<UseWebSocketStreamOptions<In, any>> = {}\n): UseWebSocketStreamResult<Req, unknown> {\n type Status = UseWebSocketStreamResult<Req, unknown>['status'];\n const [state, setState] = useState<{ value: unknown; status: Status; error: unknown }>(() => ({\n value: options.reduce ? options.initial : NO_REQUESTS,\n status: 'idle',\n error: undefined\n }));\n const latest = useLatest({ rpc, options });\n const running = useRef<AbortController | null>(null);\n\n const actions = useMemo(\n () => ({\n abort: () => running.current?.abort(),\n start(message: Req, requestOptions?: Omit<RequestOptions, 'signal'>) {\n running.current?.abort();\n const controller = (running.current = new AbortController());\n const { reduce, initial, batch } = latest.current.options;\n const fold = reduce ?? append;\n let value: unknown = reduce ? initial : NO_REQUESTS;\n let frame = false;\n // A stream that was started over no longer shows; ending also skips a pending frame.\n const show = (status: Status, error?: unknown) => {\n frame = false;\n if (running.current === controller) setState({ value, status, error });\n };\n show('streaming');\n void (async () => {\n try {\n const replies = latest.current.rpc!.stream(message, {\n ...requestOptions,\n signal: controller.signal\n });\n for await (const reply of replies) {\n value = fold(value, reply);\n if (batch !== 'frame') show('streaming');\n else if (!frame) {\n frame = true;\n nextFrame(() => frame && show('streaming'));\n }\n }\n show('done');\n } catch (error) {\n if (controller.signal.aborted) show('aborted');\n else show('error', error);\n }\n })();\n }\n }),\n [latest]\n );\n // Unmounting, or replies from another manager: stop this one's stream.\n useEffect(() => actions.abort, [actions, rpc]);\n useHold(rpc?.client ?? null, true);\n\n return { ...state, ...actions };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["createWebSocket","useRef","useId","useState","useSyncExternalStore","useMemo"],"sources":["../src/react.ts"],"sourcesContent":["import {\n useEffect,\n useId,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type RefObject\n} from 'react';\n// Import the client through the `core` entry: the build keeps it external, so both entries\n// share one copy of it (and of `MessageError`).\nimport { createWebSocket } from './core';\nimport type { AgUiAgent, AgUiThread } from './ag-ui';\nimport type { Latency, WebSocketLatency } from './latency';\nimport type { OutboxEntry, WebSocketOutbox } from './outbox';\nimport type { PresencePeer, WebSocketPresence } from './presence';\n// Types only: a value import from anything but `./core` would bundle a second copy into this entry.\nimport type { RequestOptions, WebSocketRequests } from './requests';\nimport type { IncomingRequest, WebSocketResponder } from './responder';\nimport type { AgentRun, WebSocketRuns } from './runs';\nimport type { WebSocketState } from './state';\nimport type { WebSocketTopics } from './topics';\nimport type {\n CloseInfo,\n EventMap,\n SendOptions,\n Snapshot,\n Status,\n WebSocketClient,\n WebSocketOptions\n} from './types';\n\n/**\n * Per-component event handlers. They always see the component's latest props and\n * state, and changing them never reconnects.\n */\nexport interface WebSocketHandlers<In> {\n /** The socket opened (initially or after a reconnect). */\n onOpen?: (event: Event) => void;\n /** A message arrived and passed `parse`/`json` and `schema`. */\n onMessage?: (message: In, event: MessageEvent) => void;\n /** A socket closed. Called whether or not it will be retried. */\n onClose?: (close: CloseInfo) => void;\n /** A socket error `Event`, a `MessageError`, or an `Error` from the constructor or `url` function. */\n onError?: (error: Event | Error) => void;\n /**\n * `false` releases this component's hold on the connection and stops its handlers.\n * The connection closes once nothing else holds it.\n * @default true\n */\n enabled?: boolean;\n}\n\n/** Options for `useWebSocket(url, options)`: connection options plus handlers. */\nexport interface UseWebSocketOptions<In, Out>\n extends WebSocketOptions<In, Out>, WebSocketHandlers<In> {}\n\n/** What `useWebSocket` returns: the current {@link Snapshot} plus stable actions. */\nexport interface UseWebSocketResult<In, Out> extends Snapshot<In> {\n /**\n * Send now, or queue until open (see {@link SendOptions} for volatile and keyed sends).\n * Returns `true` if sent immediately. Stable identity.\n */\n send: (message: Out, options?: SendOptions) => boolean;\n /** Close and stop reconnecting (for every component sharing the connection). Stable identity. */\n close: (code?: number, reason?: string) => void;\n /** Drop the socket and connect again right away. Stable identity. */\n reconnect: () => void;\n /** The underlying client, or `null` when the URL is `null`/`undefined`. */\n client: WebSocketClient<In, Out> | null;\n}\n\nconst IDLE: Snapshot<never> = Object.freeze({ status: 'idle', lastMessage: undefined, attempt: 0 });\nconst idleSnapshot = () => IDLE;\nconst idleStatus = (): Status => 'idle';\nconst NO_LATENCY: Latency = Object.freeze({ rtt: undefined, offset: undefined });\nconst noLatency = () => NO_LATENCY;\n// Not frozen: a call would keep it in bundles that never use the hook.\nconst NO_REQUESTS: readonly never[] = [];\nconst noRequests = () => NO_REQUESTS;\nconst noopSubscribe = () => () => {};\nconst noopSend = () => false;\nconst noop = () => {};\n\n// Same key as `ON_TEARDOWN` in ./client, which this entry must not import directly.\nconst ON_TEARDOWN = Symbol.for('react-websocket.onTeardown');\n\n// Hooks given the same URL (and protocols) share one connection: the client of the first\n// component to commit. It's registered from an effect, never during render, so a render React\n// throws away can't decide the connection's options or leave a client behind.\nconst registry = new Map<string, WebSocketClient<any, any>>();\n// Development only: where `react-websocket/devtools` finds the clients hooks share by URL.\nif (process.env.NODE_ENV !== 'production') {\n (globalThis as Record<symbol, unknown>)[Symbol.for('react-websocket.registry')] = registry;\n}\nconst registryListeners = new Set<() => void>();\n// A client started before any component using its URL committed: a render read its `ready`.\n// React retries a render that suspended on it without its state, so the retry would make a new\n// client and wait for that one instead, opening a socket on every attempt. It gets this one.\nconst started = new Map<string, WebSocketClient<any, any>>();\n\nfunction subscribeRegistry(listener: () => void) {\n registryListeners.add(listener);\n return () => {\n registryListeners.delete(listener);\n };\n}\n\nfunction registryChanged() {\n for (const listener of [...registryListeners]) listener();\n}\n\n// Development only: the options (and `useId` of the component) each URL client was created with,\n// to warn when another component passes different ones for the same URL.\nconst origins = new WeakMap<\n WebSocketClient<any, any>,\n { options: WebSocketOptions<any, any>; id: string }\n>();\nconst warned = new Set<string>();\n\n// The options that shape a shared connection, with what leaving one out means.\nconst CONNECTION_OPTIONS: { [K in keyof WebSocketOptions]-?: unknown } = {\n protocols: undefined, // part of the registry key\n binaryType: 'blob',\n json: false,\n parse: undefined,\n serialize: undefined,\n schema: undefined,\n reconnect: true,\n heartbeat: undefined,\n queue: true,\n logger: undefined, // debugging only\n WebSocket: undefined\n};\nconst IGNORED_OPTIONS = new Set(['protocols', 'logger']);\n\n// Inline functions and objects are new on every render: compare them by source and by field.\nconst sameValue = (a: unknown, b: unknown) =>\n Object.is(a, b) ||\n (typeof a === 'function' && typeof b === 'function' && String(a) === String(b));\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && Object.getPrototypeOf(value) === Object.prototype;\n\nfunction sameOption(a: unknown, b: unknown) {\n if (sameValue(a, b)) return true;\n if (!isPlainObject(a) || !isPlainObject(b)) return false;\n return [...Object.keys(a), ...Object.keys(b)].every(key => sameValue(a[key], b[key]));\n}\n\nfunction warnOnConflict(\n url: string,\n key: string,\n client: WebSocketClient<any, any>,\n options: WebSocketOptions<any, any>,\n id: string\n) {\n const origin = origins.get(client);\n if (!origin || origin.id === id || warned.has(`${key}\\u0000${id}`)) return;\n const differing = (Object.keys(CONNECTION_OPTIONS) as (keyof WebSocketOptions)[]).filter(\n name =>\n !IGNORED_OPTIONS.has(name) &&\n !sameOption(\n origin.options[name] ?? CONNECTION_OPTIONS[name],\n options[name] ?? CONNECTION_OPTIONS[name]\n )\n );\n if (!differing.length) return;\n warned.add(`${key}\\u0000${id}`);\n console.warn(\n `react-websocket: useWebSocket(\"${url}\") was given different ${differing.join(', ')} than ` +\n `the component that opened this shared connection. They are ignored: every component ` +\n `using the URL gets the first one's options. Pass the same options, or define the ` +\n `connection once with createWebSocket().`\n );\n}\n\n/** The client shared for `key`, or `own` until a component using it has committed. */\nconst shared_ = (key: string | null, own: WebSocketClient<any, any> | null) =>\n key === null ? null : (registry.get(key) ?? own);\n\n/** A component's own client for a URL. It becomes the shared one if the component commits first. */\nfunction ownClient<In, Out>(\n url: string,\n key: string,\n options: WebSocketOptions<In, Out>,\n id: string\n): WebSocketClient<In, Out> {\n const reused = started.get(key);\n if (reused) return reused;\n const created: WebSocketClient<In, Out> = createWebSocket(url, {\n ...options,\n [ON_TEARDOWN]: () => {\n if (started.get(key) === created) started.delete(key);\n if (registry.get(key) === created) {\n registry.delete(key);\n registryChanged();\n }\n }\n } as WebSocketOptions<In, Out>);\n const off = created.subscribe(() => {\n if (created.getSnapshot().status !== 'connecting') return;\n off();\n if (registry.get(key) !== created && !started.has(key)) started.set(key, created);\n });\n if (process.env.NODE_ENV !== 'production') origins.set(created, { options, id });\n return created;\n}\n\n/** Always-current value for use inside effects and event listeners (a `useEffectEvent` stand-in). */\nfunction useLatest<T>(value: T) {\n const ref = useRef(value);\n useInsertionEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n\nfunction useHold(client: WebSocketClient<any, any> | null, enabled: boolean) {\n useEffect(() => {\n if (client && enabled) return client.acquire();\n }, [client, enabled]);\n}\n\n/**\n * Connect to a WebSocket and re-render with its status and latest message.\n *\n * - With a URL, every component using the same URL (and `protocols`) shares one\n * connection. Its options come from the first of them to mount; in development, a\n * component passing different ones gets a console warning. A render React throws away\n * never decides them.\n * - With a client from `createWebSocket()`, options are the client's; only handlers\n * and `enabled` apply here.\n * - With `null` or `undefined`, nothing connects and the status stays `idle`.\n *\n * The connection opens on mount and closes one tick after the last component using it\n * unmounts, so StrictMode and `<Activity>` don't open duplicate sockets.\n *\n * Re-renders on status changes and on every message. For high-frequency streams use\n * `useWebSocketEvent` and `useWebSocketStatus` instead.\n *\n * @example\n * ```tsx\n * const { status, lastMessage, send } = useWebSocket<Price, Order>('wss://api.example.com/prices', {\n * json: true,\n * onMessage: price => console.log(price)\n * });\n * ```\n */\nexport function useWebSocket<In = unknown, Out = unknown>(\n url: string | null | undefined,\n options?: UseWebSocketOptions<In, Out>\n): UseWebSocketResult<In, Out>;\nexport function useWebSocket<In, Out>(\n client: WebSocketClient<In, Out> | null | undefined,\n options?: WebSocketHandlers<In>\n): UseWebSocketResult<In, Out>;\nexport function useWebSocket<In, Out>(\n target: string | WebSocketClient<In, Out> | null | undefined,\n options: UseWebSocketOptions<In, Out> = {}\n): UseWebSocketResult<In, Out> {\n const id = useId();\n const url = typeof target === 'string' ? target : null;\n const key = url === null ? null : `${url}\\u0000${JSON.stringify(options.protocols ?? null)}`;\n // This component's own client for the URL, made once per URL: connection options are read\n // when it's created, as for the shared client it may become.\n const make = () => ({ key, client: key === null ? null : ownClient(url!, key, options, id) });\n const [made, setMade] = useState(make);\n let own = made.client;\n if (made.key !== key) {\n const next = make();\n own = next.client;\n setMade(next);\n }\n // The shared client once a component using the URL has committed; this one's own until then.\n const shared = useSyncExternalStore(\n subscribeRegistry,\n () => shared_(key, own),\n () => own\n ) as WebSocketClient<In, Out> | null;\n const client =\n key === null ? ((target as WebSocketClient<In, Out> | null | undefined) ?? null) : shared;\n const enabled = options.enabled ?? true;\n if (process.env.NODE_ENV !== 'production' && key !== null) {\n warnOnConflict(url!, key, client!, options, id);\n }\n\n const snapshot = useSyncExternalStore<Snapshot<In>>(\n client ? client.subscribe : noopSubscribe,\n client ? client.getSnapshot : idleSnapshot,\n idleSnapshot\n );\n\n const handlers = useLatest(options);\n useEffect(() => {\n if (!client || !enabled) return;\n const offs = [\n client.on('open', event => handlers.current.onOpen?.(event)),\n client.on('message', (message, event) => handlers.current.onMessage?.(message, event)),\n client.on('close', info => handlers.current.onClose?.(info)),\n client.on('error', error => handlers.current.onError?.(error))\n ];\n return () => offs.forEach(off => off());\n }, [client, enabled, handlers]);\n useEffect(() => {\n if (key === null || !enabled) return;\n // The first component to commit shares its client. A component that rendered before then,\n // with its own, holds the shared one right away and re-renders with it.\n if (!registry.has(key)) {\n registry.set(key, own!);\n if (started.get(key) === own) started.delete(key);\n registryChanged();\n }\n return registry.get(key)!.acquire();\n }, [key, own, client, enabled]);\n useHold(key === null ? client : null, enabled);\n\n // For a URL, act on the shared client at call time, so a message sent from a mount effect\n // before this component re-renders with the shared client still reaches it.\n const actions = useMemo(\n () =>\n key === null\n ? null\n : {\n send: (message: Out, sendOptions?: SendOptions) =>\n shared_(key, own)!.send(message, sendOptions),\n close: (code?: number, reason?: string) => shared_(key, own)!.close(code, reason),\n reconnect: () => shared_(key, own)!.reconnect()\n },\n [key, own]\n );\n\n return {\n ...snapshot,\n send: actions ? actions.send : client ? client.send : noopSend,\n close: actions ? actions.close : client ? client.close : noop,\n reconnect: actions ? actions.reconnect : client ? client.reconnect : noop,\n client\n };\n}\n\n/**\n * Listen to a client's events without re-rendering the component. The listener always\n * sees the latest props and state. Keeps the connection open while mounted.\n *\n * @example\n * ```tsx\n * useWebSocketEvent(chat, 'message', message => setMessages(list => [...list, message]));\n * ```\n */\nexport function useWebSocketEvent<In, K extends keyof EventMap<In>>(\n client: WebSocketClient<In, any> | null | undefined,\n type: K,\n listener: EventMap<In>[K]\n): void {\n const latest = useLatest(listener);\n useEffect(() => {\n if (!client) return;\n const off = client.on(type, ((...args: unknown[]) =>\n (latest.current as (...a: unknown[]) => void)(...args)) as EventMap<In>[K]);\n const release = client.acquire();\n return () => {\n off();\n release();\n };\n }, [client, type, latest]);\n}\n\n/**\n * The connection status of a client. Re-renders only when the status changes, not on\n * messages. Keeps the connection open while mounted.\n *\n * @example\n * ```tsx\n * const status = useWebSocketStatus(chat); // 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed'\n * ```\n */\nexport function useWebSocketStatus(client: WebSocketClient<any, any> | null | undefined): Status {\n const status = useSyncExternalStore(\n client ? client.subscribe : noopSubscribe,\n client ? () => client.getSnapshot().status : idleStatus,\n idleStatus\n );\n useHold(client ?? null, true);\n return status;\n}\n\nconst identity = <T>(value: T) => value;\n\n/**\n * Read state built from messages by `createWebSocketState`. Re-renders only when the\n * selected value changes (by `isEqual`, default `Object.is`), however often messages\n * arrive. Keeps the connection open while mounted.\n *\n * On the server, and during hydration, it reads from `initialState`.\n *\n * @example\n * ```tsx\n * const acme = useWebSocketState(priceState, state => state.ACME);\n * ```\n */\nexport function useWebSocketState<S>(store: WebSocketState<S>): S;\nexport function useWebSocketState<S, T>(\n store: WebSocketState<S>,\n selector: (state: S) => T,\n isEqual?: (a: T, b: T) => boolean\n): T;\nexport function useWebSocketState<S, T>(\n store: WebSocketState<S>,\n selector: (state: S) => T = identity as (state: S) => T,\n isEqual: (a: T, b: T) => boolean = Object.is\n): T {\n const [getSelection, getServerSelection] = useMemo(() => {\n // Return the previous selection while the state is unchanged or the result is equal,\n // so inline selectors that build objects don't re-render (or loop) on every message.\n const memoized = (read: () => S) => {\n let memo: { state: S; selection: T } | undefined;\n return () => {\n const state = read();\n if (memo && Object.is(memo.state, state)) return memo.selection;\n const selection = selector(state);\n if (memo && isEqual(memo.selection, selection)) {\n memo = { state, selection: memo.selection };\n return memo.selection;\n }\n memo = { state, selection };\n return selection;\n };\n };\n return [memoized(store.getState), memoized(() => store.initialState)] as const;\n }, [store, selector, isEqual]);\n\n const selection = useSyncExternalStore(store.subscribe, getSelection, getServerSelection);\n useHold(store.client, true);\n return selection;\n}\n\n/**\n * Subscribe to one topic (or several) while mounted, through a `createWebSocketTopics`\n * manager. Components holding the same topic share one subscription, and it is sent again\n * after every reconnect. Keeps the connection open while mounted. `null` holds nothing.\n *\n * @example\n * ```tsx\n * useWebSocketTopic(topics, `prices:${symbol}`);\n * useWebSocketTopic(topics, watchlist.map(symbol => `prices:${symbol}`));\n * ```\n */\nexport function useWebSocketTopic(\n topics: WebSocketTopics | null | undefined,\n topic: string | readonly string[] | null | undefined\n): void {\n // A stable key, so a new array with the same topics doesn't resubscribe.\n const key = topic == null ? null : JSON.stringify(typeof topic === 'string' ? [topic] : topic);\n useEffect(() => {\n if (!topics || key === null) return;\n const releases = (JSON.parse(key) as string[]).map(t => topics.acquire(t));\n return () => releases.forEach(release => release());\n }, [topics, key]);\n}\n\n/**\n * Read round-trip time and server clock offset from a `createWebSocketLatency` instance.\n * Re-renders when a measurement changes, about once per ping interval. Keeps the connection\n * open while mounted. On the server, and during hydration, both values are `undefined`.\n *\n * @example\n * ```tsx\n * const { rtt } = useWebSocketLatency(latency);\n * return <span>{rtt === undefined ? '–' : `${Math.round(rtt)} ms`}</span>;\n * ```\n */\nexport function useWebSocketLatency(latency: WebSocketLatency): Latency {\n const snapshot = useSyncExternalStore(latency.subscribe, latency.getSnapshot, noLatency);\n useHold(latency.client, true);\n return snapshot;\n}\n\n/**\n * The messages waiting in an outbox from `react-websocket/outbox`, oldest first, for a \"3 orders\n * waiting to sync\" banner. Re-renders when they change. `null` lists nothing. On the server the\n * list is empty.\n *\n * @example\n * ```tsx\n * const waiting = useWebSocketOutbox(orders);\n * if (!waiting.length) return null;\n * return <p role=\"status\">{waiting.length} orders waiting to sync</p>;\n * ```\n */\nexport function useWebSocketOutbox<M>(\n outbox: WebSocketOutbox<M> | null | undefined\n): readonly OutboxEntry<M>[] {\n return useSyncExternalStore(\n outbox?.subscribe ?? noopSubscribe,\n outbox?.getSnapshot ?? noRequests,\n noRequests\n );\n}\n\nconst noRun = () => undefined;\n\n/**\n * Whether an element is on screen, from an `IntersectionObserver`, for dashboards that stream\n * full detail only for what's visible: pick a topic with it and pass it to `useWebSocketTopic`,\n * which switches subscriptions when it changes. `initial` until the first observation, on the\n * server, and where `IntersectionObserver` doesn't exist.\n *\n * @example\n * ```tsx\n * const ref = useRef<HTMLElement>(null);\n * useWebSocketTopic(feeds, useInView(ref) ? `run:${id}:tokens` : `run:${id}:status`);\n * return <article ref={ref}>…</article>;\n * ```\n */\nexport function useInView(\n ref: RefObject<Element | null>,\n options: { rootMargin?: string; threshold?: number; initial?: boolean } = {}\n): boolean {\n const { rootMargin, threshold } = options;\n const [inView, setInView] = useState(options.initial ?? false);\n useEffect(() => {\n const element = ref.current;\n if (!element || typeof IntersectionObserver === 'undefined') return;\n const observer = new IntersectionObserver(\n entries => setInView(entries.at(-1)!.isIntersecting),\n { rootMargin, threshold }\n );\n observer.observe(element);\n return () => observer.disconnect();\n }, [ref, rootMargin, threshold]);\n return inView;\n}\n\n/**\n * The other peers' shared state from `createWebSocketPresence`, such as their cursors.\n * Re-renders when a peer's state arrives, it leaves, or it times out. Keeps the connection open\n * while mounted. On the server the list is empty.\n *\n * @example\n * ```tsx\n * const peers = useWebSocketPresence(cursors);\n * return peers.map(({ peer, state }) => <Cursor key={peer} x={state.x} y={state.y} />);\n * ```\n */\nexport function useWebSocketPresence<P>(\n presence: WebSocketPresence<P> | null | undefined\n): readonly PresencePeer<P>[] {\n const peers = useSyncExternalStore(\n presence?.subscribe ?? noopSubscribe,\n presence?.getSnapshot ?? noRequests,\n noRequests\n );\n useHold(presence?.client ?? null, true);\n return peers;\n}\n\n/**\n * The thread of an AG-UI agent from `react-websocket/ag-ui`: its messages, shared state and\n * status, re-rendering on every event.\n *\n * @example\n * ```tsx\n * const thread = useAgUiAgent(agent);\n * return thread.messages.map(message => <Message key={message.id} message={message} />);\n * ```\n */\nexport function useAgUiAgent<S>(agent: AgUiAgent<S>): AgUiThread<S> {\n return useSyncExternalStore(agent.subscribe, agent.getSnapshot, agent.getSnapshot);\n}\n\n/**\n * A run from `createWebSocketRuns`, such as an agent's answer: its `value` reduced from the\n * events so far, and its `status`. Re-renders on every event. A run it doesn't know, such as one\n * started in another tab, is attached and followed from its first event. `undefined` while the\n * run is unknown, for a `null` id, and on the server.\n *\n * @example\n * ```tsx\n * const run = useAgentRun(runs, runId);\n * return (\n * <>\n * <Markdown>{run?.value ?? ''}</Markdown>\n * {run?.status === 'streaming' && <button onClick={() => runs.abort(runId)}>Stop</button>}\n * </>\n * );\n * ```\n */\nexport function useAgentRun<T>(\n runs: WebSocketRuns<any, T> | null | undefined,\n runId: string | null | undefined\n): AgentRun<T> | undefined {\n const run = useSyncExternalStore(\n runs?.subscribe ?? noopSubscribe,\n () => (runs && runId ? runs.get(runId) : undefined),\n noRun\n );\n useEffect(() => {\n if (runs && runId) runs.attach(runId);\n }, [runs, runId]);\n return run;\n}\n\n/**\n * Requests for `method` that a person answers, e.g. an agent asking to run a tool. While mounted,\n * it registers `method` on the responder without a handler, so new requests wait here instead of\n * getting a `NotFoundError` reply. Call `respond` or `reject` on one to answer it. Re-renders\n * when the list changes. Keeps the connection open while mounted. `null` holds nothing.\n *\n * Requests stay listed after unmount until they are answered, time out, are cancelled or lose\n * their connection, so a remounted dialog shows them again. On the server the list is empty.\n *\n * @example\n * ```tsx\n * const [request] = useAgentApproval(tools, 'approve');\n * if (!request) return null;\n * return (\n * <dialog open>\n * {request.params.summary}\n * <button onClick={() => request.respond({ approved: true })}>Approve</button>\n * <button onClick={() => request.respond({ approved: false })}>Deny</button>\n * </dialog>\n * );\n * ```\n */\nexport function useAgentApproval<P, R>(\n responder: WebSocketResponder<P, R> | null | undefined,\n method: string | null | undefined\n): readonly IncomingRequest<P, R>[] {\n const waiting = useSyncExternalStore(\n responder ? responder.subscribe : noopSubscribe,\n responder ? responder.getSnapshot : noRequests,\n noRequests\n );\n useEffect(() => {\n if (responder && method != null) return responder.onRequest(method);\n }, [responder, method]);\n useHold(responder?.client ?? null, method != null);\n return useMemo(() => {\n const mine = waiting.filter(request => request.method === method);\n return mine.length ? mine : NO_REQUESTS;\n }, [waiting, method]);\n}\n\n/** Options for `useWebSocketStream` that fold replies into one value, e.g. tokens into text. */\nexport interface UseWebSocketStreamOptions<In, T> {\n /** The value before the first reply. `start()` resets to it. */\n initial: T;\n /** Fold a reply into the value. Return a new value instead of changing the old one. */\n reduce: (value: T, reply: In) => T;\n /**\n * `'frame'`: re-render at most once per animation frame instead of once per reply. Browsers\n * pause frames in background tabs; the value catches up when the tab is visible again.\n */\n batch?: 'frame';\n}\n\n/** What `useWebSocketStream` returns: the stream's progress plus stable actions. */\nexport interface UseWebSocketStreamResult<Req, T> {\n /** The replies so far, folded with `reduce`, or as an array without it. */\n readonly value: T;\n /**\n * `idle` until `start()`. `done` after the last reply, `error` when the stream failed (see\n * `error`), `aborted` after `abort()`. The value stays as it was when the stream ended.\n */\n readonly status: 'idle' | 'streaming' | 'done' | 'error' | 'aborted';\n /** Why the stream failed, while `status` is `error`. */\n readonly error: unknown;\n /** Reset the value and stream the replies to `message`, aborting the stream before. Stable identity. */\n start: (message: Req, options?: Omit<RequestOptions, 'signal'>) => void;\n /** Stop the stream and send the manager's `cancel`. Stable identity. */\n abort: () => void;\n}\n\nconst append = <T>(list: readonly T[], reply: T): readonly T[] => [...list, reply];\n\n// Node and some workers have no animation frames: wait about one instead.\nfunction nextFrame(callback: () => void) {\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(callback);\n else setTimeout(callback, 16);\n}\n\n/**\n * Stream the replies to a request into a value, e.g. a model's tokens into its answer.\n * `start(message)` sends it with `rpc.stream()` and folds each reply in with `reduce`, or\n * collects the replies without it. Starting again aborts the stream before; so do `abort()`,\n * unmounting and a new `rpc`, and each sends the manager's `cancel`. Re-renders on every\n * reply, or once per frame with `batch: 'frame'`. Keeps the connection open while mounted.\n *\n * @example\n * ```tsx\n * const answer = useWebSocketStream(ask, {\n * initial: '',\n * reduce: (text, reply) => text + reply.delta,\n * batch: 'frame'\n * });\n * return (\n * <>\n * <button onClick={() => answer.start({ prompt })}>Ask</button>\n * <button onClick={answer.abort} disabled={answer.status !== 'streaming'}>Stop</button>\n * <p>{answer.value}</p>\n * </>\n * );\n * ```\n */\nexport function useWebSocketStream<Req, In, T>(\n rpc: WebSocketRequests<Req, In> | null | undefined,\n options: UseWebSocketStreamOptions<NoInfer<In>, T>\n): UseWebSocketStreamResult<Req, T>;\nexport function useWebSocketStream<Req, In>(\n rpc: WebSocketRequests<Req, In> | null | undefined,\n options?: { batch?: 'frame' }\n): UseWebSocketStreamResult<Req, readonly In[]>;\nexport function useWebSocketStream<Req, In>(\n rpc: WebSocketRequests<Req, In> | null | undefined,\n options: Partial<UseWebSocketStreamOptions<In, any>> = {}\n): UseWebSocketStreamResult<Req, unknown> {\n type Status = UseWebSocketStreamResult<Req, unknown>['status'];\n const [state, setState] = useState<{ value: unknown; status: Status; error: unknown }>(() => ({\n value: options.reduce ? options.initial : NO_REQUESTS,\n status: 'idle',\n error: undefined\n }));\n const latest = useLatest({ rpc, options });\n const running = useRef<AbortController | null>(null);\n\n const actions = useMemo(\n () => ({\n abort: () => running.current?.abort(),\n start(message: Req, requestOptions?: Omit<RequestOptions, 'signal'>) {\n running.current?.abort();\n const controller = (running.current = new AbortController());\n const { reduce, initial, batch } = latest.current.options;\n const fold = reduce ?? append;\n let value: unknown = reduce ? initial : NO_REQUESTS;\n let frame = false;\n // A stream that was started over no longer shows; ending also skips a pending frame.\n const show = (status: Status, error?: unknown) => {\n frame = false;\n if (running.current === controller) setState({ value, status, error });\n };\n show('streaming');\n void (async () => {\n try {\n const replies = latest.current.rpc!.stream(message, {\n ...requestOptions,\n signal: controller.signal\n });\n for await (const reply of replies) {\n value = fold(value, reply);\n if (batch !== 'frame') show('streaming');\n else if (!frame) {\n frame = true;\n nextFrame(() => frame && show('streaming'));\n }\n }\n show('done');\n } catch (error) {\n if (controller.signal.aborted) show('aborted');\n else show('error', error);\n }\n })();\n }\n }),\n [latest]\n );\n // Unmounting, or replies from another manager: stop this one's stream.\n useEffect(() => actions.abort, [actions, rpc]);\n useHold(rpc?.client ?? null, true);\n\n return { ...state, ...actions };\n}\n"],"mappings":"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"}
|