@tambo-ai/react 0.46.3 → 0.46.4

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-component-state.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-component-state.test.d.ts","sourceRoot":"","sources":["../../../src/hooks/__tests__/use-component-state.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,324 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_1 = require("@testing-library/react");
4
+ const tambo_client_provider_1 = require("../../providers/tambo-client-provider");
5
+ const tambo_thread_provider_1 = require("../../providers/tambo-thread-provider");
6
+ const use_component_state_1 = require("../use-component-state");
7
+ const use_current_message_1 = require("../use-current-message");
8
+ // Mock the required providers
9
+ jest.mock("../../providers/tambo-client-provider", () => ({
10
+ useTamboClient: jest.fn(),
11
+ }));
12
+ jest.mock("../../providers/tambo-thread-provider", () => ({
13
+ useTamboThread: jest.fn(),
14
+ }));
15
+ jest.mock("../use-current-message", () => ({
16
+ useTamboCurrentMessage: jest.fn(),
17
+ }));
18
+ // Create a mock debounced function with flush method
19
+ const createMockDebouncedFunction = (fn) => {
20
+ const debouncedFn = jest.fn((...args) => fn(...args));
21
+ debouncedFn.flush = jest.fn();
22
+ debouncedFn.cancel = jest.fn();
23
+ debouncedFn.isPending = jest.fn(() => false);
24
+ return debouncedFn;
25
+ };
26
+ // Mock use-debounce
27
+ jest.mock("use-debounce", () => ({
28
+ useDebouncedCallback: jest.fn(),
29
+ }));
30
+ // Import the mocked useDebouncedCallback
31
+ const use_debounce_1 = require("use-debounce");
32
+ describe("useTamboComponentState", () => {
33
+ // Helper function to create mock TamboThreadMessage
34
+ const createMockMessage = (overrides = {}) => ({
35
+ id: "test-message-id",
36
+ threadId: "test-thread-id",
37
+ componentState: {},
38
+ content: [{ type: "text", text: "Test message" }],
39
+ createdAt: new Date().toISOString(),
40
+ role: "assistant",
41
+ ...overrides,
42
+ });
43
+ const mockUpdateThreadMessage = jest.fn();
44
+ const mockUpdateComponentState = jest.fn();
45
+ beforeEach(() => {
46
+ jest.clearAllMocks();
47
+ // Setup default mock for useDebouncedCallback
48
+ jest
49
+ .mocked(use_debounce_1.useDebouncedCallback)
50
+ .mockImplementation((fn) => createMockDebouncedFunction(fn));
51
+ // Setup default mocks
52
+ jest.mocked(tambo_client_provider_1.useTamboClient).mockReturnValue({
53
+ beta: {
54
+ threads: {
55
+ messages: {
56
+ updateComponentState: mockUpdateComponentState,
57
+ },
58
+ },
59
+ },
60
+ });
61
+ jest.mocked(tambo_thread_provider_1.useTamboThread).mockReturnValue({
62
+ updateThreadMessage: mockUpdateThreadMessage,
63
+ });
64
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage());
65
+ });
66
+ describe("Initial State Management", () => {
67
+ it("should initialize with initialValue when no componentState exists", () => {
68
+ const initialValue = "test-initial";
69
+ jest
70
+ .mocked(use_current_message_1.useTamboCurrentMessage)
71
+ .mockReturnValue(createMockMessage({ componentState: {} }));
72
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", initialValue));
73
+ expect(result.current[0]).toBe(initialValue);
74
+ });
75
+ it("should use existing componentState value over initialValue", () => {
76
+ const initialValue = "initial";
77
+ const existingValue = "existing";
78
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
79
+ componentState: { testKey: existingValue },
80
+ }));
81
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", initialValue));
82
+ expect(result.current[0]).toBe(existingValue);
83
+ });
84
+ it("should handle undefined initialValue gracefully", () => {
85
+ jest
86
+ .mocked(use_current_message_1.useTamboCurrentMessage)
87
+ .mockReturnValue(createMockMessage({ componentState: {} }));
88
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey"));
89
+ expect(result.current[0]).toBeUndefined();
90
+ });
91
+ it("should handle different data types correctly", () => {
92
+ const testCases = [
93
+ { value: "string" },
94
+ { value: 42 },
95
+ { value: true },
96
+ { value: { name: "test" } },
97
+ { value: [1, 2, 3] },
98
+ ];
99
+ testCases.forEach(({ value }) => {
100
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
101
+ componentState: { testKey: value },
102
+ }));
103
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", value));
104
+ expect(result.current[0]).toEqual(value);
105
+ });
106
+ });
107
+ });
108
+ describe("State Updates", () => {
109
+ it("should update local state immediately when setValue is called", () => {
110
+ const initialValue = "initial";
111
+ jest
112
+ .mocked(use_current_message_1.useTamboCurrentMessage)
113
+ .mockReturnValue(createMockMessage({ componentState: { testKey: initialValue } }));
114
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", initialValue));
115
+ const newValue = "updated";
116
+ (0, react_1.act)(() => {
117
+ result.current[1](newValue);
118
+ });
119
+ expect(result.current[0]).toBe(newValue);
120
+ });
121
+ it("should trigger local thread message update when setValue is called", () => {
122
+ const message = createMockMessage({
123
+ componentState: { testKey: "initial" },
124
+ });
125
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
126
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
127
+ const newValue = "updated";
128
+ (0, react_1.act)(() => {
129
+ result.current[1](newValue);
130
+ });
131
+ expect(mockUpdateThreadMessage).toHaveBeenCalledWith(message.id, {
132
+ threadId: message.threadId,
133
+ componentState: {
134
+ testKey: newValue,
135
+ },
136
+ }, false);
137
+ });
138
+ it("should trigger debounced remote API call when setValue is called", () => {
139
+ const message = createMockMessage({
140
+ componentState: { testKey: "initial" },
141
+ });
142
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
143
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
144
+ const newValue = "updated";
145
+ (0, react_1.act)(() => {
146
+ result.current[1](newValue);
147
+ });
148
+ // The debounced function should be called
149
+ expect(mockUpdateComponentState).toHaveBeenCalledWith(message.threadId, message.id, { state: { testKey: newValue } });
150
+ });
151
+ it("should work with complex objects and arrays", () => {
152
+ const initialValue = { name: "test", items: [1, 2, 3] };
153
+ jest
154
+ .mocked(use_current_message_1.useTamboCurrentMessage)
155
+ .mockReturnValue(createMockMessage({ componentState: { testKey: initialValue } }));
156
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", initialValue));
157
+ const newValue = { name: "updated", items: [4, 5, 6] };
158
+ (0, react_1.act)(() => {
159
+ result.current[1](newValue);
160
+ });
161
+ expect(result.current[0]).toEqual(newValue);
162
+ expect(mockUpdateThreadMessage).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
163
+ componentState: {
164
+ testKey: newValue,
165
+ },
166
+ }), false);
167
+ });
168
+ });
169
+ describe("Debouncing Behavior", () => {
170
+ it("should use default debounce time of 500ms", () => {
171
+ (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
172
+ expect(use_debounce_1.useDebouncedCallback).toHaveBeenCalledWith(expect.any(Function), 500);
173
+ });
174
+ it("should use custom debounce time when provided", () => {
175
+ const customDebounceTime = 1000;
176
+ (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial", undefined, customDebounceTime));
177
+ expect(use_debounce_1.useDebouncedCallback).toHaveBeenCalledWith(expect.any(Function), customDebounceTime);
178
+ });
179
+ it("should flush debounced callback on unmount", () => {
180
+ const mockFlush = jest.fn();
181
+ const mockDebouncedFn = createMockDebouncedFunction(jest.fn());
182
+ mockDebouncedFn.flush = mockFlush;
183
+ // Mock the debounced callback to return our specific mock
184
+ jest.mocked(use_debounce_1.useDebouncedCallback).mockReturnValue(mockDebouncedFn);
185
+ const { unmount } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
186
+ unmount();
187
+ expect(mockFlush).toHaveBeenCalled();
188
+ });
189
+ });
190
+ describe("Multi-Hook Scenarios", () => {
191
+ it("should handle multiple hooks with different keyNames independently", () => {
192
+ const message = createMockMessage({
193
+ componentState: {
194
+ key1: "value1",
195
+ key2: "value2",
196
+ },
197
+ });
198
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
199
+ const { result: result1 } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("key1", "default1"));
200
+ const { result: result2 } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("key2", "default2"));
201
+ expect(result1.current[0]).toBe("value1");
202
+ expect(result2.current[0]).toBe("value2");
203
+ // Update first hook
204
+ (0, react_1.act)(() => {
205
+ result1.current[1]("updated1");
206
+ });
207
+ expect(result1.current[0]).toBe("updated1");
208
+ expect(result2.current[0]).toBe("value2"); // Should remain unchanged
209
+ });
210
+ it("should preserve existing componentState when updating another key", () => {
211
+ const message = createMockMessage({
212
+ componentState: {
213
+ existingKey: "existing",
214
+ testKey: "initial",
215
+ },
216
+ });
217
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
218
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
219
+ (0, react_1.act)(() => {
220
+ result.current[1]("updated");
221
+ });
222
+ expect(mockUpdateThreadMessage).toHaveBeenCalledWith(message.id, {
223
+ threadId: message.threadId,
224
+ componentState: {
225
+ existingKey: "existing", // Should preserve existing keys
226
+ testKey: "updated",
227
+ },
228
+ }, false);
229
+ });
230
+ });
231
+ describe("SetFromProp Feature", () => {
232
+ it("should set value from prop when hasSetFromMessage is false", () => {
233
+ jest
234
+ .mocked(use_current_message_1.useTamboCurrentMessage)
235
+ .mockReturnValue(createMockMessage({ componentState: {} }));
236
+ const propValue = "from-prop";
237
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial", propValue));
238
+ // Initially, hasSetFromMessage should be false, so prop value should be used
239
+ expect(result.current[0]).toBe(propValue);
240
+ });
241
+ it("should ignore setFromProp when initialized from message state", async () => {
242
+ const existingValue = "existing";
243
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
244
+ componentState: { testKey: existingValue },
245
+ }));
246
+ const propValue = "from-prop";
247
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial", propValue));
248
+ // Should use existing value from message, not prop value
249
+ await (0, react_1.act)(async () => {
250
+ await new Promise((resolve) => setTimeout(resolve, 0));
251
+ });
252
+ expect(result.current[0]).toBe(existingValue);
253
+ });
254
+ it("should update state from setFromProp changes when no message state exists", () => {
255
+ jest
256
+ .mocked(use_current_message_1.useTamboCurrentMessage)
257
+ .mockReturnValue(createMockMessage({ componentState: {} }));
258
+ const { result, rerender } = (0, react_1.renderHook)(({ propValue }) => (0, use_component_state_1.useTamboComponentState)("testKey", "initial", propValue), { initialProps: { propValue: "prop1" } });
259
+ expect(result.current[0]).toBe("prop1");
260
+ // Change prop value
261
+ rerender({ propValue: "prop2" });
262
+ // Since hasSetFromMessage is still false (no message state),
263
+ // it should update to new prop value
264
+ expect(result.current[0]).toBe("prop2");
265
+ });
266
+ it("should handle undefined setFromProp gracefully", () => {
267
+ jest
268
+ .mocked(use_current_message_1.useTamboCurrentMessage)
269
+ .mockReturnValue(createMockMessage({ componentState: {} }));
270
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial", undefined));
271
+ expect(result.current[0]).toBe("initial");
272
+ });
273
+ });
274
+ describe("Message State Sync", () => {
275
+ it("should sync with message.componentState changes", () => {
276
+ const { result, rerender } = (0, react_1.renderHook)(({ message }) => {
277
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
278
+ return (0, use_component_state_1.useTamboComponentState)("testKey", "initial");
279
+ }, {
280
+ initialProps: {
281
+ message: createMockMessage({
282
+ componentState: { testKey: "value1" },
283
+ }),
284
+ },
285
+ });
286
+ // Change the message
287
+ const newMessage = createMockMessage({
288
+ componentState: { testKey: "value2" },
289
+ });
290
+ rerender({ message: newMessage });
291
+ // The hook should sync with the new message state
292
+ expect(result.current[0]).toBe("value2");
293
+ expect(mockUpdateThreadMessage).not.toHaveBeenCalled();
294
+ });
295
+ it("should handle message without componentState gracefully", () => {
296
+ jest
297
+ .mocked(use_current_message_1.useTamboCurrentMessage)
298
+ .mockReturnValue(createMockMessage({ componentState: undefined }));
299
+ const { result } = (0, react_1.renderHook)(() => (0, use_component_state_1.useTamboComponentState)("testKey", "initial"));
300
+ expect(result.current[0]).toBe("initial");
301
+ });
302
+ it("should preserve state when message updates but componentState[keyName] unchanged", () => {
303
+ const message1 = createMockMessage({
304
+ id: "message1",
305
+ componentState: { testKey: "unchanged" },
306
+ });
307
+ const message2 = createMockMessage({
308
+ id: "message2",
309
+ componentState: { testKey: "unchanged" },
310
+ });
311
+ const { result, rerender } = (0, react_1.renderHook)(({ message }) => {
312
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(message);
313
+ return (0, use_component_state_1.useTamboComponentState)("testKey", "initial");
314
+ }, { initialProps: { message: message1 } });
315
+ // Clear previous calls
316
+ mockUpdateThreadMessage.mockClear();
317
+ // Change message but keep same componentState value
318
+ rerender({ message: message2 });
319
+ // Should preserve the "unchanged" value
320
+ expect(result.current[0]).toBe("unchanged");
321
+ });
322
+ });
323
+ });
324
+ //# sourceMappingURL=use-component-state.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-component-state.test.js","sourceRoot":"","sources":["../../../src/hooks/__tests__/use-component-state.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import { act, renderHook } from \"@testing-library/react\";\nimport { TamboThreadMessage } from \"../../model/generate-component-response\";\nimport { useTamboClient } from \"../../providers/tambo-client-provider\";\nimport { useTamboThread } from \"../../providers/tambo-thread-provider\";\nimport { PartialTamboAI } from \"../../testing/types\";\nimport { useTamboComponentState } from \"../use-component-state\";\nimport { useTamboCurrentMessage } from \"../use-current-message\";\n\n// Mock the required providers\njest.mock(\"../../providers/tambo-client-provider\", () => ({\n useTamboClient: jest.fn(),\n}));\n\njest.mock(\"../../providers/tambo-thread-provider\", () => ({\n useTamboThread: jest.fn(),\n}));\n\njest.mock(\"../use-current-message\", () => ({\n useTamboCurrentMessage: jest.fn(),\n}));\n\n// Create a mock debounced function with flush method\nconst createMockDebouncedFunction = (fn: any) => {\n const debouncedFn = jest.fn((...args: any[]) => fn(...args)) as jest.Mock & {\n flush: jest.Mock;\n cancel: jest.Mock;\n isPending: () => boolean;\n };\n debouncedFn.flush = jest.fn();\n debouncedFn.cancel = jest.fn();\n debouncedFn.isPending = jest.fn(() => false);\n return debouncedFn;\n};\n\n// Mock use-debounce\njest.mock(\"use-debounce\", () => ({\n useDebouncedCallback: jest.fn(),\n}));\n\n// Import the mocked useDebouncedCallback\nimport { useDebouncedCallback } from \"use-debounce\";\n\ndescribe(\"useTamboComponentState\", () => {\n // Helper function to create mock TamboThreadMessage\n const createMockMessage = (\n overrides: Partial<TamboThreadMessage> = {},\n ): TamboThreadMessage => ({\n id: \"test-message-id\",\n threadId: \"test-thread-id\",\n componentState: {},\n content: [{ type: \"text\", text: \"Test message\" }],\n createdAt: new Date().toISOString(),\n role: \"assistant\",\n ...overrides,\n });\n\n const mockUpdateThreadMessage = jest.fn();\n const mockUpdateComponentState = jest.fn();\n\n beforeEach(() => {\n jest.clearAllMocks();\n\n // Setup default mock for useDebouncedCallback\n jest\n .mocked(useDebouncedCallback)\n .mockImplementation((fn) => createMockDebouncedFunction(fn));\n\n // Setup default mocks\n jest.mocked(useTamboClient).mockReturnValue({\n beta: {\n threads: {\n messages: {\n updateComponentState: mockUpdateComponentState,\n },\n },\n },\n } satisfies PartialTamboAI as any);\n\n jest.mocked(useTamboThread).mockReturnValue({\n updateThreadMessage: mockUpdateThreadMessage,\n } as any);\n\n jest.mocked(useTamboCurrentMessage).mockReturnValue(createMockMessage());\n });\n\n describe(\"Initial State Management\", () => {\n it(\"should initialize with initialValue when no componentState exists\", () => {\n const initialValue = \"test-initial\";\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(createMockMessage({ componentState: {} }));\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", initialValue),\n );\n\n expect(result.current[0]).toBe(initialValue);\n });\n\n it(\"should use existing componentState value over initialValue\", () => {\n const initialValue = \"initial\";\n const existingValue = \"existing\";\n jest.mocked(useTamboCurrentMessage).mockReturnValue(\n createMockMessage({\n componentState: { testKey: existingValue },\n }),\n );\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", initialValue),\n );\n\n expect(result.current[0]).toBe(existingValue);\n });\n\n it(\"should handle undefined initialValue gracefully\", () => {\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(createMockMessage({ componentState: {} }));\n\n const { result } = renderHook(() => useTamboComponentState(\"testKey\"));\n\n expect(result.current[0]).toBeUndefined();\n });\n\n it(\"should handle different data types correctly\", () => {\n const testCases = [\n { value: \"string\" },\n { value: 42 },\n { value: true },\n { value: { name: \"test\" } },\n { value: [1, 2, 3] },\n ];\n\n testCases.forEach(({ value }) => {\n jest.mocked(useTamboCurrentMessage).mockReturnValue(\n createMockMessage({\n componentState: { testKey: value },\n }),\n );\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", value),\n );\n\n expect(result.current[0]).toEqual(value);\n });\n });\n });\n\n describe(\"State Updates\", () => {\n it(\"should update local state immediately when setValue is called\", () => {\n const initialValue = \"initial\";\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(\n createMockMessage({ componentState: { testKey: initialValue } }),\n );\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", initialValue),\n );\n\n const newValue = \"updated\";\n act(() => {\n result.current[1](newValue);\n });\n\n expect(result.current[0]).toBe(newValue);\n });\n\n it(\"should trigger local thread message update when setValue is called\", () => {\n const message = createMockMessage({\n componentState: { testKey: \"initial\" },\n });\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\"),\n );\n\n const newValue = \"updated\";\n act(() => {\n result.current[1](newValue);\n });\n\n expect(mockUpdateThreadMessage).toHaveBeenCalledWith(\n message.id,\n {\n threadId: message.threadId,\n componentState: {\n testKey: newValue,\n },\n },\n false,\n );\n });\n\n it(\"should trigger debounced remote API call when setValue is called\", () => {\n const message = createMockMessage({\n componentState: { testKey: \"initial\" },\n });\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\"),\n );\n\n const newValue = \"updated\";\n act(() => {\n result.current[1](newValue);\n });\n\n // The debounced function should be called\n expect(mockUpdateComponentState).toHaveBeenCalledWith(\n message.threadId,\n message.id,\n { state: { testKey: newValue } },\n );\n });\n\n it(\"should work with complex objects and arrays\", () => {\n const initialValue = { name: \"test\", items: [1, 2, 3] };\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(\n createMockMessage({ componentState: { testKey: initialValue } }),\n );\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", initialValue),\n );\n\n const newValue = { name: \"updated\", items: [4, 5, 6] };\n act(() => {\n result.current[1](newValue);\n });\n\n expect(result.current[0]).toEqual(newValue);\n expect(mockUpdateThreadMessage).toHaveBeenCalledWith(\n expect.any(String),\n expect.objectContaining({\n componentState: {\n testKey: newValue,\n },\n }),\n false,\n );\n });\n });\n\n describe(\"Debouncing Behavior\", () => {\n it(\"should use default debounce time of 500ms\", () => {\n renderHook(() => useTamboComponentState(\"testKey\", \"initial\"));\n\n expect(useDebouncedCallback).toHaveBeenCalledWith(\n expect.any(Function),\n 500,\n );\n });\n\n it(\"should use custom debounce time when provided\", () => {\n const customDebounceTime = 1000;\n\n renderHook(() =>\n useTamboComponentState(\n \"testKey\",\n \"initial\",\n undefined,\n customDebounceTime,\n ),\n );\n\n expect(useDebouncedCallback).toHaveBeenCalledWith(\n expect.any(Function),\n customDebounceTime,\n );\n });\n\n it(\"should flush debounced callback on unmount\", () => {\n const mockFlush = jest.fn();\n const mockDebouncedFn = createMockDebouncedFunction(jest.fn());\n mockDebouncedFn.flush = mockFlush;\n\n // Mock the debounced callback to return our specific mock\n jest.mocked(useDebouncedCallback).mockReturnValue(mockDebouncedFn);\n\n const { unmount } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\"),\n );\n\n unmount();\n\n expect(mockFlush).toHaveBeenCalled();\n });\n });\n\n describe(\"Multi-Hook Scenarios\", () => {\n it(\"should handle multiple hooks with different keyNames independently\", () => {\n const message = createMockMessage({\n componentState: {\n key1: \"value1\",\n key2: \"value2\",\n },\n });\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n\n const { result: result1 } = renderHook(() =>\n useTamboComponentState(\"key1\", \"default1\"),\n );\n const { result: result2 } = renderHook(() =>\n useTamboComponentState(\"key2\", \"default2\"),\n );\n\n expect(result1.current[0]).toBe(\"value1\");\n expect(result2.current[0]).toBe(\"value2\");\n\n // Update first hook\n act(() => {\n result1.current[1](\"updated1\");\n });\n\n expect(result1.current[0]).toBe(\"updated1\");\n expect(result2.current[0]).toBe(\"value2\"); // Should remain unchanged\n });\n\n it(\"should preserve existing componentState when updating another key\", () => {\n const message = createMockMessage({\n componentState: {\n existingKey: \"existing\",\n testKey: \"initial\",\n },\n });\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\"),\n );\n\n act(() => {\n result.current[1](\"updated\");\n });\n\n expect(mockUpdateThreadMessage).toHaveBeenCalledWith(\n message.id,\n {\n threadId: message.threadId,\n componentState: {\n existingKey: \"existing\", // Should preserve existing keys\n testKey: \"updated\",\n },\n },\n false,\n );\n });\n });\n\n describe(\"SetFromProp Feature\", () => {\n it(\"should set value from prop when hasSetFromMessage is false\", () => {\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(createMockMessage({ componentState: {} }));\n\n const propValue = \"from-prop\";\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\", propValue),\n );\n\n // Initially, hasSetFromMessage should be false, so prop value should be used\n expect(result.current[0]).toBe(propValue);\n });\n\n it(\"should ignore setFromProp when initialized from message state\", async () => {\n const existingValue = \"existing\";\n jest.mocked(useTamboCurrentMessage).mockReturnValue(\n createMockMessage({\n componentState: { testKey: existingValue },\n }),\n );\n\n const propValue = \"from-prop\";\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\", propValue),\n );\n\n // Should use existing value from message, not prop value\n await act(async () => {\n await new Promise((resolve) => setTimeout(resolve, 0));\n });\n\n expect(result.current[0]).toBe(existingValue);\n });\n\n it(\"should update state from setFromProp changes when no message state exists\", () => {\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(createMockMessage({ componentState: {} }));\n\n const { result, rerender } = renderHook(\n ({ propValue }) =>\n useTamboComponentState(\"testKey\", \"initial\", propValue),\n { initialProps: { propValue: \"prop1\" } },\n );\n\n expect(result.current[0]).toBe(\"prop1\");\n\n // Change prop value\n rerender({ propValue: \"prop2\" });\n\n // Since hasSetFromMessage is still false (no message state),\n // it should update to new prop value\n expect(result.current[0]).toBe(\"prop2\");\n });\n\n it(\"should handle undefined setFromProp gracefully\", () => {\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(createMockMessage({ componentState: {} }));\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\", undefined),\n );\n\n expect(result.current[0]).toBe(\"initial\");\n });\n });\n\n describe(\"Message State Sync\", () => {\n it(\"should sync with message.componentState changes\", () => {\n const { result, rerender } = renderHook(\n ({ message }) => {\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n return useTamboComponentState(\"testKey\", \"initial\");\n },\n {\n initialProps: {\n message: createMockMessage({\n componentState: { testKey: \"value1\" },\n }),\n },\n },\n );\n\n // Change the message\n const newMessage = createMockMessage({\n componentState: { testKey: \"value2\" },\n });\n\n rerender({ message: newMessage });\n\n // The hook should sync with the new message state\n expect(result.current[0]).toBe(\"value2\");\n expect(mockUpdateThreadMessage).not.toHaveBeenCalled();\n });\n\n it(\"should handle message without componentState gracefully\", () => {\n jest\n .mocked(useTamboCurrentMessage)\n .mockReturnValue(\n createMockMessage({ componentState: undefined as any }),\n );\n\n const { result } = renderHook(() =>\n useTamboComponentState(\"testKey\", \"initial\"),\n );\n\n expect(result.current[0]).toBe(\"initial\");\n });\n\n it(\"should preserve state when message updates but componentState[keyName] unchanged\", () => {\n const message1 = createMockMessage({\n id: \"message1\",\n componentState: { testKey: \"unchanged\" },\n });\n const message2 = createMockMessage({\n id: \"message2\",\n componentState: { testKey: \"unchanged\" },\n });\n\n const { result, rerender } = renderHook(\n ({ message }) => {\n jest.mocked(useTamboCurrentMessage).mockReturnValue(message);\n return useTamboComponentState(\"testKey\", \"initial\");\n },\n { initialProps: { message: message1 } },\n );\n\n // Clear previous calls\n mockUpdateThreadMessage.mockClear();\n\n // Change message but keep same componentState value\n rerender({ message: message2 });\n\n // Should preserve the \"unchanged\" value\n expect(result.current[0]).toBe(\"unchanged\");\n });\n });\n});\n"]}
@@ -1,24 +1,16 @@
1
- interface ComponentStateMeta {
2
- isPending: boolean;
3
- }
4
- type StateUpdateResult<T> = [
5
- currentState: T,
6
- setState: (newState: T) => void,
7
- meta: ComponentStateMeta
8
- ];
1
+ type StateUpdateResult<T> = [currentState: T, setState: (newState: T) => void];
9
2
  /**
10
- * A React hook that provides state management and passes user updates to Tambo.
3
+ * A React hook that acts like useState, but also automatically updates the thread message's componentState.
11
4
  * Benefits: Passes user changes to AI, and when threads are returned, state is preserved.
12
- * @param keyName - The unique key to identify this state within the message's componentState object
13
- * @param initialValue - Optional initial value for the state, used if no value exists in the message
14
- * @param debounceTime - Optional debounce time in milliseconds (default: 300ms) to limit API calls
5
+ * @param keyName - The unique key to identify this state value within the message's componentState object
6
+ * @param initialValue - Optional initial value for the state, used if no componentState value exists in the Tambo message containing this hook usage.
7
+ * @param setFromProp - Optional value used to set the state value, only while no componentState value exists in the Tambo message containing this hook usage. Use this to allow streaming updates from a prop to the state value.
8
+ * @param debounceTime - Optional debounce time in milliseconds (default: 500ms) to limit API calls.
15
9
  * @returns A tuple containing:
16
10
  * - The current state value
17
11
  * - A setter function to update the state (updates UI immediately, debounces server sync)
18
- * - A metadata object with properties like isPending to track sync status
19
12
  * @example
20
- * // Basic usage
21
- * const [count, setCount, { isPending }] = useTamboComponentState("counter", 0);
13
+ * const [count, setCount] = useTamboComponentState("counter", 0);
22
14
  *
23
15
  * // Usage with object state
24
16
  * const [formState, setFormState] = useTamboComponentState("myForm", {
@@ -35,7 +27,7 @@ type StateUpdateResult<T> = [
35
27
  * });
36
28
  * };
37
29
  */
