@flamingo-stack/openframe-frontend-core 0.0.492-1506.3956.1 → 0.0.492

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 (296) hide show
  1. package/dist/{chunk-KX3K2EH4.cjs → chunk-2XAAFAQT.cjs} +11 -11
  2. package/dist/{chunk-KX3K2EH4.cjs.map → chunk-2XAAFAQT.cjs.map} +1 -1
  3. package/dist/{chunk-JHEIOXXZ.js → chunk-4AWRPTKE.js} +5 -6
  4. package/dist/chunk-4AWRPTKE.js.map +1 -0
  5. package/dist/{chunk-PTXZB2KW.js → chunk-4J3CB4KO.js} +2 -2
  6. package/dist/{chunk-7N7YZCHR.cjs → chunk-4MLOMMDX.cjs} +5519 -7555
  7. package/dist/chunk-4MLOMMDX.cjs.map +1 -0
  8. package/dist/{chunk-MEMRQTOI.js → chunk-56J7RLMM.js} +10434 -12470
  9. package/dist/chunk-56J7RLMM.js.map +1 -0
  10. package/dist/{chunk-MEIGDK7M.cjs → chunk-5ICPQAUI.cjs} +60 -60
  11. package/dist/chunk-5ICPQAUI.cjs.map +1 -0
  12. package/dist/{chunk-BEEPU44B.js → chunk-5RHJVPL7.js} +10 -10
  13. package/dist/chunk-5RHJVPL7.js.map +1 -0
  14. package/dist/{chunk-XOFE2RT4.js → chunk-BTIRYGDT.js} +4 -4
  15. package/dist/{chunk-JSEJ5MH5.cjs → chunk-DY6AJNVK.cjs} +5 -5
  16. package/dist/{chunk-JSEJ5MH5.cjs.map → chunk-DY6AJNVK.cjs.map} +1 -1
  17. package/dist/{chunk-PLB6CQSO.js → chunk-FG5ALORQ.js} +2 -2
  18. package/dist/{chunk-QG2EBIH5.cjs → chunk-J2J36PEN.cjs} +31 -31
  19. package/dist/{chunk-QG2EBIH5.cjs.map → chunk-J2J36PEN.cjs.map} +1 -1
  20. package/dist/{chunk-A6FPYAUD.cjs → chunk-LIRXKEOE.cjs} +15 -15
  21. package/dist/{chunk-A6FPYAUD.cjs.map → chunk-LIRXKEOE.cjs.map} +1 -1
  22. package/dist/{chunk-MSL5NE3N.cjs → chunk-LVDI7R7T.cjs} +37 -37
  23. package/dist/{chunk-MSL5NE3N.cjs.map → chunk-LVDI7R7T.cjs.map} +1 -1
  24. package/dist/{chunk-MPUQFRJG.cjs → chunk-M4SCYPCR.cjs} +7 -7
  25. package/dist/{chunk-MPUQFRJG.cjs.map → chunk-M4SCYPCR.cjs.map} +1 -1
  26. package/dist/{chunk-QDGB2DKY.cjs → chunk-O6S6Q52W.cjs} +26 -26
  27. package/dist/{chunk-QDGB2DKY.cjs.map → chunk-O6S6Q52W.cjs.map} +1 -1
  28. package/dist/{chunk-W56JYDSC.cjs → chunk-OFFYASUY.cjs} +27 -28
  29. package/dist/chunk-OFFYASUY.cjs.map +1 -0
  30. package/dist/{chunk-NCAN6VUF.js → chunk-P3ITKTUE.js} +2 -2
  31. package/dist/{chunk-M6FEGPQJ.cjs → chunk-PI5BMKV3.cjs} +62 -62
  32. package/dist/{chunk-M6FEGPQJ.cjs.map → chunk-PI5BMKV3.cjs.map} +1 -1
  33. package/dist/{chunk-34TDA22K.js → chunk-QHRNQTW5.js} +2 -2
  34. package/dist/{chunk-OL3MJKUI.js → chunk-SZWMJIH3.js} +2 -2
  35. package/dist/{chunk-MZRIRQYV.js → chunk-U5LU56JE.js} +2 -2
  36. package/dist/{chunk-C65NCIMV.cjs → chunk-VML66FJQ.cjs} +9 -9
  37. package/dist/{chunk-C65NCIMV.cjs.map → chunk-VML66FJQ.cjs.map} +1 -1
  38. package/dist/{chunk-3LKB722U.js → chunk-WKWZ7LVC.js} +2 -2
  39. package/dist/{chunk-J3VBVR4W.js → chunk-WPFLV4QJ.js} +4 -4
  40. package/dist/components/case-studies/index.cjs +8 -8
  41. package/dist/components/case-studies/index.js +2 -2
  42. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  43. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  44. package/dist/components/chat/hooks/index.d.ts +1 -0
  45. package/dist/components/chat/hooks/index.d.ts.map +1 -1
  46. package/dist/components/chat/hooks/use-chat.d.ts.map +1 -1
  47. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +48 -15
  48. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  49. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts +6 -0
  50. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +1 -0
  51. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +57 -10
  52. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  53. package/dist/components/chat/index.cjs +2 -10
  54. package/dist/components/chat/index.cjs.map +1 -1
  55. package/dist/components/chat/index.d.ts +0 -1
  56. package/dist/components/chat/index.d.ts.map +1 -1
  57. package/dist/components/chat/index.js +13 -21
  58. package/dist/components/chat/types/api.types.d.ts +81 -1
  59. package/dist/components/chat/types/api.types.d.ts.map +1 -1
  60. package/dist/components/chat/types/processing.types.d.ts +93 -2
  61. package/dist/components/chat/types/processing.types.d.ts.map +1 -1
  62. package/dist/components/chat/utils/chunk-parser.d.ts +25 -0
  63. package/dist/components/chat/utils/chunk-parser.d.ts.map +1 -0
  64. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts +6 -34
  65. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
  66. package/dist/components/chat/utils/index.d.ts +3 -2
  67. package/dist/components/chat/utils/index.d.ts.map +1 -1
  68. package/dist/components/chat/utils/process-historical-messages.d.ts +11 -59
  69. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  70. package/dist/components/contact/index.cjs +3 -3
  71. package/dist/components/contact/index.js +2 -2
  72. package/dist/components/docs/index.cjs +5 -5
  73. package/dist/components/docs/index.js +4 -4
  74. package/dist/components/embeds/index.cjs +3 -3
  75. package/dist/components/embeds/index.js +2 -2
  76. package/dist/components/faq/index.cjs +3 -3
  77. package/dist/components/faq/index.js +2 -2
  78. package/dist/components/features/floating-walkthrough-video.d.ts.map +1 -1
  79. package/dist/components/features/index.cjs +2 -2
  80. package/dist/components/features/index.js +1 -1
  81. package/dist/components/help-center-pages/index.cjs +20 -20
  82. package/dist/components/help-center-pages/index.js +11 -11
  83. package/dist/components/index.cjs +130 -172
  84. package/dist/components/index.cjs.map +1 -1
  85. package/dist/components/index.js +21 -63
  86. package/dist/components/index.js.map +1 -1
  87. package/dist/components/navigation/index.cjs +2 -2
  88. package/dist/components/navigation/index.js +1 -1
  89. package/dist/components/navigation/navigation-sidebar-item.d.ts +23 -1
  90. package/dist/components/navigation/navigation-sidebar-item.d.ts.map +1 -1
  91. package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
  92. package/dist/components/onboarding-guides/index.cjs +5 -5
  93. package/dist/components/onboarding-guides/index.js +4 -4
  94. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
  95. package/dist/components/related-content/index.cjs +3 -3
  96. package/dist/components/related-content/index.js +2 -2
  97. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  98. package/dist/components/tickets/index.cjs +5 -5
  99. package/dist/components/tickets/index.js +4 -4
  100. package/dist/components/ui/data-table/data-table-column-filter.d.ts.map +1 -1
  101. package/dist/components/ui/data-table/data-table-header.d.ts.map +1 -1
  102. package/dist/components/ui/data-table/data-table-skeleton.d.ts.map +1 -1
  103. package/dist/components/ui/index.cjs +2 -44
  104. package/dist/components/ui/index.cjs.map +1 -1
  105. package/dist/components/ui/index.d.ts +2 -1
  106. package/dist/components/ui/index.d.ts.map +1 -1
  107. package/dist/components/ui/index.js +13 -55
  108. package/dist/components/ui/rich-markdown-renderer.d.ts +34 -0
  109. package/dist/components/ui/rich-markdown-renderer.d.ts.map +1 -0
  110. package/dist/components/ui/simple-markdown-renderer.d.ts +73 -0
  111. package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -0
  112. package/dist/components/ui/tab-navigation.d.ts.map +1 -1
  113. package/dist/index.cjs +2 -60
  114. package/dist/index.cjs.map +1 -1
  115. package/dist/index.js +13 -71
  116. package/dist/utils/index.cjs +40 -169
  117. package/dist/utils/index.cjs.map +1 -1
  118. package/dist/utils/index.d.ts +0 -1
  119. package/dist/utils/index.d.ts.map +1 -1
  120. package/dist/utils/index.js +41 -162
  121. package/dist/utils/index.js.map +1 -1
  122. package/dist/utils/markdown-section-extractor.d.ts.map +1 -1
  123. package/package.json +1 -7
  124. package/src/components/chat/__tests__/chat-message-list.test.tsx +5 -293
  125. package/src/components/chat/chat-message-enhanced.tsx +20 -122
  126. package/src/components/chat/chat-message-list.tsx +109 -358
  127. package/src/components/chat/embeddable-chat.tsx +0 -6
  128. package/src/components/chat/hooks/.index.md +33 -30
  129. package/src/components/chat/hooks/.use-nats-chat-adapter.md +55 -36
  130. package/src/components/chat/hooks/.use-realtime-chunk-processor.md +77 -0
  131. package/src/components/chat/hooks/index.ts +1 -0
  132. package/src/components/chat/hooks/use-chat.ts +0 -8
  133. package/src/components/chat/hooks/use-nats-chat-adapter.ts +825 -243
  134. package/src/components/chat/hooks/use-realtime-chunk-processor.ts +489 -0
  135. package/src/components/chat/hooks/use-sse-chat-adapter.ts +684 -380
  136. package/src/components/chat/index.ts +0 -5
  137. package/src/components/chat/thinking-display.tsx +1 -1
  138. package/src/components/chat/types/.api.types.md +49 -39
  139. package/src/components/chat/types/.processing.types.md +58 -29
  140. package/src/components/chat/types/api.types.ts +73 -6
  141. package/src/components/chat/types/processing.types.ts +53 -11
  142. package/src/components/chat/utils/.chunk-parser.md +62 -0
  143. package/src/components/chat/utils/.index.md +40 -31
  144. package/src/components/chat/utils/chunk-parser.ts +262 -0
  145. package/src/components/chat/utils/extract-incomplete-message-state.ts +5 -63
  146. package/src/components/chat/utils/index.ts +8 -5
  147. package/src/components/chat/utils/process-historical-messages.ts +386 -358
  148. package/src/components/features/floating-walkthrough-video.tsx +3 -1
  149. package/src/components/navigation/navigation-sidebar-item.tsx +80 -30
  150. package/src/components/navigation/navigation-sidebar.tsx +106 -46
  151. package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +4 -22
  152. package/src/components/shared/legal-document/legal-document-page.tsx +1 -1
  153. package/src/components/shared/product-release/release-detail-page.tsx +4 -19
  154. package/src/components/ui/.simple-markdown-renderer.md +52 -0
  155. package/src/components/ui/__tests__/tab-navigation.test.tsx +159 -0
  156. package/src/components/ui/data-table/data-table-column-filter.tsx +7 -1
  157. package/src/components/ui/data-table/data-table-header.tsx +4 -1
  158. package/src/components/ui/data-table/data-table-skeleton.tsx +7 -0
  159. package/src/components/ui/index.ts +2 -1
  160. package/src/components/ui/release-changelog-section.tsx +1 -1
  161. package/src/components/ui/rich-markdown-renderer.tsx +1223 -0
  162. package/src/components/ui/simple-markdown-renderer.tsx +964 -0
  163. package/src/components/ui/tab-navigation.tsx +370 -171
  164. package/src/components/ui/ticket-info-section.tsx +1 -1
  165. package/src/utils/index.ts +0 -1
  166. package/src/utils/markdown-section-extractor.ts +56 -41
  167. package/dist/chat-protocol/decode.d.ts +0 -58
  168. package/dist/chat-protocol/decode.d.ts.map +0 -1
  169. package/dist/chat-protocol/encode.d.ts +0 -44
  170. package/dist/chat-protocol/encode.d.ts.map +0 -1
  171. package/dist/chat-protocol/env-flag.d.ts +0 -35
  172. package/dist/chat-protocol/env-flag.d.ts.map +0 -1
  173. package/dist/chat-protocol/events.d.ts +0 -207
  174. package/dist/chat-protocol/events.d.ts.map +0 -1
  175. package/dist/chat-protocol/frames.d.ts +0 -213
  176. package/dist/chat-protocol/frames.d.ts.map +0 -1
  177. package/dist/chat-protocol/index.cjs +0 -500
  178. package/dist/chat-protocol/index.cjs.map +0 -1
  179. package/dist/chat-protocol/index.d.ts +0 -18
  180. package/dist/chat-protocol/index.d.ts.map +0 -1
  181. package/dist/chat-protocol/index.js +0 -484
  182. package/dist/chat-protocol/index.js.map +0 -1
  183. package/dist/chat-protocol/ip-normalize.d.ts +0 -44
  184. package/dist/chat-protocol/ip-normalize.d.ts.map +0 -1
  185. package/dist/chat-protocol/nats-decoder.d.ts +0 -26
  186. package/dist/chat-protocol/nats-decoder.d.ts.map +0 -1
  187. package/dist/chunk-7N7YZCHR.cjs.map +0 -1
  188. package/dist/chunk-BEEPU44B.js.map +0 -1
  189. package/dist/chunk-JHEIOXXZ.js.map +0 -1
  190. package/dist/chunk-MEIGDK7M.cjs.map +0 -1
  191. package/dist/chunk-MEMRQTOI.js.map +0 -1
  192. package/dist/chunk-W56JYDSC.cjs.map +0 -1
  193. package/dist/components/chat/stream/chat-dialog-store.d.ts +0 -184
  194. package/dist/components/chat/stream/chat-dialog-store.d.ts.map +0 -1
  195. package/dist/components/chat/stream/chat-stream-reducer.d.ts +0 -367
  196. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +0 -1
  197. package/dist/components/chat/stream/delta-batcher.d.ts +0 -51
  198. package/dist/components/chat/stream/delta-batcher.d.ts.map +0 -1
  199. package/dist/components/chat/stream/index.d.ts +0 -17
  200. package/dist/components/chat/stream/index.d.ts.map +0 -1
  201. package/dist/components/chat/stream/message-mutations.d.ts +0 -77
  202. package/dist/components/chat/stream/message-mutations.d.ts.map +0 -1
  203. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts +0 -22
  204. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts.map +0 -1
  205. package/dist/components/ui/markdown/base-components.d.ts +0 -51
  206. package/dist/components/ui/markdown/base-components.d.ts.map +0 -1
  207. package/dist/components/ui/markdown/engine.d.ts +0 -99
  208. package/dist/components/ui/markdown/engine.d.ts.map +0 -1
  209. package/dist/components/ui/markdown/heading-ids.d.ts +0 -65
  210. package/dist/components/ui/markdown/heading-ids.d.ts.map +0 -1
  211. package/dist/components/ui/markdown/index.d.ts +0 -18
  212. package/dist/components/ui/markdown/index.d.ts.map +0 -1
  213. package/dist/components/ui/markdown/mermaid-diagram.d.ts +0 -79
  214. package/dist/components/ui/markdown/mermaid-diagram.d.ts.map +0 -1
  215. package/dist/components/ui/markdown/rich/embed-overrides.d.ts +0 -14
  216. package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +0 -1
  217. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts +0 -40
  218. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts.map +0 -1
  219. package/dist/components/ui/markdown/rich/shortcodes.d.ts +0 -8
  220. package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +0 -1
  221. package/dist/components/ui/markdown/sanitize.d.ts +0 -153
  222. package/dist/components/ui/markdown/sanitize.d.ts.map +0 -1
  223. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts +0 -23
  224. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts.map +0 -1
  225. package/dist/components/ui/markdown/streaming.d.ts +0 -78
  226. package/dist/components/ui/markdown/streaming.d.ts.map +0 -1
  227. package/dist/components/ui/markdown/text-size.d.ts +0 -34
  228. package/dist/components/ui/markdown/text-size.d.ts.map +0 -1
  229. package/dist/utils/markdown-fences.d.ts +0 -42
  230. package/dist/utils/markdown-fences.d.ts.map +0 -1
  231. package/dist/utils/markdown-heading-id.d.ts +0 -85
  232. package/dist/utils/markdown-heading-id.d.ts.map +0 -1
  233. package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +0 -317
  234. package/src/chat-protocol/__tests__/chat-protocol.test.ts +0 -516
  235. package/src/chat-protocol/__tests__/env-flag.test.ts +0 -52
  236. package/src/chat-protocol/__tests__/ip-normalize.test.ts +0 -136
  237. package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +0 -232
  238. package/src/chat-protocol/decode.ts +0 -278
  239. package/src/chat-protocol/encode.ts +0 -71
  240. package/src/chat-protocol/env-flag.ts +0 -39
  241. package/src/chat-protocol/events.ts +0 -245
  242. package/src/chat-protocol/frames.ts +0 -245
  243. package/src/chat-protocol/index.ts +0 -21
  244. package/src/chat-protocol/ip-normalize.ts +0 -146
  245. package/src/chat-protocol/nats-decoder.ts +0 -258
  246. package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +0 -207
  247. package/src/components/chat/__tests__/chat-pending-turn.test.tsx +0 -230
  248. package/src/components/chat/hooks/__tests__/__snapshots__/conversation-id-persistence-golden.test.ts.snap +0 -37
  249. package/src/components/chat/hooks/__tests__/__snapshots__/sse-stream-golden.test.ts.snap +0 -0
  250. package/src/components/chat/hooks/__tests__/conversation-id-persistence-golden.test.ts +0 -438
  251. package/src/components/chat/hooks/__tests__/sse-stream-golden.test.ts +0 -318
  252. package/src/components/chat/stream/__tests__/__snapshots__/chat-stream-reducer-golden.test.ts.snap +0 -940
  253. package/src/components/chat/stream/__tests__/chat-dialog-store.test.ts +0 -756
  254. package/src/components/chat/stream/__tests__/chat-stream-reducer-golden.test.ts +0 -460
  255. package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +0 -973
  256. package/src/components/chat/stream/__tests__/delta-batcher.test.ts +0 -255
  257. package/src/components/chat/stream/__tests__/use-chat-stream-reducer.test.ts +0 -98
  258. package/src/components/chat/stream/chat-dialog-store.ts +0 -536
  259. package/src/components/chat/stream/chat-stream-reducer.ts +0 -1808
  260. package/src/components/chat/stream/delta-batcher.ts +0 -168
  261. package/src/components/chat/stream/index.ts +0 -55
  262. package/src/components/chat/stream/message-mutations.ts +0 -340
  263. package/src/components/chat/stream/use-chat-stream-reducer.ts +0 -126
  264. package/src/components/chat/utils/__tests__/__snapshots__/process-historical-messages-golden.test.ts.snap +0 -420
  265. package/src/components/chat/utils/__tests__/__snapshots__/segment-accumulator-golden.test.ts.snap +0 -605
  266. package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +0 -394
  267. package/src/components/chat/utils/__tests__/segment-accumulator-golden.test.ts +0 -270
  268. package/src/components/ui/__tests__/__snapshots__/markdown-parity.test.tsx.snap +0 -2420
  269. package/src/components/ui/__tests__/markdown-parity.test.tsx +0 -2528
  270. package/src/components/ui/markdown/__tests__/mermaid-security.test.ts +0 -115
  271. package/src/components/ui/markdown/__tests__/mermaid-stale-render.test.tsx +0 -95
  272. package/src/components/ui/markdown/__tests__/sanitize-invariant.test.ts +0 -309
  273. package/src/components/ui/markdown/__tests__/streaming.test.tsx +0 -388
  274. package/src/components/ui/markdown/base-components.tsx +0 -360
  275. package/src/components/ui/markdown/engine.tsx +0 -315
  276. package/src/components/ui/markdown/heading-ids.ts +0 -239
  277. package/src/components/ui/markdown/index.ts +0 -49
  278. package/src/components/ui/markdown/mermaid-diagram.tsx +0 -291
  279. package/src/components/ui/markdown/rich/embed-overrides.tsx +0 -199
  280. package/src/components/ui/markdown/rich/rich-markdown-renderer.tsx +0 -184
  281. package/src/components/ui/markdown/rich/shortcodes.ts +0 -170
  282. package/src/components/ui/markdown/sanitize.ts +0 -3499
  283. package/src/components/ui/markdown/simple-markdown-renderer.tsx +0 -28
  284. package/src/components/ui/markdown/streaming.ts +0 -362
  285. package/src/components/ui/markdown/text-size.ts +0 -106
  286. package/src/utils/markdown-fences.ts +0 -127
  287. package/src/utils/markdown-heading-id.ts +0 -348
  288. /package/dist/{chunk-PTXZB2KW.js.map → chunk-4J3CB4KO.js.map} +0 -0
  289. /package/dist/{chunk-XOFE2RT4.js.map → chunk-BTIRYGDT.js.map} +0 -0
  290. /package/dist/{chunk-PLB6CQSO.js.map → chunk-FG5ALORQ.js.map} +0 -0
  291. /package/dist/{chunk-NCAN6VUF.js.map → chunk-P3ITKTUE.js.map} +0 -0
  292. /package/dist/{chunk-34TDA22K.js.map → chunk-QHRNQTW5.js.map} +0 -0
  293. /package/dist/{chunk-OL3MJKUI.js.map → chunk-SZWMJIH3.js.map} +0 -0
  294. /package/dist/{chunk-MZRIRQYV.js.map → chunk-U5LU56JE.js.map} +0 -0
  295. /package/dist/{chunk-3LKB722U.js.map → chunk-WKWZ7LVC.js.map} +0 -0
  296. /package/dist/{chunk-J3VBVR4W.js.map → chunk-WPFLV4QJ.js.map} +0 -0
