@tambo-ai/react 0.50.0 → 0.53.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.
Files changed (89) hide show
  1. package/dist/hooks/__tests__/use-component-state.test.js +4 -1
  2. package/dist/hooks/__tests__/use-component-state.test.js.map +1 -1
  3. package/dist/hooks/__tests__/use-tambo-threads.test.js +1 -1
  4. package/dist/hooks/__tests__/use-tambo-threads.test.js.map +1 -1
  5. package/dist/hooks/use-component-state.js +3 -3
  6. package/dist/hooks/use-component-state.js.map +1 -1
  7. package/dist/hooks/use-suggestions.js +4 -2
  8. package/dist/hooks/use-suggestions.js.map +1 -1
  9. package/dist/mcp/__tests__/mcp-client.test.d.ts +2 -0
  10. package/dist/mcp/__tests__/mcp-client.test.d.ts.map +1 -0
  11. package/dist/mcp/__tests__/mcp-client.test.js +502 -0
  12. package/dist/mcp/__tests__/mcp-client.test.js.map +1 -0
  13. package/dist/mcp/mcp-client.d.ts +77 -3
  14. package/dist/mcp/mcp-client.d.ts.map +1 -1
  15. package/dist/mcp/mcp-client.js +184 -19
  16. package/dist/mcp/mcp-client.js.map +1 -1
  17. package/dist/mcp/tambo-mcp-provider.d.ts +1 -0
  18. package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
  19. package/dist/mcp/tambo-mcp-provider.js +1 -0
  20. package/dist/mcp/tambo-mcp-provider.js.map +1 -1
  21. package/dist/model/tambo-interactable.d.ts +1 -1
  22. package/dist/model/tambo-interactable.d.ts.map +1 -1
  23. package/dist/model/tambo-interactable.js.map +1 -1
  24. package/dist/providers/__tests__/tambo-interactable-provider-partial-updates.test.d.ts +2 -0
  25. package/dist/providers/__tests__/tambo-interactable-provider-partial-updates.test.d.ts.map +1 -0
  26. package/dist/providers/__tests__/tambo-interactable-provider-partial-updates.test.js +677 -0
  27. package/dist/providers/__tests__/tambo-interactable-provider-partial-updates.test.js.map +1 -0
  28. package/dist/providers/__tests__/tambo-stubs.test.js +6 -2
  29. package/dist/providers/__tests__/tambo-stubs.test.js.map +1 -1
  30. package/dist/providers/__tests__/tambo-thread-provider.test.js +14 -14
  31. package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  32. package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
  33. package/dist/providers/tambo-interactable-provider.js +26 -24
  34. package/dist/providers/tambo-interactable-provider.js.map +1 -1
  35. package/dist/providers/tambo-provider.d.ts +1 -0
  36. package/dist/providers/tambo-provider.d.ts.map +1 -1
  37. package/dist/providers/tambo-provider.js +1 -0
  38. package/dist/providers/tambo-provider.js.map +1 -1
  39. package/dist/providers/tambo-thread-provider.js +2 -2
  40. package/dist/providers/tambo-thread-provider.js.map +1 -1
  41. package/dist/setupTests.d.ts +0 -1
  42. package/dist/setupTests.d.ts.map +1 -1
  43. package/dist/setupTests.js +0 -1
  44. package/dist/setupTests.js.map +1 -1
  45. package/esm/hooks/__tests__/use-component-state.test.js +4 -1
  46. package/esm/hooks/__tests__/use-component-state.test.js.map +1 -1
  47. package/esm/hooks/__tests__/use-tambo-threads.test.js +1 -1
  48. package/esm/hooks/__tests__/use-tambo-threads.test.js.map +1 -1
  49. package/esm/hooks/use-component-state.js +3 -3
  50. package/esm/hooks/use-component-state.js.map +1 -1
  51. package/esm/hooks/use-suggestions.js +4 -2
  52. package/esm/hooks/use-suggestions.js.map +1 -1
  53. package/esm/mcp/__tests__/mcp-client.test.d.ts +2 -0
  54. package/esm/mcp/__tests__/mcp-client.test.d.ts.map +1 -0
  55. package/esm/mcp/__tests__/mcp-client.test.js +500 -0
  56. package/esm/mcp/__tests__/mcp-client.test.js.map +1 -0
  57. package/esm/mcp/mcp-client.d.ts +77 -3
  58. package/esm/mcp/mcp-client.d.ts.map +1 -1
  59. package/esm/mcp/mcp-client.js +184 -19
  60. package/esm/mcp/mcp-client.js.map +1 -1
  61. package/esm/mcp/tambo-mcp-provider.d.ts +1 -0
  62. package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
  63. package/esm/mcp/tambo-mcp-provider.js +1 -0
  64. package/esm/mcp/tambo-mcp-provider.js.map +1 -1
  65. package/esm/model/tambo-interactable.d.ts +1 -1
  66. package/esm/model/tambo-interactable.d.ts.map +1 -1
  67. package/esm/model/tambo-interactable.js.map +1 -1
  68. package/esm/providers/__tests__/tambo-interactable-provider-partial-updates.test.d.ts +2 -0
  69. package/esm/providers/__tests__/tambo-interactable-provider-partial-updates.test.d.ts.map +1 -0
  70. package/esm/providers/__tests__/tambo-interactable-provider-partial-updates.test.js +672 -0
  71. package/esm/providers/__tests__/tambo-interactable-provider-partial-updates.test.js.map +1 -0
  72. package/esm/providers/__tests__/tambo-stubs.test.js +6 -2
  73. package/esm/providers/__tests__/tambo-stubs.test.js.map +1 -1
  74. package/esm/providers/__tests__/tambo-thread-provider.test.js +14 -14
  75. package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  76. package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
  77. package/esm/providers/tambo-interactable-provider.js +26 -24
  78. package/esm/providers/tambo-interactable-provider.js.map +1 -1
  79. package/esm/providers/tambo-provider.d.ts +1 -0
  80. package/esm/providers/tambo-provider.d.ts.map +1 -1
  81. package/esm/providers/tambo-provider.js +1 -0
  82. package/esm/providers/tambo-provider.js.map +1 -1
  83. package/esm/providers/tambo-thread-provider.js +2 -2
  84. package/esm/providers/tambo-thread-provider.js.map +1 -1
  85. package/esm/setupTests.d.ts +0 -1
  86. package/esm/setupTests.d.ts.map +1 -1
  87. package/esm/setupTests.js +0 -1
  88. package/esm/setupTests.js.map +1 -1
  89. package/package.json +7 -7
