@tambo-ai/react 0.74.1 → 0.75.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 (308) hide show
  1. package/README.md +46 -449
  2. package/dist/hoc/with-tambo-interactable.d.ts +8 -0
  3. package/dist/hoc/with-tambo-interactable.d.ts.map +1 -1
  4. package/dist/hoc/with-tambo-interactable.js +5 -2
  5. package/dist/hoc/with-tambo-interactable.js.map +1 -1
  6. package/dist/hoc/with-tambo-interactable.test.js +12 -0
  7. package/dist/hoc/with-tambo-interactable.test.js.map +1 -1
  8. package/dist/hooks/use-tambo-voice.test.js +3 -0
  9. package/dist/hooks/use-tambo-voice.test.js.map +1 -1
  10. package/dist/mcp/mcp-hooks.test.js +69 -0
  11. package/dist/mcp/mcp-hooks.test.js.map +1 -1
  12. package/dist/mcp/tambo-mcp-provider.test.js +24 -0
  13. package/dist/mcp/tambo-mcp-provider.test.js.map +1 -1
  14. package/dist/mcp/use-mcp-servers.test.js +9 -0
  15. package/dist/mcp/use-mcp-servers.test.js.map +1 -1
  16. package/dist/model/component-metadata.d.ts +4 -4
  17. package/dist/model/component-metadata.js.map +1 -1
  18. package/dist/providers/__tests__/thread-input-resource-resolution.test.js +2 -2
  19. package/dist/providers/__tests__/thread-input-resource-resolution.test.js.map +1 -1
  20. package/dist/providers/tambo-client-provider.d.ts +6 -0
  21. package/dist/providers/tambo-client-provider.d.ts.map +1 -1
  22. package/dist/providers/tambo-client-provider.js +4 -1
  23. package/dist/providers/tambo-client-provider.js.map +1 -1
  24. package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
  25. package/dist/providers/tambo-interactable-provider.js +8 -0
  26. package/dist/providers/tambo-interactable-provider.js.map +1 -1
  27. package/dist/providers/tambo-interactable-provider.test.js +47 -0
  28. package/dist/providers/tambo-interactable-provider.test.js.map +1 -1
  29. package/dist/providers/tambo-provider.d.ts.map +1 -1
  30. package/dist/providers/tambo-provider.js +4 -1
  31. package/dist/providers/tambo-provider.js.map +1 -1
  32. package/dist/providers/tambo-stubs.d.ts.map +1 -1
  33. package/dist/providers/tambo-stubs.js +3 -0
  34. package/dist/providers/tambo-stubs.js.map +1 -1
  35. package/dist/providers/tambo-thread-provider-initial-messages.test.js +3 -0
  36. package/dist/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
  37. package/dist/providers/tambo-thread-provider.test.js +3 -0
  38. package/dist/providers/tambo-thread-provider.test.js.map +1 -1
  39. package/dist/util/registry-validators.js +1 -1
  40. package/dist/util/registry-validators.js.map +1 -1
  41. package/dist/util/resource-content-resolver.test.js +1 -1
  42. package/dist/util/resource-content-resolver.test.js.map +1 -1
  43. package/dist/v1/hooks/use-tambo-v1-auth-state.d.ts +11 -0
  44. package/dist/v1/hooks/use-tambo-v1-auth-state.d.ts.map +1 -0
  45. package/dist/v1/hooks/use-tambo-v1-auth-state.js +48 -0
  46. package/dist/v1/hooks/use-tambo-v1-auth-state.js.map +1 -0
  47. package/dist/v1/hooks/use-tambo-v1-auth-state.test.d.ts +2 -0
  48. package/dist/v1/hooks/use-tambo-v1-auth-state.test.d.ts.map +1 -0
  49. package/dist/v1/hooks/use-tambo-v1-auth-state.test.js +105 -0
  50. package/dist/v1/hooks/use-tambo-v1-auth-state.test.js.map +1 -0
  51. package/dist/v1/hooks/use-tambo-v1-component-state.d.ts.map +1 -1
  52. package/dist/v1/hooks/use-tambo-v1-component-state.js +68 -21
  53. package/dist/v1/hooks/use-tambo-v1-component-state.js.map +1 -1
  54. package/dist/v1/hooks/use-tambo-v1-component-state.test.js +100 -0
  55. package/dist/v1/hooks/use-tambo-v1-component-state.test.js.map +1 -1
  56. package/dist/v1/hooks/use-tambo-v1-messages.test.js +8 -0
  57. package/dist/v1/hooks/use-tambo-v1-messages.test.js.map +1 -1
  58. package/dist/v1/hooks/use-tambo-v1-send-message.d.ts +19 -1
  59. package/dist/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
  60. package/dist/v1/hooks/use-tambo-v1-send-message.js +86 -9
  61. package/dist/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
  62. package/dist/v1/hooks/use-tambo-v1-send-message.test.js +273 -0
  63. package/dist/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -1
  64. package/dist/v1/hooks/use-tambo-v1-stream-status.d.ts +2 -2
  65. package/dist/v1/hooks/use-tambo-v1-stream-status.d.ts.map +1 -1
  66. package/dist/v1/hooks/use-tambo-v1-stream-status.js +2 -2
  67. package/dist/v1/hooks/use-tambo-v1-stream-status.js.map +1 -1
  68. package/dist/v1/hooks/use-tambo-v1-stream-status.test.js +11 -11
  69. package/dist/v1/hooks/use-tambo-v1-stream-status.test.js.map +1 -1
  70. package/dist/v1/hooks/use-tambo-v1-thread-input.test.js +13 -0
  71. package/dist/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -1
  72. package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -1
  73. package/dist/v1/hooks/use-tambo-v1-thread-list.js +4 -0
  74. package/dist/v1/hooks/use-tambo-v1-thread-list.js.map +1 -1
  75. package/dist/v1/hooks/use-tambo-v1-thread-list.test.js +6 -0
  76. package/dist/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -1
  77. package/dist/v1/hooks/use-tambo-v1-thread.d.ts.map +1 -1
  78. package/dist/v1/hooks/use-tambo-v1-thread.js +4 -0
  79. package/dist/v1/hooks/use-tambo-v1-thread.js.map +1 -1
  80. package/dist/v1/hooks/use-tambo-v1-thread.test.js +6 -0
  81. package/dist/v1/hooks/use-tambo-v1-thread.test.js.map +1 -1
  82. package/dist/v1/hooks/use-tambo-v1.d.ts +11 -0
  83. package/dist/v1/hooks/use-tambo-v1.d.ts.map +1 -1
  84. package/dist/v1/hooks/use-tambo-v1.js +5 -0
  85. package/dist/v1/hooks/use-tambo-v1.js.map +1 -1
  86. package/dist/v1/hooks/use-tambo-v1.test.js +13 -0
  87. package/dist/v1/hooks/use-tambo-v1.test.js.map +1 -1
  88. package/dist/v1/index.d.ts +4 -1
  89. package/dist/v1/index.d.ts.map +1 -1
  90. package/dist/v1/index.js +5 -2
  91. package/dist/v1/index.js.map +1 -1
  92. package/dist/v1/providers/tambo-v1-provider.d.ts +16 -1
  93. package/dist/v1/providers/tambo-v1-provider.d.ts.map +1 -1
  94. package/dist/v1/providers/tambo-v1-provider.js +42 -5
  95. package/dist/v1/providers/tambo-v1-provider.js.map +1 -1
  96. package/dist/v1/providers/tambo-v1-provider.test.js +19 -2
  97. package/dist/v1/providers/tambo-v1-provider.test.js.map +1 -1
  98. package/dist/v1/providers/tambo-v1-stream-context.d.ts +6 -0
  99. package/dist/v1/providers/tambo-v1-stream-context.d.ts.map +1 -1
  100. package/dist/v1/providers/tambo-v1-stream-context.js +50 -11
  101. package/dist/v1/providers/tambo-v1-stream-context.js.map +1 -1
  102. package/dist/v1/providers/tambo-v1-stream-context.test.js +9 -0
  103. package/dist/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
  104. package/dist/v1/providers/tambo-v1-stub-provider.d.ts.map +1 -1
  105. package/dist/v1/providers/tambo-v1-stub-provider.js +4 -0
  106. package/dist/v1/providers/tambo-v1-stub-provider.js.map +1 -1
  107. package/dist/v1/providers/tambo-v1-thread-input-provider.d.ts +11 -0
  108. package/dist/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -1
  109. package/dist/v1/providers/tambo-v1-thread-input-provider.js +10 -1
  110. package/dist/v1/providers/tambo-v1-thread-input-provider.js.map +1 -1
  111. package/dist/v1/types/auth.d.ts +24 -0
  112. package/dist/v1/types/auth.d.ts.map +1 -0
  113. package/dist/v1/types/auth.js +3 -0
  114. package/dist/v1/types/auth.js.map +1 -0
  115. package/dist/v1/types/message.d.ts +12 -0
  116. package/dist/v1/types/message.d.ts.map +1 -1
  117. package/dist/v1/types/message.js.map +1 -1
  118. package/dist/v1/types/tool-choice.d.ts +8 -0
  119. package/dist/v1/types/tool-choice.d.ts.map +1 -0
  120. package/dist/v1/types/tool-choice.js +3 -0
  121. package/dist/v1/types/tool-choice.js.map +1 -0
  122. package/dist/v1/utils/event-accumulator.d.ts +28 -2
  123. package/dist/v1/utils/event-accumulator.d.ts.map +1 -1
  124. package/dist/v1/utils/event-accumulator.js +67 -15
  125. package/dist/v1/utils/event-accumulator.js.map +1 -1
  126. package/dist/v1/utils/event-accumulator.test.js +106 -0
  127. package/dist/v1/utils/event-accumulator.test.js.map +1 -1
  128. package/dist/v1/utils/keyed-throttle.d.ts +42 -0
  129. package/dist/v1/utils/keyed-throttle.d.ts.map +1 -0
  130. package/dist/v1/utils/keyed-throttle.js +86 -0
  131. package/dist/v1/utils/keyed-throttle.js.map +1 -0
  132. package/dist/v1/utils/keyed-throttle.test.d.ts +2 -0
  133. package/dist/v1/utils/keyed-throttle.test.d.ts.map +1 -0
  134. package/dist/v1/utils/keyed-throttle.test.js +147 -0
  135. package/dist/v1/utils/keyed-throttle.test.js.map +1 -0
  136. package/dist/v1/utils/registry-conversion.d.ts.map +1 -1
  137. package/dist/v1/utils/registry-conversion.js +2 -0
  138. package/dist/v1/utils/registry-conversion.js.map +1 -1
  139. package/dist/v1/utils/registry-conversion.test.js +23 -0
  140. package/dist/v1/utils/registry-conversion.test.js.map +1 -1
  141. package/dist/v1/utils/tool-call-tracker.d.ts +10 -0
  142. package/dist/v1/utils/tool-call-tracker.d.ts.map +1 -1
  143. package/dist/v1/utils/tool-call-tracker.js +13 -0
  144. package/dist/v1/utils/tool-call-tracker.js.map +1 -1
  145. package/dist/v1/utils/tool-call-tracker.test.d.ts +2 -0
  146. package/dist/v1/utils/tool-call-tracker.test.d.ts.map +1 -0
  147. package/dist/v1/utils/tool-call-tracker.test.js +67 -0
  148. package/dist/v1/utils/tool-call-tracker.test.js.map +1 -0
  149. package/dist/v1/utils/tool-executor.d.ts +34 -0
  150. package/dist/v1/utils/tool-executor.d.ts.map +1 -1
  151. package/dist/v1/utils/tool-executor.js +55 -0
  152. package/dist/v1/utils/tool-executor.js.map +1 -1
  153. package/dist/v1/utils/tool-executor.test.js +211 -0
  154. package/dist/v1/utils/tool-executor.test.js.map +1 -1
  155. package/esm/hoc/with-tambo-interactable.d.ts +8 -0
  156. package/esm/hoc/with-tambo-interactable.d.ts.map +1 -1
  157. package/esm/hoc/with-tambo-interactable.js +5 -2
  158. package/esm/hoc/with-tambo-interactable.js.map +1 -1
  159. package/esm/hoc/with-tambo-interactable.test.js +12 -0
  160. package/esm/hoc/with-tambo-interactable.test.js.map +1 -1
  161. package/esm/hooks/use-tambo-voice.test.js +3 -0
  162. package/esm/hooks/use-tambo-voice.test.js.map +1 -1
  163. package/esm/mcp/mcp-hooks.test.js +69 -0
  164. package/esm/mcp/mcp-hooks.test.js.map +1 -1
  165. package/esm/mcp/tambo-mcp-provider.test.js +24 -0
  166. package/esm/mcp/tambo-mcp-provider.test.js.map +1 -1
  167. package/esm/mcp/use-mcp-servers.test.js +9 -0
  168. package/esm/mcp/use-mcp-servers.test.js.map +1 -1
  169. package/esm/model/component-metadata.d.ts +4 -4
  170. package/esm/model/component-metadata.js.map +1 -1
  171. package/esm/providers/__tests__/thread-input-resource-resolution.test.js +2 -2
  172. package/esm/providers/__tests__/thread-input-resource-resolution.test.js.map +1 -1
  173. package/esm/providers/tambo-client-provider.d.ts +6 -0
  174. package/esm/providers/tambo-client-provider.d.ts.map +1 -1
  175. package/esm/providers/tambo-client-provider.js +4 -1
  176. package/esm/providers/tambo-client-provider.js.map +1 -1
  177. package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
  178. package/esm/providers/tambo-interactable-provider.js +8 -0
  179. package/esm/providers/tambo-interactable-provider.js.map +1 -1
  180. package/esm/providers/tambo-interactable-provider.test.js +47 -0
  181. package/esm/providers/tambo-interactable-provider.test.js.map +1 -1
  182. package/esm/providers/tambo-provider.d.ts.map +1 -1
  183. package/esm/providers/tambo-provider.js +4 -1
  184. package/esm/providers/tambo-provider.js.map +1 -1
  185. package/esm/providers/tambo-stubs.d.ts.map +1 -1
  186. package/esm/providers/tambo-stubs.js +3 -0
  187. package/esm/providers/tambo-stubs.js.map +1 -1
  188. package/esm/providers/tambo-thread-provider-initial-messages.test.js +3 -0
  189. package/esm/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
  190. package/esm/providers/tambo-thread-provider.test.js +3 -0
  191. package/esm/providers/tambo-thread-provider.test.js.map +1 -1
  192. package/esm/util/registry-validators.js +1 -1
  193. package/esm/util/registry-validators.js.map +1 -1
  194. package/esm/util/resource-content-resolver.test.js +1 -1
  195. package/esm/util/resource-content-resolver.test.js.map +1 -1
  196. package/esm/v1/hooks/use-tambo-v1-auth-state.d.ts +11 -0
  197. package/esm/v1/hooks/use-tambo-v1-auth-state.d.ts.map +1 -0
  198. package/esm/v1/hooks/use-tambo-v1-auth-state.js +45 -0
  199. package/esm/v1/hooks/use-tambo-v1-auth-state.js.map +1 -0
  200. package/esm/v1/hooks/use-tambo-v1-auth-state.test.d.ts +2 -0
  201. package/esm/v1/hooks/use-tambo-v1-auth-state.test.d.ts.map +1 -0
  202. package/esm/v1/hooks/use-tambo-v1-auth-state.test.js +100 -0
  203. package/esm/v1/hooks/use-tambo-v1-auth-state.test.js.map +1 -0
  204. package/esm/v1/hooks/use-tambo-v1-component-state.d.ts.map +1 -1
  205. package/esm/v1/hooks/use-tambo-v1-component-state.js +68 -21
  206. package/esm/v1/hooks/use-tambo-v1-component-state.js.map +1 -1
  207. package/esm/v1/hooks/use-tambo-v1-component-state.test.js +100 -0
  208. package/esm/v1/hooks/use-tambo-v1-component-state.test.js.map +1 -1
  209. package/esm/v1/hooks/use-tambo-v1-messages.test.js +8 -0
  210. package/esm/v1/hooks/use-tambo-v1-messages.test.js.map +1 -1
  211. package/esm/v1/hooks/use-tambo-v1-send-message.d.ts +19 -1
  212. package/esm/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
  213. package/esm/v1/hooks/use-tambo-v1-send-message.js +87 -10
  214. package/esm/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
  215. package/esm/v1/hooks/use-tambo-v1-send-message.test.js +273 -0
  216. package/esm/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -1
  217. package/esm/v1/hooks/use-tambo-v1-stream-status.d.ts +2 -2
  218. package/esm/v1/hooks/use-tambo-v1-stream-status.d.ts.map +1 -1
  219. package/esm/v1/hooks/use-tambo-v1-stream-status.js +2 -2
  220. package/esm/v1/hooks/use-tambo-v1-stream-status.js.map +1 -1
  221. package/esm/v1/hooks/use-tambo-v1-stream-status.test.js +11 -11
  222. package/esm/v1/hooks/use-tambo-v1-stream-status.test.js.map +1 -1
  223. package/esm/v1/hooks/use-tambo-v1-thread-input.test.js +13 -0
  224. package/esm/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -1
  225. package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -1
  226. package/esm/v1/hooks/use-tambo-v1-thread-list.js +4 -0
  227. package/esm/v1/hooks/use-tambo-v1-thread-list.js.map +1 -1
  228. package/esm/v1/hooks/use-tambo-v1-thread-list.test.js +6 -0
  229. package/esm/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -1
  230. package/esm/v1/hooks/use-tambo-v1-thread.d.ts.map +1 -1
  231. package/esm/v1/hooks/use-tambo-v1-thread.js +4 -0
  232. package/esm/v1/hooks/use-tambo-v1-thread.js.map +1 -1
  233. package/esm/v1/hooks/use-tambo-v1-thread.test.js +6 -0
  234. package/esm/v1/hooks/use-tambo-v1-thread.test.js.map +1 -1
  235. package/esm/v1/hooks/use-tambo-v1.d.ts +11 -0
  236. package/esm/v1/hooks/use-tambo-v1.d.ts.map +1 -1
  237. package/esm/v1/hooks/use-tambo-v1.js +5 -0
  238. package/esm/v1/hooks/use-tambo-v1.js.map +1 -1
  239. package/esm/v1/hooks/use-tambo-v1.test.js +13 -0
  240. package/esm/v1/hooks/use-tambo-v1.test.js.map +1 -1
  241. package/esm/v1/index.d.ts +4 -1
  242. package/esm/v1/index.d.ts.map +1 -1
  243. package/esm/v1/index.js +3 -1
  244. package/esm/v1/index.js.map +1 -1
  245. package/esm/v1/providers/tambo-v1-provider.d.ts +16 -1
  246. package/esm/v1/providers/tambo-v1-provider.d.ts.map +1 -1
  247. package/esm/v1/providers/tambo-v1-provider.js +43 -6
  248. package/esm/v1/providers/tambo-v1-provider.js.map +1 -1
  249. package/esm/v1/providers/tambo-v1-provider.test.js +19 -2
  250. package/esm/v1/providers/tambo-v1-provider.test.js.map +1 -1
  251. package/esm/v1/providers/tambo-v1-stream-context.d.ts +6 -0
  252. package/esm/v1/providers/tambo-v1-stream-context.d.ts.map +1 -1
  253. package/esm/v1/providers/tambo-v1-stream-context.js +51 -12
  254. package/esm/v1/providers/tambo-v1-stream-context.js.map +1 -1
  255. package/esm/v1/providers/tambo-v1-stream-context.test.js +9 -0
  256. package/esm/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
  257. package/esm/v1/providers/tambo-v1-stub-provider.d.ts.map +1 -1
  258. package/esm/v1/providers/tambo-v1-stub-provider.js +4 -0
  259. package/esm/v1/providers/tambo-v1-stub-provider.js.map +1 -1
  260. package/esm/v1/providers/tambo-v1-thread-input-provider.d.ts +11 -0
  261. package/esm/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -1
  262. package/esm/v1/providers/tambo-v1-thread-input-provider.js +10 -1
  263. package/esm/v1/providers/tambo-v1-thread-input-provider.js.map +1 -1
  264. package/esm/v1/types/auth.d.ts +24 -0
  265. package/esm/v1/types/auth.d.ts.map +1 -0
  266. package/esm/v1/types/auth.js +2 -0
  267. package/esm/v1/types/auth.js.map +1 -0
  268. package/esm/v1/types/message.d.ts +12 -0
  269. package/esm/v1/types/message.d.ts.map +1 -1
  270. package/esm/v1/types/message.js.map +1 -1
  271. package/esm/v1/types/tool-choice.d.ts +8 -0
  272. package/esm/v1/types/tool-choice.d.ts.map +1 -0
  273. package/esm/v1/types/tool-choice.js +2 -0
  274. package/esm/v1/types/tool-choice.js.map +1 -0
  275. package/esm/v1/utils/event-accumulator.d.ts +28 -2
  276. package/esm/v1/utils/event-accumulator.d.ts.map +1 -1
  277. package/esm/v1/utils/event-accumulator.js +66 -15
  278. package/esm/v1/utils/event-accumulator.js.map +1 -1
  279. package/esm/v1/utils/event-accumulator.test.js +106 -0
  280. package/esm/v1/utils/event-accumulator.test.js.map +1 -1
  281. package/esm/v1/utils/keyed-throttle.d.ts +42 -0
  282. package/esm/v1/utils/keyed-throttle.d.ts.map +1 -0
  283. package/esm/v1/utils/keyed-throttle.js +83 -0
  284. package/esm/v1/utils/keyed-throttle.js.map +1 -0
  285. package/esm/v1/utils/keyed-throttle.test.d.ts +2 -0
  286. package/esm/v1/utils/keyed-throttle.test.d.ts.map +1 -0
  287. package/esm/v1/utils/keyed-throttle.test.js +145 -0
  288. package/esm/v1/utils/keyed-throttle.test.js.map +1 -0
  289. package/esm/v1/utils/registry-conversion.d.ts.map +1 -1
  290. package/esm/v1/utils/registry-conversion.js +2 -0
  291. package/esm/v1/utils/registry-conversion.js.map +1 -1
  292. package/esm/v1/utils/registry-conversion.test.js +23 -0
  293. package/esm/v1/utils/registry-conversion.test.js.map +1 -1
  294. package/esm/v1/utils/tool-call-tracker.d.ts +10 -0
  295. package/esm/v1/utils/tool-call-tracker.d.ts.map +1 -1
  296. package/esm/v1/utils/tool-call-tracker.js +13 -0
  297. package/esm/v1/utils/tool-call-tracker.js.map +1 -1
  298. package/esm/v1/utils/tool-call-tracker.test.d.ts +2 -0
  299. package/esm/v1/utils/tool-call-tracker.test.d.ts.map +1 -0
  300. package/esm/v1/utils/tool-call-tracker.test.js +65 -0
  301. package/esm/v1/utils/tool-call-tracker.test.js.map +1 -0
  302. package/esm/v1/utils/tool-executor.d.ts +34 -0
  303. package/esm/v1/utils/tool-executor.d.ts.map +1 -1
  304. package/esm/v1/utils/tool-executor.js +53 -0
  305. package/esm/v1/utils/tool-executor.js.map +1 -1
  306. package/esm/v1/utils/tool-executor.test.js +212 -1
  307. package/esm/v1/utils/tool-executor.test.js.map +1 -1
  308. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"registry-validators.js","sourceRoot":"","sources":["../../src/util/registry-validators.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { TamboComponent, TamboTool } from \"../model/component-metadata\";\nimport {\n assertNoRecordSchema,\n isStandardSchema,\n looksLikeJSONSchema,\n schemaToJsonSchema,\n} from \"../schema\";\nimport { assertValidName } from \"./validate-component-name\";\n\n/**\n * Checks if a JSON Schema represents an object type.\n * @param schema - The JSON Schema to check\n * @returns True if the schema is an object type\n */\nfunction isObjectSchema(schema: JSONSchema7): boolean {\n return schema.type === \"object\" || schema.properties !== undefined;\n}\n\n/**\n * Validates that a schema is an object type.\n * Throws an error if it's not.\n * @param schema - The schema to validate (Standard Schema or JSON Schema)\n * @param context - Description of where the schema is used (for error messages)\n */\nfunction assertObjectSchema(schema: unknown, context: string): void {\n let jsonSchema: JSONSchema7;\n\n if (isStandardSchema(schema)) {\n try {\n jsonSchema = schemaToJsonSchema(schema);\n } catch {\n // If conversion fails, we can't validate - let it fail later\n return;\n }\n } else if (looksLikeJSONSchema(schema)) {\n jsonSchema = schema;\n } else {\n // Unknown schema type, can't validate\n return;\n }\n\n if (!isObjectSchema(jsonSchema)) {\n throw new Error(\n `${context} must be an object schema (e.g., z.object({...})). ` +\n `Received type: \"${jsonSchema.type ?? \"unknown\"}\". ` +\n `Tool parameters are passed as a single object argument.`,\n );\n }\n}\n\n/**\n * Validates a tool before registration.\n * Throws an error if the tool is invalid.\n * @param tool - The tool to validate\n */\nexport function validateTool(tool: unknown): asserts tool is TamboTool {\n // Basic runtime type checks before narrowing\n if (!tool || typeof tool !== \"object\") {\n throw new Error(\"Tool must be an object\");\n }\n\n // Extract name for error messages (before we know it's valid)\n const toolName =\n \"name\" in tool && typeof tool.name === \"string\" ? tool.name : \"<unknown>\";\n\n // Check for deprecated toolSchema - throw error with migration instructions\n if (\"toolSchema\" in tool) {\n throw new Error(\n `Tool \"${toolName}\" uses deprecated \"toolSchema\" property. ` +\n `Migrate to \"inputSchema\" and \"outputSchema\" properties. ` +\n `See migration guide: https://tambo.ai/docs/migration/toolschema`,\n );\n }\n\n // Validate required properties exist\n if (!(\"name\" in tool) || typeof tool.name !== \"string\") {\n throw new Error(\"Tool must have a 'name' property of type string\");\n }\n\n if (!(\"description\" in tool) || typeof tool.description !== \"string\") {\n throw new Error(\n `Tool \"${tool.name}\" must have a 'description' property of type string`,\n );\n }\n\n if (!(\"tool\" in tool) || typeof tool.tool !== \"function\") {\n throw new Error(\n `Tool \"${tool.name}\" must have a 'tool' property of type function`,\n );\n }\n\n if (!(\"inputSchema\" in tool)) {\n throw new Error(`Tool \"${tool.name}\" must have an 'inputSchema' property`);\n }\n\n if (!(\"outputSchema\" in tool)) {\n throw new Error(`Tool \"${tool.name}\" must have an 'outputSchema' property`);\n }\n\n // Validate tool name format\n assertValidName(tool.name, \"tool\");\n\n // Validate tool schemas - inputSchema must be an object schema\n if (tool.inputSchema) {\n assertObjectSchema(tool.inputSchema, `inputSchema of tool \"${tool.name}\"`);\n assertNoRecordSchema(\n tool.inputSchema,\n `inputSchema of tool \"${tool.name}\"`,\n );\n }\n\n // Validate maxCalls if provided - must be a positive integer\n if (\n \"maxCalls\" in tool &&\n tool.maxCalls !== undefined &&\n tool.maxCalls !== null\n ) {\n const maxCalls = tool.maxCalls;\n if (\n typeof maxCalls !== \"number\" ||\n !Number.isInteger(maxCalls) ||\n maxCalls < 0\n ) {\n throw new Error(\n `maxCalls for tool \"${tool.name}\" must be a positive integer`,\n );\n }\n }\n}\n\n/**\n * Converts a props schema to a serialized JSON Schema format.\n * @param propsDefinition - Deprecated: legacy props definition (will log warning)\n * @param propsSchema - The props schema (Standard Schema or JSON Schema)\n * @param name - Component/tool name for error messages\n * @returns Serialized JSON Schema object\n */\nfunction getSerializedProps(\n propsDefinition: unknown,\n propsSchema: unknown,\n name: string,\n): Record<string, unknown> {\n if (propsDefinition) {\n console.warn(`propsDefinition is deprecated. Use propsSchema instead.`);\n return propsDefinition as Record<string, unknown>;\n }\n\n // Check for Standard Schema (Zod, Valibot, ArkType, etc.)\n if (isStandardSchema(propsSchema)) {\n try {\n return schemaToJsonSchema(propsSchema) as Record<string, unknown>;\n } catch (error) {\n console.error(\n `Error converting ${name} props schema to JSON Schema:`,\n error,\n );\n throw new Error(\n `Error converting ${name} props schema to JSON Schema: ${error}`,\n );\n }\n }\n\n // Check for JSON Schema\n if (looksLikeJSONSchema(propsSchema)) {\n return propsSchema as Record<string, unknown>;\n }\n\n throw new Error(`Invalid props schema for ${name}`);\n}\n\n/**\n * Validates a component and prepares its props for registration.\n * Throws an error if the component is invalid.\n * @param component - The component to validate and prepare\n * @returns Object containing the serialized props\n */\nexport function validateAndPrepareComponent(component: TamboComponent): {\n props: Record<string, unknown>;\n} {\n const { name, propsSchema, propsDefinition } = component;\n\n // Validate component name\n assertValidName(name, \"component\");\n\n // Validate that at least one props definition is provided\n if (!propsSchema && !propsDefinition) {\n throw new Error(\n `Component ${name} must have either propsSchema (recommended) or propsDefinition defined`,\n );\n }\n\n // Validate that only one props definition is provided\n if (propsSchema && propsDefinition) {\n throw new Error(\n `Component ${name} cannot have both propsSchema and propsDefinition defined. Use only one. We recommend using propsSchema.`,\n );\n }\n\n // Validate that the propsSchema does not include record types\n if (propsSchema) {\n assertNoRecordSchema(propsSchema, `propsSchema of component \"${name}\"`);\n }\n\n // Convert propsSchema to JSON Schema if it exists\n const props = getSerializedProps(propsDefinition, propsSchema, name);\n\n return { props };\n}\n\n/**\n * Validates a tool association between a component and tool.\n * Throws an error if the association is invalid.\n * @param componentName - The component name\n * @param toolName - The tool name\n * @param componentExists - Whether the component exists in the registry\n * @param toolExists - Whether the tool exists in the registry\n */\nexport function validateToolAssociation(\n componentName: string,\n toolName: string,\n componentExists: boolean,\n toolExists: boolean,\n): void {\n // Validate component and tool names\n assertValidName(componentName, \"component\");\n assertValidName(toolName, \"tool\");\n\n if (!componentExists) {\n throw new Error(`Component ${componentName} not found in registry`);\n }\n if (!toolExists) {\n throw new Error(`Tool ${toolName} not found in registry`);\n }\n}\n"]}