38
- export declare function useTamboComponentState<S = undefined>(keyName: string, initialValue?: S, debounceTime?: number): StateUpdateResult<S | undefined>;
39
- export declare function useTamboComponentState<S>(keyName: string, initialValue: S, debounceTime?: number): StateUpdateResult<S>;
30
+ export declare function useTamboComponentState<S = undefined>(keyName: string, initialValue?: S, setFromProp?: S, debounceTime?: number): StateUpdateResult<S | undefined>;
31
+ export declare function useTamboComponentState<S>(keyName: string, initialValue: S, setFromProp?: S, debounceTime?: number): StateUpdateResult<S>;
40
32
  export {};
41
33
  //# sourceMappingURL=use-component-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-component-state.d.ts","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"AAMA,UAAU,kBAAkB;IAC1B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,KAAK,iBAAiB,CAAC,CAAC,IAAI;IAC1B,YAAY,EAAE,CAAC;IACf,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI;IAC/B,IAAI,EAAE,kBAAkB;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,SAAS,EAClD,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,CAAC,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AACpC,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"use-component-state.d.ts","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"AAMA,KAAK,iBAAiB,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,SAAS,EAClD,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,CAAC,EAChB,WAAW,CAAC,EAAE,CAAC,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AACpC,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,EACf,WAAW,CAAC,EAAE,CAAC,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,iBAAiB,CAAC,CAAC,CAAC,CAAC"}
