@tambo-ai/react 0.39.0 → 0.41.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 (85) hide show
  1. package/dist/hooks/use-component-state.js +0 -1
  2. package/dist/hooks/use-component-state.js.map +1 -1
  3. package/dist/mcp/mcp-client.d.ts +15 -15
  4. package/dist/providers/__tests__/tambo-prop-stream-provider.test.js +144 -224
  5. package/dist/providers/__tests__/tambo-prop-stream-provider.test.js.map +1 -1
  6. package/dist/providers/__tests__/tambo-thread-provider.test.js +7 -7
  7. package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  8. package/dist/providers/index.d.ts +2 -2
  9. package/dist/providers/index.d.ts.map +1 -1
  10. package/dist/providers/index.js +4 -1
  11. package/dist/providers/index.js.map +1 -1
  12. package/dist/providers/tambo-prop-stream-provider/index.d.ts +19 -0
  13. package/dist/providers/tambo-prop-stream-provider/index.d.ts.map +1 -0
  14. package/dist/providers/tambo-prop-stream-provider/index.js +43 -0
  15. package/dist/providers/tambo-prop-stream-provider/index.js.map +1 -0
  16. package/dist/providers/tambo-prop-stream-provider/pending.d.ts +12 -0
  17. package/dist/providers/tambo-prop-stream-provider/pending.d.ts.map +1 -0
  18. package/dist/providers/tambo-prop-stream-provider/pending.js +31 -0
  19. package/dist/providers/tambo-prop-stream-provider/pending.js.map +1 -0
  20. package/dist/providers/tambo-prop-stream-provider/provider.d.ts +17 -0
  21. package/dist/providers/tambo-prop-stream-provider/provider.d.ts.map +1 -0
  22. package/dist/providers/tambo-prop-stream-provider/provider.js +107 -0
  23. package/dist/providers/tambo-prop-stream-provider/provider.js.map +1 -0
  24. package/dist/providers/tambo-prop-stream-provider/streaming.d.ts +12 -0
  25. package/dist/providers/tambo-prop-stream-provider/streaming.d.ts.map +1 -0
  26. package/dist/providers/tambo-prop-stream-provider/streaming.js +28 -0
  27. package/dist/providers/tambo-prop-stream-provider/streaming.js.map +1 -0
  28. package/dist/providers/tambo-prop-stream-provider/success.d.ts +12 -0
  29. package/dist/providers/tambo-prop-stream-provider/success.d.ts.map +1 -0
  30. package/dist/providers/tambo-prop-stream-provider/success.js +28 -0
  31. package/dist/providers/tambo-prop-stream-provider/success.js.map +1 -0
  32. package/dist/providers/tambo-prop-stream-provider/types.d.ts +25 -0
  33. package/dist/providers/tambo-prop-stream-provider/types.d.ts.map +1 -0
  34. package/dist/providers/tambo-prop-stream-provider/types.js +6 -0
  35. package/dist/providers/tambo-prop-stream-provider/types.js.map +1 -0
  36. package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
  37. package/dist/providers/tambo-thread-provider.js +3 -9
  38. package/dist/providers/tambo-thread-provider.js.map +1 -1
  39. package/esm/hooks/use-component-state.js +0 -1
  40. package/esm/hooks/use-component-state.js.map +1 -1
  41. package/esm/mcp/mcp-client.d.ts +15 -15
  42. package/esm/providers/__tests__/tambo-prop-stream-provider.test.js +144 -224
  43. package/esm/providers/__tests__/tambo-prop-stream-provider.test.js.map +1 -1
  44. package/esm/providers/__tests__/tambo-thread-provider.test.js +7 -7
  45. package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  46. package/esm/providers/index.d.ts +2 -2
  47. package/esm/providers/index.d.ts.map +1 -1
  48. package/esm/providers/index.js +1 -1
  49. package/esm/providers/index.js.map +1 -1
  50. package/esm/providers/tambo-prop-stream-provider/index.d.ts +19 -0
  51. package/esm/providers/tambo-prop-stream-provider/index.d.ts.map +1 -0
  52. package/esm/providers/tambo-prop-stream-provider/index.js +22 -0
  53. package/esm/providers/tambo-prop-stream-provider/index.js.map +1 -0
  54. package/esm/providers/tambo-prop-stream-provider/pending.d.ts +12 -0
  55. package/esm/providers/tambo-prop-stream-provider/pending.d.ts.map +1 -0
  56. package/esm/providers/tambo-prop-stream-provider/pending.js +24 -0
  57. package/esm/providers/tambo-prop-stream-provider/pending.js.map +1 -0
  58. package/esm/providers/tambo-prop-stream-provider/provider.d.ts +17 -0
  59. package/esm/providers/tambo-prop-stream-provider/provider.d.ts.map +1 -0
  60. package/esm/providers/tambo-prop-stream-provider/provider.js +70 -0
  61. package/esm/providers/tambo-prop-stream-provider/provider.js.map +1 -0
  62. package/esm/providers/tambo-prop-stream-provider/streaming.d.ts +12 -0
  63. package/esm/providers/tambo-prop-stream-provider/streaming.d.ts.map +1 -0
  64. package/esm/providers/tambo-prop-stream-provider/streaming.js +21 -0
  65. package/esm/providers/tambo-prop-stream-provider/streaming.js.map +1 -0
  66. package/esm/providers/tambo-prop-stream-provider/success.d.ts +12 -0
  67. package/esm/providers/tambo-prop-stream-provider/success.d.ts.map +1 -0
  68. package/esm/providers/tambo-prop-stream-provider/success.js +21 -0
  69. package/esm/providers/tambo-prop-stream-provider/success.js.map +1 -0
  70. package/esm/providers/tambo-prop-stream-provider/types.d.ts +25 -0
  71. package/esm/providers/tambo-prop-stream-provider/types.d.ts.map +1 -0
  72. package/esm/providers/tambo-prop-stream-provider/types.js +3 -0
  73. package/esm/providers/tambo-prop-stream-provider/types.js.map +1 -0
  74. package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
  75. package/esm/providers/tambo-thread-provider.js +3 -9
  76. package/esm/providers/tambo-thread-provider.js.map +1 -1
  77. package/package.json +7 -7
  78. package/dist/providers/tambo-prop-stream-provider.d.ts +0 -96
  79. package/dist/providers/tambo-prop-stream-provider.d.ts.map +0 -1
  80. package/dist/providers/tambo-prop-stream-provider.js +0 -185
  81. package/dist/providers/tambo-prop-stream-provider.js.map +0 -1
  82. package/esm/providers/tambo-prop-stream-provider.d.ts +0 -96
  83. package/esm/providers/tambo-prop-stream-provider.d.ts.map +0 -1
  84. package/esm/providers/tambo-prop-stream-provider.js +0 -148
  85. package/esm/providers/tambo-prop-stream-provider.js.map +0 -1