1
+ {"version":3,"file":"registry-validators.js","sourceRoot":"","sources":["../../src/util/registry-validators.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JSONSchema7 } from \"json-schema\";\nimport type { TamboComponent, TamboTool } from \"../model/component-metadata\";\nimport {\n assertNoRecordSchema,\n isStandardSchema,\n looksLikeJSONSchema,\n schemaToJsonSchema,\n} from \"../schema\";\nimport { assertValidName } from \"./validate-component-name\";\n\n/**\n * Checks if a JSON Schema represents an object type.\n * @param schema - The JSON Schema to check\n * @returns True if the schema is an object type\n */\nfunction isObjectSchema(schema: JSONSchema7): boolean {\n return schema.type === \"object\" || schema.properties !== undefined;\n}\n\n/**\n * Validates that a schema is an object type.\n * Throws an error if it's not.\n * @param schema - The schema to validate (Standard Schema or JSON Schema)\n * @param context - Description of where the schema is used (for error messages)\n */\nfunction assertObjectSchema(schema: unknown, context: string): void {\n let jsonSchema: JSONSchema7;\n\n if (isStandardSchema(schema)) {\n try {\n jsonSchema = schemaToJsonSchema(schema);\n } catch {\n // If conversion fails, we can't validate - let it fail later\n return;\n }\n } else if (looksLikeJSONSchema(schema)) {\n jsonSchema = schema;\n } else {\n // Unknown schema type, can't validate\n return;\n }\n\n if (!isObjectSchema(jsonSchema)) {\n throw new Error(\n `${context} must be an object schema (e.g., z.object({...})). ` +\n `Received type: \"${jsonSchema.type ?? \"unknown\"}\". ` +\n `Tool parameters are passed as a single object argument.`,\n );\n }\n}\n\n/**\n * Validates a tool before registration.\n * Throws an error if the tool is invalid.\n * @param tool - The tool to validate\n */\nexport function validateTool(tool: unknown): asserts tool is TamboTool {\n // Basic runtime type checks before narrowing\n if (!tool || typeof tool !== \"object\") {\n throw new Error(\"Tool must be an object\");\n }\n\n // Extract name for error messages (before we know it's valid)\n const toolName =\n \"name\" in tool && typeof tool.name === \"string\" ? tool.name : \"<unknown>\";\n\n // Check for deprecated toolSchema - throw error with migration instructions\n if (\"toolSchema\" in tool) {\n throw new Error(\n `Tool \"${toolName}\" uses deprecated \"toolSchema\" property. ` +\n `Migrate to \"inputSchema\" and \"outputSchema\" properties. ` +\n `See migration guide: https://docs.tambo.co/reference/react-sdk/migration/toolschema`,\n );\n }\n\n // Validate required properties exist\n if (!(\"name\" in tool) || typeof tool.name !== \"string\") {\n throw new Error(\"Tool must have a 'name' property of type string\");\n }\n\n if (!(\"description\" in tool) || typeof tool.description !== \"string\") {\n throw new Error(\n `Tool \"${tool.name}\" must have a 'description' property of type string`,\n );\n }\n\n if (!(\"tool\" in tool) || typeof tool.tool !== \"function\") {\n throw new Error(\n `Tool \"${tool.name}\" must have a 'tool' property of type function`,\n );\n }\n\n if (!(\"inputSchema\" in tool)) {\n throw new Error(`Tool \"${tool.name}\" must have an 'inputSchema' property`);\n }\n\n if (!(\"outputSchema\" in tool)) {\n throw new Error(`Tool \"${tool.name}\" must have an 'outputSchema' property`);\n }\n\n // Validate tool name format\n assertValidName(tool.name, \"tool\");\n\n // Validate tool schemas - inputSchema must be an object schema\n if (tool.inputSchema) {\n assertObjectSchema(tool.inputSchema, `inputSchema of tool \"${tool.name}\"`);\n assertNoRecordSchema(\n tool.inputSchema,\n `inputSchema of tool \"${tool.name}\"`,\n );\n }\n\n // Validate maxCalls if provided - must be a positive integer\n if (\n \"maxCalls\" in tool &&\n tool.maxCalls !== undefined &&\n tool.maxCalls !== null\n ) {\n const maxCalls = tool.maxCalls;\n if (\n typeof maxCalls !== \"number\" ||\n !Number.isInteger(maxCalls) ||\n maxCalls < 0\n ) {\n throw new Error(\n `maxCalls for tool \"${tool.name}\" must be a positive integer`,\n );\n }\n }\n}\n\n/**\n * Converts a props schema to a serialized JSON Schema format.\n * @param propsDefinition - Deprecated: legacy props definition (will log warning)\n * @param propsSchema - The props schema (Standard Schema or JSON Schema)\n * @param name - Component/tool name for error messages\n * @returns Serialized JSON Schema object\n */\nfunction getSerializedProps(\n propsDefinition: unknown,\n propsSchema: unknown,\n name: string,\n): Record<string, unknown> {\n if (propsDefinition) {\n console.warn(`propsDefinition is deprecated. Use propsSchema instead.`);\n return propsDefinition as Record<string, unknown>;\n }\n\n // Check for Standard Schema (Zod, Valibot, ArkType, etc.)\n if (isStandardSchema(propsSchema)) {\n try {\n return schemaToJsonSchema(propsSchema) as Record<string, unknown>;\n } catch (error) {\n console.error(\n `Error converting ${name} props schema to JSON Schema:`,\n error,\n );\n throw new Error(\n `Error converting ${name} props schema to JSON Schema: ${error}`,\n );\n }\n }\n\n // Check for JSON Schema\n if (looksLikeJSONSchema(propsSchema)) {\n return propsSchema as Record<string, unknown>;\n }\n\n throw new Error(`Invalid props schema for ${name}`);\n}\n\n/**\n * Validates a component and prepares its props for registration.\n * Throws an error if the component is invalid.\n * @param component - The component to validate and prepare\n * @returns Object containing the serialized props\n */\nexport function validateAndPrepareComponent(component: TamboComponent): {\n props: Record<string, unknown>;\n} {\n const { name, propsSchema, propsDefinition } = component;\n\n // Validate component name\n assertValidName(name, \"component\");\n\n // Validate that at least one props definition is provided\n if (!propsSchema && !propsDefinition) {\n throw new Error(\n `Component ${name} must have either propsSchema (recommended) or propsDefinition defined`,\n );\n }\n\n // Validate that only one props definition is provided\n if (propsSchema && propsDefinition) {\n throw new Error(\n `Component ${name} cannot have both propsSchema and propsDefinition defined. Use only one. We recommend using propsSchema.`,\n );\n }\n\n // Validate that the propsSchema does not include record types\n if (propsSchema) {\n assertNoRecordSchema(propsSchema, `propsSchema of component \"${name}\"`);\n }\n\n // Convert propsSchema to JSON Schema if it exists\n const props = getSerializedProps(propsDefinition, propsSchema, name);\n\n return { props };\n}\n\n/**\n * Validates a tool association between a component and tool.\n * Throws an error if the association is invalid.\n * @param componentName - The component name\n * @param toolName - The tool name\n * @param componentExists - Whether the component exists in the registry\n * @param toolExists - Whether the tool exists in the registry\n */\nexport function validateToolAssociation(\n componentName: string,\n toolName: string,\n componentExists: boolean,\n toolExists: boolean,\n): void {\n // Validate component and tool names\n assertValidName(componentName, \"component\");\n assertValidName(toolName, \"tool\");\n\n if (!componentExists) {\n throw new Error(`Component ${componentName} not found in registry`);\n }\n if (!toolExists) {\n throw new Error(`Tool ${toolName} not found in registry`);\n }\n}\n"]}
@@ -66,7 +66,7 @@ describe("resolveResourceContents", () => {
66
66
  const createMockInternalServer = (serverKey) => ({
67
67
  key: serverKey,
68
68
  serverKey,
69
- url: "https://api.tambo.ai/mcp",
69
+ url: "https://api.tambo.co/mcp",
70
70
  name: "__tambo_internal_mcp_server__",
71
71
  transport: mcp_client_1.MCPTransport.HTTP,
72
72
  serverType: mcp_constants_1.ServerType.TAMBO_INTERNAL,
@@ -1 +1 @@
1
- {"version":3,"file":"resource-content-resolver.test.js","sourceRoot":"","sources":["../../src/util/resource-content-resolver.test.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReadResourceResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { ServerType } from \"../mcp/mcp-constants\";\nimport type { McpServer } from \"../mcp/tambo-mcp-provider\";\nimport type { ResourceSource } from \"../model/resource-info\";\nimport { MCPTransport } from \"../mcp/mcp-client\";\n\nimport {\n resolveResourceContents,\n extractResourceUris,\n} from \"./resource-content-resolver\";\n\ndescribe(\"extractResourceUris\", () => {\n it(\"should extract a single resource URI\", () => {\n const text = \"Check @registry:file:///path/to/doc.txt\";\n const result = extractResourceUris(text);\n expect(result).toEqual([\"registry:file:///path/to/doc.txt\"]);\n });\n\n it(\"should extract multiple resource URIs\", () => {\n const text =\n \"Check @registry:file:///doc1.txt and @server-a:file:///doc2.txt\";\n const result = extractResourceUris(text);\n expect(result).toEqual([\n \"registry:file:///doc1.txt\",\n \"server-a:file:///doc2.txt\",\n ]);\n });\n\n it(\"should extract resource URIs with internal server prefix\", () => {\n const text = \"Check @tambo-abc123:tambo:test://resource/1\";\n const result = extractResourceUris(text);\n expect(result).toEqual([\"tambo-abc123:tambo:test://resource/1\"]);\n });\n\n it(\"should extract resource URIs with hyphens in server key\", () => {\n const text = \"@my-mcp-server:file:///path/file.txt\";\n const result = extractResourceUris(text);\n expect(result).toEqual([\"my-mcp-server:file:///path/file.txt\"]);\n });\n\n it(\"should return empty array for text without resource references\", () => {\n const text = \"Just some regular text without @mentions\";\n const result = extractResourceUris(text);\n expect(result).toEqual([]);\n });\n\n it(\"should not extract malformed references without colon\", () => {\n const text = \"@server-without-colon is not a resource\";\n const result = extractResourceUris(text);\n expect(result).toEqual([]);\n });\n\n it(\"should handle URIs with multiple colons\", () => {\n const text = \"@server:http://example.com:8080/path\";\n const result = extractResourceUris(text);\n expect(result).toEqual([\"server:http://example.com:8080/path\"]);\n });\n});\n\ndescribe(\"resolveResourceContents\", () => {\n const createMockConnectedServer = (\n serverKey: string,\n readResource: jest.Mock,\n serverType: ServerType = ServerType.BROWSER_SIDE,\n ): McpServer =>\n ({\n key: `mcp-${serverKey}`,\n serverKey,\n url: `https://${serverKey}.example.com`,\n name: `MCP Server ${serverKey}`,\n transport: MCPTransport.HTTP,\n serverType,\n client: {\n client: {\n readResource,\n listResources: jest.fn().mockResolvedValue({ resources: [] }),\n listTools: jest.fn().mockResolvedValue({ tools: [] }),\n listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),\n getPrompt: jest.fn().mockResolvedValue(null),\n callTool: jest.fn().mockResolvedValue(null),\n },\n close: jest.fn().mockResolvedValue(undefined),\n } as unknown as McpServer[\"client\"],\n }) as McpServer;\n\n const createMockInternalServer = (serverKey: string): McpServer =>\n ({\n key: serverKey,\n serverKey,\n url: \"https://api.tambo.ai/mcp\",\n name: \"__tambo_internal_mcp_server__\",\n transport: MCPTransport.HTTP,\n serverType: ServerType.TAMBO_INTERNAL,\n connectionError: undefined, // No client for internal servers\n }) as unknown as McpServer;\n\n const createMockResourceSource = (\n getResource: jest.Mock,\n ): ResourceSource => ({\n listResources: jest.fn().mockResolvedValue([]),\n getResource,\n });\n\n it(\"should resolve registry resources via resourceSource\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [\n {\n uri: \"file:///local/doc.txt\",\n mimeType: \"text/plain\",\n text: \"Registry document content\",\n },\n ],\n } satisfies ReadResourceResult);\n\n const resourceSource = createMockResourceSource(mockGetResource);\n\n const result = await resolveResourceContents(\n [\"registry:file:///local/doc.txt\"],\n [], // Registry resources don't need a server - they're handled directly\n resourceSource,\n );\n\n expect(mockGetResource).toHaveBeenCalledWith(\"file:///local/doc.txt\");\n expect(result.size).toBe(1);\n expect(result.get(\"registry:file:///local/doc.txt\")).toEqual({\n contents: [\n {\n uri: \"file:///local/doc.txt\",\n mimeType: \"text/plain\",\n text: \"Registry document content\",\n },\n ],\n });\n });\n\n it(\"should resolve client-side MCP resources via mcpServer\", async () => {\n const mockReadResource = jest.fn().mockResolvedValue({\n contents: [\n {\n uri: \"file:///mcp/file.txt\",\n mimeType: \"text/plain\",\n text: \"MCP server content\",\n },\n ],\n } satisfies ReadResourceResult);\n\n const mcpServer = createMockConnectedServer(\"linear\", mockReadResource);\n\n const result = await resolveResourceContents(\n [\"linear:file:///mcp/file.txt\"],\n [mcpServer],\n undefined,\n );\n\n expect(mockReadResource).toHaveBeenCalledWith({\n uri: \"file:///mcp/file.txt\",\n });\n expect(result.size).toBe(1);\n expect(result.get(\"linear:file:///mcp/file.txt\")).toEqual({\n contents: [\n {\n uri: \"file:///mcp/file.txt\",\n mimeType: \"text/plain\",\n text: \"MCP server content\",\n },\n ],\n });\n });\n\n it(\"should skip internal server resources (serverType: TAMBO_INTERNAL)\", async () => {\n const mockGetResource = jest.fn();\n const resourceSource = createMockResourceSource(mockGetResource);\n const internalServer = createMockInternalServer(\"tambo-abc123\");\n\n const result = await resolveResourceContents(\n [\"tambo-abc123:tambo:test://resource/1\"],\n [internalServer],\n resourceSource,\n );\n\n // Should not call getResource for internal server resources\n expect(mockGetResource).not.toHaveBeenCalled();\n // Should return empty map since internal resources are skipped\n expect(result.size).toBe(0);\n });\n\n it(\"should resolve multiple resources in parallel\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [{ uri: \"file:///doc.txt\", text: \"registry content\" }],\n });\n const mockReadResource = jest.fn().mockResolvedValue({\n contents: [{ uri: \"file:///mcp.txt\", text: \"mcp content\" }],\n });\n\n const resourceSource = createMockResourceSource(mockGetResource);\n const mcpServer = createMockConnectedServer(\"mcp-server\", mockReadResource);\n\n const result = await resolveResourceContents(\n [\"registry:file:///doc.txt\", \"mcp-server:file:///mcp.txt\"],\n [mcpServer], // Only MCP server needed - registry handled directly\n resourceSource,\n );\n\n expect(result.size).toBe(2);\n expect(result.has(\"registry:file:///doc.txt\")).toBe(true);\n expect(result.has(\"mcp-server:file:///mcp.txt\")).toBe(true);\n });\n\n it(\"should handle mixed internal and client-side resources\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [{ uri: \"file:///doc.txt\", text: \"registry content\" }],\n });\n const resourceSource = createMockResourceSource(mockGetResource);\n const internalServer = createMockInternalServer(\"tambo-abc123\");\n\n const result = await resolveResourceContents(\n [\n \"registry:file:///doc.txt\", // client-side registry\n \"tambo-abc123:tambo:test://resource/1\", // internal - should skip\n ],\n [internalServer], // Only internal server needed - registry handled directly\n resourceSource,\n );\n\n // Only the registry resource should be resolved\n expect(mockGetResource).toHaveBeenCalledTimes(1);\n expect(result.size).toBe(1);\n expect(result.has(\"registry:file:///doc.txt\")).toBe(true);\n expect(result.has(\"tambo-abc123:tambo:test://resource/1\")).toBe(false);\n });\n\n it(\"should gracefully handle registry resource fetch failure\", async () => {\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n const mockGetResource = jest\n .fn()\n .mockRejectedValue(new Error(\"Resource not found\"));\n const resourceSource = createMockResourceSource(mockGetResource);\n\n const result = await resolveResourceContents(\n [\"registry:file:///missing.txt\"],\n [], // Registry resources don't need a server\n resourceSource,\n );\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"Failed to fetch resource content for registry:file:///missing.txt:\",\n expect.any(Error),\n );\n expect(result.size).toBe(0);\n\n consoleSpy.mockRestore();\n });\n\n it(\"should gracefully handle MCP resource fetch failure\", async () => {\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n const mockReadResource = jest\n .fn()\n .mockRejectedValue(new Error(\"MCP server error\"));\n const mcpServer = createMockConnectedServer(\"linear\", mockReadResource);\n\n const result = await resolveResourceContents(\n [\"linear:file:///missing.txt\"],\n [mcpServer],\n undefined,\n );\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"Failed to fetch resource content for linear:file:///missing.txt:\",\n expect.any(Error),\n );\n expect(result.size).toBe(0);\n\n consoleSpy.mockRestore();\n });\n\n it(\"should warn when no resourceSource available for registry resource\", async () => {\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n\n const result = await resolveResourceContents(\n [\"registry:file:///doc.txt\"],\n [], // Registry resources don't need a server\n undefined, // no resourceSource\n );\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"No resource source available to resolve registry resource: registry:file:///doc.txt\",\n );\n expect(result.size).toBe(0);\n\n consoleSpy.mockRestore();\n });\n\n it(\"should warn when no server found for resource\", async () => {\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n\n const result = await resolveResourceContents(\n [\"unknown-server:file:///doc.txt\"],\n [], // no servers\n undefined,\n );\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"No server found for resource: unknown-server:file:///doc.txt\",\n );\n expect(result.size).toBe(0);\n\n consoleSpy.mockRestore();\n });\n\n it(\"should handle resource content with blob data\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [\n {\n uri: \"file:///image.png\",\n mimeType: \"image/png\",\n blob: \"base64encodeddata\",\n },\n ],\n } satisfies ReadResourceResult);\n\n const resourceSource = createMockResourceSource(mockGetResource);\n\n const result = await resolveResourceContents(\n [\"registry:file:///image.png\"],\n [], // Registry resources don't need a server\n resourceSource,\n );\n\n expect(result.get(\"registry:file:///image.png\")).toEqual({\n contents: [\n {\n uri: \"file:///image.png\",\n mimeType: \"image/png\",\n blob: \"base64encodeddata\",\n },\n ],\n });\n });\n\n it(\"should handle URIs with no colon (invalid format)\", async () => {\n const result = await resolveResourceContents(\n [\"invalid-uri-no-colon\"],\n [],\n undefined,\n );\n\n expect(result.size).toBe(0);\n });\n\n it(\"should handle null content from getResource\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue(null);\n const resourceSource = createMockResourceSource(mockGetResource);\n\n const result = await resolveResourceContents(\n [\"registry:file:///doc.txt\"],\n [], // Registry resources don't need a server\n resourceSource,\n );\n\n expect(mockGetResource).toHaveBeenCalled();\n expect(result.size).toBe(0);\n });\n\n it(\"should handle null content from readResource\", async () => {\n const mockReadResource = jest.fn().mockResolvedValue(null);\n const mcpServer = createMockConnectedServer(\"linear\", mockReadResource);\n\n const result = await resolveResourceContents(\n [\"linear:file:///doc.txt\"],\n [mcpServer],\n undefined,\n );\n\n expect(mockReadResource).toHaveBeenCalled();\n expect(result.size).toBe(0);\n });\n});\n"]}
1
+ {"version":3,"file":"resource-content-resolver.test.js","sourceRoot":"","sources":["../../src/util/resource-content-resolver.test.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReadResourceResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { ServerType } from \"../mcp/mcp-constants\";\nimport type { McpServer } from \"../mcp/tambo-mcp-provider\";\nimport type { ResourceSource } from \"../model/resource-info\";\nimport { MCPTransport } from \"../mcp/mcp-client\";\n\nimport {\n resolveResourceContents,\n extractResourceUris,\n} from \"./resource-content-resolver\";\n\ndescribe(\"extractResourceUris\", () => {\n it(\"should extract a single resource URI\", () => {\n const text = \"Check @registry:file:///path/to/doc.txt\";\n const result = extractResourceUris(text);\n expect(result).toEqual([\"registry:file:///path/to/doc.txt\"]);\n });\n\n it(\"should extract multiple resource URIs\", () => {\n const text =\n \"Check @registry:file:///doc1.txt and @server-a:file:///doc2.txt\";\n const result = extractResourceUris(text);\n expect(result).toEqual([\n \"registry:file:///doc1.txt\",\n \"server-a:file:///doc2.txt\",\n ]);\n });\n\n it(\"should extract resource URIs with internal server prefix\", () => {\n const text = \"Check @tambo-abc123:tambo:test://resource/1\";\n const result = extractResourceUris(text);\n expect(result).toEqual([\"tambo-abc123:tambo:test://resource/1\"]);\n });\n\n it(\"should extract resource URIs with hyphens in server key\", () => {\n const text = \"@my-mcp-server:file:///path/file.txt\";\n const result = extractResourceUris(text);\n expect(result).toEqual([\"my-mcp-server:file:///path/file.txt\"]);\n });\n\n it(\"should return empty array for text without resource references\", () => {\n const text = \"Just some regular text without @mentions\";\n const result = extractResourceUris(text);\n expect(result).toEqual([]);\n });\n\n it(\"should not extract malformed references without colon\", () => {\n const text = \"@server-without-colon is not a resource\";\n const result = extractResourceUris(text);\n expect(result).toEqual([]);\n });\n\n it(\"should handle URIs with multiple colons\", () => {\n const text = \"@server:http://example.com:8080/path\";\n const result = extractResourceUris(text);\n expect(result).toEqual([\"server:http://example.com:8080/path\"]);\n });\n});\n\ndescribe(\"resolveResourceContents\", () => {\n const createMockConnectedServer = (\n serverKey: string,\n readResource: jest.Mock,\n serverType: ServerType = ServerType.BROWSER_SIDE,\n ): McpServer =>\n ({\n key: `mcp-${serverKey}`,\n serverKey,\n url: `https://${serverKey}.example.com`,\n name: `MCP Server ${serverKey}`,\n transport: MCPTransport.HTTP,\n serverType,\n client: {\n client: {\n readResource,\n listResources: jest.fn().mockResolvedValue({ resources: [] }),\n listTools: jest.fn().mockResolvedValue({ tools: [] }),\n listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),\n getPrompt: jest.fn().mockResolvedValue(null),\n callTool: jest.fn().mockResolvedValue(null),\n },\n close: jest.fn().mockResolvedValue(undefined),\n } as unknown as McpServer[\"client\"],\n }) as McpServer;\n\n const createMockInternalServer = (serverKey: string): McpServer =>\n ({\n key: serverKey,\n serverKey,\n url: \"https://api.tambo.co/mcp\",\n name: \"__tambo_internal_mcp_server__\",\n transport: MCPTransport.HTTP,\n serverType: ServerType.TAMBO_INTERNAL,\n connectionError: undefined, // No client for internal servers\n }) as unknown as McpServer;\n\n const createMockResourceSource = (\n getResource: jest.Mock,\n ): ResourceSource => ({\n listResources: jest.fn().mockResolvedValue([]),\n getResource,\n });\n\n it(\"should resolve registry resources via resourceSource\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [\n {\n uri: \"file:///local/doc.txt\",\n mimeType: \"text/plain\",\n text: \"Registry document content\",\n },\n ],\n } satisfies ReadResourceResult);\n\n const resourceSource = createMockResourceSource(mockGetResource);\n\n const result = await resolveResourceContents(\n [\"registry:file:///local/doc.txt\"],\n [], // Registry resources don't need a server - they're handled directly\n resourceSource,\n );\n\n expect(mockGetResource).toHaveBeenCalledWith(\"file:///local/doc.txt\");\n expect(result.size).toBe(1);\n expect(result.get(\"registry:file:///local/doc.txt\")).toEqual({\n contents: [\n {\n uri: \"file:///local/doc.txt\",\n mimeType: \"text/plain\",\n text: \"Registry document content\",\n },\n ],\n });\n });\n\n it(\"should resolve client-side MCP resources via mcpServer\", async () => {\n const mockReadResource = jest.fn().mockResolvedValue({\n contents: [\n {\n uri: \"file:///mcp/file.txt\",\n mimeType: \"text/plain\",\n text: \"MCP server content\",\n },\n ],\n } satisfies ReadResourceResult);\n\n const mcpServer = createMockConnectedServer(\"linear\", mockReadResource);\n\n const result = await resolveResourceContents(\n [\"linear:file:///mcp/file.txt\"],\n [mcpServer],\n undefined,\n );\n\n expect(mockReadResource).toHaveBeenCalledWith({\n uri: \"file:///mcp/file.txt\",\n });\n expect(result.size).toBe(1);\n expect(result.get(\"linear:file:///mcp/file.txt\")).toEqual({\n contents: [\n {\n uri: \"file:///mcp/file.txt\",\n mimeType: \"text/plain\",\n text: \"MCP server content\",\n },\n ],\n });\n });\n\n it(\"should skip internal server resources (serverType: TAMBO_INTERNAL)\", async () => {\n const mockGetResource = jest.fn();\n const resourceSource = createMockResourceSource(mockGetResource);\n const internalServer = createMockInternalServer(\"tambo-abc123\");\n\n const result = await resolveResourceContents(\n [\"tambo-abc123:tambo:test://resource/1\"],\n [internalServer],\n resourceSource,\n );\n\n // Should not call getResource for internal server resources\n expect(mockGetResource).not.toHaveBeenCalled();\n // Should return empty map since internal resources are skipped\n expect(result.size).toBe(0);\n });\n\n it(\"should resolve multiple resources in parallel\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [{ uri: \"file:///doc.txt\", text: \"registry content\" }],\n });\n const mockReadResource = jest.fn().mockResolvedValue({\n contents: [{ uri: \"file:///mcp.txt\", text: \"mcp content\" }],\n });\n\n const resourceSource = createMockResourceSource(mockGetResource);\n const mcpServer = createMockConnectedServer(\"mcp-server\", mockReadResource);\n\n const result = await resolveResourceContents(\n [\"registry:file:///doc.txt\", \"mcp-server:file:///mcp.txt\"],\n [mcpServer], // Only MCP server needed - registry handled directly\n resourceSource,\n );\n\n expect(result.size).toBe(2);\n expect(result.has(\"registry:file:///doc.txt\")).toBe(true);\n expect(result.has(\"mcp-server:file:///mcp.txt\")).toBe(true);\n });\n\n it(\"should handle mixed internal and client-side resources\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [{ uri: \"file:///doc.txt\", text: \"registry content\" }],\n });\n const resourceSource = createMockResourceSource(mockGetResource);\n const internalServer = createMockInternalServer(\"tambo-abc123\");\n\n const result = await resolveResourceContents(\n [\n \"registry:file:///doc.txt\", // client-side registry\n \"tambo-abc123:tambo:test://resource/1\", // internal - should skip\n ],\n [internalServer], // Only internal server needed - registry handled directly\n resourceSource,\n );\n\n // Only the registry resource should be resolved\n expect(mockGetResource).toHaveBeenCalledTimes(1);\n expect(result.size).toBe(1);\n expect(result.has(\"registry:file:///doc.txt\")).toBe(true);\n expect(result.has(\"tambo-abc123:tambo:test://resource/1\")).toBe(false);\n });\n\n it(\"should gracefully handle registry resource fetch failure\", async () => {\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n const mockGetResource = jest\n .fn()\n .mockRejectedValue(new Error(\"Resource not found\"));\n const resourceSource = createMockResourceSource(mockGetResource);\n\n const result = await resolveResourceContents(\n [\"registry:file:///missing.txt\"],\n [], // Registry resources don't need a server\n resourceSource,\n );\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"Failed to fetch resource content for registry:file:///missing.txt:\",\n expect.any(Error),\n );\n expect(result.size).toBe(0);\n\n consoleSpy.mockRestore();\n });\n\n it(\"should gracefully handle MCP resource fetch failure\", async () => {\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n const mockReadResource = jest\n .fn()\n .mockRejectedValue(new Error(\"MCP server error\"));\n const mcpServer = createMockConnectedServer(\"linear\", mockReadResource);\n\n const result = await resolveResourceContents(\n [\"linear:file:///missing.txt\"],\n [mcpServer],\n undefined,\n );\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"Failed to fetch resource content for linear:file:///missing.txt:\",\n expect.any(Error),\n );\n expect(result.size).toBe(0);\n\n consoleSpy.mockRestore();\n });\n\n it(\"should warn when no resourceSource available for registry resource\", async () => {\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n\n const result = await resolveResourceContents(\n [\"registry:file:///doc.txt\"],\n [], // Registry resources don't need a server\n undefined, // no resourceSource\n );\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"No resource source available to resolve registry resource: registry:file:///doc.txt\",\n );\n expect(result.size).toBe(0);\n\n consoleSpy.mockRestore();\n });\n\n it(\"should warn when no server found for resource\", async () => {\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n\n const result = await resolveResourceContents(\n [\"unknown-server:file:///doc.txt\"],\n [], // no servers\n undefined,\n );\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"No server found for resource: unknown-server:file:///doc.txt\",\n );\n expect(result.size).toBe(0);\n\n consoleSpy.mockRestore();\n });\n\n it(\"should handle resource content with blob data\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue({\n contents: [\n {\n uri: \"file:///image.png\",\n mimeType: \"image/png\",\n blob: \"base64encodeddata\",\n },\n ],\n } satisfies ReadResourceResult);\n\n const resourceSource = createMockResourceSource(mockGetResource);\n\n const result = await resolveResourceContents(\n [\"registry:file:///image.png\"],\n [], // Registry resources don't need a server\n resourceSource,\n );\n\n expect(result.get(\"registry:file:///image.png\")).toEqual({\n contents: [\n {\n uri: \"file:///image.png\",\n mimeType: \"image/png\",\n blob: \"base64encodeddata\",\n },\n ],\n });\n });\n\n it(\"should handle URIs with no colon (invalid format)\", async () => {\n const result = await resolveResourceContents(\n [\"invalid-uri-no-colon\"],\n [],\n undefined,\n );\n\n expect(result.size).toBe(0);\n });\n\n it(\"should handle null content from getResource\", async () => {\n const mockGetResource = jest.fn().mockResolvedValue(null);\n const resourceSource = createMockResourceSource(mockGetResource);\n\n const result = await resolveResourceContents(\n [\"registry:file:///doc.txt\"],\n [], // Registry resources don't need a server\n resourceSource,\n );\n\n expect(mockGetResource).toHaveBeenCalled();\n expect(result.size).toBe(0);\n });\n\n it(\"should handle null content from readResource\", async () => {\n const mockReadResource = jest.fn().mockResolvedValue(null);\n const mcpServer = createMockConnectedServer(\"linear\", mockReadResource);\n\n const result = await resolveResourceContents(\n [\"linear:file:///doc.txt\"],\n [mcpServer],\n undefined,\n );\n\n expect(mockReadResource).toHaveBeenCalled();\n expect(result.size).toBe(0);\n });\n});\n"]}
@@ -0,0 +1,11 @@
1
+ import type { TamboV1AuthState } from "../types/auth";
2
+ /**
3
+ * Hook to compute the current authentication state for the v1 SDK.
4
+ *
5
+ * Reads from TamboClientContext and TamboV1ConfigContext to determine
6
+ * whether the SDK is ready to make API calls.
7
+ * @returns The current auth state as a discriminated union
8
+ * @throws {Error} If used outside TamboV1Provider
9
+ */
10
+ export declare function useTamboV1AuthState(): TamboV1AuthState;
11
+ //# sourceMappingURL=use-tambo-v1-auth-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-v1-auth-state.d.ts","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-auth-state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,CAqCtD"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ "use client";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.useTamboV1AuthState = useTamboV1AuthState;
5
+ const react_1 = require("react");
6
+ const tambo_client_provider_1 = require("../../providers/tambo-client-provider");
7
+ const tambo_v1_provider_1 = require("../providers/tambo-v1-provider");
8
+ /**
9
+ * Hook to compute the current authentication state for the v1 SDK.
10
+ *
11
+ * Reads from TamboClientContext and TamboV1ConfigContext to determine
12
+ * whether the SDK is ready to make API calls.
13
+ * @returns The current auth state as a discriminated union
14
+ * @throws {Error} If used outside TamboV1Provider
15
+ */
16
+ function useTamboV1AuthState() {
17
+ const clientContext = (0, react_1.useContext)(tambo_client_provider_1.TamboClientContext);
18
+ if (!clientContext) {
19
+ throw new Error("useTamboV1AuthState must be used within TamboV1Provider");
20
+ }
21
+ const config = (0, react_1.useContext)(tambo_v1_provider_1.TamboV1ConfigContext);
22
+ if (!config) {
23
+ throw new Error("useTamboV1AuthState must be used within TamboV1Provider");
24
+ }
25
+ const { tokenExchangeError, userToken, hasValidToken } = clientContext;
26
+ const { userKey } = config;
27
+ // Invalid: both userKey AND userToken provided
28
+ if (userKey && userToken) {
29
+ return { status: "invalid" };
30
+ }
31
+ // Identified via userKey
32
+ if (userKey) {
33
+ return { status: "identified", source: "userKey" };
34
+ }
35
+ // Token exchange scenarios
36
+ if (userToken) {
37
+ if (tokenExchangeError) {
38
+ return { status: "error", error: tokenExchangeError };
39
+ }
40
+ if (hasValidToken) {
41
+ return { status: "identified", source: "tokenExchange" };
42
+ }
43
+ return { status: "exchanging" };
44
+ }
45
+ // Neither provided
46
+ return { status: "unauthenticated" };
47
+ }
48
+ //# sourceMappingURL=use-tambo-v1-auth-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-v1-auth-state.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-auth-state.ts"],"names":[],"mappings":";AAAA,YAAY,CAAC;;AAeb,kDAqCC;AAlDD,iCAAmC;AACnC,iFAA2E;AAC3E,sEAAsE;AAGtE;;;;;;;GAOG;AACH,SAAgB,mBAAmB;IACjC,MAAM,aAAa,GAAG,IAAA,kBAAU,EAAC,0CAAkB,CAAC,CAAC;IACrD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,kBAAU,EAAC,wCAAoB,CAAC,CAAC;IAChD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,EAAE,kBAAkB,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,aAAa,CAAC;IACvE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAE3B,+CAA+C;IAC/C,IAAI,OAAO,IAAI,SAAS,EAAE,CAAC;QACzB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC/B,CAAC;IAED,yBAAyB;IACzB,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACrD,CAAC;IAED,2BAA2B;IAC3B,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,kBAAkB,EAAE,CAAC;YACvB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;QACxD,CAAC;QACD,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;QAC3D,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAClC,CAAC;IAED,mBAAmB;IACnB,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;AACvC,CAAC","sourcesContent":["\"use client\";\n\nimport { useContext } from \"react\";\nimport { TamboClientContext } from \"../../providers/tambo-client-provider\";\nimport { TamboV1ConfigContext } from \"../providers/tambo-v1-provider\";\nimport type { TamboV1AuthState } from \"../types/auth\";\n\n/**\n * Hook to compute the current authentication state for the v1 SDK.\n *\n * Reads from TamboClientContext and TamboV1ConfigContext to determine\n * whether the SDK is ready to make API calls.\n * @returns The current auth state as a discriminated union\n * @throws {Error} If used outside TamboV1Provider\n */\nexport function useTamboV1AuthState(): TamboV1AuthState {\n const clientContext = useContext(TamboClientContext);\n if (!clientContext) {\n throw new Error(\"useTamboV1AuthState must be used within TamboV1Provider\");\n }\n\n const config = useContext(TamboV1ConfigContext);\n if (!config) {\n throw new Error(\"useTamboV1AuthState must be used within TamboV1Provider\");\n }\n\n const { tokenExchangeError, userToken, hasValidToken } = clientContext;\n const { userKey } = config;\n\n // Invalid: both userKey AND userToken provided\n if (userKey && userToken) {\n return { status: \"invalid\" };\n }\n\n // Identified via userKey\n if (userKey) {\n return { status: \"identified\", source: \"userKey\" };\n }\n\n // Token exchange scenarios\n if (userToken) {\n if (tokenExchangeError) {\n return { status: \"error\", error: tokenExchangeError };\n }\n if (hasValidToken) {\n return { status: \"identified\", source: \"tokenExchange\" };\n }\n return { status: \"exchanging\" };\n }\n\n // Neither provided\n return { status: \"unauthenticated\" };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-tambo-v1-auth-state.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-v1-auth-state.test.d.ts","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-auth-state.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = require("@testing-library/react");
7
+ const react_2 = __importDefault(require("react"));
8
+ const react_query_1 = require("@tanstack/react-query");
9
+ const tambo_client_provider_1 = require("../../providers/tambo-client-provider");
10
+ const tambo_v1_provider_1 = require("../providers/tambo-v1-provider");
11
+ const use_tambo_v1_auth_state_1 = require("./use-tambo-v1-auth-state");
12
+ function createWrapper(clientOverrides, configOverrides = {}) {
13
+ const clientContext = {
14
+ client: {},
15
+ queryClient: new react_query_1.QueryClient(),
16
+ isUpdatingToken: false,
17
+ tokenExchangeError: null,
18
+ userToken: undefined,
19
+ hasValidToken: false,
20
+ ...clientOverrides,
21
+ };
22
+ const config = {
23
+ userKey: undefined,
24
+ ...configOverrides,
25
+ };
26
+ function Wrapper({ children }) {
27
+ return react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: clientContext }, react_2.default.createElement(tambo_v1_provider_1.TamboV1ConfigContext.Provider, { value: config }, children));
28
+ }
29
+ return Wrapper;
30
+ }
31
+ describe("useTamboV1AuthState", () => {
32
+ it("returns 'identified' with source 'userKey' when only userKey is provided", () => {
33
+ const { result } = (0, react_1.renderHook)(() => (0, use_tambo_v1_auth_state_1.useTamboV1AuthState)(), {
34
+ wrapper: createWrapper({}, { userKey: "user_123" }),
35
+ });
36
+ expect(result.current).toEqual({
37
+ status: "identified",
38
+ source: "userKey",
39
+ });
40
+ });
41
+ it("returns 'identified' with source 'tokenExchange' when token exchange succeeded", () => {
42
+ const { result } = (0, react_1.renderHook)(() => (0, use_tambo_v1_auth_state_1.useTamboV1AuthState)(), {
43
+ wrapper: createWrapper({
44
+ userToken: "oauth_token",
45
+ hasValidToken: true,
46
+ }),
47
+ });
48
+ expect(result.current).toEqual({
49
+ status: "identified",
50
+ source: "tokenExchange",
51
+ });
52
+ });
53
+ it("returns 'exchanging' when userToken provided but exchange not yet complete", () => {
54
+ const { result } = (0, react_1.renderHook)(() => (0, use_tambo_v1_auth_state_1.useTamboV1AuthState)(), {
55
+ wrapper: createWrapper({
56
+ userToken: "oauth_token",
57
+ hasValidToken: false,
58
+ }),
59
+ });
60
+ expect(result.current).toEqual({ status: "exchanging" });
61
+ });
62
+ it("returns 'error' when token exchange failed", () => {
63
+ const exchangeError = new Error("Token exchange failed");
64
+ const { result } = (0, react_1.renderHook)(() => (0, use_tambo_v1_auth_state_1.useTamboV1AuthState)(), {
65
+ wrapper: createWrapper({
66
+ userToken: "oauth_token",
67
+ tokenExchangeError: exchangeError,
68
+ hasValidToken: false,
69
+ }),
70
+ });
71
+ expect(result.current).toEqual({
72
+ status: "error",
73
+ error: exchangeError,
74
+ });
75
+ });
76
+ it("returns 'invalid' when both userKey and userToken are provided", () => {
77
+ const { result } = (0, react_1.renderHook)(() => (0, use_tambo_v1_auth_state_1.useTamboV1AuthState)(), {
78
+ wrapper: createWrapper({ userToken: "oauth_token" }, { userKey: "user_123" }),
79
+ });
80
+ expect(result.current).toEqual({ status: "invalid" });
81
+ });
82
+ it("returns 'unauthenticated' when neither userKey nor userToken is provided", () => {
83
+ const { result } = (0, react_1.renderHook)(() => (0, use_tambo_v1_auth_state_1.useTamboV1AuthState)(), {
84
+ wrapper: createWrapper({}),
85
+ });
86
+ expect(result.current).toEqual({ status: "unauthenticated" });
87
+ });
88
+ it("throws when used outside TamboClientContext", () => {
89
+ const wrapper = ({ children }) => react_2.default.createElement(tambo_v1_provider_1.TamboV1ConfigContext.Provider, { value: { userKey: "x" } }, children);
90
+ expect(() => (0, react_1.renderHook)(() => (0, use_tambo_v1_auth_state_1.useTamboV1AuthState)(), { wrapper })).toThrow("useTamboV1AuthState must be used within TamboV1Provider");
91
+ });
92
+ it("throws when used outside TamboV1ConfigContext", () => {
93
+ const clientContext = {
94
+ client: {},
95
+ queryClient: new react_query_1.QueryClient(),
96
+ isUpdatingToken: false,
97
+ tokenExchangeError: null,
98
+ userToken: undefined,
99
+ hasValidToken: false,
100
+ };
101
+ const wrapper = ({ children }) => react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: clientContext }, children);
102
+ expect(() => (0, react_1.renderHook)(() => (0, use_tambo_v1_auth_state_1.useTamboV1AuthState)(), { wrapper })).toThrow("useTamboV1AuthState must be used within TamboV1Provider");
103
+ });
104
+ });
105
+ //# sourceMappingURL=use-tambo-v1-auth-state.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-tambo-v1-auth-state.test.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-auth-state.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderHook } from \"@testing-library/react\";\nimport React from \"react\";\nimport { QueryClient } from \"@tanstack/react-query\";\nimport TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { TamboClientContext } from \"../../providers/tambo-client-provider\";\nimport type { TamboClientContextProps } from \"../../providers/tambo-client-provider\";\nimport {\n TamboV1ConfigContext,\n type TamboV1Config,\n} from \"../providers/tambo-v1-provider\";\nimport { useTamboV1AuthState } from \"./use-tambo-v1-auth-state\";\n\nfunction createWrapper(\n clientOverrides: Partial<TamboClientContextProps>,\n configOverrides: Partial<TamboV1Config> = {},\n) {\n const clientContext: TamboClientContextProps = {\n client: {} as TamboAI,\n queryClient: new QueryClient(),\n isUpdatingToken: false,\n tokenExchangeError: null,\n userToken: undefined,\n hasValidToken: false,\n ...clientOverrides,\n };\n\n const config: TamboV1Config = {\n userKey: undefined,\n ...configOverrides,\n };\n\n function Wrapper({ children }: { children: React.ReactNode }) {\n return React.createElement(\n TamboClientContext.Provider,\n { value: clientContext },\n React.createElement(\n TamboV1ConfigContext.Provider,\n { value: config },\n children,\n ),\n );\n }\n return Wrapper;\n}\n\ndescribe(\"useTamboV1AuthState\", () => {\n it(\"returns 'identified' with source 'userKey' when only userKey is provided\", () => {\n const { result } = renderHook(() => useTamboV1AuthState(), {\n wrapper: createWrapper({}, { userKey: \"user_123\" }),\n });\n\n expect(result.current).toEqual({\n status: \"identified\",\n source: \"userKey\",\n });\n });\n\n it(\"returns 'identified' with source 'tokenExchange' when token exchange succeeded\", () => {\n const { result } = renderHook(() => useTamboV1AuthState(), {\n wrapper: createWrapper({\n userToken: \"oauth_token\",\n hasValidToken: true,\n }),\n });\n\n expect(result.current).toEqual({\n status: \"identified\",\n source: \"tokenExchange\",\n });\n });\n\n it(\"returns 'exchanging' when userToken provided but exchange not yet complete\", () => {\n const { result } = renderHook(() => useTamboV1AuthState(), {\n wrapper: createWrapper({\n userToken: \"oauth_token\",\n hasValidToken: false,\n }),\n });\n\n expect(result.current).toEqual({ status: \"exchanging\" });\n });\n\n it(\"returns 'error' when token exchange failed\", () => {\n const exchangeError = new Error(\"Token exchange failed\");\n const { result } = renderHook(() => useTamboV1AuthState(), {\n wrapper: createWrapper({\n userToken: \"oauth_token\",\n tokenExchangeError: exchangeError,\n hasValidToken: false,\n }),\n });\n\n expect(result.current).toEqual({\n status: \"error\",\n error: exchangeError,\n });\n });\n\n it(\"returns 'invalid' when both userKey and userToken are provided\", () => {\n const { result } = renderHook(() => useTamboV1AuthState(), {\n wrapper: createWrapper(\n { userToken: \"oauth_token\" },\n { userKey: \"user_123\" },\n ),\n });\n\n expect(result.current).toEqual({ status: \"invalid\" });\n });\n\n it(\"returns 'unauthenticated' when neither userKey nor userToken is provided\", () => {\n const { result } = renderHook(() => useTamboV1AuthState(), {\n wrapper: createWrapper({}),\n });\n\n expect(result.current).toEqual({ status: \"unauthenticated\" });\n });\n\n it(\"throws when used outside TamboClientContext\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) =>\n React.createElement(\n TamboV1ConfigContext.Provider,\n { value: { userKey: \"x\" } },\n children,\n );\n\n expect(() => renderHook(() => useTamboV1AuthState(), { wrapper })).toThrow(\n \"useTamboV1AuthState must be used within TamboV1Provider\",\n );\n });\n\n it(\"throws when used outside TamboV1ConfigContext\", () => {\n const clientContext: TamboClientContextProps = {\n client: {} as TamboAI,\n queryClient: new QueryClient(),\n isUpdatingToken: false,\n tokenExchangeError: null,\n userToken: undefined,\n hasValidToken: false,\n };\n\n const wrapper = ({ children }: { children: React.ReactNode }) =>\n React.createElement(\n TamboClientContext.Provider,\n { value: clientContext },\n children,\n );\n\n expect(() => renderHook(() => useTamboV1AuthState(), { wrapper })).toThrow(\n \"useTamboV1AuthState must be used within TamboV1Provider\",\n );\n });\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"use-tambo-v1-component-state.d.ts","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-component-state.ts"],"names":[],"mappings":"AAoBA;;;GAGG;AACH,MAAM,MAAM,8BAA8B,CAAC,CAAC,IAAI;IAC9C,YAAY,EAAE,CAAC;IACf,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI;IAClD,IAAI,EAAE;QACJ,SAAS,EAAE,OAAO,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;QACpB,KAAK,EAAE,MAAM,IAAI,CAAC;KACnB;CACF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,SAAS,EACpD,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,CAAC,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,8BAA8B,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AACjD,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,8BAA8B,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"use-tambo-v1-component-state.d.ts","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-component-state.ts"],"names":[],"mappings":"AAqBA;;;GAGG;AACH,MAAM,MAAM,8BAA8B,CAAC,CAAC,IAAI;IAC9C,YAAY,EAAE,CAAC;IACf,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI;IAClD,IAAI,EAAE;QACJ,SAAS,EAAE,OAAO,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;QACpB,KAAK,EAAE,MAAM,IAAI,CAAC;KACnB;CACF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,SAAS,EACpD,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,CAAC,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,8BAA8B,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AACjD,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,CAAC,EACf,YAAY,CAAC,EAAE,MAAM,GACpB,8BAA8B,CAAC,CAAC,CAAC,CAAC"}
@@ -15,6 +15,7 @@ const react_1 = require("react");
15
15
  const use_debounce_1 = require("use-debounce");
16
16
  const fast_equals_1 = require("fast-equals");
17
17
  const tambo_client_provider_1 = require("../../providers/tambo-client-provider");
18
+ const tambo_interactable_provider_1 = require("../../providers/tambo-interactable-provider");
18
19
  const component_renderer_1 = require("../utils/component-renderer");
19
20
  const tambo_v1_stream_context_1 = require("../providers/tambo-v1-stream-context");
20
21
  const thread_utils_1 = require("../utils/thread-utils");
@@ -22,12 +23,21 @@ function useTamboV1ComponentState(keyName, initialValue, debounceTime = 500) {
22
23
  const client = (0, tambo_client_provider_1.useTamboClient)();
23
24
  const { componentId, threadId } = (0, component_renderer_1.useV1ComponentContent)();
24
25
  const streamState = (0, tambo_v1_stream_context_1.useStreamState)();
25
- // Find the component content to get server state (only search current thread)
26
- const componentContent = (0, thread_utils_1.findComponentContent)(streamState, threadId, componentId);
26
+ const { setInteractableState, getInteractableComponentState } = (0, tambo_interactable_provider_1.useTamboInteractable)();
27
+ // Interactable components use threadId="" (set by withTamboInteractable)
28
+ const isInteractable = threadId === "";
29
+ // Find the component content to get server state (only for v1-rendered components)
30
+ const componentContent = isInteractable
31
+ ? undefined
32
+ : (0, thread_utils_1.findComponentContent)(streamState, threadId, componentId);
27
33
  const serverState = componentContent?.state;
28
34
  const serverValue = serverState?.[keyName];
29
- // Local state - initialized from server state or initial value
30
- const [localState, setLocalState] = (0, react_1.useState)(() => serverValue ?? initialValue);
35
+ // For interactable components, read state from the interactable provider
36
+ const interactableState = isInteractable
37
+ ? getInteractableComponentState(componentId)?.[keyName]
38
+ : undefined;
39
+ // Local state - initialized from interactable state, server state, or initial value
40
+ const [localState, setLocalState] = (0, react_1.useState)(() => interactableState ?? serverValue ?? initialValue);
31
41
  // Track pending state and errors
32
42
  const [isPending, setIsPending] = (0, react_1.useState)(false);
33
43
  const [error, setError] = (0, react_1.useState)(null);
@@ -37,8 +47,10 @@ function useTamboV1ComponentState(keyName, initialValue, debounceTime = 500) {
37
47
  const hasPendingLocalChangeRef = (0, react_1.useRef)(false);
38
48
  // Track in-flight sync requests to avoid stale completions clearing pending state
39
49
  const syncSeqRef = (0, react_1.useRef)(0);
40
- // Debounced function to sync state to server
50
+ // Debounced function to sync state to server (only used for v1-rendered components)
41
51
  const syncToServer = (0, use_debounce_1.useDebouncedCallback)(async (newState) => {
52
+ if (isInteractable)
53
+ return;
42
54
  const seq = ++syncSeqRef.current;
43
55
  setIsPending(true);
44
56
  setError(null);
@@ -65,28 +77,59 @@ function useTamboV1ComponentState(keyName, initialValue, debounceTime = 500) {
65
77
  }
66
78
  }
67
79
  }, debounceTime);
68
- // setState function that updates local state and triggers debounced sync
80
+ // setState function that updates local state and syncs appropriately
69
81
  const setState = (0, react_1.useCallback)((newState) => {
70
82
  setLocalState((prev) => {
71
83
  const nextState = typeof newState === "function"
72
84
  ? newState(prev)
73
85
  : newState;
74
- // Mark that we have a pending local change
75
- hasPendingLocalChangeRef.current = true;
76
- // Trigger debounced sync to server
77
- void syncToServer(nextState);
86
+ if (isInteractable) {
87
+ // For interactable components, update the interactable provider's state
88
+ setInteractableState(componentId, keyName, nextState);
89
+ }
90
+ else {
91
+ // For v1-rendered components, trigger debounced sync to server
92
+ hasPendingLocalChangeRef.current = true;
93
+ void syncToServer(nextState);
94
+ }
78
95
  return nextState;
79
96
  });
80
- }, [syncToServer]);
81
- // Sync from server state when it changes (e.g., from streaming events)
97
+ }, [isInteractable, syncToServer, setInteractableState, componentId, keyName]);
98
+ // Set initial value in interactable state on mount if no existing state
99
+ const existingInteractableState = isInteractable
100
+ ? getInteractableComponentState(componentId)?.[keyName]
101
+ : undefined;
102
+ const shouldUpdateInteractableInitial = isInteractable &&
103
+ existingInteractableState === undefined &&
104
+ initialValue !== undefined;
82
105
  (0, react_1.useEffect)(() => {
83
- if (serverValue === undefined) {
106
+ if (!shouldUpdateInteractableInitial)
107
+ return;
108
+ setInteractableState(componentId, keyName, initialValue);
109
+ }, [
110
+ shouldUpdateInteractableInitial,
111
+ componentId,
112
+ keyName,
113
+ initialValue,
114
+ setInteractableState,
115
+ ]);
116
+ // Sync from interactable provider when state changes externally (e.g., from AI tool call)
117
+ (0, react_1.useEffect)(() => {
118
+ if (!isInteractable)
119
+ return;
120
+ if (interactableState === undefined)
121
+ return;
122
+ setLocalState((prev) => (0, fast_equals_1.deepEqual)(prev, interactableState) ? prev : interactableState);
123
+ }, [isInteractable, interactableState]);
124
+ // Sync from server state when it changes (e.g., from streaming events) - v1-rendered only
125
+ (0, react_1.useEffect)(() => {
126
+ if (isInteractable)
127
+ return;
128
+ if (serverValue === undefined)
84
129
  return;
85
- }
86
130
  // Don't overwrite local changes that haven't synced yet
87
- if (hasPendingLocalChangeRef.current) {
131
+ if (hasPendingLocalChangeRef.current)
88
132
  return;
89
- }
90
133
  // Only sync if the server value is different from what we last sent
91
134
  // This prevents overwriting local state with stale server values
92
135
  if (lastSentValueRef.current !== undefined &&
@@ -95,17 +138,21 @@ function useTamboV1ComponentState(keyName, initialValue, debounceTime = 500) {
95
138
  }
96
139
  // Use functional update to avoid localState in deps
97
140
  setLocalState((prev) => (0, fast_equals_1.deepEqual)(serverValue, prev) ? prev : serverValue);
98
- }, [serverValue]);
99
- // Flush pending updates on unmount
141
+ }, [isInteractable, serverValue]);
142
+ // Flush pending updates on unmount (only for v1-rendered components)
100
143
  (0, react_1.useEffect)(() => {
144
+ if (isInteractable)
145
+ return;
101
146
  return () => {
102
147
  void syncToServer.flush();
103
148
  };
104
- }, [syncToServer]);
105
- // Flush function for immediate sync
149
+ }, [isInteractable, syncToServer]);
150
+ // Flush function for immediate sync (noop for interactable)
106
151
  const flush = (0, react_1.useCallback)(() => {
152
+ if (isInteractable)
153
+ return;
107
154
  void syncToServer.flush();
108
- }, [syncToServer]);
155
+ }, [isInteractable, syncToServer]);
109
156
  return [localState, setState, { isPending, error, flush }];
