@wix/legends-platform-sdk 1.14.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -109,7 +109,7 @@ const { status, join, leave } = useConversation({
109
109
  |--------|------|-------------|
110
110
  | `status` | `'pending' \| 'created' \| 'destroying' \| 'destroyed' \| 'exceeded' \| 'inactivity-timeout' \| 'voice-not-found' \| 'initialization-error' \| 'error'` | Current conversation state |
111
111
  | `join()` | `() => void` | Start the conversation |
112
- | `leave()` | `(reason?) => void` | End the conversation |
112
+ | `leave()` | `(destroy?: boolean) => Promise<void>` | End the conversation |
113
113
 
114
114
  #### `useMicrophone()`
115
115
 
@@ -153,29 +153,60 @@ const status = useConversationStatus();
153
153
  const { elapsed, remaining } = useConversationTimer();
154
154
  ```
155
155
 
156
- #### `useTools(registry)`
156
+ #### `useTools()`
157
157
 
158
- Register custom tool handlers for function calling.
158
+ Read the list of tool calls executed by the SDK. To register tool handlers, pass a `tools` registry to `LegendsPlatform`.
159
159
 
160
160
  ```tsx
161
- import { useTools } from '@wix/legends-platform-sdk';
162
-
163
- const tools = useTools({
164
- get_weather: {
165
- handler: async ({ city }) => {
166
- const data = await fetchWeather(city);
167
- return { temperature: data.temp };
168
- },
161
+ import { LegendsPlatform, useTools, type ToolsRegistry } from '@wix/legends-platform-sdk';
162
+
163
+ const tools: ToolsRegistry = {
164
+ get_weather: async ({ city }) => {
165
+ const data = await fetchWeather(city as string);
166
+ return { temperature: data.temp };
169
167
  },
170
- });
168
+ };
169
+
170
+ function ToolLog() {
171
+ const calls = useTools(); // returns ToolCallSnapshot[]
172
+ return <pre>{JSON.stringify(calls, null, 2)}</pre>;
173
+ }
171
174
 
172
175
  return (
173
176
  <LegendsPlatform ... tools={tools}>
174
- ...
177
+ <ToolLog />
175
178
  </LegendsPlatform>
176
179
  );
177
180
  ```
178
181
 
182
+ #### `useTextChatStream(options)`
183
+
184
+ Streaming counterpart to `useTextChat`. Sends messages over SSE and drips the assistant reply into `streamingText` at a steady rate via `requestAnimationFrame`.
185
+
186
+ ```tsx
187
+ const { messages, send } = useTextChatStream({
188
+ streamUrl: '/api/chat/stream',
189
+ onError: (err) => console.error(err),
190
+ });
191
+
192
+ // messages[n].streamStatus === 'streaming' while the reply is in-flight
193
+ await send({ message: 'Hello' });
194
+ ```
195
+
196
+ #### `useSpeechActivityDetector(options)`
197
+
198
+ Acoustic speech onset/offset detector driven by `AudioWorkletNode`. Useful for measuring end-to-end latency or building custom VAD UI.
199
+
200
+ ```tsx
201
+ useSpeechActivityDetector({
202
+ source: 'user',
203
+ thresholdRms: 0.015,
204
+ hangoverMs: 300,
205
+ onSpeechStart: (timestampMs) => console.log('started', timestampMs),
206
+ onSpeechEnd: (timestampMs) => console.log('ended', timestampMs),
207
+ });
208
+ ```
209
+
179
210
  ---
180
211
 
181
212
  ## Advanced: Custom Service
@@ -7,4 +7,10 @@ Object.keys(_useTextChat).forEach(function (key) {
7
7
  if (key in exports && exports[key] === _useTextChat[key]) return;
8
8
  exports[key] = _useTextChat[key];
9
9
  });
10
+ var _useTextChatStream = require("./useTextChatStream");
11
+ Object.keys(_useTextChatStream).forEach(function (key) {
12
+ if (key === "default" || key === "__esModule") return;
13
+ if (key in exports && exports[key] === _useTextChatStream[key]) return;
14
+ exports[key] = _useTextChatStream[key];
15
+ });
10
16
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_useTextChat","require","Object","keys","forEach","key","exports"],"sources":["../../../../src/hooks/useTextChat/index.ts"],"sourcesContent":["export * from './useTextChat';\n"],"mappings":";;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,YAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,YAAA,CAAAK,GAAA;EAAAC,OAAA,CAAAD,GAAA,IAAAL,YAAA,CAAAK,GAAA;AAAA","ignoreList":[]}
1
+ {"version":3,"names":["_useTextChat","require","Object","keys","forEach","key","exports","_useTextChatStream"],"sources":["../../../../src/hooks/useTextChat/index.ts"],"sourcesContent":["export * from './useTextChat';\nexport * from './useTextChatStream';\n"],"mappings":";;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,YAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,YAAA,CAAAK,GAAA;EAAAC,OAAA,CAAAD,GAAA,IAAAL,YAAA,CAAAK,GAAA;AAAA;AACA,IAAAE,kBAAA,GAAAN,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAI,kBAAA,EAAAH,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAE,kBAAA,CAAAF,GAAA;EAAAC,OAAA,CAAAD,GAAA,IAAAE,kBAAA,CAAAF,GAAA;AAAA","ignoreList":[]}
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.createObjectScanner = createObjectScanner;
5
+ exports.extractContent = extractContent;
6
+ exports.streamChatMessage = streamChatMessage;
7
+ // Same-origin Wix persona-chat gateway path. The transport always posts here
8
+ // with `credentials: 'include'`, so the session cookie rides along and a
9
+ // consumer needs no proxy route of its own.
10
+ const DEFAULT_STREAM_PATH = '/_api/persona-chat-service/v1/chat/messages/stream';
11
+ // Read a cookie value by name in the browser. Returns undefined outside a
12
+ // browser or when the cookie is absent.
13
+ function readCookie(name) {
14
+ var _document$cookie$spli;
15
+ if (typeof document === 'undefined') {
16
+ return undefined;
17
+ }
18
+ return (_document$cookie$spli = document.cookie.split(';').map(part => part.trim()).find(part => part.startsWith(`${name}=`))) == null ? void 0 : _document$cookie$spli.slice(name.length + 1);
19
+ }
20
+
21
+ // Pull the assistant text out of one streamed frame. Two framings reach us: the
22
+ // proxy route yields `{ content }` objects, while the gateway wraps each frame
23
+ // as `{ body: base64(json) }`. Returns null for frames without content.
24
+ function extractContent(frame) {
25
+ try {
26
+ const parsed = JSON.parse(frame);
27
+ if (typeof parsed.content === 'string') {
28
+ return parsed.content;
29
+ }
30
+ if (typeof parsed.body === 'string') {
31
+ const decoded = new TextDecoder().decode(Uint8Array.from(atob(parsed.body), char => char.charCodeAt(0)));
32
+ const inner = JSON.parse(decoded);
33
+ return typeof inner.content === 'string' ? inner.content : null;
34
+ }
35
+ } catch {
36
+ // Incomplete or non-object frame — skip it.
37
+ }
38
+ return null;
39
+ }
40
+
41
+ // Feeds decoded text through a running scanner that emits each complete
42
+ // top-level `{…}` object as its closing brace arrives.
43
+ function createObjectScanner(onObject) {
44
+ let buffer = '';
45
+ let cursor = 0;
46
+ let braceDepth = 0;
47
+ let objectStart = -1;
48
+ let inString = false;
49
+ let escaped = false;
50
+ return function feed(text) {
51
+ buffer += text;
52
+ for (; cursor < buffer.length; cursor++) {
53
+ const char = buffer[cursor];
54
+ if (inString) {
55
+ if (escaped) {
56
+ escaped = false;
57
+ } else if (char === '\\') {
58
+ escaped = true;
59
+ } else if (char === '"') {
60
+ inString = false;
61
+ }
62
+ continue;
63
+ }
64
+ if (char === '"') {
65
+ inString = true;
66
+ } else if (char === '{') {
67
+ if (braceDepth === 0) {
68
+ objectStart = cursor;
69
+ }
70
+ braceDepth++;
71
+ } else if (char === '}' && braceDepth > 0) {
72
+ braceDepth--;
73
+ if (braceDepth === 0 && objectStart >= 0) {
74
+ onObject(buffer.slice(objectStart, cursor + 1));
75
+ objectStart = -1;
76
+ }
77
+ }
78
+ }
79
+ };
80
+ }
81
+ async function streamChatMessage(params, callbacks, signal) {
82
+ const {
83
+ chatId,
84
+ message,
85
+ namespace,
86
+ chatConfig,
87
+ attachments,
88
+ metadata,
89
+ headers
90
+ } = params;
91
+ const {
92
+ onChunk,
93
+ onDone,
94
+ onError
95
+ } = callbacks;
96
+ const xsrfToken = readCookie('XSRF-TOKEN');
97
+ const autoXsrf = !(headers != null && headers['x-xsrf-token']) && xsrfToken ? {
98
+ 'x-xsrf-token': xsrfToken
99
+ } : undefined;
100
+ try {
101
+ // eslint-disable-next-line no-undef
102
+ const response = await fetch(DEFAULT_STREAM_PATH, {
103
+ method: 'POST',
104
+ credentials: 'include',
105
+ headers: {
106
+ 'Content-Type': 'application/json',
107
+ ...autoXsrf,
108
+ ...headers
109
+ },
110
+ body: JSON.stringify({
111
+ chatId,
112
+ message,
113
+ namespace,
114
+ chatConfig,
115
+ attachments,
116
+ metadata
117
+ }),
118
+ signal
119
+ });
120
+ if (!response.ok || !response.body) {
121
+ onError(new Error(`Stream request failed: ${response.status}`));
122
+ return;
123
+ }
124
+ const scanForObjects = createObjectScanner(frame => {
125
+ const content = extractContent(frame);
126
+ if (content !== null) {
127
+ onChunk(content);
128
+ }
129
+ });
130
+ const reader = response.body.getReader();
131
+ const decoder = new TextDecoder();
132
+ for (;;) {
133
+ const {
134
+ done,
135
+ value
136
+ } = await reader.read();
137
+ if (done) {
138
+ break;
139
+ }
140
+ scanForObjects(decoder.decode(value, {
141
+ stream: true
142
+ }));
143
+ }
144
+ scanForObjects(decoder.decode());
145
+ onDone();
146
+ } catch (err) {
147
+ if (err.name !== 'AbortError') {
148
+ onError(err);
149
+ }
150
+ }
151
+ }
152
+ //# sourceMappingURL=streamChatMessage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["DEFAULT_STREAM_PATH","readCookie","name","_document$cookie$spli","document","undefined","cookie","split","map","part","trim","find","startsWith","slice","length","extractContent","frame","parsed","JSON","parse","content","body","decoded","TextDecoder","decode","Uint8Array","from","atob","char","charCodeAt","inner","createObjectScanner","onObject","buffer","cursor","braceDepth","objectStart","inString","escaped","feed","text","streamChatMessage","params","callbacks","signal","chatId","message","namespace","chatConfig","attachments","metadata","headers","onChunk","onDone","onError","xsrfToken","autoXsrf","response","fetch","method","credentials","stringify","ok","Error","status","scanForObjects","reader","getReader","decoder","done","value","read","stream","err"],"sources":["../../../../src/hooks/useTextChat/streamChatMessage.ts"],"sourcesContent":["// Same-origin Wix persona-chat gateway path. The transport always posts here\n// with `credentials: 'include'`, so the session cookie rides along and a\n// consumer needs no proxy route of its own.\nconst DEFAULT_STREAM_PATH =\n '/_api/persona-chat-service/v1/chat/messages/stream';\n\nexport type StreamCallbacks = {\n onChunk: (token: string) => void;\n onDone: () => void;\n onError: (err: Error) => void;\n};\n\nexport type StreamChatMessageParams = {\n chatId: string;\n message: string;\n namespace: string;\n chatConfig?: object;\n attachments?: object[];\n // Request metadata forwarded to the RPC (e.g. `additionalMetadata` carrying\n // opening-message context). Mirrors the top-level `metadata` object the unary\n // `sendChatMessage` request accepts, so a streamed send carries it identically.\n metadata?: object;\n // Extra request headers merged onto the POST. Lets the caller satisfy the\n // host's transport requirements the SDK can't know about — e.g. an\n // `x-xsrf-token` when the endpoint sits behind a CSRF-protected gateway.\n headers?: Record<string, string>;\n};\n\n// Read a cookie value by name in the browser. Returns undefined outside a\n// browser or when the cookie is absent.\nfunction readCookie(name: string): string | undefined {\n if (typeof document === 'undefined') {\n return undefined;\n }\n return document.cookie\n .split(';')\n .map((part) => part.trim())\n .find((part) => part.startsWith(`${name}=`))\n ?.slice(name.length + 1);\n}\n\n// Pull the assistant text out of one streamed frame. Two framings reach us: the\n// proxy route yields `{ content }` objects, while the gateway wraps each frame\n// as `{ body: base64(json) }`. Returns null for frames without content.\nexport function extractContent(frame: string): string | null {\n try {\n const parsed = JSON.parse(frame);\n if (typeof parsed.content === 'string') {\n return parsed.content;\n }\n if (typeof parsed.body === 'string') {\n const decoded = new TextDecoder().decode(\n Uint8Array.from(atob(parsed.body), (char) => char.charCodeAt(0)),\n );\n const inner = JSON.parse(decoded);\n return typeof inner.content === 'string' ? inner.content : null;\n }\n } catch {\n // Incomplete or non-object frame — skip it.\n }\n return null;\n}\n\n// Feeds decoded text through a running scanner that emits each complete\n// top-level `{…}` object as its closing brace arrives.\nexport function createObjectScanner(onObject: (frame: string) => void) {\n let buffer = '';\n let cursor = 0;\n let braceDepth = 0;\n let objectStart = -1;\n let inString = false;\n let escaped = false;\n\n return function feed(text: string) {\n buffer += text;\n for (; cursor < buffer.length; cursor++) {\n const char = buffer[cursor];\n if (inString) {\n if (escaped) {\n escaped = false;\n } else if (char === '\\\\') {\n escaped = true;\n } else if (char === '\"') {\n inString = false;\n }\n continue;\n }\n if (char === '\"') {\n inString = true;\n } else if (char === '{') {\n if (braceDepth === 0) {\n objectStart = cursor;\n }\n braceDepth++;\n } else if (char === '}' && braceDepth > 0) {\n braceDepth--;\n if (braceDepth === 0 && objectStart >= 0) {\n onObject(buffer.slice(objectStart, cursor + 1));\n objectStart = -1;\n }\n }\n }\n };\n}\n\nexport async function streamChatMessage(\n params: StreamChatMessageParams,\n callbacks: StreamCallbacks,\n signal?: AbortSignal,\n): Promise<void> {\n const {\n chatId,\n message,\n namespace,\n chatConfig,\n attachments,\n metadata,\n headers,\n } = params;\n const { onChunk, onDone, onError } = callbacks;\n\n const xsrfToken = readCookie('XSRF-TOKEN');\n const autoXsrf =\n !headers?.['x-xsrf-token'] && xsrfToken\n ? { 'x-xsrf-token': xsrfToken }\n : undefined;\n\n try {\n // eslint-disable-next-line no-undef\n const response = await fetch(DEFAULT_STREAM_PATH, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json', ...autoXsrf, ...headers },\n body: JSON.stringify({\n chatId,\n message,\n namespace,\n chatConfig,\n attachments,\n metadata,\n }),\n signal,\n });\n\n if (!response.ok || !response.body) {\n onError(new Error(`Stream request failed: ${response.status}`));\n return;\n }\n\n const scanForObjects = createObjectScanner((frame) => {\n const content = extractContent(frame);\n if (content !== null) {\n onChunk(content);\n }\n });\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n scanForObjects(decoder.decode(value, { stream: true }));\n }\n scanForObjects(decoder.decode());\n onDone();\n } catch (err) {\n if ((err as Error).name !== 'AbortError') {\n onError(err as Error);\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,228 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.useTextChatStream = useTextChatStream;
5
+ var _react = require("react");
6
+ var _reactQuery = require("@tanstack/react-query");
7
+ var _sdkConsumer = require("../../sdk-consumer");
8
+ var _streamChatMessage = require("./streamChatMessage");
9
+ /**
10
+ * A platform `ChatMessage` augmented with streaming metadata. The host renderer
11
+ * uses `streamStatus`/`streamingText` to drive its typewriter; confirmed
12
+ * (non-streamed) messages carry neither.
13
+ */
14
+
15
+ /**
16
+ * Per-send outcome resolved by `send`. Reported at the transport level,
17
+ * independent of the rAF reveal state — so a consumer can implement a
18
+ * race-free, reveal-independent safe fallback.
19
+ */
20
+
21
+ const messageKey = m => `${m.role}::${(m.content ?? '').trim()}`;
22
+
23
+ /**
24
+ * Streaming counterpart to `useTextChat`. Sends a message over the SSE transport
25
+ * and drips the accumulated reply into `streamingText` at a steady, time-paced
26
+ * rate via requestAnimationFrame.
27
+ *
28
+ * Uses the same React Query key as `useTextChat` so mounting both hooks
29
+ * does not double-fetch the message history.
30
+ *
31
+ * NOTE: uses `credentials: 'include'` + XSRF cookie auth — intended for
32
+ * Wix-internal consumers where the persona-chat gateway is same-origin.
33
+ */
34
+ function useTextChatStream(options = {}) {
35
+ const {
36
+ limit = 50,
37
+ headers,
38
+ onError,
39
+ revealMsPerChar = 16
40
+ } = options;
41
+ const {
42
+ chatId,
43
+ namespace,
44
+ personaChatService
45
+ } = (0, _sdkConsumer.useSdkConsumer)();
46
+
47
+ // Same query key as useTextChat — dedupes the history fetch when both are mounted.
48
+ const {
49
+ data: sdkMessages = [],
50
+ status,
51
+ fetchNextPage,
52
+ refetch
53
+ } = (0, _reactQuery.useInfiniteQuery)({
54
+ queryKey: ['chat-messages', chatId, namespace],
55
+ queryFn: async ({
56
+ pageParam
57
+ }) => {
58
+ if (!personaChatService) {
59
+ return {
60
+ messages: [],
61
+ nextCursor: undefined
62
+ };
63
+ }
64
+ return personaChatService.queryChatMessages({
65
+ chatId,
66
+ namespace,
67
+ limit,
68
+ cursor: pageParam
69
+ });
70
+ },
71
+ getNextPageParam: lastPage => lastPage.nextCursor,
72
+ initialPageParam: undefined,
73
+ select: data => data.pages.flatMap(page => page.messages)
74
+ });
75
+ const [streamingMessages, setStreamingMessages] = (0, _react.useState)([]);
76
+ const [completedMessages, setCompletedMessages] = (0, _react.useState)([]);
77
+ const abortRef = (0, _react.useRef)(null);
78
+ const rafRef = (0, _react.useRef)(null);
79
+ const lastConfirmedRef = (0, _react.useRef)([]);
80
+ const sendSeqRef = (0, _react.useRef)(0);
81
+ const stopRaf = (0, _react.useCallback)(() => {
82
+ if (rafRef.current !== null) {
83
+ cancelAnimationFrame(rafRef.current);
84
+ rafRef.current = null;
85
+ }
86
+ }, []);
87
+ (0, _react.useEffect)(() => {
88
+ return () => {
89
+ var _abortRef$current;
90
+ (_abortRef$current = abortRef.current) == null || _abortRef$current.abort();
91
+ stopRaf();
92
+ };
93
+ }, [stopRaf]);
94
+ const messages = (0, _react.useMemo)(() => {
95
+ const converted = [...sdkMessages].reverse();
96
+ const confirmedMessages = converted.length > 0 ? converted : lastConfirmedRef.current;
97
+ const completedKeys = new Set(completedMessages.map(messageKey));
98
+ const confirmedShown = confirmedMessages.filter(m => !completedKeys.has(messageKey(m)));
99
+ const chronological = [...confirmedShown, ...completedMessages, ...streamingMessages];
100
+ return chronological.reverse();
101
+ }, [sdkMessages, streamingMessages, completedMessages]);
102
+ (0, _react.useEffect)(() => {
103
+ const converted = [...sdkMessages].reverse();
104
+ if (converted.length > 0) {
105
+ lastConfirmedRef.current = converted;
106
+ }
107
+ }, [sdkMessages]);
108
+ const send = (0, _react.useCallback)(async ({
109
+ message,
110
+ chatConfig,
111
+ attachments,
112
+ metadata
113
+ }) => {
114
+ var _abortRef$current2;
115
+ (_abortRef$current2 = abortRef.current) == null || _abortRef$current2.abort();
116
+ const controller = new AbortController();
117
+ abortRef.current = controller;
118
+ const seq = sendSeqRef.current += 1;
119
+ const userMsgId = `stream-user-${Date.now()}-${seq}`;
120
+ const assistantMsgId = `stream-assistant-${Date.now()}-${seq}`;
121
+ const optimisticUserMsg = {
122
+ id: userMsgId,
123
+ role: 'USER',
124
+ content: message,
125
+ attachments: attachments
126
+ };
127
+ const streamingAssistantMsg = {
128
+ id: assistantMsgId,
129
+ role: 'ASSISTANT',
130
+ content: '',
131
+ streamStatus: 'streaming',
132
+ streamingText: ''
133
+ };
134
+ setStreamingMessages([optimisticUserMsg, streamingAssistantMsg]);
135
+ let accumulated = '';
136
+ let revealedLen = 0;
137
+ let streamDone = false;
138
+ let carry = 0;
139
+ let lastFrameTime = 0;
140
+ let receivedContent = false;
141
+ let errored = false;
142
+ const settle = () => {
143
+ stopRaf();
144
+ const finalAssistantMsg = {
145
+ ...streamingAssistantMsg,
146
+ content: accumulated,
147
+ streamStatus: 'settled',
148
+ streamingText: accumulated
149
+ };
150
+ setCompletedMessages(prev => [...prev, optimisticUserMsg, finalAssistantMsg]);
151
+ setStreamingMessages([]);
152
+ void refetch();
153
+ };
154
+ const frame = now => {
155
+ if (lastFrameTime === 0) {
156
+ lastFrameTime = now;
157
+ }
158
+ const elapsedMs = now - lastFrameTime;
159
+ lastFrameTime = now;
160
+ if (revealedLen < accumulated.length) {
161
+ carry += elapsedMs / revealMsPerChar;
162
+ const step = Math.floor(carry);
163
+ if (step > 0) {
164
+ carry -= step;
165
+ revealedLen = Math.min(accumulated.length, revealedLen + step);
166
+ setStreamingMessages([optimisticUserMsg, {
167
+ ...streamingAssistantMsg,
168
+ streamStatus: 'streaming',
169
+ streamingText: accumulated.slice(0, revealedLen)
170
+ }]);
171
+ }
172
+ rafRef.current = requestAnimationFrame(frame);
173
+ } else if (streamDone) {
174
+ settle();
175
+ } else {
176
+ stopRaf();
177
+ }
178
+ };
179
+ const startRaf = () => {
180
+ if (rafRef.current === null) {
181
+ lastFrameTime = 0;
182
+ rafRef.current = requestAnimationFrame(frame);
183
+ }
184
+ };
185
+ await (0, _streamChatMessage.streamChatMessage)({
186
+ chatId,
187
+ message,
188
+ namespace,
189
+ chatConfig,
190
+ attachments,
191
+ metadata,
192
+ headers
193
+ }, {
194
+ onChunk: token => {
195
+ receivedContent = true;
196
+ accumulated += token;
197
+ startRaf();
198
+ },
199
+ onDone: () => {
200
+ streamDone = true;
201
+ startRaf();
202
+ },
203
+ onError: err => {
204
+ errored = true;
205
+ stopRaf();
206
+ onError == null || onError(err);
207
+ setStreamingMessages([]);
208
+ }
209
+ }, controller.signal);
210
+ return {
211
+ ok: streamDone && !errored,
212
+ producedContent: receivedContent,
213
+ aborted: !streamDone && !errored
214
+ };
215
+ }, [chatId, namespace, headers, onError, refetch, revealMsPerChar, stopRaf]);
216
+ return {
217
+ status,
218
+ pending: [],
219
+ messages,
220
+ fetchNext,
221
+ refetch,
222
+ send
223
+ };
224
+ function fetchNext() {
225
+ void fetchNextPage();
226
+ }
227
+ }
228
+ //# sourceMappingURL=useTextChatStream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","require","_reactQuery","_sdkConsumer","_streamChatMessage","messageKey","m","role","content","trim","useTextChatStream","options","limit","headers","onError","revealMsPerChar","chatId","namespace","personaChatService","useSdkConsumer","data","sdkMessages","status","fetchNextPage","refetch","useInfiniteQuery","queryKey","queryFn","pageParam","messages","nextCursor","undefined","queryChatMessages","cursor","getNextPageParam","lastPage","initialPageParam","select","pages","flatMap","page","streamingMessages","setStreamingMessages","useState","completedMessages","setCompletedMessages","abortRef","useRef","rafRef","lastConfirmedRef","sendSeqRef","stopRaf","useCallback","current","cancelAnimationFrame","useEffect","_abortRef$current","abort","useMemo","converted","reverse","confirmedMessages","length","completedKeys","Set","map","confirmedShown","filter","has","chronological","send","message","chatConfig","attachments","metadata","_abortRef$current2","controller","AbortController","seq","userMsgId","Date","now","assistantMsgId","optimisticUserMsg","id","streamingAssistantMsg","streamStatus","streamingText","accumulated","revealedLen","streamDone","carry","lastFrameTime","receivedContent","errored","settle","finalAssistantMsg","prev","frame","elapsedMs","step","Math","floor","min","slice","requestAnimationFrame","startRaf","streamChatMessage","onChunk","token","onDone","err","signal","ok","producedContent","aborted","pending","fetchNext"],"sources":["../../../../src/hooks/useTextChat/useTextChatStream.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useInfiniteQuery } from '@tanstack/react-query';\nimport type { ChatMessage, ChatMessageAttachment } from '../../types';\nimport { useSdkConsumer } from '../../sdk-consumer';\nimport { streamChatMessage } from './streamChatMessage';\n\nexport type {\n StreamCallbacks,\n StreamChatMessageParams,\n} from './streamChatMessage';\n\n/**\n * A platform `ChatMessage` augmented with streaming metadata. The host renderer\n * uses `streamStatus`/`streamingText` to drive its typewriter; confirmed\n * (non-streamed) messages carry neither.\n */\nexport type StreamingChatMessage = ChatMessage & {\n streamingText?: string;\n streamStatus?: 'streaming' | 'settled';\n};\n\nexport interface UseTextChatStreamOptions {\n limit?: number;\n // Extra headers forwarded on every stream request — e.g. an `x-xsrf-token`\n // for the CSRF-protected persona-chat gateway.\n headers?: Record<string, string>;\n // Stream failures surfaced to the host (e.g. report to Sentry + toast).\n onError?: (err: Error) => void;\n // Milliseconds per character for the requestAnimationFrame reveal drip.\n // Default 16 (~62 chars/s).\n revealMsPerChar?: number;\n}\n\nexport interface SendStreamMessageParams {\n message: string;\n chatConfig?: object;\n attachments?: ChatMessageAttachment[];\n metadata?: object;\n}\n\n/**\n * Per-send outcome resolved by `send`. Reported at the transport level,\n * independent of the rAF reveal state — so a consumer can implement a\n * race-free, reveal-independent safe fallback.\n */\nexport type StreamSendResult = {\n ok: boolean; // stream completed successfully (reached onDone)\n producedContent: boolean; // at least one content chunk was received\n aborted: boolean; // the send was superseded/cancelled — not a real failure\n};\n\nconst messageKey = (m: StreamingChatMessage) =>\n `${m.role}::${(m.content ?? '').trim()}`;\n\n/**\n * Streaming counterpart to `useTextChat`. Sends a message over the SSE transport\n * and drips the accumulated reply into `streamingText` at a steady, time-paced\n * rate via requestAnimationFrame.\n *\n * Uses the same React Query key as `useTextChat` so mounting both hooks\n * does not double-fetch the message history.\n *\n * NOTE: uses `credentials: 'include'` + XSRF cookie auth — intended for\n * Wix-internal consumers where the persona-chat gateway is same-origin.\n */\nexport function useTextChatStream(options: UseTextChatStreamOptions = {}) {\n const { limit = 50, headers, onError, revealMsPerChar = 16 } = options;\n\n const { chatId, namespace, personaChatService } = useSdkConsumer();\n\n // Same query key as useTextChat — dedupes the history fetch when both are mounted.\n const {\n data: sdkMessages = [],\n status,\n fetchNextPage,\n refetch,\n } = useInfiniteQuery({\n queryKey: ['chat-messages', chatId, namespace],\n queryFn: async ({ pageParam }: { pageParam?: string }) => {\n if (!personaChatService) {\n return { messages: [], nextCursor: undefined };\n }\n return personaChatService.queryChatMessages({\n chatId,\n namespace,\n limit,\n cursor: pageParam,\n });\n },\n getNextPageParam: (lastPage) => lastPage.nextCursor,\n initialPageParam: undefined as string | undefined,\n select: (data) =>\n data.pages.flatMap((page) => page.messages as ChatMessage[]),\n });\n\n const [streamingMessages, setStreamingMessages] = useState<\n StreamingChatMessage[]\n >([]);\n const [completedMessages, setCompletedMessages] = useState<\n StreamingChatMessage[]\n >([]);\n const abortRef = useRef<AbortController | null>(null);\n const rafRef = useRef<number | null>(null);\n const lastConfirmedRef = useRef<StreamingChatMessage[]>([]);\n const sendSeqRef = useRef(0);\n\n const stopRaf = useCallback(() => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n }, []);\n\n useEffect(() => {\n return () => {\n abortRef.current?.abort();\n stopRaf();\n };\n }, [stopRaf]);\n\n const messages = useMemo<StreamingChatMessage[]>(() => {\n const converted = [...sdkMessages].reverse() as StreamingChatMessage[];\n const confirmedMessages =\n converted.length > 0 ? converted : lastConfirmedRef.current;\n\n const completedKeys = new Set(completedMessages.map(messageKey));\n const confirmedShown = confirmedMessages.filter(\n (m) => !completedKeys.has(messageKey(m)),\n );\n const chronological = [\n ...confirmedShown,\n ...completedMessages,\n ...streamingMessages,\n ];\n return chronological.reverse();\n }, [sdkMessages, streamingMessages, completedMessages]);\n\n useEffect(() => {\n const converted = [...sdkMessages].reverse() as StreamingChatMessage[];\n if (converted.length > 0) {\n lastConfirmedRef.current = converted;\n }\n }, [sdkMessages]);\n\n const send = useCallback(\n async ({\n message,\n chatConfig,\n attachments,\n metadata,\n }: SendStreamMessageParams): Promise<StreamSendResult> => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n const seq = (sendSeqRef.current += 1);\n const userMsgId = `stream-user-${Date.now()}-${seq}`;\n const assistantMsgId = `stream-assistant-${Date.now()}-${seq}`;\n\n const optimisticUserMsg: StreamingChatMessage = {\n id: userMsgId,\n role: 'USER' as ChatMessage['role'],\n content: message,\n attachments: attachments as StreamingChatMessage['attachments'],\n };\n\n const streamingAssistantMsg: StreamingChatMessage = {\n id: assistantMsgId,\n role: 'ASSISTANT' as ChatMessage['role'],\n content: '',\n streamStatus: 'streaming',\n streamingText: '',\n };\n\n setStreamingMessages([optimisticUserMsg, streamingAssistantMsg]);\n\n let accumulated = '';\n let revealedLen = 0;\n let streamDone = false;\n let carry = 0;\n let lastFrameTime = 0;\n let receivedContent = false;\n let errored = false;\n\n const settle = () => {\n stopRaf();\n const finalAssistantMsg: StreamingChatMessage = {\n ...streamingAssistantMsg,\n content: accumulated,\n streamStatus: 'settled',\n streamingText: accumulated,\n };\n setCompletedMessages((prev) => [\n ...prev,\n optimisticUserMsg,\n finalAssistantMsg,\n ]);\n setStreamingMessages([]);\n void refetch();\n };\n\n const frame = (now: number) => {\n if (lastFrameTime === 0) {\n lastFrameTime = now;\n }\n const elapsedMs = now - lastFrameTime;\n lastFrameTime = now;\n\n if (revealedLen < accumulated.length) {\n carry += elapsedMs / revealMsPerChar;\n const step = Math.floor(carry);\n if (step > 0) {\n carry -= step;\n revealedLen = Math.min(accumulated.length, revealedLen + step);\n setStreamingMessages([\n optimisticUserMsg,\n {\n ...streamingAssistantMsg,\n streamStatus: 'streaming',\n streamingText: accumulated.slice(0, revealedLen),\n },\n ]);\n }\n rafRef.current = requestAnimationFrame(frame);\n } else if (streamDone) {\n settle();\n } else {\n stopRaf();\n }\n };\n\n const startRaf = () => {\n if (rafRef.current === null) {\n lastFrameTime = 0;\n rafRef.current = requestAnimationFrame(frame);\n }\n };\n\n await streamChatMessage(\n {\n chatId,\n message,\n namespace,\n chatConfig,\n attachments,\n metadata,\n headers,\n },\n {\n onChunk: (token) => {\n receivedContent = true;\n accumulated += token;\n startRaf();\n },\n onDone: () => {\n streamDone = true;\n startRaf();\n },\n onError: (err) => {\n errored = true;\n stopRaf();\n onError?.(err);\n setStreamingMessages([]);\n },\n },\n controller.signal,\n );\n\n return {\n ok: streamDone && !errored,\n producedContent: receivedContent,\n aborted: !streamDone && !errored,\n };\n },\n [chatId, namespace, headers, onError, refetch, revealMsPerChar, stopRaf],\n );\n\n return {\n status,\n pending: [] as never[],\n messages,\n fetchNext,\n refetch,\n send,\n };\n\n function fetchNext() {\n void fetchNextPage();\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,2 +1,3 @@
1
1
  export * from './useTextChat';
2
+ export * from './useTextChatStream';
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["../../../../src/hooks/useTextChat/index.ts"],"sourcesContent":["export * from './useTextChat';\n"],"mappings":"AAAA,cAAc,eAAe","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["../../../../src/hooks/useTextChat/index.ts"],"sourcesContent":["export * from './useTextChat';\nexport * from './useTextChatStream';\n"],"mappings":"AAAA,cAAc,eAAe;AAC7B,cAAc,qBAAqB","ignoreList":[]}
@@ -0,0 +1,141 @@
1
+ // Same-origin Wix persona-chat gateway path. The transport always posts here
2
+ // with `credentials: 'include'`, so the session cookie rides along and a
3
+ // consumer needs no proxy route of its own.
4
+ const DEFAULT_STREAM_PATH = '/_api/persona-chat-service/v1/chat/messages/stream';
5
+ // Read a cookie value by name in the browser. Returns undefined outside a
6
+ // browser or when the cookie is absent.
7
+ function readCookie(name) {
8
+ var _document$cookie$spli;
9
+ if (typeof document === 'undefined') {
10
+ return undefined;
11
+ }
12
+ return (_document$cookie$spli = document.cookie.split(';').map(part => part.trim()).find(part => part.startsWith(`${name}=`))) == null ? void 0 : _document$cookie$spli.slice(name.length + 1);
13
+ }
14
+
15
+ // Pull the assistant text out of one streamed frame. Two framings reach us: the
16
+ // proxy route yields `{ content }` objects, while the gateway wraps each frame
17
+ // as `{ body: base64(json) }`. Returns null for frames without content.
18
+ export function extractContent(frame) {
19
+ try {
20
+ const parsed = JSON.parse(frame);
21
+ if (typeof parsed.content === 'string') {
22
+ return parsed.content;
23
+ }
24
+ if (typeof parsed.body === 'string') {
25
+ const decoded = new TextDecoder().decode(Uint8Array.from(atob(parsed.body), char => char.charCodeAt(0)));
26
+ const inner = JSON.parse(decoded);
27
+ return typeof inner.content === 'string' ? inner.content : null;
28
+ }
29
+ } catch {
30
+ // Incomplete or non-object frame — skip it.
31
+ }
32
+ return null;
33
+ }
34
+
35
+ // Feeds decoded text through a running scanner that emits each complete
36
+ // top-level `{…}` object as its closing brace arrives.
37
+ export function createObjectScanner(onObject) {
38
+ let buffer = '';
39
+ let cursor = 0;
40
+ let braceDepth = 0;
41
+ let objectStart = -1;
42
+ let inString = false;
43
+ let escaped = false;
44
+ return function feed(text) {
45
+ buffer += text;
46
+ for (; cursor < buffer.length; cursor++) {
47
+ const char = buffer[cursor];
48
+ if (inString) {
49
+ if (escaped) {
50
+ escaped = false;
51
+ } else if (char === '\\') {
52
+ escaped = true;
53
+ } else if (char === '"') {
54
+ inString = false;
55
+ }
56
+ continue;
57
+ }
58
+ if (char === '"') {
59
+ inString = true;
60
+ } else if (char === '{') {
61
+ if (braceDepth === 0) {
62
+ objectStart = cursor;
63
+ }
64
+ braceDepth++;
65
+ } else if (char === '}' && braceDepth > 0) {
66
+ braceDepth--;
67
+ if (braceDepth === 0 && objectStart >= 0) {
68
+ onObject(buffer.slice(objectStart, cursor + 1));
69
+ objectStart = -1;
70
+ }
71
+ }
72
+ }
73
+ };
74
+ }
75
+ export async function streamChatMessage(params, callbacks, signal) {
76
+ const chatId = params.chatId,
77
+ message = params.message,
78
+ namespace = params.namespace,
79
+ chatConfig = params.chatConfig,
80
+ attachments = params.attachments,
81
+ metadata = params.metadata,
82
+ headers = params.headers;
83
+ const onChunk = callbacks.onChunk,
84
+ onDone = callbacks.onDone,
85
+ onError = callbacks.onError;
86
+ const xsrfToken = readCookie('XSRF-TOKEN');
87
+ const autoXsrf = !(headers != null && headers['x-xsrf-token']) && xsrfToken ? {
88
+ 'x-xsrf-token': xsrfToken
89
+ } : undefined;
90
+ try {
91
+ // eslint-disable-next-line no-undef
92
+ const response = await fetch(DEFAULT_STREAM_PATH, {
93
+ method: 'POST',
94
+ credentials: 'include',
95
+ headers: {
96
+ 'Content-Type': 'application/json',
97
+ ...autoXsrf,
98
+ ...headers
99
+ },
100
+ body: JSON.stringify({
101
+ chatId,
102
+ message,
103
+ namespace,
104
+ chatConfig,
105
+ attachments,
106
+ metadata
107
+ }),
108
+ signal
109
+ });
110
+ if (!response.ok || !response.body) {
111
+ onError(new Error(`Stream request failed: ${response.status}`));
112
+ return;
113
+ }
114
+ const scanForObjects = createObjectScanner(frame => {
115
+ const content = extractContent(frame);
116
+ if (content !== null) {
117
+ onChunk(content);
118
+ }
119
+ });
120
+ const reader = response.body.getReader();
121
+ const decoder = new TextDecoder();
122
+ for (;;) {
123
+ const _await$reader$read = await reader.read(),
124
+ done = _await$reader$read.done,
125
+ value = _await$reader$read.value;
126
+ if (done) {
127
+ break;
128
+ }
129
+ scanForObjects(decoder.decode(value, {
130
+ stream: true
131
+ }));
132
+ }
133
+ scanForObjects(decoder.decode());
134
+ onDone();
135
+ } catch (err) {
136
+ if (err.name !== 'AbortError') {
137
+ onError(err);
138
+ }
139
+ }
140
+ }
141
+ //# sourceMappingURL=streamChatMessage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["DEFAULT_STREAM_PATH","readCookie","name","_document$cookie$spli","document","undefined","cookie","split","map","part","trim","find","startsWith","slice","length","extractContent","frame","parsed","JSON","parse","content","body","decoded","TextDecoder","decode","Uint8Array","from","atob","char","charCodeAt","inner","createObjectScanner","onObject","buffer","cursor","braceDepth","objectStart","inString","escaped","feed","text","streamChatMessage","params","callbacks","signal","chatId","message","namespace","chatConfig","attachments","metadata","headers","onChunk","onDone","onError","xsrfToken","autoXsrf","response","fetch","method","credentials","stringify","ok","Error","status","scanForObjects","reader","getReader","decoder","_await$reader$read","read","done","value","stream","err"],"sources":["../../../../src/hooks/useTextChat/streamChatMessage.ts"],"sourcesContent":["// Same-origin Wix persona-chat gateway path. The transport always posts here\n// with `credentials: 'include'`, so the session cookie rides along and a\n// consumer needs no proxy route of its own.\nconst DEFAULT_STREAM_PATH =\n '/_api/persona-chat-service/v1/chat/messages/stream';\n\nexport type StreamCallbacks = {\n onChunk: (token: string) => void;\n onDone: () => void;\n onError: (err: Error) => void;\n};\n\nexport type StreamChatMessageParams = {\n chatId: string;\n message: string;\n namespace: string;\n chatConfig?: object;\n attachments?: object[];\n // Request metadata forwarded to the RPC (e.g. `additionalMetadata` carrying\n // opening-message context). Mirrors the top-level `metadata` object the unary\n // `sendChatMessage` request accepts, so a streamed send carries it identically.\n metadata?: object;\n // Extra request headers merged onto the POST. Lets the caller satisfy the\n // host's transport requirements the SDK can't know about — e.g. an\n // `x-xsrf-token` when the endpoint sits behind a CSRF-protected gateway.\n headers?: Record<string, string>;\n};\n\n// Read a cookie value by name in the browser. Returns undefined outside a\n// browser or when the cookie is absent.\nfunction readCookie(name: string): string | undefined {\n if (typeof document === 'undefined') {\n return undefined;\n }\n return document.cookie\n .split(';')\n .map((part) => part.trim())\n .find((part) => part.startsWith(`${name}=`))\n ?.slice(name.length + 1);\n}\n\n// Pull the assistant text out of one streamed frame. Two framings reach us: the\n// proxy route yields `{ content }` objects, while the gateway wraps each frame\n// as `{ body: base64(json) }`. Returns null for frames without content.\nexport function extractContent(frame: string): string | null {\n try {\n const parsed = JSON.parse(frame);\n if (typeof parsed.content === 'string') {\n return parsed.content;\n }\n if (typeof parsed.body === 'string') {\n const decoded = new TextDecoder().decode(\n Uint8Array.from(atob(parsed.body), (char) => char.charCodeAt(0)),\n );\n const inner = JSON.parse(decoded);\n return typeof inner.content === 'string' ? inner.content : null;\n }\n } catch {\n // Incomplete or non-object frame — skip it.\n }\n return null;\n}\n\n// Feeds decoded text through a running scanner that emits each complete\n// top-level `{…}` object as its closing brace arrives.\nexport function createObjectScanner(onObject: (frame: string) => void) {\n let buffer = '';\n let cursor = 0;\n let braceDepth = 0;\n let objectStart = -1;\n let inString = false;\n let escaped = false;\n\n return function feed(text: string) {\n buffer += text;\n for (; cursor < buffer.length; cursor++) {\n const char = buffer[cursor];\n if (inString) {\n if (escaped) {\n escaped = false;\n } else if (char === '\\\\') {\n escaped = true;\n } else if (char === '\"') {\n inString = false;\n }\n continue;\n }\n if (char === '\"') {\n inString = true;\n } else if (char === '{') {\n if (braceDepth === 0) {\n objectStart = cursor;\n }\n braceDepth++;\n } else if (char === '}' && braceDepth > 0) {\n braceDepth--;\n if (braceDepth === 0 && objectStart >= 0) {\n onObject(buffer.slice(objectStart, cursor + 1));\n objectStart = -1;\n }\n }\n }\n };\n}\n\nexport async function streamChatMessage(\n params: StreamChatMessageParams,\n callbacks: StreamCallbacks,\n signal?: AbortSignal,\n): Promise<void> {\n const {\n chatId,\n message,\n namespace,\n chatConfig,\n attachments,\n metadata,\n headers,\n } = params;\n const { onChunk, onDone, onError } = callbacks;\n\n const xsrfToken = readCookie('XSRF-TOKEN');\n const autoXsrf =\n !headers?.['x-xsrf-token'] && xsrfToken\n ? { 'x-xsrf-token': xsrfToken }\n : undefined;\n\n try {\n // eslint-disable-next-line no-undef\n const response = await fetch(DEFAULT_STREAM_PATH, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json', ...autoXsrf, ...headers },\n body: JSON.stringify({\n chatId,\n message,\n namespace,\n chatConfig,\n attachments,\n metadata,\n }),\n signal,\n });\n\n if (!response.ok || !response.body) {\n onError(new Error(`Stream request failed: ${response.status}`));\n return;\n }\n\n const scanForObjects = createObjectScanner((frame) => {\n const content = extractContent(frame);\n if (content !== null) {\n onChunk(content);\n }\n });\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n scanForObjects(decoder.decode(value, { stream: true }));\n }\n scanForObjects(decoder.decode());\n onDone();\n } catch (err) {\n if ((err as Error).name !== 'AbortError') {\n onError(err as Error);\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,230 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useInfiniteQuery } from '@tanstack/react-query';
3
+ import { useSdkConsumer } from '../../sdk-consumer';
4
+ import { streamChatMessage } from './streamChatMessage';
5
+
6
+ /**
7
+ * A platform `ChatMessage` augmented with streaming metadata. The host renderer
8
+ * uses `streamStatus`/`streamingText` to drive its typewriter; confirmed
9
+ * (non-streamed) messages carry neither.
10
+ */
11
+
12
+ /**
13
+ * Per-send outcome resolved by `send`. Reported at the transport level,
14
+ * independent of the rAF reveal state — so a consumer can implement a
15
+ * race-free, reveal-independent safe fallback.
16
+ */
17
+
18
+ const messageKey = m => `${m.role}::${(m.content ?? '').trim()}`;
19
+
20
+ /**
21
+ * Streaming counterpart to `useTextChat`. Sends a message over the SSE transport
22
+ * and drips the accumulated reply into `streamingText` at a steady, time-paced
23
+ * rate via requestAnimationFrame.
24
+ *
25
+ * Uses the same React Query key as `useTextChat` so mounting both hooks
26
+ * does not double-fetch the message history.
27
+ *
28
+ * NOTE: uses `credentials: 'include'` + XSRF cookie auth — intended for
29
+ * Wix-internal consumers where the persona-chat gateway is same-origin.
30
+ */
31
+ export function useTextChatStream(options) {
32
+ if (options === void 0) {
33
+ options = {};
34
+ }
35
+ const _options = options,
36
+ _options$limit = _options.limit,
37
+ limit = _options$limit === void 0 ? 50 : _options$limit,
38
+ headers = _options.headers,
39
+ onError = _options.onError,
40
+ _options$revealMsPerC = _options.revealMsPerChar,
41
+ revealMsPerChar = _options$revealMsPerC === void 0 ? 16 : _options$revealMsPerC;
42
+ const _useSdkConsumer = useSdkConsumer(),
43
+ chatId = _useSdkConsumer.chatId,
44
+ namespace = _useSdkConsumer.namespace,
45
+ personaChatService = _useSdkConsumer.personaChatService;
46
+
47
+ // Same query key as useTextChat — dedupes the history fetch when both are mounted.
48
+ const _useInfiniteQuery = useInfiniteQuery({
49
+ queryKey: ['chat-messages', chatId, namespace],
50
+ queryFn: async _ref => {
51
+ let pageParam = _ref.pageParam;
52
+ if (!personaChatService) {
53
+ return {
54
+ messages: [],
55
+ nextCursor: undefined
56
+ };
57
+ }
58
+ return personaChatService.queryChatMessages({
59
+ chatId,
60
+ namespace,
61
+ limit,
62
+ cursor: pageParam
63
+ });
64
+ },
65
+ getNextPageParam: lastPage => lastPage.nextCursor,
66
+ initialPageParam: undefined,
67
+ select: data => data.pages.flatMap(page => page.messages)
68
+ }),
69
+ _useInfiniteQuery$dat = _useInfiniteQuery.data,
70
+ sdkMessages = _useInfiniteQuery$dat === void 0 ? [] : _useInfiniteQuery$dat,
71
+ status = _useInfiniteQuery.status,
72
+ fetchNextPage = _useInfiniteQuery.fetchNextPage,
73
+ refetch = _useInfiniteQuery.refetch;
74
+ const _useState = useState([]),
75
+ streamingMessages = _useState[0],
76
+ setStreamingMessages = _useState[1];
77
+ const _useState2 = useState([]),
78
+ completedMessages = _useState2[0],
79
+ setCompletedMessages = _useState2[1];
80
+ const abortRef = useRef(null);
81
+ const rafRef = useRef(null);
82
+ const lastConfirmedRef = useRef([]);
83
+ const sendSeqRef = useRef(0);
84
+ const stopRaf = useCallback(() => {
85
+ if (rafRef.current !== null) {
86
+ cancelAnimationFrame(rafRef.current);
87
+ rafRef.current = null;
88
+ }
89
+ }, []);
90
+ useEffect(() => {
91
+ return () => {
92
+ var _abortRef$current;
93
+ (_abortRef$current = abortRef.current) == null || _abortRef$current.abort();
94
+ stopRaf();
95
+ };
96
+ }, [stopRaf]);
97
+ const messages = useMemo(() => {
98
+ const converted = [...sdkMessages].reverse();
99
+ const confirmedMessages = converted.length > 0 ? converted : lastConfirmedRef.current;
100
+ const completedKeys = new Set(completedMessages.map(messageKey));
101
+ const confirmedShown = confirmedMessages.filter(m => !completedKeys.has(messageKey(m)));
102
+ const chronological = [...confirmedShown, ...completedMessages, ...streamingMessages];
103
+ return chronological.reverse();
104
+ }, [sdkMessages, streamingMessages, completedMessages]);
105
+ useEffect(() => {
106
+ const converted = [...sdkMessages].reverse();
107
+ if (converted.length > 0) {
108
+ lastConfirmedRef.current = converted;
109
+ }
110
+ }, [sdkMessages]);
111
+ const send = useCallback(async _ref2 => {
112
+ var _abortRef$current2;
113
+ let message = _ref2.message,
114
+ chatConfig = _ref2.chatConfig,
115
+ attachments = _ref2.attachments,
116
+ metadata = _ref2.metadata;
117
+ (_abortRef$current2 = abortRef.current) == null || _abortRef$current2.abort();
118
+ const controller = new AbortController();
119
+ abortRef.current = controller;
120
+ const seq = sendSeqRef.current += 1;
121
+ const userMsgId = `stream-user-${Date.now()}-${seq}`;
122
+ const assistantMsgId = `stream-assistant-${Date.now()}-${seq}`;
123
+ const optimisticUserMsg = {
124
+ id: userMsgId,
125
+ role: 'USER',
126
+ content: message,
127
+ attachments: attachments
128
+ };
129
+ const streamingAssistantMsg = {
130
+ id: assistantMsgId,
131
+ role: 'ASSISTANT',
132
+ content: '',
133
+ streamStatus: 'streaming',
134
+ streamingText: ''
135
+ };
136
+ setStreamingMessages([optimisticUserMsg, streamingAssistantMsg]);
137
+ let accumulated = '';
138
+ let revealedLen = 0;
139
+ let streamDone = false;
140
+ let carry = 0;
141
+ let lastFrameTime = 0;
142
+ let receivedContent = false;
143
+ let errored = false;
144
+ const settle = () => {
145
+ stopRaf();
146
+ const finalAssistantMsg = {
147
+ ...streamingAssistantMsg,
148
+ content: accumulated,
149
+ streamStatus: 'settled',
150
+ streamingText: accumulated
151
+ };
152
+ setCompletedMessages(prev => [...prev, optimisticUserMsg, finalAssistantMsg]);
153
+ setStreamingMessages([]);
154
+ void refetch();
155
+ };
156
+ const frame = now => {
157
+ if (lastFrameTime === 0) {
158
+ lastFrameTime = now;
159
+ }
160
+ const elapsedMs = now - lastFrameTime;
161
+ lastFrameTime = now;
162
+ if (revealedLen < accumulated.length) {
163
+ carry += elapsedMs / revealMsPerChar;
164
+ const step = Math.floor(carry);
165
+ if (step > 0) {
166
+ carry -= step;
167
+ revealedLen = Math.min(accumulated.length, revealedLen + step);
168
+ setStreamingMessages([optimisticUserMsg, {
169
+ ...streamingAssistantMsg,
170
+ streamStatus: 'streaming',
171
+ streamingText: accumulated.slice(0, revealedLen)
172
+ }]);
173
+ }
174
+ rafRef.current = requestAnimationFrame(frame);
175
+ } else if (streamDone) {
176
+ settle();
177
+ } else {
178
+ stopRaf();
179
+ }
180
+ };
181
+ const startRaf = () => {
182
+ if (rafRef.current === null) {
183
+ lastFrameTime = 0;
184
+ rafRef.current = requestAnimationFrame(frame);
185
+ }
186
+ };
187
+ await streamChatMessage({
188
+ chatId,
189
+ message,
190
+ namespace,
191
+ chatConfig,
192
+ attachments,
193
+ metadata,
194
+ headers
195
+ }, {
196
+ onChunk: token => {
197
+ receivedContent = true;
198
+ accumulated += token;
199
+ startRaf();
200
+ },
201
+ onDone: () => {
202
+ streamDone = true;
203
+ startRaf();
204
+ },
205
+ onError: err => {
206
+ errored = true;
207
+ stopRaf();
208
+ onError == null || onError(err);
209
+ setStreamingMessages([]);
210
+ }
211
+ }, controller.signal);
212
+ return {
213
+ ok: streamDone && !errored,
214
+ producedContent: receivedContent,
215
+ aborted: !streamDone && !errored
216
+ };
217
+ }, [chatId, namespace, headers, onError, refetch, revealMsPerChar, stopRaf]);
218
+ return {
219
+ status,
220
+ pending: [],
221
+ messages,
222
+ fetchNext,
223
+ refetch,
224
+ send
225
+ };
226
+ function fetchNext() {
227
+ void fetchNextPage();
228
+ }
229
+ }
230
+ //# sourceMappingURL=useTextChatStream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useCallback","useEffect","useMemo","useRef","useState","useInfiniteQuery","useSdkConsumer","streamChatMessage","messageKey","m","role","content","trim","useTextChatStream","options","_options","_options$limit","limit","headers","onError","_options$revealMsPerC","revealMsPerChar","_useSdkConsumer","chatId","namespace","personaChatService","_useInfiniteQuery","queryKey","queryFn","_ref","pageParam","messages","nextCursor","undefined","queryChatMessages","cursor","getNextPageParam","lastPage","initialPageParam","select","data","pages","flatMap","page","_useInfiniteQuery$dat","sdkMessages","status","fetchNextPage","refetch","_useState","streamingMessages","setStreamingMessages","_useState2","completedMessages","setCompletedMessages","abortRef","rafRef","lastConfirmedRef","sendSeqRef","stopRaf","current","cancelAnimationFrame","_abortRef$current","abort","converted","reverse","confirmedMessages","length","completedKeys","Set","map","confirmedShown","filter","has","chronological","send","_ref2","_abortRef$current2","message","chatConfig","attachments","metadata","controller","AbortController","seq","userMsgId","Date","now","assistantMsgId","optimisticUserMsg","id","streamingAssistantMsg","streamStatus","streamingText","accumulated","revealedLen","streamDone","carry","lastFrameTime","receivedContent","errored","settle","finalAssistantMsg","prev","frame","elapsedMs","step","Math","floor","min","slice","requestAnimationFrame","startRaf","onChunk","token","onDone","err","signal","ok","producedContent","aborted","pending","fetchNext"],"sources":["../../../../src/hooks/useTextChat/useTextChatStream.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useInfiniteQuery } from '@tanstack/react-query';\nimport type { ChatMessage, ChatMessageAttachment } from '../../types';\nimport { useSdkConsumer } from '../../sdk-consumer';\nimport { streamChatMessage } from './streamChatMessage';\n\nexport type {\n StreamCallbacks,\n StreamChatMessageParams,\n} from './streamChatMessage';\n\n/**\n * A platform `ChatMessage` augmented with streaming metadata. The host renderer\n * uses `streamStatus`/`streamingText` to drive its typewriter; confirmed\n * (non-streamed) messages carry neither.\n */\nexport type StreamingChatMessage = ChatMessage & {\n streamingText?: string;\n streamStatus?: 'streaming' | 'settled';\n};\n\nexport interface UseTextChatStreamOptions {\n limit?: number;\n // Extra headers forwarded on every stream request — e.g. an `x-xsrf-token`\n // for the CSRF-protected persona-chat gateway.\n headers?: Record<string, string>;\n // Stream failures surfaced to the host (e.g. report to Sentry + toast).\n onError?: (err: Error) => void;\n // Milliseconds per character for the requestAnimationFrame reveal drip.\n // Default 16 (~62 chars/s).\n revealMsPerChar?: number;\n}\n\nexport interface SendStreamMessageParams {\n message: string;\n chatConfig?: object;\n attachments?: ChatMessageAttachment[];\n metadata?: object;\n}\n\n/**\n * Per-send outcome resolved by `send`. Reported at the transport level,\n * independent of the rAF reveal state — so a consumer can implement a\n * race-free, reveal-independent safe fallback.\n */\nexport type StreamSendResult = {\n ok: boolean; // stream completed successfully (reached onDone)\n producedContent: boolean; // at least one content chunk was received\n aborted: boolean; // the send was superseded/cancelled — not a real failure\n};\n\nconst messageKey = (m: StreamingChatMessage) =>\n `${m.role}::${(m.content ?? '').trim()}`;\n\n/**\n * Streaming counterpart to `useTextChat`. Sends a message over the SSE transport\n * and drips the accumulated reply into `streamingText` at a steady, time-paced\n * rate via requestAnimationFrame.\n *\n * Uses the same React Query key as `useTextChat` so mounting both hooks\n * does not double-fetch the message history.\n *\n * NOTE: uses `credentials: 'include'` + XSRF cookie auth — intended for\n * Wix-internal consumers where the persona-chat gateway is same-origin.\n */\nexport function useTextChatStream(options: UseTextChatStreamOptions = {}) {\n const { limit = 50, headers, onError, revealMsPerChar = 16 } = options;\n\n const { chatId, namespace, personaChatService } = useSdkConsumer();\n\n // Same query key as useTextChat — dedupes the history fetch when both are mounted.\n const {\n data: sdkMessages = [],\n status,\n fetchNextPage,\n refetch,\n } = useInfiniteQuery({\n queryKey: ['chat-messages', chatId, namespace],\n queryFn: async ({ pageParam }: { pageParam?: string }) => {\n if (!personaChatService) {\n return { messages: [], nextCursor: undefined };\n }\n return personaChatService.queryChatMessages({\n chatId,\n namespace,\n limit,\n cursor: pageParam,\n });\n },\n getNextPageParam: (lastPage) => lastPage.nextCursor,\n initialPageParam: undefined as string | undefined,\n select: (data) =>\n data.pages.flatMap((page) => page.messages as ChatMessage[]),\n });\n\n const [streamingMessages, setStreamingMessages] = useState<\n StreamingChatMessage[]\n >([]);\n const [completedMessages, setCompletedMessages] = useState<\n StreamingChatMessage[]\n >([]);\n const abortRef = useRef<AbortController | null>(null);\n const rafRef = useRef<number | null>(null);\n const lastConfirmedRef = useRef<StreamingChatMessage[]>([]);\n const sendSeqRef = useRef(0);\n\n const stopRaf = useCallback(() => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n }, []);\n\n useEffect(() => {\n return () => {\n abortRef.current?.abort();\n stopRaf();\n };\n }, [stopRaf]);\n\n const messages = useMemo<StreamingChatMessage[]>(() => {\n const converted = [...sdkMessages].reverse() as StreamingChatMessage[];\n const confirmedMessages =\n converted.length > 0 ? converted : lastConfirmedRef.current;\n\n const completedKeys = new Set(completedMessages.map(messageKey));\n const confirmedShown = confirmedMessages.filter(\n (m) => !completedKeys.has(messageKey(m)),\n );\n const chronological = [\n ...confirmedShown,\n ...completedMessages,\n ...streamingMessages,\n ];\n return chronological.reverse();\n }, [sdkMessages, streamingMessages, completedMessages]);\n\n useEffect(() => {\n const converted = [...sdkMessages].reverse() as StreamingChatMessage[];\n if (converted.length > 0) {\n lastConfirmedRef.current = converted;\n }\n }, [sdkMessages]);\n\n const send = useCallback(\n async ({\n message,\n chatConfig,\n attachments,\n metadata,\n }: SendStreamMessageParams): Promise<StreamSendResult> => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n const seq = (sendSeqRef.current += 1);\n const userMsgId = `stream-user-${Date.now()}-${seq}`;\n const assistantMsgId = `stream-assistant-${Date.now()}-${seq}`;\n\n const optimisticUserMsg: StreamingChatMessage = {\n id: userMsgId,\n role: 'USER' as ChatMessage['role'],\n content: message,\n attachments: attachments as StreamingChatMessage['attachments'],\n };\n\n const streamingAssistantMsg: StreamingChatMessage = {\n id: assistantMsgId,\n role: 'ASSISTANT' as ChatMessage['role'],\n content: '',\n streamStatus: 'streaming',\n streamingText: '',\n };\n\n setStreamingMessages([optimisticUserMsg, streamingAssistantMsg]);\n\n let accumulated = '';\n let revealedLen = 0;\n let streamDone = false;\n let carry = 0;\n let lastFrameTime = 0;\n let receivedContent = false;\n let errored = false;\n\n const settle = () => {\n stopRaf();\n const finalAssistantMsg: StreamingChatMessage = {\n ...streamingAssistantMsg,\n content: accumulated,\n streamStatus: 'settled',\n streamingText: accumulated,\n };\n setCompletedMessages((prev) => [\n ...prev,\n optimisticUserMsg,\n finalAssistantMsg,\n ]);\n setStreamingMessages([]);\n void refetch();\n };\n\n const frame = (now: number) => {\n if (lastFrameTime === 0) {\n lastFrameTime = now;\n }\n const elapsedMs = now - lastFrameTime;\n lastFrameTime = now;\n\n if (revealedLen < accumulated.length) {\n carry += elapsedMs / revealMsPerChar;\n const step = Math.floor(carry);\n if (step > 0) {\n carry -= step;\n revealedLen = Math.min(accumulated.length, revealedLen + step);\n setStreamingMessages([\n optimisticUserMsg,\n {\n ...streamingAssistantMsg,\n streamStatus: 'streaming',\n streamingText: accumulated.slice(0, revealedLen),\n },\n ]);\n }\n rafRef.current = requestAnimationFrame(frame);\n } else if (streamDone) {\n settle();\n } else {\n stopRaf();\n }\n };\n\n const startRaf = () => {\n if (rafRef.current === null) {\n lastFrameTime = 0;\n rafRef.current = requestAnimationFrame(frame);\n }\n };\n\n await streamChatMessage(\n {\n chatId,\n message,\n namespace,\n chatConfig,\n attachments,\n metadata,\n headers,\n },\n {\n onChunk: (token) => {\n receivedContent = true;\n accumulated += token;\n startRaf();\n },\n onDone: () => {\n streamDone = true;\n startRaf();\n },\n onError: (err) => {\n errored = true;\n stopRaf();\n onError?.(err);\n setStreamingMessages([]);\n },\n },\n controller.signal,\n );\n\n return {\n ok: streamDone && !errored,\n producedContent: receivedContent,\n aborted: !streamDone && !errored,\n };\n },\n [chatId, namespace, headers, onError, refetch, revealMsPerChar, stopRaf],\n );\n\n return {\n status,\n pending: [] as never[],\n messages,\n fetchNext,\n refetch,\n send,\n };\n\n function fetchNext() {\n void fetchNextPage();\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,2 +1,3 @@
1
1
  export * from './useTextChat';
2
+ export * from './useTextChatStream';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextChat/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextChat/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,18 @@
1
+ export type StreamCallbacks = {
2
+ onChunk: (token: string) => void;
3
+ onDone: () => void;
4
+ onError: (err: Error) => void;
5
+ };
6
+ export type StreamChatMessageParams = {
7
+ chatId: string;
8
+ message: string;
9
+ namespace: string;
10
+ chatConfig?: object;
11
+ attachments?: object[];
12
+ metadata?: object;
13
+ headers?: Record<string, string>;
14
+ };
15
+ export declare function extractContent(frame: string): string | null;
16
+ export declare function createObjectScanner(onObject: (frame: string) => void): (text: string) => void;
17
+ export declare function streamChatMessage(params: StreamChatMessageParams, callbacks: StreamCallbacks, signal?: AbortSignal): Promise<void>;
18
+ //# sourceMappingURL=streamChatMessage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamChatMessage.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextChat/streamChatMessage.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAIvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAIlB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAkBF,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiB3D;AAID,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,UAQxC,MAAM,UA8BlC;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,uBAAuB,EAC/B,SAAS,EAAE,eAAe,EAC1B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA+Df"}
@@ -0,0 +1,53 @@
1
+ import type { ChatMessage, ChatMessageAttachment } from '../../types';
2
+ export type { StreamCallbacks, StreamChatMessageParams, } from './streamChatMessage';
3
+ /**
4
+ * A platform `ChatMessage` augmented with streaming metadata. The host renderer
5
+ * uses `streamStatus`/`streamingText` to drive its typewriter; confirmed
6
+ * (non-streamed) messages carry neither.
7
+ */
8
+ export type StreamingChatMessage = ChatMessage & {
9
+ streamingText?: string;
10
+ streamStatus?: 'streaming' | 'settled';
11
+ };
12
+ export interface UseTextChatStreamOptions {
13
+ limit?: number;
14
+ headers?: Record<string, string>;
15
+ onError?: (err: Error) => void;
16
+ revealMsPerChar?: number;
17
+ }
18
+ export interface SendStreamMessageParams {
19
+ message: string;
20
+ chatConfig?: object;
21
+ attachments?: ChatMessageAttachment[];
22
+ metadata?: object;
23
+ }
24
+ /**
25
+ * Per-send outcome resolved by `send`. Reported at the transport level,
26
+ * independent of the rAF reveal state — so a consumer can implement a
27
+ * race-free, reveal-independent safe fallback.
28
+ */
29
+ export type StreamSendResult = {
30
+ ok: boolean;
31
+ producedContent: boolean;
32
+ aborted: boolean;
33
+ };
34
+ /**
35
+ * Streaming counterpart to `useTextChat`. Sends a message over the SSE transport
36
+ * and drips the accumulated reply into `streamingText` at a steady, time-paced
37
+ * rate via requestAnimationFrame.
38
+ *
39
+ * Uses the same React Query key as `useTextChat` so mounting both hooks
40
+ * does not double-fetch the message history.
41
+ *
42
+ * NOTE: uses `credentials: 'include'` + XSRF cookie auth — intended for
43
+ * Wix-internal consumers where the persona-chat gateway is same-origin.
44
+ */
45
+ export declare function useTextChatStream(options?: UseTextChatStreamOptions): {
46
+ status: "pending" | "error" | "success";
47
+ pending: never[];
48
+ messages: StreamingChatMessage[];
49
+ fetchNext: () => void;
50
+ refetch: (options?: import("@tanstack/query-core/build/legacy/hydration-BlEVG2Lp").aq | undefined) => Promise<import("@tanstack/query-core/build/legacy/hydration-BlEVG2Lp").aH<ChatMessage[], Error>>;
51
+ send: ({ message, chatConfig, attachments, metadata, }: SendStreamMessageParams) => Promise<StreamSendResult>;
52
+ };
53
+ //# sourceMappingURL=useTextChatStream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTextChatStream.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextChat/useTextChatStream.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAItE,YAAY,EACV,eAAe,EACf,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAE7B;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,WAAW,GAAG;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CACxC,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IAGf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;IAG/B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,qBAAqB,EAAE,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAKF;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B;;;;;;4DAqFjE,uBAAuB,KAAG,QAAQ,gBAAgB,CAAC;EA2IzD"}
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.14.0",
2
+ "version": "1.16.0",
3
3
  "name": "@wix/legends-platform-sdk",
4
4
  "license": "MIT",
5
5
  "author": {
@@ -103,5 +103,5 @@
103
103
  "wallaby": {
104
104
  "autoDetect": true
105
105
  },
106
- "falconPackageHash": "39a885b1b2fc1889cfc0cb3701e0ece68ad5da768eb647b0b9970e23"
106
+ "falconPackageHash": "f9a5891b405ebfa64741ade86c1a924e1e787822a89b42952a939184"
107
107
  }