@@ -6,7 +6,6 @@ const react_1 = require("react");
6
6
  const use_debounce_1 = require("use-debounce");
7
7
  const providers_1 = require("../providers");
8
8
  const use_current_message_1 = require("./use-current-message");
9
- // eslint-disable-next-line jsdoc/require-jsdoc
10
9
  function useTamboComponentState(keyName, initialValue, debounceTime = 500) {
11
10
  const { messageId } = (0, use_current_message_1.useTamboMessageContext)();
12
11
  const { updateThreadMessage, thread } = (0, providers_1.useTamboThread)();
@@ -1 +1 @@
1
- {"version":3,"file":"use-component-state.js","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { useDebouncedCallback } from \"use-debounce\";\nimport { useTamboClient, useTamboThread } from \"../providers\";\nimport {\n useTamboCurrentMessage,\n useTamboMessageContext,\n} from \"./use-current-message\";\n// Define metadata interface for better extensibility\ninterface ComponentStateMeta {\n isPending: boolean;\n}\n\ntype StateUpdateResult<T> = [\n currentState: T,\n setState: (newState: T) => void,\n meta: ComponentStateMeta,\n];\n\n/**\n * A React hook that provides state management and passes user updates to Tambo.\n * Benefits: Passes user changes to AI, and when threads are returned, state is preserved.\n * @param keyName - The unique key to identify this state within the message's componentState object\n * @param initialValue - Optional initial value for the state, used if no value exists in the message\n * @param debounceTime - Optional debounce time in milliseconds (default: 300ms) to limit API calls\n * @returns A tuple containing:\n * - The current state value\n * - A setter function to update the state (updates UI immediately, debounces server sync)\n * - A metadata object with properties like isPending to track sync status\n * @example\n * // Basic usage\n * const [count, setCount, { isPending }] = useTamboComponentState(\"counter\", 0);\n *\n * // Usage with object state\n * const [formState, setFormState] = useTamboComponentState(\"myForm\", {\n * name: \"\",\n * email: \"\",\n * message: \"\"\n * });\n *\n * // Handling form input\n * const handleChange = (e) => {\n * setFormState({\n * ...formState,\n * [e.target.name]: e.target.value\n * });\n * };\n */\nexport function useTamboComponentState<S = undefined>(\n keyName: string,\n initialValue?: S,\n debounceTime?: number,\n): StateUpdateResult<S | undefined>;\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue: S,\n debounceTime?: number,\n): StateUpdateResult<S>;\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue?: S,\n debounceTime = 500,\n): StateUpdateResult<S> {\n const { messageId } = useTamboMessageContext();\n const { updateThreadMessage, thread } = useTamboThread();\n const client = useTamboClient();\n const message = useTamboCurrentMessage();\n const threadId = thread.id;\n\n // Initial value management\n const [cachedInitialValue] = useState(() => initialValue);\n // UI state management\n const [localState, setLocalState] = useState<S | undefined>(\n cachedInitialValue,\n );\n // Synchronization state\n const [isPending, setIsPending] = useState(false);\n // Track the last user-initiated value instead of a simple boolean flag\n const [lastUserValue, setLastUserValue] = useState<S | null>(null);\n const [haveInitialized, setHaveInitialized] = useState(false);\n\n // Determine if we need to initialize state\n const shouldInitialize =\n !haveInitialized &&\n message &&\n cachedInitialValue !== undefined &&\n (!message.componentState || !(keyName in message.componentState));\n\n // Sync local state with message state on initial load and when message changes\n useEffect(() => {\n if (message?.componentState && keyName in message.componentState) {\n const messageState = message.componentState[keyName] as S;\n\n // Only update local state if we haven't had any user changes yet\n if (lastUserValue === null) {\n setLocalState(messageState);\n }\n }\n // Otherwise fall back to initial value if we have one and no user changes\n else if (\n cachedInitialValue !== undefined &&\n !localState &&\n lastUserValue === null\n ) {\n setLocalState(cachedInitialValue);\n }\n }, [\n keyName,\n message?.componentState,\n cachedInitialValue,\n lastUserValue,\n localState,\n ]);\n\n // Create debounced save function for efficient server synchronization\n const debouncedServerWrite = useDebouncedCallback(async (newValue: S) => {\n setIsPending(true);\n try {\n const componentStateUpdate = {\n state: { [keyName]: newValue },\n };\n\n await client.beta.threads.messages.updateComponentState(\n threadId,\n messageId,\n componentStateUpdate,\n );\n } catch (err) {\n console.error(\n `Failed to save component state for key \"${keyName}\":`,\n err,\n );\n } finally {\n setIsPending(false);\n }\n }, debounceTime);\n\n // Initialize state on first render if needed\n const initializeState = useCallback(async () => {\n if (!message) {\n console.warn(\n `Cannot initialize state for missing message ${messageId} with key \"${keyName}\"`,\n );\n return;\n }\n\n try {\n const messageUpdate = {\n ...message,\n componentState: {\n ...message.componentState,\n [keyName]: cachedInitialValue,\n },\n };\n\n const componentStateUpdate = {\n state: { [keyName]: cachedInitialValue },\n };\n\n await Promise.all([\n updateThreadMessage(messageId, messageUpdate, false),\n client.beta.threads.messages.updateComponentState(\n threadId,\n messageId,\n componentStateUpdate,\n ),\n ]);\n } catch (err) {\n console.warn(\n `Failed to initialize component state for key \"${keyName}\":`,\n err,\n );\n }\n }, [\n cachedInitialValue,\n client.beta.threads.messages,\n keyName,\n message,\n messageId,\n threadId,\n updateThreadMessage,\n ]);\n\n // Send initial state when component mounts\n useEffect(() => {\n if (shouldInitialize) {\n initializeState();\n setHaveInitialized(true);\n }\n }, [initializeState, shouldInitialize]);\n\n // setValue function for updating state\n // Updates local state immediately and schedules debounced server sync\n const setValue = useCallback(\n (newValue: S) => {\n // Track this as a user-initiated update\n setLastUserValue(newValue);\n setLocalState(newValue);\n\n // Only trigger server updates if we have a message\n if (message) {\n debouncedServerWrite(newValue);\n const messageUpdate = {\n ...message,\n componentState: {\n ...message.componentState,\n [keyName]: newValue,\n },\n };\n\n updateThreadMessage(messageId, messageUpdate, false);\n } else {\n console.warn(\n `Cannot update server for missing message ${messageId} with key \"${keyName}\"`,\n );\n }\n },\n [message, debouncedServerWrite, keyName, updateThreadMessage, messageId],\n );\n\n // Ensure pending changes are flushed on unmount\n useEffect(() => {\n return () => {\n debouncedServerWrite.flush();\n };\n }, [debouncedServerWrite]);\n\n // Return the local state for immediate UI rendering\n return [localState as S, setValue, { isPending }];\n}\n"]}
1
+ {"version":3,"file":"use-component-state.js","sourceRoot":"","sources":["../../src/hooks/use-component-state.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { useDebouncedCallback } from \"use-debounce\";\nimport { useTamboClient, useTamboThread } from \"../providers\";\nimport {\n useTamboCurrentMessage,\n useTamboMessageContext,\n} from \"./use-current-message\";\n// Define metadata interface for better extensibility\ninterface ComponentStateMeta {\n isPending: boolean;\n}\n\ntype StateUpdateResult<T> = [\n currentState: T,\n setState: (newState: T) => void,\n meta: ComponentStateMeta,\n];\n\n/**\n * A React hook that provides state management and passes user updates to Tambo.\n * Benefits: Passes user changes to AI, and when threads are returned, state is preserved.\n * @param keyName - The unique key to identify this state within the message's componentState object\n * @param initialValue - Optional initial value for the state, used if no value exists in the message\n * @param debounceTime - Optional debounce time in milliseconds (default: 300ms) to limit API calls\n * @returns A tuple containing:\n * - The current state value\n * - A setter function to update the state (updates UI immediately, debounces server sync)\n * - A metadata object with properties like isPending to track sync status\n * @example\n * // Basic usage\n * const [count, setCount, { isPending }] = useTamboComponentState(\"counter\", 0);\n *\n * // Usage with object state\n * const [formState, setFormState] = useTamboComponentState(\"myForm\", {\n * name: \"\",\n * email: \"\",\n * message: \"\"\n * });\n *\n * // Handling form input\n * const handleChange = (e) => {\n * setFormState({\n * ...formState,\n * [e.target.name]: e.target.value\n * });\n * };\n */\nexport function useTamboComponentState<S = undefined>(\n keyName: string,\n initialValue?: S,\n debounceTime?: number,\n): StateUpdateResult<S | undefined>;\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue: S,\n debounceTime?: number,\n): StateUpdateResult<S>;\n\nexport function useTamboComponentState<S>(\n keyName: string,\n initialValue?: S,\n debounceTime = 500,\n): StateUpdateResult<S> {\n const { messageId } = useTamboMessageContext();\n const { updateThreadMessage, thread } = useTamboThread();\n const client = useTamboClient();\n const message = useTamboCurrentMessage();\n const threadId = thread.id;\n\n // Initial value management\n const [cachedInitialValue] = useState(() => initialValue);\n // UI state management\n const [localState, setLocalState] = useState<S | undefined>(\n cachedInitialValue,\n );\n // Synchronization state\n const [isPending, setIsPending] = useState(false);\n // Track the last user-initiated value instead of a simple boolean flag\n const [lastUserValue, setLastUserValue] = useState<S | null>(null);\n const [haveInitialized, setHaveInitialized] = useState(false);\n\n // Determine if we need to initialize state\n const shouldInitialize =\n !haveInitialized &&\n message &&\n cachedInitialValue !== undefined &&\n (!message.componentState || !(keyName in message.componentState));\n\n // Sync local state with message state on initial load and when message changes\n useEffect(() => {\n if (message?.componentState && keyName in message.componentState) {\n const messageState = message.componentState[keyName] as S;\n\n // Only update local state if we haven't had any user changes yet\n if (lastUserValue === null) {\n setLocalState(messageState);\n }\n }\n // Otherwise fall back to initial value if we have one and no user changes\n else if (\n cachedInitialValue !== undefined &&\n !localState &&\n lastUserValue === null\n ) {\n setLocalState(cachedInitialValue);\n }\n }, [\n keyName,\n message?.componentState,\n cachedInitialValue,\n lastUserValue,\n localState,\n ]);\n\n // Create debounced save function for efficient server synchronization\n const debouncedServerWrite = useDebouncedCallback(async (newValue: S) => {\n setIsPending(true);\n try {\n const componentStateUpdate = {\n state: { [keyName]: newValue },\n };\n\n await client.beta.threads.messages.updateComponentState(\n threadId,\n messageId,\n componentStateUpdate,\n );\n } catch (err) {\n console.error(\n `Failed to save component state for key \"${keyName}\":`,\n err,\n );\n } finally {\n setIsPending(false);\n }\n }, debounceTime);\n\n // Initialize state on first render if needed\n const initializeState = useCallback(async () => {\n if (!message) {\n console.warn(\n `Cannot initialize state for missing message ${messageId} with key \"${keyName}\"`,\n );\n return;\n }\n\n try {\n const messageUpdate = {\n ...message,\n componentState: {\n ...message.componentState,\n [keyName]: cachedInitialValue,\n },\n };\n\n const componentStateUpdate = {\n state: { [keyName]: cachedInitialValue },\n };\n\n await Promise.all([\n updateThreadMessage(messageId, messageUpdate, false),\n client.beta.threads.messages.updateComponentState(\n threadId,\n messageId,\n componentStateUpdate,\n ),\n ]);\n } catch (err) {\n console.warn(\n `Failed to initialize component state for key \"${keyName}\":`,\n err,\n );\n }\n }, [\n cachedInitialValue,\n client.beta.threads.messages,\n keyName,\n message,\n messageId,\n threadId,\n updateThreadMessage,\n ]);\n\n // Send initial state when component mounts\n useEffect(() => {\n if (shouldInitialize) {\n initializeState();\n setHaveInitialized(true);\n }\n }, [initializeState, shouldInitialize]);\n\n // setValue function for updating state\n // Updates local state immediately and schedules debounced server sync\n const setValue = useCallback(\n (newValue: S) => {\n // Track this as a user-initiated update\n setLastUserValue(newValue);\n setLocalState(newValue);\n\n // Only trigger server updates if we have a message\n if (message) {\n debouncedServerWrite(newValue);\n const messageUpdate = {\n ...message,\n componentState: {\n ...message.componentState,\n [keyName]: newValue,\n },\n };\n\n updateThreadMessage(messageId, messageUpdate, false);\n } else {\n console.warn(\n `Cannot update server for missing message ${messageId} with key \"${keyName}\"`,\n );\n }\n },\n [message, debouncedServerWrite, keyName, updateThreadMessage, messageId],\n );\n\n // Ensure pending changes are flushed on unmount\n useEffect(() => {\n return () => {\n debouncedServerWrite.flush();\n };\n }, [debouncedServerWrite]);\n\n // Return the local state for immediate UI rendering\n return [localState as S, setValue, { isPending }];\n}\n"]}
@@ -65,32 +65,32 @@ export declare class MCPClient {
65
65
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
66
66
  }, import("zod").ZodTypeAny, "passthrough">>, import("zod").ZodObject<{
67
67
  type: import("zod").ZodLiteral<"image">;
68
- data: import("zod").ZodString;
68
+ data: import("zod").ZodEffects<import("zod").ZodString, string, string>;
69
69
  mimeType: import("zod").ZodString;
70
70
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
71
71
  }, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{
72
72
  type: import("zod").ZodLiteral<"image">;
73
- data: import("zod").ZodString;
73
+ data: import("zod").ZodEffects<import("zod").ZodString, string, string>;
74
74
  mimeType: import("zod").ZodString;
75
75
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
76
76
  }, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{
77
77
  type: import("zod").ZodLiteral<"image">;
78
- data: import("zod").ZodString;
78
+ data: import("zod").ZodEffects<import("zod").ZodString, string, string>;
79
79
  mimeType: import("zod").ZodString;
80
80
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
81
81
  }, import("zod").ZodTypeAny, "passthrough">>, import("zod").ZodObject<{
82
82
  type: import("zod").ZodLiteral<"audio">;
83
- data: import("zod").ZodString;
83
+ data: import("zod").ZodEffects<import("zod").ZodString, string, string>;
84
84
  mimeType: import("zod").ZodString;
85
85
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
86
86
  }, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{
87
87
  type: import("zod").ZodLiteral<"audio">;
88
- data: import("zod").ZodString;
88
+ data: import("zod").ZodEffects<import("zod").ZodString, string, string>;
89
89
  mimeType: import("zod").ZodString;
90
90
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
91
91
  }, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{
92
92
  type: import("zod").ZodLiteral<"audio">;
93
- data: import("zod").ZodString;
93
+ data: import("zod").ZodEffects<import("zod").ZodString, string, string>;
94
94
  mimeType: import("zod").ZodString;
95
95
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
96
96
  }, import("zod").ZodTypeAny, "passthrough">>, import("zod").ZodObject<import("zod").objectUtil.extendShape<import("zod").objectUtil.extendShape<{
@@ -148,19 +148,19 @@ export declare class MCPClient {
148
148
  mimeType: import("zod").ZodOptional<import("zod").ZodString>;
149
149
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
150
150
  }, {
151
- blob: import("zod").ZodString;
151
+ blob: import("zod").ZodEffects<import("zod").ZodString, string, string>;
152
152
  }>, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
153
153
  uri: import("zod").ZodString;
154
154
  mimeType: import("zod").ZodOptional<import("zod").ZodString>;
155
155
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
156
156
  }, {
157
- blob: import("zod").ZodString;
157
+ blob: import("zod").ZodEffects<import("zod").ZodString, string, string>;
158
158
  }>, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<import("zod").objectUtil.extendShape<{
159
159
  uri: import("zod").ZodString;
160
160
  mimeType: import("zod").ZodOptional<import("zod").ZodString>;
161
161
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
162
162
  }, {
163
- blob: import("zod").ZodString;
163
+ blob: import("zod").ZodEffects<import("zod").ZodString, string, string>;
164
164
  }>, import("zod").ZodTypeAny, "passthrough">>]>;