110
157
  }
111
158
  //# sourceMappingURL=use-tambo-v1-component-state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-tambo-v1-component-state.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-component-state.ts"],"names":[],"mappings":";AAAA,YAAY,CAAC;;AAwEb,4DAiIC;AAvMD;;;;;;;;GAQG;AAEH,iCAAiE;AACjE,+CAAoD;AACpD,6CAAwC;AACxC,iFAAuE;AACvE,oEAAoE;AACpE,kFAAsE;AACtE,wDAA6D;AAsD7D,SAAgB,wBAAwB,CACtC,OAAe,EACf,YAAgB,EAChB,YAAY,GAAG,GAAG;IAElB,MAAM,MAAM,GAAG,IAAA,sCAAc,GAAE,CAAC;IAChC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,IAAA,0CAAqB,GAAE,CAAC;IAC1D,MAAM,WAAW,GAAG,IAAA,wCAAc,GAAE,CAAC;IAErC,8EAA8E;IAC9E,MAAM,gBAAgB,GAAG,IAAA,mCAAoB,EAC3C,WAAW,EACX,QAAQ,EACR,WAAW,CACZ,CAAC;IACF,MAAM,WAAW,GAAG,gBAAgB,EAAE,KAEzB,CAAC;IACd,MAAM,WAAW,GAAG,WAAW,EAAE,CAAC,OAAO,CAAkB,CAAC;IAE5D,+DAA+D;IAC/D,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,IAAA,gBAAQ,EAC1C,GAAG,EAAE,CAAC,WAAW,IAAK,YAAkB,CACzC,CAAC;IAEF,iCAAiC;IACjC,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,IAAA,gBAAQ,EAAC,KAAK,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAA,gBAAQ,EAAe,IAAI,CAAC,CAAC;IAEvD,4EAA4E;IAC5E,MAAM,gBAAgB,GAAG,IAAA,cAAM,EAAgB,SAAS,CAAC,CAAC;IAE1D,sEAAsE;IACtE,MAAM,wBAAwB,GAAG,IAAA,cAAM,EAAC,KAAK,CAAC,CAAC;IAE/C,kFAAkF;IAClF,MAAM,UAAU,GAAG,IAAA,cAAM,EAAC,CAAC,CAAC,CAAC;IAE7B,6CAA6C;IAC7C,MAAM,YAAY,GAAG,IAAA,mCAAoB,EAAC,KAAK,EAAE,QAAW,EAAE,EAAE;QAC9D,MAAM,GAAG,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC;QACjC,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,gBAAgB,CAAC,OAAO,GAAG,QAAQ,CAAC;QAEpC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,WAAW,EAAE;gBAClD,QAAQ;gBACR,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE;aAC/B,CAAC,CAAC;YACH,2CAA2C;YAC3C,wBAAwB,CAAC,OAAO,GAAG,KAAK,CAAC;QAC3C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,6DAA6D;YAC7D,wBAAwB,CAAC,OAAO,GAAG,KAAK,CAAC;YACzC,MAAM,SAAS,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YACtE,QAAQ,CAAC,SAAS,CAAC,CAAC;YACpB,OAAO,CAAC,KAAK,CACX,uDAAuD,WAAW,GAAG,EACrE,SAAS,CACV,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,0DAA0D;YAC1D,IAAI,GAAG,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;gBAC/B,YAAY,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC,EAAE,YAAY,CAAC,CAAC;IAEjB,yEAAyE;IACzE,MAAM,QAAQ,GAAG,IAAA,mBAAW,EAC1B,CAAC,QAA8B,EAAE,EAAE;QACjC,aAAa,CAAC,CAAC,IAAI,EAAE,EAAE;YACrB,MAAM,SAAS,GACb,OAAO,QAAQ,KAAK,UAAU;gBAC5B,CAAC,CAAE,QAA2B,CAAC,IAAI,CAAC;gBACpC,CAAC,CAAC,QAAQ,CAAC;YAEf,2CAA2C;YAC3C,wBAAwB,CAAC,OAAO,GAAG,IAAI,CAAC;YAExC,mCAAmC;YACnC,KAAK,YAAY,CAAC,SAAS,CAAC,CAAC;YAE7B,OAAO,SAAS,CAAC;QACnB,CAAC,CAAC,CAAC;IACL,CAAC,EACD,CAAC,YAAY,CAAC,CACf,CAAC;IAEF,uEAAuE;IACvE,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,wDAAwD;QACxD,IAAI,wBAAwB,CAAC,OAAO,EAAE,CAAC;YACrC,OAAO;QACT,CAAC;QAED,oEAAoE;QACpE,iEAAiE;QACjE,IACE,gBAAgB,CAAC,OAAO,KAAK,SAAS;YACtC,IAAA,uBAAS,EAAC,WAAW,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAChD,CAAC;YACD,OAAO;QACT,CAAC;QAED,oDAAoD;QACpD,aAAa,CAAC,CAAC,IAAI,EAAE,EAAE,CACrB,IAAA,uBAAS,EAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAClD,CAAC;IACJ,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,mCAAmC;IACnC,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,OAAO,GAAG,EAAE;YACV,KAAK,YAAY,CAAC,KAAK,EAAE,CAAC;QAC5B,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,oCAAoC;IACpC,MAAM,KAAK,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;QAC7B,KAAK,YAAY,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,OAAO,CAAC,UAAU,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAC7D,CAAC","sourcesContent":["\"use client\";\n\n/**\n * useTamboV1ComponentState - Component State Hook for v1 API\n *\n * Provides bidirectional state synchronization between React components\n * and the Tambo backend. State changes are debounced before syncing to\n * the server, and server state updates are reflected in the component.\n *\n * Must be used within a component rendered via the component renderer.\n */\n\nimport { useCallback, useEffect, useState, useRef } from \"react\";\nimport { useDebouncedCallback } from \"use-debounce\";\nimport { deepEqual } from \"fast-equals\";\nimport { useTamboClient } from \"../../providers/tambo-client-provider\";\nimport { useV1ComponentContent } from \"../utils/component-renderer\";\nimport { useStreamState } from \"../providers/tambo-v1-stream-context\";\nimport { findComponentContent } from \"../utils/thread-utils\";\n\n/**\n * Return type for useTamboV1ComponentState hook.\n * Similar to useState but with additional metadata.\n */\nexport type UseTamboV1ComponentStateReturn<S> = [\n currentState: S,\n setState: (newState: S | ((prev: S) => S)) => void,\n meta: {\n isPending: boolean;\n error: Error | null;\n flush: () => void;\n },\n];\n\n/**\n * Hook for managing component state with bidirectional server sync.\n *\n * This hook acts like useState but automatically syncs state changes\n * to the Tambo backend. Server-side state updates are also reflected\n * in the component.\n *\n * Must be used within a component rendered via the component renderer.\n * @param keyName - The unique key to identify this state value within the component's state\n * @param initialValue - Initial value for the state (used if no server state exists)\n * @param debounceTime - Debounce time in milliseconds (default: 500ms)\n * @returns Tuple of [currentState, setState, meta]\n * @example\n * ```tsx\n * function Counter() {\n * const [count, setCount, { isPending }] = useTamboV1ComponentState('count', 0);\n *\n * return (\n * <div>\n * <span>{count}</span>\n * <button onClick={() => setCount(c => c + 1)} disabled={isPending}>\n * Increment\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useTamboV1ComponentState<S = undefined>(\n keyName: string,\n initialValue?: S,\n debounceTime?: number,\n): UseTamboV1ComponentStateReturn<S | undefined>;\nexport function useTamboV1ComponentState<S>(\n keyName: string,\n initialValue: S,\n debounceTime?: number,\n): UseTamboV1ComponentStateReturn<S>;\nexport function useTamboV1ComponentState<S>(\n keyName: string,\n initialValue?: S,\n debounceTime = 500,\n): UseTamboV1ComponentStateReturn<S> {\n const client = useTamboClient();\n const { componentId, threadId } = useV1ComponentContent();\n const streamState = useStreamState();\n\n // Find the component content to get server state (only search current thread)\n const componentContent = findComponentContent(\n streamState,\n threadId,\n componentId,\n );\n const serverState = componentContent?.state as\n | Record<string, unknown>\n | undefined;\n const serverValue = serverState?.[keyName] as S | undefined;\n\n // Local state - initialized from server state or initial value\n const [localState, setLocalState] = useState<S>(\n () => serverValue ?? (initialValue as S),\n );\n\n // Track pending state and errors\n const [isPending, setIsPending] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Track the last value we sent to avoid overwriting with stale server state\n const lastSentValueRef = useRef<S | undefined>(undefined);\n\n // Track whether there's a pending local change that hasn't synced yet\n const hasPendingLocalChangeRef = useRef(false);\n\n // Track in-flight sync requests to avoid stale completions clearing pending state\n const syncSeqRef = useRef(0);\n\n // Debounced function to sync state to server\n const syncToServer = useDebouncedCallback(async (newState: S) => {\n const seq = ++syncSeqRef.current;\n setIsPending(true);\n setError(null);\n lastSentValueRef.current = newState;\n\n try {\n await client.threads.state.updateState(componentId, {\n threadId,\n state: { [keyName]: newState },\n });\n // Clear pending flag after successful sync\n hasPendingLocalChangeRef.current = false;\n } catch (err) {\n // Clear pending flag on error to allow server reconciliation\n hasPendingLocalChangeRef.current = false;\n const syncError = err instanceof Error ? err : new Error(String(err));\n setError(syncError);\n console.error(\n `[useTamboV1ComponentState] Failed to sync state for ${componentId}:`,\n syncError,\n );\n } finally {\n // Only clear isPending if this is the most recent request\n if (seq === syncSeqRef.current) {\n setIsPending(false);\n }\n }\n }, debounceTime);\n\n // setState function that updates local state and triggers debounced sync\n const setState = useCallback(\n (newState: S | ((prev: S) => S)) => {\n setLocalState((prev) => {\n const nextState =\n typeof newState === \"function\"\n ? (newState as (prev: S) => S)(prev)\n : newState;\n\n // Mark that we have a pending local change\n hasPendingLocalChangeRef.current = true;\n\n // Trigger debounced sync to server\n void syncToServer(nextState);\n\n return nextState;\n });\n },\n [syncToServer],\n );\n\n // Sync from server state when it changes (e.g., from streaming events)\n useEffect(() => {\n if (serverValue === undefined) {\n return;\n }\n\n // Don't overwrite local changes that haven't synced yet\n if (hasPendingLocalChangeRef.current) {\n return;\n }\n\n // Only sync if the server value is different from what we last sent\n // This prevents overwriting local state with stale server values\n if (\n lastSentValueRef.current !== undefined &&\n deepEqual(serverValue, lastSentValueRef.current)\n ) {\n return;\n }\n\n // Use functional update to avoid localState in deps\n setLocalState((prev) =>\n deepEqual(serverValue, prev) ? prev : serverValue,\n );\n }, [serverValue]);\n\n // Flush pending updates on unmount\n useEffect(() => {\n return () => {\n void syncToServer.flush();\n };\n }, [syncToServer]);\n\n // Flush function for immediate sync\n const flush = useCallback(() => {\n void syncToServer.flush();\n }, [syncToServer]);\n\n return [localState, setState, { isPending, error, flush }];\n}\n"]}
