@cossistant/react 0.0.29 → 0.0.31

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 (305) hide show
  1. package/README.md +3 -1
  2. package/_virtual/rolldown_runtime.js +9 -23
  3. package/hooks/index.d.ts +2 -2
  4. package/hooks/private/store/use-conversations-store.d.ts +2 -0
  5. package/hooks/private/store/use-conversations-store.d.ts.map +1 -1
  6. package/hooks/private/store/use-conversations-store.js +15 -8
  7. package/hooks/private/store/use-conversations-store.js.map +1 -1
  8. package/hooks/private/store/use-store-selector.d.ts +3 -0
  9. package/hooks/private/store/use-store-selector.d.ts.map +1 -1
  10. package/hooks/private/store/use-store-selector.js +4 -8
  11. package/hooks/private/store/use-store-selector.js.map +1 -1
  12. package/hooks/private/store/use-website-store.d.ts +3 -1
  13. package/hooks/private/store/use-website-store.d.ts.map +1 -1
  14. package/hooks/private/store/use-website-store.js +14 -6
  15. package/hooks/private/store/use-website-store.js.map +1 -1
  16. package/hooks/private/use-client-query.d.ts +1 -1
  17. package/hooks/private/use-client-query.d.ts.map +1 -1
  18. package/hooks/private/use-client-query.js +1 -0
  19. package/hooks/private/use-client-query.js.map +1 -1
  20. package/hooks/private/use-default-messages.d.ts +1 -1
  21. package/hooks/private/use-grouped-messages.d.ts +2 -2
  22. package/hooks/private/use-grouped-messages.d.ts.map +1 -1
  23. package/hooks/private/use-grouped-messages.js +34 -10
  24. package/hooks/private/use-grouped-messages.js.map +1 -1
  25. package/hooks/private/use-rest-client.d.ts +13 -3
  26. package/hooks/private/use-rest-client.d.ts.map +1 -1
  27. package/hooks/private/use-rest-client.js +49 -22
  28. package/hooks/private/use-rest-client.js.map +1 -1
  29. package/hooks/private/use-visitor-typing-reporter.d.ts +1 -1
  30. package/hooks/use-conversation-auto-seen.d.ts +1 -1
  31. package/hooks/use-conversation-page.d.ts +1 -1
  32. package/hooks/use-conversation-page.d.ts.map +1 -1
  33. package/hooks/use-conversation-page.js +13 -4
  34. package/hooks/use-conversation-page.js.map +1 -1
  35. package/hooks/use-conversation-preview.d.ts +3 -1
  36. package/hooks/use-conversation-preview.d.ts.map +1 -1
  37. package/hooks/use-conversation-preview.js +8 -4
  38. package/hooks/use-conversation-preview.js.map +1 -1
  39. package/hooks/use-conversation-seen.d.ts +1 -1
  40. package/hooks/use-conversation-timeline-items.d.ts +1 -1
  41. package/hooks/use-conversation-timeline-items.js +2 -3
  42. package/hooks/use-conversation-timeline-items.js.map +1 -1
  43. package/hooks/use-conversation-timeline.d.ts +1 -1
  44. package/hooks/use-conversation.d.ts +1 -1
  45. package/hooks/use-conversation.js +2 -3
  46. package/hooks/use-conversation.js.map +1 -1
  47. package/hooks/use-conversations.d.ts +1 -1
  48. package/hooks/use-conversations.js +5 -3
  49. package/hooks/use-conversations.js.map +1 -1
  50. package/hooks/use-create-conversation.d.ts +3 -3
  51. package/hooks/use-create-conversation.js +1 -0
  52. package/hooks/use-create-conversation.js.map +1 -1
  53. package/hooks/use-file-upload.d.ts +1 -1
  54. package/hooks/use-file-upload.js +3 -3
  55. package/hooks/use-file-upload.js.map +1 -1
  56. package/hooks/use-home-page.js +3 -3
  57. package/hooks/use-home-page.js.map +1 -1
  58. package/hooks/use-message-composer.d.ts +10 -3
  59. package/hooks/use-message-composer.d.ts.map +1 -1
  60. package/hooks/use-message-composer.js +5 -2
  61. package/hooks/use-message-composer.js.map +1 -1
  62. package/hooks/use-realtime-support.d.ts +1 -1
  63. package/hooks/use-send-message.d.ts +8 -2
  64. package/hooks/use-send-message.d.ts.map +1 -1
  65. package/hooks/use-send-message.js +5 -3
  66. package/hooks/use-send-message.js.map +1 -1
  67. package/hooks/use-visitor.js +2 -2
  68. package/hooks/use-visitor.js.map +1 -1
  69. package/identify-visitor.d.ts.map +1 -1
  70. package/identify-visitor.js +15 -1
  71. package/identify-visitor.js.map +1 -1
  72. package/index.d.ts +2 -2
  73. package/index.js +1 -1
  74. package/package.json +6 -3
  75. package/{conversation.d.ts → packages/types/src/api/conversation.d.ts} +374 -64
  76. package/packages/types/src/api/conversation.d.ts.map +1 -0
  77. package/packages/types/src/api/timeline-item.d.ts +460 -0
  78. package/packages/types/src/api/timeline-item.d.ts.map +1 -0
  79. package/{realtime-events.d.ts → packages/types/src/realtime-events.d.ts} +449 -47
  80. package/packages/types/src/realtime-events.d.ts.map +1 -0
  81. package/{schemas3.d.ts → packages/types/src/schemas.d.ts} +97 -19
  82. package/packages/types/src/schemas.d.ts.map +1 -0
  83. package/primitives/avatar/avatar.js +1 -1
  84. package/primitives/avatar/avatar.js.map +1 -1
  85. package/primitives/avatar/fallback.js +1 -1
  86. package/primitives/avatar/fallback.js.map +1 -1
  87. package/primitives/avatar/image.js +1 -1
  88. package/primitives/avatar/image.js.map +1 -1
  89. package/primitives/button.js +1 -1
  90. package/primitives/button.js.map +1 -1
  91. package/primitives/conversation-timeline.d.ts +1 -1
  92. package/primitives/conversation-timeline.js +4 -4
  93. package/primitives/conversation-timeline.js.map +1 -1
  94. package/primitives/day-separator.js +3 -3
  95. package/primitives/day-separator.js.map +1 -1
  96. package/primitives/multimodal-input.d.ts +2 -2
  97. package/primitives/multimodal-input.d.ts.map +1 -1
  98. package/primitives/multimodal-input.js +2 -2
  99. package/primitives/multimodal-input.js.map +1 -1
  100. package/primitives/timeline-item-attachments.d.ts +1 -1
  101. package/primitives/timeline-item-attachments.js +6 -7
  102. package/primitives/timeline-item-attachments.js.map +1 -1
  103. package/primitives/timeline-item-group.d.ts +1 -1
  104. package/primitives/timeline-item-group.js +7 -7
  105. package/primitives/timeline-item-group.js.map +1 -1
  106. package/primitives/timeline-item.d.ts +1 -1
  107. package/primitives/timeline-item.d.ts.map +1 -1
  108. package/primitives/timeline-item.js +54 -14
  109. package/primitives/timeline-item.js.map +1 -1
  110. package/primitives/trigger.js +1 -1
  111. package/primitives/trigger.js.map +1 -1
  112. package/primitives/window.js +1 -1
  113. package/primitives/window.js.map +1 -1
  114. package/provider.d.ts +4 -2
  115. package/provider.d.ts.map +1 -1
  116. package/provider.js +56 -8
  117. package/provider.js.map +1 -1
  118. package/realtime/event-filter.d.ts +4 -1
  119. package/realtime/event-filter.d.ts.map +1 -1
  120. package/realtime/event-filter.js +14 -0
  121. package/realtime/event-filter.js.map +1 -1
  122. package/realtime/provider.d.ts +1 -1
  123. package/realtime/provider.d.ts.map +1 -1
  124. package/realtime/provider.js +1 -2
  125. package/realtime/provider.js.map +1 -1
  126. package/realtime/seen-store.d.ts +2 -2
  127. package/realtime/support-provider.js +3 -2
  128. package/realtime/support-provider.js.map +1 -1
  129. package/realtime/typing-store.d.ts +1 -1
  130. package/realtime/use-realtime.d.ts +1 -1
  131. package/support/components/avatar-stack.d.ts.map +1 -1
  132. package/support/components/avatar-stack.js +32 -12
  133. package/support/components/avatar-stack.js.map +1 -1
  134. package/support/components/avatar.d.ts +34 -3
  135. package/support/components/avatar.d.ts.map +1 -1
  136. package/support/components/avatar.js +61 -8
  137. package/support/components/avatar.js.map +1 -1
  138. package/support/components/button.d.ts +4 -2
  139. package/support/components/button.d.ts.map +1 -1
  140. package/support/components/button.js +3 -3
  141. package/support/components/button.js.map +1 -1
  142. package/support/components/configuration-error.d.ts +16 -0
  143. package/support/components/configuration-error.d.ts.map +1 -0
  144. package/support/components/configuration-error.js +162 -0
  145. package/support/components/configuration-error.js.map +1 -0
  146. package/support/components/content.js +1 -2
  147. package/support/components/content.js.map +1 -1
  148. package/support/components/conversation-button-link.js +18 -23
  149. package/support/components/conversation-button-link.js.map +1 -1
  150. package/support/components/conversation-event.d.ts.map +1 -1
  151. package/support/components/conversation-event.js +7 -5
  152. package/support/components/conversation-event.js.map +1 -1
  153. package/support/components/conversation-resolved-feedback.d.ts +21 -0
  154. package/support/components/conversation-resolved-feedback.d.ts.map +1 -0
  155. package/support/components/conversation-resolved-feedback.js +59 -0
  156. package/support/components/conversation-resolved-feedback.js.map +1 -0
  157. package/support/components/conversation-timeline-utils.d.ts +5 -0
  158. package/support/components/conversation-timeline-utils.d.ts.map +1 -0
  159. package/support/components/conversation-timeline-utils.js +10 -0
  160. package/support/components/conversation-timeline-utils.js.map +1 -0
  161. package/support/components/conversation-timeline.d.ts +1 -1
  162. package/support/components/conversation-timeline.d.ts.map +1 -1
  163. package/support/components/conversation-timeline.js +5 -4
  164. package/support/components/conversation-timeline.js.map +1 -1
  165. package/support/components/header.js +1 -1
  166. package/support/components/icons.d.ts +1 -1
  167. package/support/components/icons.d.ts.map +1 -1
  168. package/support/components/icons.js +6 -2
  169. package/support/components/icons.js.map +1 -1
  170. package/support/components/image-lightbox.d.ts +1 -1
  171. package/support/components/image-lightbox.js +1 -2
  172. package/support/components/image-lightbox.js.map +1 -1
  173. package/support/components/index.d.ts +2 -1
  174. package/support/components/index.js +3 -2
  175. package/support/components/multimodal-input.js +0 -1
  176. package/support/components/multimodal-input.js.map +1 -1
  177. package/support/components/navigation-tab.js +1 -1
  178. package/support/components/online-indicator.d.ts +50 -0
  179. package/support/components/online-indicator.d.ts.map +1 -0
  180. package/support/components/online-indicator.js +65 -0
  181. package/support/components/online-indicator.js.map +1 -0
  182. package/support/components/root.js +0 -1
  183. package/support/components/root.js.map +1 -1
  184. package/support/components/timeline-identification-tool.js +4 -4
  185. package/support/components/timeline-identification-tool.js.map +1 -1
  186. package/support/components/timeline-message-group.d.ts +1 -1
  187. package/support/components/timeline-message-group.d.ts.map +1 -1
  188. package/support/components/timeline-message-group.js +6 -4
  189. package/support/components/timeline-message-group.js.map +1 -1
  190. package/support/components/timeline-message-item.d.ts +1 -1
  191. package/support/components/timeline-message-item.js +4 -4
  192. package/support/components/timeline-message-item.js.map +1 -1
  193. package/support/components/trigger.js +1 -2
  194. package/support/components/trigger.js.map +1 -1
  195. package/support/components/typing-indicator.js +1 -1
  196. package/support/components/typing-indicator.js.map +1 -1
  197. package/support/context/controlled-state.js +0 -1
  198. package/support/context/controlled-state.js.map +1 -1
  199. package/support/context/events.d.ts +1 -1
  200. package/support/context/events.js +0 -1
  201. package/support/context/events.js.map +1 -1
  202. package/support/context/handle.js +0 -1
  203. package/support/context/handle.js.map +1 -1
  204. package/support/context/identification.d.ts +33 -0
  205. package/support/context/identification.d.ts.map +1 -0
  206. package/support/context/identification.js +34 -0
  207. package/support/context/identification.js.map +1 -0
  208. package/support/context/positioning.js +0 -1
  209. package/support/context/positioning.js.map +1 -1
  210. package/support/context/slots.js +0 -1
  211. package/support/context/slots.js.map +1 -1
  212. package/support/context/websocket.d.ts +1 -1
  213. package/support/context/websocket.js +0 -1
  214. package/support/context/websocket.js.map +1 -1
  215. package/support/index.d.ts.map +1 -1
  216. package/support/index.js +51 -18
  217. package/support/index.js.map +1 -1
  218. package/support/pages/conversation-history.js +2 -1
  219. package/support/pages/conversation-history.js.map +1 -1
  220. package/support/pages/conversation.d.ts +1 -1
  221. package/support/pages/conversation.d.ts.map +1 -1
  222. package/support/pages/conversation.js +34 -8
  223. package/support/pages/conversation.js.map +1 -1
  224. package/support/pages/home.js +5 -3
  225. package/support/pages/home.js.map +1 -1
  226. package/support/router.d.ts.map +1 -1
  227. package/support/router.js +4 -0
  228. package/support/router.js.map +1 -1
  229. package/support/store/support-store.js +0 -1
  230. package/support/store/support-store.js.map +1 -1
  231. package/support/{support-C7Xaw-N6.css → support-DmViRaga.css} +2 -2
  232. package/support/{support-C7Xaw-N6.css.map → support-DmViRaga.css.map} +1 -1
  233. package/support/text/index.js +1 -1
  234. package/support/text/index.js.map +1 -1
  235. package/support/text/locales/en.js +10 -1
  236. package/support/text/locales/en.js.map +1 -1
  237. package/support/text/locales/es.js +10 -1
  238. package/support/text/locales/es.js.map +1 -1
  239. package/support/text/locales/fr.js +10 -1
  240. package/support/text/locales/fr.js.map +1 -1
  241. package/support/text/locales/keys.d.ts +11 -0
  242. package/support/text/locales/keys.d.ts.map +1 -1
  243. package/support/text/locales/keys.js +3 -0
  244. package/support/text/locales/keys.js.map +1 -1
  245. package/support/utils/index.d.ts +1 -1
  246. package/support-config.js +0 -1
  247. package/support-config.js.map +1 -1
  248. package/support.css +1 -1
  249. package/tailwind.css +1 -1
  250. package/utils/conversation.d.ts.map +1 -1
  251. package/utils/conversation.js +1 -3
  252. package/utils/conversation.js.map +1 -1
  253. package/utils/use-render-element.js +2 -2
  254. package/utils/use-render-element.js.map +1 -1
  255. package/api.d.ts +0 -71
  256. package/api.d.ts.map +0 -1
  257. package/checks.d.ts +0 -189
  258. package/checks.d.ts.map +0 -1
  259. package/clsx.d.ts +0 -7
  260. package/clsx.d.ts.map +0 -1
  261. package/coerce.d.ts +0 -9
  262. package/coerce.d.ts.map +0 -1
  263. package/conversation.d.ts.map +0 -1
  264. package/core.d.ts +0 -35
  265. package/core.d.ts.map +0 -1
  266. package/errors.d.ts +0 -130
  267. package/errors.d.ts.map +0 -1
  268. package/errors2.d.ts +0 -24
  269. package/errors2.d.ts.map +0 -1
  270. package/index2.d.ts +0 -4
  271. package/index3.d.ts +0 -1
  272. package/json-schema.d.ts +0 -70
  273. package/json-schema.d.ts.map +0 -1
  274. package/metadata.d.ts +0 -1
  275. package/openapi-generator.d.ts +0 -1
  276. package/openapi-generator2.d.ts +0 -1
  277. package/openapi-generator3.d.ts +0 -1
  278. package/openapi30.d.ts +0 -125
  279. package/openapi30.d.ts.map +0 -1
  280. package/openapi31.d.ts +0 -131
  281. package/openapi31.d.ts.map +0 -1
  282. package/parse.d.ts +0 -17
  283. package/parse.d.ts.map +0 -1
  284. package/realtime-events.d.ts.map +0 -1
  285. package/registries.d.ts +0 -32
  286. package/registries.d.ts.map +0 -1
  287. package/schemas.d.ts +0 -971
  288. package/schemas.d.ts.map +0 -1
  289. package/schemas2.d.ts +0 -345
  290. package/schemas2.d.ts.map +0 -1
  291. package/schemas3.d.ts.map +0 -1
  292. package/specification-extension.d.ts +0 -9
  293. package/specification-extension.d.ts.map +0 -1
  294. package/standard-schema.d.ts +0 -121
  295. package/standard-schema.d.ts.map +0 -1
  296. package/timeline-item.d.ts +0 -227
  297. package/timeline-item.d.ts.map +0 -1
  298. package/to-json-schema.d.ts +0 -96
  299. package/to-json-schema.d.ts.map +0 -1
  300. package/util.d.ts +0 -45
  301. package/util.d.ts.map +0 -1
  302. package/versions.d.ts +0 -9
  303. package/versions.d.ts.map +0 -1
  304. package/zod-extensions.d.ts +0 -39
  305. package/zod-extensions.d.ts.map +0 -1