165
165
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
166
166
  }, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{
@@ -188,19 +188,19 @@ export declare class MCPClient {
188
188
  mimeType: import("zod").ZodOptional<import("zod").ZodString>;
189
189
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
190
190
  }, {
191
- blob: import("zod").ZodString;
191
+ blob: import("zod").ZodEffects<import("zod").ZodString, string, string>;
192
192
  }>, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
193
193
  uri: import("zod").ZodString;
194
194
  mimeType: import("zod").ZodOptional<import("zod").ZodString>;
195
195
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
196
196
  }, {
197
- blob: import("zod").ZodString;
197
+ blob: import("zod").ZodEffects<import("zod").ZodString, string, string>;
198
198
  }>, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<import("zod").objectUtil.extendShape<{
199
199
  uri: import("zod").ZodString;
200
200
  mimeType: import("zod").ZodOptional<import("zod").ZodString>;
201
201
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
202
202
  }, {
203
- blob: import("zod").ZodString;
203
+ blob: import("zod").ZodEffects<import("zod").ZodString, string, string>;
204
204
  }>, import("zod").ZodTypeAny, "passthrough">>]>;
205
205
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
206
206
  }, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{
@@ -228,19 +228,19 @@ export declare class MCPClient {
228
228
  mimeType: import("zod").ZodOptional<import("zod").ZodString>;
229
229
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
230
230
  }, {
231
- blob: import("zod").ZodString;
231
+ blob: import("zod").ZodEffects<import("zod").ZodString, string, string>;
232
232
  }>, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<import("zod").objectUtil.extendShape<{
233
233
  uri: import("zod").ZodString;
234
234
  mimeType: import("zod").ZodOptional<import("zod").ZodString>;
235
235
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
236
236
  }, {
237
- blob: import("zod").ZodString;
237
+ blob: import("zod").ZodEffects<import("zod").ZodString, string, string>;
238
238
  }>, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<import("zod").objectUtil.extendShape<{
239
239
  uri: import("zod").ZodString;
240
240
  mimeType: import("zod").ZodOptional<import("zod").ZodString>;
241
241
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
242
242
  }, {
243
- blob: import("zod").ZodString;
243
+ blob: import("zod").ZodEffects<import("zod").ZodString, string, string>;
244
244
  }>, import("zod").ZodTypeAny, "passthrough">>]>;