1
+ {"version":3,"file":"use-tambo-v1-component-state.js","sourceRoot":"","sources":["../../../src/v1/hooks/use-tambo-v1-component-state.ts"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * useTamboV1ComponentState - Component State Hook for v1 API\n *\n * Provides bidirectional state synchronization between React components\n * and the Tambo backend. State changes are debounced before syncing to\n * the server, and server state updates are reflected in the component.\n *\n * Must be used within a component rendered via the component renderer.\n */\n\nimport { useCallback, useEffect, useState, useRef } from \"react\";\nimport { useDebouncedCallback } from \"use-debounce\";\nimport { deepEqual } from \"fast-equals\";\nimport { useTamboClient } from \"../../providers/tambo-client-provider\";\nimport { useTamboInteractable } from \"../../providers/tambo-interactable-provider\";\nimport { useV1ComponentContent } from \"../utils/component-renderer\";\nimport { useStreamState } from \"../providers/tambo-v1-stream-context\";\nimport { findComponentContent } from \"../utils/thread-utils\";\n\n/**\n * Return type for useTamboV1ComponentState hook.\n * Similar to useState but with additional metadata.\n */\nexport type UseTamboV1ComponentStateReturn<S> = [\n currentState: S,\n setState: (newState: S | ((prev: S) => S)) => void,\n meta: {\n isPending: boolean;\n error: Error | null;\n flush: () => void;\n },\n];\n\n/**\n * Hook for managing component state with bidirectional server sync.\n *\n * This hook acts like useState but automatically syncs state changes\n * to the Tambo backend. Server-side state updates are also reflected\n * in the component.\n *\n * Must be used within a component rendered via the component renderer.\n * @param keyName - The unique key to identify this state value within the component's state\n * @param initialValue - Initial value for the state (used if no server state exists)\n * @param debounceTime - Debounce time in milliseconds (default: 500ms)\n * @returns Tuple of [currentState, setState, meta]\n * @example\n * ```tsx\n * function Counter() {\n * const [count, setCount, { isPending }] = useTamboV1ComponentState('count', 0);\n *\n * return (\n * <div>\n * <span>{count}</span>\n * <button onClick={() => setCount(c => c + 1)} disabled={isPending}>\n * Increment\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useTamboV1ComponentState<S = undefined>(\n keyName: string,\n initialValue?: S,\n debounceTime?: number,\n): UseTamboV1ComponentStateReturn<S | undefined>;\nexport function useTamboV1ComponentState<S>(\n keyName: string,\n initialValue: S,\n debounceTime?: number,\n): UseTamboV1ComponentStateReturn<S>;\nexport function useTamboV1ComponentState<S>(\n keyName: string,\n initialValue?: S,\n debounceTime = 500,\n): UseTamboV1ComponentStateReturn<S> {\n const client = useTamboClient();\n const { componentId, threadId } = useV1ComponentContent();\n const streamState = useStreamState();\n const { setInteractableState, getInteractableComponentState } =\n useTamboInteractable();\n\n // Interactable components use threadId=\"\" (set by withTamboInteractable)\n const isInteractable = threadId === \"\";\n\n // Find the component content to get server state (only for v1-rendered components)\n const componentContent = isInteractable\n ? undefined\n : findComponentContent(streamState, threadId, componentId);\n const serverState = componentContent?.state as\n | Record<string, unknown>\n | undefined;\n const serverValue = serverState?.[keyName] as S | undefined;\n\n // For interactable components, read state from the interactable provider\n const interactableState = isInteractable\n ? (getInteractableComponentState(componentId)?.[keyName] as S | undefined)\n : undefined;\n\n // Local state - initialized from interactable state, server state, or initial value\n const [localState, setLocalState] = useState<S>(\n () => interactableState ?? serverValue ?? (initialValue as S),\n );\n\n // Track pending state and errors\n const [isPending, setIsPending] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Track the last value we sent to avoid overwriting with stale server state\n const lastSentValueRef = useRef<S | undefined>(undefined);\n\n // Track whether there's a pending local change that hasn't synced yet\n const hasPendingLocalChangeRef = useRef(false);\n\n // Track in-flight sync requests to avoid stale completions clearing pending state\n const syncSeqRef = useRef(0);\n\n // Debounced function to sync state to server (only used for v1-rendered components)\n const syncToServer = useDebouncedCallback(async (newState: S) => {\n if (isInteractable) return;\n\n const seq = ++syncSeqRef.current;\n setIsPending(true);\n setError(null);\n lastSentValueRef.current = newState;\n\n try {\n await client.threads.state.updateState(componentId, {\n threadId,\n state: { [keyName]: newState },\n });\n // Clear pending flag after successful sync\n hasPendingLocalChangeRef.current = false;\n } catch (err) {\n // Clear pending flag on error to allow server reconciliation\n hasPendingLocalChangeRef.current = false;\n const syncError = err instanceof Error ? err : new Error(String(err));\n setError(syncError);\n console.error(\n `[useTamboV1ComponentState] Failed to sync state for ${componentId}:`,\n syncError,\n );\n } finally {\n // Only clear isPending if this is the most recent request\n if (seq === syncSeqRef.current) {\n setIsPending(false);\n }\n }\n }, debounceTime);\n\n // setState function that updates local state and syncs appropriately\n const setState = useCallback(\n (newState: S | ((prev: S) => S)) => {\n setLocalState((prev) => {\n const nextState =\n typeof newState === \"function\"\n ? (newState as (prev: S) => S)(prev)\n : newState;\n\n if (isInteractable) {\n // For interactable components, update the interactable provider's state\n setInteractableState(componentId, keyName, nextState);\n } else {\n // For v1-rendered components, trigger debounced sync to server\n hasPendingLocalChangeRef.current = true;\n void syncToServer(nextState);\n }\n\n return nextState;\n });\n },\n [isInteractable, syncToServer, setInteractableState, componentId, keyName],\n );\n\n // Set initial value in interactable state on mount if no existing state\n const existingInteractableState = isInteractable\n ? getInteractableComponentState(componentId)?.[keyName]\n : undefined;\n const shouldUpdateInteractableInitial =\n isInteractable &&\n existingInteractableState === undefined &&\n initialValue !== undefined;\n\n useEffect(() => {\n if (!shouldUpdateInteractableInitial) return;\n setInteractableState(componentId, keyName, initialValue!);\n }, [\n shouldUpdateInteractableInitial,\n componentId,\n keyName,\n initialValue,\n setInteractableState,\n ]);\n\n // Sync from interactable provider when state changes externally (e.g., from AI tool call)\n useEffect(() => {\n if (!isInteractable) return;\n if (interactableState === undefined) return;\n setLocalState((prev) =>\n deepEqual(prev, interactableState) ? prev : (interactableState as S),\n );\n }, [isInteractable, interactableState]);\n\n // Sync from server state when it changes (e.g., from streaming events) - v1-rendered only\n useEffect(() => {\n if (isInteractable) return;\n if (serverValue === undefined) return;\n\n // Don't overwrite local changes that haven't synced yet\n if (hasPendingLocalChangeRef.current) return;\n\n // Only sync if the server value is different from what we last sent\n // This prevents overwriting local state with stale server values\n if (\n lastSentValueRef.current !== undefined &&\n deepEqual(serverValue, lastSentValueRef.current)\n ) {\n return;\n }\n\n // Use functional update to avoid localState in deps\n setLocalState((prev) =>\n deepEqual(serverValue, prev) ? prev : serverValue,\n );\n }, [isInteractable, serverValue]);\n\n // Flush pending updates on unmount (only for v1-rendered components)\n useEffect(() => {\n if (isInteractable) return;\n return () => {\n void syncToServer.flush();\n };\n }, [isInteractable, syncToServer]);\n\n // Flush function for immediate sync (noop for interactable)\n const flush = useCallback(() => {\n if (isInteractable) return;\n void syncToServer.flush();\n }, [isInteractable, syncToServer]);\n\n return [localState, setState, { isPending, error, flush }];\n}\n"]}