@cossistant/react 0.0.1

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 (372) hide show
  1. package/_virtual/rolldown_runtime.js +13 -0
  2. package/conversation.d.ts +312 -0
  3. package/conversation.d.ts.map +1 -0
  4. package/hooks/index.d.ts +23 -0
  5. package/hooks/index.js +24 -0
  6. package/hooks/private/store/use-conversations-store.d.ts +13 -0
  7. package/hooks/private/store/use-conversations-store.d.ts.map +1 -0
  8. package/hooks/private/store/use-conversations-store.js +34 -0
  9. package/hooks/private/store/use-conversations-store.js.map +1 -0
  10. package/hooks/private/store/use-store-selector.d.ts +10 -0
  11. package/hooks/private/store/use-store-selector.d.ts.map +1 -0
  12. package/hooks/private/store/use-store-selector.js +17 -0
  13. package/hooks/private/store/use-store-selector.js.map +1 -0
  14. package/hooks/private/store/use-website-store.d.ts +18 -0
  15. package/hooks/private/store/use-website-store.d.ts.map +1 -0
  16. package/hooks/private/store/use-website-store.js +39 -0
  17. package/hooks/private/store/use-website-store.js.map +1 -0
  18. package/hooks/private/use-client-query.d.ts +25 -0
  19. package/hooks/private/use-client-query.d.ts.map +1 -0
  20. package/hooks/private/use-client-query.js +122 -0
  21. package/hooks/private/use-client-query.js.map +1 -0
  22. package/hooks/private/use-default-messages.d.ts +18 -0
  23. package/hooks/private/use-default-messages.d.ts.map +1 -0
  24. package/hooks/private/use-default-messages.js +45 -0
  25. package/hooks/private/use-default-messages.js.map +1 -0
  26. package/hooks/private/use-grouped-messages.d.ts +54 -0
  27. package/hooks/private/use-grouped-messages.d.ts.map +1 -0
  28. package/hooks/private/use-grouped-messages.js +157 -0
  29. package/hooks/private/use-grouped-messages.js.map +1 -0
  30. package/hooks/private/use-multimodal-input.d.ts +40 -0
  31. package/hooks/private/use-multimodal-input.d.ts.map +1 -0
  32. package/hooks/private/use-multimodal-input.js +129 -0
  33. package/hooks/private/use-multimodal-input.js.map +1 -0
  34. package/hooks/private/use-rest-client.d.ts +17 -0
  35. package/hooks/private/use-rest-client.d.ts.map +1 -0
  36. package/hooks/private/use-rest-client.js +41 -0
  37. package/hooks/private/use-rest-client.js.map +1 -0
  38. package/hooks/private/use-visitor-typing-reporter.d.ts +19 -0
  39. package/hooks/private/use-visitor-typing-reporter.d.ts.map +1 -0
  40. package/hooks/private/use-visitor-typing-reporter.js +140 -0
  41. package/hooks/private/use-visitor-typing-reporter.js.map +1 -0
  42. package/hooks/use-composer-refocus.d.ts +20 -0
  43. package/hooks/use-composer-refocus.d.ts.map +1 -0
  44. package/hooks/use-composer-refocus.js +32 -0
  45. package/hooks/use-composer-refocus.js.map +1 -0
  46. package/hooks/use-conversation-auto-seen.d.ts +54 -0
  47. package/hooks/use-conversation-auto-seen.d.ts.map +1 -0
  48. package/hooks/use-conversation-auto-seen.js +106 -0
  49. package/hooks/use-conversation-auto-seen.js.map +1 -0
  50. package/hooks/use-conversation-history-page.d.ts +86 -0
  51. package/hooks/use-conversation-history-page.d.ts.map +1 -0
  52. package/hooks/use-conversation-history-page.js +97 -0
  53. package/hooks/use-conversation-history-page.js.map +1 -0
  54. package/hooks/use-conversation-lifecycle.d.ts +80 -0
  55. package/hooks/use-conversation-lifecycle.d.ts.map +1 -0
  56. package/hooks/use-conversation-lifecycle.js +54 -0
  57. package/hooks/use-conversation-lifecycle.js.map +1 -0
  58. package/hooks/use-conversation-page.d.ts +82 -0
  59. package/hooks/use-conversation-page.d.ts.map +1 -0
  60. package/hooks/use-conversation-page.js +138 -0
  61. package/hooks/use-conversation-page.js.map +1 -0
  62. package/hooks/use-conversation-seen.d.ts +17 -0
  63. package/hooks/use-conversation-seen.d.ts.map +1 -0
  64. package/hooks/use-conversation-seen.js +58 -0
  65. package/hooks/use-conversation-seen.js.map +1 -0
  66. package/hooks/use-conversation-timeline-items.d.ts +21 -0
  67. package/hooks/use-conversation-timeline-items.d.ts.map +1 -0
  68. package/hooks/use-conversation-timeline-items.js +87 -0
  69. package/hooks/use-conversation-timeline-items.js.map +1 -0
  70. package/hooks/use-conversation-typing.d.ts +13 -0
  71. package/hooks/use-conversation-typing.d.ts.map +1 -0
  72. package/hooks/use-conversation-typing.js +34 -0
  73. package/hooks/use-conversation-typing.js.map +1 -0
  74. package/hooks/use-conversation.d.ts +18 -0
  75. package/hooks/use-conversation.d.ts.map +1 -0
  76. package/hooks/use-conversation.js +44 -0
  77. package/hooks/use-conversation.js.map +1 -0
  78. package/hooks/use-conversations.d.ts +20 -0
  79. package/hooks/use-conversations.d.ts.map +1 -0
  80. package/hooks/use-conversations.js +68 -0
  81. package/hooks/use-conversations.js.map +1 -0
  82. package/hooks/use-create-conversation.d.ts +30 -0
  83. package/hooks/use-create-conversation.d.ts.map +1 -0
  84. package/hooks/use-create-conversation.js +67 -0
  85. package/hooks/use-create-conversation.js.map +1 -0
  86. package/hooks/use-home-page.d.ts +82 -0
  87. package/hooks/use-home-page.d.ts.map +1 -0
  88. package/hooks/use-home-page.js +89 -0
  89. package/hooks/use-home-page.js.map +1 -0
  90. package/hooks/use-message-composer.d.ts +88 -0
  91. package/hooks/use-message-composer.d.ts.map +1 -0
  92. package/hooks/use-message-composer.js +94 -0
  93. package/hooks/use-message-composer.js.map +1 -0
  94. package/hooks/use-realtime-support.d.ts +25 -0
  95. package/hooks/use-realtime-support.d.ts.map +1 -0
  96. package/hooks/use-realtime-support.js +29 -0
  97. package/hooks/use-realtime-support.js.map +1 -0
  98. package/hooks/use-send-message.d.ts +34 -0
  99. package/hooks/use-send-message.d.ts.map +1 -0
  100. package/hooks/use-send-message.js +118 -0
  101. package/hooks/use-send-message.js.map +1 -0
  102. package/hooks/use-visitor.d.ts +28 -0
  103. package/hooks/use-visitor.d.ts.map +1 -0
  104. package/hooks/use-visitor.js +59 -0
  105. package/hooks/use-visitor.js.map +1 -0
  106. package/hooks/use-window-visibility-focus.d.ts +9 -0
  107. package/hooks/use-window-visibility-focus.d.ts.map +1 -0
  108. package/hooks/use-window-visibility-focus.js +53 -0
  109. package/hooks/use-window-visibility-focus.js.map +1 -0
  110. package/identify-visitor.d.ts +18 -0
  111. package/identify-visitor.d.ts.map +1 -0
  112. package/identify-visitor.js +26 -0
  113. package/identify-visitor.js.map +1 -0
  114. package/index.d.ts +38 -0
  115. package/index.js +38 -0
  116. package/package.json +121 -0
  117. package/primitives/avatar/avatar.d.ts +31 -0
  118. package/primitives/avatar/avatar.d.ts.map +1 -0
  119. package/primitives/avatar/avatar.js +49 -0
  120. package/primitives/avatar/avatar.js.map +1 -0
  121. package/primitives/avatar/fallback.d.ts +24 -0
  122. package/primitives/avatar/fallback.d.ts.map +1 -0
  123. package/primitives/avatar/fallback.js +57 -0
  124. package/primitives/avatar/fallback.js.map +1 -0
  125. package/primitives/avatar/image.d.ts +27 -0
  126. package/primitives/avatar/image.d.ts.map +1 -0
  127. package/primitives/avatar/image.js +58 -0
  128. package/primitives/avatar/image.js.map +1 -0
  129. package/primitives/avatar/index.d.ts +4 -0
  130. package/primitives/avatar/index.js +5 -0
  131. package/primitives/avatar/index.parts.d.ts +4 -0
  132. package/primitives/avatar/index.parts.js +5 -0
  133. package/primitives/bubble.d.ts +28 -0
  134. package/primitives/bubble.d.ts.map +1 -0
  135. package/primitives/bubble.js +43 -0
  136. package/primitives/bubble.js.map +1 -0
  137. package/primitives/button.d.ts +19 -0
  138. package/primitives/button.d.ts.map +1 -0
  139. package/primitives/button.js +27 -0
  140. package/primitives/button.js.map +1 -0
  141. package/primitives/conversation-timeline.d.ts +86 -0
  142. package/primitives/conversation-timeline.d.ts.map +1 -0
  143. package/primitives/conversation-timeline.js +119 -0
  144. package/primitives/conversation-timeline.js.map +1 -0
  145. package/primitives/index.d.ts +20 -0
  146. package/primitives/index.d.ts.map +1 -0
  147. package/primitives/index.js +45 -0
  148. package/primitives/index.js.map +1 -0
  149. package/primitives/index.parts.d.ts +13 -0
  150. package/primitives/index.parts.js +14 -0
  151. package/primitives/multimodal-input.d.ts +53 -0
  152. package/primitives/multimodal-input.d.ts.map +1 -0
  153. package/primitives/multimodal-input.js +106 -0
  154. package/primitives/multimodal-input.js.map +1 -0
  155. package/primitives/timeline-item-group.d.ts +166 -0
  156. package/primitives/timeline-item-group.d.ts.map +1 -0
  157. package/primitives/timeline-item-group.js +204 -0
  158. package/primitives/timeline-item-group.js.map +1 -0
  159. package/primitives/timeline-item.d.ts +75 -0
  160. package/primitives/timeline-item.d.ts.map +1 -0
  161. package/primitives/timeline-item.js +145 -0
  162. package/primitives/timeline-item.js.map +1 -0
  163. package/primitives/window.d.ts +31 -0
  164. package/primitives/window.d.ts.map +1 -0
  165. package/primitives/window.js +58 -0
  166. package/primitives/window.js.map +1 -0
  167. package/provider.d.ts +95 -0
  168. package/provider.d.ts.map +1 -0
  169. package/provider.js +124 -0
  170. package/provider.js.map +1 -0
  171. package/realtime/event-filter.d.ts +8 -0
  172. package/realtime/event-filter.d.ts.map +1 -0
  173. package/realtime/event-filter.js +21 -0
  174. package/realtime/event-filter.js.map +1 -0
  175. package/realtime/index.d.ts +6 -0
  176. package/realtime/index.js +7 -0
  177. package/realtime/provider.d.ts +57 -0
  178. package/realtime/provider.d.ts.map +1 -0
  179. package/realtime/provider.js +351 -0
  180. package/realtime/provider.js.map +1 -0
  181. package/realtime/seen-store.d.ts +23 -0
  182. package/realtime/seen-store.d.ts.map +1 -0
  183. package/realtime/seen-store.js +34 -0
  184. package/realtime/seen-store.js.map +1 -0
  185. package/realtime/support-provider.d.ts +17 -0
  186. package/realtime/support-provider.d.ts.map +1 -0
  187. package/realtime/support-provider.js +54 -0
  188. package/realtime/support-provider.js.map +1 -0
  189. package/realtime/typing-store.d.ts +30 -0
  190. package/realtime/typing-store.d.ts.map +1 -0
  191. package/realtime/typing-store.js +34 -0
  192. package/realtime/typing-store.js.map +1 -0
  193. package/realtime/use-realtime.d.ts +29 -0
  194. package/realtime/use-realtime.d.ts.map +1 -0
  195. package/realtime/use-realtime.js +47 -0
  196. package/realtime/use-realtime.js.map +1 -0
  197. package/realtime-events.d.ts +344 -0
  198. package/realtime-events.d.ts.map +1 -0
  199. package/schemas.d.ts +90 -0
  200. package/schemas.d.ts.map +1 -0
  201. package/support/components/avatar-stack.d.ts +45 -0
  202. package/support/components/avatar-stack.d.ts.map +1 -0
  203. package/support/components/avatar-stack.js +72 -0
  204. package/support/components/avatar-stack.js.map +1 -0
  205. package/support/components/avatar.d.ts +15 -0
  206. package/support/components/avatar.d.ts.map +1 -0
  207. package/support/components/avatar.js +23 -0
  208. package/support/components/avatar.js.map +1 -0
  209. package/support/components/bubble.d.ts +10 -0
  210. package/support/components/bubble.d.ts.map +1 -0
  211. package/support/components/bubble.js +95 -0
  212. package/support/components/bubble.js.map +1 -0
  213. package/support/components/button.d.ts +20 -0
  214. package/support/components/button.d.ts.map +1 -0
  215. package/support/components/button.js +41 -0
  216. package/support/components/button.js.map +1 -0
  217. package/support/components/container.d.ts +14 -0
  218. package/support/components/container.d.ts.map +1 -0
  219. package/support/components/container.js +115 -0
  220. package/support/components/container.js.map +1 -0
  221. package/support/components/conversation-button-link.d.ts +34 -0
  222. package/support/components/conversation-button-link.d.ts.map +1 -0
  223. package/support/components/conversation-button-link.js +195 -0
  224. package/support/components/conversation-button-link.js.map +1 -0
  225. package/support/components/conversation-event.d.ts +14 -0
  226. package/support/components/conversation-event.d.ts.map +1 -0
  227. package/support/components/conversation-event.js +76 -0
  228. package/support/components/conversation-event.js.map +1 -0
  229. package/support/components/conversation-timeline.d.ts +17 -0
  230. package/support/components/conversation-timeline.d.ts.map +1 -0
  231. package/support/components/conversation-timeline.js +95 -0
  232. package/support/components/conversation-timeline.js.map +1 -0
  233. package/support/components/cossistant-branding.d.ts +12 -0
  234. package/support/components/cossistant-branding.d.ts.map +1 -0
  235. package/support/components/cossistant-branding.js +22 -0
  236. package/support/components/cossistant-branding.js.map +1 -0
  237. package/support/components/header.d.ts +11 -0
  238. package/support/components/header.d.ts.map +1 -0
  239. package/support/components/header.js +43 -0
  240. package/support/components/header.js.map +1 -0
  241. package/support/components/icons.d.ts +21 -0
  242. package/support/components/icons.d.ts.map +1 -0
  243. package/support/components/icons.js +131 -0
  244. package/support/components/icons.js.map +1 -0
  245. package/support/components/index.d.ts +11 -0
  246. package/support/components/index.js +12 -0
  247. package/support/components/multimodal-input.d.ts +28 -0
  248. package/support/components/multimodal-input.d.ts.map +1 -0
  249. package/support/components/multimodal-input.js +138 -0
  250. package/support/components/multimodal-input.js.map +1 -0
  251. package/support/components/navigation-tab.d.ts +7 -0
  252. package/support/components/navigation-tab.d.ts.map +1 -0
  253. package/support/components/navigation-tab.js +40 -0
  254. package/support/components/navigation-tab.js.map +1 -0
  255. package/support/components/support-content.d.ts +22 -0
  256. package/support/components/support-content.d.ts.map +1 -0
  257. package/support/components/support-content.js +50 -0
  258. package/support/components/support-content.js.map +1 -0
  259. package/support/components/text-effect.d.ts +49 -0
  260. package/support/components/text-effect.d.ts.map +1 -0
  261. package/support/components/text-effect.js +221 -0
  262. package/support/components/text-effect.js.map +1 -0
  263. package/support/components/timeline-message-group.d.ts +16 -0
  264. package/support/components/timeline-message-group.d.ts.map +1 -0
  265. package/support/components/timeline-message-group.js +117 -0
  266. package/support/components/timeline-message-group.js.map +1 -0
  267. package/support/components/timeline-message-item.d.ts +17 -0
  268. package/support/components/timeline-message-item.d.ts.map +1 -0
  269. package/support/components/timeline-message-item.js +42 -0
  270. package/support/components/timeline-message-item.js.map +1 -0
  271. package/support/components/typing-indicator.d.ts +26 -0
  272. package/support/components/typing-indicator.d.ts.map +1 -0
  273. package/support/components/typing-indicator.js +37 -0
  274. package/support/components/typing-indicator.js.map +1 -0
  275. package/support/components/watermark.d.ts +8 -0
  276. package/support/components/watermark.d.ts.map +1 -0
  277. package/support/components/watermark.js +34 -0
  278. package/support/components/watermark.js.map +1 -0
  279. package/support/context/config.d.ts +32 -0
  280. package/support/context/config.d.ts.map +1 -0
  281. package/support/context/config.js +27 -0
  282. package/support/context/config.js.map +1 -0
  283. package/support/context/websocket.d.ts +22 -0
  284. package/support/context/websocket.d.ts.map +1 -0
  285. package/support/context/websocket.js +113 -0
  286. package/support/context/websocket.js.map +1 -0
  287. package/support/index.d.ts +39 -0
  288. package/support/index.d.ts.map +1 -0
  289. package/support/index.js +43 -0
  290. package/support/index.js.map +1 -0
  291. package/support/pages/articles.d.ts +7 -0
  292. package/support/pages/articles.d.ts.map +1 -0
  293. package/support/pages/articles.js +39 -0
  294. package/support/pages/articles.js.map +1 -0
  295. package/support/pages/conversation-history.d.ts +18 -0
  296. package/support/pages/conversation-history.d.ts.map +1 -0
  297. package/support/pages/conversation-history.js +120 -0
  298. package/support/pages/conversation-history.js.map +1 -0
  299. package/support/pages/conversation.d.ts +32 -0
  300. package/support/pages/conversation.d.ts.map +1 -0
  301. package/support/pages/conversation.js +92 -0
  302. package/support/pages/conversation.js.map +1 -0
  303. package/support/pages/home.d.ts +20 -0
  304. package/support/pages/home.d.ts.map +1 -0
  305. package/support/pages/home.js +184 -0
  306. package/support/pages/home.js.map +1 -0
  307. package/support/router.d.ts +14 -0
  308. package/support/router.d.ts.map +1 -0
  309. package/support/router.js +31 -0
  310. package/support/router.js.map +1 -0
  311. package/support/store/index.d.ts +2 -0
  312. package/support/store/index.js +3 -0
  313. package/support/store/support-store.d.ts +42 -0
  314. package/support/store/support-store.d.ts.map +1 -0
  315. package/support/store/support-store.js +66 -0
  316. package/support/store/support-store.js.map +1 -0
  317. package/support/support-CMoDLQoC.css +408 -0
  318. package/support/support-CMoDLQoC.css.map +1 -0
  319. package/support/support.js +1 -0
  320. package/support/text/index.d.ts +35 -0
  321. package/support/text/index.d.ts.map +1 -0
  322. package/support/text/index.js +71 -0
  323. package/support/text/index.js.map +1 -0
  324. package/support/text/locales/en.d.ts +7 -0
  325. package/support/text/locales/en.d.ts.map +1 -0
  326. package/support/text/locales/en.js +65 -0
  327. package/support/text/locales/en.js.map +1 -0
  328. package/support/text/locales/es.d.ts +7 -0
  329. package/support/text/locales/es.d.ts.map +1 -0
  330. package/support/text/locales/es.js +64 -0
  331. package/support/text/locales/es.js.map +1 -0
  332. package/support/text/locales/fr.d.ts +7 -0
  333. package/support/text/locales/fr.d.ts.map +1 -0
  334. package/support/text/locales/fr.js +64 -0
  335. package/support/text/locales/fr.js.map +1 -0
  336. package/support/text/locales/keys.d.ts +216 -0
  337. package/support/text/locales/keys.d.ts.map +1 -0
  338. package/support/text/locales/keys.js +54 -0
  339. package/support/text/locales/keys.js.map +1 -0
  340. package/support/text/runtime.d.ts +17 -0
  341. package/support/text/runtime.d.ts.map +1 -0
  342. package/support/text/runtime.js +156 -0
  343. package/support/text/runtime.js.map +1 -0
  344. package/support/utils/index.d.ts +7 -0
  345. package/support/utils/index.d.ts.map +1 -0
  346. package/support/utils/index.js +11 -0
  347. package/support/utils/index.js.map +1 -0
  348. package/support/utils/time.d.ts +5 -0
  349. package/support/utils/time.d.ts.map +1 -0
  350. package/support/utils/time.js +28 -0
  351. package/support/utils/time.js.map +1 -0
  352. package/support-config.d.ts +20 -0
  353. package/support-config.d.ts.map +1 -0
  354. package/support-config.js +25 -0
  355. package/support-config.js.map +1 -0
  356. package/support.css +2 -0
  357. package/timeline-item.d.ts +133 -0
  358. package/timeline-item.d.ts.map +1 -0
  359. package/utils/id.d.ts +6 -0
  360. package/utils/id.d.ts.map +1 -0
  361. package/utils/id.js +13 -0
  362. package/utils/id.js.map +1 -0
  363. package/utils/index.d.ts +3 -0
  364. package/utils/index.js +4 -0
  365. package/utils/text.d.ts +5 -0
  366. package/utils/text.d.ts.map +1 -0
  367. package/utils/text.js +9 -0
  368. package/utils/text.js.map +1 -0
  369. package/utils/use-render-element.d.ts +22 -0
  370. package/utils/use-render-element.d.ts.map +1 -0
  371. package/utils/use-render-element.js +35 -0
  372. package/utils/use-render-element.js.map +1 -0