245
245
  _meta: import("zod").ZodOptional<import("zod").ZodObject<{}, "passthrough", import("zod").ZodTypeAny, import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">, import("zod").objectInputType<{}, import("zod").ZodTypeAny, "passthrough">>>;
246
246
  }, import("zod").ZodTypeAny, "passthrough">>]>, "many">>;
@@ -5,17 +5,63 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = require("@testing-library/react");
7
7
  const react_2 = __importDefault(require("react"));
8
+ const use_current_message_1 = require("../../hooks/use-current-message");
9
+ const generate_component_response_1 = require("../../model/generate-component-response");
10
+ const tambo_thread_provider_1 = require("../../providers/tambo-thread-provider");
8
11
  const tambo_prop_stream_provider_1 = require("../tambo-prop-stream-provider");
12
+ // Mock the required providers
13
+ jest.mock("../../providers/tambo-thread-provider", () => ({
14
+ useTamboThread: jest.fn(),
15
+ }));
16
+ jest.mock("../../hooks/use-current-message", () => ({
17
+ useTamboCurrentMessage: jest.fn(),
18
+ }));
19
+ // Mock window for SSR tests
20
+ const originalWindow = global.window;
21
+ // Helper function to create mock ComponentDecisionV2
22
+ const createMockComponent = (props = {}) => ({
23
+ componentName: "TestComponent",
24
+ componentState: {},
25
+ message: "Component generated",
26
+ props,
27
+ reasoning: "Test reasoning",
28
+ });
29
+ // Helper function to create mock TamboThreadMessage
30
+ const createMockMessage = (overrides = {}) => ({
31
+ id: "test-message",
32
+ componentState: {},
33
+ content: [{ type: "text", text: "test content" }],
34
+ createdAt: new Date().toISOString(),
35
+ role: "assistant",
36
+ threadId: "test-thread",
37
+ ...overrides,
38
+ });
9
39
  // Helper component to test hook usage