@@ -4,136 +4,59 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.useTamboComponentState = useTamboComponentState;
5
5
  const react_1 = require("react");
6
6
  const use_debounce_1 = require("use-debounce");
7
- const providers_1 = require("../providers");
7
+ const __1 = require("..");
8
8
  const use_current_message_1 = require("./use-current-message");
9
- function useTamboComponentState(keyName, initialValue, debounceTime = 500) {
9
+ function useTamboComponentState(keyName, initialValue, setFromProp, debounceTime = 500) {
10
10
  const message = (0, use_current_message_1.useTamboCurrentMessage)();
11
- const { updateThreadMessage, thread } = (0, providers_1.useTamboThread)();
12
- const client = (0, providers_1.useTamboClient)();
13
- const messageId = message.id;
14
- const threadId = thread.id;
15
- // Initial value management
16
- const [cachedInitialValue] = (0, react_1.useState)(() => initialValue);
17
- // UI state management
18
- const [localState, setLocalState] = (0, react_1.useState)(cachedInitialValue);
19
- // Synchronization state
20
- const [isPending, setIsPending] = (0, react_1.useState)(false);
21
- // Track the last user-initiated value instead of a simple boolean flag
22
- const [lastUserValue, setLastUserValue] = (0, react_1.useState)(null);
23
- const [haveInitialized, setHaveInitialized] = (0, react_1.useState)(false);
24
- // Determine if we need to initialize state
25
- const shouldInitialize = !haveInitialized &&
26
- message &&
27
- cachedInitialValue !== undefined &&
28
- (!message.componentState || !(keyName in message.componentState));
29
- // Sync local state with message state on initial load and when message changes
30
- (0, react_1.useEffect)(() => {
31
- if (message?.componentState && keyName in message.componentState) {
32
- const messageState = message.componentState[keyName];
33
- // Only update local state if we haven't had any user changes yet
34
- if (lastUserValue === null) {
35
- setLocalState(messageState);
36
- }
37
- }
38
- // Otherwise fall back to initial value if we have one and no user changes
39
- else if (cachedInitialValue !== undefined &&
40
- !localState &&
41
- lastUserValue === null) {
42
- setLocalState(cachedInitialValue);
43
- }
44
- }, [
45
- keyName,
46
- message?.componentState,
47
- cachedInitialValue,
48
- lastUserValue,
49
- localState,
50
- ]);
51
- // Create debounced save function for efficient server synchronization
52
- const debouncedServerWrite = (0, use_debounce_1.useDebouncedCallback)(async (newValue) => {
53
- setIsPending(true);
54
- try {
55
- const componentStateUpdate = {
56
- state: { [keyName]: newValue },
57
- };
58
- await client.beta.threads.messages.updateComponentState(threadId, messageId, componentStateUpdate);
59
- }
60
- catch (err) {
61
- console.error(`Failed to save component state for key "${keyName}":`, err);
62
- }
63
- finally {
64
- setIsPending(false);
65
- }
11
+ const { updateThreadMessage } = (0, __1.useTamboThread)();
12
+ const client = (0, __1.useTamboClient)();
13
+ const messageState = message?.componentState?.[keyName];
14
+ const [localState, setLocalState] = (0, react_1.useState)(messageState ?? initialValue);
15
+ const [initializedFromThreadMessage, setInitializedFromThreadMessage] = (0, react_1.useState)(messageState ? true : false);
16
+ // Optimistically update the local thread message's componentState
17
+ const updateLocalThreadMessage = (0, react_1.useCallback)((newState, existingMessage) => {
18
+ const updatedMessage = {
19
+ threadId: existingMessage.threadId,
20
+ componentState: {
21
+ ...existingMessage.componentState,
22
+ [keyName]: newState,
23
+ },
24
+ };
25
+ updateThreadMessage(existingMessage.id, updatedMessage, false);
26
+ }, [updateThreadMessage, keyName]);
27
+ // Debounced callback to update the remote thread message's componentState
28
+ const updateRemoteThreadMessage = (0, use_debounce_1.useDebouncedCallback)(async (newState, existingMessage) => {
29
+ const componentStateUpdate = {
30
+ state: { [keyName]: newState },
31
+ };
32
+ await client.beta.threads.messages.updateComponentState(existingMessage.threadId, existingMessage.id, componentStateUpdate);
66
33
  }, debounceTime);
67
- // Initialize state on first render if needed
68
- const initializeState = (0, react_1.useCallback)(async () => {
69
- if (!message) {
70
- console.warn(`Cannot initialize state for missing message ${messageId} with key "${keyName}"`);
34
+ const setValue = (0, react_1.useCallback)((newState) => {
35
+ setLocalState(newState);
36
+ updateLocalThreadMessage(newState, message);
37
+ updateRemoteThreadMessage(newState, message);
38
+ }, [message, updateLocalThreadMessage, updateRemoteThreadMessage]);
39
+ // Mirror the thread message's componentState value to the local state
40
+ (0, react_1.useEffect)(() => {
41
+ const messageState = message?.componentState?.[keyName];
42
+ if (!messageState) {
71
43
  return;
72
44
  }
73
- try {
74
- const messageUpdate = {
75
- ...message,
76
- componentState: {
77
- ...message.componentState,
78
- [keyName]: cachedInitialValue,
79
- },
80
- };
81
- const componentStateUpdate = {
82
- state: { [keyName]: cachedInitialValue },
83
- };
84
- await Promise.all([
85
- updateThreadMessage(messageId, messageUpdate, false),
86
- client.beta.threads.messages.updateComponentState(threadId, messageId, componentStateUpdate),
87
- ]);
88
- }
89
- catch (err) {
90
- console.warn(`Failed to initialize component state for key "${keyName}":`, err);
91
- }
92
- }, [
93
- cachedInitialValue,
94
- client.beta.threads.messages,
95
- keyName,
96
- message,
97
- messageId,
98
- threadId,
99
- updateThreadMessage,
100
- ]);
101
- // Send initial state when component mounts
45
+ setInitializedFromThreadMessage(true);
46
+ setLocalState(message.componentState?.[keyName]);
47
+ }, [message?.componentState?.[keyName], message, keyName]);
48
+ // For editable fields that are set from a prop to allow streaming updates, don't overwrite a fetched state value set from the thread message with prop value on initial load.
102
49
  (0, react_1.useEffect)(() => {
103
- if (shouldInitialize) {
104
- initializeState();
105
- setHaveInitialized(true);
106
- }
107
- }, [initializeState, shouldInitialize]);
108
- // setValue function for updating state
109
- // Updates local state immediately and schedules debounced server sync
110
- const setValue = (0, react_1.useCallback)((newValue) => {
111
- // Track this as a user-initiated update
112
- setLastUserValue(newValue);
113
- setLocalState(newValue);
114
- // Only trigger server updates if we have a message
115
- if (message) {
116
- debouncedServerWrite(newValue);
117
- const messageUpdate = {
118
- ...message,
119
- componentState: {
120
- ...message.componentState,
121
- [keyName]: newValue,
122
- },
123
- };
124
- updateThreadMessage(messageId, messageUpdate, false);
125
- }
126
- else {
127
- console.warn(`Cannot update server for missing message ${messageId} with key "${keyName}"`);
50
+ if (setFromProp !== undefined && !initializedFromThreadMessage) {
51
+ setLocalState(setFromProp);
128
52
  }
129
- }, [message, debouncedServerWrite, keyName, updateThreadMessage, messageId]);
53
+ }, [setFromProp, initializedFromThreadMessage]);
130
54
  // Ensure pending changes are flushed on unmount
131
55
  (0, react_1.useEffect)(() => {
132
56
  return () => {
133
- debouncedServerWrite.flush();
57
+ updateRemoteThreadMessage.flush();
134
58
  };
135
- }, [debouncedServerWrite]);
136
- // Return the local state for immediate UI rendering
137
- return [localState, setValue, { isPending }];
59
+ }, [updateRemoteThreadMessage]);
60
+ return [localState, setValue];
138
61
  }
139
62
  //# sourceMappingURL=use-component-state.js.map