@@ -0,0 +1,672 @@
1
+ import { act, renderHook } from "@testing-library/react";
2
+ import React from "react";
3
+ import { z } from "zod";
4
+ import { TamboInteractableProvider, useTamboInteractable, } from "../tambo-interactable-provider";
5
+ // Mock the context helpers
6
+ const mockAddContextHelper = jest.fn();
7
+ const mockRemoveContextHelper = jest.fn();
8
+ jest.mock("../tambo-context-helpers-provider", () => ({
9
+ TamboContextHelpersProvider: ({ children, }) => React.createElement(React.Fragment, null, children),
10
+ useTamboContextHelpers: () => ({
11
+ addContextHelper: mockAddContextHelper,
12
+ removeContextHelper: mockRemoveContextHelper,
13
+ }),
14
+ }));
15
+ // Mock the component provider
16
+ const mockRegisterTool = jest.fn();
17
+ jest.mock("../tambo-component-provider", () => ({
18
+ useTamboComponent: () => ({
19
+ registerTool: mockRegisterTool,
20
+ }),
21
+ }));
22
+ // Mock the context helper creation
23
+ jest.mock("../../context-helpers/current-interactables-context-helper", () => ({
24
+ createInteractablesContextHelper: () => jest.fn(() => ({
25
+ name: "interactables",
26
+ context: {
27
+ description: "Test interactables context",
28
+ components: [],
29
+ },
30
+ })),
31
+ }));
32
+ describe("updateInteractableComponentProps - Partial Updates", () => {
33
+ beforeEach(() => {
34
+ jest.clearAllMocks();
35
+ });
36
+ const wrapper = ({ children }) => (React.createElement(TamboInteractableProvider, null, children));
37
+ describe("Partial Updates Functionality", () => {
38
+ it("should apply partial updates to existing props", () => {
39
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
40
+ const component = {
41
+ name: "TestComponent",
42
+ description: "A test component",
43
+ component: () => React.createElement("div", null, "Test"),
44
+ props: {
45
+ title: "Original Title",
46
+ count: 0,
47
+ active: true,
48
+ metadata: { type: "test", version: "1.0" },
49
+ },
50
+ propsSchema: z.object({
51
+ title: z.string(),
52
+ count: z.number(),
53
+ active: z.boolean(),
54
+ metadata: z.object({
55
+ type: z.string(),
56
+ version: z.string(),
57
+ }),
58
+ }),
59
+ };
60
+ let componentId = "";
61
+ act(() => {
62
+ componentId = result.current.addInteractableComponent(component);
63
+ });
64
+ // Verify initial state
65
+ expect(result.current.interactableComponents[0].props).toEqual({
66
+ title: "Original Title",
67
+ count: 0,
68
+ active: true,
69
+ metadata: { type: "test", version: "1.0" },
70
+ });
71
+ // Apply partial update - only change count
72
+ let updateResult = "";
73
+ act(() => {
74
+ updateResult = result.current.updateInteractableComponentProps(componentId, {
75
+ count: 5,
76
+ });
77
+ });
78
+ expect(updateResult).toBe("Updated successfully");
79
+ expect(result.current.interactableComponents[0].props).toEqual({
80
+ title: "Original Title", // unchanged
81
+ count: 5, // updated
82
+ active: true, // unchanged
83
+ metadata: { type: "test", version: "1.0" }, // unchanged
84
+ });
85
+ });
86
+ it("should apply multiple partial updates in sequence", () => {
87
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
88
+ const component = {
89
+ name: "TestComponent",
90
+ description: "A test component",
91
+ component: () => React.createElement("div", null, "Test"),
92
+ props: {
93
+ title: "Original Title",
94
+ count: 0,
95
+ active: true,
96
+ description: "Original description",
97
+ },
98
+ propsSchema: z.object({
99
+ title: z.string(),
100
+ count: z.number(),
101
+ active: z.boolean(),
102
+ description: z.string(),
103
+ }),
104
+ };
105
+ let componentId = "";
106
+ act(() => {
107
+ componentId = result.current.addInteractableComponent(component);
108
+ });
109
+ // First partial update - change title and count
110
+ act(() => {
111
+ result.current.updateInteractableComponentProps(componentId, {
112
+ title: "Updated Title",
113
+ count: 10,
114
+ });
115
+ });
116
+ expect(result.current.interactableComponents[0].props).toEqual({
117
+ title: "Updated Title",
118
+ count: 10,
119
+ active: true,
120
+ description: "Original description",
121
+ });
122
+ // Second partial update - change active and description
123
+ act(() => {
124
+ result.current.updateInteractableComponentProps(componentId, {
125
+ active: false,
126
+ description: "Updated description",
127
+ });
128
+ });
129
+ expect(result.current.interactableComponents[0].props).toEqual({
130
+ title: "Updated Title", // from previous update
131
+ count: 10, // from previous update
132
+ active: false, // new update
133
+ description: "Updated description", // new update
134
+ });
135
+ });
136
+ it("should handle nested object partial updates (shallow merge behavior)", () => {
137
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
138
+ const component = {
139
+ name: "TestComponent",
140
+ description: "A test component",
141
+ component: () => React.createElement("div", null, "Test"),
142
+ props: {
143
+ title: "Original Title",
144
+ config: {
145
+ theme: "light",
146
+ language: "en",
147
+ features: {
148
+ notifications: true,
149
+ analytics: false,
150
+ },
151
+ },
152
+ },
153
+ propsSchema: z.object({
154
+ title: z.string(),
155
+ config: z.object({
156
+ theme: z.string(),
157
+ language: z.string(),
158
+ features: z.object({
159
+ notifications: z.boolean(),
160
+ analytics: z.boolean(),
161
+ }),
162
+ }),
163
+ }),
164
+ };
165
+ let componentId = "";
166
+ act(() => {
167
+ componentId = result.current.addInteractableComponent(component);
168
+ });
169
+ // Partial update - replace entire config object (shallow merge behavior)
170
+ act(() => {
171
+ result.current.updateInteractableComponentProps(componentId, {
172
+ config: {
173
+ theme: "dark",
174
+ // Note: language and features are not provided, so they will be undefined
175
+ // This demonstrates the shallow merge behavior
176
+ },
177
+ });
178
+ });
179
+ expect(result.current.interactableComponents[0].props).toEqual({
180
+ title: "Original Title",
181
+ config: {
182
+ theme: "dark", // updated
183
+ // language and features are now undefined due to shallow merge
184
+ },
185
+ });
186
+ });
187
+ it("should handle array partial updates", () => {
188
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
189
+ const component = {
190
+ name: "TestComponent",
191
+ description: "A test component",
192
+ component: () => React.createElement("div", null, "Test"),
193
+ props: {
194
+ title: "Original Title",
195
+ items: ["item1", "item2", "item3"],
196
+ tags: ["tag1", "tag2"],
197
+ },
198
+ propsSchema: z.object({
199
+ title: z.string(),
200
+ items: z.array(z.string()),
201
+ tags: z.array(z.string()),
202
+ }),
203
+ };
204
+ let componentId = "";
205
+ act(() => {
206
+ componentId = result.current.addInteractableComponent(component);
207
+ });
208
+ // Partial update - only change items array
209
+ act(() => {
210
+ result.current.updateInteractableComponentProps(componentId, {
211
+ items: ["newItem1", "newItem2"],
212
+ });
213
+ });
214
+ expect(result.current.interactableComponents[0].props).toEqual({
215
+ title: "Original Title", // unchanged
216
+ items: ["newItem1", "newItem2"], // updated
217
+ tags: ["tag1", "tag2"], // unchanged
218
+ });
219
+ });
220
+ it("should handle null and undefined values in partial updates", () => {
221
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
222
+ const component = {
223
+ name: "TestComponent",
224
+ description: "A test component",
225
+ component: () => React.createElement("div", null, "Test"),
226
+ props: {
227
+ title: "Original Title",
228
+ count: 5,
229
+ description: "Original description",
230
+ },
231
+ propsSchema: z.object({
232
+ title: z.string(),
233
+ count: z.number(),
234
+ description: z.string(),
235
+ }),
236
+ };
237
+ let componentId = "";
238
+ act(() => {
239
+ componentId = result.current.addInteractableComponent(component);
240
+ });
241
+ // Partial update with null value
242
+ act(() => {
243
+ result.current.updateInteractableComponentProps(componentId, {
244
+ description: null,
245
+ });
246
+ });
247
+ expect(result.current.interactableComponents[0].props).toEqual({
248
+ title: "Original Title",
249
+ count: 5,
250
+ description: null,
251
+ });
252
+ // Partial update with undefined value
253
+ act(() => {
254
+ result.current.updateInteractableComponentProps(componentId, {
255
+ count: undefined,
256
+ });
257
+ });
258
+ expect(result.current.interactableComponents[0].props).toEqual({
259
+ title: "Original Title",
260
+ count: undefined,
261
+ description: null,
262
+ });
263
+ });
264
+ });
265
+ describe("Error Handling", () => {
266
+ it("should return error for non-existent component", () => {
267
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
268
+ let updateResult = "";
269
+ act(() => {
270
+ updateResult = result.current.updateInteractableComponentProps("non-existent", {
271
+ title: "New Title",
272
+ });
273
+ });
274
+ expect(updateResult).toBe("Updated successfully");
275
+ });
276
+ it("should return warning for empty props object", () => {
277
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
278
+ const component = {
279
+ name: "TestComponent",
280
+ description: "A test component",
281
+ component: () => React.createElement("div", null, "Test"),
282
+ props: { title: "Original Title" },
283
+ propsSchema: z.object({ title: z.string() }),
284
+ };
285
+ let componentId = "";
286
+ act(() => {
287
+ componentId = result.current.addInteractableComponent(component);
288
+ });
289
+ let updateResult = "";
290
+ act(() => {
291
+ updateResult = result.current.updateInteractableComponentProps(componentId, {});
292
+ });
293
+ expect(updateResult).toBe(`Warning: No props provided for component with ID ${componentId}.`);
294
+ });
295
+ it("should return warning for null props", () => {
296
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
297
+ const component = {
298
+ name: "TestComponent",
299
+ description: "A test component",
300
+ component: () => React.createElement("div", null, "Test"),
301
+ props: { title: "Original Title" },
302
+ propsSchema: z.object({ title: z.string() }),
303
+ };
304
+ let componentId = "";
305
+ act(() => {
306
+ componentId = result.current.addInteractableComponent(component);
307
+ });
308
+ let updateResult = "";
309
+ act(() => {
310
+ updateResult = result.current.updateInteractableComponentProps(componentId, null);
311
+ });
312
+ expect(updateResult).toBe(`Warning: No props provided for component with ID ${componentId}.`);
313
+ });
314
+ it("should return warning for undefined props", () => {
315
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
316
+ const component = {
317
+ name: "TestComponent",
318
+ description: "A test component",
319
+ component: () => React.createElement("div", null, "Test"),
320
+ props: { title: "Original Title" },
321
+ propsSchema: z.object({ title: z.string() }),
322
+ };
323
+ let componentId = "";
324
+ act(() => {
325
+ componentId = result.current.addInteractableComponent(component);
326
+ });
327
+ let updateResult = "";
328
+ act(() => {
329
+ updateResult = result.current.updateInteractableComponentProps(componentId, undefined);
330
+ });
331
+ expect(updateResult).toBe(`Warning: No props provided for component with ID ${componentId}.`);
332
+ });
333
+ });
334
+ describe("Performance and Efficiency", () => {
335
+ it("should only update changed properties without affecting others", () => {
336
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
337
+ const component = {
338
+ name: "TestComponent",
339
+ description: "A test component",
340
+ component: () => React.createElement("div", null, "Test"),
341
+ props: {
342
+ title: "Original Title",
343
+ count: 0,
344
+ active: true,
345
+ metadata: { type: "test", version: "1.0" },
346
+ items: ["item1", "item2"],
347
+ },
348
+ propsSchema: z.object({
349
+ title: z.string(),
350
+ count: z.number(),
351
+ active: z.boolean(),
352
+ metadata: z.object({
353
+ type: z.string(),
354
+ version: z.string(),
355
+ }),
356
+ items: z.array(z.string()),
357
+ }),
358
+ };
359
+ let componentId = "";
360
+ act(() => {
361
+ componentId = result.current.addInteractableComponent(component);
362
+ });
363
+ const originalProps = result.current.interactableComponents[0].props;
364
+ const originalMetadata = originalProps.metadata;
365
+ const originalItems = originalProps.items;
366
+ // Apply minimal partial update - only change count
367
+ act(() => {
368
+ result.current.updateInteractableComponentProps(componentId, {
369
+ count: 1,
370
+ });
371
+ });
372
+ const updatedProps = result.current.interactableComponents[0].props;
373
+ // Verify that only count changed
374
+ expect(updatedProps.count).toBe(1);
375
+ expect(updatedProps.title).toBe("Original Title");
376
+ expect(updatedProps.active).toBe(true);
377
+ // Verify that nested objects are preserved (same reference for efficiency)
378
+ expect(updatedProps.metadata).toBe(originalMetadata);
379
+ expect(updatedProps.items).toBe(originalItems);
380
+ });
381
+ it("should handle large objects efficiently with partial updates", () => {
382
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
383
+ // Create a component with a large initial state
384
+ const largeData = {
385
+ users: Array.from({ length: 1000 }, (_, i) => ({
386
+ id: i,
387
+ name: `User ${i}`,
388
+ })),
389
+ settings: {
390
+ theme: "light",
391
+ language: "en",
392
+ notifications: true,
393
+ privacy: {
394
+ shareData: false,
395
+ analytics: true,
396
+ marketing: false,
397
+ },
398
+ },
399
+ metadata: {
400
+ version: "1.0.0",
401
+ build: "12345",
402
+ timestamp: Date.now(),
403
+ },
404
+ };
405
+ const component = {
406
+ name: "TestComponent",
407
+ description: "A test component with large data",
408
+ component: () => React.createElement("div", null, "Test"),
409
+ props: largeData,
410
+ propsSchema: z.object({
411
+ users: z.array(z.object({
412
+ id: z.number(),
413
+ name: z.string(),
414
+ })),
415
+ settings: z.object({
416
+ theme: z.string(),
417
+ language: z.string(),
418
+ notifications: z.boolean(),
419
+ privacy: z.object({
420
+ shareData: z.boolean(),
421
+ analytics: z.boolean(),
422
+ marketing: z.boolean(),
423
+ }),
424
+ }),
425
+ metadata: z.object({
426
+ version: z.string(),
427
+ build: z.string(),
428
+ timestamp: z.number(),
429
+ }),
430
+ }),
431
+ };
432
+ let componentId = "";
433
+ act(() => {
434
+ componentId = result.current.addInteractableComponent(component);
435
+ });
436
+ const originalProps = result.current.interactableComponents[0].props;
437
+ const originalUsers = originalProps.users;
438
+ // Apply a small partial update - only change theme (shallow merge behavior)
439
+ act(() => {
440
+ result.current.updateInteractableComponentProps(componentId, {
441
+ settings: {
442
+ theme: "dark",
443
+ // Note: other settings properties are not provided, so they will be undefined
444
+ // This demonstrates the shallow merge behavior
445
+ },
446
+ });
447
+ });
448
+ const updatedProps = result.current.interactableComponents[0].props;
449
+ // Verify the update worked
450
+ expect(updatedProps.settings.theme).toBe("dark");
451
+ // Due to shallow merge, other properties are now undefined
452
+ expect(updatedProps.settings.language).toBeUndefined();
453
+ expect(updatedProps.settings.notifications).toBeUndefined();
454
+ // Verify that large arrays are preserved (same reference for efficiency)
455
+ expect(updatedProps.users).toBe(originalUsers);
456
+ // Verify that metadata is preserved (not updated)
457
+ expect(updatedProps.metadata).toBe(originalProps.metadata);
458
+ });
459
+ });
460
+ describe("Edge Cases", () => {
461
+ it("should handle updating a property that doesn't exist in original props", () => {
462
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
463
+ const component = {
464
+ name: "TestComponent",
465
+ description: "A test component",
466
+ component: () => React.createElement("div", null, "Test"),
467
+ props: {
468
+ title: "Original Title",
469
+ count: 0,
470
+ },
471
+ propsSchema: z.object({
472
+ title: z.string(),
473
+ count: z.number(),
474
+ newProperty: z.string().optional(),
475
+ }),
476
+ };
477
+ let componentId = "";
478
+ act(() => {
479
+ componentId = result.current.addInteractableComponent(component);
480
+ });
481
+ // Add a new property that wasn't in the original props
482
+ act(() => {
483
+ result.current.updateInteractableComponentProps(componentId, {
484
+ newProperty: "New Value",
485
+ });
486
+ });
487
+ expect(result.current.interactableComponents[0].props).toEqual({
488
+ title: "Original Title",
489
+ count: 0,
490
+ newProperty: "New Value",
491
+ });
492
+ });
493
+ it("should handle updating with same values (no-op updates)", () => {
494
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
495
+ const component = {
496
+ name: "TestComponent",
497
+ description: "A test component",
498
+ component: () => React.createElement("div", null, "Test"),
499
+ props: {
500
+ title: "Original Title",
501
+ count: 5,
502
+ active: true,
503
+ },
504
+ propsSchema: z.object({
505
+ title: z.string(),
506
+ count: z.number(),
507
+ active: z.boolean(),
508
+ }),
509
+ };
510
+ let componentId = "";
511
+ act(() => {
512
+ componentId = result.current.addInteractableComponent(component);
513
+ });
514
+ const originalProps = result.current.interactableComponents[0].props;
515
+ // Update with the same values
516
+ let updateResult = "";
517
+ act(() => {
518
+ updateResult = result.current.updateInteractableComponentProps(componentId, {
519
+ title: "Original Title",
520
+ count: 5,
521
+ active: true,
522
+ });
523
+ });
524
+ expect(updateResult).toBe("Updated successfully");
525
+ expect(result.current.interactableComponents[0].props).toEqual(originalProps);
526
+ });
527
+ it("should handle proper nested partial updates by providing complete nested structure", () => {
528
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
529
+ const component = {
530
+ name: "TestComponent",
531
+ description: "A test component",
532
+ component: () => React.createElement("div", null, "Test"),
533
+ props: {
534
+ title: "Original Title",
535
+ config: {
536
+ theme: "light",
537
+ language: "en",
538
+ features: {
539
+ notifications: true,
540
+ analytics: false,
541
+ experimental: {
542
+ beta: false,
543
+ alpha: true,
544
+ },
545
+ },
546
+ },
547
+ },
548
+ propsSchema: z.object({
549
+ title: z.string(),
550
+ config: z.object({
551
+ theme: z.string(),
552
+ language: z.string(),
553
+ features: z.object({
554
+ notifications: z.boolean(),
555
+ analytics: z.boolean(),
556
+ experimental: z.object({
557
+ beta: z.boolean(),
558
+ alpha: z.boolean(),
559
+ }),
560
+ }),
561
+ }),
562
+ }),
563
+ };
564
+ let componentId = "";
565
+ act(() => {
566
+ componentId = result.current.addInteractableComponent(component);
567
+ });
568
+ // Proper nested partial update - provide complete nested structure
569
+ act(() => {
570
+ result.current.updateInteractableComponentProps(componentId, {
571
+ config: {
572
+ theme: "light", // keep original
573
+ language: "en", // keep original
574
+ features: {
575
+ notifications: true, // keep original
576
+ analytics: false, // keep original
577
+ experimental: {
578
+ beta: true, // update this
579
+ alpha: true, // keep original
580
+ },
581
+ },
582
+ },
583
+ });
584
+ });
585
+ expect(result.current.interactableComponents[0].props).toEqual({
586
+ title: "Original Title",
587
+ config: {
588
+ theme: "light", // unchanged
589
+ language: "en", // unchanged
590
+ features: {
591
+ notifications: true, // unchanged
592
+ analytics: false, // unchanged
593
+ experimental: {
594
+ beta: true, // updated
595
+ alpha: true, // unchanged
596
+ },
597
+ },
598
+ },
599
+ });
600
+ });
601
+ it("should handle complex nested partial updates (shallow merge behavior)", () => {
602
+ const { result } = renderHook(() => useTamboInteractable(), { wrapper });
603
+ const component = {
604
+ name: "TestComponent",
605
+ description: "A test component",
606
+ component: () => React.createElement("div", null, "Test"),
607
+ props: {
608
+ title: "Original Title",
609
+ config: {
610
+ theme: "light",
611
+ language: "en",
612
+ features: {
613
+ notifications: true,
614
+ analytics: false,
615
+ experimental: {
616
+ beta: false,
617
+ alpha: true,
618
+ },
619
+ },
620
+ },
621
+ },
622
+ propsSchema: z.object({
623
+ title: z.string(),
624
+ config: z.object({
625
+ theme: z.string(),
626
+ language: z.string(),
627
+ features: z.object({
628
+ notifications: z.boolean(),
629
+ analytics: z.boolean(),
630
+ experimental: z.object({
631
+ beta: z.boolean(),
632
+ alpha: z.boolean(),
633
+ }),
634
+ }),
635
+ }),
636
+ }),
637
+ };
638
+ let componentId = "";
639
+ act(() => {
640
+ componentId = result.current.addInteractableComponent(component);
641
+ });
642
+ // Deep nested partial update - only change beta flag (shallow merge behavior)
643
+ act(() => {
644
+ result.current.updateInteractableComponentProps(componentId, {
645
+ config: {
646
+ features: {
647
+ experimental: {
648
+ beta: true,
649
+ // Note: alpha is not provided, so it will be undefined
650
+ // This demonstrates the shallow merge behavior
651
+ },
652
+ },
653
+ },
654
+ });
655
+ });
656
+ expect(result.current.interactableComponents[0].props).toEqual({
657
+ title: "Original Title",
658
+ config: {
659
+ // theme and language are now undefined due to shallow merge
660
+ features: {
661
+ // notifications and analytics are now undefined due to shallow merge
662
+ experimental: {
663
+ beta: true, // updated
664
+ // alpha is now undefined due to shallow merge
665
+ },
666
+ },
667
+ },
668
+ });
669
+ });
670
+ });
671
+ });
672
+ //# sourceMappingURL=tambo-interactable-provider-partial-updates.test.js.map