10
40
  const TestHookComponent = ({ testKey = "default", }) => {
11
- const { data, streamStatus, getStatusForKey } = (0, tambo_prop_stream_provider_1.useTamboStream)();
41
+ const { streamStatus, getStatusForKey } = (0, tambo_prop_stream_provider_1.useTamboStream)();
12
42
  const status = getStatusForKey(testKey);
13
43
  return (react_2.default.createElement("div", null,
14
- react_2.default.createElement("div", { "data-testid": "data" }, JSON.stringify(data)),
15
44
  react_2.default.createElement("div", { "data-testid": "stream-status" }, JSON.stringify(streamStatus)),
16
45
  react_2.default.createElement("div", { "data-testid": "key-status" }, JSON.stringify(status))));
17
46
  };
18
47
  describe("TamboPropStreamProvider", () => {
48
+ beforeEach(() => {
49
+ // Restore window for client-side tests
50
+ global.window = originalWindow;
51
+ // Default mock implementations
52
+ jest.mocked(tambo_thread_provider_1.useTamboThread).mockReturnValue({
53
+ generationStage: generate_component_response_1.GenerationStage.IDLE,
54
+ });
55
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue({
56
+ id: "test-message",
57
+ component: {
58
+ props: {},
59
+ },
60
+ });
61
+ });
62
+ afterEach(() => {
63
+ jest.clearAllMocks();
64
+ });
19
65
  describe("Hook Error Handling", () => {
20
66
  it("should throw error when useTamboStream is used outside provider", () => {
21
67
  // Suppress console.error for this test
@@ -27,250 +73,124 @@ describe("TamboPropStreamProvider", () => {
27
73
  console.error = originalError;
28
74
  });
29
75
  });
30
- describe("Basic Functionality", () => {
31
- it("should provide data and stream status through context", () => {
32
- const testData = { message: "Hello World" };
33
- const testStreamStatus = {
34
- isPending: false,
35
- isStreaming: false,
36
- isSuccess: true,
37
- isError: false,
38
- streamError: undefined,
39
- };
40
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: testData, streamStatus: testStreamStatus },
41
- react_2.default.createElement(TestHookComponent, null)));
42
- expect(react_1.screen.getByTestId("data")).toHaveTextContent(JSON.stringify(testData));
43
- expect(react_1.screen.getByTestId("stream-status")).toHaveTextContent(JSON.stringify(testStreamStatus));
44
- });
45
- it("should use default stream status when none provided", () => {
46
- const testData = { message: "Hello World" };
47
- const expectedDefaultStatus = {
48
- isPending: false,
49
- isStreaming: false,
50
- isSuccess: true,
51
- isError: false,
52
- streamError: undefined,
53
- };
54
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: testData },
55
- react_2.default.createElement(TestHookComponent, null)));
56
- expect(react_1.screen.getByTestId("stream-status")).toHaveTextContent(JSON.stringify(expectedDefaultStatus));
57
- });
58
- });
59
76
  describe("Compound Components", () => {
60
- describe("Loading Component", () => {
61
- it("should render loading when isPending is true", () => {
62
- const streamStatus = {
63
- isPending: true,
64
- isStreaming: false,
65
- isSuccess: false,
66
- isError: false,
67
- };
68
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: null, streamStatus: streamStatus },
69
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Loading, null,
77
+ describe("Streaming Component", () => {
78
+ it("should render streaming when isPending is true", () => {
79
+ jest.mocked(tambo_thread_provider_1.useTamboThread).mockReturnValue({
80
+ generationStage: generate_component_response_1.GenerationStage.IDLE,
81
+ });
82
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
83
+ component: createMockComponent({ title: "" }),
84
+ }));
85
+ (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, null,
86
+ react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Streaming, null,
70
87
  react_2.default.createElement("div", { "data-testid": "loading" }, "Loading..."))));
71
88
  expect(react_1.screen.getByTestId("loading")).toBeInTheDocument();
72
89
  });
73
- it("should render loading when isStreaming is true", () => {
74
- const streamStatus = {
75
- isPending: false,
76
- isStreaming: true,
77
- isSuccess: false,
78
- isError: false,
79
- };
80
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: null, streamStatus: streamStatus },
81
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Loading, null,
90
+ it("should render streaming when isStreaming is true", () => {
91
+ jest.mocked(tambo_thread_provider_1.useTamboThread).mockReturnValue({
92
+ generationStage: generate_component_response_1.GenerationStage.STREAMING_RESPONSE,
93
+ });
94
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
95
+ component: createMockComponent({ title: "Partial" }),
96
+ }));
97
+ (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, null,
98
+ react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Streaming, null,
82
99
  react_2.default.createElement("div", { "data-testid": "loading" }, "Loading..."))));
