@deltakit/react 0.2.1 → 0.2.3
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/dist/index.cjs +111 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +111 -18
- package/dist/index.js.map +1 -1
- package/package.json +56 -49
package/dist/index.cjs
CHANGED
|
@@ -42,25 +42,70 @@ function useAutoScroll(dependencies, options) {
|
|
|
42
42
|
const isAtBottomRef = (0, import_react.useRef)(true);
|
|
43
43
|
const [isAtBottom, setIsAtBottom] = (0, import_react.useState)(true);
|
|
44
44
|
const rafRef = (0, import_react.useRef)(null);
|
|
45
|
+
const smoothRafRef = (0, import_react.useRef)(null);
|
|
46
|
+
const smoothTargetRef = (0, import_react.useRef)(null);
|
|
47
|
+
const lastAutoScrollHeightRef = (0, import_react.useRef)(null);
|
|
48
|
+
const cancelSmoothScroll = (0, import_react.useCallback)(() => {
|
|
49
|
+
if (smoothRafRef.current != null) {
|
|
50
|
+
cancelAnimationFrame(smoothRafRef.current);
|
|
51
|
+
smoothRafRef.current = null;
|
|
52
|
+
}
|
|
53
|
+
smoothTargetRef.current = null;
|
|
54
|
+
}, []);
|
|
55
|
+
const runSmoothScroll = (0, import_react.useCallback)(() => {
|
|
56
|
+
if (smoothRafRef.current != null) return;
|
|
57
|
+
const tick = () => {
|
|
58
|
+
const el = ref.current;
|
|
59
|
+
if (!el || !isAtBottomRef.current) {
|
|
60
|
+
cancelSmoothScroll();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const target = smoothTargetRef.current ?? el.scrollHeight;
|
|
64
|
+
const delta = target - el.scrollTop;
|
|
65
|
+
if (delta <= 1) {
|
|
66
|
+
el.scrollTop = target;
|
|
67
|
+
lastAutoScrollHeightRef.current = target;
|
|
68
|
+
smoothRafRef.current = null;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const step = Math.min(Math.max(delta * 0.35, 12), 120);
|
|
72
|
+
el.scrollTop = Math.min(target, el.scrollTop + step);
|
|
73
|
+
smoothRafRef.current = requestAnimationFrame(tick);
|
|
74
|
+
};
|
|
75
|
+
smoothRafRef.current = requestAnimationFrame(tick);
|
|
76
|
+
}, [cancelSmoothScroll]);
|
|
45
77
|
const scheduleScroll = (0, import_react.useCallback)(() => {
|
|
46
78
|
if (rafRef.current != null) return;
|
|
47
79
|
rafRef.current = requestAnimationFrame(() => {
|
|
48
80
|
rafRef.current = null;
|
|
49
81
|
const el = ref.current;
|
|
50
|
-
if (el
|
|
51
|
-
|
|
82
|
+
if (!el || !isAtBottomRef.current) return;
|
|
83
|
+
const nextHeight = el.scrollHeight;
|
|
84
|
+
if (lastAutoScrollHeightRef.current === nextHeight) return;
|
|
85
|
+
lastAutoScrollHeightRef.current = nextHeight;
|
|
86
|
+
if (behavior === "smooth") {
|
|
87
|
+
smoothTargetRef.current = nextHeight;
|
|
88
|
+
runSmoothScroll();
|
|
89
|
+
return;
|
|
52
90
|
}
|
|
91
|
+
cancelSmoothScroll();
|
|
92
|
+
el.scrollTop = nextHeight;
|
|
53
93
|
});
|
|
54
|
-
}, [behavior]);
|
|
94
|
+
}, [behavior, cancelSmoothScroll, runSmoothScroll]);
|
|
55
95
|
(0, import_react.useEffect)(() => {
|
|
56
96
|
const el = ref.current;
|
|
57
97
|
if (!el || !enabled) return;
|
|
58
98
|
const handleScroll = () => {
|
|
59
99
|
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight <= threshold;
|
|
60
100
|
isAtBottomRef.current = atBottom;
|
|
101
|
+
if (!atBottom) {
|
|
102
|
+
lastAutoScrollHeightRef.current = null;
|
|
103
|
+
cancelSmoothScroll();
|
|
104
|
+
}
|
|
61
105
|
setIsAtBottom((prev) => prev === atBottom ? prev : atBottom);
|
|
62
106
|
};
|
|
63
107
|
el.addEventListener("scroll", handleScroll, { passive: true });
|
|
108
|
+
handleScroll();
|
|
64
109
|
return () => el.removeEventListener("scroll", handleScroll);
|
|
65
110
|
}, [enabled, threshold]);
|
|
66
111
|
(0, import_react.useEffect)(() => {
|
|
@@ -96,15 +141,18 @@ function useAutoScroll(dependencies, options) {
|
|
|
96
141
|
cancelAnimationFrame(rafRef.current);
|
|
97
142
|
rafRef.current = null;
|
|
98
143
|
}
|
|
144
|
+
cancelSmoothScroll();
|
|
99
145
|
};
|
|
100
|
-
}, []);
|
|
146
|
+
}, [cancelSmoothScroll]);
|
|
101
147
|
const scrollToBottom = (0, import_react.useCallback)(() => {
|
|
102
148
|
const el = ref.current;
|
|
103
149
|
if (!el) return;
|
|
150
|
+
cancelSmoothScroll();
|
|
104
151
|
isAtBottomRef.current = true;
|
|
152
|
+
lastAutoScrollHeightRef.current = el.scrollHeight;
|
|
105
153
|
setIsAtBottom(true);
|
|
106
154
|
el.scrollTo({ top: el.scrollHeight, behavior });
|
|
107
|
-
}, [behavior]);
|
|
155
|
+
}, [behavior, cancelSmoothScroll]);
|
|
108
156
|
return { ref, scrollToBottom, isAtBottom };
|
|
109
157
|
}
|
|
110
158
|
|
|
@@ -504,7 +552,14 @@ function defaultOnEvent(event, helpers) {
|
|
|
504
552
|
}
|
|
505
553
|
}
|
|
506
554
|
function useStreamChat(options) {
|
|
507
|
-
const {
|
|
555
|
+
const {
|
|
556
|
+
initialMessages,
|
|
557
|
+
onEvent,
|
|
558
|
+
onMessage,
|
|
559
|
+
onError,
|
|
560
|
+
onFinish,
|
|
561
|
+
textBatchMs = 0
|
|
562
|
+
} = options;
|
|
508
563
|
const [messages, setMessages] = (0, import_react2.useState)(initialMessages ?? []);
|
|
509
564
|
const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
|
|
510
565
|
const [error, setError] = (0, import_react2.useState)(null);
|
|
@@ -514,10 +569,29 @@ function useStreamChat(options) {
|
|
|
514
569
|
const manuallyStoppedRef = (0, import_react2.useRef)(false);
|
|
515
570
|
const messagesRef = (0, import_react2.useRef)(messages);
|
|
516
571
|
messagesRef.current = messages;
|
|
572
|
+
const updateMessages = (0, import_react2.useCallback)(
|
|
573
|
+
(next) => {
|
|
574
|
+
setMessages((prev) => {
|
|
575
|
+
const resolved = typeof next === "function" ? next(prev) : next;
|
|
576
|
+
messagesRef.current = resolved;
|
|
577
|
+
return resolved;
|
|
578
|
+
});
|
|
579
|
+
},
|
|
580
|
+
[]
|
|
581
|
+
);
|
|
517
582
|
const transportOptionsRef = (0, import_react2.useRef)(options.transportOptions);
|
|
518
583
|
transportOptionsRef.current = options.transportOptions;
|
|
519
|
-
const
|
|
520
|
-
|
|
584
|
+
const pendingTextRef = (0, import_react2.useRef)("");
|
|
585
|
+
const textFlushTimerRef = (0, import_react2.useRef)(null);
|
|
586
|
+
const flushPendingText = (0, import_react2.useCallback)(() => {
|
|
587
|
+
if (textFlushTimerRef.current !== null) {
|
|
588
|
+
clearTimeout(textFlushTimerRef.current);
|
|
589
|
+
textFlushTimerRef.current = null;
|
|
590
|
+
}
|
|
591
|
+
const delta = pendingTextRef.current;
|
|
592
|
+
if (!delta) return;
|
|
593
|
+
pendingTextRef.current = "";
|
|
594
|
+
updateMessages((prev) => {
|
|
521
595
|
const last = prev[prev.length - 1];
|
|
522
596
|
if (!last || last.role !== "assistant") return prev;
|
|
523
597
|
const parts = [...last.parts];
|
|
@@ -533,9 +607,22 @@ function useStreamChat(options) {
|
|
|
533
607
|
}
|
|
534
608
|
return [...prev.slice(0, -1), { ...last, parts }];
|
|
535
609
|
});
|
|
536
|
-
}, []);
|
|
610
|
+
}, [updateMessages]);
|
|
611
|
+
const appendText = (0, import_react2.useCallback)((delta) => {
|
|
612
|
+
if (textBatchMs <= 0) {
|
|
613
|
+
pendingTextRef.current += delta;
|
|
614
|
+
flushPendingText();
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
pendingTextRef.current += delta;
|
|
618
|
+
if (textFlushTimerRef.current !== null) return;
|
|
619
|
+
textFlushTimerRef.current = setTimeout(() => {
|
|
620
|
+
flushPendingText();
|
|
621
|
+
}, textBatchMs);
|
|
622
|
+
}, [flushPendingText, textBatchMs]);
|
|
537
623
|
const appendPart = (0, import_react2.useCallback)((part) => {
|
|
538
|
-
|
|
624
|
+
flushPendingText();
|
|
625
|
+
updateMessages((prev) => {
|
|
539
626
|
const last = prev[prev.length - 1];
|
|
540
627
|
if (!last || last.role !== "assistant") return prev;
|
|
541
628
|
return [
|
|
@@ -546,7 +633,7 @@ function useStreamChat(options) {
|
|
|
546
633
|
}
|
|
547
634
|
];
|
|
548
635
|
});
|
|
549
|
-
}, []);
|
|
636
|
+
}, [flushPendingText, updateMessages]);
|
|
550
637
|
const transportRef = (0, import_react2.useRef)(null);
|
|
551
638
|
if (!transportRef.current) {
|
|
552
639
|
transportRef.current = resolveTransport(options);
|
|
@@ -566,20 +653,23 @@ function useStreamChat(options) {
|
|
|
566
653
|
appendPart,
|
|
567
654
|
appendText,
|
|
568
655
|
eventHandler: (event, helpers) => eventHandlerRef.current(event, helpers),
|
|
569
|
-
getMessages: () =>
|
|
656
|
+
getMessages: () => {
|
|
657
|
+
flushPendingText();
|
|
658
|
+
return messagesRef.current;
|
|
659
|
+
},
|
|
570
660
|
onError: (...args) => onErrorRef.current?.(...args),
|
|
571
661
|
onFinish: (...args) => onFinishRef.current?.(...args),
|
|
572
662
|
onMessage: (...args) => onMessageRef.current?.(...args),
|
|
573
663
|
setError,
|
|
574
664
|
setIsLoading,
|
|
575
|
-
setMessages,
|
|
665
|
+
setMessages: updateMessages,
|
|
576
666
|
setRunId: (next) => {
|
|
577
667
|
setRunId(next);
|
|
578
668
|
transportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);
|
|
579
669
|
transportOptionsRef.current?.websocket?.onRunIdChange?.(next);
|
|
580
670
|
}
|
|
581
671
|
}),
|
|
582
|
-
[appendPart, appendText]
|
|
672
|
+
[appendPart, appendText, flushPendingText, updateMessages]
|
|
583
673
|
);
|
|
584
674
|
const stop = (0, import_react2.useCallback)(() => {
|
|
585
675
|
const activeRun = runRef.current;
|
|
@@ -600,9 +690,8 @@ function useStreamChat(options) {
|
|
|
600
690
|
{ type: "text", text }
|
|
601
691
|
]);
|
|
602
692
|
const assistantMessage = createMessage("assistant", []);
|
|
603
|
-
|
|
693
|
+
updateMessages((prev) => {
|
|
604
694
|
const next = [...prev, userMessage, assistantMessage];
|
|
605
|
-
messagesRef.current = next;
|
|
606
695
|
return next;
|
|
607
696
|
});
|
|
608
697
|
onMessage?.(userMessage);
|
|
@@ -616,7 +705,7 @@ function useStreamChat(options) {
|
|
|
616
705
|
setRunId(run.runId);
|
|
617
706
|
}
|
|
618
707
|
},
|
|
619
|
-
[isLoading, onMessage, transport, transportContext]
|
|
708
|
+
[isLoading, onMessage, transport, transportContext, updateMessages]
|
|
620
709
|
);
|
|
621
710
|
const candidateRunId = options.transportOptions?.backgroundSSE?.runId ?? options.transportOptions?.backgroundSSE?.getResumeKey?.() ?? options.transportOptions?.websocket?.runId ?? options.transportOptions?.websocket?.getResumeKey?.() ?? null;
|
|
622
711
|
(0, import_react2.useEffect)(() => {
|
|
@@ -647,6 +736,10 @@ function useStreamChat(options) {
|
|
|
647
736
|
}, [candidateRunId, transport, transportContext]);
|
|
648
737
|
(0, import_react2.useEffect)(() => {
|
|
649
738
|
return () => {
|
|
739
|
+
if (textFlushTimerRef.current !== null) {
|
|
740
|
+
clearTimeout(textFlushTimerRef.current);
|
|
741
|
+
textFlushTimerRef.current = null;
|
|
742
|
+
}
|
|
650
743
|
void runRef.current?.close?.();
|
|
651
744
|
runRef.current = null;
|
|
652
745
|
};
|
|
@@ -664,7 +757,7 @@ function useStreamChat(options) {
|
|
|
664
757
|
messages,
|
|
665
758
|
runId,
|
|
666
759
|
sendMessage,
|
|
667
|
-
setMessages,
|
|
760
|
+
setMessages: updateMessages,
|
|
668
761
|
stop
|
|
669
762
|
};
|
|
670
763
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/use-auto-scroll.ts","../src/use-stream-chat.ts","../src/chat-controller.ts","../src/transports.ts"],"sourcesContent":["// Re-export core types so consumers only need to import from @deltakit/react\nexport type {\n\tContentPart,\n\tMessage,\n\tReasoningPart,\n\tSSEEvent,\n\tTextDeltaEvent,\n\tTextPart,\n\tToolCallEvent,\n\tToolCallPart,\n\tToolResultEvent,\n} from \"@deltakit/core\";\nexport {\n\tfromAgnoAgents,\n\tfromOpenAiAgents,\n\tparseSSEStream,\n} from \"@deltakit/core\";\n\nexport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tEventHelpers,\n\tTransportOptions,\n\tUseAutoScrollOptions,\n\tUseAutoScrollReturn,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n\tWebSocketTransportOptions,\n} from \"./types\";\nexport { useAutoScroll } from \"./use-auto-scroll\";\nexport { useStreamChat } from \"./use-stream-chat\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseAutoScrollOptions, UseAutoScrollReturn } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_THRESHOLD = 50;\n\n// ---------------------------------------------------------------------------\n// useAutoScroll\n// ---------------------------------------------------------------------------\n\nexport function useAutoScroll<T extends HTMLElement = HTMLDivElement>(\n\tdependencies: unknown[],\n\toptions?: UseAutoScrollOptions,\n): UseAutoScrollReturn<T> {\n\tconst {\n\t\tbehavior = \"instant\",\n\t\tenabled = true,\n\t\tthreshold = DEFAULT_THRESHOLD,\n\t} = options ?? {};\n\n\tconst ref = useRef<T | null>(null);\n\tconst isAtBottomRef = useRef(true);\n\tconst [isAtBottom, setIsAtBottom] = useState(true);\n\n\t// A single rAF id shared across all scroll sources — ensures we never\n\t// call scrollTo() more than once per frame, no matter how many\n\t// MutationObserver / ResizeObserver callbacks fire.\n\tconst rafRef = useRef<number | null>(null);\n\n\tconst scheduleScroll = useCallback(() => {\n\t\tif (rafRef.current != null) return;\n\t\trafRef.current = requestAnimationFrame(() => {\n\t\t\trafRef.current = null;\n\t\t\tconst el = ref.current;\n\t\t\tif (el && isAtBottomRef.current) {\n\t\t\t\tel.scrollTo({ top: el.scrollHeight, behavior });\n\t\t\t}\n\t\t});\n\t}, [behavior]);\n\n\t// -----------------------------------------------------------------------\n\t// Track whether the user is near the bottom via scroll events.\n\t// Only triggers a React re-render when the boolean actually changes.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst handleScroll = () => {\n\t\t\tconst atBottom =\n\t\t\t\tel.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n\t\t\tisAtBottomRef.current = atBottom;\n\t\t\tsetIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n\t\t};\n\n\t\tel.addEventListener(\"scroll\", handleScroll, { passive: true });\n\t\treturn () => el.removeEventListener(\"scroll\", handleScroll);\n\t}, [enabled, threshold]);\n\n\t// -----------------------------------------------------------------------\n\t// Scroll to bottom when dependencies change (if pinned).\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tif (!enabled || !isAtBottomRef.current) return;\n\t\tscheduleScroll();\n\t\t// biome-ignore lint/correctness/useExhaustiveDependencies: dependencies are passed dynamically by the consumer\n\t}, dependencies);\n\n\t// -----------------------------------------------------------------------\n\t// MutationObserver + ResizeObserver — catch content changes during\n\t// streaming that happen between React re-renders (e.g. DOM mutations\n\t// from markdown renderers). Scroll calls are batched via rAF so we\n\t// scroll at most once per frame.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst resizeObserver = new ResizeObserver(scheduleScroll);\n\n\t\t// Observe existing children for size changes.\n\t\tfor (const child of el.children) {\n\t\t\tresizeObserver.observe(child);\n\t\t}\n\n\t\t// Watch for new children added to the container.\n\t\tconst mutationObserver = new MutationObserver((mutations) => {\n\t\t\tfor (const mutation of mutations) {\n\t\t\t\tfor (const node of mutation.addedNodes) {\n\t\t\t\t\tif (node instanceof Element) {\n\t\t\t\t\t\tresizeObserver.observe(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tscheduleScroll();\n\t\t});\n\n\t\tmutationObserver.observe(el, { childList: true, subtree: true });\n\n\t\treturn () => {\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [enabled, scheduleScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Cancel any pending rAF on unmount.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (rafRef.current != null) {\n\t\t\t\tcancelAnimationFrame(rafRef.current);\n\t\t\t\trafRef.current = null;\n\t\t\t}\n\t\t};\n\t}, []);\n\n\t// -----------------------------------------------------------------------\n\t// scrollToBottom — imperative function that scrolls to the bottom and\n\t// re-pins auto-scroll.\n\t// -----------------------------------------------------------------------\n\n\tconst scrollToBottom = useCallback(() => {\n\t\tconst el = ref.current;\n\t\tif (!el) return;\n\n\t\tisAtBottomRef.current = true;\n\t\tsetIsAtBottom(true);\n\t\tel.scrollTo({ top: el.scrollHeight, behavior });\n\t}, [behavior]);\n\n\treturn { ref, scrollToBottom, isAtBottom };\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { createChatTransportContext, createMessage } from \"./chat-controller\";\nimport { resolveTransport } from \"./transports\";\nimport type {\n\tChatTransportRun,\n\tEventHelpers,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Default event handler — accumulates `text_delta` into the last\n// assistant message's parts.\n// ---------------------------------------------------------------------------\n\nfunction defaultOnEvent(\n\tevent: SSEEvent,\n\thelpers: EventHelpers<ContentPart>,\n): void {\n\tif (event.type === \"text_delta\") {\n\t\thelpers.appendText(event.delta);\n\t}\n\t// Other event types (e.g. tool_call) are silently ignored by default.\n\t// Users can provide their own `onEvent` to handle them.\n}\n\n// ---------------------------------------------------------------------------\n// useStreamChat\n// ---------------------------------------------------------------------------\n\nexport function useStreamChat<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): UseStreamChatReturn<TPart> {\n\tconst { initialMessages, onEvent, onMessage, onError, onFinish } = options;\n\n\tconst [messages, setMessages] = useState(initialMessages ?? []);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [runId, setRunId] = useState<string | null>(null);\n\n\tconst runRef = useRef<ChatTransportRun | null>(null);\n\n\t// Track which run id has already been resumed to prevent re-triggering.\n\tconst resumedRunIdRef = useRef<string | null>(null);\n\n\t// When the user manually stops a run, suppress auto-resume until the\n\t// next explicit `sendMessage` call.\n\tconst manuallyStoppedRef = useRef(false);\n\n\t// We use a ref for the latest messages so callbacks created inside\n\t// transport handlers always see the current value without re-creating\n\t// closures.\n\tconst messagesRef = useRef(messages);\n\tmessagesRef.current = messages;\n\n\t// Keep transport options in a ref so that callbacks always read the\n\t// latest values without causing memoisation instability.\n\tconst transportOptionsRef = useRef(options.transportOptions);\n\ttransportOptionsRef.current = options.transportOptions;\n\n\tconst appendText = useCallback((delta: string) => {\n\t\tsetMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\tconst parts = [...last.parts];\n\t\t\tconst lastPart = parts[parts.length - 1];\n\n\t\t\tif (lastPart && lastPart.type === \"text\" && \"text\" in lastPart) {\n\t\t\t\tconst textPart = lastPart as { type: \"text\"; text: string };\n\t\t\t\tparts[parts.length - 1] = {\n\t\t\t\t\t...lastPart,\n\t\t\t\t\ttext: textPart.text + delta,\n\t\t\t\t} as unknown as TPart;\n\t\t\t} else {\n\t\t\t\tparts.push({ type: \"text\", text: delta } as unknown as TPart);\n\t\t\t}\n\n\t\t\treturn [...prev.slice(0, -1), { ...last, parts }];\n\t\t});\n\t}, []);\n\n\tconst appendPart = useCallback((part: TPart) => {\n\t\tsetMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\treturn [\n\t\t\t\t...prev.slice(0, -1),\n\t\t\t\t{\n\t\t\t\t\t...last,\n\t\t\t\t\tparts: [...last.parts, part],\n\t\t\t\t},\n\t\t\t];\n\t\t});\n\t}, []);\n\n\t// Stabilise transport creation: resolve once and store in a ref so that\n\t// changing values like `runId` in transportOptions won't cause a new\n\t// transport instance (and therefore a new WebSocket) to be created.\n\tconst transportRef = useRef<ReturnType<\n\t\ttypeof resolveTransport<TPart, TEvent>\n\t> | null>(null);\n\tif (!transportRef.current) {\n\t\ttransportRef.current = resolveTransport(options);\n\t}\n\tconst transport = transportRef.current;\n\n\tconst eventHandler =\n\t\tonEvent ??\n\t\t(defaultOnEvent as unknown as (\n\t\t\tevent: TEvent,\n\t\t\thelpers: EventHelpers<TPart>,\n\t\t) => void);\n\n\t// Stabilise the transport context: use refs for values that change\n\t// frequently (transportOptions callbacks) so the context object itself\n\t// stays referentially stable.\n\tconst eventHandlerRef = useRef(eventHandler);\n\teventHandlerRef.current = eventHandler;\n\n\tconst onErrorRef = useRef(onError);\n\tonErrorRef.current = onError;\n\n\tconst onFinishRef = useRef(onFinish);\n\tonFinishRef.current = onFinish;\n\n\tconst onMessageRef = useRef(onMessage);\n\tonMessageRef.current = onMessage;\n\n\tconst transportContext = useMemo(\n\t\t() =>\n\t\t\tcreateChatTransportContext({\n\t\t\t\tappendPart,\n\t\t\t\tappendText,\n\t\t\t\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) =>\n\t\t\t\t\teventHandlerRef.current(event, helpers),\n\t\t\t\tgetMessages: () => messagesRef.current,\n\t\t\t\tonError: (...args) => onErrorRef.current?.(...args),\n\t\t\t\tonFinish: (...args) => onFinishRef.current?.(...args),\n\t\t\t\tonMessage: (...args) => onMessageRef.current?.(...args),\n\t\t\t\tsetError,\n\t\t\t\tsetIsLoading,\n\t\t\t\tsetMessages,\n\t\t\t\tsetRunId: (next) => {\n\t\t\t\t\tsetRunId(next);\n\t\t\t\t\ttransportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);\n\t\t\t\t\ttransportOptionsRef.current?.websocket?.onRunIdChange?.(next);\n\t\t\t\t},\n\t\t\t}),\n\t\t[appendPart, appendText],\n\t);\n\n\tconst stop = useCallback(() => {\n\t\tconst activeRun = runRef.current;\n\t\tif (!activeRun?.stop) {\n\t\t\treturn;\n\t\t}\n\n\t\tmanuallyStoppedRef.current = true;\n\t\tvoid activeRun.stop();\n\t\trunRef.current = null;\n\t\tsetIsLoading(false);\n\t}, []);\n\n\tconst sendMessage = useCallback(\n\t\t(text: string) => {\n\t\t\tif (runRef.current || isLoading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst userMessage = createMessage<TPart>(\"user\", [\n\t\t\t\t{ type: \"text\", text } as unknown as TPart,\n\t\t\t]);\n\t\t\tconst assistantMessage = createMessage<TPart>(\"assistant\", []);\n\n\t\t\tsetMessages((prev) => {\n\t\t\t\tconst next = [...prev, userMessage, assistantMessage];\n\t\t\t\tmessagesRef.current = next;\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t\tonMessage?.(userMessage);\n\t\t\tsetError(null);\n\t\t\tsetIsLoading(true);\n\n\t\t\t// Reset the resume guard so a future resume for a new run is allowed.\n\t\t\tresumedRunIdRef.current = null;\n\t\t\tmanuallyStoppedRef.current = false;\n\n\t\t\tconst run = transport.start({ context: transportContext, message: text });\n\t\t\trunRef.current = run ?? null;\n\t\t\tif (run?.runId) {\n\t\t\t\tsetRunId(run.runId);\n\t\t\t}\n\t\t},\n\t\t[isLoading, onMessage, transport, transportContext],\n\t);\n\n\t// -----------------------------------------------------------------------\n\t// Auto-resume effect: attempt to reconnect to an in-flight run on mount.\n\t//\n\t// The candidate run id is read from transport options (which may change\n\t// when the app updates state). We guard against duplicate resumes for the\n\t// same run id using `resumedRunIdRef`.\n\t// -----------------------------------------------------------------------\n\n\tconst candidateRunId =\n\t\toptions.transportOptions?.backgroundSSE?.runId ??\n\t\toptions.transportOptions?.backgroundSSE?.getResumeKey?.() ??\n\t\toptions.transportOptions?.websocket?.runId ??\n\t\toptions.transportOptions?.websocket?.getResumeKey?.() ??\n\t\tnull;\n\n\tuseEffect(() => {\n\t\t// Already have an active run — don't start another.\n\t\tif (runRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// User explicitly stopped — don't auto-resume until next sendMessage.\n\t\tif (manuallyStoppedRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Already resumed this exact run id — don't retry.\n\t\tif (resumedRunIdRef.current === candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!transport.resume) {\n\t\t\treturn;\n\t\t}\n\n\t\tresumedRunIdRef.current = candidateRunId;\n\n\t\tsetError(null);\n\t\tsetIsLoading(true);\n\t\tconst run = transport.resume({\n\t\t\tcontext: transportContext,\n\t\t\trunId: candidateRunId,\n\t\t});\n\t\trunRef.current = run ?? null;\n\t\tsetRunId(candidateRunId);\n\t}, [candidateRunId, transport, transportContext]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tvoid runRef.current?.close?.();\n\t\t\trunRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst prevIsLoadingRef = useRef(isLoading);\n\tuseEffect(() => {\n\t\t// Only clear the run ref on a true → false transition, not on mount\n\t\t// where isLoading starts as false. Clearing on mount would race with\n\t\t// the auto-resume effect and null out the run it just created.\n\t\tif (prevIsLoadingRef.current && !isLoading) {\n\t\t\trunRef.current = null;\n\t\t}\n\t\tprevIsLoadingRef.current = isLoading;\n\t}, [isLoading]);\n\n\treturn {\n\t\terror,\n\t\tisLoading,\n\t\tmessages,\n\t\trunId,\n\t\tsendMessage,\n\t\tsetMessages,\n\t\tstop,\n\t};\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport type { Dispatch, SetStateAction } from \"react\";\nimport type {\n\tChatTransportContext,\n\tEventHelpers,\n\tUseStreamChatOptions,\n} from \"./types\";\n\nexport interface ChatControllerOptions<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n> {\n\tappendPart: EventHelpers<TPart>[\"appendPart\"];\n\tappendText: EventHelpers<TPart>[\"appendText\"];\n\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) => void;\n\tgetMessages: () => Message<TPart>[];\n\tonError?: UseStreamChatOptions<TPart, TEvent>[\"onError\"];\n\tonFinish?: UseStreamChatOptions<TPart, TEvent>[\"onFinish\"];\n\tonMessage?: UseStreamChatOptions<TPart, TEvent>[\"onMessage\"];\n\tsetError: Dispatch<SetStateAction<Error | null>>;\n\tsetIsLoading: Dispatch<SetStateAction<boolean>>;\n\tsetMessages: Dispatch<SetStateAction<Message<TPart>[]>>;\n\tsetRunId: (runId: string | null) => void;\n}\n\nlet counter = 0;\n\nexport function generateId(): string {\n\treturn `msg_${Date.now()}_${++counter}`;\n}\n\nexport function createMessage<TPart extends { type: string }>(\n\trole: Message[\"role\"],\n\tparts: TPart[],\n): Message<TPart> {\n\treturn { id: generateId(), role, parts };\n}\n\nexport function createChatTransportContext<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(\n\toptions: ChatControllerOptions<TPart, TEvent>,\n): ChatTransportContext<TPart, TEvent> {\n\tconst helpers: EventHelpers<TPart> = {\n\t\tappendPart: options.appendPart,\n\t\tappendText: options.appendText,\n\t\tsetMessages: options.setMessages,\n\t};\n\n\treturn {\n\t\temit: (event) => {\n\t\t\toptions.eventHandler(event, helpers);\n\t\t},\n\t\tensureAssistantMessage: () => {\n\t\t\toptions.setMessages((prev) => {\n\t\t\t\tconst last = prev[prev.length - 1];\n\t\t\t\tif (last?.role === \"assistant\") {\n\t\t\t\t\treturn prev;\n\t\t\t\t}\n\n\t\t\t\treturn [...prev, createMessage<TPart>(\"assistant\", [])];\n\t\t\t});\n\t\t},\n\t\tfail: (error) => {\n\t\t\toptions.setError(error);\n\t\t\toptions.onError?.(error);\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\t\t},\n\t\tfinish: () => {\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\n\t\t\tconst finalMessages = options.getMessages();\n\t\t\tconst lastMessage = finalMessages[finalMessages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\toptions.onMessage?.(lastMessage);\n\t\t\t}\n\n\t\t\toptions.onFinish?.(finalMessages);\n\t\t},\n\t\tgetMessages: options.getMessages,\n\t\tsetRunId: (runId) => {\n\t\t\toptions.setRunId(runId);\n\t\t},\n\t};\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { parseSSEStream } from \"@deltakit/core\";\nimport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tUseStreamChatOptions,\n\tWebSocketTransportOptions,\n} from \"./types\";\n\nfunction toError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction resolveRunId(response: unknown): string {\n\tif (!response || typeof response !== \"object\") {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\tconst maybeRunId =\n\t\t\"runId\" in response\n\t\t\t? response.runId\n\t\t\t: \"job_id\" in response\n\t\t\t\t? response.job_id\n\t\t\t\t: null;\n\n\tif (typeof maybeRunId !== \"string\" || maybeRunId.length === 0) {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\treturn maybeRunId;\n}\n\nfunction resolveUrl(\n\turl: string | ((runId: string) => string),\n\trunId: string,\n): string {\n\treturn typeof url === \"function\" ? url(runId) : url.replace(\":runId\", runId);\n}\n\nasync function streamFetchSSE<\n\tTPart extends { type: string },\n\tTEvent extends { type: string },\n>(\n\tresponse: Response,\n\tcontext: ChatTransportContext<TPart, TEvent>,\n\tsignal: AbortSignal,\n): Promise<void> {\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`SSE request failed: ${response.status} ${response.statusText}`,\n\t\t);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"Response body is null — SSE streaming not supported\");\n\t}\n\n\tfor await (const event of parseSSEStream(response.body, signal)) {\n\t\tcontext.emit(event as unknown as TEvent);\n\t}\n}\n\nexport function createDirectSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: DirectSSETransportOptions): ChatTransport<TPart, TEvent> {\n\treturn {\n\t\tstart: ({ context, message }) => {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst run: ChatTransportRun = {\n\t\t\t\tclose: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst fetchImpl = config.fetch ?? fetch;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.api, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.body }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.headers,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.method ?? \"POST\",\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\t\tcontext.finish();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn run;\n\t\t},\n\t};\n}\n\nexport function createBackgroundSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: BackgroundSSETransportOptions): ChatTransport<TPart, TEvent> {\n\tconst fetchImpl = config.fetch ?? fetch;\n\n\tconst connect = (\n\t\trunId: string,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t): ChatTransportRun => {\n\t\tconst controller = new AbortController();\n\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst response = await fetchImpl(resolveUrl(config.eventsApi, runId), {\n\t\t\t\t\theaders: config.eventHeaders,\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\tcontext.finish();\n\t\t\t} catch (error) {\n\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\n\t\treturn {\n\t\t\tclose: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t},\n\t\t\tstop: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t\tif (config.cancelApi) {\n\t\t\t\t\tvoid fetchImpl(resolveUrl(config.cancelApi, runId), {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\trunId,\n\t\t};\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\treturn connect(runId, context);\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst startController = new AbortController();\n\t\t\tlet activeRun: ChatTransportRun | undefined;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.startApi, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.startBody }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.startHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.startMethod ?? \"POST\",\n\t\t\t\t\t\tsignal: startController.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Background SSE start failed: ${response.status} ${response.statusText}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as unknown;\n\t\t\t\t\tconst runId = (config.resolveRunId ?? resolveRunId)(data);\n\t\t\t\t\tcontext.setRunId(runId);\n\t\t\t\t\tactiveRun = connect(runId, context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tvoid activeRun?.close?.();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tactiveRun?.stop?.();\n\t\t\t\t},\n\t\t\t\trunId: null,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction defaultParseWebSocketMessage<TEvent extends { type: string }>(\n\tdata: unknown,\n): TEvent | TEvent[] | null {\n\tif (typeof data !== \"string\") {\n\t\treturn null;\n\t}\n\n\tconst parsed = JSON.parse(data) as TEvent | TEvent[];\n\treturn parsed;\n}\n\nexport function createWebSocketTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: WebSocketTransportOptions<TEvent>): ChatTransport<TPart, TEvent> {\n\tconst parseMessage = config.parseMessage ?? defaultParseWebSocketMessage;\n\tconst serializeMessage = config.serializeMessage ?? JSON.stringify;\n\n\tlet resolvedRunId: string | null = null;\n\n\tconst applyIncomingEvents = (\n\t\tparsed: TEvent | TEvent[] | null,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t) => {\n\t\tconst events = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];\n\t\tfor (const item of events) {\n\t\t\tconst nextRunId = config.resolveRunId?.(item) ?? null;\n\t\t\tif (nextRunId) {\n\t\t\t\tresolvedRunId = nextRunId;\n\t\t\t\tcontext.setRunId(nextRunId);\n\t\t\t}\n\t\t\tcontext.emit(item);\n\n\t\t\tif (item.type === \"done\") {\n\t\t\t\tcontext.finish();\n\t\t\t} else if (item.type === \"error\") {\n\t\t\t\tcontext.fail(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\"message\" in item && typeof item.message === \"string\"\n\t\t\t\t\t\t\t? item.message\n\t\t\t\t\t\t\t: \"Stream error\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\tcontext.ensureAssistantMessage();\n\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentResumePayload = false;\n\n\t\t\tconst sendResumePayload = () => {\n\t\t\t\tif (sentResumePayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentResumePayload = true;\n\t\t\t\tconst payload =\n\t\t\t\t\tconfig.buildResumePayload?.(runId) ??\n\t\t\t\t\t({ [config.runIdKey ?? \"runId\"]: runId } as Record<string, unknown>);\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendResumePayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendResumePayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst runId = config.runId ?? config.getResumeKey?.() ?? null;\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentStartPayload = false;\n\n\t\t\tconst sendStartPayload = () => {\n\t\t\t\tif (sentStartPayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentStartPayload = true;\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst payload: Record<string, unknown> = {\n\t\t\t\t\tmessage,\n\t\t\t\t\t...config.body,\n\t\t\t\t};\n\n\t\t\t\tif (runId) {\n\t\t\t\t\tpayload[config.runIdKey ?? \"runId\"] = runId;\n\t\t\t\t}\n\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendStartPayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendStartPayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport function resolveTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): ChatTransport<TPart, TEvent> {\n\tif (typeof options.transport === \"object\" && options.transport) {\n\t\treturn options.transport;\n\t}\n\n\tconst transportKind = options.transport ?? \"sse\";\n\n\tif (transportKind === \"background-sse\") {\n\t\tconst config = options.transportOptions?.backgroundSSE;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.backgroundSSE` is required when transport is \"background-sse\"',\n\t\t\t);\n\t\t}\n\t\treturn createBackgroundSSETransport(config);\n\t}\n\n\tif (transportKind === \"websocket\") {\n\t\tconst config = options.transportOptions?.websocket;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.websocket` is required when transport is \"websocket\"',\n\t\t\t);\n\t\t}\n\t\treturn createWebSocketTransport(config);\n\t}\n\n\tconst sseConfig = options.transportOptions?.sse ?? {\n\t\tapi: options.api,\n\t\tbody: options.body,\n\t\theaders: options.headers,\n\t};\n\n\tif (!sseConfig.api) {\n\t\tthrow new Error(\n\t\t\t\"`api` or `transportOptions.sse.api` is required when using the default SSE transport\",\n\t\t);\n\t}\n\n\treturn createDirectSSETransport({\n\t\t...sseConfig,\n\t\tapi: sseConfig.api,\n\t});\n}\n"],"mappings":"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","names":["import_core","import_react"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/use-auto-scroll.ts","../src/use-stream-chat.ts","../src/chat-controller.ts","../src/transports.ts"],"sourcesContent":["// Re-export core types so consumers only need to import from @deltakit/react\nexport type {\n\tContentPart,\n\tMessage,\n\tReasoningPart,\n\tSSEEvent,\n\tTextDeltaEvent,\n\tTextPart,\n\tToolCallEvent,\n\tToolCallPart,\n\tToolResultEvent,\n} from \"@deltakit/core\";\nexport {\n\tfromAgnoAgents,\n\tfromOpenAiAgents,\n\tparseSSEStream,\n} from \"@deltakit/core\";\n\nexport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tEventHelpers,\n\tTransportOptions,\n\tUseAutoScrollOptions,\n\tUseAutoScrollReturn,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n\tWebSocketTransportOptions,\n} from \"./types\";\nexport { useAutoScroll } from \"./use-auto-scroll\";\nexport { useStreamChat } from \"./use-stream-chat\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseAutoScrollOptions, UseAutoScrollReturn } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_THRESHOLD = 50;\n\n// ---------------------------------------------------------------------------\n// useAutoScroll\n// ---------------------------------------------------------------------------\n\nexport function useAutoScroll<T extends HTMLElement = HTMLDivElement>(\n\tdependencies: unknown[],\n\toptions?: UseAutoScrollOptions,\n): UseAutoScrollReturn<T> {\n\tconst {\n\t\tbehavior = \"instant\",\n\t\tenabled = true,\n\t\tthreshold = DEFAULT_THRESHOLD,\n\t} = options ?? {};\n\n\tconst ref = useRef<T | null>(null);\n\tconst isAtBottomRef = useRef(true);\n\tconst [isAtBottom, setIsAtBottom] = useState(true);\n\n\t// A single rAF id shared across all scroll sources — ensures we never\n\t// call scrollTo() more than once per frame, no matter how many\n\t// MutationObserver / ResizeObserver callbacks fire.\n\tconst rafRef = useRef<number | null>(null);\n\tconst smoothRafRef = useRef<number | null>(null);\n\tconst smoothTargetRef = useRef<number | null>(null);\n\tconst lastAutoScrollHeightRef = useRef<number | null>(null);\n\n\tconst cancelSmoothScroll = useCallback(() => {\n\t\tif (smoothRafRef.current != null) {\n\t\t\tcancelAnimationFrame(smoothRafRef.current);\n\t\t\tsmoothRafRef.current = null;\n\t\t}\n\t\tsmoothTargetRef.current = null;\n\t}, []);\n\n\tconst runSmoothScroll = useCallback(() => {\n\t\tif (smoothRafRef.current != null) return;\n\n\t\tconst tick = () => {\n\t\t\tconst el = ref.current;\n\t\t\tif (!el || !isAtBottomRef.current) {\n\t\t\t\tcancelSmoothScroll();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst target = smoothTargetRef.current ?? el.scrollHeight;\n\t\t\tconst delta = target - el.scrollTop;\n\n\t\t\tif (delta <= 1) {\n\t\t\t\tel.scrollTop = target;\n\t\t\t\tlastAutoScrollHeightRef.current = target;\n\t\t\t\tsmoothRafRef.current = null;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst step = Math.min(Math.max(delta * 0.35, 12), 120);\n\t\t\tel.scrollTop = Math.min(target, el.scrollTop + step);\n\t\t\tsmoothRafRef.current = requestAnimationFrame(tick);\n\t\t};\n\n\t\tsmoothRafRef.current = requestAnimationFrame(tick);\n\t}, [cancelSmoothScroll]);\n\n\tconst scheduleScroll = useCallback(() => {\n\t\tif (rafRef.current != null) return;\n\t\trafRef.current = requestAnimationFrame(() => {\n\t\t\trafRef.current = null;\n\t\t\tconst el = ref.current;\n\t\t\tif (!el || !isAtBottomRef.current) return;\n\n\t\t\tconst nextHeight = el.scrollHeight;\n\t\t\tif (lastAutoScrollHeightRef.current === nextHeight) return;\n\n\t\t\tlastAutoScrollHeightRef.current = nextHeight;\n\n\t\t\tif (behavior === \"smooth\") {\n\t\t\t\tsmoothTargetRef.current = nextHeight;\n\t\t\t\trunSmoothScroll();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcancelSmoothScroll();\n\t\t\tel.scrollTop = nextHeight;\n\t\t});\n\t}, [behavior, cancelSmoothScroll, runSmoothScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Track whether the user is near the bottom via scroll events.\n\t// Only triggers a React re-render when the boolean actually changes.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst handleScroll = () => {\n\t\t\tconst atBottom =\n\t\t\t\tel.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n\t\t\tisAtBottomRef.current = atBottom;\n\t\t\tif (!atBottom) {\n\t\t\t\tlastAutoScrollHeightRef.current = null;\n\t\t\t\tcancelSmoothScroll();\n\t\t\t}\n\t\t\tsetIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n\t\t};\n\n\t\tel.addEventListener(\"scroll\", handleScroll, { passive: true });\n\t\thandleScroll();\n\t\treturn () => el.removeEventListener(\"scroll\", handleScroll);\n\t}, [enabled, threshold]);\n\n\t// -----------------------------------------------------------------------\n\t// Scroll to bottom when dependencies change (if pinned).\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tif (!enabled || !isAtBottomRef.current) return;\n\t\tscheduleScroll();\n\t\t// biome-ignore lint/correctness/useExhaustiveDependencies: dependencies are passed dynamically by the consumer\n\t}, dependencies);\n\n\t// -----------------------------------------------------------------------\n\t// MutationObserver + ResizeObserver — catch content changes during\n\t// streaming that happen between React re-renders (e.g. DOM mutations\n\t// from markdown renderers). Scroll calls are batched via rAF so we\n\t// scroll at most once per frame.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst resizeObserver = new ResizeObserver(scheduleScroll);\n\n\t\t// Observe existing children for size changes.\n\t\tfor (const child of el.children) {\n\t\t\tresizeObserver.observe(child);\n\t\t}\n\n\t\t// Watch for new children added to the container.\n\t\tconst mutationObserver = new MutationObserver((mutations) => {\n\t\t\tfor (const mutation of mutations) {\n\t\t\t\tfor (const node of mutation.addedNodes) {\n\t\t\t\t\tif (node instanceof Element) {\n\t\t\t\t\t\tresizeObserver.observe(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tscheduleScroll();\n\t\t});\n\n\t\tmutationObserver.observe(el, { childList: true, subtree: true });\n\n\t\treturn () => {\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [enabled, scheduleScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Cancel any pending rAF on unmount.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (rafRef.current != null) {\n\t\t\t\tcancelAnimationFrame(rafRef.current);\n\t\t\t\trafRef.current = null;\n\t\t\t}\n\t\t\tcancelSmoothScroll();\n\t\t};\n\t}, [cancelSmoothScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// scrollToBottom — imperative function that scrolls to the bottom and\n\t// re-pins auto-scroll.\n\t// -----------------------------------------------------------------------\n\n\tconst scrollToBottom = useCallback(() => {\n\t\tconst el = ref.current;\n\t\tif (!el) return;\n\n\t\tcancelSmoothScroll();\n\t\tisAtBottomRef.current = true;\n\t\tlastAutoScrollHeightRef.current = el.scrollHeight;\n\t\tsetIsAtBottom(true);\n\t\tel.scrollTo({ top: el.scrollHeight, behavior });\n\t}, [behavior, cancelSmoothScroll]);\n\n\treturn { ref, scrollToBottom, isAtBottom };\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { createChatTransportContext, createMessage } from \"./chat-controller\";\nimport { resolveTransport } from \"./transports\";\nimport type {\n\tChatTransportRun,\n\tEventHelpers,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Default event handler — accumulates `text_delta` into the last\n// assistant message's parts.\n// ---------------------------------------------------------------------------\n\nfunction defaultOnEvent(\n\tevent: SSEEvent,\n\thelpers: EventHelpers<ContentPart>,\n): void {\n\tif (event.type === \"text_delta\") {\n\t\thelpers.appendText(event.delta);\n\t}\n\t// Other event types (e.g. tool_call) are silently ignored by default.\n\t// Users can provide their own `onEvent` to handle them.\n}\n\n// ---------------------------------------------------------------------------\n// useStreamChat\n// ---------------------------------------------------------------------------\n\nexport function useStreamChat<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): UseStreamChatReturn<TPart> {\n\tconst {\n\t\tinitialMessages,\n\t\tonEvent,\n\t\tonMessage,\n\t\tonError,\n\t\tonFinish,\n\t\ttextBatchMs = 0,\n\t} = options;\n\n\tconst [messages, setMessages] = useState(initialMessages ?? []);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [runId, setRunId] = useState<string | null>(null);\n\n\tconst runRef = useRef<ChatTransportRun | null>(null);\n\n\t// Track which run id has already been resumed to prevent re-triggering.\n\tconst resumedRunIdRef = useRef<string | null>(null);\n\n\t// When the user manually stops a run, suppress auto-resume until the\n\t// next explicit `sendMessage` call.\n\tconst manuallyStoppedRef = useRef(false);\n\n\t// We use a ref for the latest messages so callbacks created inside\n\t// transport handlers always see the current value without re-creating\n\t// closures.\n\tconst messagesRef = useRef(messages);\n\tmessagesRef.current = messages;\n\n\tconst updateMessages = useCallback(\n\t\t(next:\n\t\t\t| Message<TPart>[]\n\t\t\t| ((prev: Message<TPart>[]) => Message<TPart>[])) => {\n\t\t\tsetMessages((prev) => {\n\t\t\t\tconst resolved =\n\t\t\t\t\ttypeof next === \"function\"\n\t\t\t\t\t\t? (next as (prev: Message<TPart>[]) => Message<TPart>[])(prev)\n\t\t\t\t\t\t: next;\n\t\t\t\tmessagesRef.current = resolved;\n\t\t\t\treturn resolved;\n\t\t\t});\n\t\t},\n\t\t[],\n\t);\n\n\t// Keep transport options in a ref so that callbacks always read the\n\t// latest values without causing memoisation instability.\n\tconst transportOptionsRef = useRef(options.transportOptions);\n\ttransportOptionsRef.current = options.transportOptions;\n\n\tconst pendingTextRef = useRef(\"\");\n\tconst textFlushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\tconst flushPendingText = useCallback(() => {\n\t\tif (textFlushTimerRef.current !== null) {\n\t\t\tclearTimeout(textFlushTimerRef.current);\n\t\t\ttextFlushTimerRef.current = null;\n\t\t}\n\n\t\tconst delta = pendingTextRef.current;\n\t\tif (!delta) return;\n\n\t\tpendingTextRef.current = \"\";\n\t\tupdateMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\tconst parts = [...last.parts];\n\t\t\tconst lastPart = parts[parts.length - 1];\n\n\t\t\tif (lastPart && lastPart.type === \"text\" && \"text\" in lastPart) {\n\t\t\t\tconst textPart = lastPart as { type: \"text\"; text: string };\n\t\t\t\tparts[parts.length - 1] = {\n\t\t\t\t\t...lastPart,\n\t\t\t\t\ttext: textPart.text + delta,\n\t\t\t\t} as unknown as TPart;\n\t\t\t} else {\n\t\t\t\tparts.push({ type: \"text\", text: delta } as unknown as TPart);\n\t\t\t}\n\n\t\t\treturn [...prev.slice(0, -1), { ...last, parts }];\n\t\t});\n\t}, [updateMessages]);\n\n\tconst appendText = useCallback((delta: string) => {\n\t\tif (textBatchMs <= 0) {\n\t\t\tpendingTextRef.current += delta;\n\t\t\tflushPendingText();\n\t\t\treturn;\n\t\t}\n\n\t\tpendingTextRef.current += delta;\n\t\tif (textFlushTimerRef.current !== null) return;\n\n\t\ttextFlushTimerRef.current = setTimeout(() => {\n\t\t\tflushPendingText();\n\t\t}, textBatchMs);\n\t}, [flushPendingText, textBatchMs]);\n\n\tconst appendPart = useCallback((part: TPart) => {\n\t\tflushPendingText();\n\t\tupdateMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\treturn [\n\t\t\t\t...prev.slice(0, -1),\n\t\t\t\t{\n\t\t\t\t\t...last,\n\t\t\t\t\tparts: [...last.parts, part],\n\t\t\t\t},\n\t\t\t];\n\t\t});\n\t}, [flushPendingText, updateMessages]);\n\n\t// Stabilise transport creation: resolve once and store in a ref so that\n\t// changing values like `runId` in transportOptions won't cause a new\n\t// transport instance (and therefore a new WebSocket) to be created.\n\tconst transportRef = useRef<ReturnType<\n\t\ttypeof resolveTransport<TPart, TEvent>\n\t> | null>(null);\n\tif (!transportRef.current) {\n\t\ttransportRef.current = resolveTransport(options);\n\t}\n\tconst transport = transportRef.current;\n\n\tconst eventHandler =\n\t\tonEvent ??\n\t\t(defaultOnEvent as unknown as (\n\t\t\tevent: TEvent,\n\t\t\thelpers: EventHelpers<TPart>,\n\t\t) => void);\n\n\t// Stabilise the transport context: use refs for values that change\n\t// frequently (transportOptions callbacks) so the context object itself\n\t// stays referentially stable.\n\tconst eventHandlerRef = useRef(eventHandler);\n\teventHandlerRef.current = eventHandler;\n\n\tconst onErrorRef = useRef(onError);\n\tonErrorRef.current = onError;\n\n\tconst onFinishRef = useRef(onFinish);\n\tonFinishRef.current = onFinish;\n\n\tconst onMessageRef = useRef(onMessage);\n\tonMessageRef.current = onMessage;\n\n\tconst transportContext = useMemo(\n\t\t() =>\n\t\t\tcreateChatTransportContext({\n\t\t\t\tappendPart,\n\t\t\t\tappendText,\n\t\t\t\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) =>\n\t\t\t\t\teventHandlerRef.current(event, helpers),\n\t\t\t\tgetMessages: () => {\n\t\t\t\t\tflushPendingText();\n\t\t\t\t\treturn messagesRef.current;\n\t\t\t\t},\n\t\t\t\tonError: (...args) => onErrorRef.current?.(...args),\n\t\t\t\tonFinish: (...args) => onFinishRef.current?.(...args),\n\t\t\t\tonMessage: (...args) => onMessageRef.current?.(...args),\n\t\t\t\tsetError,\n\t\t\t\tsetIsLoading,\n\t\t\t\tsetMessages: updateMessages,\n\t\t\t\tsetRunId: (next) => {\n\t\t\t\t\tsetRunId(next);\n\t\t\t\t\ttransportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);\n\t\t\t\t\ttransportOptionsRef.current?.websocket?.onRunIdChange?.(next);\n\t\t\t\t},\n\t\t\t}),\n\t\t[appendPart, appendText, flushPendingText, updateMessages],\n\t);\n\n\tconst stop = useCallback(() => {\n\t\tconst activeRun = runRef.current;\n\t\tif (!activeRun?.stop) {\n\t\t\treturn;\n\t\t}\n\n\t\tmanuallyStoppedRef.current = true;\n\t\tvoid activeRun.stop();\n\t\trunRef.current = null;\n\t\tsetIsLoading(false);\n\t}, []);\n\n\tconst sendMessage = useCallback(\n\t\t(text: string) => {\n\t\t\tif (runRef.current || isLoading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst userMessage = createMessage<TPart>(\"user\", [\n\t\t\t\t{ type: \"text\", text } as unknown as TPart,\n\t\t\t]);\n\t\t\tconst assistantMessage = createMessage<TPart>(\"assistant\", []);\n\n\t\t\tupdateMessages((prev) => {\n\t\t\t\tconst next = [...prev, userMessage, assistantMessage];\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t\tonMessage?.(userMessage);\n\t\t\tsetError(null);\n\t\t\tsetIsLoading(true);\n\n\t\t\t// Reset the resume guard so a future resume for a new run is allowed.\n\t\t\tresumedRunIdRef.current = null;\n\t\t\tmanuallyStoppedRef.current = false;\n\n\t\t\tconst run = transport.start({ context: transportContext, message: text });\n\t\t\trunRef.current = run ?? null;\n\t\t\tif (run?.runId) {\n\t\t\t\tsetRunId(run.runId);\n\t\t\t}\n\t\t},\n\t\t[isLoading, onMessage, transport, transportContext, updateMessages],\n\t);\n\n\t// -----------------------------------------------------------------------\n\t// Auto-resume effect: attempt to reconnect to an in-flight run on mount.\n\t//\n\t// The candidate run id is read from transport options (which may change\n\t// when the app updates state). We guard against duplicate resumes for the\n\t// same run id using `resumedRunIdRef`.\n\t// -----------------------------------------------------------------------\n\n\tconst candidateRunId =\n\t\toptions.transportOptions?.backgroundSSE?.runId ??\n\t\toptions.transportOptions?.backgroundSSE?.getResumeKey?.() ??\n\t\toptions.transportOptions?.websocket?.runId ??\n\t\toptions.transportOptions?.websocket?.getResumeKey?.() ??\n\t\tnull;\n\n\tuseEffect(() => {\n\t\t// Already have an active run — don't start another.\n\t\tif (runRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// User explicitly stopped — don't auto-resume until next sendMessage.\n\t\tif (manuallyStoppedRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Already resumed this exact run id — don't retry.\n\t\tif (resumedRunIdRef.current === candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!transport.resume) {\n\t\t\treturn;\n\t\t}\n\n\t\tresumedRunIdRef.current = candidateRunId;\n\n\t\tsetError(null);\n\t\tsetIsLoading(true);\n\t\tconst run = transport.resume({\n\t\t\tcontext: transportContext,\n\t\t\trunId: candidateRunId,\n\t\t});\n\t\trunRef.current = run ?? null;\n\t\tsetRunId(candidateRunId);\n\t}, [candidateRunId, transport, transportContext]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (textFlushTimerRef.current !== null) {\n\t\t\t\tclearTimeout(textFlushTimerRef.current);\n\t\t\t\ttextFlushTimerRef.current = null;\n\t\t\t}\n\t\t\tvoid runRef.current?.close?.();\n\t\t\trunRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst prevIsLoadingRef = useRef(isLoading);\n\tuseEffect(() => {\n\t\t// Only clear the run ref on a true → false transition, not on mount\n\t\t// where isLoading starts as false. Clearing on mount would race with\n\t\t// the auto-resume effect and null out the run it just created.\n\t\tif (prevIsLoadingRef.current && !isLoading) {\n\t\t\trunRef.current = null;\n\t\t}\n\t\tprevIsLoadingRef.current = isLoading;\n\t}, [isLoading]);\n\n\treturn {\n\t\terror,\n\t\tisLoading,\n\t\tmessages,\n\t\trunId,\n\t\tsendMessage,\n\t\tsetMessages: updateMessages,\n\t\tstop,\n\t};\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport type { Dispatch, SetStateAction } from \"react\";\nimport type {\n\tChatTransportContext,\n\tEventHelpers,\n\tUseStreamChatOptions,\n} from \"./types\";\n\nexport interface ChatControllerOptions<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n> {\n\tappendPart: EventHelpers<TPart>[\"appendPart\"];\n\tappendText: EventHelpers<TPart>[\"appendText\"];\n\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) => void;\n\tgetMessages: () => Message<TPart>[];\n\tonError?: UseStreamChatOptions<TPart, TEvent>[\"onError\"];\n\tonFinish?: UseStreamChatOptions<TPart, TEvent>[\"onFinish\"];\n\tonMessage?: UseStreamChatOptions<TPart, TEvent>[\"onMessage\"];\n\tsetError: Dispatch<SetStateAction<Error | null>>;\n\tsetIsLoading: Dispatch<SetStateAction<boolean>>;\n\tsetMessages: Dispatch<SetStateAction<Message<TPart>[]>>;\n\tsetRunId: (runId: string | null) => void;\n}\n\nlet counter = 0;\n\nexport function generateId(): string {\n\treturn `msg_${Date.now()}_${++counter}`;\n}\n\nexport function createMessage<TPart extends { type: string }>(\n\trole: Message[\"role\"],\n\tparts: TPart[],\n): Message<TPart> {\n\treturn { id: generateId(), role, parts };\n}\n\nexport function createChatTransportContext<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(\n\toptions: ChatControllerOptions<TPart, TEvent>,\n): ChatTransportContext<TPart, TEvent> {\n\tconst helpers: EventHelpers<TPart> = {\n\t\tappendPart: options.appendPart,\n\t\tappendText: options.appendText,\n\t\tsetMessages: options.setMessages,\n\t};\n\n\treturn {\n\t\temit: (event) => {\n\t\t\toptions.eventHandler(event, helpers);\n\t\t},\n\t\tensureAssistantMessage: () => {\n\t\t\toptions.setMessages((prev) => {\n\t\t\t\tconst last = prev[prev.length - 1];\n\t\t\t\tif (last?.role === \"assistant\") {\n\t\t\t\t\treturn prev;\n\t\t\t\t}\n\n\t\t\t\treturn [...prev, createMessage<TPart>(\"assistant\", [])];\n\t\t\t});\n\t\t},\n\t\tfail: (error) => {\n\t\t\toptions.setError(error);\n\t\t\toptions.onError?.(error);\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\t\t},\n\t\tfinish: () => {\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\n\t\t\tconst finalMessages = options.getMessages();\n\t\t\tconst lastMessage = finalMessages[finalMessages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\toptions.onMessage?.(lastMessage);\n\t\t\t}\n\n\t\t\toptions.onFinish?.(finalMessages);\n\t\t},\n\t\tgetMessages: options.getMessages,\n\t\tsetRunId: (runId) => {\n\t\t\toptions.setRunId(runId);\n\t\t},\n\t};\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { parseSSEStream } from \"@deltakit/core\";\nimport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tUseStreamChatOptions,\n\tWebSocketTransportOptions,\n} from \"./types\";\n\nfunction toError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction resolveRunId(response: unknown): string {\n\tif (!response || typeof response !== \"object\") {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\tconst maybeRunId =\n\t\t\"runId\" in response\n\t\t\t? response.runId\n\t\t\t: \"job_id\" in response\n\t\t\t\t? response.job_id\n\t\t\t\t: null;\n\n\tif (typeof maybeRunId !== \"string\" || maybeRunId.length === 0) {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\treturn maybeRunId;\n}\n\nfunction resolveUrl(\n\turl: string | ((runId: string) => string),\n\trunId: string,\n): string {\n\treturn typeof url === \"function\" ? url(runId) : url.replace(\":runId\", runId);\n}\n\nasync function streamFetchSSE<\n\tTPart extends { type: string },\n\tTEvent extends { type: string },\n>(\n\tresponse: Response,\n\tcontext: ChatTransportContext<TPart, TEvent>,\n\tsignal: AbortSignal,\n): Promise<void> {\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`SSE request failed: ${response.status} ${response.statusText}`,\n\t\t);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"Response body is null — SSE streaming not supported\");\n\t}\n\n\tfor await (const event of parseSSEStream(response.body, signal)) {\n\t\tcontext.emit(event as unknown as TEvent);\n\t}\n}\n\nexport function createDirectSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: DirectSSETransportOptions): ChatTransport<TPart, TEvent> {\n\treturn {\n\t\tstart: ({ context, message }) => {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst run: ChatTransportRun = {\n\t\t\t\tclose: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst fetchImpl = config.fetch ?? fetch;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.api, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.body }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.headers,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.method ?? \"POST\",\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\t\tcontext.finish();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn run;\n\t\t},\n\t};\n}\n\nexport function createBackgroundSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: BackgroundSSETransportOptions): ChatTransport<TPart, TEvent> {\n\tconst fetchImpl = config.fetch ?? fetch;\n\n\tconst connect = (\n\t\trunId: string,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t): ChatTransportRun => {\n\t\tconst controller = new AbortController();\n\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst response = await fetchImpl(resolveUrl(config.eventsApi, runId), {\n\t\t\t\t\theaders: config.eventHeaders,\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\tcontext.finish();\n\t\t\t} catch (error) {\n\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\n\t\treturn {\n\t\t\tclose: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t},\n\t\t\tstop: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t\tif (config.cancelApi) {\n\t\t\t\t\tvoid fetchImpl(resolveUrl(config.cancelApi, runId), {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\trunId,\n\t\t};\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\treturn connect(runId, context);\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst startController = new AbortController();\n\t\t\tlet activeRun: ChatTransportRun | undefined;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.startApi, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.startBody }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.startHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.startMethod ?? \"POST\",\n\t\t\t\t\t\tsignal: startController.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Background SSE start failed: ${response.status} ${response.statusText}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as unknown;\n\t\t\t\t\tconst runId = (config.resolveRunId ?? resolveRunId)(data);\n\t\t\t\t\tcontext.setRunId(runId);\n\t\t\t\t\tactiveRun = connect(runId, context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tvoid activeRun?.close?.();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tactiveRun?.stop?.();\n\t\t\t\t},\n\t\t\t\trunId: null,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction defaultParseWebSocketMessage<TEvent extends { type: string }>(\n\tdata: unknown,\n): TEvent | TEvent[] | null {\n\tif (typeof data !== \"string\") {\n\t\treturn null;\n\t}\n\n\tconst parsed = JSON.parse(data) as TEvent | TEvent[];\n\treturn parsed;\n}\n\nexport function createWebSocketTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: WebSocketTransportOptions<TEvent>): ChatTransport<TPart, TEvent> {\n\tconst parseMessage = config.parseMessage ?? defaultParseWebSocketMessage;\n\tconst serializeMessage = config.serializeMessage ?? JSON.stringify;\n\n\tlet resolvedRunId: string | null = null;\n\n\tconst applyIncomingEvents = (\n\t\tparsed: TEvent | TEvent[] | null,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t) => {\n\t\tconst events = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];\n\t\tfor (const item of events) {\n\t\t\tconst nextRunId = config.resolveRunId?.(item) ?? null;\n\t\t\tif (nextRunId) {\n\t\t\t\tresolvedRunId = nextRunId;\n\t\t\t\tcontext.setRunId(nextRunId);\n\t\t\t}\n\t\t\tcontext.emit(item);\n\n\t\t\tif (item.type === \"done\") {\n\t\t\t\tcontext.finish();\n\t\t\t} else if (item.type === \"error\") {\n\t\t\t\tcontext.fail(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\"message\" in item && typeof item.message === \"string\"\n\t\t\t\t\t\t\t? item.message\n\t\t\t\t\t\t\t: \"Stream error\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\tcontext.ensureAssistantMessage();\n\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentResumePayload = false;\n\n\t\t\tconst sendResumePayload = () => {\n\t\t\t\tif (sentResumePayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentResumePayload = true;\n\t\t\t\tconst payload =\n\t\t\t\t\tconfig.buildResumePayload?.(runId) ??\n\t\t\t\t\t({ [config.runIdKey ?? \"runId\"]: runId } as Record<string, unknown>);\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendResumePayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendResumePayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst runId = config.runId ?? config.getResumeKey?.() ?? null;\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentStartPayload = false;\n\n\t\t\tconst sendStartPayload = () => {\n\t\t\t\tif (sentStartPayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentStartPayload = true;\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst payload: Record<string, unknown> = {\n\t\t\t\t\tmessage,\n\t\t\t\t\t...config.body,\n\t\t\t\t};\n\n\t\t\t\tif (runId) {\n\t\t\t\t\tpayload[config.runIdKey ?? \"runId\"] = runId;\n\t\t\t\t}\n\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendStartPayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendStartPayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport function resolveTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): ChatTransport<TPart, TEvent> {\n\tif (typeof options.transport === \"object\" && options.transport) {\n\t\treturn options.transport;\n\t}\n\n\tconst transportKind = options.transport ?? \"sse\";\n\n\tif (transportKind === \"background-sse\") {\n\t\tconst config = options.transportOptions?.backgroundSSE;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.backgroundSSE` is required when transport is \"background-sse\"',\n\t\t\t);\n\t\t}\n\t\treturn createBackgroundSSETransport(config);\n\t}\n\n\tif (transportKind === \"websocket\") {\n\t\tconst config = options.transportOptions?.websocket;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.websocket` is required when transport is \"websocket\"',\n\t\t\t);\n\t\t}\n\t\treturn createWebSocketTransport(config);\n\t}\n\n\tconst sseConfig = options.transportOptions?.sse ?? {\n\t\tapi: options.api,\n\t\tbody: options.body,\n\t\theaders: options.headers,\n\t};\n\n\tif (!sseConfig.api) {\n\t\tthrow new Error(\n\t\t\t\"`api` or `transportOptions.sse.api` is required when using the default SSE transport\",\n\t\t);\n\t}\n\n\treturn createDirectSSETransport({\n\t\t...sseConfig,\n\t\tapi: sseConfig.api,\n\t});\n}\n"],"mappings":"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","names":["import_core","import_react"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -163,6 +163,11 @@ interface UseStreamChatOptions<TPart extends {
|
|
|
163
163
|
headers?: Record<string, string>;
|
|
164
164
|
/** Extra fields merged into the direct-SSE POST body alongside `message`. */
|
|
165
165
|
body?: Record<string, unknown>;
|
|
166
|
+
/**
|
|
167
|
+
* Buffer incoming `text_delta` events and commit them to React state on a
|
|
168
|
+
* short cadence instead of every token. Default: `0` (disabled).
|
|
169
|
+
*/
|
|
170
|
+
textBatchMs?: number;
|
|
166
171
|
/**
|
|
167
172
|
* Custom handler for each SSE event. When provided, this **replaces** the
|
|
168
173
|
* default `text_delta` handling — giving you full control over how events
|
|
@@ -198,7 +203,7 @@ interface UseStreamChatReturn<TPart extends {
|
|
|
198
203
|
setMessages: Dispatch<SetStateAction<Message<TPart>[]>>;
|
|
199
204
|
}
|
|
200
205
|
interface UseAutoScrollOptions {
|
|
201
|
-
/** Scroll behavior
|
|
206
|
+
/** Scroll behavior for explicit `scrollToBottom()` calls. Default: `"instant"`. */
|
|
202
207
|
behavior?: ScrollBehavior;
|
|
203
208
|
/** Whether auto-scroll is enabled. Default: `true`. */
|
|
204
209
|
enabled?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -163,6 +163,11 @@ interface UseStreamChatOptions<TPart extends {
|
|
|
163
163
|
headers?: Record<string, string>;
|
|
164
164
|
/** Extra fields merged into the direct-SSE POST body alongside `message`. */
|
|
165
165
|
body?: Record<string, unknown>;
|
|
166
|
+
/**
|
|
167
|
+
* Buffer incoming `text_delta` events and commit them to React state on a
|
|
168
|
+
* short cadence instead of every token. Default: `0` (disabled).
|
|
169
|
+
*/
|
|
170
|
+
textBatchMs?: number;
|
|
166
171
|
/**
|
|
167
172
|
* Custom handler for each SSE event. When provided, this **replaces** the
|
|
168
173
|
* default `text_delta` handling — giving you full control over how events
|
|
@@ -198,7 +203,7 @@ interface UseStreamChatReturn<TPart extends {
|
|
|
198
203
|
setMessages: Dispatch<SetStateAction<Message<TPart>[]>>;
|
|
199
204
|
}
|
|
200
205
|
interface UseAutoScrollOptions {
|
|
201
|
-
/** Scroll behavior
|
|
206
|
+
/** Scroll behavior for explicit `scrollToBottom()` calls. Default: `"instant"`. */
|
|
202
207
|
behavior?: ScrollBehavior;
|
|
203
208
|
/** Whether auto-scroll is enabled. Default: `true`. */
|
|
204
209
|
enabled?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -18,25 +18,70 @@ function useAutoScroll(dependencies, options) {
|
|
|
18
18
|
const isAtBottomRef = useRef(true);
|
|
19
19
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
20
20
|
const rafRef = useRef(null);
|
|
21
|
+
const smoothRafRef = useRef(null);
|
|
22
|
+
const smoothTargetRef = useRef(null);
|
|
23
|
+
const lastAutoScrollHeightRef = useRef(null);
|
|
24
|
+
const cancelSmoothScroll = useCallback(() => {
|
|
25
|
+
if (smoothRafRef.current != null) {
|
|
26
|
+
cancelAnimationFrame(smoothRafRef.current);
|
|
27
|
+
smoothRafRef.current = null;
|
|
28
|
+
}
|
|
29
|
+
smoothTargetRef.current = null;
|
|
30
|
+
}, []);
|
|
31
|
+
const runSmoothScroll = useCallback(() => {
|
|
32
|
+
if (smoothRafRef.current != null) return;
|
|
33
|
+
const tick = () => {
|
|
34
|
+
const el = ref.current;
|
|
35
|
+
if (!el || !isAtBottomRef.current) {
|
|
36
|
+
cancelSmoothScroll();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const target = smoothTargetRef.current ?? el.scrollHeight;
|
|
40
|
+
const delta = target - el.scrollTop;
|
|
41
|
+
if (delta <= 1) {
|
|
42
|
+
el.scrollTop = target;
|
|
43
|
+
lastAutoScrollHeightRef.current = target;
|
|
44
|
+
smoothRafRef.current = null;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const step = Math.min(Math.max(delta * 0.35, 12), 120);
|
|
48
|
+
el.scrollTop = Math.min(target, el.scrollTop + step);
|
|
49
|
+
smoothRafRef.current = requestAnimationFrame(tick);
|
|
50
|
+
};
|
|
51
|
+
smoothRafRef.current = requestAnimationFrame(tick);
|
|
52
|
+
}, [cancelSmoothScroll]);
|
|
21
53
|
const scheduleScroll = useCallback(() => {
|
|
22
54
|
if (rafRef.current != null) return;
|
|
23
55
|
rafRef.current = requestAnimationFrame(() => {
|
|
24
56
|
rafRef.current = null;
|
|
25
57
|
const el = ref.current;
|
|
26
|
-
if (el
|
|
27
|
-
|
|
58
|
+
if (!el || !isAtBottomRef.current) return;
|
|
59
|
+
const nextHeight = el.scrollHeight;
|
|
60
|
+
if (lastAutoScrollHeightRef.current === nextHeight) return;
|
|
61
|
+
lastAutoScrollHeightRef.current = nextHeight;
|
|
62
|
+
if (behavior === "smooth") {
|
|
63
|
+
smoothTargetRef.current = nextHeight;
|
|
64
|
+
runSmoothScroll();
|
|
65
|
+
return;
|
|
28
66
|
}
|
|
67
|
+
cancelSmoothScroll();
|
|
68
|
+
el.scrollTop = nextHeight;
|
|
29
69
|
});
|
|
30
|
-
}, [behavior]);
|
|
70
|
+
}, [behavior, cancelSmoothScroll, runSmoothScroll]);
|
|
31
71
|
useEffect(() => {
|
|
32
72
|
const el = ref.current;
|
|
33
73
|
if (!el || !enabled) return;
|
|
34
74
|
const handleScroll = () => {
|
|
35
75
|
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight <= threshold;
|
|
36
76
|
isAtBottomRef.current = atBottom;
|
|
77
|
+
if (!atBottom) {
|
|
78
|
+
lastAutoScrollHeightRef.current = null;
|
|
79
|
+
cancelSmoothScroll();
|
|
80
|
+
}
|
|
37
81
|
setIsAtBottom((prev) => prev === atBottom ? prev : atBottom);
|
|
38
82
|
};
|
|
39
83
|
el.addEventListener("scroll", handleScroll, { passive: true });
|
|
84
|
+
handleScroll();
|
|
40
85
|
return () => el.removeEventListener("scroll", handleScroll);
|
|
41
86
|
}, [enabled, threshold]);
|
|
42
87
|
useEffect(() => {
|
|
@@ -72,15 +117,18 @@ function useAutoScroll(dependencies, options) {
|
|
|
72
117
|
cancelAnimationFrame(rafRef.current);
|
|
73
118
|
rafRef.current = null;
|
|
74
119
|
}
|
|
120
|
+
cancelSmoothScroll();
|
|
75
121
|
};
|
|
76
|
-
}, []);
|
|
122
|
+
}, [cancelSmoothScroll]);
|
|
77
123
|
const scrollToBottom = useCallback(() => {
|
|
78
124
|
const el = ref.current;
|
|
79
125
|
if (!el) return;
|
|
126
|
+
cancelSmoothScroll();
|
|
80
127
|
isAtBottomRef.current = true;
|
|
128
|
+
lastAutoScrollHeightRef.current = el.scrollHeight;
|
|
81
129
|
setIsAtBottom(true);
|
|
82
130
|
el.scrollTo({ top: el.scrollHeight, behavior });
|
|
83
|
-
}, [behavior]);
|
|
131
|
+
}, [behavior, cancelSmoothScroll]);
|
|
84
132
|
return { ref, scrollToBottom, isAtBottom };
|
|
85
133
|
}
|
|
86
134
|
|
|
@@ -480,7 +528,14 @@ function defaultOnEvent(event, helpers) {
|
|
|
480
528
|
}
|
|
481
529
|
}
|
|
482
530
|
function useStreamChat(options) {
|
|
483
|
-
const {
|
|
531
|
+
const {
|
|
532
|
+
initialMessages,
|
|
533
|
+
onEvent,
|
|
534
|
+
onMessage,
|
|
535
|
+
onError,
|
|
536
|
+
onFinish,
|
|
537
|
+
textBatchMs = 0
|
|
538
|
+
} = options;
|
|
484
539
|
const [messages, setMessages] = useState2(initialMessages ?? []);
|
|
485
540
|
const [isLoading, setIsLoading] = useState2(false);
|
|
486
541
|
const [error, setError] = useState2(null);
|
|
@@ -490,10 +545,29 @@ function useStreamChat(options) {
|
|
|
490
545
|
const manuallyStoppedRef = useRef2(false);
|
|
491
546
|
const messagesRef = useRef2(messages);
|
|
492
547
|
messagesRef.current = messages;
|
|
548
|
+
const updateMessages = useCallback2(
|
|
549
|
+
(next) => {
|
|
550
|
+
setMessages((prev) => {
|
|
551
|
+
const resolved = typeof next === "function" ? next(prev) : next;
|
|
552
|
+
messagesRef.current = resolved;
|
|
553
|
+
return resolved;
|
|
554
|
+
});
|
|
555
|
+
},
|
|
556
|
+
[]
|
|
557
|
+
);
|
|
493
558
|
const transportOptionsRef = useRef2(options.transportOptions);
|
|
494
559
|
transportOptionsRef.current = options.transportOptions;
|
|
495
|
-
const
|
|
496
|
-
|
|
560
|
+
const pendingTextRef = useRef2("");
|
|
561
|
+
const textFlushTimerRef = useRef2(null);
|
|
562
|
+
const flushPendingText = useCallback2(() => {
|
|
563
|
+
if (textFlushTimerRef.current !== null) {
|
|
564
|
+
clearTimeout(textFlushTimerRef.current);
|
|
565
|
+
textFlushTimerRef.current = null;
|
|
566
|
+
}
|
|
567
|
+
const delta = pendingTextRef.current;
|
|
568
|
+
if (!delta) return;
|
|
569
|
+
pendingTextRef.current = "";
|
|
570
|
+
updateMessages((prev) => {
|
|
497
571
|
const last = prev[prev.length - 1];
|
|
498
572
|
if (!last || last.role !== "assistant") return prev;
|
|
499
573
|
const parts = [...last.parts];
|
|
@@ -509,9 +583,22 @@ function useStreamChat(options) {
|
|
|
509
583
|
}
|
|
510
584
|
return [...prev.slice(0, -1), { ...last, parts }];
|
|
511
585
|
});
|
|
512
|
-
}, []);
|
|
586
|
+
}, [updateMessages]);
|
|
587
|
+
const appendText = useCallback2((delta) => {
|
|
588
|
+
if (textBatchMs <= 0) {
|
|
589
|
+
pendingTextRef.current += delta;
|
|
590
|
+
flushPendingText();
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
pendingTextRef.current += delta;
|
|
594
|
+
if (textFlushTimerRef.current !== null) return;
|
|
595
|
+
textFlushTimerRef.current = setTimeout(() => {
|
|
596
|
+
flushPendingText();
|
|
597
|
+
}, textBatchMs);
|
|
598
|
+
}, [flushPendingText, textBatchMs]);
|
|
513
599
|
const appendPart = useCallback2((part) => {
|
|
514
|
-
|
|
600
|
+
flushPendingText();
|
|
601
|
+
updateMessages((prev) => {
|
|
515
602
|
const last = prev[prev.length - 1];
|
|
516
603
|
if (!last || last.role !== "assistant") return prev;
|
|
517
604
|
return [
|
|
@@ -522,7 +609,7 @@ function useStreamChat(options) {
|
|
|
522
609
|
}
|
|
523
610
|
];
|
|
524
611
|
});
|
|
525
|
-
}, []);
|
|
612
|
+
}, [flushPendingText, updateMessages]);
|
|
526
613
|
const transportRef = useRef2(null);
|
|
527
614
|
if (!transportRef.current) {
|
|
528
615
|
transportRef.current = resolveTransport(options);
|
|
@@ -542,20 +629,23 @@ function useStreamChat(options) {
|
|
|
542
629
|
appendPart,
|
|
543
630
|
appendText,
|
|
544
631
|
eventHandler: (event, helpers) => eventHandlerRef.current(event, helpers),
|
|
545
|
-
getMessages: () =>
|
|
632
|
+
getMessages: () => {
|
|
633
|
+
flushPendingText();
|
|
634
|
+
return messagesRef.current;
|
|
635
|
+
},
|
|
546
636
|
onError: (...args) => onErrorRef.current?.(...args),
|
|
547
637
|
onFinish: (...args) => onFinishRef.current?.(...args),
|
|
548
638
|
onMessage: (...args) => onMessageRef.current?.(...args),
|
|
549
639
|
setError,
|
|
550
640
|
setIsLoading,
|
|
551
|
-
setMessages,
|
|
641
|
+
setMessages: updateMessages,
|
|
552
642
|
setRunId: (next) => {
|
|
553
643
|
setRunId(next);
|
|
554
644
|
transportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);
|
|
555
645
|
transportOptionsRef.current?.websocket?.onRunIdChange?.(next);
|
|
556
646
|
}
|
|
557
647
|
}),
|
|
558
|
-
[appendPart, appendText]
|
|
648
|
+
[appendPart, appendText, flushPendingText, updateMessages]
|
|
559
649
|
);
|
|
560
650
|
const stop = useCallback2(() => {
|
|
561
651
|
const activeRun = runRef.current;
|
|
@@ -576,9 +666,8 @@ function useStreamChat(options) {
|
|
|
576
666
|
{ type: "text", text }
|
|
577
667
|
]);
|
|
578
668
|
const assistantMessage = createMessage("assistant", []);
|
|
579
|
-
|
|
669
|
+
updateMessages((prev) => {
|
|
580
670
|
const next = [...prev, userMessage, assistantMessage];
|
|
581
|
-
messagesRef.current = next;
|
|
582
671
|
return next;
|
|
583
672
|
});
|
|
584
673
|
onMessage?.(userMessage);
|
|
@@ -592,7 +681,7 @@ function useStreamChat(options) {
|
|
|
592
681
|
setRunId(run.runId);
|
|
593
682
|
}
|
|
594
683
|
},
|
|
595
|
-
[isLoading, onMessage, transport, transportContext]
|
|
684
|
+
[isLoading, onMessage, transport, transportContext, updateMessages]
|
|
596
685
|
);
|
|
597
686
|
const candidateRunId = options.transportOptions?.backgroundSSE?.runId ?? options.transportOptions?.backgroundSSE?.getResumeKey?.() ?? options.transportOptions?.websocket?.runId ?? options.transportOptions?.websocket?.getResumeKey?.() ?? null;
|
|
598
687
|
useEffect2(() => {
|
|
@@ -623,6 +712,10 @@ function useStreamChat(options) {
|
|
|
623
712
|
}, [candidateRunId, transport, transportContext]);
|
|
624
713
|
useEffect2(() => {
|
|
625
714
|
return () => {
|
|
715
|
+
if (textFlushTimerRef.current !== null) {
|
|
716
|
+
clearTimeout(textFlushTimerRef.current);
|
|
717
|
+
textFlushTimerRef.current = null;
|
|
718
|
+
}
|
|
626
719
|
void runRef.current?.close?.();
|
|
627
720
|
runRef.current = null;
|
|
628
721
|
};
|
|
@@ -640,7 +733,7 @@ function useStreamChat(options) {
|
|
|
640
733
|
messages,
|
|
641
734
|
runId,
|
|
642
735
|
sendMessage,
|
|
643
|
-
setMessages,
|
|
736
|
+
setMessages: updateMessages,
|
|
644
737
|
stop
|
|
645
738
|
};
|
|
646
739
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/use-auto-scroll.ts","../src/use-stream-chat.ts","../src/chat-controller.ts","../src/transports.ts"],"sourcesContent":["// Re-export core types so consumers only need to import from @deltakit/react\nexport type {\n\tContentPart,\n\tMessage,\n\tReasoningPart,\n\tSSEEvent,\n\tTextDeltaEvent,\n\tTextPart,\n\tToolCallEvent,\n\tToolCallPart,\n\tToolResultEvent,\n} from \"@deltakit/core\";\nexport {\n\tfromAgnoAgents,\n\tfromOpenAiAgents,\n\tparseSSEStream,\n} from \"@deltakit/core\";\n\nexport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tEventHelpers,\n\tTransportOptions,\n\tUseAutoScrollOptions,\n\tUseAutoScrollReturn,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n\tWebSocketTransportOptions,\n} from \"./types\";\nexport { useAutoScroll } from \"./use-auto-scroll\";\nexport { useStreamChat } from \"./use-stream-chat\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseAutoScrollOptions, UseAutoScrollReturn } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_THRESHOLD = 50;\n\n// ---------------------------------------------------------------------------\n// useAutoScroll\n// ---------------------------------------------------------------------------\n\nexport function useAutoScroll<T extends HTMLElement = HTMLDivElement>(\n\tdependencies: unknown[],\n\toptions?: UseAutoScrollOptions,\n): UseAutoScrollReturn<T> {\n\tconst {\n\t\tbehavior = \"instant\",\n\t\tenabled = true,\n\t\tthreshold = DEFAULT_THRESHOLD,\n\t} = options ?? {};\n\n\tconst ref = useRef<T | null>(null);\n\tconst isAtBottomRef = useRef(true);\n\tconst [isAtBottom, setIsAtBottom] = useState(true);\n\n\t// A single rAF id shared across all scroll sources — ensures we never\n\t// call scrollTo() more than once per frame, no matter how many\n\t// MutationObserver / ResizeObserver callbacks fire.\n\tconst rafRef = useRef<number | null>(null);\n\n\tconst scheduleScroll = useCallback(() => {\n\t\tif (rafRef.current != null) return;\n\t\trafRef.current = requestAnimationFrame(() => {\n\t\t\trafRef.current = null;\n\t\t\tconst el = ref.current;\n\t\t\tif (el && isAtBottomRef.current) {\n\t\t\t\tel.scrollTo({ top: el.scrollHeight, behavior });\n\t\t\t}\n\t\t});\n\t}, [behavior]);\n\n\t// -----------------------------------------------------------------------\n\t// Track whether the user is near the bottom via scroll events.\n\t// Only triggers a React re-render when the boolean actually changes.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst handleScroll = () => {\n\t\t\tconst atBottom =\n\t\t\t\tel.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n\t\t\tisAtBottomRef.current = atBottom;\n\t\t\tsetIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n\t\t};\n\n\t\tel.addEventListener(\"scroll\", handleScroll, { passive: true });\n\t\treturn () => el.removeEventListener(\"scroll\", handleScroll);\n\t}, [enabled, threshold]);\n\n\t// -----------------------------------------------------------------------\n\t// Scroll to bottom when dependencies change (if pinned).\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tif (!enabled || !isAtBottomRef.current) return;\n\t\tscheduleScroll();\n\t\t// biome-ignore lint/correctness/useExhaustiveDependencies: dependencies are passed dynamically by the consumer\n\t}, dependencies);\n\n\t// -----------------------------------------------------------------------\n\t// MutationObserver + ResizeObserver — catch content changes during\n\t// streaming that happen between React re-renders (e.g. DOM mutations\n\t// from markdown renderers). Scroll calls are batched via rAF so we\n\t// scroll at most once per frame.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst resizeObserver = new ResizeObserver(scheduleScroll);\n\n\t\t// Observe existing children for size changes.\n\t\tfor (const child of el.children) {\n\t\t\tresizeObserver.observe(child);\n\t\t}\n\n\t\t// Watch for new children added to the container.\n\t\tconst mutationObserver = new MutationObserver((mutations) => {\n\t\t\tfor (const mutation of mutations) {\n\t\t\t\tfor (const node of mutation.addedNodes) {\n\t\t\t\t\tif (node instanceof Element) {\n\t\t\t\t\t\tresizeObserver.observe(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tscheduleScroll();\n\t\t});\n\n\t\tmutationObserver.observe(el, { childList: true, subtree: true });\n\n\t\treturn () => {\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [enabled, scheduleScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Cancel any pending rAF on unmount.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (rafRef.current != null) {\n\t\t\t\tcancelAnimationFrame(rafRef.current);\n\t\t\t\trafRef.current = null;\n\t\t\t}\n\t\t};\n\t}, []);\n\n\t// -----------------------------------------------------------------------\n\t// scrollToBottom — imperative function that scrolls to the bottom and\n\t// re-pins auto-scroll.\n\t// -----------------------------------------------------------------------\n\n\tconst scrollToBottom = useCallback(() => {\n\t\tconst el = ref.current;\n\t\tif (!el) return;\n\n\t\tisAtBottomRef.current = true;\n\t\tsetIsAtBottom(true);\n\t\tel.scrollTo({ top: el.scrollHeight, behavior });\n\t}, [behavior]);\n\n\treturn { ref, scrollToBottom, isAtBottom };\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { createChatTransportContext, createMessage } from \"./chat-controller\";\nimport { resolveTransport } from \"./transports\";\nimport type {\n\tChatTransportRun,\n\tEventHelpers,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Default event handler — accumulates `text_delta` into the last\n// assistant message's parts.\n// ---------------------------------------------------------------------------\n\nfunction defaultOnEvent(\n\tevent: SSEEvent,\n\thelpers: EventHelpers<ContentPart>,\n): void {\n\tif (event.type === \"text_delta\") {\n\t\thelpers.appendText(event.delta);\n\t}\n\t// Other event types (e.g. tool_call) are silently ignored by default.\n\t// Users can provide their own `onEvent` to handle them.\n}\n\n// ---------------------------------------------------------------------------\n// useStreamChat\n// ---------------------------------------------------------------------------\n\nexport function useStreamChat<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): UseStreamChatReturn<TPart> {\n\tconst { initialMessages, onEvent, onMessage, onError, onFinish } = options;\n\n\tconst [messages, setMessages] = useState(initialMessages ?? []);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [runId, setRunId] = useState<string | null>(null);\n\n\tconst runRef = useRef<ChatTransportRun | null>(null);\n\n\t// Track which run id has already been resumed to prevent re-triggering.\n\tconst resumedRunIdRef = useRef<string | null>(null);\n\n\t// When the user manually stops a run, suppress auto-resume until the\n\t// next explicit `sendMessage` call.\n\tconst manuallyStoppedRef = useRef(false);\n\n\t// We use a ref for the latest messages so callbacks created inside\n\t// transport handlers always see the current value without re-creating\n\t// closures.\n\tconst messagesRef = useRef(messages);\n\tmessagesRef.current = messages;\n\n\t// Keep transport options in a ref so that callbacks always read the\n\t// latest values without causing memoisation instability.\n\tconst transportOptionsRef = useRef(options.transportOptions);\n\ttransportOptionsRef.current = options.transportOptions;\n\n\tconst appendText = useCallback((delta: string) => {\n\t\tsetMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\tconst parts = [...last.parts];\n\t\t\tconst lastPart = parts[parts.length - 1];\n\n\t\t\tif (lastPart && lastPart.type === \"text\" && \"text\" in lastPart) {\n\t\t\t\tconst textPart = lastPart as { type: \"text\"; text: string };\n\t\t\t\tparts[parts.length - 1] = {\n\t\t\t\t\t...lastPart,\n\t\t\t\t\ttext: textPart.text + delta,\n\t\t\t\t} as unknown as TPart;\n\t\t\t} else {\n\t\t\t\tparts.push({ type: \"text\", text: delta } as unknown as TPart);\n\t\t\t}\n\n\t\t\treturn [...prev.slice(0, -1), { ...last, parts }];\n\t\t});\n\t}, []);\n\n\tconst appendPart = useCallback((part: TPart) => {\n\t\tsetMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\treturn [\n\t\t\t\t...prev.slice(0, -1),\n\t\t\t\t{\n\t\t\t\t\t...last,\n\t\t\t\t\tparts: [...last.parts, part],\n\t\t\t\t},\n\t\t\t];\n\t\t});\n\t}, []);\n\n\t// Stabilise transport creation: resolve once and store in a ref so that\n\t// changing values like `runId` in transportOptions won't cause a new\n\t// transport instance (and therefore a new WebSocket) to be created.\n\tconst transportRef = useRef<ReturnType<\n\t\ttypeof resolveTransport<TPart, TEvent>\n\t> | null>(null);\n\tif (!transportRef.current) {\n\t\ttransportRef.current = resolveTransport(options);\n\t}\n\tconst transport = transportRef.current;\n\n\tconst eventHandler =\n\t\tonEvent ??\n\t\t(defaultOnEvent as unknown as (\n\t\t\tevent: TEvent,\n\t\t\thelpers: EventHelpers<TPart>,\n\t\t) => void);\n\n\t// Stabilise the transport context: use refs for values that change\n\t// frequently (transportOptions callbacks) so the context object itself\n\t// stays referentially stable.\n\tconst eventHandlerRef = useRef(eventHandler);\n\teventHandlerRef.current = eventHandler;\n\n\tconst onErrorRef = useRef(onError);\n\tonErrorRef.current = onError;\n\n\tconst onFinishRef = useRef(onFinish);\n\tonFinishRef.current = onFinish;\n\n\tconst onMessageRef = useRef(onMessage);\n\tonMessageRef.current = onMessage;\n\n\tconst transportContext = useMemo(\n\t\t() =>\n\t\t\tcreateChatTransportContext({\n\t\t\t\tappendPart,\n\t\t\t\tappendText,\n\t\t\t\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) =>\n\t\t\t\t\teventHandlerRef.current(event, helpers),\n\t\t\t\tgetMessages: () => messagesRef.current,\n\t\t\t\tonError: (...args) => onErrorRef.current?.(...args),\n\t\t\t\tonFinish: (...args) => onFinishRef.current?.(...args),\n\t\t\t\tonMessage: (...args) => onMessageRef.current?.(...args),\n\t\t\t\tsetError,\n\t\t\t\tsetIsLoading,\n\t\t\t\tsetMessages,\n\t\t\t\tsetRunId: (next) => {\n\t\t\t\t\tsetRunId(next);\n\t\t\t\t\ttransportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);\n\t\t\t\t\ttransportOptionsRef.current?.websocket?.onRunIdChange?.(next);\n\t\t\t\t},\n\t\t\t}),\n\t\t[appendPart, appendText],\n\t);\n\n\tconst stop = useCallback(() => {\n\t\tconst activeRun = runRef.current;\n\t\tif (!activeRun?.stop) {\n\t\t\treturn;\n\t\t}\n\n\t\tmanuallyStoppedRef.current = true;\n\t\tvoid activeRun.stop();\n\t\trunRef.current = null;\n\t\tsetIsLoading(false);\n\t}, []);\n\n\tconst sendMessage = useCallback(\n\t\t(text: string) => {\n\t\t\tif (runRef.current || isLoading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst userMessage = createMessage<TPart>(\"user\", [\n\t\t\t\t{ type: \"text\", text } as unknown as TPart,\n\t\t\t]);\n\t\t\tconst assistantMessage = createMessage<TPart>(\"assistant\", []);\n\n\t\t\tsetMessages((prev) => {\n\t\t\t\tconst next = [...prev, userMessage, assistantMessage];\n\t\t\t\tmessagesRef.current = next;\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t\tonMessage?.(userMessage);\n\t\t\tsetError(null);\n\t\t\tsetIsLoading(true);\n\n\t\t\t// Reset the resume guard so a future resume for a new run is allowed.\n\t\t\tresumedRunIdRef.current = null;\n\t\t\tmanuallyStoppedRef.current = false;\n\n\t\t\tconst run = transport.start({ context: transportContext, message: text });\n\t\t\trunRef.current = run ?? null;\n\t\t\tif (run?.runId) {\n\t\t\t\tsetRunId(run.runId);\n\t\t\t}\n\t\t},\n\t\t[isLoading, onMessage, transport, transportContext],\n\t);\n\n\t// -----------------------------------------------------------------------\n\t// Auto-resume effect: attempt to reconnect to an in-flight run on mount.\n\t//\n\t// The candidate run id is read from transport options (which may change\n\t// when the app updates state). We guard against duplicate resumes for the\n\t// same run id using `resumedRunIdRef`.\n\t// -----------------------------------------------------------------------\n\n\tconst candidateRunId =\n\t\toptions.transportOptions?.backgroundSSE?.runId ??\n\t\toptions.transportOptions?.backgroundSSE?.getResumeKey?.() ??\n\t\toptions.transportOptions?.websocket?.runId ??\n\t\toptions.transportOptions?.websocket?.getResumeKey?.() ??\n\t\tnull;\n\n\tuseEffect(() => {\n\t\t// Already have an active run — don't start another.\n\t\tif (runRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// User explicitly stopped — don't auto-resume until next sendMessage.\n\t\tif (manuallyStoppedRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Already resumed this exact run id — don't retry.\n\t\tif (resumedRunIdRef.current === candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!transport.resume) {\n\t\t\treturn;\n\t\t}\n\n\t\tresumedRunIdRef.current = candidateRunId;\n\n\t\tsetError(null);\n\t\tsetIsLoading(true);\n\t\tconst run = transport.resume({\n\t\t\tcontext: transportContext,\n\t\t\trunId: candidateRunId,\n\t\t});\n\t\trunRef.current = run ?? null;\n\t\tsetRunId(candidateRunId);\n\t}, [candidateRunId, transport, transportContext]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tvoid runRef.current?.close?.();\n\t\t\trunRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst prevIsLoadingRef = useRef(isLoading);\n\tuseEffect(() => {\n\t\t// Only clear the run ref on a true → false transition, not on mount\n\t\t// where isLoading starts as false. Clearing on mount would race with\n\t\t// the auto-resume effect and null out the run it just created.\n\t\tif (prevIsLoadingRef.current && !isLoading) {\n\t\t\trunRef.current = null;\n\t\t}\n\t\tprevIsLoadingRef.current = isLoading;\n\t}, [isLoading]);\n\n\treturn {\n\t\terror,\n\t\tisLoading,\n\t\tmessages,\n\t\trunId,\n\t\tsendMessage,\n\t\tsetMessages,\n\t\tstop,\n\t};\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport type { Dispatch, SetStateAction } from \"react\";\nimport type {\n\tChatTransportContext,\n\tEventHelpers,\n\tUseStreamChatOptions,\n} from \"./types\";\n\nexport interface ChatControllerOptions<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n> {\n\tappendPart: EventHelpers<TPart>[\"appendPart\"];\n\tappendText: EventHelpers<TPart>[\"appendText\"];\n\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) => void;\n\tgetMessages: () => Message<TPart>[];\n\tonError?: UseStreamChatOptions<TPart, TEvent>[\"onError\"];\n\tonFinish?: UseStreamChatOptions<TPart, TEvent>[\"onFinish\"];\n\tonMessage?: UseStreamChatOptions<TPart, TEvent>[\"onMessage\"];\n\tsetError: Dispatch<SetStateAction<Error | null>>;\n\tsetIsLoading: Dispatch<SetStateAction<boolean>>;\n\tsetMessages: Dispatch<SetStateAction<Message<TPart>[]>>;\n\tsetRunId: (runId: string | null) => void;\n}\n\nlet counter = 0;\n\nexport function generateId(): string {\n\treturn `msg_${Date.now()}_${++counter}`;\n}\n\nexport function createMessage<TPart extends { type: string }>(\n\trole: Message[\"role\"],\n\tparts: TPart[],\n): Message<TPart> {\n\treturn { id: generateId(), role, parts };\n}\n\nexport function createChatTransportContext<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(\n\toptions: ChatControllerOptions<TPart, TEvent>,\n): ChatTransportContext<TPart, TEvent> {\n\tconst helpers: EventHelpers<TPart> = {\n\t\tappendPart: options.appendPart,\n\t\tappendText: options.appendText,\n\t\tsetMessages: options.setMessages,\n\t};\n\n\treturn {\n\t\temit: (event) => {\n\t\t\toptions.eventHandler(event, helpers);\n\t\t},\n\t\tensureAssistantMessage: () => {\n\t\t\toptions.setMessages((prev) => {\n\t\t\t\tconst last = prev[prev.length - 1];\n\t\t\t\tif (last?.role === \"assistant\") {\n\t\t\t\t\treturn prev;\n\t\t\t\t}\n\n\t\t\t\treturn [...prev, createMessage<TPart>(\"assistant\", [])];\n\t\t\t});\n\t\t},\n\t\tfail: (error) => {\n\t\t\toptions.setError(error);\n\t\t\toptions.onError?.(error);\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\t\t},\n\t\tfinish: () => {\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\n\t\t\tconst finalMessages = options.getMessages();\n\t\t\tconst lastMessage = finalMessages[finalMessages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\toptions.onMessage?.(lastMessage);\n\t\t\t}\n\n\t\t\toptions.onFinish?.(finalMessages);\n\t\t},\n\t\tgetMessages: options.getMessages,\n\t\tsetRunId: (runId) => {\n\t\t\toptions.setRunId(runId);\n\t\t},\n\t};\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { parseSSEStream } from \"@deltakit/core\";\nimport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tUseStreamChatOptions,\n\tWebSocketTransportOptions,\n} from \"./types\";\n\nfunction toError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction resolveRunId(response: unknown): string {\n\tif (!response || typeof response !== \"object\") {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\tconst maybeRunId =\n\t\t\"runId\" in response\n\t\t\t? response.runId\n\t\t\t: \"job_id\" in response\n\t\t\t\t? response.job_id\n\t\t\t\t: null;\n\n\tif (typeof maybeRunId !== \"string\" || maybeRunId.length === 0) {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\treturn maybeRunId;\n}\n\nfunction resolveUrl(\n\turl: string | ((runId: string) => string),\n\trunId: string,\n): string {\n\treturn typeof url === \"function\" ? url(runId) : url.replace(\":runId\", runId);\n}\n\nasync function streamFetchSSE<\n\tTPart extends { type: string },\n\tTEvent extends { type: string },\n>(\n\tresponse: Response,\n\tcontext: ChatTransportContext<TPart, TEvent>,\n\tsignal: AbortSignal,\n): Promise<void> {\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`SSE request failed: ${response.status} ${response.statusText}`,\n\t\t);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"Response body is null — SSE streaming not supported\");\n\t}\n\n\tfor await (const event of parseSSEStream(response.body, signal)) {\n\t\tcontext.emit(event as unknown as TEvent);\n\t}\n}\n\nexport function createDirectSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: DirectSSETransportOptions): ChatTransport<TPart, TEvent> {\n\treturn {\n\t\tstart: ({ context, message }) => {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst run: ChatTransportRun = {\n\t\t\t\tclose: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst fetchImpl = config.fetch ?? fetch;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.api, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.body }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.headers,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.method ?? \"POST\",\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\t\tcontext.finish();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn run;\n\t\t},\n\t};\n}\n\nexport function createBackgroundSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: BackgroundSSETransportOptions): ChatTransport<TPart, TEvent> {\n\tconst fetchImpl = config.fetch ?? fetch;\n\n\tconst connect = (\n\t\trunId: string,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t): ChatTransportRun => {\n\t\tconst controller = new AbortController();\n\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst response = await fetchImpl(resolveUrl(config.eventsApi, runId), {\n\t\t\t\t\theaders: config.eventHeaders,\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\tcontext.finish();\n\t\t\t} catch (error) {\n\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\n\t\treturn {\n\t\t\tclose: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t},\n\t\t\tstop: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t\tif (config.cancelApi) {\n\t\t\t\t\tvoid fetchImpl(resolveUrl(config.cancelApi, runId), {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\trunId,\n\t\t};\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\treturn connect(runId, context);\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst startController = new AbortController();\n\t\t\tlet activeRun: ChatTransportRun | undefined;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.startApi, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.startBody }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.startHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.startMethod ?? \"POST\",\n\t\t\t\t\t\tsignal: startController.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Background SSE start failed: ${response.status} ${response.statusText}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as unknown;\n\t\t\t\t\tconst runId = (config.resolveRunId ?? resolveRunId)(data);\n\t\t\t\t\tcontext.setRunId(runId);\n\t\t\t\t\tactiveRun = connect(runId, context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tvoid activeRun?.close?.();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tactiveRun?.stop?.();\n\t\t\t\t},\n\t\t\t\trunId: null,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction defaultParseWebSocketMessage<TEvent extends { type: string }>(\n\tdata: unknown,\n): TEvent | TEvent[] | null {\n\tif (typeof data !== \"string\") {\n\t\treturn null;\n\t}\n\n\tconst parsed = JSON.parse(data) as TEvent | TEvent[];\n\treturn parsed;\n}\n\nexport function createWebSocketTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: WebSocketTransportOptions<TEvent>): ChatTransport<TPart, TEvent> {\n\tconst parseMessage = config.parseMessage ?? defaultParseWebSocketMessage;\n\tconst serializeMessage = config.serializeMessage ?? JSON.stringify;\n\n\tlet resolvedRunId: string | null = null;\n\n\tconst applyIncomingEvents = (\n\t\tparsed: TEvent | TEvent[] | null,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t) => {\n\t\tconst events = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];\n\t\tfor (const item of events) {\n\t\t\tconst nextRunId = config.resolveRunId?.(item) ?? null;\n\t\t\tif (nextRunId) {\n\t\t\t\tresolvedRunId = nextRunId;\n\t\t\t\tcontext.setRunId(nextRunId);\n\t\t\t}\n\t\t\tcontext.emit(item);\n\n\t\t\tif (item.type === \"done\") {\n\t\t\t\tcontext.finish();\n\t\t\t} else if (item.type === \"error\") {\n\t\t\t\tcontext.fail(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\"message\" in item && typeof item.message === \"string\"\n\t\t\t\t\t\t\t? item.message\n\t\t\t\t\t\t\t: \"Stream error\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\tcontext.ensureAssistantMessage();\n\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentResumePayload = false;\n\n\t\t\tconst sendResumePayload = () => {\n\t\t\t\tif (sentResumePayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentResumePayload = true;\n\t\t\t\tconst payload =\n\t\t\t\t\tconfig.buildResumePayload?.(runId) ??\n\t\t\t\t\t({ [config.runIdKey ?? \"runId\"]: runId } as Record<string, unknown>);\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendResumePayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendResumePayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst runId = config.runId ?? config.getResumeKey?.() ?? null;\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentStartPayload = false;\n\n\t\t\tconst sendStartPayload = () => {\n\t\t\t\tif (sentStartPayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentStartPayload = true;\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst payload: Record<string, unknown> = {\n\t\t\t\t\tmessage,\n\t\t\t\t\t...config.body,\n\t\t\t\t};\n\n\t\t\t\tif (runId) {\n\t\t\t\t\tpayload[config.runIdKey ?? \"runId\"] = runId;\n\t\t\t\t}\n\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendStartPayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendStartPayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport function resolveTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): ChatTransport<TPart, TEvent> {\n\tif (typeof options.transport === \"object\" && options.transport) {\n\t\treturn options.transport;\n\t}\n\n\tconst transportKind = options.transport ?? \"sse\";\n\n\tif (transportKind === \"background-sse\") {\n\t\tconst config = options.transportOptions?.backgroundSSE;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.backgroundSSE` is required when transport is \"background-sse\"',\n\t\t\t);\n\t\t}\n\t\treturn createBackgroundSSETransport(config);\n\t}\n\n\tif (transportKind === \"websocket\") {\n\t\tconst config = options.transportOptions?.websocket;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.websocket` is required when transport is \"websocket\"',\n\t\t\t);\n\t\t}\n\t\treturn createWebSocketTransport(config);\n\t}\n\n\tconst sseConfig = options.transportOptions?.sse ?? {\n\t\tapi: options.api,\n\t\tbody: options.body,\n\t\theaders: options.headers,\n\t};\n\n\tif (!sseConfig.api) {\n\t\tthrow new Error(\n\t\t\t\"`api` or `transportOptions.sse.api` is required when using the default SSE transport\",\n\t\t);\n\t}\n\n\treturn createDirectSSETransport({\n\t\t...sseConfig,\n\t\tapi: sseConfig.api,\n\t});\n}\n"],"mappings":"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","names":["parseSSEStream","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/use-auto-scroll.ts","../src/use-stream-chat.ts","../src/chat-controller.ts","../src/transports.ts"],"sourcesContent":["// Re-export core types so consumers only need to import from @deltakit/react\nexport type {\n\tContentPart,\n\tMessage,\n\tReasoningPart,\n\tSSEEvent,\n\tTextDeltaEvent,\n\tTextPart,\n\tToolCallEvent,\n\tToolCallPart,\n\tToolResultEvent,\n} from \"@deltakit/core\";\nexport {\n\tfromAgnoAgents,\n\tfromOpenAiAgents,\n\tparseSSEStream,\n} from \"@deltakit/core\";\n\nexport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tEventHelpers,\n\tTransportOptions,\n\tUseAutoScrollOptions,\n\tUseAutoScrollReturn,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n\tWebSocketTransportOptions,\n} from \"./types\";\nexport { useAutoScroll } from \"./use-auto-scroll\";\nexport { useStreamChat } from \"./use-stream-chat\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { UseAutoScrollOptions, UseAutoScrollReturn } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_THRESHOLD = 50;\n\n// ---------------------------------------------------------------------------\n// useAutoScroll\n// ---------------------------------------------------------------------------\n\nexport function useAutoScroll<T extends HTMLElement = HTMLDivElement>(\n\tdependencies: unknown[],\n\toptions?: UseAutoScrollOptions,\n): UseAutoScrollReturn<T> {\n\tconst {\n\t\tbehavior = \"instant\",\n\t\tenabled = true,\n\t\tthreshold = DEFAULT_THRESHOLD,\n\t} = options ?? {};\n\n\tconst ref = useRef<T | null>(null);\n\tconst isAtBottomRef = useRef(true);\n\tconst [isAtBottom, setIsAtBottom] = useState(true);\n\n\t// A single rAF id shared across all scroll sources — ensures we never\n\t// call scrollTo() more than once per frame, no matter how many\n\t// MutationObserver / ResizeObserver callbacks fire.\n\tconst rafRef = useRef<number | null>(null);\n\tconst smoothRafRef = useRef<number | null>(null);\n\tconst smoothTargetRef = useRef<number | null>(null);\n\tconst lastAutoScrollHeightRef = useRef<number | null>(null);\n\n\tconst cancelSmoothScroll = useCallback(() => {\n\t\tif (smoothRafRef.current != null) {\n\t\t\tcancelAnimationFrame(smoothRafRef.current);\n\t\t\tsmoothRafRef.current = null;\n\t\t}\n\t\tsmoothTargetRef.current = null;\n\t}, []);\n\n\tconst runSmoothScroll = useCallback(() => {\n\t\tif (smoothRafRef.current != null) return;\n\n\t\tconst tick = () => {\n\t\t\tconst el = ref.current;\n\t\t\tif (!el || !isAtBottomRef.current) {\n\t\t\t\tcancelSmoothScroll();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst target = smoothTargetRef.current ?? el.scrollHeight;\n\t\t\tconst delta = target - el.scrollTop;\n\n\t\t\tif (delta <= 1) {\n\t\t\t\tel.scrollTop = target;\n\t\t\t\tlastAutoScrollHeightRef.current = target;\n\t\t\t\tsmoothRafRef.current = null;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst step = Math.min(Math.max(delta * 0.35, 12), 120);\n\t\t\tel.scrollTop = Math.min(target, el.scrollTop + step);\n\t\t\tsmoothRafRef.current = requestAnimationFrame(tick);\n\t\t};\n\n\t\tsmoothRafRef.current = requestAnimationFrame(tick);\n\t}, [cancelSmoothScroll]);\n\n\tconst scheduleScroll = useCallback(() => {\n\t\tif (rafRef.current != null) return;\n\t\trafRef.current = requestAnimationFrame(() => {\n\t\t\trafRef.current = null;\n\t\t\tconst el = ref.current;\n\t\t\tif (!el || !isAtBottomRef.current) return;\n\n\t\t\tconst nextHeight = el.scrollHeight;\n\t\t\tif (lastAutoScrollHeightRef.current === nextHeight) return;\n\n\t\t\tlastAutoScrollHeightRef.current = nextHeight;\n\n\t\t\tif (behavior === \"smooth\") {\n\t\t\t\tsmoothTargetRef.current = nextHeight;\n\t\t\t\trunSmoothScroll();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcancelSmoothScroll();\n\t\t\tel.scrollTop = nextHeight;\n\t\t});\n\t}, [behavior, cancelSmoothScroll, runSmoothScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Track whether the user is near the bottom via scroll events.\n\t// Only triggers a React re-render when the boolean actually changes.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst handleScroll = () => {\n\t\t\tconst atBottom =\n\t\t\t\tel.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n\t\t\tisAtBottomRef.current = atBottom;\n\t\t\tif (!atBottom) {\n\t\t\t\tlastAutoScrollHeightRef.current = null;\n\t\t\t\tcancelSmoothScroll();\n\t\t\t}\n\t\t\tsetIsAtBottom((prev) => (prev === atBottom ? prev : atBottom));\n\t\t};\n\n\t\tel.addEventListener(\"scroll\", handleScroll, { passive: true });\n\t\thandleScroll();\n\t\treturn () => el.removeEventListener(\"scroll\", handleScroll);\n\t}, [enabled, threshold]);\n\n\t// -----------------------------------------------------------------------\n\t// Scroll to bottom when dependencies change (if pinned).\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tif (!enabled || !isAtBottomRef.current) return;\n\t\tscheduleScroll();\n\t\t// biome-ignore lint/correctness/useExhaustiveDependencies: dependencies are passed dynamically by the consumer\n\t}, dependencies);\n\n\t// -----------------------------------------------------------------------\n\t// MutationObserver + ResizeObserver — catch content changes during\n\t// streaming that happen between React re-renders (e.g. DOM mutations\n\t// from markdown renderers). Scroll calls are batched via rAF so we\n\t// scroll at most once per frame.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\tconst el = ref.current;\n\t\tif (!el || !enabled) return;\n\n\t\tconst resizeObserver = new ResizeObserver(scheduleScroll);\n\n\t\t// Observe existing children for size changes.\n\t\tfor (const child of el.children) {\n\t\t\tresizeObserver.observe(child);\n\t\t}\n\n\t\t// Watch for new children added to the container.\n\t\tconst mutationObserver = new MutationObserver((mutations) => {\n\t\t\tfor (const mutation of mutations) {\n\t\t\t\tfor (const node of mutation.addedNodes) {\n\t\t\t\t\tif (node instanceof Element) {\n\t\t\t\t\t\tresizeObserver.observe(node);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tscheduleScroll();\n\t\t});\n\n\t\tmutationObserver.observe(el, { childList: true, subtree: true });\n\n\t\treturn () => {\n\t\t\tresizeObserver.disconnect();\n\t\t\tmutationObserver.disconnect();\n\t\t};\n\t}, [enabled, scheduleScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// Cancel any pending rAF on unmount.\n\t// -----------------------------------------------------------------------\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (rafRef.current != null) {\n\t\t\t\tcancelAnimationFrame(rafRef.current);\n\t\t\t\trafRef.current = null;\n\t\t\t}\n\t\t\tcancelSmoothScroll();\n\t\t};\n\t}, [cancelSmoothScroll]);\n\n\t// -----------------------------------------------------------------------\n\t// scrollToBottom — imperative function that scrolls to the bottom and\n\t// re-pins auto-scroll.\n\t// -----------------------------------------------------------------------\n\n\tconst scrollToBottom = useCallback(() => {\n\t\tconst el = ref.current;\n\t\tif (!el) return;\n\n\t\tcancelSmoothScroll();\n\t\tisAtBottomRef.current = true;\n\t\tlastAutoScrollHeightRef.current = el.scrollHeight;\n\t\tsetIsAtBottom(true);\n\t\tel.scrollTo({ top: el.scrollHeight, behavior });\n\t}, [behavior, cancelSmoothScroll]);\n\n\treturn { ref, scrollToBottom, isAtBottom };\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { createChatTransportContext, createMessage } from \"./chat-controller\";\nimport { resolveTransport } from \"./transports\";\nimport type {\n\tChatTransportRun,\n\tEventHelpers,\n\tUseStreamChatOptions,\n\tUseStreamChatReturn,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Default event handler — accumulates `text_delta` into the last\n// assistant message's parts.\n// ---------------------------------------------------------------------------\n\nfunction defaultOnEvent(\n\tevent: SSEEvent,\n\thelpers: EventHelpers<ContentPart>,\n): void {\n\tif (event.type === \"text_delta\") {\n\t\thelpers.appendText(event.delta);\n\t}\n\t// Other event types (e.g. tool_call) are silently ignored by default.\n\t// Users can provide their own `onEvent` to handle them.\n}\n\n// ---------------------------------------------------------------------------\n// useStreamChat\n// ---------------------------------------------------------------------------\n\nexport function useStreamChat<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): UseStreamChatReturn<TPart> {\n\tconst {\n\t\tinitialMessages,\n\t\tonEvent,\n\t\tonMessage,\n\t\tonError,\n\t\tonFinish,\n\t\ttextBatchMs = 0,\n\t} = options;\n\n\tconst [messages, setMessages] = useState(initialMessages ?? []);\n\tconst [isLoading, setIsLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [runId, setRunId] = useState<string | null>(null);\n\n\tconst runRef = useRef<ChatTransportRun | null>(null);\n\n\t// Track which run id has already been resumed to prevent re-triggering.\n\tconst resumedRunIdRef = useRef<string | null>(null);\n\n\t// When the user manually stops a run, suppress auto-resume until the\n\t// next explicit `sendMessage` call.\n\tconst manuallyStoppedRef = useRef(false);\n\n\t// We use a ref for the latest messages so callbacks created inside\n\t// transport handlers always see the current value without re-creating\n\t// closures.\n\tconst messagesRef = useRef(messages);\n\tmessagesRef.current = messages;\n\n\tconst updateMessages = useCallback(\n\t\t(next:\n\t\t\t| Message<TPart>[]\n\t\t\t| ((prev: Message<TPart>[]) => Message<TPart>[])) => {\n\t\t\tsetMessages((prev) => {\n\t\t\t\tconst resolved =\n\t\t\t\t\ttypeof next === \"function\"\n\t\t\t\t\t\t? (next as (prev: Message<TPart>[]) => Message<TPart>[])(prev)\n\t\t\t\t\t\t: next;\n\t\t\t\tmessagesRef.current = resolved;\n\t\t\t\treturn resolved;\n\t\t\t});\n\t\t},\n\t\t[],\n\t);\n\n\t// Keep transport options in a ref so that callbacks always read the\n\t// latest values without causing memoisation instability.\n\tconst transportOptionsRef = useRef(options.transportOptions);\n\ttransportOptionsRef.current = options.transportOptions;\n\n\tconst pendingTextRef = useRef(\"\");\n\tconst textFlushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\tconst flushPendingText = useCallback(() => {\n\t\tif (textFlushTimerRef.current !== null) {\n\t\t\tclearTimeout(textFlushTimerRef.current);\n\t\t\ttextFlushTimerRef.current = null;\n\t\t}\n\n\t\tconst delta = pendingTextRef.current;\n\t\tif (!delta) return;\n\n\t\tpendingTextRef.current = \"\";\n\t\tupdateMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\tconst parts = [...last.parts];\n\t\t\tconst lastPart = parts[parts.length - 1];\n\n\t\t\tif (lastPart && lastPart.type === \"text\" && \"text\" in lastPart) {\n\t\t\t\tconst textPart = lastPart as { type: \"text\"; text: string };\n\t\t\t\tparts[parts.length - 1] = {\n\t\t\t\t\t...lastPart,\n\t\t\t\t\ttext: textPart.text + delta,\n\t\t\t\t} as unknown as TPart;\n\t\t\t} else {\n\t\t\t\tparts.push({ type: \"text\", text: delta } as unknown as TPart);\n\t\t\t}\n\n\t\t\treturn [...prev.slice(0, -1), { ...last, parts }];\n\t\t});\n\t}, [updateMessages]);\n\n\tconst appendText = useCallback((delta: string) => {\n\t\tif (textBatchMs <= 0) {\n\t\t\tpendingTextRef.current += delta;\n\t\t\tflushPendingText();\n\t\t\treturn;\n\t\t}\n\n\t\tpendingTextRef.current += delta;\n\t\tif (textFlushTimerRef.current !== null) return;\n\n\t\ttextFlushTimerRef.current = setTimeout(() => {\n\t\t\tflushPendingText();\n\t\t}, textBatchMs);\n\t}, [flushPendingText, textBatchMs]);\n\n\tconst appendPart = useCallback((part: TPart) => {\n\t\tflushPendingText();\n\t\tupdateMessages((prev) => {\n\t\t\tconst last = prev[prev.length - 1];\n\t\t\tif (!last || last.role !== \"assistant\") return prev;\n\n\t\t\treturn [\n\t\t\t\t...prev.slice(0, -1),\n\t\t\t\t{\n\t\t\t\t\t...last,\n\t\t\t\t\tparts: [...last.parts, part],\n\t\t\t\t},\n\t\t\t];\n\t\t});\n\t}, [flushPendingText, updateMessages]);\n\n\t// Stabilise transport creation: resolve once and store in a ref so that\n\t// changing values like `runId` in transportOptions won't cause a new\n\t// transport instance (and therefore a new WebSocket) to be created.\n\tconst transportRef = useRef<ReturnType<\n\t\ttypeof resolveTransport<TPart, TEvent>\n\t> | null>(null);\n\tif (!transportRef.current) {\n\t\ttransportRef.current = resolveTransport(options);\n\t}\n\tconst transport = transportRef.current;\n\n\tconst eventHandler =\n\t\tonEvent ??\n\t\t(defaultOnEvent as unknown as (\n\t\t\tevent: TEvent,\n\t\t\thelpers: EventHelpers<TPart>,\n\t\t) => void);\n\n\t// Stabilise the transport context: use refs for values that change\n\t// frequently (transportOptions callbacks) so the context object itself\n\t// stays referentially stable.\n\tconst eventHandlerRef = useRef(eventHandler);\n\teventHandlerRef.current = eventHandler;\n\n\tconst onErrorRef = useRef(onError);\n\tonErrorRef.current = onError;\n\n\tconst onFinishRef = useRef(onFinish);\n\tonFinishRef.current = onFinish;\n\n\tconst onMessageRef = useRef(onMessage);\n\tonMessageRef.current = onMessage;\n\n\tconst transportContext = useMemo(\n\t\t() =>\n\t\t\tcreateChatTransportContext({\n\t\t\t\tappendPart,\n\t\t\t\tappendText,\n\t\t\t\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) =>\n\t\t\t\t\teventHandlerRef.current(event, helpers),\n\t\t\t\tgetMessages: () => {\n\t\t\t\t\tflushPendingText();\n\t\t\t\t\treturn messagesRef.current;\n\t\t\t\t},\n\t\t\t\tonError: (...args) => onErrorRef.current?.(...args),\n\t\t\t\tonFinish: (...args) => onFinishRef.current?.(...args),\n\t\t\t\tonMessage: (...args) => onMessageRef.current?.(...args),\n\t\t\t\tsetError,\n\t\t\t\tsetIsLoading,\n\t\t\t\tsetMessages: updateMessages,\n\t\t\t\tsetRunId: (next) => {\n\t\t\t\t\tsetRunId(next);\n\t\t\t\t\ttransportOptionsRef.current?.backgroundSSE?.onRunIdChange?.(next);\n\t\t\t\t\ttransportOptionsRef.current?.websocket?.onRunIdChange?.(next);\n\t\t\t\t},\n\t\t\t}),\n\t\t[appendPart, appendText, flushPendingText, updateMessages],\n\t);\n\n\tconst stop = useCallback(() => {\n\t\tconst activeRun = runRef.current;\n\t\tif (!activeRun?.stop) {\n\t\t\treturn;\n\t\t}\n\n\t\tmanuallyStoppedRef.current = true;\n\t\tvoid activeRun.stop();\n\t\trunRef.current = null;\n\t\tsetIsLoading(false);\n\t}, []);\n\n\tconst sendMessage = useCallback(\n\t\t(text: string) => {\n\t\t\tif (runRef.current || isLoading) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst userMessage = createMessage<TPart>(\"user\", [\n\t\t\t\t{ type: \"text\", text } as unknown as TPart,\n\t\t\t]);\n\t\t\tconst assistantMessage = createMessage<TPart>(\"assistant\", []);\n\n\t\t\tupdateMessages((prev) => {\n\t\t\t\tconst next = [...prev, userMessage, assistantMessage];\n\t\t\t\treturn next;\n\t\t\t});\n\n\t\t\tonMessage?.(userMessage);\n\t\t\tsetError(null);\n\t\t\tsetIsLoading(true);\n\n\t\t\t// Reset the resume guard so a future resume for a new run is allowed.\n\t\t\tresumedRunIdRef.current = null;\n\t\t\tmanuallyStoppedRef.current = false;\n\n\t\t\tconst run = transport.start({ context: transportContext, message: text });\n\t\t\trunRef.current = run ?? null;\n\t\t\tif (run?.runId) {\n\t\t\t\tsetRunId(run.runId);\n\t\t\t}\n\t\t},\n\t\t[isLoading, onMessage, transport, transportContext, updateMessages],\n\t);\n\n\t// -----------------------------------------------------------------------\n\t// Auto-resume effect: attempt to reconnect to an in-flight run on mount.\n\t//\n\t// The candidate run id is read from transport options (which may change\n\t// when the app updates state). We guard against duplicate resumes for the\n\t// same run id using `resumedRunIdRef`.\n\t// -----------------------------------------------------------------------\n\n\tconst candidateRunId =\n\t\toptions.transportOptions?.backgroundSSE?.runId ??\n\t\toptions.transportOptions?.backgroundSSE?.getResumeKey?.() ??\n\t\toptions.transportOptions?.websocket?.runId ??\n\t\toptions.transportOptions?.websocket?.getResumeKey?.() ??\n\t\tnull;\n\n\tuseEffect(() => {\n\t\t// Already have an active run — don't start another.\n\t\tif (runRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\t// User explicitly stopped — don't auto-resume until next sendMessage.\n\t\tif (manuallyStoppedRef.current) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Already resumed this exact run id — don't retry.\n\t\tif (resumedRunIdRef.current === candidateRunId) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!transport.resume) {\n\t\t\treturn;\n\t\t}\n\n\t\tresumedRunIdRef.current = candidateRunId;\n\n\t\tsetError(null);\n\t\tsetIsLoading(true);\n\t\tconst run = transport.resume({\n\t\t\tcontext: transportContext,\n\t\t\trunId: candidateRunId,\n\t\t});\n\t\trunRef.current = run ?? null;\n\t\tsetRunId(candidateRunId);\n\t}, [candidateRunId, transport, transportContext]);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (textFlushTimerRef.current !== null) {\n\t\t\t\tclearTimeout(textFlushTimerRef.current);\n\t\t\t\ttextFlushTimerRef.current = null;\n\t\t\t}\n\t\t\tvoid runRef.current?.close?.();\n\t\t\trunRef.current = null;\n\t\t};\n\t}, []);\n\n\tconst prevIsLoadingRef = useRef(isLoading);\n\tuseEffect(() => {\n\t\t// Only clear the run ref on a true → false transition, not on mount\n\t\t// where isLoading starts as false. Clearing on mount would race with\n\t\t// the auto-resume effect and null out the run it just created.\n\t\tif (prevIsLoadingRef.current && !isLoading) {\n\t\t\trunRef.current = null;\n\t\t}\n\t\tprevIsLoadingRef.current = isLoading;\n\t}, [isLoading]);\n\n\treturn {\n\t\terror,\n\t\tisLoading,\n\t\tmessages,\n\t\trunId,\n\t\tsendMessage,\n\t\tsetMessages: updateMessages,\n\t\tstop,\n\t};\n}\n","import type { ContentPart, Message, SSEEvent } from \"@deltakit/core\";\nimport type { Dispatch, SetStateAction } from \"react\";\nimport type {\n\tChatTransportContext,\n\tEventHelpers,\n\tUseStreamChatOptions,\n} from \"./types\";\n\nexport interface ChatControllerOptions<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n> {\n\tappendPart: EventHelpers<TPart>[\"appendPart\"];\n\tappendText: EventHelpers<TPart>[\"appendText\"];\n\teventHandler: (event: TEvent, helpers: EventHelpers<TPart>) => void;\n\tgetMessages: () => Message<TPart>[];\n\tonError?: UseStreamChatOptions<TPart, TEvent>[\"onError\"];\n\tonFinish?: UseStreamChatOptions<TPart, TEvent>[\"onFinish\"];\n\tonMessage?: UseStreamChatOptions<TPart, TEvent>[\"onMessage\"];\n\tsetError: Dispatch<SetStateAction<Error | null>>;\n\tsetIsLoading: Dispatch<SetStateAction<boolean>>;\n\tsetMessages: Dispatch<SetStateAction<Message<TPart>[]>>;\n\tsetRunId: (runId: string | null) => void;\n}\n\nlet counter = 0;\n\nexport function generateId(): string {\n\treturn `msg_${Date.now()}_${++counter}`;\n}\n\nexport function createMessage<TPart extends { type: string }>(\n\trole: Message[\"role\"],\n\tparts: TPart[],\n): Message<TPart> {\n\treturn { id: generateId(), role, parts };\n}\n\nexport function createChatTransportContext<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(\n\toptions: ChatControllerOptions<TPart, TEvent>,\n): ChatTransportContext<TPart, TEvent> {\n\tconst helpers: EventHelpers<TPart> = {\n\t\tappendPart: options.appendPart,\n\t\tappendText: options.appendText,\n\t\tsetMessages: options.setMessages,\n\t};\n\n\treturn {\n\t\temit: (event) => {\n\t\t\toptions.eventHandler(event, helpers);\n\t\t},\n\t\tensureAssistantMessage: () => {\n\t\t\toptions.setMessages((prev) => {\n\t\t\t\tconst last = prev[prev.length - 1];\n\t\t\t\tif (last?.role === \"assistant\") {\n\t\t\t\t\treturn prev;\n\t\t\t\t}\n\n\t\t\t\treturn [...prev, createMessage<TPart>(\"assistant\", [])];\n\t\t\t});\n\t\t},\n\t\tfail: (error) => {\n\t\t\toptions.setError(error);\n\t\t\toptions.onError?.(error);\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\t\t},\n\t\tfinish: () => {\n\t\t\toptions.setIsLoading(false);\n\t\t\toptions.setRunId(null);\n\n\t\t\tconst finalMessages = options.getMessages();\n\t\t\tconst lastMessage = finalMessages[finalMessages.length - 1];\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\toptions.onMessage?.(lastMessage);\n\t\t\t}\n\n\t\t\toptions.onFinish?.(finalMessages);\n\t\t},\n\t\tgetMessages: options.getMessages,\n\t\tsetRunId: (runId) => {\n\t\t\toptions.setRunId(runId);\n\t\t},\n\t};\n}\n","import type { ContentPart, SSEEvent } from \"@deltakit/core\";\nimport { parseSSEStream } from \"@deltakit/core\";\nimport type {\n\tBackgroundSSETransportOptions,\n\tChatTransport,\n\tChatTransportContext,\n\tChatTransportRun,\n\tDirectSSETransportOptions,\n\tUseStreamChatOptions,\n\tWebSocketTransportOptions,\n} from \"./types\";\n\nfunction toError(error: unknown): Error {\n\treturn error instanceof Error ? error : new Error(String(error));\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction resolveRunId(response: unknown): string {\n\tif (!response || typeof response !== \"object\") {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\tconst maybeRunId =\n\t\t\"runId\" in response\n\t\t\t? response.runId\n\t\t\t: \"job_id\" in response\n\t\t\t\t? response.job_id\n\t\t\t\t: null;\n\n\tif (typeof maybeRunId !== \"string\" || maybeRunId.length === 0) {\n\t\tthrow new Error(\"Background SSE start response did not contain a run id\");\n\t}\n\n\treturn maybeRunId;\n}\n\nfunction resolveUrl(\n\turl: string | ((runId: string) => string),\n\trunId: string,\n): string {\n\treturn typeof url === \"function\" ? url(runId) : url.replace(\":runId\", runId);\n}\n\nasync function streamFetchSSE<\n\tTPart extends { type: string },\n\tTEvent extends { type: string },\n>(\n\tresponse: Response,\n\tcontext: ChatTransportContext<TPart, TEvent>,\n\tsignal: AbortSignal,\n): Promise<void> {\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`SSE request failed: ${response.status} ${response.statusText}`,\n\t\t);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"Response body is null — SSE streaming not supported\");\n\t}\n\n\tfor await (const event of parseSSEStream(response.body, signal)) {\n\t\tcontext.emit(event as unknown as TEvent);\n\t}\n}\n\nexport function createDirectSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: DirectSSETransportOptions): ChatTransport<TPart, TEvent> {\n\treturn {\n\t\tstart: ({ context, message }) => {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst run: ChatTransportRun = {\n\t\t\t\tclose: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tcontroller.abort();\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tconst fetchImpl = config.fetch ?? fetch;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.api, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.body }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.headers,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.method ?? \"POST\",\n\t\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\t\tcontext.finish();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn run;\n\t\t},\n\t};\n}\n\nexport function createBackgroundSSETransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: BackgroundSSETransportOptions): ChatTransport<TPart, TEvent> {\n\tconst fetchImpl = config.fetch ?? fetch;\n\n\tconst connect = (\n\t\trunId: string,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t): ChatTransportRun => {\n\t\tconst controller = new AbortController();\n\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst response = await fetchImpl(resolveUrl(config.eventsApi, runId), {\n\t\t\t\t\theaders: config.eventHeaders,\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tawait streamFetchSSE(response, context, controller.signal);\n\t\t\t\tcontext.finish();\n\t\t\t} catch (error) {\n\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\n\t\treturn {\n\t\t\tclose: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t},\n\t\t\tstop: () => {\n\t\t\t\tcontroller.abort();\n\t\t\t\tif (config.cancelApi) {\n\t\t\t\t\tvoid fetchImpl(resolveUrl(config.cancelApi, runId), {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\trunId,\n\t\t};\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\treturn connect(runId, context);\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst startController = new AbortController();\n\t\t\tlet activeRun: ChatTransportRun | undefined;\n\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await fetchImpl(config.startApi, {\n\t\t\t\t\t\tbody: JSON.stringify({ message, ...config.startBody }),\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...config.startHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmethod: config.startMethod ?? \"POST\",\n\t\t\t\t\t\tsignal: startController.signal,\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Background SSE start failed: ${response.status} ${response.statusText}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as unknown;\n\t\t\t\t\tconst runId = (config.resolveRunId ?? resolveRunId)(data);\n\t\t\t\t\tcontext.setRunId(runId);\n\t\t\t\t\tactiveRun = connect(runId, context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!isAbortError(error)) {\n\t\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tvoid activeRun?.close?.();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tstartController.abort();\n\t\t\t\t\tactiveRun?.stop?.();\n\t\t\t\t},\n\t\t\t\trunId: null,\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction defaultParseWebSocketMessage<TEvent extends { type: string }>(\n\tdata: unknown,\n): TEvent | TEvent[] | null {\n\tif (typeof data !== \"string\") {\n\t\treturn null;\n\t}\n\n\tconst parsed = JSON.parse(data) as TEvent | TEvent[];\n\treturn parsed;\n}\n\nexport function createWebSocketTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(config: WebSocketTransportOptions<TEvent>): ChatTransport<TPart, TEvent> {\n\tconst parseMessage = config.parseMessage ?? defaultParseWebSocketMessage;\n\tconst serializeMessage = config.serializeMessage ?? JSON.stringify;\n\n\tlet resolvedRunId: string | null = null;\n\n\tconst applyIncomingEvents = (\n\t\tparsed: TEvent | TEvent[] | null,\n\t\tcontext: ChatTransportContext<TPart, TEvent>,\n\t) => {\n\t\tconst events = Array.isArray(parsed) ? parsed : parsed ? [parsed] : [];\n\t\tfor (const item of events) {\n\t\t\tconst nextRunId = config.resolveRunId?.(item) ?? null;\n\t\t\tif (nextRunId) {\n\t\t\t\tresolvedRunId = nextRunId;\n\t\t\t\tcontext.setRunId(nextRunId);\n\t\t\t}\n\t\t\tcontext.emit(item);\n\n\t\t\tif (item.type === \"done\") {\n\t\t\t\tcontext.finish();\n\t\t\t} else if (item.type === \"error\") {\n\t\t\t\tcontext.fail(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\"message\" in item && typeof item.message === \"string\"\n\t\t\t\t\t\t\t? item.message\n\t\t\t\t\t\t\t: \"Stream error\",\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn {\n\t\tresume: ({ context, runId }) => {\n\t\t\tcontext.setRunId(runId);\n\t\t\tcontext.ensureAssistantMessage();\n\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentResumePayload = false;\n\n\t\t\tconst sendResumePayload = () => {\n\t\t\t\tif (sentResumePayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentResumePayload = true;\n\t\t\t\tconst payload =\n\t\t\t\t\tconfig.buildResumePayload?.(runId) ??\n\t\t\t\t\t({ [config.runIdKey ?? \"runId\"]: runId } as Record<string, unknown>);\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendResumePayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendResumePayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t\tstart: ({ context, message }) => {\n\t\t\tconst runId = config.runId ?? config.getResumeKey?.() ?? null;\n\t\t\tconst socket = new WebSocket(\n\t\t\t\ttypeof config.url === \"function\" ? config.url(runId) : config.url,\n\t\t\t\tconfig.protocols,\n\t\t\t);\n\t\t\tlet manuallyClosed = false;\n\t\t\tlet sentStartPayload = false;\n\n\t\t\tconst sendStartPayload = () => {\n\t\t\t\tif (sentStartPayload || socket.readyState !== WebSocket.OPEN) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsentStartPayload = true;\n\t\t\t\tcontext.ensureAssistantMessage();\n\t\t\t\tconst payload: Record<string, unknown> = {\n\t\t\t\t\tmessage,\n\t\t\t\t\t...config.body,\n\t\t\t\t};\n\n\t\t\t\tif (runId) {\n\t\t\t\t\tpayload[config.runIdKey ?? \"runId\"] = runId;\n\t\t\t\t}\n\n\t\t\t\tsocket.send(serializeMessage(payload));\n\t\t\t};\n\n\t\t\tsocket.onopen = sendStartPayload;\n\n\t\t\tsocket.onmessage = (event) => {\n\t\t\t\ttry {\n\t\t\t\t\tapplyIncomingEvents(parseMessage(event.data), context);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcontext.fail(toError(error));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsocket.onerror = () => {\n\t\t\t\tcontext.fail(new Error(\"WebSocket connection failed\"));\n\t\t\t};\n\n\t\t\tsocket.onclose = () => {\n\t\t\t\tif (!manuallyClosed) {\n\t\t\t\t\tcontext.finish();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tqueueMicrotask(sendStartPayload);\n\n\t\t\treturn {\n\t\t\t\tclose: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\tstop: () => {\n\t\t\t\t\tmanuallyClosed = true;\n\t\t\t\t\tconst stopRunId = resolvedRunId ?? runId;\n\t\t\t\t\tif (stopRunId && config.cancelUrl) {\n\t\t\t\t\t\tvoid fetch(resolveUrl(config.cancelUrl, stopRunId), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tsocket.close();\n\t\t\t\t},\n\t\t\t\trunId,\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport function resolveTransport<\n\tTPart extends { type: string } = ContentPart,\n\tTEvent extends { type: string } = SSEEvent,\n>(options: UseStreamChatOptions<TPart, TEvent>): ChatTransport<TPart, TEvent> {\n\tif (typeof options.transport === \"object\" && options.transport) {\n\t\treturn options.transport;\n\t}\n\n\tconst transportKind = options.transport ?? \"sse\";\n\n\tif (transportKind === \"background-sse\") {\n\t\tconst config = options.transportOptions?.backgroundSSE;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.backgroundSSE` is required when transport is \"background-sse\"',\n\t\t\t);\n\t\t}\n\t\treturn createBackgroundSSETransport(config);\n\t}\n\n\tif (transportKind === \"websocket\") {\n\t\tconst config = options.transportOptions?.websocket;\n\t\tif (!config) {\n\t\t\tthrow new Error(\n\t\t\t\t'`transportOptions.websocket` is required when transport is \"websocket\"',\n\t\t\t);\n\t\t}\n\t\treturn createWebSocketTransport(config);\n\t}\n\n\tconst sseConfig = options.transportOptions?.sse ?? {\n\t\tapi: options.api,\n\t\tbody: options.body,\n\t\theaders: options.headers,\n\t};\n\n\tif (!sseConfig.api) {\n\t\tthrow new Error(\n\t\t\t\"`api` or `transportOptions.sse.api` is required when using the default SSE transport\",\n\t\t);\n\t}\n\n\treturn createDirectSSETransport({\n\t\t...sseConfig,\n\t\tapi: sseConfig.api,\n\t});\n}\n"],"mappings":"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","names":["parseSSEStream","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect"]}
|
package/package.json
CHANGED
|
@@ -1,50 +1,57 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
2
|
+
"name": "@deltakit/react",
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"description": "React hooks for streaming AI conversations with real-time updates",
|
|
5
|
+
"homepage": "https://deltakit.dev",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "DeltaKit HQ",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/deltakithq/deltakit-monorepo",
|
|
12
|
+
"directory": "packages/react"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"deltakit",
|
|
16
|
+
"react"
|
|
17
|
+
],
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"require": "./dist/index.cjs"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"main": "./dist/index.cjs",
|
|
26
|
+
"module": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsup",
|
|
33
|
+
"dev": "tsup --watch",
|
|
34
|
+
"check": "biome check src/",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"clean": "rm -rf dist"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@deltakit/core": "workspace:*"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@testing-library/react": "^16.3.0",
|
|
44
|
+
"@types/react": "^18.3.18",
|
|
45
|
+
"@types/react-dom": "^18.3.5",
|
|
46
|
+
"jsdom": "^28.1.0",
|
|
47
|
+
"react": "^18.3.1",
|
|
48
|
+
"react-dom": "^18.3.1",
|
|
49
|
+
"tsup": "8.5.1",
|
|
50
|
+
"typescript": "^5.7.2",
|
|
51
|
+
"vitest": "^3.0.5"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"react": ">=18",
|
|
55
|
+
"react-dom": ">=18"
|
|
56
|
+
}
|
|
57
|
+
}
|