@@ -1,255 +0,0 @@
1
- /**
2
- * `createDeltaBatcher` — the SHARED (framework-free) delta batching policy.
3
- *
4
- * These tests exist because the batcher was previously inlined in the React
5
- * hook and re-implemented, near-verbatim but WITHOUT coalescing, by a product
6
- * app's non-React mirror. Coalescing is the difference between one `apply`
7
- * per frame and sixty.
8
- */
9
-
10
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
11
- import { createDeltaBatcher, type DeltaEvent } from '../delta-batcher'
12
- import type { ChatStreamEvent } from '../../../../chat-protocol/events'
13
-
14
- const text = (t: string, extra: Partial<ChatStreamEvent> = {}): ChatStreamEvent =>
15
- ({ type: 'text-delta', text: t, ...extra }) as ChatStreamEvent
16
- const thinking = (t: string): ChatStreamEvent => ({ type: 'thinking-delta', text: t }) as ChatStreamEvent
17
-
18
- beforeEach(() => {
19
- vi.useFakeTimers()
20
- // No rAF in this environment → the always-armed timer fallback drives flushes.
21
- vi.stubGlobal('requestAnimationFrame', undefined)
22
- vi.stubGlobal('cancelAnimationFrame', undefined)
23
- })
24
- afterEach(() => {
25
- vi.useRealTimers()
26
- vi.unstubAllGlobals()
27
- })
28
-
29
- describe('createDeltaBatcher', () => {
30
- it('coalesces a burst of consecutive text-deltas into ONE apply', () => {
31
- const applied: DeltaEvent[] = []
32
- const b = createDeltaBatcher({ applyOne: (e) => applied.push(e) })
33
-
34
- for (let i = 0; i < 60; i += 1) b.push(text(`${i} `))
35
- expect(applied).toHaveLength(0)
36
- expect(b.pendingCount).toBe(1)
37
-
38
- b.flush()
39
- expect(applied).toHaveLength(1)
40
- expect(applied[0].text).toBe(
41
- Array.from({ length: 60 }, (_, i) => `${i} `).join(''),
42
- )
43
- })
44
-
45
- it('does NOT coalesce across delta types, or across a differing `leading` flag', () => {
46
- const applied: DeltaEvent[] = []
47
- const b = createDeltaBatcher({ applyOne: (e) => applied.push(e) })
48
-
49
- b.push(text('a'))
50
- b.push(thinking('t1'))
51
- b.push(thinking('t2'))
52
- b.push(text('b', { leading: true } as Partial<ChatStreamEvent>))
53
- b.push(text('c', { leading: true } as Partial<ChatStreamEvent>))
54
- b.push(text('d'))
55
- b.flush()
56
-
57
- expect(applied.map((e) => [e.type, e.text])).toEqual([
58
- ['text-delta', 'a'],
59
- ['thinking-delta', 't1t2'],
60
- ['text-delta', 'bc'],
61
- ['text-delta', 'd'],
62
- ])
63
- })
64
-
65
- it('carries the LATEST seq onto the coalesced event', () => {
66
- const applied: DeltaEvent[] = []
67
- const b = createDeltaBatcher({ applyOne: (e) => applied.push(e) })
68
- b.push(text('a', { seq: 1 } as Partial<ChatStreamEvent>))
69
- b.push(text('b', { seq: 2 } as Partial<ChatStreamEvent>))
70
- b.push(text('c', { seq: 3 } as Partial<ChatStreamEvent>))
71
- b.flush()
72
- expect(applied).toHaveLength(1)
73
- expect(applied[0].seq).toBe(3)
74
- })
75
-
76
- /**
77
- * REGRESSION (round 3): coalescing must not swallow a redelivered /
78
- * out-of-order delta. Merging it would concatenate its text (the reducer
79
- * never sees a duplicate to drop) AND rewind the batch's seq below an
80
- * already-applied one, defeating the reducer's `seq <= lastAppliedSeq`
81
- * gate. It is pushed as its OWN entry so the gate can do its job.
82
- */
83
- it('does NOT coalesce a delta whose seq does not advance past the tail', () => {
84
- const applied: DeltaEvent[] = []
85
- const b = createDeltaBatcher({ applyOne: (e) => applied.push(e) })
86
- b.push(text('a', { seq: 1 } as Partial<ChatStreamEvent>))
87
- b.push(text('b', { seq: 2 } as Partial<ChatStreamEvent>))
88
- // Redelivery of seq 2, then an out-of-order seq 1.
89
- b.push(text('b', { seq: 2 } as Partial<ChatStreamEvent>))
90
- b.push(text('a', { seq: 1 } as Partial<ChatStreamEvent>))
91
- expect(b.pendingCount).toBe(3)
92
- b.flush()
93
- expect(applied.map((e) => [e.seq, e.text])).toEqual([
94
- [2, 'ab'],
95
- [2, 'b'],
96
- [1, 'a'],
97
- ])
98
- })
99
-
100
- /**
101
- * REGRESSION (round 4): a seq-LESS tail is not "always advancing". Merging a
102
- * seq-BEARING delta onto it stamps the merged entry with that seq, so on a
103
- * stream mixing both shapes a redelivered delta could still rewind the
104
- * batch's position past the reducer's gate.
105
- */
106
- it('does NOT coalesce a seq-bearing delta onto a seq-LESS tail', () => {
107
- const applied: DeltaEvent[] = []
108
- const b = createDeltaBatcher({ applyOne: (e) => applied.push(e) })
109
- b.push(text('a'))
110
- b.push(text('b', { seq: 2 } as Partial<ChatStreamEvent>))
111
- expect(b.pendingCount).toBe(2)
112
- b.flush()
113
- expect(applied.map((e) => [e.seq, e.text])).toEqual([
114
- [undefined, 'a'],
115
- [2, 'b'],
116
- ])
117
- })
118
-
119
- it('does NOT coalesce a seq-less delta onto a seq-BEARING tail', () => {
120
- const applied: DeltaEvent[] = []
121
- const b = createDeltaBatcher({ applyOne: (e) => applied.push(e) })
122
- b.push(text('a', { seq: 1 } as Partial<ChatStreamEvent>))
123
- b.push(text('b'))
124
- expect(b.pendingCount).toBe(2)
125
- b.flush()
126
- expect(applied.map((e) => [e.seq, e.text])).toEqual([
127
- [1, 'a'],
128
- [undefined, 'b'],
129
- ])
130
- })
131
-
132
- it('never mutates the caller-supplied event objects', () => {
133
- const b = createDeltaBatcher({ applyOne: () => {} })
134
- const first = text('a')
135
- b.push(first)
136
- b.push(text('b'))
137
- b.flush()
138
- expect((first as DeltaEvent).text).toBe('a')
139
- })
140
-
141
- it('returns false for non-delta events and leaves them unqueued', () => {
142
- const applied: DeltaEvent[] = []
143
- const b = createDeltaBatcher({ applyOne: (e) => applied.push(e) })
144
- expect(b.push(text('a'))).toBe(true)
145
- expect(b.push({ type: 'turn-end' } as ChatStreamEvent)).toBe(false)
146
- expect(b.pendingCount).toBe(1)
147
- expect(applied).toHaveLength(0)
148
- })
149
-
150
- it('flushes on the timer fallback when rAF is unavailable', () => {
151
- const applied: DeltaEvent[] = []
152
- const flushes: number[] = []
153
- const b = createDeltaBatcher({
154
- applyOne: (e) => applied.push(e),
155
- onFlushed: (n) => flushes.push(n),
156
- })
157
-
158
- b.push(text('a'))
159
- b.push(text('b'))
160
- expect(applied).toHaveLength(0)
161
-
162
- vi.advanceTimersByTime(50)
163
- expect(applied.map((e) => e.text)).toEqual(['ab'])
164
- expect(flushes).toEqual([1])
165
-
166
- // Timer is one-shot per batch: a later delta arms a fresh one.
167
- b.push(text('c'))
168
- vi.advanceTimersByTime(50)
169
- expect(applied.map((e) => e.text)).toEqual(['ab', 'c'])
170
- })
171
-
172
- it('honours a custom fallbackMs', () => {
173
- const applied: DeltaEvent[] = []
174
- const b = createDeltaBatcher({ applyOne: (e) => applied.push(e), fallbackMs: 200 })
175
- b.push(text('a'))
176
- vi.advanceTimersByTime(199)
177
- expect(applied).toHaveLength(0)
178
- vi.advanceTimersByTime(1)
179
- expect(applied).toHaveLength(1)
180
- })
181
-
182
- it('flushes the pending batch against the PREVIOUS key when the key changes', () => {
183
- const applied: Array<[string, string]> = []
184
- const b = createDeltaBatcher<string>({
185
- applyOne: (e, key) => applied.push([key ?? '<none>', e.text]),
186
- })
187
-
188
- b.push(text('for-a1'), 'A')
189
- b.push(text('|for-a2'), 'A')
190
- // Switching keys must land the A batch on A BEFORE queueing for B.
191
- b.push(text('for-b'), 'B')
192
- expect(applied).toEqual([['A', 'for-a1|for-a2']])
193
-
194
- b.flush()
195
- expect(applied).toEqual([
196
- ['A', 'for-a1|for-a2'],
197
- ['B', 'for-b'],
198
- ])
199
- })
200
-
201
- it('flushes on key change even for a NON-delta event', () => {
202
- const applied: Array<[string, string]> = []
203
- const b = createDeltaBatcher<string>({
204
- applyOne: (e, key) => applied.push([key ?? '<none>', e.text]),
205
- })
206
- b.push(text('a'), 'A')
207
- expect(b.push({ type: 'turn-end' } as ChatStreamEvent, 'B')).toBe(false)
208
- expect(applied).toEqual([['A', 'a']])
209
- })
210
-
211
- it('dispose() flushes the tail and cancels the pending timer', () => {
212
- const applied: DeltaEvent[] = []
213
- const b = createDeltaBatcher({ applyOne: (e) => applied.push(e) })
214
- b.push(text('tail'))
215
- b.dispose()
216
- expect(applied.map((e) => e.text)).toEqual(['tail'])
217
- vi.advanceTimersByTime(1000)
218
- expect(applied).toHaveLength(1)
219
- })
220
-
221
- it('flush() on an empty batch is a no-op (no applyOne, no onFlushed)', () => {
222
- const applyOne = vi.fn()
223
- const onFlushed = vi.fn()
224
- const b = createDeltaBatcher({ applyOne, onFlushed })
225
- b.flush()
226
- b.flush()
227
- expect(applyOne).not.toHaveBeenCalled()
228
- expect(onFlushed).not.toHaveBeenCalled()
229
- })
230
- })
231
-
232
- describe('createDeltaBatcher — rAF path', () => {
233
- it('prefers requestAnimationFrame and cancels it on an explicit flush', () => {
234
- let rafCb: (() => void) | null = null
235
- const cancel = vi.fn()
236
- vi.stubGlobal('requestAnimationFrame', (cb: () => void) => {
237
- rafCb = cb
238
- return 7
239
- })
240
- vi.stubGlobal('cancelAnimationFrame', cancel)
241
-
242
- const applied: DeltaEvent[] = []
243
- const b = createDeltaBatcher({ applyOne: (e) => applied.push(e) })
244
- b.push(text('a'))
245
- expect(rafCb).toBeTypeOf('function')
246
-
247
- b.flush()
248
- expect(cancel).toHaveBeenCalledWith(7)
249
- expect(applied.map((e) => e.text)).toEqual(['a'])
250
-
251
- // A late rAF callback for the already-flushed batch is harmless.
252
- rafCb!()
253
- expect(applied).toHaveLength(1)
254
- })
255
- })
@@ -1,98 +0,0 @@
1
- /**
2
- * Phase-3 tests — `useChatStreamReducer` delta batching.
3
- *
4
- * Text/thinking deltas coalesce to ~one animation frame (≤50ms timer
5
- * fallback); any non-delta event force-flushes the batch first so ordering
6
- * holds and completion always lands on fully-applied delta state.
7
- */
8
-
9
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
10
- import { renderHook, act } from '@testing-library/react'
11
- import { createChatDialogStore } from '../chat-dialog-store'
12
- import { useChatStreamReducer } from '../use-chat-stream-reducer'
13
-
14
- beforeEach(() => {
15
- vi.useFakeTimers()
16
- // Force the timer fallback path (deterministic under fake timers).
17
- vi.stubGlobal('requestAnimationFrame', undefined)
18
- vi.stubGlobal('cancelAnimationFrame', undefined)
19
- })
20
-
21
- afterEach(() => {
22
- vi.unstubAllGlobals()
23
- vi.useRealTimers()
24
- })
25
-
26
- function setup() {
27
- const store = createChatDialogStore()
28
- const hook = renderHook(() =>
29
- useChatStreamReducer({
30
- store,
31
- dialogId: 'dlg-1',
32
- createReducerOptions: () => ({ transport: 'nats' }),
33
- }),
34
- )
35
- return { store, hook }
36
- }
37
-
38
- describe('useChatStreamReducer — delta batching', () => {
39
- it('coalesces text deltas and applies them on the ≤50ms fallback timer', () => {
40
- const { store, hook } = setup()
41
- act(() => {
42
- hook.result.current.applyEvent({ type: 'turn-start' })
43
- hook.result.current.applyEvent({ type: 'text-delta', text: 'a' })
44
- hook.result.current.applyEvent({ type: 'text-delta', text: 'b' })
45
- })
46
- // Not yet applied — pending in the batch.
47
- let trailing = store.getSnapshot('dlg-1').messages.at(-1)
48
- expect(trailing?.segments ?? []).toEqual([])
49
-
50
- act(() => {
51
- vi.advanceTimersByTime(50)
52
- })
53
- trailing = store.getSnapshot('dlg-1').messages.at(-1)
54
- expect(trailing?.segments).toEqual([{ type: 'text', text: 'ab' }])
55
- })
56
-
57
- it('a non-delta event force-flushes the pending batch synchronously first', () => {
58
- const { store, hook } = setup()
59
- act(() => {
60
- hook.result.current.applyEvent({ type: 'turn-start' })
61
- hook.result.current.applyEvent({ type: 'text-delta', text: 'partial ' })
62
- hook.result.current.applyEvent({ type: 'text-delta', text: 'answer' })
63
- // Completion — must land on fully-applied delta state.
64
- hook.result.current.applyEvent({ type: 'turn-end' })
65
- })
66
- const state = store.getSnapshot('dlg-1')
67
- const trailing = state.messages.at(-1)
68
- expect(trailing?.segments).toEqual([{ type: 'text', text: 'partial answer' }])
69
- expect(state.streamingPhase).toBe('idle')
70
- })
71
-
72
- it('flushDeltas applies the batch on demand (SSE end-of-stream path)', () => {
73
- const { store, hook } = setup()
74
- act(() => {
75
- hook.result.current.applyEvent({ type: 'turn-start' })
76
- hook.result.current.applyEvent({ type: 'thinking-delta', text: 'hmm ' })
77
- hook.result.current.applyEvent({ type: 'thinking-delta', text: 'ok' })
78
- hook.result.current.flushDeltas()
79
- })
80
- const trailing = store.getSnapshot('dlg-1').messages.at(-1)
81
- expect(trailing?.segments).toEqual([{ type: 'thinking', text: 'hmm ok' }])
82
- })
83
-
84
- it('interleaved text/thinking preserve order across the batch', () => {
85
- const { store, hook } = setup()
86
- act(() => {
87
- hook.result.current.applyEvent({ type: 'turn-start' })
88
- hook.result.current.applyEvent({ type: 'thinking-delta', text: 'think' })
89
- hook.result.current.applyEvent({ type: 'text-delta', text: 'answer' })
90
- hook.result.current.flushDeltas()
91
- })
92
- const trailing = store.getSnapshot('dlg-1').messages.at(-1)
93
- expect(trailing?.segments).toEqual([
94
- { type: 'thinking', text: 'think' },
95
- { type: 'text', text: 'answer' },
96
- ])
97
- })
98
- })