83
100
  expect(react_1.screen.getByTestId("loading")).toBeInTheDocument();
84
101
  });
85
- it("should not render loading when not pending or streaming", () => {
86
- const streamStatus = {
87
- isPending: false,
88
- isStreaming: false,
89
- isSuccess: true,
90
- isError: false,
91
- };
92
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: "test", streamStatus: streamStatus },
93
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Loading, null,
94
- react_2.default.createElement("div", { "data-testid": "loading" }, "Loading..."))));
95
- expect(react_1.screen.queryByTestId("loading")).not.toBeInTheDocument();
96
- });
97
102
  });
98
- describe("Complete Component", () => {
99
- it("should render complete when isSuccess is true and data exists", () => {
100
- const streamStatus = {
101
- isPending: false,
102
- isStreaming: false,
103
- isSuccess: true,
104
- isError: false,
105
- };
106
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: "test data", streamStatus: streamStatus },
107
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Complete, null,
108
- react_2.default.createElement("div", { "data-testid": "complete" }, "Complete!"))));
109
- expect(react_1.screen.getByTestId("complete")).toBeInTheDocument();
110
- });
111
- it("should not render complete when data is null", () => {
112
- const streamStatus = {
113
- isPending: false,
114
- isStreaming: false,
115
- isSuccess: true,
116
- isError: false,
117
- };
118
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: null, streamStatus: streamStatus },
119
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Complete, null,
120
- react_2.default.createElement("div", { "data-testid": "complete" }, "Complete!"))));
121
- expect(react_1.screen.queryByTestId("complete")).not.toBeInTheDocument();
122
- });
123
- it("should not render complete when data is undefined", () => {
124
- const streamStatus = {
125
- isPending: false,
126
- isStreaming: false,
127
- isSuccess: true,
128
- isError: false,
129
- };
130
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: undefined, streamStatus: streamStatus },
131
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Complete, null,
103
+ describe("Success Component", () => {
104
+ it("should not render success when isSuccess is false", () => {
105
+ jest.mocked(tambo_thread_provider_1.useTamboThread).mockReturnValue({
106
+ generationStage: generate_component_response_1.GenerationStage.IDLE,
107
+ });
108
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
109
+ component: createMockComponent({ title: "" }),
110
+ }));
111
+ (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, null,
112
+ react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Success, null,
132
113
  react_2.default.createElement("div", { "data-testid": "complete" }, "Complete!"))));
