@tanstack/ai-client 0.23.2 → 0.24.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 +10 -1
- package/dist/esm/chat-client.d.ts +6 -8
- package/dist/esm/chat-client.js +32 -18
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/connection-adapters.d.ts +49 -0
- package/dist/esm/connection-adapters.js +266 -26
- package/dist/esm/connection-adapters.js.map +1 -1
- package/dist/esm/devtools.d.ts +0 -1
- package/dist/esm/devtools.js +11 -14
- package/dist/esm/devtools.js.map +1 -1
- package/dist/esm/generation-client.d.ts +5 -11
- package/dist/esm/generation-client.js +18 -6
- package/dist/esm/generation-client.js.map +1 -1
- package/dist/esm/generation-types.d.ts +15 -45
- package/dist/esm/generation-types.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/types.d.ts +27 -36
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/video-generation-client.d.ts +5 -11
- package/dist/esm/video-generation-client.js +18 -6
- package/dist/esm/video-generation-client.js.map +1 -1
- package/package.json +5 -5
- package/src/chat-client.ts +52 -37
- package/src/connection-adapters.ts +491 -45
- package/src/devtools.ts +6 -22
- package/src/generation-client.ts +36 -24
- package/src/generation-types.ts +15 -47
- package/src/index.ts +3 -0
- package/src/types.ts +34 -39
- package/src/video-generation-client.ts +33 -20
|
@@ -49,6 +49,32 @@ export interface ReconnectOptions {
|
|
|
49
49
|
/** Delay between reconnect attempts, in ms, to avoid hammering. Default 250. */
|
|
50
50
|
delayMs?: number;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Reconnect bookkeeping shared by every resumable-stream driver: de-dupes
|
|
54
|
+
* offsets, tracks the last acknowledged offset, honors the SSE empty-id reset
|
|
55
|
+
* convention, and bounds consecutive no-progress reconnects behind a
|
|
56
|
+
* throttling delay. Extracted out of {@link resumableStream} so a WebSocket
|
|
57
|
+
* reconnect driver can reuse the exact same semantics.
|
|
58
|
+
*/
|
|
59
|
+
export interface ReconnectTracker {
|
|
60
|
+
/** The most recently accepted (non-duplicate, non-empty) offset, if any. */
|
|
61
|
+
readonly lastEventId: string | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Record an incoming offset. Returns `'reset'` for an empty id (SSE's
|
|
64
|
+
* resume-cursor reset — clears the de-dupe set and `lastEventId`),
|
|
65
|
+
* `'duplicate'` for an already-seen id, and `'new'` otherwise (including
|
|
66
|
+
* `undefined`, which is untracked — no offset to remember).
|
|
67
|
+
*/
|
|
68
|
+
note: (id: string | undefined) => 'new' | 'duplicate' | 'reset';
|
|
69
|
+
/**
|
|
70
|
+
* Throttle before a reconnect attempt. Resets the no-progress counter when
|
|
71
|
+
* `madeProgress` is true; otherwise increments it and throws
|
|
72
|
+
* {@link StreamReconnectLimitError} once it exceeds the configured ceiling.
|
|
73
|
+
*/
|
|
74
|
+
waitBeforeReconnect: (madeProgress: boolean, signal?: AbortSignal) => Promise<void>;
|
|
75
|
+
}
|
|
76
|
+
/** Create a {@link ReconnectTracker} bound to the given reconnect bounds. */
|
|
77
|
+
export declare function createReconnectTracker(options?: ReconnectOptions): ReconnectTracker;
|
|
52
78
|
/**
|
|
53
79
|
* Per-send context provided by the chat client to the connection adapter.
|
|
54
80
|
* The adapter combines this with serialized messages to build a full
|
|
@@ -327,6 +353,29 @@ export declare function xhrServerSentEvents(url: string | (() => string), option
|
|
|
327
353
|
* (bare-line) response is a single plain request, exactly as before.
|
|
328
354
|
*/
|
|
329
355
|
export declare function xhrHttpStream(url: string | (() => string), options?: XhrConnectionOptionsResolver): ResumableConnectConnectionAdapter;
|
|
356
|
+
export interface WebSocketConnectionOptions {
|
|
357
|
+
protocols?: string | Array<string>;
|
|
358
|
+
body?: Record<string, unknown>;
|
|
359
|
+
reconnect?: ReconnectOptions;
|
|
360
|
+
/** Override the WebSocket implementation (tests / non-browser runtimes). */
|
|
361
|
+
WebSocketImpl?: typeof WebSocket;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Full-duplex, conversation-scoped WebSocket connection adapter. Pairs with the
|
|
365
|
+
* server `toWebSocketResponse` / `toWebSocketStream`. `send()` writes a
|
|
366
|
+
* RunAgentInput frame; `subscribe()` yields inbound chunks.
|
|
367
|
+
*
|
|
368
|
+
* Resumable: `send()` establishes a run session backed by a
|
|
369
|
+
* {@link createReconnectTracker}. If the socket closes before a terminal
|
|
370
|
+
* (`RUN_FINISHED`/`RUN_ERROR`) chunk is seen and the run is durable
|
|
371
|
+
* (offset-tagged `{ id, chunk }` envelopes), the socket is reopened at
|
|
372
|
+
* `?runId=&offset=<lastEventId>`, de-duping the replayed boundary. A drop with
|
|
373
|
+
* no offset ever observed (non-durable) surfaces {@link StreamReadError}
|
|
374
|
+
* instead of reconnecting — there is nothing to resume from.
|
|
375
|
+
*/
|
|
376
|
+
export declare function webSocket(url: string | (() => string), options?: WebSocketConnectionOptions): SubscribeConnectionAdapter & {
|
|
377
|
+
joinRun: (runId: string, abortSignal?: AbortSignal) => AsyncIterable<StreamChunk>;
|
|
378
|
+
};
|
|
330
379
|
/**
|
|
331
380
|
* Optional persistence handlers for the lightweight adapters (`stream()`,
|
|
332
381
|
* `rpcStream()`). These are one-shot, request-scoped calls with no built-in
|
|
@@ -68,6 +68,38 @@ function resolveReconnectOptions(options) {
|
|
|
68
68
|
delayMs
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
|
+
/** Create a {@link ReconnectTracker} bound to the given reconnect bounds. */
|
|
72
|
+
function createReconnectTracker(options) {
|
|
73
|
+
const reconnect = resolveReconnectOptions(options);
|
|
74
|
+
const seen = /* @__PURE__ */ new Set();
|
|
75
|
+
let lastEventId;
|
|
76
|
+
let reconnectAttempts = 0;
|
|
77
|
+
return {
|
|
78
|
+
get lastEventId() {
|
|
79
|
+
return lastEventId;
|
|
80
|
+
},
|
|
81
|
+
note(id) {
|
|
82
|
+
if (id === void 0) return "new";
|
|
83
|
+
if (id === "") {
|
|
84
|
+
lastEventId = void 0;
|
|
85
|
+
seen.clear();
|
|
86
|
+
return "reset";
|
|
87
|
+
}
|
|
88
|
+
if (seen.has(id)) return "duplicate";
|
|
89
|
+
seen.add(id);
|
|
90
|
+
lastEventId = id;
|
|
91
|
+
return "new";
|
|
92
|
+
},
|
|
93
|
+
async waitBeforeReconnect(madeProgress, signal) {
|
|
94
|
+
if (madeProgress) reconnectAttempts = 0;
|
|
95
|
+
else {
|
|
96
|
+
reconnectAttempts += 1;
|
|
97
|
+
if (reconnectAttempts > reconnect.maxAttempts) throw new StreamReconnectLimitError(reconnect.maxAttempts);
|
|
98
|
+
}
|
|
99
|
+
await abortableDelay(reconnect.delayMs, signal);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
71
103
|
/** Resolve after `ms`, or immediately once `signal` aborts. Never rejects. */
|
|
72
104
|
function abortableDelay(ms, signal) {
|
|
73
105
|
if (ms <= 0 || signal?.aborted) return Promise.resolve();
|
|
@@ -350,50 +382,32 @@ function fetchEventSource(fetchClient, url, requestInit, parseResponse) {
|
|
|
350
382
|
* only difference being the {@link StreamEventSource} they pass in.
|
|
351
383
|
*/
|
|
352
384
|
async function* resumableStream(openEventSource, abortSignal, reconnectOptions) {
|
|
353
|
-
const
|
|
354
|
-
let lastEventId;
|
|
355
|
-
const reconnect = resolveReconnectOptions(reconnectOptions);
|
|
356
|
-
let reconnectAttempts = 0;
|
|
357
|
-
async function waitBeforeReconnect(madeProgress) {
|
|
358
|
-
if (madeProgress) reconnectAttempts = 0;
|
|
359
|
-
else {
|
|
360
|
-
reconnectAttempts += 1;
|
|
361
|
-
if (reconnectAttempts > reconnect.maxAttempts) throw new StreamReconnectLimitError(reconnect.maxAttempts);
|
|
362
|
-
}
|
|
363
|
-
await abortableDelay(reconnect.delayMs, abortSignal);
|
|
364
|
-
}
|
|
385
|
+
const tracker = createReconnectTracker(reconnectOptions);
|
|
365
386
|
for (;;) {
|
|
366
387
|
if (abortSignal?.aborted) return;
|
|
367
|
-
const extraHeaders = lastEventId !== void 0 ? { "Last-Event-ID": lastEventId } : {};
|
|
388
|
+
const extraHeaders = tracker.lastEventId !== void 0 ? { "Last-Event-ID": tracker.lastEventId } : {};
|
|
368
389
|
let sawTerminal = false;
|
|
369
390
|
let progressed = false;
|
|
370
391
|
try {
|
|
371
392
|
for await (const { chunk, id } of openEventSource(extraHeaders, abortSignal)) {
|
|
372
|
-
if (id
|
|
373
|
-
lastEventId = void 0;
|
|
374
|
-
seen.clear();
|
|
375
|
-
} else {
|
|
376
|
-
if (seen.has(id)) continue;
|
|
377
|
-
seen.add(id);
|
|
378
|
-
lastEventId = id;
|
|
379
|
-
}
|
|
393
|
+
if (tracker.note(id) === "duplicate") continue;
|
|
380
394
|
progressed = true;
|
|
381
395
|
if (chunk.type === "RUN_FINISHED" || chunk.type === "RUN_ERROR") sawTerminal = true;
|
|
382
396
|
yield chunk;
|
|
383
397
|
}
|
|
384
398
|
} catch (error) {
|
|
385
399
|
if (abortSignal?.aborted) return;
|
|
386
|
-
if ((error instanceof StreamTruncatedError || error instanceof StreamReadError) && lastEventId !== void 0) {
|
|
387
|
-
await waitBeforeReconnect(progressed);
|
|
400
|
+
if ((error instanceof StreamTruncatedError || error instanceof StreamReadError) && tracker.lastEventId !== void 0) {
|
|
401
|
+
await tracker.waitBeforeReconnect(progressed, abortSignal);
|
|
388
402
|
continue;
|
|
389
403
|
}
|
|
390
404
|
throw error;
|
|
391
405
|
}
|
|
392
406
|
if (abortSignal?.aborted) return;
|
|
393
407
|
if (sawTerminal) return;
|
|
394
|
-
if (lastEventId !== void 0) {
|
|
408
|
+
if (tracker.lastEventId !== void 0) {
|
|
395
409
|
if (progressed) {
|
|
396
|
-
await waitBeforeReconnect(true);
|
|
410
|
+
await tracker.waitBeforeReconnect(true, abortSignal);
|
|
397
411
|
continue;
|
|
398
412
|
}
|
|
399
413
|
throw new DurableStreamIncompleteError();
|
|
@@ -900,6 +914,232 @@ function xhrHttpStream(url, options = {}) {
|
|
|
900
914
|
}
|
|
901
915
|
};
|
|
902
916
|
}
|
|
917
|
+
function runIdQuery(url, runId) {
|
|
918
|
+
return runId ? withSearchParams(url, { runId }) : url;
|
|
919
|
+
}
|
|
920
|
+
function isPingFrame(parsed) {
|
|
921
|
+
return typeof parsed === "object" && parsed !== null && parsed.type === "ping";
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* A push→pull bridge from socket callbacks to an async iterable: chunks queue
|
|
925
|
+
* until the consumer pulls, a recorded failure rejects the iterator, and
|
|
926
|
+
* `end()` (or the abort signal) finishes it cleanly. Shared by `webSocket()`'s
|
|
927
|
+
* `subscribe()` and `joinRun()`.
|
|
928
|
+
*/
|
|
929
|
+
function createChunkPipe(abortSignal, onFinally) {
|
|
930
|
+
const queue = [];
|
|
931
|
+
const waiters = [];
|
|
932
|
+
let failure;
|
|
933
|
+
let ended = false;
|
|
934
|
+
const wake = () => waiters.shift()?.(null);
|
|
935
|
+
const push = (chunk) => {
|
|
936
|
+
const w = waiters.shift();
|
|
937
|
+
if (w) w(chunk);
|
|
938
|
+
else queue.push(chunk);
|
|
939
|
+
};
|
|
940
|
+
const fail = (error) => {
|
|
941
|
+
failure = error;
|
|
942
|
+
wake();
|
|
943
|
+
};
|
|
944
|
+
const end = () => {
|
|
945
|
+
ended = true;
|
|
946
|
+
wake();
|
|
947
|
+
};
|
|
948
|
+
const onAbort = () => wake();
|
|
949
|
+
abortSignal?.addEventListener("abort", onAbort);
|
|
950
|
+
return {
|
|
951
|
+
push,
|
|
952
|
+
fail,
|
|
953
|
+
end,
|
|
954
|
+
iterable: (async function* () {
|
|
955
|
+
try {
|
|
956
|
+
while (!abortSignal?.aborted) {
|
|
957
|
+
const buffered = queue.shift();
|
|
958
|
+
if (buffered !== void 0) {
|
|
959
|
+
yield buffered;
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
if (failure !== void 0) throw failure;
|
|
963
|
+
if (ended) return;
|
|
964
|
+
const chunk = await new Promise((r) => waiters.push(r));
|
|
965
|
+
if (failure !== void 0) throw failure;
|
|
966
|
+
if (chunk === null) return;
|
|
967
|
+
yield chunk;
|
|
968
|
+
}
|
|
969
|
+
} finally {
|
|
970
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
971
|
+
onFinally();
|
|
972
|
+
}
|
|
973
|
+
})()
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Full-duplex, conversation-scoped WebSocket connection adapter. Pairs with the
|
|
978
|
+
* server `toWebSocketResponse` / `toWebSocketStream`. `send()` writes a
|
|
979
|
+
* RunAgentInput frame; `subscribe()` yields inbound chunks.
|
|
980
|
+
*
|
|
981
|
+
* Resumable: `send()` establishes a run session backed by a
|
|
982
|
+
* {@link createReconnectTracker}. If the socket closes before a terminal
|
|
983
|
+
* (`RUN_FINISHED`/`RUN_ERROR`) chunk is seen and the run is durable
|
|
984
|
+
* (offset-tagged `{ id, chunk }` envelopes), the socket is reopened at
|
|
985
|
+
* `?runId=&offset=<lastEventId>`, de-duping the replayed boundary. A drop with
|
|
986
|
+
* no offset ever observed (non-durable) surfaces {@link StreamReadError}
|
|
987
|
+
* instead of reconnecting — there is nothing to resume from.
|
|
988
|
+
*/
|
|
989
|
+
function webSocket(url, options = {}) {
|
|
990
|
+
const Impl = options.WebSocketImpl ?? WebSocket;
|
|
991
|
+
let socket;
|
|
992
|
+
let socketMode;
|
|
993
|
+
let openPromise;
|
|
994
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
995
|
+
let currentSession;
|
|
996
|
+
function failAll(error) {
|
|
997
|
+
for (const l of listeners) l.fail(error);
|
|
998
|
+
}
|
|
999
|
+
function openOnce(target, mode) {
|
|
1000
|
+
if (socket && socket.readyState <= 1 && mode === "run" && socketMode === "run") return socket;
|
|
1001
|
+
const prior = socket;
|
|
1002
|
+
const ws = options.protocols ? new Impl(target, options.protocols) : new Impl(target);
|
|
1003
|
+
socket = ws;
|
|
1004
|
+
socketMode = mode;
|
|
1005
|
+
openPromise = new Promise((resolve, reject) => {
|
|
1006
|
+
ws.onopen = () => resolve();
|
|
1007
|
+
ws.onerror = (e) => reject(new StreamReadError(e));
|
|
1008
|
+
});
|
|
1009
|
+
openPromise.catch(() => {});
|
|
1010
|
+
ws.onmessage = (event) => {
|
|
1011
|
+
if (ws !== socket) return;
|
|
1012
|
+
let parsed;
|
|
1013
|
+
try {
|
|
1014
|
+
parsed = JSON.parse(String(event.data));
|
|
1015
|
+
} catch (error) {
|
|
1016
|
+
failAll(new StreamReadError(error));
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
if (isPingFrame(parsed)) return;
|
|
1020
|
+
const envelopeId = isNdjsonEnvelope(parsed) ? parsed.id : void 0;
|
|
1021
|
+
const chunk = isNdjsonEnvelope(parsed) ? parsed.chunk : parsed;
|
|
1022
|
+
const session = currentSession;
|
|
1023
|
+
if (session) {
|
|
1024
|
+
if (session.tracker.note(envelopeId) === "duplicate") return;
|
|
1025
|
+
session.progressed = true;
|
|
1026
|
+
if (session.runId === void 0) session.runId = getChunkRunId(chunk);
|
|
1027
|
+
if (chunk.type === "RUN_FINISHED" || chunk.type === "RUN_ERROR") session.sawTerminal = true;
|
|
1028
|
+
}
|
|
1029
|
+
for (const l of listeners) l.push(chunk);
|
|
1030
|
+
};
|
|
1031
|
+
ws.onclose = () => {
|
|
1032
|
+
if (ws !== socket) return;
|
|
1033
|
+
const session = currentSession;
|
|
1034
|
+
if (!session) {
|
|
1035
|
+
failAll(new StreamReadError(/* @__PURE__ */ new Error("WebSocket connection closed")));
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
if (session.signal?.aborted || session.sawTerminal) return;
|
|
1039
|
+
const lastEventId = session.tracker.lastEventId;
|
|
1040
|
+
if (lastEventId === void 0) {
|
|
1041
|
+
currentSession = void 0;
|
|
1042
|
+
failAll(new StreamReadError(/* @__PURE__ */ new Error("WebSocket connection closed")));
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
reconnect(session, lastEventId);
|
|
1046
|
+
};
|
|
1047
|
+
if (prior && prior.readyState <= 1) prior.close();
|
|
1048
|
+
return ws;
|
|
1049
|
+
}
|
|
1050
|
+
async function reconnect(session, offset) {
|
|
1051
|
+
try {
|
|
1052
|
+
await session.tracker.waitBeforeReconnect(session.progressed, session.signal);
|
|
1053
|
+
} catch (error) {
|
|
1054
|
+
if (currentSession === session) currentSession = void 0;
|
|
1055
|
+
failAll(error);
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
if (session.signal?.aborted) return;
|
|
1059
|
+
if (currentSession !== session) return;
|
|
1060
|
+
if (socket && socket.readyState <= 1) return;
|
|
1061
|
+
session.progressed = false;
|
|
1062
|
+
openOnce(withSearchParams(typeof url === "function" ? url() : url, {
|
|
1063
|
+
...session.runId !== void 0 ? { runId: session.runId } : {},
|
|
1064
|
+
offset
|
|
1065
|
+
}), "resume");
|
|
1066
|
+
}
|
|
1067
|
+
function waitOpen(ws) {
|
|
1068
|
+
if (ws.readyState === 1) return Promise.resolve();
|
|
1069
|
+
return openPromise ?? Promise.resolve();
|
|
1070
|
+
}
|
|
1071
|
+
return {
|
|
1072
|
+
subscribe(abortSignal) {
|
|
1073
|
+
const pipe = createChunkPipe(abortSignal, () => listeners.delete(sink));
|
|
1074
|
+
const sink = {
|
|
1075
|
+
push: pipe.push,
|
|
1076
|
+
fail: pipe.fail
|
|
1077
|
+
};
|
|
1078
|
+
listeners.add(sink);
|
|
1079
|
+
return pipe.iterable;
|
|
1080
|
+
},
|
|
1081
|
+
async send(messages, data, abortSignal, runContext) {
|
|
1082
|
+
const ws = openOnce(runIdQuery(typeof url === "function" ? url() : url, runContext?.runId), "run");
|
|
1083
|
+
await waitOpen(ws);
|
|
1084
|
+
if (!currentSession || currentSession.runId !== runContext?.runId) currentSession = {
|
|
1085
|
+
runId: runContext?.runId,
|
|
1086
|
+
tracker: createReconnectTracker(options.reconnect),
|
|
1087
|
+
sawTerminal: false,
|
|
1088
|
+
progressed: false,
|
|
1089
|
+
signal: abortSignal
|
|
1090
|
+
};
|
|
1091
|
+
else {
|
|
1092
|
+
currentSession.signal = abortSignal;
|
|
1093
|
+
currentSession.sawTerminal = false;
|
|
1094
|
+
currentSession.progressed = false;
|
|
1095
|
+
}
|
|
1096
|
+
const session = currentSession;
|
|
1097
|
+
abortSignal?.addEventListener("abort", () => {
|
|
1098
|
+
const abortRunId = session.runId;
|
|
1099
|
+
const live = socket;
|
|
1100
|
+
if (abortRunId === void 0 || session.sawTerminal || socketMode !== "run" || live === void 0 || live.readyState !== 1) return;
|
|
1101
|
+
try {
|
|
1102
|
+
live.send(JSON.stringify({
|
|
1103
|
+
type: "abort",
|
|
1104
|
+
runId: abortRunId
|
|
1105
|
+
}));
|
|
1106
|
+
} catch {}
|
|
1107
|
+
}, { once: true });
|
|
1108
|
+
const body = buildRunAgentInputBody(messages, data, runContext, { body: options.body });
|
|
1109
|
+
ws.send(JSON.stringify(body));
|
|
1110
|
+
},
|
|
1111
|
+
joinRun(runId, abortSignal) {
|
|
1112
|
+
const target = withSearchParams(typeof url === "function" ? url() : url, {
|
|
1113
|
+
offset: "-1",
|
|
1114
|
+
runId
|
|
1115
|
+
});
|
|
1116
|
+
const ws = options.protocols ? new Impl(target, options.protocols) : new Impl(target);
|
|
1117
|
+
const pipe = createChunkPipe(abortSignal, () => {
|
|
1118
|
+
if (ws.readyState <= 1) ws.close();
|
|
1119
|
+
});
|
|
1120
|
+
ws.onmessage = (event) => {
|
|
1121
|
+
let parsed;
|
|
1122
|
+
try {
|
|
1123
|
+
parsed = JSON.parse(String(event.data));
|
|
1124
|
+
} catch (error) {
|
|
1125
|
+
pipe.fail(new StreamReadError(error));
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
if (isPingFrame(parsed)) return;
|
|
1129
|
+
pipe.push(isNdjsonEnvelope(parsed) ? parsed.chunk : parsed);
|
|
1130
|
+
};
|
|
1131
|
+
ws.onclose = (event) => {
|
|
1132
|
+
if (event?.code === 1e3) {
|
|
1133
|
+
pipe.end();
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
const detail = event ? `${event.code}${event.reason ? `: ${event.reason}` : ""}` : "unknown";
|
|
1137
|
+
pipe.fail(new StreamReadError(/* @__PURE__ */ new Error(`WebSocket connection closed (${detail})`)));
|
|
1138
|
+
};
|
|
1139
|
+
return pipe.iterable;
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
903
1143
|
/**
|
|
904
1144
|
* Create a direct stream connection adapter (for server functions or direct streams)
|
|
905
1145
|
*
|
|
@@ -1021,6 +1261,6 @@ function rpcStream(rpcCall, handlers) {
|
|
|
1021
1261
|
};
|
|
1022
1262
|
}
|
|
1023
1263
|
//#endregion
|
|
1024
|
-
export { DurableStreamIncompleteError, StreamReconnectLimitError, StreamTruncatedError, fetchHttpStream, fetchServerSentEvents, fetcherToConnectionAdapter, getChunkRunId, normalizeConnectionAdapter, rpcStream, stream, xhrHttpStream, xhrServerSentEvents };
|
|
1264
|
+
export { DurableStreamIncompleteError, StreamReconnectLimitError, StreamTruncatedError, createReconnectTracker, fetchHttpStream, fetchServerSentEvents, fetcherToConnectionAdapter, getChunkRunId, normalizeConnectionAdapter, rpcStream, stream, webSocket, xhrHttpStream, xhrServerSentEvents };
|
|
1025
1265
|
|
|
1026
1266
|
//# sourceMappingURL=connection-adapters.js.map
|