@@ -0,0 +1,351 @@
1
+ "use client";
2
+
3
+
4
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ import { isValidEventType, validateRealtimeEvent } from "@cossistant/types/realtime-events";
7
+ import useWebSocket, { ReadyState } from "react-use-websocket";
8
+
9
+ //#region src/realtime/provider.tsx
10
+ const DEFAULT_HEARTBEAT_INTERVAL_MS = 15e3;
11
+ const DEFAULT_HEARTBEAT_TIMEOUT_MS = 45e3;
12
+ const DEFAULT_WS_URL = "wss://api.cossistant.com/ws";
13
+ const RealtimeContext = createContext(null);
14
+ /**
15
+ * Decodes WebSocket message data into a string.
16
+ * Handles string, ArrayBuffer, and ArrayBufferView formats.
17
+ */
18
+ function decodeMessageData(data) {
19
+ if (typeof data === "string") return {
20
+ type: "raw-text",
21
+ data
22
+ };
23
+ if (data instanceof ArrayBuffer) try {
24
+ return {
25
+ type: "raw-text",
26
+ data: new TextDecoder().decode(data)
27
+ };
28
+ } catch {
29
+ return { type: "unsupported" };
30
+ }
31
+ if (ArrayBuffer.isView(data)) try {
32
+ return {
33
+ type: "raw-text",
34
+ data: new TextDecoder().decode(data.buffer)
35
+ };
36
+ } catch {
37
+ return { type: "unsupported" };
38
+ }
39
+ return { type: "unsupported" };
40
+ }
41
+ /**
42
+ * Safely parses JSON string, returning null if invalid.
43
+ */
44
+ function parseJson(raw) {
45
+ try {
46
+ return JSON.parse(raw);
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+ /**
52
+ * Extracts a string field from an unknown object, with optional validation.
53
+ */
54
+ function extractStringField(obj, field, required = false) {
55
+ if (!obj || typeof obj !== "object" || !(field in obj)) return required ? null : null;
56
+ const value = obj[field];
57
+ if (typeof value === "string" && value.length > 0) return value;
58
+ return required ? null : null;
59
+ }
60
+ /**
61
+ * Parses a WebSocket message and determines its type and content.
62
+ */
63
+ function parseWebSocketMessage(rawText) {
64
+ if (rawText === "pong") return { type: "pong" };
65
+ const parsed = parseJson(rawText);
66
+ if (!parsed || typeof parsed !== "object") return { type: "invalid" };
67
+ const messageType = extractStringField(parsed, "type");
68
+ if (messageType === "CONNECTION_ESTABLISHED") {
69
+ const payload = parsed.payload;
70
+ return {
71
+ type: "connection-established",
72
+ connectionId: extractStringField(payload, "connectionId")
73
+ };
74
+ }
75
+ if ("error" in parsed && "message" in parsed) return {
76
+ type: "error",
77
+ message: extractStringField(parsed, "message") || "Realtime connection error"
78
+ };
79
+ if (messageType && isValidEventType(messageType)) try {
80
+ const event = constructRealtimeEvent(parsed);
81
+ if (!event) return { type: "invalid" };
82
+ return {
83
+ type: "event",
84
+ event
85
+ };
86
+ } catch (error) {
87
+ console.error("[Realtime] Failed to construct event", error);
88
+ return { type: "invalid" };
89
+ }
90
+ return { type: "invalid" };
91
+ }
92
+ /**
93
+ * Constructs a RealtimeEvent from parsed JSON data.
94
+ * Returns null if required fields are missing or validation fails.
95
+ */
96
+ function constructRealtimeEvent(parsed) {
97
+ if (!parsed || typeof parsed !== "object" || !("type" in parsed)) return null;
98
+ const type = parsed.type;
99
+ if (!isValidEventType(type)) return null;
100
+ const eventType = type;
101
+ const payloadSource = parsed.payload;
102
+ let payload;
103
+ try {
104
+ payload = validateRealtimeEvent(eventType, payloadSource);
105
+ } catch (error) {
106
+ console.error("[Realtime] Received invalid event payload", error);
107
+ return null;
108
+ }
109
+ const organizationId = extractStringField(payloadSource, "organizationId", true);
110
+ const websiteId = extractStringField(payloadSource, "websiteId", true);
111
+ if (!organizationId) {
112
+ console.error("[Realtime] Received event without organizationId", parsed);
113
+ return null;
114
+ }
115
+ if (!websiteId) {
116
+ console.error("[Realtime] Received event without websiteId", parsed);
117
+ return null;
118
+ }
119
+ const visitorId = extractStringField(parsed, "visitorId");
120
+ return {
121
+ type: eventType,
122
+ payload,
123
+ organizationId,
124
+ websiteId,
125
+ visitorId
126
+ };
127
+ }
128
+ /**
129
+ * Checks if heartbeat has timed out.
130
+ */
131
+ function isHeartbeatTimedOut(lastHeartbeat, timeoutMs) {
132
+ return Date.now() - lastHeartbeat > timeoutMs;
133
+ }
134
+ function resolvePublicKey(explicit) {
135
+ const trimmed = explicit?.trim();
136
+ if (trimmed) return trimmed;
137
+ const normalized = (process.env.NEXT_PUBLIC_COSSISTANT_KEY || process.env.COSSISTANT_PUBLIC_KEY || null)?.trim();
138
+ return normalized && normalized.length > 0 ? normalized : null;
139
+ }
140
+ function normalizeAuth(auth) {
141
+ if (!auth) return null;
142
+ if (auth.kind === "visitor") {
143
+ const visitorId = auth.visitorId?.trim() || null;
144
+ if (!visitorId) return null;
145
+ return {
146
+ type: "visitor",
147
+ visitorId,
148
+ websiteId: auth.websiteId?.trim() || null,
149
+ userId: null,
150
+ sessionToken: null,
151
+ publicKey: resolvePublicKey(auth.publicKey ?? null)
152
+ };
153
+ }
154
+ const sessionToken = auth.sessionToken?.trim() || null;
155
+ if (!sessionToken) return null;
156
+ return {
157
+ type: "session",
158
+ visitorId: null,
159
+ websiteId: auth.websiteId?.trim() || null,
160
+ userId: auth.userId?.trim() || null,
161
+ sessionToken,
162
+ publicKey: null
163
+ };
164
+ }
165
+ function buildSocketUrl(baseUrl, auth) {
166
+ if (!auth) return null;
167
+ try {
168
+ const url = new URL(baseUrl);
169
+ if (auth.type === "visitor") {
170
+ url.searchParams.set("visitorId", auth.visitorId ?? "");
171
+ const publicKey = auth.publicKey;
172
+ if (publicKey) url.searchParams.set("publicKey", publicKey);
173
+ } else {
174
+ url.searchParams.set("sessionToken", auth.sessionToken ?? "");
175
+ if (auth.websiteId) url.searchParams.set("websiteId", auth.websiteId);
176
+ }
177
+ return url.toString();
178
+ } catch (error) {
179
+ console.error("[Realtime] Failed to build WebSocket URL", error);
180
+ return null;
181
+ }
182
+ }
183
+ function RealtimeProvider({ children, wsUrl = DEFAULT_WS_URL, auth, autoConnect = true, onConnect, onDisconnect, onError }) {
184
+ const normalizedAuth = normalizeAuth(auth);
185
+ const socketUrl = buildSocketUrl(wsUrl, normalizedAuth);
186
+ const eventHandlersRef = useRef(/* @__PURE__ */ new Set());
187
+ const lastHeartbeatRef = useRef(Date.now());
188
+ const [connectionError, setConnectionError] = useState(null);
189
+ const [lastEvent, setLastEvent] = useState(null);
190
+ const [connectionId, setConnectionId] = useState(null);
191
+ const heartbeatIntervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS;
192
+ const heartbeatTimeoutMs = DEFAULT_HEARTBEAT_TIMEOUT_MS;
193
+ const canConnect = Boolean(autoConnect && socketUrl);
194
+ const connectionUrl = canConnect ? socketUrl : null;
195
+ const { sendMessage, sendJsonMessage, lastMessage, readyState, getWebSocket } = useWebSocket(connectionUrl, {
196
+ shouldReconnect: (closeEvent) => {
197
+ if (!canConnect) return false;
198
+ if (closeEvent.code === 1008 || closeEvent.code === 1011) {
199
+ const err = new Error(closeEvent.reason || "Realtime connection closed by server. Please check your credentials.");
200
+ setConnectionError(err);
201
+ onError?.(err);
202
+ return false;
203
+ }
204
+ return true;
205
+ },
206
+ reconnectAttempts: autoConnect ? void 0 : 0,
207
+ reconnectInterval: (attempt) => {
208
+ const base = 500 * 2 ** attempt;
209
+ return Math.min(base, 3e4);
210
+ },
211
+ retryOnError: false,
212
+ onOpen: () => {
213
+ setConnectionError(null);
214
+ lastHeartbeatRef.current = Date.now();
215
+ onConnect?.();
216
+ },
217
+ onClose: () => {
218
+ setConnectionId(null);
219
+ onDisconnect?.();
220
+ },
221
+ onError: (event) => {
222
+ const err = /* @__PURE__ */ new Error(`WebSocket error: ${event.type}`);
223
+ setConnectionError(err);
224
+ onError?.(err);
225
+ }
226
+ }, canConnect);
227
+ useEffect(() => {
228
+ if (!lastMessage) return;
229
+ const decoded = decodeMessageData(lastMessage.data);
230
+ if (decoded.type === "unsupported") return;
231
+ const message = parseWebSocketMessage(decoded.data);
232
+ switch (message.type) {
233
+ case "pong":
234
+ lastHeartbeatRef.current = Date.now();
235
+ break;
236
+ case "connection-established":
237
+ setConnectionId(message.connectionId);
238
+ lastHeartbeatRef.current = Date.now();
239
+ break;
240
+ case "error": {
241
+ const err = new Error(message.message);
242
+ setConnectionError(err);
243
+ onError?.(err);
244
+ break;
245
+ }
246
+ case "event":
247
+ lastHeartbeatRef.current = Date.now();
248
+ setLastEvent(message.event);
249
+ for (const handler of eventHandlersRef.current) Promise.resolve(handler(message.event)).catch((error) => {
250
+ const err = error instanceof Error ? error : /* @__PURE__ */ new Error(`Subscriber threw an exception: ${String(error)}`);
251
+ onError?.(err);
252
+ });
253
+ break;
254
+ default: break;
255
+ }
256
+ }, [lastMessage, onError]);
257
+ useEffect(() => {
258
+ if (!canConnect) return;
259
+ const interval = window.setInterval(() => {
260
+ if (readyState !== ReadyState.OPEN) return;
261
+ if (isHeartbeatTimedOut(lastHeartbeatRef.current, heartbeatTimeoutMs)) {
262
+ getWebSocket()?.close(4e3, "Heartbeat timeout");
263
+ return;
264
+ }
265
+ try {
266
+ sendMessage("ping");
267
+ } catch {}
268
+ }, heartbeatIntervalMs);
269
+ return () => {
270
+ window.clearInterval(interval);
271
+ };
272
+ }, [
273
+ canConnect,
274
+ heartbeatIntervalMs,
275
+ heartbeatTimeoutMs,
276
+ readyState,
277
+ sendMessage,
278
+ getWebSocket
279
+ ]);
280
+ const send = useCallback((event) => {
281
+ if (!connectionUrl) throw new Error("Realtime connection is disabled");
282
+ if (readyState !== ReadyState.OPEN) throw new Error("Realtime connection is not established");
283
+ sendJsonMessage(event);
284
+ }, [
285
+ connectionUrl,
286
+ readyState,
287
+ sendJsonMessage
288
+ ]);
289
+ const sendRaw = useCallback((data) => {
290
+ if (!connectionUrl) throw new Error("Realtime connection is disabled");
291
+ if (readyState !== ReadyState.OPEN) throw new Error("Realtime connection is not established");
292
+ sendMessage(data);
293
+ }, [
294
+ connectionUrl,
295
+ readyState,
296
+ sendMessage
297
+ ]);
298
+ const subscribe = useCallback((handler) => {
299
+ eventHandlersRef.current.add(handler);
300
+ return () => {
301
+ eventHandlersRef.current.delete(handler);
302
+ };
303
+ }, []);
304
+ const reconnect = useCallback(() => {
305
+ getWebSocket()?.close();
306
+ }, [getWebSocket]);
307
+ const connection = useMemo(() => ({
308
+ isConnected: readyState === ReadyState.OPEN,
309
+ isConnecting: readyState === ReadyState.CONNECTING,
310
+ error: connectionError,
311
+ send,
312
+ sendRaw,
313
+ subscribe,
314
+ lastEvent,
315
+ connectionId,
316
+ reconnect
317
+ }), [
318
+ readyState,
319
+ connectionError,
320
+ send,
321
+ sendRaw,
322
+ subscribe,
323
+ lastEvent,
324
+ connectionId,
325
+ reconnect
326
+ ]);
327
+ const value = useMemo(() => ({
328
+ ...connection,
329
+ visitorId: normalizedAuth?.visitorId ?? null,
330
+ websiteId: normalizedAuth?.websiteId ?? null,
331
+ userId: normalizedAuth?.userId ?? null
332
+ }), [
333
+ connection,
334
+ normalizedAuth?.visitorId,
335
+ normalizedAuth?.websiteId,
336
+ normalizedAuth?.userId
337
+ ]);
338
+ return /* @__PURE__ */ jsx(RealtimeContext.Provider, {
339
+ value,
340
+ children
341
+ });
342
+ }
343
+ function useRealtimeConnection() {
344
+ const context = useContext(RealtimeContext);
345
+ if (!context) throw new Error("useRealtimeConnection must be used within RealtimeProvider");
346
+ return context;
347
+ }
348
+
349
+ //#endregion
350
+ export { RealtimeProvider, useRealtimeConnection };
351
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","names":["payload: unknown"],"sources":["../../src/realtime/provider.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\ttype AnyRealtimeEvent,\n\tisValidEventType,\n\ttype RealtimeEvent,\n\tvalidateRealtimeEvent,\n} from \"@cossistant/types/realtime-events\";\nimport type React from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport useWebSocket, { ReadyState } from \"react-use-websocket\";\n\nconst DEFAULT_HEARTBEAT_INTERVAL_MS = 15_000;\nconst DEFAULT_HEARTBEAT_TIMEOUT_MS = 45_000;\n\ntype SubscribeHandler = (event: AnyRealtimeEvent) => void;\n\ntype MessageDecodeResult =\n\t| {\n\t\t\ttype: \"raw-text\";\n\t\t\tdata: string;\n\t }\n\t| {\n\t\t\ttype: \"unsupported\";\n\t };\n\ntype ParsedMessage =\n\t| {\n\t\t\ttype: \"pong\";\n\t }\n\t| {\n\t\t\ttype: \"connection-established\";\n\t\t\tconnectionId: string | null;\n\t }\n\t| {\n\t\t\ttype: \"error\";\n\t\t\tmessage: string;\n\t }\n\t| {\n\t\t\ttype: \"event\";\n\t\t\tevent: AnyRealtimeEvent;\n\t }\n\t| {\n\t\t\ttype: \"invalid\";\n\t };\n\ntype VisitorAuthConfig = {\n\tkind: \"visitor\";\n\tvisitorId: string | null;\n\twebsiteId?: string | null;\n\tpublicKey?: string | null;\n};\n\ntype SessionAuthConfig = {\n\tkind: \"session\";\n\tsessionToken: string | null;\n\twebsiteId?: string | null;\n\tuserId?: string | null;\n};\n\ntype RealtimeAuthConfig = VisitorAuthConfig | SessionAuthConfig;\n\ntype ResolvedAuthConfig = {\n\ttype: \"visitor\" | \"session\";\n\tvisitorId: string | null;\n\twebsiteId: string | null;\n\tuserId: string | null;\n\tsessionToken: string | null;\n\tpublicKey: string | null;\n};\n\ntype RealtimeProviderProps = {\n\tchildren: React.ReactNode;\n\twsUrl?: string;\n\tauth: RealtimeAuthConfig | null;\n\tautoConnect?: boolean;\n\tonConnect?: () => void;\n\tonDisconnect?: () => void;\n\tonError?: (error: Error) => void;\n};\n\ntype RealtimeConnectionState = {\n\tisConnected: boolean;\n\tisConnecting: boolean;\n\terror: Error | null;\n\tsend: (event: AnyRealtimeEvent) => void;\n\tsendRaw: (data: string) => void;\n\tsubscribe: (handler: SubscribeHandler) => () => void;\n\tlastEvent: AnyRealtimeEvent | null;\n\tconnectionId: string | null;\n\treconnect: () => void;\n};\n\ntype RealtimeContextValue = RealtimeConnectionState & {\n\tvisitorId: string | null;\n\twebsiteId: string | null;\n\tuserId: string | null;\n};\n\nconst DEFAULT_WS_URL = \"wss://api.cossistant.com/ws\";\n\nconst RealtimeContext = createContext<RealtimeContextValue | null>(null);\n\n/**\n * Decodes WebSocket message data into a string.\n * Handles string, ArrayBuffer, and ArrayBufferView formats.\n */\nfunction decodeMessageData(data: unknown): MessageDecodeResult {\n\tif (typeof data === \"string\") {\n\t\treturn { type: \"raw-text\", data };\n\t}\n\n\tif (data instanceof ArrayBuffer) {\n\t\ttry {\n\t\t\treturn { type: \"raw-text\", data: new TextDecoder().decode(data) };\n\t\t} catch {\n\t\t\treturn { type: \"unsupported\" };\n\t\t}\n\t}\n\n\tif (ArrayBuffer.isView(data)) {\n\t\ttry {\n\t\t\treturn { type: \"raw-text\", data: new TextDecoder().decode(data.buffer) };\n\t\t} catch {\n\t\t\treturn { type: \"unsupported\" };\n\t\t}\n\t}\n\n\treturn { type: \"unsupported\" };\n}\n\n/**\n * Safely parses JSON string, returning null if invalid.\n */\nfunction parseJson(raw: string): unknown {\n\ttry {\n\t\treturn JSON.parse(raw);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Extracts a string field from an unknown object, with optional validation.\n */\nfunction extractStringField(\n\tobj: unknown,\n\tfield: string,\n\trequired = false\n): string | null {\n\tif (!obj || typeof obj !== \"object\" || !(field in obj)) {\n\t\treturn required ? null : null;\n\t}\n\tconst value = (obj as Record<string, unknown>)[field];\n\tif (typeof value === \"string\" && value.length > 0) {\n\t\treturn value;\n\t}\n\treturn required ? null : null;\n}\n\n/**\n * Parses a WebSocket message and determines its type and content.\n */\nfunction parseWebSocketMessage(rawText: string): ParsedMessage {\n\t// Handle pong heartbeat\n\tif (rawText === \"pong\") {\n\t\treturn { type: \"pong\" };\n\t}\n\n\t// Try to parse as JSON\n\tconst parsed = parseJson(rawText);\n\tif (!parsed || typeof parsed !== \"object\") {\n\t\treturn { type: \"invalid\" };\n\t}\n\n\tconst messageType = extractStringField(parsed, \"type\");\n\n\t// Handle CONNECTION_ESTABLISHED\n\tif (messageType === \"CONNECTION_ESTABLISHED\") {\n\t\tconst payload = (parsed as { payload?: unknown }).payload;\n\t\tconst connectionId = extractStringField(payload, \"connectionId\");\n\t\treturn { type: \"connection-established\", connectionId };\n\t}\n\n\t// Handle error messages\n\tif (\"error\" in parsed && \"message\" in parsed) {\n\t\tconst message =\n\t\t\textractStringField(parsed, \"message\") || \"Realtime connection error\";\n\t\treturn { type: \"error\", message };\n\t}\n\n\t// Handle realtime events\n\tif (messageType && isValidEventType(messageType)) {\n\t\ttry {\n\t\t\tconst event = constructRealtimeEvent(parsed);\n\t\t\tif (!event) {\n\t\t\t\treturn { type: \"invalid\" };\n\t\t\t}\n\t\t\treturn { type: \"event\", event };\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[Realtime] Failed to construct event\", error);\n\t\t\treturn { type: \"invalid\" };\n\t\t}\n\t}\n\n\treturn { type: \"invalid\" };\n}\n\n/**\n * Constructs a RealtimeEvent from parsed JSON data.\n * Returns null if required fields are missing or validation fails.\n */\nfunction constructRealtimeEvent(parsed: unknown): AnyRealtimeEvent | null {\n\tif (!parsed || typeof parsed !== \"object\" || !(\"type\" in parsed)) {\n\t\treturn null;\n\t}\n\n\tconst type = (parsed as { type: unknown }).type;\n\tif (!isValidEventType(type)) {\n\t\treturn null;\n\t}\n\n\tconst eventType = type;\n\n\t// Extract payload directly\n\tconst payloadSource = (parsed as { payload?: unknown }).payload;\n\n\tlet payload: unknown;\n\ttry {\n\t\tpayload = validateRealtimeEvent(eventType, payloadSource);\n\t} catch (error) {\n\t\tconsole.error(\"[Realtime] Received invalid event payload\", error);\n\t\treturn null;\n\t}\n\n\tconst organizationId = extractStringField(\n\t\tpayloadSource,\n\t\t\"organizationId\",\n\t\ttrue\n\t);\n\tconst websiteId = extractStringField(payloadSource, \"websiteId\", true);\n\n\tif (!organizationId) {\n\t\tconsole.error(\"[Realtime] Received event without organizationId\", parsed);\n\t\treturn null;\n\t}\n\n\tif (!websiteId) {\n\t\tconsole.error(\"[Realtime] Received event without websiteId\", parsed);\n\t\treturn null;\n\t}\n\n\tconst visitorId = extractStringField(parsed, \"visitorId\");\n\n\treturn {\n\t\ttype: eventType,\n\t\tpayload,\n\t\torganizationId,\n\t\twebsiteId,\n\t\tvisitorId,\n\t} as AnyRealtimeEvent;\n}\n\n/**\n * Checks if heartbeat has timed out.\n */\nfunction isHeartbeatTimedOut(\n\tlastHeartbeat: number,\n\ttimeoutMs: number\n): boolean {\n\tconst elapsed = Date.now() - lastHeartbeat;\n\treturn elapsed > timeoutMs;\n}\n\nfunction resolvePublicKey(explicit?: string | null): string | null {\n\tconst trimmed = explicit?.trim();\n\tif (trimmed) {\n\t\treturn trimmed;\n\t}\n\n\tconst fromEnv =\n\t\tprocess.env.NEXT_PUBLIC_COSSISTANT_KEY ||\n\t\tprocess.env.COSSISTANT_PUBLIC_KEY ||\n\t\tnull;\n\n\tconst normalized = fromEnv?.trim();\n\treturn normalized && normalized.length > 0 ? normalized : null;\n}\n\nfunction normalizeAuth(\n\tauth: RealtimeAuthConfig | null\n): ResolvedAuthConfig | null {\n\tif (!auth) {\n\t\treturn null;\n\t}\n\n\tif (auth.kind === \"visitor\") {\n\t\tconst visitorId = auth.visitorId?.trim() || null;\n\n\t\tif (!visitorId) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn {\n\t\t\ttype: \"visitor\",\n\t\t\tvisitorId,\n\t\t\twebsiteId: auth.websiteId?.trim() || null,\n\t\t\tuserId: null,\n\t\t\tsessionToken: null,\n\t\t\tpublicKey: resolvePublicKey(auth.publicKey ?? null),\n\t\t} satisfies ResolvedAuthConfig;\n\t}\n\n\tconst sessionToken = auth.sessionToken?.trim() || null;\n\n\tif (!sessionToken) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\ttype: \"session\",\n\t\tvisitorId: null,\n\t\twebsiteId: auth.websiteId?.trim() || null,\n\t\tuserId: auth.userId?.trim() || null,\n\t\tsessionToken,\n\t\tpublicKey: null,\n\t} satisfies ResolvedAuthConfig;\n}\n\nfunction buildSocketUrl(\n\tbaseUrl: string,\n\tauth: ResolvedAuthConfig | null\n): string | null {\n\tif (!auth) {\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst url = new URL(baseUrl);\n\n\t\tif (auth.type === \"visitor\") {\n\t\t\turl.searchParams.set(\"visitorId\", auth.visitorId ?? \"\");\n\t\t\tconst publicKey = auth.publicKey;\n\t\t\tif (publicKey) {\n\t\t\t\turl.searchParams.set(\"publicKey\", publicKey);\n\t\t\t}\n\t\t} else {\n\t\t\turl.searchParams.set(\"sessionToken\", auth.sessionToken ?? \"\");\n\t\t\tif (auth.websiteId) {\n\t\t\t\turl.searchParams.set(\"websiteId\", auth.websiteId);\n\t\t\t}\n\t\t}\n\n\t\treturn url.toString();\n\t} catch (error) {\n\t\tconsole.error(\"[Realtime] Failed to build WebSocket URL\", error);\n\t\treturn null;\n\t}\n}\n\nexport function RealtimeProvider({\n\tchildren,\n\twsUrl = DEFAULT_WS_URL,\n\tauth,\n\tautoConnect = true,\n\tonConnect,\n\tonDisconnect,\n\tonError,\n}: RealtimeProviderProps) {\n\tconst normalizedAuth = normalizeAuth(auth);\n\n\tconst socketUrl = buildSocketUrl(wsUrl, normalizedAuth);\n\tconst eventHandlersRef = useRef<Set<SubscribeHandler>>(new Set());\n\tconst lastHeartbeatRef = useRef<number>(Date.now());\n\tconst [connectionError, setConnectionError] = useState<Error | null>(null);\n\tconst [lastEvent, setLastEvent] = useState<AnyRealtimeEvent | null>(null);\n\tconst [connectionId, setConnectionId] = useState<string | null>(null);\n\n\tconst heartbeatIntervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS;\n\tconst heartbeatTimeoutMs = DEFAULT_HEARTBEAT_TIMEOUT_MS;\n\n\tconst canConnect = Boolean(autoConnect && socketUrl);\n\tconst connectionUrl = canConnect ? socketUrl : null;\n\n\tconst {\n\t\tsendMessage,\n\t\tsendJsonMessage,\n\t\tlastMessage,\n\t\treadyState,\n\t\tgetWebSocket,\n\t} = useWebSocket(\n\t\tconnectionUrl,\n\t\t{\n\t\t\tshouldReconnect: (closeEvent) => {\n\t\t\t\tif (!canConnect) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (closeEvent.code === 1008 || closeEvent.code === 1011) {\n\t\t\t\t\tconst err = new Error(\n\t\t\t\t\t\tcloseEvent.reason ||\n\t\t\t\t\t\t\t\"Realtime connection closed by server. Please check your credentials.\"\n\t\t\t\t\t);\n\t\t\t\t\tsetConnectionError(err);\n\t\t\t\t\tonError?.(err);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\treconnectAttempts: autoConnect ? undefined : 0,\n\t\t\treconnectInterval: (attempt) => {\n\t\t\t\tconst base = 500 * 2 ** attempt;\n\t\t\t\treturn Math.min(base, 30_000);\n\t\t\t},\n\t\t\tretryOnError: false,\n\t\t\tonOpen: () => {\n\t\t\t\tsetConnectionError(null);\n\t\t\t\tlastHeartbeatRef.current = Date.now();\n\t\t\t\tonConnect?.();\n\t\t\t},\n\t\t\tonClose: () => {\n\t\t\t\tsetConnectionId(null);\n\t\t\t\tonDisconnect?.();\n\t\t\t},\n\t\t\tonError: (event) => {\n\t\t\t\tconst err = new Error(`WebSocket error: ${event.type}`);\n\t\t\t\tsetConnectionError(err);\n\t\t\t\tonError?.(err);\n\t\t\t},\n\t\t},\n\t\tcanConnect\n\t);\n\n\tuseEffect(() => {\n\t\tif (!lastMessage) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Decode message data from various formats\n\t\tconst decoded = decodeMessageData(lastMessage.data);\n\t\tif (decoded.type === \"unsupported\") {\n\t\t\treturn;\n\t\t}\n\n\t\t// Parse the message and determine its type\n\t\tconst message = parseWebSocketMessage(decoded.data);\n\n\t\t// Handle different message types\n\t\tswitch (message.type) {\n\t\t\tcase \"pong\":\n\t\t\t\tlastHeartbeatRef.current = Date.now();\n\t\t\t\tbreak;\n\n\t\t\tcase \"connection-established\":\n\t\t\t\tsetConnectionId(message.connectionId);\n\t\t\t\tlastHeartbeatRef.current = Date.now();\n\t\t\t\tbreak;\n\n\t\t\tcase \"error\": {\n\t\t\t\tconst err = new Error(message.message);\n\t\t\t\tsetConnectionError(err);\n\t\t\t\tonError?.(err);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"event\":\n\t\t\t\tlastHeartbeatRef.current = Date.now();\n\t\t\t\tsetLastEvent(message.event);\n\t\t\t\tfor (const handler of eventHandlersRef.current) {\n\t\t\t\t\tPromise.resolve(handler(message.event)).catch((error) => {\n\t\t\t\t\t\tconst err =\n\t\t\t\t\t\t\terror instanceof Error\n\t\t\t\t\t\t\t\t? error\n\t\t\t\t\t\t\t\t: new Error(`Subscriber threw an exception: ${String(error)}`);\n\t\t\t\t\t\tonError?.(err);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\t// Silently ignore invalid or unknown messages\n\t\t\t\tbreak;\n\t\t}\n\t}, [lastMessage, onError]);\n\n\tuseEffect(() => {\n\t\tif (!canConnect) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst interval = window.setInterval(() => {\n\t\t\tif (readyState !== ReadyState.OPEN) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Check if heartbeat has timed out\n\t\t\tif (isHeartbeatTimedOut(lastHeartbeatRef.current, heartbeatTimeoutMs)) {\n\t\t\t\tconst socket = getWebSocket();\n\t\t\t\tsocket?.close(4000, \"Heartbeat timeout\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Send ping to keep connection alive\n\t\t\ttry {\n\t\t\t\tsendMessage(\"ping\");\n\t\t\t} catch {\n\t\t\t\t// Ignore send failures; reconnect logic will handle it\n\t\t\t}\n\t\t}, heartbeatIntervalMs);\n\n\t\treturn () => {\n\t\t\twindow.clearInterval(interval);\n\t\t};\n\t}, [\n\t\tcanConnect,\n\t\theartbeatIntervalMs,\n\t\theartbeatTimeoutMs,\n\t\treadyState,\n\t\tsendMessage,\n\t\tgetWebSocket,\n\t]);\n\n\tconst send = useCallback(\n\t\t(event: AnyRealtimeEvent) => {\n\t\t\tif (!connectionUrl) {\n\t\t\t\tthrow new Error(\"Realtime connection is disabled\");\n\t\t\t}\n\n\t\t\tif (readyState !== ReadyState.OPEN) {\n\t\t\t\tthrow new Error(\"Realtime connection is not established\");\n\t\t\t}\n\n\t\t\tsendJsonMessage(event);\n\t\t},\n\t\t[connectionUrl, readyState, sendJsonMessage]\n\t);\n\n\tconst sendRaw = useCallback(\n\t\t(data: string) => {\n\t\t\tif (!connectionUrl) {\n\t\t\t\tthrow new Error(\"Realtime connection is disabled\");\n\t\t\t}\n\n\t\t\tif (readyState !== ReadyState.OPEN) {\n\t\t\t\tthrow new Error(\"Realtime connection is not established\");\n\t\t\t}\n\n\t\t\tsendMessage(data);\n\t\t},\n\t\t[connectionUrl, readyState, sendMessage]\n\t);\n\n\tconst subscribe = useCallback((handler: SubscribeHandler) => {\n\t\teventHandlersRef.current.add(handler);\n\t\treturn () => {\n\t\t\teventHandlersRef.current.delete(handler);\n\t\t};\n\t}, []);\n\n\tconst reconnect = useCallback(() => {\n\t\tconst socket = getWebSocket();\n\t\tsocket?.close();\n\t}, [getWebSocket]);\n\n\tconst connection = useMemo<RealtimeConnectionState>(\n\t\t() => ({\n\t\t\tisConnected: readyState === ReadyState.OPEN,\n\t\t\tisConnecting: readyState === ReadyState.CONNECTING,\n\t\t\terror: connectionError,\n\t\t\tsend,\n\t\t\tsendRaw,\n\t\t\tsubscribe,\n\t\t\tlastEvent,\n\t\t\tconnectionId,\n\t\t\treconnect,\n\t\t}),\n\t\t[\n\t\t\treadyState,\n\t\t\tconnectionError,\n\t\t\tsend,\n\t\t\tsendRaw,\n\t\t\tsubscribe,\n\t\t\tlastEvent,\n\t\t\tconnectionId,\n\t\t\treconnect,\n\t\t]\n\t);\n\n\tconst value = useMemo<RealtimeContextValue>(\n\t\t() => ({\n\t\t\t...connection,\n\t\t\tvisitorId: normalizedAuth?.visitorId ?? null,\n\t\t\twebsiteId: normalizedAuth?.websiteId ?? null,\n\t\t\tuserId: normalizedAuth?.userId ?? null,\n\t\t}),\n\t\t[\n\t\t\tconnection,\n\t\t\tnormalizedAuth?.visitorId,\n\t\t\tnormalizedAuth?.websiteId,\n\t\t\tnormalizedAuth?.userId,\n\t\t]\n\t);\n\n\treturn (\n\t\t<RealtimeContext.Provider value={value}>\n\t\t\t{children}\n\t\t</RealtimeContext.Provider>\n\t);\n}\n\nexport function useRealtimeConnection(): RealtimeContextValue {\n\tconst context = useContext(RealtimeContext);\n\tif (!context) {\n\t\tthrow new Error(\n\t\t\t\"useRealtimeConnection must be used within RealtimeProvider\"\n\t\t);\n\t}\n\n\treturn context;\n}\n\nexport type { RealtimeContextValue };\nexport type { RealtimeAuthConfig };\nexport type { RealtimeProviderProps };\nexport type { RealtimeEvent } from \"@cossistant/types/realtime-events\";\n"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { ConversationSeen } from "../schemas.js";
2
+ import { RealtimeEvent } from "../realtime-events.js";
3
+ import { SeenActorType, SeenState } from "@cossistant/core";
4
+
5
+ //#region src/realtime/seen-store.d.ts
6
+ type Selector<T> = (state: SeenState) => T;
7
+ type EqualityChecker<T> = (previous: T, next: T) => boolean;
8
+ declare function useSeenStore<TSelected>(selector: Selector<TSelected>, isEqual?: EqualityChecker<TSelected>): TSelected;
9
+ declare function hydrateConversationSeen(conversationId: string, entries: ConversationSeen[]): void;
10
+ declare function upsertConversationSeen(options: {
11
+ conversationId: string;
12
+ actorType: SeenActorType;
13
+ actorId: string;
14
+ lastSeenAt: Date;
15
+ }): void;
16
+ declare function applyConversationSeenEvent(event: RealtimeEvent<"conversationSeen">, options?: {
17
+ ignoreVisitorId?: string | null;
18
+ ignoreUserId?: string | null;
19
+ ignoreAiAgentId?: string | null;
20
+ }): void;
21
+ //#endregion
22
+ export { applyConversationSeenEvent, hydrateConversationSeen, upsertConversationSeen, useSeenStore };
23
+ //# sourceMappingURL=seen-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seen-store.d.ts","names":[],"sources":["../../src/realtime/seen-store.ts"],"sourcesContent":[],"mappings":";;;;;KAeK,sBAAsB,cAAc;KAEpC,gCAAgC,SAAS;AAFzC,iBAiCW,YAjCH,CAAA,SAAA,CAAA,CAAA,QAAA,EAkCF,QAlCE,CAkCO,SAlCP,CAAA,EAAA,OAAA,CAAA,EAmCF,eAnCE,CAmCc,SAnCd,CAAA,CAAA,EAoCV,SApCU;AAAA,iBAwCG,uBAAA,CAxCH,cAAA,EAAA,MAAA,EAAA,OAAA,EA0CH,gBA1CG,EAAA,CAAA,EAAA,IAAA;AAAc,iBA+CX,sBAAA,CA/CW,OAAA,EAAA;gBAAc,EAAA,MAAA;EAAC,SAAA,EAiD9B,aAjD8B;EAErC,OAAA,EAAA,MAAA;EAAe,UAAA,EAiDP,IAjDO;QAAiB;AAAS,iBAyD9B,0BAAA,CAzD8B,KAAA,EA0DtC,aA1DsC,CAAA,kBAAA,CAAA,EAAA,QAAA,EAAA;EAAC,eAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EA+B/B,YAAA,CAAA,EAAY,MAAA,GAAA,IAAA;EAAA,eAAA,CAAA,EAAA,MAAA,GAAA,IAAA;QACR"}
@@ -0,0 +1,34 @@
1
+ import { useRef, useSyncExternalStore } from "react";
2
+ import { applyConversationSeenEvent as applyConversationSeenEvent$1, createSeenStore, hydrateConversationSeen as hydrateConversationSeen$1, upsertConversationSeen as upsertConversationSeen$1 } from "@cossistant/core";
3
+
4
+ //#region src/realtime/seen-store.ts
5
+ const store = createSeenStore();
6
+ function useSelector(selector, isEqual = Object.is) {
7
+ const selectionRef = useRef(void 0);
8
+ const subscribe = (onStoreChange) => store.subscribe(() => {
9
+ onStoreChange();
10
+ });
11
+ const snapshot = useSyncExternalStore(subscribe, store.getState, store.getState);
12
+ const selected = selector(snapshot);
13
+ if (selectionRef.current === void 0 || !isEqual(selectionRef.current, selected)) selectionRef.current = selected;
14
+ return selectionRef.current;
15
+ }
16
+ function useSeenStore(selector, isEqual) {
17
+ return useSelector(selector, isEqual);
18
+ }
19
+ function hydrateConversationSeen(conversationId, entries) {
20
+ hydrateConversationSeen$1(store, conversationId, entries);
21
+ }
22
+ function upsertConversationSeen(options) {
23
+ upsertConversationSeen$1(store, {
24
+ ...options,
25
+ lastSeenAt: options.lastSeenAt.toISOString()
26
+ });
27
+ }
28
+ function applyConversationSeenEvent(event, options) {
29
+ applyConversationSeenEvent$1(store, event, options);
30
+ }
31
+
32
+ //#endregion
33
+ export { applyConversationSeenEvent, hydrateConversationSeen, upsertConversationSeen, useSeenStore };
34
+ //# sourceMappingURL=seen-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seen-store.js","names":[],"sources":["../../src/realtime/seen-store.ts"],"sourcesContent":["import {\n\tapplyConversationSeenEvent as applyEvent,\n\tcreateSeenStore,\n\thydrateConversationSeen as hydrateStore,\n\ttype SeenActorType,\n\ttype SeenEntry,\n\ttype SeenState,\n\tupsertConversationSeen as upsertStore,\n} from \"@cossistant/core\";\nimport type { RealtimeEvent } from \"@cossistant/types/realtime-events\";\nimport type { ConversationSeen } from \"@cossistant/types/schemas\";\nimport { useRef, useSyncExternalStore } from \"react\";\n\nconst store = createSeenStore();\n\ntype Selector<T> = (state: SeenState) => T;\n\ntype EqualityChecker<T> = (previous: T, next: T) => boolean;\n\nfunction useSelector<TSelected>(\n\tselector: Selector<TSelected>,\n\tisEqual: EqualityChecker<TSelected> = Object.is\n): TSelected {\n\tconst selectionRef = useRef<TSelected>(undefined);\n\n\tconst subscribe = (onStoreChange: () => void) =>\n\t\tstore.subscribe(() => {\n\t\t\tonStoreChange();\n\t\t});\n\n\tconst snapshot = useSyncExternalStore(\n\t\tsubscribe,\n\t\tstore.getState,\n\t\tstore.getState\n\t);\n\n\tconst selected = selector(snapshot);\n\n\tif (\n\t\tselectionRef.current === undefined ||\n\t\t!isEqual(selectionRef.current, selected)\n\t) {\n\t\tselectionRef.current = selected;\n\t}\n\n\treturn selectionRef.current as TSelected;\n}\n\nexport function useSeenStore<TSelected>(\n\tselector: Selector<TSelected>,\n\tisEqual?: EqualityChecker<TSelected>\n): TSelected {\n\treturn useSelector(selector, isEqual);\n}\n\nexport function hydrateConversationSeen(\n\tconversationId: string,\n\tentries: ConversationSeen[]\n) {\n\thydrateStore(store, conversationId, entries);\n}\n\nexport function upsertConversationSeen(options: {\n\tconversationId: string;\n\tactorType: SeenActorType;\n\tactorId: string;\n\tlastSeenAt: Date;\n}) {\n\tupsertStore(store, {\n\t\t...options,\n\t\tlastSeenAt: options.lastSeenAt.toISOString(),\n\t});\n}\n\nexport function applyConversationSeenEvent(\n\tevent: RealtimeEvent<\"conversationSeen\">,\n\toptions?: {\n\t\tignoreVisitorId?: string | null;\n\t\tignoreUserId?: string | null;\n\t\tignoreAiAgentId?: string | null;\n\t}\n) {\n\tapplyEvent(store, event, options);\n}\n"],"mappings":";;;;AAaA,MAAM,QAAQ,iBAAiB;AAM/B,SAAS,YACR,UACA,UAAsC,OAAO,IACjC;CACZ,MAAM,eAAe,OAAkB,OAAU;CAEjD,MAAM,aAAa,kBAClB,MAAM,gBAAgB;AACrB,iBAAe;GACd;CAEH,MAAM,WAAW,qBAChB,WACA,MAAM,UACN,MAAM,SACN;CAED,MAAM,WAAW,SAAS,SAAS;AAEnC,KACC,aAAa,YAAY,UACzB,CAAC,QAAQ,aAAa,SAAS,SAAS,CAExC,cAAa,UAAU;AAGxB,QAAO,aAAa;;AAGrB,SAAgB,aACf,UACA,SACY;AACZ,QAAO,YAAY,UAAU,QAAQ;;AAGtC,SAAgB,wBACf,gBACA,SACC;AACD,2BAAa,OAAO,gBAAgB,QAAQ;;AAG7C,SAAgB,uBAAuB,SAKpC;AACF,0BAAY,OAAO;EAClB,GAAG;EACH,YAAY,QAAQ,WAAW,aAAa;EAC5C,CAAC;;AAGH,SAAgB,2BACf,OACA,SAKC;AACD,8BAAW,OAAO,OAAO,QAAQ"}
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+
4
+ //#region src/realtime/support-provider.d.ts
5
+ type SupportRealtimeProviderProps = {
6
+ children: React.ReactNode;
7
+ };
8
+ /**
9
+ * Bridges websocket events into the core client stores so support hooks stay
10
+ * in sync without forcing refetches.
11
+ */
12
+ declare function SupportRealtimeProvider({
13
+ children
14
+ }: SupportRealtimeProviderProps): react_jsx_runtime0.JSX.Element;
15
+ //#endregion
16
+ export { SupportRealtimeProvider };
17
+ //# sourceMappingURL=support-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"support-provider.d.ts","names":[],"sources":["../../src/realtime/support-provider.tsx"],"sourcesContent":[],"mappings":";;;;KAkBK,4BAAA;YACM,KAAA,CAAM;;AAjBc;AAwB/B;;;AAEG,iBAFa,uBAAA,CAEb;EAAA;AAAA,CAAA,EAAA,4BAAA,CAAA,EAA4B,kBAAA,CAAA,GAAA,CAAA,OAA5B"}
@@ -0,0 +1,54 @@
1
+ import { applyConversationSeenEvent } from "./seen-store.js";
2
+ import { applyConversationTypingEvent, clearTypingFromTimelineItem } from "./typing-store.js";
3
+ import { useRealtime } from "./use-realtime.js";
4
+ import { useSupport } from "../provider.js";
5
+ import { useMemo } from "react";
6
+ import { Fragment, jsx } from "react/jsx-runtime";
7
+
8
+ //#region src/realtime/support-provider.tsx
9
+ /**
10
+ * Bridges websocket events into the core client stores so support hooks stay
11
+ * in sync without forcing refetches.
12
+ */
13
+ function SupportRealtimeProvider({ children }) {
14
+ const { website, client, visitor } = useSupport();
15
+ const realtimeContext = useMemo(() => ({
16
+ websiteId: website?.id ?? null,
17
+ visitorId: visitor?.id ?? null,
18
+ client
19
+ }), [
20
+ website?.id,
21
+ visitor?.id,
22
+ client
23
+ ]);
24
+ const events = useMemo(() => ({
25
+ timelineItemCreated: (_data, { event, context }) => {
26
+ if (context.websiteId && event.payload.websiteId !== context.websiteId) return;
27
+ clearTypingFromTimelineItem(event);
28
+ context.client.handleRealtimeEvent(event);
29
+ },
30
+ conversationSeen: (_data, { event, context }) => {
31
+ if (context.websiteId && event.payload.websiteId !== context.websiteId) return;
32
+ applyConversationSeenEvent(event);
33
+ },
34
+ conversationTyping: (_data, { event, context }) => {
35
+ if (context.websiteId && event.payload.websiteId !== context.websiteId) return;
36
+ applyConversationTypingEvent(event, { ignoreVisitorId: context.visitorId });
37
+ },
38
+ conversationEventCreated: (_data, { event, context }) => {
39
+ if (context.websiteId && event.payload.websiteId !== context.websiteId) return;
40
+ context.client.handleRealtimeEvent(event);
41
+ }
42
+ }), []);
43
+ useRealtime({
44
+ context: realtimeContext,
45
+ events,
46
+ websiteId: realtimeContext.websiteId,
47
+ visitorId: realtimeContext.visitorId
48
+ });
49
+ return /* @__PURE__ */ jsx(Fragment, { children });
50
+ }
51
+
52
+ //#endregion
53
+ export { SupportRealtimeProvider };
54
+ //# sourceMappingURL=support-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"support-provider.js","names":[],"sources":["../../src/realtime/support-provider.tsx"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport type { RealtimeEvent } from \"@cossistant/types/realtime-events\";\nimport type React from \"react\";\nimport { useMemo } from \"react\";\nimport { useSupport } from \"../provider\";\nimport { applyConversationSeenEvent } from \"./seen-store\";\nimport {\n\tapplyConversationTypingEvent,\n\tclearTypingFromTimelineItem,\n} from \"./typing-store\";\nimport { useRealtime } from \"./use-realtime\";\n\ntype SupportRealtimeContext = {\n\twebsiteId: string | null;\n\tvisitorId: string | null;\n\tclient: CossistantClient;\n};\n\ntype SupportRealtimeProviderProps = {\n\tchildren: React.ReactNode;\n};\n\n/**\n * Bridges websocket events into the core client stores so support hooks stay\n * in sync without forcing refetches.\n */\nexport function SupportRealtimeProvider({\n\tchildren,\n}: SupportRealtimeProviderProps) {\n\tconst { website, client, visitor } = useSupport();\n\n\tconst realtimeContext = useMemo<SupportRealtimeContext>(\n\t\t() => ({\n\t\t\twebsiteId: website?.id ?? null,\n\t\t\tvisitorId: visitor?.id ?? null,\n\t\t\tclient,\n\t\t}),\n\t\t[website?.id, visitor?.id, client]\n\t);\n\n\tconst events = useMemo(\n\t\t() => ({\n\t\t\ttimelineItemCreated: (\n\t\t\t\t_data: unknown,\n\t\t\t\t{\n\t\t\t\t\tevent,\n\t\t\t\t\tcontext,\n\t\t\t\t}: {\n\t\t\t\t\tevent: RealtimeEvent<\"timelineItemCreated\">;\n\t\t\t\t\tcontext: SupportRealtimeContext;\n\t\t\t\t}\n\t\t\t) => {\n\t\t\t\tif (\n\t\t\t\t\tcontext.websiteId &&\n\t\t\t\t\tevent.payload.websiteId !== context.websiteId\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Clear typing state when a timeline item is created\n\t\t\t\tclearTypingFromTimelineItem(event);\n\n\t\t\t\tcontext.client.handleRealtimeEvent(event);\n\t\t\t},\n\t\t\tconversationSeen: (\n\t\t\t\t_data: unknown,\n\t\t\t\t{\n\t\t\t\t\tevent,\n\t\t\t\t\tcontext,\n\t\t\t\t}: {\n\t\t\t\t\tevent: RealtimeEvent<\"conversationSeen\">;\n\t\t\t\t\tcontext: SupportRealtimeContext;\n\t\t\t\t}\n\t\t\t) => {\n\t\t\t\tif (\n\t\t\t\t\tcontext.websiteId &&\n\t\t\t\t\tevent.payload.websiteId !== context.websiteId\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Update the seen store so the UI reflects who has seen messages\n\t\t\t\tapplyConversationSeenEvent(event);\n\t\t\t},\n\t\t\tconversationTyping: (\n\t\t\t\t_data: unknown,\n\t\t\t\t{\n\t\t\t\t\tevent,\n\t\t\t\t\tcontext,\n\t\t\t\t}: {\n\t\t\t\t\tevent: RealtimeEvent<\"conversationTyping\">;\n\t\t\t\t\tcontext: SupportRealtimeContext;\n\t\t\t\t}\n\t\t\t) => {\n\t\t\t\tif (\n\t\t\t\t\tcontext.websiteId &&\n\t\t\t\t\tevent.payload.websiteId !== context.websiteId\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Update typing store, but ignore events from the current visitor (their own typing)\n\t\t\t\t// Note: We use context.visitorId which is fresh from the context object\n\t\t\t\tapplyConversationTypingEvent(event, {\n\t\t\t\t\tignoreVisitorId: context.visitorId,\n\t\t\t\t});\n\t\t\t},\n\t\t\tconversationEventCreated: (\n\t\t\t\t_data: unknown,\n\t\t\t\t{\n\t\t\t\t\tevent,\n\t\t\t\t\tcontext,\n\t\t\t\t}: {\n\t\t\t\t\tevent: RealtimeEvent<\"conversationEventCreated\">;\n\t\t\t\t\tcontext: SupportRealtimeContext;\n\t\t\t\t}\n\t\t\t) => {\n\t\t\t\tif (\n\t\t\t\t\tcontext.websiteId &&\n\t\t\t\t\tevent.payload.websiteId !== context.websiteId\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcontext.client.handleRealtimeEvent(event);\n\t\t\t},\n\t\t}),\n\t\t// Empty dependencies is fine here since we use the context parameter\n\t\t// which always has fresh data from the memoized realtimeContext\n\t\t[]\n\t);\n\n\tuseRealtime<SupportRealtimeContext>({\n\t\tcontext: realtimeContext,\n\t\tevents,\n\t\twebsiteId: realtimeContext.websiteId,\n\t\tvisitorId: realtimeContext.visitorId,\n\t});\n\n\treturn <>{children}</>;\n}\n"],"mappings":";;;;;;;;;;;;AA0BA,SAAgB,wBAAwB,EACvC,YACgC;CAChC,MAAM,EAAE,SAAS,QAAQ,YAAY,YAAY;CAEjD,MAAM,kBAAkB,eAChB;EACN,WAAW,SAAS,MAAM;EAC1B,WAAW,SAAS,MAAM;EAC1B;EACA,GACD;EAAC,SAAS;EAAI,SAAS;EAAI;EAAO,CAClC;CAED,MAAM,SAAS,eACP;EACN,sBACC,OACA,EACC,OACA,cAKG;AACJ,OACC,QAAQ,aACR,MAAM,QAAQ,cAAc,QAAQ,UAEpC;AAID,+BAA4B,MAAM;AAElC,WAAQ,OAAO,oBAAoB,MAAM;;EAE1C,mBACC,OACA,EACC,OACA,cAKG;AACJ,OACC,QAAQ,aACR,MAAM,QAAQ,cAAc,QAAQ,UAEpC;AAID,8BAA2B,MAAM;;EAElC,qBACC,OACA,EACC,OACA,cAKG;AACJ,OACC,QAAQ,aACR,MAAM,QAAQ,cAAc,QAAQ,UAEpC;AAKD,gCAA6B,OAAO,EACnC,iBAAiB,QAAQ,WACzB,CAAC;;EAEH,2BACC,OACA,EACC,OACA,cAKG;AACJ,OACC,QAAQ,aACR,MAAM,QAAQ,cAAc,QAAQ,UAEpC;AAGD,WAAQ,OAAO,oBAAoB,MAAM;;EAE1C,GAGD,EAAE,CACF;AAED,aAAoC;EACnC,SAAS;EACT;EACA,WAAW,gBAAgB;EAC3B,WAAW,gBAAgB;EAC3B,CAAC;AAEF,QAAO,gCAAG,WAAY"}
@@ -0,0 +1,30 @@
1
+ import { RealtimeEvent } from "../realtime-events.js";
2
+ import { TypingActorType, TypingState } from "@cossistant/core";
3
+
4
+ //#region src/realtime/typing-store.d.ts
5
+ type Selector<T> = (state: TypingState) => T;
6
+ type EqualityChecker<T> = (previous: T, next: T) => boolean;
7
+ declare function useTypingStore<TSelected>(selector: Selector<TSelected>, isEqual?: EqualityChecker<TSelected>): TSelected;
8
+ declare function setTypingState(options: {
9
+ conversationId: string;
10
+ actorType: TypingActorType;
11
+ actorId: string;
12
+ isTyping: boolean;
13
+ preview?: string | null;
14
+ ttlMs?: number;
15
+ }): void;
16
+ declare function clearTypingState(options: {
17
+ conversationId: string;
18
+ actorType: TypingActorType;
19
+ actorId: string;
20
+ }): void;
21
+ declare function applyConversationTypingEvent(event: RealtimeEvent<"conversationTyping">, options?: {
22
+ ignoreVisitorId?: string | null;
23
+ ignoreUserId?: string | null;
24
+ ignoreAiAgentId?: string | null;
25
+ ttlMs?: number;
26
+ }): void;
27
+ declare function clearTypingFromTimelineItem(event: RealtimeEvent<"timelineItemCreated">): void;
28
+ //#endregion
29
+ export { applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingState, setTypingState, useTypingStore };
30
+ //# sourceMappingURL=typing-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typing-store.d.ts","names":[],"sources":["../../src/realtime/typing-store.ts"],"sourcesContent":[],"mappings":";;;;KAgBK,sBAAsB,gBAAgB;KAEtC,gCAAgC,SAAS;AAFzC,iBAiCW,cAjCH,CAAA,SAAA,CAAA,CAAA,QAAA,EAkCF,QAlCE,CAkCO,SAlCP,CAAA,EAAA,OAAA,CAAA,EAmCF,eAnCE,CAmCc,SAnCd,CAAA,CAAA,EAoCV,SApCU;AAAA,iBAwCG,cAAA,CAxCH,OAAA,EAAA;gBAAc,EAAA,MAAA;WAAgB,EA0C/B,eA1C+B;EAAC,OAAA,EAAA,MAAA;EAEvC,QAAA,EAAA,OAAA;EAAe,OAAA,CAAA,EAAA,MAAA,GAAA,IAAA;OAAiB,CAAA,EAAA,MAAA;QAAS;AAAC,iBAiD/B,gBAAA,CAjD+B,OAAA,EAAA;EA+B/B,cAAA,EAAA,MAAc;EAAA,SAAA,EAoBlB,eApBkB;SACV,EAAA,MAAA;QAAT;AACgB,iBAwBX,4BAAA,CAxBW,KAAA,EAyBnB,aAzBmB,CAAA,oBAAA,CAAA,EAAA,OAgB3B,CAhB2B,EAAA;iBAAhB,CAAA,EAAA,MAAA,GAAA,IAAA;cACR,CAAA,EAAA,MAAA,GAAA,IAAA;EAAS,eAAA,CAAA,EAAA,MAAA,GAAA,IAAA;EAII,KAAA,CAAA,EAAA,MAAA;AAWhB,CAAA,CAAA,EAAgB,IAAA;AAQA,iBAYA,2BAAA,CAXR,KAAA,EAYA,aAZa,CAAA,qBAAA,CAAA,CAAA,EAAA,IAAA"}
@@ -0,0 +1,34 @@
1
+ import { useRef, useSyncExternalStore } from "react";
2
+ import { applyConversationTypingEvent as applyConversationTypingEvent$1, clearTypingFromTimelineItem as clearTypingFromTimelineItem$1, clearTypingState as clearTypingState$1, createTypingStore, setTypingState as setTypingState$1 } from "@cossistant/core";
3
+
4
+ //#region src/realtime/typing-store.ts
5
+ const store = createTypingStore();
6
+ function useSelector(selector, isEqual = Object.is) {
7
+ const selectionRef = useRef(void 0);
8
+ const subscribe = (onStoreChange) => store.subscribe(() => {
9
+ onStoreChange();
10
+ });
11
+ const snapshot = useSyncExternalStore(subscribe, store.getState, store.getState);
12
+ const selected = selector(snapshot);
13
+ if (selectionRef.current === void 0 || !isEqual(selectionRef.current, selected)) selectionRef.current = selected;
14
+ return selectionRef.current;
15
+ }
16
+ function useTypingStore(selector, isEqual) {
17
+ return useSelector(selector, isEqual);
18
+ }
19
+ function setTypingState(options) {
20
+ setTypingState$1(store, options);
21
+ }
22
+ function clearTypingState(options) {
23
+ clearTypingState$1(store, options);
24
+ }
25
+ function applyConversationTypingEvent(event, options) {
26
+ applyConversationTypingEvent$1(store, event, options);
27
+ }
28
+ function clearTypingFromTimelineItem(event) {
29
+ clearTypingFromTimelineItem$1(store, event);
30
+ }
31
+
32
+ //#endregion
33
+ export { applyConversationTypingEvent, clearTypingFromTimelineItem, clearTypingState, setTypingState, useTypingStore };
34
+ //# sourceMappingURL=typing-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typing-store.js","names":[],"sources":["../../src/realtime/typing-store.ts"],"sourcesContent":["import {\n\tapplyConversationTypingEvent as applyEvent,\n\ttype ConversationTypingState,\n\tclearTypingFromTimelineItem as clearFromTimelineItem,\n\tclearTypingState as clearState,\n\tcreateTypingStore,\n\tsetTypingState as setState,\n\ttype TypingActorType,\n\ttype TypingEntry,\n\ttype TypingState,\n} from \"@cossistant/core\";\nimport type { RealtimeEvent } from \"@cossistant/types/realtime-events\";\nimport { useRef, useSyncExternalStore } from \"react\";\n\nconst store = createTypingStore();\n\ntype Selector<T> = (state: TypingState) => T;\n\ntype EqualityChecker<T> = (previous: T, next: T) => boolean;\n\nfunction useSelector<TSelected>(\n\tselector: Selector<TSelected>,\n\tisEqual: EqualityChecker<TSelected> = Object.is\n): TSelected {\n\tconst selectionRef = useRef<TSelected>(undefined);\n\n\tconst subscribe = (onStoreChange: () => void) =>\n\t\tstore.subscribe(() => {\n\t\t\tonStoreChange();\n\t\t});\n\n\tconst snapshot = useSyncExternalStore(\n\t\tsubscribe,\n\t\tstore.getState,\n\t\tstore.getState\n\t);\n\n\tconst selected = selector(snapshot);\n\n\tif (\n\t\tselectionRef.current === undefined ||\n\t\t!isEqual(selectionRef.current, selected)\n\t) {\n\t\tselectionRef.current = selected;\n\t}\n\n\treturn selectionRef.current as TSelected;\n}\n\nexport function useTypingStore<TSelected>(\n\tselector: Selector<TSelected>,\n\tisEqual?: EqualityChecker<TSelected>\n): TSelected {\n\treturn useSelector(selector, isEqual);\n}\n\nexport function setTypingState(options: {\n\tconversationId: string;\n\tactorType: TypingActorType;\n\tactorId: string;\n\tisTyping: boolean;\n\tpreview?: string | null;\n\tttlMs?: number;\n}) {\n\tsetState(store, options);\n}\n\nexport function clearTypingState(options: {\n\tconversationId: string;\n\tactorType: TypingActorType;\n\tactorId: string;\n}) {\n\tclearState(store, options);\n}\n\nexport function applyConversationTypingEvent(\n\tevent: RealtimeEvent<\"conversationTyping\">,\n\toptions?: {\n\t\tignoreVisitorId?: string | null;\n\t\tignoreUserId?: string | null;\n\t\tignoreAiAgentId?: string | null;\n\t\tttlMs?: number;\n\t}\n) {\n\tapplyEvent(store, event, options);\n}\n\nexport function clearTypingFromTimelineItem(\n\tevent: RealtimeEvent<\"timelineItemCreated\">\n) {\n\tclearFromTimelineItem(store, event);\n}\n"],"mappings":";;;;AAcA,MAAM,QAAQ,mBAAmB;AAMjC,SAAS,YACR,UACA,UAAsC,OAAO,IACjC;CACZ,MAAM,eAAe,OAAkB,OAAU;CAEjD,MAAM,aAAa,kBAClB,MAAM,gBAAgB;AACrB,iBAAe;GACd;CAEH,MAAM,WAAW,qBAChB,WACA,MAAM,UACN,MAAM,SACN;CAED,MAAM,WAAW,SAAS,SAAS;AAEnC,KACC,aAAa,YAAY,UACzB,CAAC,QAAQ,aAAa,SAAS,SAAS,CAExC,cAAa,UAAU;AAGxB,QAAO,aAAa;;AAGrB,SAAgB,eACf,UACA,SACY;AACZ,QAAO,YAAY,UAAU,QAAQ;;AAGtC,SAAgB,eAAe,SAO5B;AACF,kBAAS,OAAO,QAAQ;;AAGzB,SAAgB,iBAAiB,SAI9B;AACF,oBAAW,OAAO,QAAQ;;AAG3B,SAAgB,6BACf,OACA,SAMC;AACD,gCAAW,OAAO,OAAO,QAAQ;;AAGlC,SAAgB,4BACf,OACC;AACD,+BAAsB,OAAO,MAAM"}