133
114
  expect(react_1.screen.queryByTestId("complete")).not.toBeInTheDocument();
134
115
  });
135
116
  });
136
- describe("Empty Component", () => {
137
- it("should render empty when no data and not loading/streaming/success/error", () => {
138
- const streamStatus = {
139
- isPending: false,
140
- isStreaming: false,
141
- isSuccess: false,
142
- isError: false,
143
- };
144
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: null, streamStatus: streamStatus },
145
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Empty, null,
146
- react_2.default.createElement("div", { "data-testid": "empty" }, "No data"))));
117
+ describe("Pending Component", () => {
118
+ it("should render pending when no active status", () => {
119
+ jest.mocked(tambo_thread_provider_1.useTamboThread).mockReturnValue({
120
+ generationStage: generate_component_response_1.GenerationStage.IDLE,
121
+ });
122
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
123
+ component: createMockComponent({ title: "" }),
124
+ }));
125
+ (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, null,
126
+ react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Pending, null,
127
+ react_2.default.createElement("div", { "data-testid": "empty" }, "Empty!"))));
147
128
  expect(react_1.screen.getByTestId("empty")).toBeInTheDocument();
148
129
  });
149
- it("should render empty when data is empty string", () => {
150
- const streamStatus = {
151
- isPending: false,
152
- isStreaming: false,
153
- isSuccess: false,
154
- isError: false,
155
- };
156
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: "", streamStatus: streamStatus },
157
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Empty, null,
158
- react_2.default.createElement("div", { "data-testid": "empty" }, "No data"))));
159
- expect(react_1.screen.getByTestId("empty")).toBeInTheDocument();
160
- });
161
- it("should not render empty when loading", () => {
162
- const streamStatus = {
163
- isPending: true,
164
- isStreaming: false,
165
- isSuccess: false,
166
- isError: false,
167
- };
168
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: null, streamStatus: streamStatus },
169
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Empty, null,
170
- react_2.default.createElement("div", { "data-testid": "empty" }, "No data"))));
171
- expect(react_1.screen.queryByTestId("empty")).not.toBeInTheDocument();
172
- });
173
- it("should not render empty when successful", () => {
174
- const streamStatus = {
175
- isPending: false,
176
- isStreaming: false,
177
- isSuccess: true,
178
- isError: false,
179
- };
180
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: null, streamStatus: streamStatus },
181
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Empty, null,
182
- react_2.default.createElement("div", { "data-testid": "empty" }, "No data"))));
130
+ it("should not render pending when isPending is true", () => {
131
+ jest.mocked(tambo_thread_provider_1.useTamboThread).mockReturnValue({
132
+ generationStage: generate_component_response_1.GenerationStage.STREAMING_RESPONSE,
133
+ });
134
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
135
+ component: createMockComponent({ title: "Partial" }),
136
+ }));
137
+ (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, null,
138
+ react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Pending, null,
139
+ react_2.default.createElement("div", { "data-testid": "empty" }, "Empty!"))));
183
140
  expect(react_1.screen.queryByTestId("empty")).not.toBeInTheDocument();
184
141
  });
185
142
  });
186
143
  });
187
- describe("Per-Key Status Tracking", () => {
188
- it("should track status for object keys", () => {
189
- const testData = { name: "John", age: null };
190
- const streamStatus = {
191
- isPending: false,
192
- isStreaming: false,
193
- isSuccess: true,
194
- isError: false,
195
- };
196
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: testData, streamStatus: streamStatus },
197
- react_2.default.createElement(TestHookComponent, { testKey: "name" })));
198
- const keyStatus = JSON.parse(react_1.screen.getByTestId("key-status").textContent ?? "{}");
199
- expect(keyStatus.isSuccess).toBe(true); // name has data
200
- });
201
- it("should show loading for keys without data", () => {
202
- const testData = { name: "John", age: null };
203
- const streamStatus = {
204
- isPending: false,
205
- isStreaming: false,
206
- isSuccess: true,
207
- isError: false,
208
- };
209
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: testData, streamStatus: streamStatus },
210
- react_2.default.createElement(TestHookComponent, { testKey: "age" })));
211
- const keyStatus = JSON.parse(react_1.screen.getByTestId("key-status").textContent ?? "{}");
212
- expect(keyStatus.isPending).toBe(true); // age has no data
213
- });
214
- it("should handle per-key loading states", () => {
215
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: { name: "John", age: null } },
216
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Loading, { streamKey: "name" },
217
- react_2.default.createElement("div", { "data-testid": "name-loading" }, "Name loading...")),
218
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Loading, { streamKey: "age" },
219
- react_2.default.createElement("div", { "data-testid": "age-loading" }, "Age loading...")),
220
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Complete, { streamKey: "name" },
221
- react_2.default.createElement("div", { "data-testid": "name-complete" }, "Name: John"))));
222
- // Name should be complete (has data)
223
- expect(react_1.screen.getByTestId("name-complete")).toBeInTheDocument();
224
- expect(react_1.screen.queryByTestId("name-loading")).not.toBeInTheDocument();
225
- // Age should be loading (no data)
226
- expect(react_1.screen.getByTestId("age-loading")).toBeInTheDocument();
144
+ describe("Key-based Status", () => {
145
+ it("should provide status for keys not in propStatus", () => {
146
+ jest.mocked(tambo_thread_provider_1.useTamboThread).mockReturnValue({
147
+ generationStage: generate_component_response_1.GenerationStage.COMPLETE,
148
+ });
149
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
150
+ component: createMockComponent({ name: "John" }),
151
+ }));
152
+ (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, null,
153
+ react_2.default.createElement(TestHookComponent, { testKey: "nonexistent" })));
154
+ const keyStatus = JSON.parse(react_1.screen.getByTestId("key-status").textContent);
155
+ expect(keyStatus.isPending).toBe(true);
227
156
  });