package/README.md CHANGED
@@ -84,7 +84,7 @@ export function Dashboard({
84
84
  defaultMessages={[
85
85
  {
86
86
  content:
87
- "Welcome in our your dashboard, if you need any help I'm here!",
87
+ "Welcome to your dashboard. If you need any help, I'm here!",
88
88
  senderType: SenderType.TeamMember,
89
89
  },
90
90
  ]}
@@ -94,6 +94,8 @@ export function Dashboard({
94
94
  }
95
95
  ```
96
96
 
97
+ Make sure `IdentifySupportVisitor` and `SupportConfig` are rendered inside `SupportProvider`, and keep `<Support />` mounted somewhere in that tree.
98
+
97
99
  ## Need help or spot a typo?
98
100
 
99
101
  Open an issue in the main repository or start a discussion so we can improve the docs together. Screenshots, reproduction steps, and suggestions are welcome.
@@ -1,32 +1,18 @@
1
1
  //#region rolldown:runtime
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (all) => {
3
+ var __export = (all, symbols) => {
9
4
  let target = {};
10
- for (var name in all) __defProp(target, name, {
11
- get: all[name],
12
- enumerable: true
13
- });
14
- return target;
15
- };
16
- var __copyProps = (to, from, except, desc) => {
17
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
18
- key = keys[i];
19
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
20
- get: ((k) => from[k]).bind(null, key),
21
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
5
+ for (var name in all) {
6
+ __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
22
9
  });
23
10
  }
24
- return to;
11
+ if (symbols) {
12
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
13
+ }
14
+ return target;
25
15
  };
26
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
27
- value: mod,
28
- enumerable: true
29
- }) : target, mod));
30
16
 
31
17
  //#endregion
32
18
  export { __export };
package/hooks/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { useClientQuery } from "./private/use-client-query.js";
2
2
  import { useDefaultMessages } from "./private/use-default-messages.js";
3
3
  import { ConversationItem, DaySeparatorItem, GroupedMessage, TimelineEventItem, TimelineToolItem, UseGroupedMessagesOptions, UseGroupedMessagesProps, useGroupedMessages } from "./private/use-grouped-messages.js";
4
4
  import { UseMultimodalInputOptions, UseMultimodalInputReturn, useMultimodalInput } from "./private/use-multimodal-input.js";
5
- import { UseClientResult, useClient } from "./private/use-rest-client.js";
5
+ import { ConfigurationError, UseClientResult, useClient } from "./private/use-rest-client.js";
6
6
  import { UseComposerRefocusOptions, UseComposerRefocusReturn, useComposerRefocus } from "./use-composer-refocus.js";
7
7
  import { UseConversationOptions, UseConversationResult, useConversation } from "./use-conversation.js";
8
8
  import { CONVERSATION_AUTO_SEEN_DELAY_MS, UseConversationAutoSeenOptions, useConversationAutoSeen } from "./use-conversation-auto-seen.js";
@@ -27,4 +27,4 @@ import { UseSoundEffectOptions, UseSoundEffectReturn, useSoundEffect } from "./u
27
27
  import { useTypingSound } from "./use-typing-sound.js";
28
28
  import { UseVisitorReturn, useVisitor } from "./use-visitor.js";
29
29
  import { WindowVisibilityFocusState, useWindowVisibilityFocus } from "./use-window-visibility-focus.js";
30
- export { CONVERSATION_AUTO_SEEN_DELAY_MS, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CreateConversationVariables, DaySeparatorItem, FileUploadPart, GroupedMessage, SendMessageOptions, SendMessageResult, TimelineEventItem, TimelineToolItem, UseClientResult, UseComposerRefocusOptions, UseComposerRefocusReturn, UseConversationAutoSeenOptions, UseConversationHistoryPageOptions, UseConversationHistoryPageReturn, UseConversationLifecycleOptions, UseConversationLifecycleReturn, UseConversationOptions, UseConversationPageOptions, UseConversationPageReturn, UseConversationPreviewOptions, UseConversationPreviewReturn, UseConversationResult, UseConversationTimelineItemsOptions, UseConversationTimelineItemsResult, UseConversationTimelineOptions, UseConversationTimelineReturn, UseConversationsOptions, UseConversationsResult, UseCreateConversationOptions, UseCreateConversationResult, UseFileUploadOptions, UseFileUploadReturn, UseGroupedMessagesOptions, UseGroupedMessagesProps, UseHomePageOptions, UseHomePageReturn, UseMessageComposerOptions, UseMessageComposerReturn, UseMultimodalInputOptions, UseMultimodalInputReturn, UseRealtimeSupportOptions, UseRealtimeSupportResult, UseScrollMaskOptions, UseScrollMaskReturn, UseSendMessageOptions, UseSendMessageResult, UseSoundEffectOptions, UseSoundEffectReturn, UseVisitorReturn, WindowVisibilityFocusState, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useFileUpload, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useTypingSound, useVisitor, useWindowVisibilityFocus };
30
+ export { CONVERSATION_AUTO_SEEN_DELAY_MS, ConfigurationError, ConversationItem, ConversationLifecycleState, ConversationPreviewAssignedAgent, ConversationPreviewLastMessage, ConversationPreviewTypingParticipant, ConversationPreviewTypingState, ConversationTimelineTypingParticipant, ConversationTypingParticipant, CreateConversationVariables, DaySeparatorItem, FileUploadPart, GroupedMessage, SendMessageOptions, SendMessageResult, TimelineEventItem, TimelineToolItem, UseClientResult, UseComposerRefocusOptions, UseComposerRefocusReturn, UseConversationAutoSeenOptions, UseConversationHistoryPageOptions, UseConversationHistoryPageReturn, UseConversationLifecycleOptions, UseConversationLifecycleReturn, UseConversationOptions, UseConversationPageOptions, UseConversationPageReturn, UseConversationPreviewOptions, UseConversationPreviewReturn, UseConversationResult, UseConversationTimelineItemsOptions, UseConversationTimelineItemsResult, UseConversationTimelineOptions, UseConversationTimelineReturn, UseConversationsOptions, UseConversationsResult, UseCreateConversationOptions, UseCreateConversationResult, UseFileUploadOptions, UseFileUploadReturn, UseGroupedMessagesOptions, UseGroupedMessagesProps, UseHomePageOptions, UseHomePageReturn, UseMessageComposerOptions, UseMessageComposerReturn, UseMultimodalInputOptions, UseMultimodalInputReturn, UseRealtimeSupportOptions, UseRealtimeSupportResult, UseScrollMaskOptions, UseScrollMaskReturn, UseSendMessageOptions, UseSendMessageResult, UseSoundEffectOptions, UseSoundEffectReturn, UseVisitorReturn, WindowVisibilityFocusState, useClient, useClientQuery, useComposerRefocus, useConversation, useConversationAutoSeen, useConversationHistoryPage, useConversationLifecycle, useConversationPage, useConversationPreview, useConversationSeen, useConversationTimeline, useConversationTimelineItems, useConversationTyping, useConversations, useCreateConversation, useDebouncedConversationSeen, useDefaultMessages, useFileUpload, useGroupedMessages, useHomePage, useMessageComposer, useMultimodalInput, useNewMessageSound, useRealtimeSupport, useScrollMask, useSendMessage, useSoundEffect, useTypingSound, useVisitor, useWindowVisibilityFocus };
@@ -9,6 +9,8 @@ type ConversationSelection = {
9
9
  /**
10
10
  * Selector hook that exposes the normalized conversations list from the
11
11
  * internal store alongside pagination metadata.
12
+ *
13
+ * Returns empty selection when client is not available (configuration error).
12
14
  */
13
15
  declare function useConversationsStore(): ConversationSelection;
14
16
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"use-conversations-store.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-conversations-store.ts"],"sourcesContent":[],"mappings":";;;;KAQK,qBAAA;iBACW;EADX,UAAA,EAEQ,sBAFa,GACV,IAAA;AAwChB,CAAA;AA4BA;;;;iBA5BgB,qBAAA,CAAA,GAAyB;;;;;iBA4BzB,mBAAA,iCAEb"}
1
+ {"version":3,"file":"use-conversations-store.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-conversations-store.ts"],"sourcesContent":[],"mappings":";;;;KAQK,qBAAA;iBACW;EADX,UAAA,EAEQ,sBAFa,GACV,IAAA;AA+ChB,CAAA;AA2BA;;;;;;iBA3BgB,qBAAA,CAAA,GAAyB;;;;;iBA2BzB,mBAAA,iCAEb"}
@@ -2,6 +2,10 @@ import { useStoreSelector } from "./use-store-selector.js";
2
2
  import { useSupport } from "../../../provider.js";
3
3
 
4
4
  //#region src/hooks/private/store/use-conversations-store.ts
5
+ const EMPTY_SELECTION = {
6
+ conversations: [],
7
+ pagination: null
8
+ };
5
9
  function areSelectionsEqual(a, b) {
6
10
  if (a === b) return true;
7
11
  if (a.pagination !== b.pagination) {
@@ -15,14 +19,18 @@ function areSelectionsEqual(a, b) {
15
19
  /**
16
20
  * Selector hook that exposes the normalized conversations list from the
17
21
  * internal store alongside pagination metadata.
22
+ *
23
+ * Returns empty selection when client is not available (configuration error).
18
24
  */
19
25
  function useConversationsStore() {
20
26
  const { client } = useSupport();
21
- if (!client) throw new Error("useConversationsStore requires a configured Cossistant client");
22
- return useStoreSelector(client.conversationsStore, (state) => ({
23
- conversations: state.ids.map((id) => state.byId[id]).filter((conversation) => conversation !== void 0),
24
- pagination: state.pagination
25
- }), areSelectionsEqual);
27
+ return useStoreSelector(client?.conversationsStore ?? null, (state) => {
28
+ if (!state) return EMPTY_SELECTION;
29
+ return {
30
+ conversations: state.ids.map((id) => state.byId[id]).filter((conversation) => conversation !== void 0),
31
+ pagination: state.pagination
32
+ };
33
+ }, areSelectionsEqual);
26
34
  }
27
35
  /**
28
36
  * Picks a single conversation entity from the store by id. Returns `null` when
@@ -30,9 +38,8 @@ function useConversationsStore() {
30
38
  */
31
39
  function useConversationById(conversationId) {
32
40
  const { client } = useSupport();
33
- if (!client) throw new Error("useConversationById requires a configured Cossistant client");
34
- return useStoreSelector(client.conversationsStore, (state) => {
35
- if (!conversationId) return null;
41
+ return useStoreSelector(client?.conversationsStore ?? null, (state) => {
42
+ if (!(state && conversationId)) return null;
36
43
  return state.byId[conversationId] ?? null;
37
44
  });
38
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-conversations-store.js","names":[],"sources":["../../../../src/hooks/private/store/use-conversations-store.ts"],"sourcesContent":["import type {\n\tConversationPagination,\n\tConversationsState,\n} from \"@cossistant/core\";\nimport type { Conversation } from \"@cossistant/types\";\nimport { useSupport } from \"../../../provider\";\nimport { useStoreSelector } from \"./use-store-selector\";\n\ntype ConversationSelection = {\n\tconversations: Conversation[];\n\tpagination: ConversationPagination | null;\n};\n\nfunction areSelectionsEqual(\n\ta: ConversationSelection,\n\tb: ConversationSelection\n): boolean {\n\tif (a === b) {\n\t\treturn true;\n\t}\n\tif (a.pagination !== b.pagination) {\n\t\tif (!(a.pagination && b.pagination)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (\n\t\t\ta.pagination.page !== b.pagination.page ||\n\t\t\ta.pagination.limit !== b.pagination.limit ||\n\t\t\ta.pagination.total !== b.pagination.total ||\n\t\t\ta.pagination.totalPages !== b.pagination.totalPages ||\n\t\t\ta.pagination.hasMore !== b.pagination.hasMore\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tif (a.conversations.length !== b.conversations.length) {\n\t\treturn false;\n\t}\n\tfor (let index = 0; index < a.conversations.length; index += 1) {\n\t\tif (a.conversations[index] !== b.conversations[index]) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n/**\n * Selector hook that exposes the normalized conversations list from the\n * internal store alongside pagination metadata.\n */\nexport function useConversationsStore(): ConversationSelection {\n\tconst { client } = useSupport();\n\n\tif (!client) {\n\t\tthrow new Error(\n\t\t\t\"useConversationsStore requires a configured Cossistant client\"\n\t\t);\n\t}\n\n\treturn useStoreSelector(\n\t\tclient.conversationsStore,\n\t\t(state: ConversationsState) => ({\n\t\t\tconversations: state.ids\n\t\t\t\t.map((id) => state.byId[id])\n\t\t\t\t.filter(\n\t\t\t\t\t(conversation): conversation is Conversation =>\n\t\t\t\t\t\tconversation !== undefined\n\t\t\t\t),\n\t\t\tpagination: state.pagination,\n\t\t}),\n\t\tareSelectionsEqual\n\t);\n}\n\n/**\n * Picks a single conversation entity from the store by id. Returns `null` when\n * the identifier is missing or the entity has not been fetched yet.\n */\nexport function useConversationById(\n\tconversationId: string | null\n): Conversation | null {\n\tconst { client } = useSupport();\n\n\tif (!client) {\n\t\tthrow new Error(\n\t\t\t\"useConversationById requires a configured Cossistant client\"\n\t\t);\n\t}\n\n\treturn useStoreSelector(client.conversationsStore, (state) => {\n\t\tif (!conversationId) {\n\t\t\treturn null;\n\t\t}\n\t\treturn state.byId[conversationId] ?? null;\n\t});\n}\n"],"mappings":";;;;AAaA,SAAS,mBACR,GACA,GACU;AACV,KAAI,MAAM,EACT,QAAO;AAER,KAAI,EAAE,eAAe,EAAE,YAAY;AAClC,MAAI,EAAE,EAAE,cAAc,EAAE,YACvB,QAAO;AAER,MACC,EAAE,WAAW,SAAS,EAAE,WAAW,QACnC,EAAE,WAAW,UAAU,EAAE,WAAW,SACpC,EAAE,WAAW,UAAU,EAAE,WAAW,SACpC,EAAE,WAAW,eAAe,EAAE,WAAW,cACzC,EAAE,WAAW,YAAY,EAAE,WAAW,QAEtC,QAAO;;AAGT,KAAI,EAAE,cAAc,WAAW,EAAE,cAAc,OAC9C,QAAO;AAER,MAAK,IAAI,QAAQ,GAAG,QAAQ,EAAE,cAAc,QAAQ,SAAS,EAC5D,KAAI,EAAE,cAAc,WAAW,EAAE,cAAc,OAC9C,QAAO;AAGT,QAAO;;;;;;AAOR,SAAgB,wBAA+C;CAC9D,MAAM,EAAE,WAAW,YAAY;AAE/B,KAAI,CAAC,OACJ,OAAM,IAAI,MACT,gEACA;AAGF,QAAO,iBACN,OAAO,qBACN,WAA+B;EAC/B,eAAe,MAAM,IACnB,KAAK,OAAO,MAAM,KAAK,IAAI,CAC3B,QACC,iBACA,iBAAiB,OAClB;EACF,YAAY,MAAM;EAClB,GACD,mBACA;;;;;;AAOF,SAAgB,oBACf,gBACsB;CACtB,MAAM,EAAE,WAAW,YAAY;AAE/B,KAAI,CAAC,OACJ,OAAM,IAAI,MACT,8DACA;AAGF,QAAO,iBAAiB,OAAO,qBAAqB,UAAU;AAC7D,MAAI,CAAC,eACJ,QAAO;AAER,SAAO,MAAM,KAAK,mBAAmB;GACpC"}
1
+ {"version":3,"file":"use-conversations-store.js","names":["EMPTY_SELECTION: ConversationSelection"],"sources":["../../../../src/hooks/private/store/use-conversations-store.ts"],"sourcesContent":["import type {\n\tConversationPagination,\n\tConversationsState,\n} from \"@cossistant/core\";\nimport type { Conversation } from \"@cossistant/types\";\nimport { useSupport } from \"../../../provider\";\nimport { useStoreSelector } from \"./use-store-selector\";\n\ntype ConversationSelection = {\n\tconversations: Conversation[];\n\tpagination: ConversationPagination | null;\n};\n\nconst EMPTY_SELECTION: ConversationSelection = {\n\tconversations: [],\n\tpagination: null,\n};\n\nfunction areSelectionsEqual(\n\ta: ConversationSelection,\n\tb: ConversationSelection\n): boolean {\n\tif (a === b) {\n\t\treturn true;\n\t}\n\tif (a.pagination !== b.pagination) {\n\t\tif (!(a.pagination && b.pagination)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (\n\t\t\ta.pagination.page !== b.pagination.page ||\n\t\t\ta.pagination.limit !== b.pagination.limit ||\n\t\t\ta.pagination.total !== b.pagination.total ||\n\t\t\ta.pagination.totalPages !== b.pagination.totalPages ||\n\t\t\ta.pagination.hasMore !== b.pagination.hasMore\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t}\n\tif (a.conversations.length !== b.conversations.length) {\n\t\treturn false;\n\t}\n\tfor (let index = 0; index < a.conversations.length; index += 1) {\n\t\tif (a.conversations[index] !== b.conversations[index]) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n/**\n * Selector hook that exposes the normalized conversations list from the\n * internal store alongside pagination metadata.\n *\n * Returns empty selection when client is not available (configuration error).\n */\nexport function useConversationsStore(): ConversationSelection {\n\tconst { client } = useSupport();\n\n\treturn useStoreSelector(\n\t\tclient?.conversationsStore ?? null,\n\t\t(state: ConversationsState | null) => {\n\t\t\tif (!state) {\n\t\t\t\treturn EMPTY_SELECTION;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tconversations: state.ids\n\t\t\t\t\t.map((id) => state.byId[id])\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(conversation): conversation is Conversation =>\n\t\t\t\t\t\t\tconversation !== undefined\n\t\t\t\t\t),\n\t\t\t\tpagination: state.pagination,\n\t\t\t};\n\t\t},\n\t\tareSelectionsEqual\n\t);\n}\n\n/**\n * Picks a single conversation entity from the store by id. Returns `null` when\n * the identifier is missing or the entity has not been fetched yet.\n */\nexport function useConversationById(\n\tconversationId: string | null\n): Conversation | null {\n\tconst { client } = useSupport();\n\n\treturn useStoreSelector(\n\t\tclient?.conversationsStore ?? null,\n\t\t(state: ConversationsState | null) => {\n\t\t\tif (!(state && conversationId)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn state.byId[conversationId] ?? null;\n\t\t}\n\t);\n}\n"],"mappings":";;;;AAaA,MAAMA,kBAAyC;CAC9C,eAAe,EAAE;CACjB,YAAY;CACZ;AAED,SAAS,mBACR,GACA,GACU;AACV,KAAI,MAAM,EACT,QAAO;AAER,KAAI,EAAE,eAAe,EAAE,YAAY;AAClC,MAAI,EAAE,EAAE,cAAc,EAAE,YACvB,QAAO;AAER,MACC,EAAE,WAAW,SAAS,EAAE,WAAW,QACnC,EAAE,WAAW,UAAU,EAAE,WAAW,SACpC,EAAE,WAAW,UAAU,EAAE,WAAW,SACpC,EAAE,WAAW,eAAe,EAAE,WAAW,cACzC,EAAE,WAAW,YAAY,EAAE,WAAW,QAEtC,QAAO;;AAGT,KAAI,EAAE,cAAc,WAAW,EAAE,cAAc,OAC9C,QAAO;AAER,MAAK,IAAI,QAAQ,GAAG,QAAQ,EAAE,cAAc,QAAQ,SAAS,EAC5D,KAAI,EAAE,cAAc,WAAW,EAAE,cAAc,OAC9C,QAAO;AAGT,QAAO;;;;;;;;AASR,SAAgB,wBAA+C;CAC9D,MAAM,EAAE,WAAW,YAAY;AAE/B,QAAO,iBACN,QAAQ,sBAAsB,OAC7B,UAAqC;AACrC,MAAI,CAAC,MACJ,QAAO;AAER,SAAO;GACN,eAAe,MAAM,IACnB,KAAK,OAAO,MAAM,KAAK,IAAI,CAC3B,QACC,iBACA,iBAAiB,OAClB;GACF,YAAY,MAAM;GAClB;IAEF,mBACA;;;;;;AAOF,SAAgB,oBACf,gBACsB;CACtB,MAAM,EAAE,WAAW,YAAY;AAE/B,QAAO,iBACN,QAAQ,sBAAsB,OAC7B,UAAqC;AACrC,MAAI,EAAE,SAAS,gBACd,QAAO;AAER,SAAO,MAAM,KAAK,mBAAmB;GAEtC"}
@@ -7,8 +7,11 @@ type BasicStore<TState> = {
7
7
  /**
8
8
  * React hook that bridges Zustand-like stores with React components by
9
9
  * memoizing selector results and resubscribing when dependencies change.
10
+ *
11
+ * Overloaded to support both nullable and non-nullable stores.
10
12
  */
11
13
  declare function useStoreSelector<TState, TSelected>(store: BasicStore<TState>, selector: (state: TState) => TSelected, isEqual?: (previous: TSelected, next: TSelected) => boolean): TSelected;
14
+ declare function useStoreSelector<TState, TSelected>(store: BasicStore<TState> | null, selector: (state: TState | null) => TSelected, isEqual?: (previous: TSelected, next: TSelected) => boolean): TSelected;
12
15
  //#endregion
13
16
  export { useStoreSelector };
14
17
  //# sourceMappingURL=use-store-selector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-store-selector.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":[],"mappings":";KAEK,+BAA+B;KAE/B,UAFA,CAAA,MAAY,CAAA,GAAA;EAEZ,QAAA,EAAA,EACQ,MADE;EACF,SAAA,CAAA,QAAA,EACQ,YADR,CACqB,MADrB,CAAA,CAAA,EAAA,GAAA,GAAA,IAAA;CACqB;;;AAOlC;;AACQ,iBADQ,gBACR,CAAA,MAAA,EAAA,SAAA,CAAA,CAAA,KAAA,EAAA,UAAA,CAAW,MAAX,CAAA,EAAA,QAAA,EAAA,CAAA,KAAA,EACW,MADX,EAAA,GACsB,SADtB,EAAA,OAAA,CAAA,EAAA,CAAA,QAAA,EAEa,SAFb,EAAA,IAAA,EAE8B,SAF9B,EAAA,GAAA,OAAA,CAAA,EAGL,SAHK"}
1
+ {"version":3,"file":"use-store-selector.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":[],"mappings":";KAEK,+BAA+B;KAE/B,UAFA,CAAA,MAAY,CAAA,GAAA;EAEZ,QAAA,EAAA,EACQ,MADE;EACF,SAAA,CAAA,QAAA,EACQ,YADR,CACqB,MADrB,CAAA,CAAA,EAAA,GAAA,GAAA,IAAA;CACqB;;;AAYlC;;;;AAE8B,iBAFd,gBAEc,CAAA,MAAA,EAAA,SAAA,CAAA,CAAA,KAAA,EADtB,UACsB,CADX,MACW,CAAA,EAAA,QAAA,EAAA,CAAA,KAAA,EAAX,MAAW,EAAA,GAAA,SAAA,EAAA,OAAA,CAAA,EAAA,CAAA,QAAA,EACR,SADQ,EAAA,IAAA,EACS,SADT,EAAA,GAAA,OAAA,CAAA,EAE3B,SAF2B;AACR,iBAGN,gBAHM,CAAA,MAAA,EAAA,SAAA,CAAA,CAAA,KAAA,EAId,UAJc,CAIH,MAJG,CAAA,GAAA,IAAA,EAAA,QAAA,EAAA,CAAA,KAAA,EAKH,MALG,GAAA,IAAA,EAAA,GAKe,SALf,EAAA,OAAA,CAAA,EAAA,CAAA,QAAA,EAMA,SANA,EAAA,IAAA,EAMiB,SANjB,EAAA,GAAA,OAAA,CAAA,EAOnB,SAPmB"}
@@ -1,16 +1,12 @@
1
1
  import { useRef, useSyncExternalStore } from "react";
2
2
 
3
3
  //#region src/hooks/private/store/use-store-selector.ts
4
- /**
5
- * React hook that bridges Zustand-like stores with React components by
6
- * memoizing selector results and resubscribing when dependencies change.
7
- */
4
+ const noopSubscribe = () => () => {};
8
5
  function useStoreSelector(store, selector, isEqual = Object.is) {
9
6
  const selectionRef = useRef(void 0);
10
- const subscribe = (onStoreChange) => store.subscribe(() => {
11
- onStoreChange();
12
- });
13
- const selected = selector(useSyncExternalStore(subscribe, () => store.getState(), () => store.getState()));
7
+ const subscribe = store ? (onStoreChange) => store.subscribe(() => onStoreChange()) : noopSubscribe;
8
+ const getSnapshot = store ? () => store.getState() : () => null;
9
+ const selected = selector(useSyncExternalStore(subscribe, getSnapshot, getSnapshot));
14
10
  if (selectionRef.current === void 0 || !isEqual(selectionRef.current, selected)) selectionRef.current = selected;
15
11
  return selectionRef.current;
16
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-store-selector.js","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":["import { useRef, useSyncExternalStore } from \"react\";\n\ntype Subscription<TState> = (state: TState) => void;\n\ntype BasicStore<TState> = {\n\tgetState(): TState;\n\tsubscribe(listener: Subscription<TState>): () => void;\n};\n\n/**\n * React hook that bridges Zustand-like stores with React components by\n * memoizing selector results and resubscribing when dependencies change.\n */\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState>,\n\tselector: (state: TState) => TSelected,\n\tisEqual: (previous: TSelected, next: TSelected) => boolean = 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\t() => store.getState(),\n\t\t() => store.getState()\n\t);\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"],"mappings":";;;;;;;AAaA,SAAgB,iBACf,OACA,UACA,UAA6D,OAAO,IACxD;CACZ,MAAM,eAAe,OAAkB,OAAU;CAEjD,MAAM,aAAa,kBAClB,MAAM,gBAAgB;AACrB,iBAAe;GACd;CAOH,MAAM,WAAW,SALA,qBAChB,iBACM,MAAM,UAAU,QAChB,MAAM,UAAU,CACtB,CACkC;AAEnC,KACC,aAAa,YAAY,UACzB,CAAC,QAAQ,aAAa,SAAS,SAAS,CAExC,cAAa,UAAU;AAGxB,QAAO,aAAa"}
1
+ {"version":3,"file":"use-store-selector.js","names":[],"sources":["../../../../src/hooks/private/store/use-store-selector.ts"],"sourcesContent":["import { useRef, useSyncExternalStore } from \"react\";\n\ntype Subscription<TState> = (state: TState) => void;\n\ntype BasicStore<TState> = {\n\tgetState(): TState;\n\tsubscribe(listener: Subscription<TState>): () => void;\n};\n\n// No-op subscribe function for null store case\nconst noopSubscribe = () => () => {};\n\n/**\n * React hook that bridges Zustand-like stores with React components by\n * memoizing selector results and resubscribing when dependencies change.\n *\n * Overloaded to support both nullable and non-nullable stores.\n */\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState>,\n\tselector: (state: TState) => TSelected,\n\tisEqual?: (previous: TSelected, next: TSelected) => boolean\n): TSelected;\n\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState> | null,\n\tselector: (state: TState | null) => TSelected,\n\tisEqual?: (previous: TSelected, next: TSelected) => boolean\n): TSelected;\n\nexport function useStoreSelector<TState, TSelected>(\n\tstore: BasicStore<TState> | null,\n\tselector: (state: TState | null) => TSelected,\n\tisEqual: (previous: TSelected, next: TSelected) => boolean = Object.is\n): TSelected {\n\tconst selectionRef = useRef<TSelected>(undefined);\n\n\t// Create stable subscribe function\n\tconst subscribe = store\n\t\t? (onStoreChange: () => void) => store.subscribe(() => onStoreChange())\n\t\t: noopSubscribe;\n\n\t// Create stable getSnapshot function\n\tconst getSnapshot = store ? () => store.getState() : () => null;\n\n\t// Always call useSyncExternalStore unconditionally\n\tconst snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\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"],"mappings":";;;AAUA,MAAM,4BAA4B;AAoBlC,SAAgB,iBACf,OACA,UACA,UAA6D,OAAO,IACxD;CACZ,MAAM,eAAe,OAAkB,OAAU;CAGjD,MAAM,YAAY,SACd,kBAA8B,MAAM,gBAAgB,eAAe,CAAC,GACrE;CAGH,MAAM,cAAc,cAAc,MAAM,UAAU,SAAS;CAK3D,MAAM,WAAW,SAFA,qBAAqB,WAAW,aAAa,YAAY,CAEvC;AAEnC,KACC,aAAa,YAAY,UACzB,CAAC,QAAQ,aAAa,SAAS,SAAS,CAExC,cAAa,UAAU;AAGxB,QAAO,aAAa"}
@@ -15,8 +15,10 @@ type UseWebsiteStoreOptions = {
15
15
  /**
16
16
  * Subscribes to the shared website store on the SDK client and exposes
17
17
  * convenient loading/error state plus a manual refresh helper.
18
+ *
19
+ * When client is null (e.g., due to configuration error), returns an idle state.
18
20
  */
19
- declare function useWebsiteStore(client: CossistantClient, options?: UseWebsiteStoreOptions): UseWebsiteStoreResult;
21
+ declare function useWebsiteStore(client: CossistantClient | null, options?: UseWebsiteStoreOptions): UseWebsiteStoreResult;
20
22
  //#endregion
21
23
  export { UseWebsiteStoreOptions, UseWebsiteStoreResult, useWebsiteStore };
22
24
  //# sourceMappingURL=use-website-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-website-store.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-website-store.ts"],"sourcesContent":[],"mappings":";;;KAgBY,qBAAA;WACF;EADE,MAAA,EAEH,YAFG,CAAA,QAAqB,CAAA;EACvB,SAAA,EAAA,OAAA;EACD,KAAA,EAED,KAFC,GAAA,IAAA;EAED,OAAA,EAAA,GAAA,GACQ,OADR,CACgB,YADhB,CAAA,SAAA,CAAA,GAAA,IAAA,CAAA;CACgB;AAAR,KAGJ,sBAAA,GAHI;EAAO,eAAA,CAAA,EAAA,MAAA,GAAA,KAAA;EAGX,oBAAA,CAAA,EAAA,OAAsB;AAqBlC,CAAA;;;;;iBAAgB,eAAA,SACP,4BACC,yBACP"}
1
+ {"version":3,"file":"use-website-store.d.ts","names":[],"sources":["../../../../src/hooks/private/store/use-website-store.ts"],"sourcesContent":[],"mappings":";;;KAgBY,qBAAA;WACF;EADE,MAAA,EAEH,YAFG,CAAA,QAAqB,CAAA;EACvB,SAAA,EAAA,OAAA;EACD,KAAA,EAED,KAFC,GAAA,IAAA;EAED,OAAA,EAAA,GAAA,GACQ,OADR,CACgB,YADhB,CAAA,SAAA,CAAA,GAAA,IAAA,CAAA;CACgB;AAAR,KAGJ,sBAAA,GAHI;EAAO,eAAA,CAAA,EAAA,MAAA,GAAA,KAAA;EAGX,oBAAA,CAAA,EAAA,OAAsB;AAuBlC,CAAA;;;;;;;iBAAgB,eAAA,SACP,mCACC,yBACP"}
@@ -3,6 +3,11 @@ import { useStoreSelector } from "./use-store-selector.js";
3
3
  import { useMemo } from "react";
4
4
 
5
5
  //#region src/hooks/private/store/use-website-store.ts
6
+ const EMPTY_STATE = {
7
+ website: null,
8
+ status: "idle",
9
+ error: null
10
+ };
6
11
  function toError(state, fallback) {
7
12
  if (fallback) return fallback;
8
13
  if (!state.error) return null;
@@ -11,24 +16,27 @@ function toError(state, fallback) {
11
16
  /**
12
17
  * Subscribes to the shared website store on the SDK client and exposes
13
18
  * convenient loading/error state plus a manual refresh helper.
19
+ *
20
+ * When client is null (e.g., due to configuration error), returns an idle state.
14
21
  */
15
22
  function useWebsiteStore(client, options = {}) {
16
- const state = useStoreSelector(client.websiteStore ?? (() => {
17
- throw new Error("Website store is not available on the client instance");
18
- })(), (current) => current);
23
+ const state = useStoreSelector(client?.websiteStore ?? null, (current) => current ? current : EMPTY_STATE);
19
24
  const query = useClientQuery({
20
25
  client,
21
26
  queryKey: "website",
22
27
  queryFn: (instance, params) => instance.fetchWebsite(params ?? {}),
23
- enabled: true,
28
+ enabled: client !== null,
24
29
  refetchInterval: options.refetchInterval ?? false,
25
30
  refetchOnWindowFocus: options.refetchOnWindowFocus ?? false,
26
31
  refetchOnMount: state.status === "idle",
27
32
  initialData: state.website ?? void 0
28
33
  });
29
34
  const error = useMemo(() => toError(state, query.error), [state, query.error]);
30
- const isLoading = query.isLoading || state.status === "loading" || state.status === "idle";
31
- const refresh = () => query.refetch({ force: true }).then((result) => result ?? client.websiteStore.getState().website).catch(() => client.websiteStore.getState().website).then((website) => website ?? null);
35
+ const isLoading = client !== null && (query.isLoading || state.status === "loading" || state.status === "idle");
36
+ const refresh = () => {
37
+ if (!client) return Promise.resolve(null);
38
+ return query.refetch({ force: true }).then((result) => result ?? client.websiteStore.getState().website).catch(() => client.websiteStore.getState().website).then((website) => website ?? null);
39
+ };
32
40
  return {
33
41
  website: state.website,
34
42
  status: state.status,
@@ -1 +1 @@
1
- {"version":3,"file":"use-website-store.js","names":[],"sources":["../../../../src/hooks/private/store/use-website-store.ts"],"sourcesContent":["import type {\n\tCossistantClient,\n\tWebsiteState,\n\tWebsiteStore,\n} from \"@cossistant/core\";\nimport type { PublicWebsiteResponse } from \"@cossistant/types\";\nimport { useMemo } from \"react\";\nimport { useClientQuery } from \"../use-client-query\";\nimport { useStoreSelector } from \"./use-store-selector\";\n\nconst EMPTY_STATE: WebsiteState = {\n\twebsite: null,\n\tstatus: \"idle\",\n\terror: null,\n};\n\nexport type UseWebsiteStoreResult = {\n\twebsite: WebsiteState[\"website\"];\n\tstatus: WebsiteState[\"status\"];\n\tisLoading: boolean;\n\terror: Error | null;\n\trefresh: () => Promise<WebsiteState[\"website\"] | null>;\n};\n\nexport type UseWebsiteStoreOptions = {\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n};\n\nfunction toError(state: WebsiteState, fallback: Error | null): Error | null {\n\tif (fallback) {\n\t\treturn fallback;\n\t}\n\n\tif (!state.error) {\n\t\treturn null;\n\t}\n\n\treturn new Error(state.error.message);\n}\n\n/**\n * Subscribes to the shared website store on the SDK client and exposes\n * convenient loading/error state plus a manual refresh helper.\n */\nexport function useWebsiteStore(\n\tclient: CossistantClient,\n\toptions: UseWebsiteStoreOptions = {}\n): UseWebsiteStoreResult {\n\tconst store =\n\t\tclient.websiteStore ??\n\t\t((): WebsiteStore => {\n\t\t\tthrow new Error(\"Website store is not available on the client instance\");\n\t\t})();\n\tconst state = useStoreSelector(store, (current) => current);\n\n\tconst query = useClientQuery<PublicWebsiteResponse, { force?: boolean }>({\n\t\tclient,\n\t\tqueryKey: \"website\",\n\t\tqueryFn: (instance, params) => instance.fetchWebsite(params ?? {}),\n\t\tenabled: true,\n\t\trefetchInterval: options.refetchInterval ?? false,\n\t\trefetchOnWindowFocus: options.refetchOnWindowFocus ?? false,\n\t\trefetchOnMount: state.status === \"idle\",\n\t\tinitialData: state.website ?? undefined,\n\t});\n\n\tconst error = useMemo(\n\t\t() => toError(state, query.error),\n\t\t[state, query.error]\n\t);\n\tconst isLoading =\n\t\tquery.isLoading || state.status === \"loading\" || state.status === \"idle\";\n\n\tconst refresh = () =>\n\t\tquery\n\t\t\t.refetch({ force: true })\n\t\t\t.then((result) => result ?? client.websiteStore.getState().website)\n\t\t\t.catch(() => client.websiteStore.getState().website)\n\t\t\t.then((website) => website ?? null);\n\n\treturn {\n\t\twebsite: state.website,\n\t\tstatus: state.status,\n\t\tisLoading,\n\t\terror,\n\t\trefresh,\n\t};\n}\n"],"mappings":";;;;;AA6BA,SAAS,QAAQ,OAAqB,UAAsC;AAC3E,KAAI,SACH,QAAO;AAGR,KAAI,CAAC,MAAM,MACV,QAAO;AAGR,QAAO,IAAI,MAAM,MAAM,MAAM,QAAQ;;;;;;AAOtC,SAAgB,gBACf,QACA,UAAkC,EAAE,EACZ;CAMxB,MAAM,QAAQ,iBAJb,OAAO,uBACc;AACpB,QAAM,IAAI,MAAM,wDAAwD;KACrE,GACkC,YAAY,QAAQ;CAE3D,MAAM,QAAQ,eAA2D;EACxE;EACA,UAAU;EACV,UAAU,UAAU,WAAW,SAAS,aAAa,UAAU,EAAE,CAAC;EAClE,SAAS;EACT,iBAAiB,QAAQ,mBAAmB;EAC5C,sBAAsB,QAAQ,wBAAwB;EACtD,gBAAgB,MAAM,WAAW;EACjC,aAAa,MAAM,WAAW;EAC9B,CAAC;CAEF,MAAM,QAAQ,cACP,QAAQ,OAAO,MAAM,MAAM,EACjC,CAAC,OAAO,MAAM,MAAM,CACpB;CACD,MAAM,YACL,MAAM,aAAa,MAAM,WAAW,aAAa,MAAM,WAAW;CAEnE,MAAM,gBACL,MACE,QAAQ,EAAE,OAAO,MAAM,CAAC,CACxB,MAAM,WAAW,UAAU,OAAO,aAAa,UAAU,CAAC,QAAQ,CAClE,YAAY,OAAO,aAAa,UAAU,CAAC,QAAQ,CACnD,MAAM,YAAY,WAAW,KAAK;AAErC,QAAO;EACN,SAAS,MAAM;EACf,QAAQ,MAAM;EACd;EACA;EACA;EACA"}
1
+ {"version":3,"file":"use-website-store.js","names":["EMPTY_STATE: WebsiteState"],"sources":["../../../../src/hooks/private/store/use-website-store.ts"],"sourcesContent":["import type {\n\tCossistantClient,\n\tWebsiteState,\n\tWebsiteStore,\n} from \"@cossistant/core\";\nimport type { PublicWebsiteResponse } from \"@cossistant/types\";\nimport { useMemo } from \"react\";\nimport { useClientQuery } from \"../use-client-query\";\nimport { useStoreSelector } from \"./use-store-selector\";\n\nconst EMPTY_STATE: WebsiteState = {\n\twebsite: null,\n\tstatus: \"idle\",\n\terror: null,\n};\n\nexport type UseWebsiteStoreResult = {\n\twebsite: WebsiteState[\"website\"];\n\tstatus: WebsiteState[\"status\"];\n\tisLoading: boolean;\n\terror: Error | null;\n\trefresh: () => Promise<WebsiteState[\"website\"] | null>;\n};\n\nexport type UseWebsiteStoreOptions = {\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n};\n\nfunction toError(state: WebsiteState, fallback: Error | null): Error | null {\n\tif (fallback) {\n\t\treturn fallback;\n\t}\n\n\tif (!state.error) {\n\t\treturn null;\n\t}\n\n\treturn new Error(state.error.message);\n}\n\n/**\n * Subscribes to the shared website store on the SDK client and exposes\n * convenient loading/error state plus a manual refresh helper.\n *\n * When client is null (e.g., due to configuration error), returns an idle state.\n */\nexport function useWebsiteStore(\n\tclient: CossistantClient | null,\n\toptions: UseWebsiteStoreOptions = {}\n): UseWebsiteStoreResult {\n\t// Handle null client (configuration error case)\n\tconst store = client?.websiteStore ?? null;\n\n\tconst state = useStoreSelector(store, (current) =>\n\t\tcurrent ? current : EMPTY_STATE\n\t);\n\n\tconst query = useClientQuery<PublicWebsiteResponse, { force?: boolean }>({\n\t\tclient,\n\t\tqueryKey: \"website\",\n\t\tqueryFn: (instance, params) => instance.fetchWebsite(params ?? {}),\n\t\tenabled: client !== null,\n\t\trefetchInterval: options.refetchInterval ?? false,\n\t\trefetchOnWindowFocus: options.refetchOnWindowFocus ?? false,\n\t\trefetchOnMount: state.status === \"idle\",\n\t\tinitialData: state.website ?? undefined,\n\t});\n\n\tconst error = useMemo(\n\t\t() => toError(state, query.error),\n\t\t[state, query.error]\n\t);\n\tconst isLoading =\n\t\tclient !== null &&\n\t\t(query.isLoading || state.status === \"loading\" || state.status === \"idle\");\n\n\tconst refresh = () => {\n\t\tif (!client) {\n\t\t\treturn Promise.resolve(null);\n\t\t}\n\t\treturn query\n\t\t\t.refetch({ force: true })\n\t\t\t.then((result) => result ?? client.websiteStore.getState().website)\n\t\t\t.catch(() => client.websiteStore.getState().website)\n\t\t\t.then((website) => website ?? null);\n\t};\n\n\treturn {\n\t\twebsite: state.website,\n\t\tstatus: state.status,\n\t\tisLoading,\n\t\terror,\n\t\trefresh,\n\t};\n}\n"],"mappings":";;;;;AAUA,MAAMA,cAA4B;CACjC,SAAS;CACT,QAAQ;CACR,OAAO;CACP;AAeD,SAAS,QAAQ,OAAqB,UAAsC;AAC3E,KAAI,SACH,QAAO;AAGR,KAAI,CAAC,MAAM,MACV,QAAO;AAGR,QAAO,IAAI,MAAM,MAAM,MAAM,QAAQ;;;;;;;;AAStC,SAAgB,gBACf,QACA,UAAkC,EAAE,EACZ;CAIxB,MAAM,QAAQ,iBAFA,QAAQ,gBAAgB,OAEC,YACtC,UAAU,UAAU,YACpB;CAED,MAAM,QAAQ,eAA2D;EACxE;EACA,UAAU;EACV,UAAU,UAAU,WAAW,SAAS,aAAa,UAAU,EAAE,CAAC;EAClE,SAAS,WAAW;EACpB,iBAAiB,QAAQ,mBAAmB;EAC5C,sBAAsB,QAAQ,wBAAwB;EACtD,gBAAgB,MAAM,WAAW;EACjC,aAAa,MAAM,WAAW;EAC9B,CAAC;CAEF,MAAM,QAAQ,cACP,QAAQ,OAAO,MAAM,MAAM,EACjC,CAAC,OAAO,MAAM,MAAM,CACpB;CACD,MAAM,YACL,WAAW,SACV,MAAM,aAAa,MAAM,WAAW,aAAa,MAAM,WAAW;CAEpE,MAAM,gBAAgB;AACrB,MAAI,CAAC,OACJ,QAAO,QAAQ,QAAQ,KAAK;AAE7B,SAAO,MACL,QAAQ,EAAE,OAAO,MAAM,CAAC,CACxB,MAAM,WAAW,UAAU,OAAO,aAAa,UAAU,CAAC,QAAQ,CAClE,YAAY,OAAO,aAAa,UAAU,CAAC,QAAQ,CACnD,MAAM,YAAY,WAAW,KAAK;;AAGrC,QAAO;EACN,SAAS,MAAM;EACf,QAAQ,MAAM;EACd;EACA;EACA;EACA"}
@@ -3,7 +3,7 @@ import { CossistantClient } from "@cossistant/core";
3
3
  //#region src/hooks/private/use-client-query.d.ts
4
4
  type QueryFn<TData, TArgs> = (client: CossistantClient, args?: TArgs | undefined) => Promise<TData>;
5
5
  type UseClientQueryOptions<TData, TArgs> = {
6
- client: CossistantClient;
6
+ client: CossistantClient | null;
7
7
  queryFn: QueryFn<TData, TArgs>;
8
8
  /**
9
9
  * Unique key to identify this query for deduplication.
@@ -1 +1 @@
1
- {"version":3,"file":"use-client-query.d.ts","names":[],"sources":["../../../src/hooks/private/use-client-query.ts"],"sourcesContent":[],"mappings":";;;KAGK,iCACI,yBACD,sBACH,QAAQ;KAER;EALA,MAAA,EAMI,gBANG;EACH,OAAA,EAMC,OAND,CAMS,KANT,EAMgB,KANhB,CAAA;EACD;;;;AACI;EAGH,QAAA,CAAA,EAAA,MAAA;EACS,OAAA,CAAA,EAAA,OAAA;EAAO,eAAA,CAAA,EAAA,MAAA,GAAA,KAAA;EAAf,oBAAA,CAAA,EAAA,OAAA;EAWK,cAAA,CAAA,EAAA,OAAA;EACA,WAAA,CAAA,EADA,KACA;EAAK,WAAA,CAAA,EAAL,KAAK;EAIf,YAAA,CAAA,EAAA,SAAoB,OAAA,EAAA;CAClB;KADF,oBAEG,CAAA,KAAA,EAAA,KAAA,CAAA,GAAA;EAEU,IAAA,EAHX,KAGW,GAAA,SAAA;EAAkB,KAAA,EAF5B,KAE4B,GAAA,IAAA;EAAR,SAAA,EAAA,OAAA;EAAO,OAAA,EAAA,CAAA,IAAA,CAAA,EAAjB,KAAiB,EAAA,GAAP,OAAO,CAAC,KAAD,GAAA,SAAA,CAAA;AAqDnC,CAAA;;;;;;AAEG,iBAFa,cAEb,CAAA,KAAA,EAAA,QAAA,IAAA,CAAA,CAAA,OAAA,EADO,qBACP,CAD6B,KAC7B,EADoC,KACpC,CAAA,CAAA,EAAA,oBAAA,CAAqB,KAArB,EAA4B,KAA5B,CAAA"}
1
+ {"version":3,"file":"use-client-query.d.ts","names":[],"sources":["../../../src/hooks/private/use-client-query.ts"],"sourcesContent":[],"mappings":";;;KAGK,iCACI,yBACD,sBACH,QAAQ;KAER;EALA,MAAA,EAMI,gBANG,GAAA,IAAA;EACH,OAAA,EAMC,OAND,CAMS,KANT,EAMgB,KANhB,CAAA;EACD;;;;AACI;EAGH,QAAA,CAAA,EAAA,MAAA;EACS,OAAA,CAAA,EAAA,OAAA;EAAO,eAAA,CAAA,EAAA,MAAA,GAAA,KAAA;EAAf,oBAAA,CAAA,EAAA,OAAA;EAWK,cAAA,CAAA,EAAA,OAAA;EACA,WAAA,CAAA,EADA,KACA;EAAK,WAAA,CAAA,EAAL,KAAK;EAIf,YAAA,CAAA,EAAA,SAAoB,OAAA,EAAA;CAClB;KADF,oBAEG,CAAA,KAAA,EAAA,KAAA,CAAA,GAAA;EAEU,IAAA,EAHX,KAGW,GAAA,SAAA;EAAkB,KAAA,EAF5B,KAE4B,GAAA,IAAA;EAAR,SAAA,EAAA,OAAA;EAAO,OAAA,EAAA,CAAA,IAAA,CAAA,EAAjB,KAAiB,EAAA,GAAP,OAAO,CAAC,KAAD,GAAA,SAAA,CAAA;AAqDnC,CAAA;;;;;;AAEG,iBAFa,cAEb,CAAA,KAAA,EAAA,QAAA,IAAA,CAAA,CAAA,OAAA,EADO,qBACP,CAD6B,KAC7B,EADoC,KACpC,CAAA,CAAA,EAAA,oBAAA,CAAqB,KAArB,EAA4B,KAA5B,CAAA"}
@@ -51,6 +51,7 @@ function useClientQuery(options) {
51
51
  argsRef.current = initialArgs;
52
52
  }, [initialArgs]);
53
53
  const execute = useCallback(async (args, ignoreEnabled = false) => {
54
+ if (!client) return dataRef.current;
54
55
  if (!(enabled || ignoreEnabled)) return dataRef.current;
55
56
  const nextArgs = args ?? argsRef.current;
56
57
  argsRef.current = nextArgs;
@@ -1 +1 @@
1
- {"version":3,"file":"use-client-query.js","names":["EMPTY_DEPENDENCIES: readonly unknown[]","raw: unknown"],"sources":["../../../src/hooks/private/use-client-query.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\ntype QueryFn<TData, TArgs> = (\n\tclient: CossistantClient,\n\targs?: TArgs | undefined\n) => Promise<TData>;\n\ntype UseClientQueryOptions<TData, TArgs> = {\n\tclient: CossistantClient;\n\tqueryFn: QueryFn<TData, TArgs>;\n\t/**\n\t * Unique key to identify this query for deduplication.\n\t * When provided, concurrent requests with the same key will share a single\n\t * in-flight promise instead of making duplicate API calls.\n\t */\n\tqueryKey?: string;\n\tenabled?: boolean;\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n\trefetchOnMount?: boolean;\n\tinitialData?: TData;\n\tinitialArgs?: TArgs;\n\tdependencies?: readonly unknown[];\n};\n\ntype UseClientQueryResult<TData, TArgs> = {\n\tdata: TData | undefined;\n\terror: Error | null;\n\tisLoading: boolean;\n\trefetch: (args?: TArgs) => Promise<TData | undefined>;\n};\n\nfunction toError(error: unknown): Error {\n\tif (error instanceof Error) {\n\t\treturn error;\n\t}\n\n\treturn new Error(typeof error === \"string\" ? error : \"Unknown error\");\n}\n\nconst EMPTY_DEPENDENCIES: readonly unknown[] = [];\n\n/**\n * Module-level cache for in-flight requests.\n * Maps query keys to their pending promises for deduplication.\n */\nconst inFlightRequests = new Map<string, Promise<unknown>>();\n\n/**\n * Execute a query with deduplication support.\n * If a query with the same key is already in flight, returns the existing promise.\n */\nfunction executeWithDeduplication<TData>(\n\tqueryKey: string | undefined,\n\tqueryFn: () => Promise<TData>\n): Promise<TData> {\n\t// No deduplication if no key provided\n\tif (!queryKey) {\n\t\treturn queryFn();\n\t}\n\n\t// Check for existing in-flight request\n\tconst existing = inFlightRequests.get(queryKey);\n\tif (existing) {\n\t\treturn existing as Promise<TData>;\n\t}\n\n\t// Create new request and track it\n\tconst promise = queryFn().finally(() => {\n\t\t// Clean up after request completes (success or error)\n\t\tinFlightRequests.delete(queryKey);\n\t});\n\n\tinFlightRequests.set(queryKey, promise);\n\treturn promise;\n}\n\n/**\n * Lightweight data-fetching abstraction that plugs into the SDK client instead\n * of React Query. It tracks loading/error state, supports polling, window\n * focus refetching and exposes a typed refetch helper.\n */\nexport function useClientQuery<TData, TArgs = void>(\n\toptions: UseClientQueryOptions<TData, TArgs>\n): UseClientQueryResult<TData, TArgs> {\n\tconst {\n\t\tclient,\n\t\tqueryFn,\n\t\tqueryKey,\n\t\tenabled = true,\n\t\trefetchInterval = false,\n\t\trefetchOnWindowFocus = false,\n\t\trefetchOnMount = true,\n\t\tinitialData,\n\t\tinitialArgs,\n\t\tdependencies = EMPTY_DEPENDENCIES,\n\t} = options;\n\n\tconst [data, setData] = useState<TData | undefined>(initialData);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [isLoading, setIsLoading] = useState(\n\t\tinitialData === undefined && Boolean(enabled)\n\t);\n\n\tconst dataRef = useRef(data);\n\tdataRef.current = data;\n\n\tconst argsRef = useRef<TArgs | undefined>(initialArgs);\n\tconst fetchIdRef = useRef(0);\n\tconst hasMountedRef = useRef(false);\n\tconst hasFetchedRef = useRef(initialData !== undefined);\n\tconst isMountedRef = useRef(true);\n\tconst queryFnRef = useRef(queryFn);\n\n\tqueryFnRef.current = queryFn;\n\n\tuseEffect(\n\t\t() => () => {\n\t\t\tisMountedRef.current = false;\n\t\t},\n\t\t[]\n\t);\n\n\tuseEffect(() => {\n\t\targsRef.current = initialArgs;\n\t}, [initialArgs]);\n\n\tconst execute = useCallback(\n\t\tasync (args?: TArgs, ignoreEnabled = false): Promise<TData | undefined> => {\n\t\t\tif (!(enabled || ignoreEnabled)) {\n\t\t\t\treturn dataRef.current;\n\t\t\t}\n\n\t\t\tconst nextArgs = args ?? argsRef.current;\n\t\t\targsRef.current = nextArgs;\n\n\t\t\tconst fetchId = fetchIdRef.current + 1;\n\t\t\tfetchIdRef.current = fetchId;\n\n\t\t\tsetIsLoading(true);\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\t// Use deduplication to share in-flight requests with the same key\n\t\t\t\tconst result = await executeWithDeduplication(queryKey, () =>\n\t\t\t\t\tqueryFnRef.current(client, nextArgs)\n\t\t\t\t);\n\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tdataRef.current = result;\n\t\t\t\tsetData(result);\n\t\t\t\tsetError(null);\n\t\t\t\tsetIsLoading(false);\n\t\t\t\thasFetchedRef.current = true;\n\n\t\t\t\treturn result;\n\t\t\t} catch (raw: unknown) {\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tconst normalized = toError(raw);\n\t\t\t\tsetError(normalized);\n\t\t\t\tsetIsLoading(false);\n\n\t\t\t\tthrow normalized;\n\t\t\t}\n\t\t},\n\t\t[client, enabled, queryKey]\n\t);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\tsetIsLoading(false);\n\t\t\treturn;\n\t\t}\n\n\t\tconst shouldFetchInitially = hasMountedRef.current\n\t\t\t? true\n\t\t\t: refetchOnMount || !hasFetchedRef.current;\n\n\t\thasMountedRef.current = true;\n\n\t\tif (!shouldFetchInitially) {\n\t\t\treturn;\n\t\t}\n\n\t\tvoid execute(argsRef.current);\n\t}, [enabled, execute, refetchOnMount, ...dependencies]);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\trefetchInterval === false ||\n\t\t\trefetchInterval === null ||\n\t\t\trefetchInterval <= 0 ||\n\t\t\ttypeof window === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst timer = window.setInterval(() => {\n\t\t\tvoid execute(argsRef.current);\n\t\t}, refetchInterval);\n\n\t\treturn () => {\n\t\t\twindow.clearInterval(timer);\n\t\t};\n\t}, [enabled, execute, refetchInterval]);\n\n\tuseEffect(() => {\n\t\tif (\n\t\t\t!refetchOnWindowFocus ||\n\t\t\ttypeof window === \"undefined\" ||\n\t\t\ttypeof document === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst handleRefetch = () => {\n\t\t\tif (!enabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvoid execute(argsRef.current);\n\t\t};\n\n\t\tconst onFocus = () => {\n\t\t\tvoid handleRefetch();\n\t\t};\n\n\t\tconst onVisibilityChange = () => {\n\t\t\tif (document.visibilityState === \"visible\") {\n\t\t\t\tvoid handleRefetch();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"focus\", onFocus);\n\t\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"focus\", onFocus);\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t};\n\t}, [enabled, execute, refetchOnWindowFocus]);\n\n\tconst refetch = useCallback(\n\t\tasync (args?: TArgs) => execute(args, true),\n\t\t[execute]\n\t);\n\n\treturn useMemo(\n\t\t() => ({\n\t\t\tdata,\n\t\t\terror,\n\t\t\tisLoading,\n\t\t\trefetch,\n\t\t}),\n\t\t[data, error, isLoading, refetch]\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-client-query.js","names":["EMPTY_DEPENDENCIES: readonly unknown[]","raw: unknown"],"sources":["../../../src/hooks/private/use-client-query.ts"],"sourcesContent":["import type { CossistantClient } from \"@cossistant/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\ntype QueryFn<TData, TArgs> = (\n\tclient: CossistantClient,\n\targs?: TArgs | undefined\n) => Promise<TData>;\n\ntype UseClientQueryOptions<TData, TArgs> = {\n\tclient: CossistantClient | null;\n\tqueryFn: QueryFn<TData, TArgs>;\n\t/**\n\t * Unique key to identify this query for deduplication.\n\t * When provided, concurrent requests with the same key will share a single\n\t * in-flight promise instead of making duplicate API calls.\n\t */\n\tqueryKey?: string;\n\tenabled?: boolean;\n\trefetchInterval?: number | false;\n\trefetchOnWindowFocus?: boolean;\n\trefetchOnMount?: boolean;\n\tinitialData?: TData;\n\tinitialArgs?: TArgs;\n\tdependencies?: readonly unknown[];\n};\n\ntype UseClientQueryResult<TData, TArgs> = {\n\tdata: TData | undefined;\n\terror: Error | null;\n\tisLoading: boolean;\n\trefetch: (args?: TArgs) => Promise<TData | undefined>;\n};\n\nfunction toError(error: unknown): Error {\n\tif (error instanceof Error) {\n\t\treturn error;\n\t}\n\n\treturn new Error(typeof error === \"string\" ? error : \"Unknown error\");\n}\n\nconst EMPTY_DEPENDENCIES: readonly unknown[] = [];\n\n/**\n * Module-level cache for in-flight requests.\n * Maps query keys to their pending promises for deduplication.\n */\nconst inFlightRequests = new Map<string, Promise<unknown>>();\n\n/**\n * Execute a query with deduplication support.\n * If a query with the same key is already in flight, returns the existing promise.\n */\nfunction executeWithDeduplication<TData>(\n\tqueryKey: string | undefined,\n\tqueryFn: () => Promise<TData>\n): Promise<TData> {\n\t// No deduplication if no key provided\n\tif (!queryKey) {\n\t\treturn queryFn();\n\t}\n\n\t// Check for existing in-flight request\n\tconst existing = inFlightRequests.get(queryKey);\n\tif (existing) {\n\t\treturn existing as Promise<TData>;\n\t}\n\n\t// Create new request and track it\n\tconst promise = queryFn().finally(() => {\n\t\t// Clean up after request completes (success or error)\n\t\tinFlightRequests.delete(queryKey);\n\t});\n\n\tinFlightRequests.set(queryKey, promise);\n\treturn promise;\n}\n\n/**\n * Lightweight data-fetching abstraction that plugs into the SDK client instead\n * of React Query. It tracks loading/error state, supports polling, window\n * focus refetching and exposes a typed refetch helper.\n */\nexport function useClientQuery<TData, TArgs = void>(\n\toptions: UseClientQueryOptions<TData, TArgs>\n): UseClientQueryResult<TData, TArgs> {\n\tconst {\n\t\tclient,\n\t\tqueryFn,\n\t\tqueryKey,\n\t\tenabled = true,\n\t\trefetchInterval = false,\n\t\trefetchOnWindowFocus = false,\n\t\trefetchOnMount = true,\n\t\tinitialData,\n\t\tinitialArgs,\n\t\tdependencies = EMPTY_DEPENDENCIES,\n\t} = options;\n\n\tconst [data, setData] = useState<TData | undefined>(initialData);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst [isLoading, setIsLoading] = useState(\n\t\tinitialData === undefined && Boolean(enabled)\n\t);\n\n\tconst dataRef = useRef(data);\n\tdataRef.current = data;\n\n\tconst argsRef = useRef<TArgs | undefined>(initialArgs);\n\tconst fetchIdRef = useRef(0);\n\tconst hasMountedRef = useRef(false);\n\tconst hasFetchedRef = useRef(initialData !== undefined);\n\tconst isMountedRef = useRef(true);\n\tconst queryFnRef = useRef(queryFn);\n\n\tqueryFnRef.current = queryFn;\n\n\tuseEffect(\n\t\t() => () => {\n\t\t\tisMountedRef.current = false;\n\t\t},\n\t\t[]\n\t);\n\n\tuseEffect(() => {\n\t\targsRef.current = initialArgs;\n\t}, [initialArgs]);\n\n\tconst execute = useCallback(\n\t\tasync (args?: TArgs, ignoreEnabled = false): Promise<TData | undefined> => {\n\t\t\t// Handle null client (configuration error case)\n\t\t\tif (!client) {\n\t\t\t\treturn dataRef.current;\n\t\t\t}\n\n\t\t\tif (!(enabled || ignoreEnabled)) {\n\t\t\t\treturn dataRef.current;\n\t\t\t}\n\n\t\t\tconst nextArgs = args ?? argsRef.current;\n\t\t\targsRef.current = nextArgs;\n\n\t\t\tconst fetchId = fetchIdRef.current + 1;\n\t\t\tfetchIdRef.current = fetchId;\n\n\t\t\tsetIsLoading(true);\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\t// Use deduplication to share in-flight requests with the same key\n\t\t\t\tconst result = await executeWithDeduplication(queryKey, () =>\n\t\t\t\t\tqueryFnRef.current(client, nextArgs)\n\t\t\t\t);\n\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tdataRef.current = result;\n\t\t\t\tsetData(result);\n\t\t\t\tsetError(null);\n\t\t\t\tsetIsLoading(false);\n\t\t\t\thasFetchedRef.current = true;\n\n\t\t\t\treturn result;\n\t\t\t} catch (raw: unknown) {\n\t\t\t\tif (!isMountedRef.current || fetchId !== fetchIdRef.current) {\n\t\t\t\t\treturn dataRef.current;\n\t\t\t\t}\n\n\t\t\t\tconst normalized = toError(raw);\n\t\t\t\tsetError(normalized);\n\t\t\t\tsetIsLoading(false);\n\n\t\t\t\tthrow normalized;\n\t\t\t}\n\t\t},\n\t\t[client, enabled, queryKey]\n\t);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\tsetIsLoading(false);\n\t\t\treturn;\n\t\t}\n\n\t\tconst shouldFetchInitially = hasMountedRef.current\n\t\t\t? true\n\t\t\t: refetchOnMount || !hasFetchedRef.current;\n\n\t\thasMountedRef.current = true;\n\n\t\tif (!shouldFetchInitially) {\n\t\t\treturn;\n\t\t}\n\n\t\tvoid execute(argsRef.current);\n\t}, [enabled, execute, refetchOnMount, ...dependencies]);\n\n\tuseEffect(() => {\n\t\tif (!enabled) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\trefetchInterval === false ||\n\t\t\trefetchInterval === null ||\n\t\t\trefetchInterval <= 0 ||\n\t\t\ttypeof window === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst timer = window.setInterval(() => {\n\t\t\tvoid execute(argsRef.current);\n\t\t}, refetchInterval);\n\n\t\treturn () => {\n\t\t\twindow.clearInterval(timer);\n\t\t};\n\t}, [enabled, execute, refetchInterval]);\n\n\tuseEffect(() => {\n\t\tif (\n\t\t\t!refetchOnWindowFocus ||\n\t\t\ttypeof window === \"undefined\" ||\n\t\t\ttypeof document === \"undefined\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst handleRefetch = () => {\n\t\t\tif (!enabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvoid execute(argsRef.current);\n\t\t};\n\n\t\tconst onFocus = () => {\n\t\t\tvoid handleRefetch();\n\t\t};\n\n\t\tconst onVisibilityChange = () => {\n\t\t\tif (document.visibilityState === \"visible\") {\n\t\t\t\tvoid handleRefetch();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"focus\", onFocus);\n\t\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"focus\", onFocus);\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\t\t};\n\t}, [enabled, execute, refetchOnWindowFocus]);\n\n\tconst refetch = useCallback(\n\t\tasync (args?: TArgs) => execute(args, true),\n\t\t[execute]\n\t);\n\n\treturn useMemo(\n\t\t() => ({\n\t\t\tdata,\n\t\t\terror,\n\t\t\tisLoading,\n\t\t\trefetch,\n\t\t}),\n\t\t[data, error, isLoading, refetch]\n\t);\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { TimelineItem } from "../../timeline-item.js";
1
+ import { TimelineItem } from "../../packages/types/src/api/timeline-item.js";
2
2
 
3
3
  //#region src/hooks/private/use-default-messages.d.ts
4
4
  type UseDefaultMessagesParams = {
@@ -1,5 +1,5 @@
1
- import { TimelineItem } from "../../timeline-item.js";
2
- import { ConversationSeen } from "../../schemas3.js";
1
+ import { TimelineItem } from "../../packages/types/src/api/timeline-item.js";
2
+ import { ConversationSeen } from "../../packages/types/src/schemas.js";
3
3
  import { SenderType } from "@cossistant/types";
4
4
 
5
5
  //#region src/hooks/private/use-grouped-messages.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"use-grouped-messages.d.ts","names":[],"sources":["../../../src/hooks/private/use-grouped-messages.ts"],"sourcesContent":[],"mappings":";;;;;KAKY,cAAA;;EAAA,QAAA,EAAA,MAAA;EAGC,UAAA,EAAA,UAAA;EACL,KAAA,EAAA,YAAA,EAAA;EAGW,cAAA,EAAA,MAAA;EACD,aAAA,EAAA,MAAA;EAAI,gBAAA,EADH,IACG;EAGV,eAAA,EAHM,IAGW;AAM7B,CAAA;AAOY,KAbA,iBAAA,GAagB;EAMhB,IAAA,EAAA,gBAAgB;EACzB,IAAA,EAlBI,YAkBJ;EACA,SAAA,EAlBS,IAkBT;CACA;AACA,KAjBS,gBAAA,GAiBT;EAAgB,IAAA,EAAA,eAAA;EAEP,IAAA,EAjBL,YAiBK;EAMA,IAAA,EAAA,MAAA,GAAA,IAAA;EAsOC,SAAA,EA3PD,IA2PC;CAAsB;AAAA,KAxPvB,gBAAA,GAwPuB;EAAA,IAAA,EAAA,eAAA;EAIhC,IAAA,EA1PI,IA0PJ;;;KAtPS,gBAAA,GACT,iBACA,oBACA,mBACA;KAES,yBAAA;SACJ;aACI;;;KAIA,uBAAA,GAA0B;;;;;;;cAsOzB;;;;GAIV"}
1
+ {"version":3,"file":"use-grouped-messages.d.ts","names":[],"sources":["../../../src/hooks/private/use-grouped-messages.ts"],"sourcesContent":[],"mappings":";;;;;KAKY,cAAA;;EAAA,QAAA,EAAA,MAAA;EAGC,UAAA,EAAA,UAAA;EACL,KAAA,EAAA,YAAA,EAAA;EAGW,cAAA,EAAA,MAAA;EACD,aAAA,EAAA,MAAA;EAAI,gBAAA,EADH,IACG;EAGV,eAAA,EAHM,IAGW;AAM7B,CAAA;AAOY,KAbA,iBAAA,GAagB;EAMhB,IAAA,EAAA,gBAAgB;EACzB,IAAA,EAlBI,YAkBJ;EACA,SAAA,EAlBS,IAkBT;CACA;AACA,KAjBS,gBAAA,GAiBT;EAAgB,IAAA,EAAA,eAAA;EAEP,IAAA,EAjBL,YAiBK;EAMA,IAAA,EAAA,MAAA,GAAA,IAAA;EA6OC,SAAA,EAlQD,IAkQC;CAAsB;AAAA,KA/PvB,gBAAA,GA+PuB;EAAA,IAAA,EAAA,eAAA;EAIhC,IAAA,EAjQI,IAiQJ;;;KA7PS,gBAAA,GACT,iBACA,oBACA,mBACA;KAES,yBAAA;SACJ;aACI;;;KAIA,uBAAA,GAA0B;;;;;;;cA6OzB;;;;GAIV"}
@@ -107,7 +107,7 @@ const groupTimelineItems = (items) => {
107
107
  if (currentGroup) result.push(currentGroup);
108
108
  return result;
109
109
  };
110
- const buildTimelineReadReceiptData = (seenData, items, sortedMessageItems) => {
110
+ const buildTimelineReadReceiptData = (seenData, items, sortedMessageItems, sortedMessageTimes) => {
111
111
  const seenByMap = /* @__PURE__ */ new Map();
112
112
  const lastReadMessageMap = /* @__PURE__ */ new Map();
113
113
  const unreadCountMap = /* @__PURE__ */ new Map();
@@ -118,13 +118,17 @@ const buildTimelineReadReceiptData = (seenData, items, sortedMessageItems) => {
118
118
  if (!viewerId) continue;
119
119
  let lastReadItem = null;
120
120
  let unreadCount = 0;
121
- for (const item of sortedMessageItems) if (getTimestamp(item.createdAt) <= seenTime) {
122
- if (item.id) {
123
- const seenBy = seenByMap.get(item.id);
124
- if (seenBy) seenBy.add(viewerId);
125
- }
126
- lastReadItem = item;
127
- } else unreadCount++;
121
+ for (let index = 0; index < sortedMessageItems.length; index++) {
122
+ const item = sortedMessageItems[index];
123
+ if (!item) continue;
124
+ if ((sortedMessageTimes[index] ?? getTimestamp(item.createdAt)) <= seenTime) {
125
+ if (item.id) {
126
+ const seenBy = seenByMap.get(item.id);
127
+ if (seenBy) seenBy.add(viewerId);
128
+ }
129
+ lastReadItem = item;
130
+ } else unreadCount++;
131
+ }
128
132
  if (lastReadItem?.id) lastReadMessageMap.set(viewerId, lastReadItem.id);
129
133
  unreadCountMap.set(viewerId, unreadCount);
130
134
  }
@@ -143,13 +147,33 @@ const buildTimelineReadReceiptData = (seenData, items, sortedMessageItems) => {
143
147
  const useGroupedMessages = ({ items, seenData = [], currentViewerId }) => {
144
148
  return useMemo(() => {
145
149
  const groupedItems = groupTimelineItems(items);
146
- const sortedMessageItems = items.filter((item) => item.type === "message").sort((a, b) => getTimestamp(a.createdAt) - getTimestamp(b.createdAt));
150
+ const messageItems = items.filter((item) => item.type === "message");
151
+ let sortedMessageItems = messageItems;
152
+ let sortedMessageTimes = messageItems.map((item) => getTimestamp(item.createdAt));
153
+ let isSorted = true;
154
+ for (let index = 1; index < sortedMessageTimes.length; index++) {
155
+ const currentTime = sortedMessageTimes[index];
156
+ const previousTime = sortedMessageTimes[index - 1];
157
+ if (currentTime !== void 0 && previousTime !== void 0 && currentTime < previousTime) {
158
+ isSorted = false;
159
+ break;
160
+ }
161
+ }
162
+ if (!isSorted) {
163
+ const itemsWithTimes = messageItems.map((item, index) => ({
164
+ item,
165
+ time: sortedMessageTimes[index] ?? 0
166
+ }));
167
+ itemsWithTimes.sort((a, b) => a.time - b.time);
168
+ sortedMessageItems = itemsWithTimes.map((entry) => entry.item);
169
+ sortedMessageTimes = itemsWithTimes.map((entry) => entry.time);
170
+ }
147
171
  const messageIndexMap = /* @__PURE__ */ new Map();
148
172
  for (let i = 0; i < sortedMessageItems.length; i++) {
149
173
  const item = sortedMessageItems[i];
150
174
  if (item?.id) messageIndexMap.set(item.id, i);
151
175
  }
152
- const { seenByMap, lastReadMessageMap, unreadCountMap } = buildTimelineReadReceiptData(seenData, items, sortedMessageItems);
176
+ const { seenByMap, lastReadMessageMap, unreadCountMap } = buildTimelineReadReceiptData(seenData, items, sortedMessageItems, sortedMessageTimes);
153
177
  const seenByArrayCache = /* @__PURE__ */ new Map();
154
178
  return {
155
179
  items: groupedItems,
@@ -1 +1 @@
1
- {"version":3,"file":"use-grouped-messages.js","names":["EMPTY_STRING_ARRAY: readonly string[]","result: ConversationItem[]","currentGroup: GroupedMessage | null","currentDayString: string | null","lastReadItem: TimelineItem | null"],"sources":["../../../src/hooks/private/use-grouped-messages.ts"],"sourcesContent":["import { SenderType } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type { ConversationSeen } from \"@cossistant/types/schemas\";\nimport { useMemo } from \"react\";\n\nexport type GroupedMessage = {\n\ttype: \"message_group\";\n\tsenderId: string;\n\tsenderType: SenderType;\n\titems: TimelineItem[];\n\tfirstMessageId: string;\n\tlastMessageId: string;\n\tfirstMessageTime: Date;\n\tlastMessageTime: Date;\n};\n\nexport type TimelineEventItem = {\n\ttype: \"timeline_event\";\n\titem: TimelineItem;\n\ttimestamp: Date;\n};\n\nexport type TimelineToolItem = {\n\ttype: \"timeline_tool\";\n\titem: TimelineItem;\n\ttool: string | null;\n\ttimestamp: Date;\n};\n\nexport type DaySeparatorItem = {\n\ttype: \"day_separator\";\n\tdate: Date;\n\tdateString: string; // ISO date string (YYYY-MM-DD) for stable keys\n};\n\nexport type ConversationItem =\n\t| GroupedMessage\n\t| TimelineEventItem\n\t| TimelineToolItem\n\t| DaySeparatorItem;\n\nexport type UseGroupedMessagesOptions = {\n\titems: TimelineItem[];\n\tseenData?: ConversationSeen[];\n\tcurrentViewerId?: string; // The ID of the current viewer (visitor, user, or AI agent)\n};\n\nexport type UseGroupedMessagesProps = UseGroupedMessagesOptions;\n\n// Helper function to safely get timestamp from Date or string\nconst getTimestamp = (date: Date | string | null | undefined): number => {\n\tif (!date) {\n\t\treturn 0;\n\t}\n\tif (typeof date === \"string\") {\n\t\treturn new Date(date).getTime();\n\t}\n\treturn date.getTime();\n};\n\n// Helper function to safely convert to Date\nconst toDate = (date: Date | string | null | undefined): Date => {\n\tif (!date) {\n\t\treturn typeof window !== \"undefined\" ? new Date() : new Date(0);\n\t}\n\tif (typeof date === \"string\") {\n\t\treturn new Date(date);\n\t}\n\treturn date;\n};\n\n// Helper to extract the date string (YYYY-MM-DD) from a Date for day comparison\nconst getDateString = (date: Date): string => {\n\tconst year = date.getFullYear();\n\tconst month = String(date.getMonth() + 1).padStart(2, \"0\");\n\tconst day = String(date.getDate()).padStart(2, \"0\");\n\treturn `${year}-${month}-${day}`;\n};\n\n// Helper to create a Date at midnight for a given date string\nconst createDayDate = (dateString: string): Date => {\n\tconst [year, month, day] = dateString.split(\"-\").map(Number);\n\treturn new Date(year ?? 0, (month ?? 1) - 1, day ?? 1, 0, 0, 0, 0);\n};\n\n// Helper to determine sender ID and type from a timeline item\nconst getSenderIdAndTypeFromTimelineItem = (\n\titem: TimelineItem\n): { senderId: string; senderType: SenderType } => {\n\tif (item.visitorId) {\n\t\treturn { senderId: item.visitorId, senderType: SenderType.VISITOR };\n\t}\n\tif (item.aiAgentId) {\n\t\treturn { senderId: item.aiAgentId, senderType: SenderType.AI };\n\t}\n\tif (item.userId) {\n\t\treturn { senderId: item.userId, senderType: SenderType.TEAM_MEMBER };\n\t}\n\n\t// Fallback\n\treturn {\n\t\tsenderId: item.id || \"default-sender\",\n\t\tsenderType: SenderType.TEAM_MEMBER,\n\t};\n};\n\nconst EMPTY_STRING_ARRAY: readonly string[] = Object.freeze([]);\n\n// Helper function to group timeline items (messages only, events stay separate)\n// Also inserts day separators when the day changes between items\nconst groupTimelineItems = (items: TimelineItem[]): ConversationItem[] => {\n\tconst result: ConversationItem[] = [];\n\tlet currentGroup: GroupedMessage | null = null;\n\tlet currentDayString: string | null = null;\n\n\tconst maybeInsertDaySeparator = (itemDate: Date): void => {\n\t\tconst itemDayString = getDateString(itemDate);\n\n\t\tif (currentDayString !== itemDayString) {\n\t\t\t// Finalize any existing group before inserting day separator\n\t\t\tif (currentGroup) {\n\t\t\t\tresult.push(currentGroup);\n\t\t\t\tcurrentGroup = null;\n\t\t\t}\n\n\t\t\t// Insert day separator\n\t\t\tresult.push({\n\t\t\t\ttype: \"day_separator\",\n\t\t\t\tdate: createDayDate(itemDayString),\n\t\t\t\tdateString: itemDayString,\n\t\t\t});\n\n\t\t\tcurrentDayString = itemDayString;\n\t\t}\n\t};\n\n\tfor (const item of items) {\n\t\tconst itemDate = toDate(item.createdAt);\n\n\t\t// Check for day boundary before processing any item\n\t\tmaybeInsertDaySeparator(itemDate);\n\n\t\t// Events don't get grouped\n\t\tif (item.type === \"event\") {\n\t\t\t// Finalize any existing group\n\t\t\tif (currentGroup) {\n\t\t\t\tresult.push(currentGroup);\n\t\t\t\tcurrentGroup = null;\n\t\t\t}\n\n\t\t\t// Add event as standalone item\n\t\t\tresult.push({\n\t\t\t\ttype: \"timeline_event\",\n\t\t\t\titem,\n\t\t\t\ttimestamp: itemDate,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (item.type === \"identification\") {\n\t\t\t// Finalize any existing group\n\t\t\tif (currentGroup) {\n\t\t\t\tresult.push(currentGroup);\n\t\t\t\tcurrentGroup = null;\n\t\t\t}\n\n\t\t\t// Add tool item as standalone entry\n\t\t\tresult.push({\n\t\t\t\ttype: \"timeline_tool\",\n\t\t\t\titem,\n\t\t\t\ttool: item.tool ?? null,\n\t\t\t\ttimestamp: itemDate,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Group messages by sender\n\t\tconst { senderId, senderType } = getSenderIdAndTypeFromTimelineItem(item);\n\n\t\tif (currentGroup && currentGroup.senderId === senderId) {\n\t\t\t// Add to existing group (day boundary already handled above)\n\t\t\tcurrentGroup.items.push(item);\n\t\t\tcurrentGroup.lastMessageId = item.id || currentGroup.lastMessageId;\n\t\t\tcurrentGroup.lastMessageTime = itemDate;\n\t\t} else {\n\t\t\t// Finalize previous group if exists\n\t\t\tif (currentGroup) {\n\t\t\t\tresult.push(currentGroup);\n\t\t\t}\n\n\t\t\t// Start new group\n\t\t\tcurrentGroup = {\n\t\t\t\ttype: \"message_group\",\n\t\t\t\tsenderId,\n\t\t\t\tsenderType,\n\t\t\t\titems: [item],\n\t\t\t\tfirstMessageId: item.id || \"\",\n\t\t\t\tlastMessageId: item.id || \"\",\n\t\t\t\tfirstMessageTime: itemDate,\n\t\t\t\tlastMessageTime: itemDate,\n\t\t\t};\n\t\t}\n\t}\n\n\tif (currentGroup) {\n\t\tresult.push(currentGroup);\n\t}\n\n\treturn result;\n};\n\n// Build read receipt data for timeline items\n// Accepts pre-sorted message items for performance\nconst buildTimelineReadReceiptData = (\n\tseenData: ConversationSeen[],\n\titems: TimelineItem[],\n\tsortedMessageItems: TimelineItem[]\n) => {\n\tconst seenByMap = new Map<string, Set<string>>();\n\tconst lastReadMessageMap = new Map<string, string>();\n\tconst unreadCountMap = new Map<string, number>();\n\n\t// Initialize map for all message-type timeline items\n\tfor (const item of items) {\n\t\tif (item.type === \"message\" && item.id) {\n\t\t\tseenByMap.set(item.id, new Set());\n\t\t}\n\t}\n\n\t// Process seen data for each viewer\n\tfor (const seen of seenData) {\n\t\tconst seenTime = getTimestamp(seen.lastSeenAt);\n\t\tconst viewerId = seen.userId || seen.visitorId || seen.aiAgentId;\n\t\tif (!viewerId) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet lastReadItem: TimelineItem | null = null;\n\t\tlet unreadCount = 0;\n\n\t\t// Process items in chronological order (using pre-sorted array)\n\t\tfor (const item of sortedMessageItems) {\n\t\t\tconst itemTime = getTimestamp(item.createdAt);\n\n\t\t\tif (itemTime <= seenTime) {\n\t\t\t\t// This item has been seen\n\t\t\t\tif (item.id) {\n\t\t\t\t\tconst seenBy = seenByMap.get(item.id);\n\t\t\t\t\tif (seenBy) {\n\t\t\t\t\t\tseenBy.add(viewerId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlastReadItem = item;\n\t\t\t} else {\n\t\t\t\t// This item is unread\n\t\t\t\tunreadCount++;\n\t\t\t}\n\t\t}\n\n\t\t// Store the last read item for this viewer\n\t\tif (lastReadItem?.id) {\n\t\t\tlastReadMessageMap.set(viewerId, lastReadItem.id);\n\t\t}\n\n\t\t// Store unread count\n\t\tunreadCountMap.set(viewerId, unreadCount);\n\t}\n\n\treturn { seenByMap, lastReadMessageMap, unreadCountMap };\n};\n\n/**\n * Batches sequential timeline items from the same sender into groups and enriches\n * them with read-receipt helpers so UIs can render conversation timelines with\n * minimal effort. Seen data is normalised into quick lookup maps for unread\n * indicators.\n */\nexport const useGroupedMessages = ({\n\titems,\n\tseenData = [],\n\tcurrentViewerId,\n}: UseGroupedMessagesOptions) => {\n\treturn useMemo(() => {\n\t\tconst groupedItems = groupTimelineItems(items);\n\n\t\t// Pre-sort message items once for reuse (performance optimization)\n\t\tconst sortedMessageItems = items\n\t\t\t.filter((item) => item.type === \"message\")\n\t\t\t.sort((a, b) => getTimestamp(a.createdAt) - getTimestamp(b.createdAt));\n\n\t\t// Build index map from sorted items for O(1) chronological lookups\n\t\t// Must use sortedMessageItems (not raw items) to ensure indices reflect time order\n\t\tconst messageIndexMap = new Map<string, number>();\n\t\tfor (let i = 0; i < sortedMessageItems.length; i++) {\n\t\t\tconst item = sortedMessageItems[i];\n\t\t\tif (item?.id) {\n\t\t\t\tmessageIndexMap.set(item.id, i);\n\t\t\t}\n\t\t}\n\n\t\t// Build read receipt data with pre-sorted items\n\t\tconst { seenByMap, lastReadMessageMap, unreadCountMap } =\n\t\t\tbuildTimelineReadReceiptData(seenData, items, sortedMessageItems);\n\n\t\t// Cache for turning seen sets into stable arrays across renders\n\t\tconst seenByArrayCache = new Map<string, readonly string[]>();\n\n\t\treturn {\n\t\t\titems: groupedItems,\n\t\t\tseenByMap,\n\t\t\tlastReadMessageMap,\n\t\t\tunreadCountMap,\n\n\t\t\tisMessageSeenByViewer: (messageId: string): boolean => {\n\t\t\t\tif (!currentViewerId) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tconst seenBy = seenByMap.get(messageId);\n\t\t\t\treturn seenBy ? seenBy.has(currentViewerId) : false;\n\t\t\t},\n\n\t\t\tgetMessageSeenBy: (messageId: string): readonly string[] => {\n\t\t\t\tif (seenByArrayCache.has(messageId)) {\n\t\t\t\t\treturn seenByArrayCache.get(messageId) ?? EMPTY_STRING_ARRAY;\n\t\t\t\t}\n\n\t\t\t\tconst seenBy = seenByMap.get(messageId);\n\t\t\t\tif (!seenBy || seenBy.size === 0) {\n\t\t\t\t\tseenByArrayCache.set(messageId, EMPTY_STRING_ARRAY);\n\t\t\t\t\treturn EMPTY_STRING_ARRAY;\n\t\t\t\t}\n\n\t\t\t\tconst result = Object.freeze(Array.from(seenBy)) as readonly string[];\n\t\t\t\tseenByArrayCache.set(messageId, result);\n\t\t\t\treturn result;\n\t\t\t},\n\n\t\t\tgetLastReadMessageId: (userId: string): string | undefined =>\n\t\t\t\tlastReadMessageMap.get(userId),\n\n\t\t\tisLastReadMessage: (messageId: string, userId: string): boolean =>\n\t\t\t\tlastReadMessageMap.get(userId) === messageId,\n\n\t\t\tgetUnreadCount: (userId: string): number =>\n\t\t\t\tunreadCountMap.get(userId) || 0,\n\n\t\t\thasUnreadAfter: (messageId: string, userId: string): boolean => {\n\t\t\t\tconst lastRead = lastReadMessageMap.get(userId);\n\t\t\t\tif (!lastRead) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Use index map for O(1) lookups instead of findIndex O(n)\n\t\t\t\tconst messageIndex = messageIndexMap.get(messageId);\n\t\t\t\tconst lastReadIndex = messageIndexMap.get(lastRead);\n\n\t\t\t\tif (messageIndex === undefined || lastReadIndex === undefined) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\treturn messageIndex < lastReadIndex;\n\t\t\t},\n\t\t};\n\t}, [items, seenData, currentViewerId]);\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-grouped-messages.js","names":["EMPTY_STRING_ARRAY: readonly string[]","result: ConversationItem[]","currentGroup: GroupedMessage | null","currentDayString: string | null","lastReadItem: TimelineItem | null"],"sources":["../../../src/hooks/private/use-grouped-messages.ts"],"sourcesContent":["import { SenderType } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport type { ConversationSeen } from \"@cossistant/types/schemas\";\nimport { useMemo } from \"react\";\n\nexport type GroupedMessage = {\n\ttype: \"message_group\";\n\tsenderId: string;\n\tsenderType: SenderType;\n\titems: TimelineItem[];\n\tfirstMessageId: string;\n\tlastMessageId: string;\n\tfirstMessageTime: Date;\n\tlastMessageTime: Date;\n};\n\nexport type TimelineEventItem = {\n\ttype: \"timeline_event\";\n\titem: TimelineItem;\n\ttimestamp: Date;\n};\n\nexport type TimelineToolItem = {\n\ttype: \"timeline_tool\";\n\titem: TimelineItem;\n\ttool: string | null;\n\ttimestamp: Date;\n};\n\nexport type DaySeparatorItem = {\n\ttype: \"day_separator\";\n\tdate: Date;\n\tdateString: string; // ISO date string (YYYY-MM-DD) for stable keys\n};\n\nexport type ConversationItem =\n\t| GroupedMessage\n\t| TimelineEventItem\n\t| TimelineToolItem\n\t| DaySeparatorItem;\n\nexport type UseGroupedMessagesOptions = {\n\titems: TimelineItem[];\n\tseenData?: ConversationSeen[];\n\tcurrentViewerId?: string; // The ID of the current viewer (visitor, user, or AI agent)\n};\n\nexport type UseGroupedMessagesProps = UseGroupedMessagesOptions;\n\n// Helper function to safely get timestamp from Date or string\nconst getTimestamp = (date: Date | string | null | undefined): number => {\n\tif (!date) {\n\t\treturn 0;\n\t}\n\tif (typeof date === \"string\") {\n\t\treturn new Date(date).getTime();\n\t}\n\treturn date.getTime();\n};\n\n// Helper function to safely convert to Date\nconst toDate = (date: Date | string | null | undefined): Date => {\n\tif (!date) {\n\t\treturn typeof window !== \"undefined\" ? new Date() : new Date(0);\n\t}\n\tif (typeof date === \"string\") {\n\t\treturn new Date(date);\n\t}\n\treturn date;\n};\n\n// Helper to extract the date string (YYYY-MM-DD) from a Date for day comparison\nconst getDateString = (date: Date): string => {\n\tconst year = date.getFullYear();\n\tconst month = String(date.getMonth() + 1).padStart(2, \"0\");\n\tconst day = String(date.getDate()).padStart(2, \"0\");\n\treturn `${year}-${month}-${day}`;\n};\n\n// Helper to create a Date at midnight for a given date string\nconst createDayDate = (dateString: string): Date => {\n\tconst [year, month, day] = dateString.split(\"-\").map(Number);\n\treturn new Date(year ?? 0, (month ?? 1) - 1, day ?? 1, 0, 0, 0, 0);\n};\n\n// Helper to determine sender ID and type from a timeline item\nconst getSenderIdAndTypeFromTimelineItem = (\n\titem: TimelineItem\n): { senderId: string; senderType: SenderType } => {\n\tif (item.visitorId) {\n\t\treturn { senderId: item.visitorId, senderType: SenderType.VISITOR };\n\t}\n\tif (item.aiAgentId) {\n\t\treturn { senderId: item.aiAgentId, senderType: SenderType.AI };\n\t}\n\tif (item.userId) {\n\t\treturn { senderId: item.userId, senderType: SenderType.TEAM_MEMBER };\n\t}\n\n\t// Fallback\n\treturn {\n\t\tsenderId: item.id || \"default-sender\",\n\t\tsenderType: SenderType.TEAM_MEMBER,\n\t};\n};\n\nconst EMPTY_STRING_ARRAY: readonly string[] = Object.freeze([]);\n\n// Helper function to group timeline items (messages only, events stay separate)\n// Also inserts day separators when the day changes between items\nconst groupTimelineItems = (items: TimelineItem[]): ConversationItem[] => {\n\tconst result: ConversationItem[] = [];\n\tlet currentGroup: GroupedMessage | null = null;\n\tlet currentDayString: string | null = null;\n\n\tconst maybeInsertDaySeparator = (itemDate: Date): void => {\n\t\tconst itemDayString = getDateString(itemDate);\n\n\t\tif (currentDayString !== itemDayString) {\n\t\t\t// Finalize any existing group before inserting day separator\n\t\t\tif (currentGroup) {\n\t\t\t\tresult.push(currentGroup);\n\t\t\t\tcurrentGroup = null;\n\t\t\t}\n\n\t\t\t// Insert day separator\n\t\t\tresult.push({\n\t\t\t\ttype: \"day_separator\",\n\t\t\t\tdate: createDayDate(itemDayString),\n\t\t\t\tdateString: itemDayString,\n\t\t\t});\n\n\t\t\tcurrentDayString = itemDayString;\n\t\t}\n\t};\n\n\tfor (const item of items) {\n\t\tconst itemDate = toDate(item.createdAt);\n\n\t\t// Check for day boundary before processing any item\n\t\tmaybeInsertDaySeparator(itemDate);\n\n\t\t// Events don't get grouped\n\t\tif (item.type === \"event\") {\n\t\t\t// Finalize any existing group\n\t\t\tif (currentGroup) {\n\t\t\t\tresult.push(currentGroup);\n\t\t\t\tcurrentGroup = null;\n\t\t\t}\n\n\t\t\t// Add event as standalone item\n\t\t\tresult.push({\n\t\t\t\ttype: \"timeline_event\",\n\t\t\t\titem,\n\t\t\t\ttimestamp: itemDate,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (item.type === \"identification\") {\n\t\t\t// Finalize any existing group\n\t\t\tif (currentGroup) {\n\t\t\t\tresult.push(currentGroup);\n\t\t\t\tcurrentGroup = null;\n\t\t\t}\n\n\t\t\t// Add tool item as standalone entry\n\t\t\tresult.push({\n\t\t\t\ttype: \"timeline_tool\",\n\t\t\t\titem,\n\t\t\t\ttool: item.tool ?? null,\n\t\t\t\ttimestamp: itemDate,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Group messages by sender\n\t\tconst { senderId, senderType } = getSenderIdAndTypeFromTimelineItem(item);\n\n\t\tif (currentGroup && currentGroup.senderId === senderId) {\n\t\t\t// Add to existing group (day boundary already handled above)\n\t\t\tcurrentGroup.items.push(item);\n\t\t\tcurrentGroup.lastMessageId = item.id || currentGroup.lastMessageId;\n\t\t\tcurrentGroup.lastMessageTime = itemDate;\n\t\t} else {\n\t\t\t// Finalize previous group if exists\n\t\t\tif (currentGroup) {\n\t\t\t\tresult.push(currentGroup);\n\t\t\t}\n\n\t\t\t// Start new group\n\t\t\tcurrentGroup = {\n\t\t\t\ttype: \"message_group\",\n\t\t\t\tsenderId,\n\t\t\t\tsenderType,\n\t\t\t\titems: [item],\n\t\t\t\tfirstMessageId: item.id || \"\",\n\t\t\t\tlastMessageId: item.id || \"\",\n\t\t\t\tfirstMessageTime: itemDate,\n\t\t\t\tlastMessageTime: itemDate,\n\t\t\t};\n\t\t}\n\t}\n\n\tif (currentGroup) {\n\t\tresult.push(currentGroup);\n\t}\n\n\treturn result;\n};\n\n// Build read receipt data for timeline items\n// Accepts pre-sorted message items for performance\nconst buildTimelineReadReceiptData = (\n\tseenData: ConversationSeen[],\n\titems: TimelineItem[],\n\tsortedMessageItems: TimelineItem[],\n\tsortedMessageTimes: number[]\n) => {\n\tconst seenByMap = new Map<string, Set<string>>();\n\tconst lastReadMessageMap = new Map<string, string>();\n\tconst unreadCountMap = new Map<string, number>();\n\n\t// Initialize map for all message-type timeline items\n\tfor (const item of items) {\n\t\tif (item.type === \"message\" && item.id) {\n\t\t\tseenByMap.set(item.id, new Set());\n\t\t}\n\t}\n\n\t// Process seen data for each viewer\n\tfor (const seen of seenData) {\n\t\tconst seenTime = getTimestamp(seen.lastSeenAt);\n\t\tconst viewerId = seen.userId || seen.visitorId || seen.aiAgentId;\n\t\tif (!viewerId) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet lastReadItem: TimelineItem | null = null;\n\t\tlet unreadCount = 0;\n\n\t\t// Process items in chronological order (using pre-sorted array)\n\t\tfor (let index = 0; index < sortedMessageItems.length; index++) {\n\t\t\tconst item = sortedMessageItems[index];\n\t\t\tif (!item) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst itemTime =\n\t\t\t\tsortedMessageTimes[index] ?? getTimestamp(item.createdAt);\n\n\t\t\tif (itemTime <= seenTime) {\n\t\t\t\t// This item has been seen\n\t\t\t\tif (item.id) {\n\t\t\t\t\tconst seenBy = seenByMap.get(item.id);\n\t\t\t\t\tif (seenBy) {\n\t\t\t\t\t\tseenBy.add(viewerId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlastReadItem = item;\n\t\t\t} else {\n\t\t\t\t// This item is unread\n\t\t\t\tunreadCount++;\n\t\t\t}\n\t\t}\n\n\t\t// Store the last read item for this viewer\n\t\tif (lastReadItem?.id) {\n\t\t\tlastReadMessageMap.set(viewerId, lastReadItem.id);\n\t\t}\n\n\t\t// Store unread count\n\t\tunreadCountMap.set(viewerId, unreadCount);\n\t}\n\n\treturn { seenByMap, lastReadMessageMap, unreadCountMap };\n};\n\n/**\n * Batches sequential timeline items from the same sender into groups and enriches\n * them with read-receipt helpers so UIs can render conversation timelines with\n * minimal effort. Seen data is normalised into quick lookup maps for unread\n * indicators.\n */\nexport const useGroupedMessages = ({\n\titems,\n\tseenData = [],\n\tcurrentViewerId,\n}: UseGroupedMessagesOptions) => {\n\treturn useMemo(() => {\n\t\tconst groupedItems = groupTimelineItems(items);\n\n\t\t// Pre-compute message items and timestamps once for reuse\n\t\tconst messageItems = items.filter((item) => item.type === \"message\");\n\t\tlet sortedMessageItems = messageItems;\n\t\tlet sortedMessageTimes = messageItems.map((item) =>\n\t\t\tgetTimestamp(item.createdAt)\n\t\t);\n\n\t\t// Avoid sorting if items are already in chronological order\n\t\tlet isSorted = true;\n\t\tfor (let index = 1; index < sortedMessageTimes.length; index++) {\n\t\t\tconst currentTime = sortedMessageTimes[index];\n\t\t\tconst previousTime = sortedMessageTimes[index - 1];\n\t\t\tif (\n\t\t\t\tcurrentTime !== undefined &&\n\t\t\t\tpreviousTime !== undefined &&\n\t\t\t\tcurrentTime < previousTime\n\t\t\t) {\n\t\t\t\tisSorted = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!isSorted) {\n\t\t\tconst itemsWithTimes = messageItems.map((item, index) => ({\n\t\t\t\titem,\n\t\t\t\ttime: sortedMessageTimes[index] ?? 0,\n\t\t\t}));\n\n\t\t\titemsWithTimes.sort((a, b) => a.time - b.time);\n\n\t\t\tsortedMessageItems = itemsWithTimes.map((entry) => entry.item);\n\t\t\tsortedMessageTimes = itemsWithTimes.map((entry) => entry.time);\n\t\t}\n\n\t\t// Build index map from sorted items for O(1) chronological lookups\n\t\t// Must use sortedMessageItems (not raw items) to ensure indices reflect time order\n\t\tconst messageIndexMap = new Map<string, number>();\n\t\tfor (let i = 0; i < sortedMessageItems.length; i++) {\n\t\t\tconst item = sortedMessageItems[i];\n\t\t\tif (item?.id) {\n\t\t\t\tmessageIndexMap.set(item.id, i);\n\t\t\t}\n\t\t}\n\n\t\t// Build read receipt data with pre-sorted items\n\t\tconst { seenByMap, lastReadMessageMap, unreadCountMap } =\n\t\t\tbuildTimelineReadReceiptData(\n\t\t\t\tseenData,\n\t\t\t\titems,\n\t\t\t\tsortedMessageItems,\n\t\t\t\tsortedMessageTimes\n\t\t\t);\n\n\t\t// Cache for turning seen sets into stable arrays across renders\n\t\tconst seenByArrayCache = new Map<string, readonly string[]>();\n\n\t\treturn {\n\t\t\titems: groupedItems,\n\t\t\tseenByMap,\n\t\t\tlastReadMessageMap,\n\t\t\tunreadCountMap,\n\n\t\t\tisMessageSeenByViewer: (messageId: string): boolean => {\n\t\t\t\tif (!currentViewerId) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tconst seenBy = seenByMap.get(messageId);\n\t\t\t\treturn seenBy ? seenBy.has(currentViewerId) : false;\n\t\t\t},\n\n\t\t\tgetMessageSeenBy: (messageId: string): readonly string[] => {\n\t\t\t\tif (seenByArrayCache.has(messageId)) {\n\t\t\t\t\treturn seenByArrayCache.get(messageId) ?? EMPTY_STRING_ARRAY;\n\t\t\t\t}\n\n\t\t\t\tconst seenBy = seenByMap.get(messageId);\n\t\t\t\tif (!seenBy || seenBy.size === 0) {\n\t\t\t\t\tseenByArrayCache.set(messageId, EMPTY_STRING_ARRAY);\n\t\t\t\t\treturn EMPTY_STRING_ARRAY;\n\t\t\t\t}\n\n\t\t\t\tconst result = Object.freeze(Array.from(seenBy)) as readonly string[];\n\t\t\t\tseenByArrayCache.set(messageId, result);\n\t\t\t\treturn result;\n\t\t\t},\n\n\t\t\tgetLastReadMessageId: (userId: string): string | undefined =>\n\t\t\t\tlastReadMessageMap.get(userId),\n\n\t\t\tisLastReadMessage: (messageId: string, userId: string): boolean =>\n\t\t\t\tlastReadMessageMap.get(userId) === messageId,\n\n\t\t\tgetUnreadCount: (userId: string): number =>\n\t\t\t\tunreadCountMap.get(userId) || 0,\n\n\t\t\thasUnreadAfter: (messageId: string, userId: string): boolean => {\n\t\t\t\tconst lastRead = lastReadMessageMap.get(userId);\n\t\t\t\tif (!lastRead) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// Use index map for O(1) lookups instead of findIndex O(n)\n\t\t\t\tconst messageIndex = messageIndexMap.get(messageId);\n\t\t\t\tconst lastReadIndex = messageIndexMap.get(lastRead);\n\n\t\t\t\tif (messageIndex === undefined || lastReadIndex === undefined) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\treturn messageIndex < lastReadIndex;\n\t\t\t},\n\t\t};\n\t}, [items, seenData, currentViewerId]);\n};\n"],"mappings":"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"}
@@ -1,17 +1,27 @@
1
1
  import { CossistantClient } from "@cossistant/core";
2
2
 
3
3
  //#region src/hooks/private/use-rest-client.d.ts
4
+ type ConfigurationError = {
5
+ type: "missing_api_key" | "invalid_api_key";
6
+ message: string;
7
+ envVarName: string;
8
+ };
4
9
  type UseClientResult = {
5
10
  client: CossistantClient;
6
- error: Error | null;
11
+ error: null;
12
+ configurationError: null;
13
+ } | {
14
+ client: null;
15
+ error: null;
16
+ configurationError: ConfigurationError;
7
17
  };
8
18
  /**
9
19
  * Creates a memoised `CossistantClient` instance using the provided endpoints
10
20
  * and public key. When no key is passed the hook falls back to environment
11
21
  * variables and surfaces missing configuration errors through the returned
12
- * `error` field.
22
+ * `configurationError` field instead of throwing.
13
23
  */
14
24
  declare function useClient(publicKey: string | undefined, apiUrl?: string, wsUrl?: string): UseClientResult;
15
25
  //#endregion
16
- export { UseClientResult, useClient };
26
+ export { ConfigurationError, UseClientResult, useClient };
17
27
  //# sourceMappingURL=use-rest-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-rest-client.d.ts","names":[],"sources":["../../../src/hooks/private/use-rest-client.ts"],"sourcesContent":[],"mappings":";;;KAMY,eAAA;UACH;EADG,KAAA,EAEJ,KAFI,GAAA,IAAe;AAW3B,CAAA;;;;;;;iBAAgB,SAAA,kEAIb"}
1
+ {"version":3,"file":"use-rest-client.d.ts","names":[],"sources":["../../../src/hooks/private/use-rest-client.ts"],"sourcesContent":[],"mappings":";;;KAMY,kBAAA;;EAAA,OAAA,EAAA,MAAA;EAMA,UAAA,EAAA,MAAe;AA8B3B,CAAA;KA9BY,eAAA;UAED;;;;;;sBAOY;;;;;;;;iBAqBP,SAAA,kEAIb"}