228
157
  });
229
- describe("Edge Cases", () => {
230
- it("should handle null data gracefully", () => {
231
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: null },
232
- react_2.default.createElement(TestHookComponent, null)));
233
- expect(react_1.screen.getByTestId("data")).toHaveTextContent("null");
234
- });
235
- it("should handle undefined data gracefully", () => {
236
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: undefined },
237
- react_2.default.createElement(TestHookComponent, null)));
238
- expect(react_1.screen.getByTestId("data")).toHaveTextContent("");
239
- });
240
- it("should handle array data", () => {
241
- const arrayData = ["item1", "item2"];
242
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: arrayData },
243
- react_2.default.createElement(TestHookComponent, null)));
244
- expect(react_1.screen.getByTestId("data")).toHaveTextContent(JSON.stringify(arrayData));
245
- });
246
- it("should handle primitive data types", () => {
247
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: "string data" },
248
- react_2.default.createElement(TestHookComponent, null)));
249
- expect(react_1.screen.getByTestId("data")).toHaveTextContent('"string data"');
250
- });
251
- it("should fallback to default status for unknown keys", () => {
252
- const testData = { name: "John" };
253
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: testData },
254
- react_2.default.createElement(TestHookComponent, { testKey: "unknown-key" })));
255
- const keyStatus = JSON.parse(react_1.screen.getByTestId("key-status").textContent ?? "{}");
256
- // Should fallback to default status
257
- expect(keyStatus.isSuccess).toBe(true);
158
+ describe("Compound Components with Keys", () => {
159
+ it("should render loading for specific key when pending", () => {
160
+ jest.mocked(tambo_thread_provider_1.useTamboThread).mockReturnValue({
161
+ generationStage: generate_component_response_1.GenerationStage.STREAMING_RESPONSE,
162
+ });
163
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
164
+ component: createMockComponent({
165
+ name: "Partial",
166
+ age: 25,
167
+ }),
168
+ }));
169
+ (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, null,
170
+ react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Streaming, { streamKey: "name" },
171
+ react_2.default.createElement("div", { "data-testid": "name-loading" }, "Name Loading...")),
172
+ react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Streaming, { streamKey: "age" },
173
+ react_2.default.createElement("div", { "data-testid": "age-loading" }, "Age Loading...")),
174
+ react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Success, { streamKey: "name" },
175
+ react_2.default.createElement("div", { "data-testid": "name-complete" }, "Name Complete!"))));
176
+ // Both props should be loading since they're in streaming stage
177
+ expect(react_1.screen.getByTestId("name-loading")).toBeInTheDocument();
178
+ expect(react_1.screen.getByTestId("age-loading")).toBeInTheDocument();
179
+ expect(react_1.screen.queryByTestId("name-complete")).not.toBeInTheDocument();
258
180
  });
259
181
  });
260
- describe("Component Attributes", () => {
261
- it("should add correct data attributes to components", () => {
262
- const streamStatus = {
263
- isPending: true,
264
- isStreaming: false,
265
- isSuccess: false,
266
- isError: false,
267
- };
268
- (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, { data: null, streamStatus: streamStatus },
269
- react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Loading, { className: "loading-class" },
270
- react_2.default.createElement("div", null, "Loading..."))));
271
- const loadingElement = react_1.screen.getByText("Loading...").parentElement;
272
- expect(loadingElement).toHaveAttribute("data-stream-key", "default");
273
- expect(loadingElement).toHaveAttribute("data-stream-state", "loading");
182
+ describe("Styling", () => {
183
+ it("should apply className to loading component", () => {
184
+ jest.mocked(tambo_thread_provider_1.useTamboThread).mockReturnValue({
185
+ generationStage: generate_component_response_1.GenerationStage.STREAMING_RESPONSE,
186
+ });
187
+ jest.mocked(use_current_message_1.useTamboCurrentMessage).mockReturnValue(createMockMessage({
188
+ component: createMockComponent({ title: "Partial" }),
189
+ }));
190
+ (0, react_1.render)(react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider, null,
191
+ react_2.default.createElement(tambo_prop_stream_provider_1.TamboPropStreamProvider.Streaming, { className: "loading-class" },
192
+ react_2.default.createElement("div", { "data-testid": "loading" }, "Loading..."))));
193
+ const loadingElement = react_1.screen.getByTestId("loading").parentElement;
274
194
  expect(loadingElement).toHaveClass("loading-class");
275
195
  });
276
196
  });