@marimo-team/frontend 0.25.2 → 0.25.3-dev100

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 (476) hide show
  1. package/dist/assets/CellStatus-fK4n2NPc.js +1 -0
  2. package/dist/assets/{ConnectedDataExplorerComponent-t8fH_TNc.js → ConnectedDataExplorerComponent-Dft8R6l5.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-Gl5nCA8C.js → ErrorBoundary-CAEGdoFW.js} +2 -2
  4. package/dist/assets/JsonOutput-CyAKmZDD.js +11 -0
  5. package/dist/assets/{LazyAnyLanguageCodeMirror-CKcCbG2G.js → LazyAnyLanguageCodeMirror-BPYE76jR.js} +2 -2
  6. package/dist/assets/MarimoErrorOutput-CgZvP0p0.js +10 -0
  7. package/dist/assets/{RenderHTML-BbOqmHy3.js → RenderHTML-DQvxDDNS.js} +1 -1
  8. package/dist/assets/RunButton-BBDnDL6G.js +1 -0
  9. package/dist/assets/__vite-browser-external-8vDMR0Kk.js +1 -0
  10. package/dist/assets/__vite-browser-external-mKcnhO20.js +1 -0
  11. package/dist/assets/{add-cell-with-ai-8UBC0xwR.js → add-cell-with-ai-Clg5FELx.js} +12 -12
  12. package/dist/assets/{add-connection-dialog-DXQnd6eB.js → add-connection-dialog-BqdWmD-7.js} +2 -2
  13. package/dist/assets/{agent-panel-D7kMZLG-.js → agent-panel-5RxG5Y82.js} +5 -5
  14. package/dist/assets/ai-model-dropdown-Bvl1ktQq.js +13 -0
  15. package/dist/assets/{alert-dialog-BaPGzeOK.js → alert-dialog-C-sWmKva.js} +1 -1
  16. package/dist/assets/any-language-editor-DUMe1UCk.js +1 -0
  17. package/dist/assets/{app-config-button-BKEG4x9u.js → app-config-button-0QTeQ2Mv.js} +1 -1
  18. package/dist/assets/arrays-0R2reF2y.js +1 -0
  19. package/dist/assets/{blob-D5nfNHOo.js → blob-5VcB8K_t.js} +1 -1
  20. package/dist/assets/{button-COI-NGrx.js → button-D8gVKwe-.js} +1 -1
  21. package/dist/assets/{cache-panel-DTY1dqvi.js → cache-panel-BV-c7ed0.js} +1 -1
  22. package/dist/assets/{capabilities-BJvf-MD4.js → capabilities-Pq1YNPlK.js} +1 -1
  23. package/dist/assets/cell-editor-Byw5y-oI.js +42 -0
  24. package/dist/assets/cell-link-C5yYyzuF.js +1 -0
  25. package/dist/assets/{cells-BRkCaSsh.js → cells-D_11MjVB.js} +60 -60
  26. package/dist/assets/{chat-display-Dnagy4Nv.js → chat-display-BCR_ySiV.js} +1 -1
  27. package/dist/assets/chat-panel-RPRvBsaU.js +4 -0
  28. package/dist/assets/chat-ui-CGviVaRA.js +4 -0
  29. package/dist/assets/{circle-DXf-VWZG.js → circle-B6D_6D8w.js} +1 -1
  30. package/dist/assets/code-visibility-DYDagVy7.js +1 -0
  31. package/dist/assets/coerce-DWjFeXOY.js +1 -0
  32. package/dist/assets/column-preview-xe533-Cn.js +1 -0
  33. package/dist/assets/command-palette-BB9bWdWp.js +1 -0
  34. package/dist/assets/{common-Cu6CDbuw.js → common-CaeItbcI.js} +1 -1
  35. package/dist/assets/components-CBE8cOLK.js +1 -0
  36. package/dist/assets/{components-CP_UoFtT.js → components-DCo7mAWO.js} +1 -1
  37. package/dist/assets/{components-CGw16R_N.js → components-G6frbw0c.js} +1 -1
  38. package/dist/assets/config-CD1IuZFJ.js +1 -0
  39. package/dist/assets/config-DMz7oN0r.js +1 -0
  40. package/dist/assets/config-DpY86_Yq.js +1 -0
  41. package/dist/assets/{copy-D8O_CYCG.js → copy-KlhazlhB.js} +1 -1
  42. package/dist/assets/{copy-icon-BIiRXh_3.js → copy-icon-8-a5vovc.js} +1 -1
  43. package/dist/assets/{createReducer-DgRODTW6.js → createReducer-CaI_Oa0v.js} +1 -1
  44. package/dist/assets/{datasource-Cq6x_5ML.js → datasource-deuXnlU-.js} +1 -1
  45. package/dist/assets/{dates-BbFQ2oE4.js → dates-CQMwFko2.js} +1 -1
  46. package/dist/assets/{dependency-graph-panel--Kgtktcw.js → dependency-graph-panel-CIykLnJe.js} +2 -2
  47. package/dist/assets/{dist-iaThVDkA.js → dist-jlhMTKo_.js} +1 -1
  48. package/dist/assets/{documentation-panel-BHmbeznw.js → documentation-panel-2PQ8kQLa.js} +1 -1
  49. package/dist/assets/{download-DCJXy66H.js → download-dK52bLxZ.js} +1 -1
  50. package/dist/assets/{dropdown-menu-C1rnl-0o.js → dropdown-menu-DS6umAnH.js} +1 -1
  51. package/dist/assets/edit-page-DrElzw10.js +9 -0
  52. package/dist/assets/{error-banner-Cd69vvyX.js → error-banner-B5kzVf_0.js} +1 -1
  53. package/dist/assets/{error-entries-Dk3VZ-OK.js → error-entries-3GVk2KjS.js} +1 -1
  54. package/dist/assets/{error-panel-BPRYmC5J.js → error-panel-CzDRGPeC.js} +1 -1
  55. package/dist/assets/{errors-DCrsa1nH.js → errors-CNsgAw6m.js} +1 -1
  56. package/dist/assets/{es-Cqst59WX.js → es-N9SHpmG7.js} +1 -1
  57. package/dist/assets/{feedback-button-Bik2-e33.js → feedback-button-C5NgWpFg.js} +2 -2
  58. package/dist/assets/feedback-button-CONwLFpw.js +1 -0
  59. package/dist/assets/file-explorer-panel-B50xDnTZ.js +1 -0
  60. package/dist/assets/{file-name-input-Cdc-TVqO.js → file-name-input-DqwMSgGU.js} +1 -1
  61. package/dist/assets/{floating-outline-XD9FAV-f.js → floating-outline-ChmAuvxz.js} +1 -1
  62. package/dist/assets/{formats-yZ06h1Pl.js → formats-zylfwejq.js} +1 -1
  63. package/dist/assets/{formatting-BqrP-n49.js → formatting-uom1es4b.js} +1 -1
  64. package/dist/assets/{fullscreen-B9tHcfpV.js → fullscreen-BWTs8pYZ.js} +1 -1
  65. package/dist/assets/{gallery-page-Dm6mGwBi.js → gallery-page-CiL8aMS1.js} +1 -1
  66. package/dist/assets/{ganttDiagram-EL5Y4UJY-2n7Ig5Jm.js → ganttDiagram-EL5Y4UJY-CYqkCfFG.js} +1 -1
  67. package/dist/assets/{glide-data-editor-J9CAb70d.js → glide-data-editor-CqRclBkp.js} +2 -2
  68. package/dist/assets/{glide-utils-CIrAxdjv.js → glide-utils-HFqhvWHI.js} +2 -2
  69. package/dist/assets/home-page-BgZVl4dP.js +1 -0
  70. package/dist/assets/{hooks-93IwHd2u.js → hooks-DM5n7Osw.js} +1 -1
  71. package/dist/assets/{html-to-image-DK9Zl1lU.js → html-to-image-D_0rd3_B.js} +1 -1
  72. package/dist/assets/index-BnhSnwvB.css +2 -0
  73. package/dist/assets/index-EG2n8Oht.js +30 -0
  74. package/dist/assets/{input-BoBxR50a.js → input-DmHMEZGh.js} +1 -1
  75. package/dist/assets/kiosk-mode-DFS6HopF.js +1 -0
  76. package/dist/assets/layout-BpP8nTjb.js +9 -0
  77. package/dist/assets/linear-Bma8J3iM.js +1 -0
  78. package/dist/assets/links-DX2w97N9.js +1 -0
  79. package/dist/assets/{logs-panel-ABM16htT.js → logs-panel-B5eVAZtH.js} +1 -1
  80. package/dist/assets/{markdown-renderer-BG3sp2rs.js → markdown-renderer-BWUzqkJM.js} +1 -1
  81. package/dist/assets/{menu-items-BrwnTgyK.js → menu-items-BgUyNUpu.js} +1 -1
  82. package/dist/assets/{mermaid-a3xYymTy.js → mermaid-BKXlWk4F.js} +2 -2
  83. package/dist/assets/{name-cell-input-BgEBmqyL.js → name-cell-input-DXW68gRW.js} +1 -1
  84. package/dist/assets/numbers-CsZVw3Dc.js +1 -0
  85. package/dist/assets/{objects-snlMn4yk.js → objects-rLPlQ3X_.js} +1 -1
  86. package/dist/assets/{once-Haz_uxEL.js → once-6KNTRW0u.js} +1 -1
  87. package/dist/assets/{outline-panel-DCXQ3_F6.js → outline-panel-DPxJhJ_A.js} +1 -1
  88. package/dist/assets/{package-data-dv4FBHxR.js → package-data-CKPmBDtg.js} +1 -1
  89. package/dist/assets/packages-panel-gBE2FWQr.js +1 -0
  90. package/dist/assets/{panel-accordion-state-C1sdPp0m.js → panel-accordion-state-CCF5Gxnc.js} +1 -1
  91. package/dist/assets/panels-Dr6qMCvU.js +105 -0
  92. package/dist/assets/precisionRound-BxlUs8G4.js +1 -0
  93. package/dist/assets/process-output-Cw9IDcKQ.js +1 -0
  94. package/dist/assets/{quadrantDiagram-AXDQQJYC-yBhCDI7E.js → quadrantDiagram-AXDQQJYC-D9KH5Zge.js} +1 -1
  95. package/dist/assets/react-vega-BTNeboR2.js +189 -0
  96. package/dist/assets/{readonly-code-display-Do50PHI8.js → readonly-code-display-DYcZQ28T.js} +1 -1
  97. package/dist/assets/{readonly-python-code-DCSMIsTX.js → readonly-python-code-RFuQP50v.js} +1 -1
  98. package/dist/assets/{renderShortcut-BkVJQ_Zh.js → renderShortcut-rUwGqKgC.js} +1 -1
  99. package/dist/assets/{requests-lt0JK8Jt.js → requests-BIFR-h4M.js} +1 -1
  100. package/dist/assets/requests-DuLJN9Tf.js +1 -0
  101. package/dist/assets/{reveal-component-ltaepAqw.js → reveal-component-yRrDmCQz.js} +37 -37
  102. package/dist/assets/run-page-B69M4-zE.js +1 -0
  103. package/dist/assets/{runs-BLp5jezv.js → runs-C49hleBy.js} +1 -1
  104. package/dist/assets/{runtime-details-state-CSPobEdR.js → runtime-details-state-pobtu6TO.js} +1 -1
  105. package/dist/assets/{save-worker-BbUSwZRG.js → save-worker-pCkSHhuz.js} +1 -1
  106. package/dist/assets/{schemas-Dv42wLFq.js → schemas-ChRqMOTL.js} +1 -1
  107. package/dist/assets/scratchpad-panel-H44_pvb_.js +1 -0
  108. package/dist/assets/{scroll-to-bottom-button-1FyowpWV.js → scroll-to-bottom-button-CIkON2WJ.js} +1 -1
  109. package/dist/assets/{secrets-panel-Bctbtkrp.js → secrets-panel-bcfEKxRx.js} +1 -1
  110. package/dist/assets/{select-DTBn5eTb.js → select-3CVGlYrC.js} +1 -1
  111. package/dist/assets/{session-DqoWmb7X.js → session-CMLIzZKw.js} +1 -1
  112. package/dist/assets/session-panel-Bnd1iXkO.js +1 -0
  113. package/dist/assets/{snippets-panel-Bh1oTw4m.js → snippets-panel-DKApxKx9.js} +1 -1
  114. package/dist/assets/{spec-BfSBcO5B.js → spec-uCpVvaAR.js} +1 -1
  115. package/dist/assets/{state-uwRvJLb8.js → state-3mcyTS9q.js} +1 -1
  116. package/dist/assets/state-B_MCKzBL.js +1 -0
  117. package/dist/assets/state-Bl9BiY_s.js +3 -0
  118. package/dist/assets/{state-C3oDoyFe.js → state-C5WAeuTu.js} +1 -1
  119. package/dist/assets/{state-DxQnC_1I.js → state-Cv2JW3l9.js} +1 -1
  120. package/dist/assets/{state-XsPO8hNi.js → state-Dv059B3A.js} +1 -1
  121. package/dist/assets/{strings-B2vv-ENv.js → strings-C25fhMR_.js} +1 -1
  122. package/dist/assets/{swiper-component-a07domrZ.js → swiper-component-CRGRy5_O.js} +1 -1
  123. package/dist/assets/{switch-CTaiAjrz.js → switch-DYjBfpNJ.js} +1 -1
  124. package/dist/assets/{tabs-BuQlZFbt.js → tabs-CY8E4r9U.js} +1 -1
  125. package/dist/assets/{terminal-Ck_vK-34.js → terminal-Ca-kubKb.js} +1 -1
  126. package/dist/assets/{time-MgToa8VM.js → time-DswSXBDk.js} +1 -1
  127. package/dist/assets/{toast-BGzLGnvw.js → toast-Gzxi-le_.js} +1 -1
  128. package/dist/assets/tooltip-Brya4Ghn.js +61 -0
  129. package/dist/assets/{tooltip-C9TuY18o.js → tooltip-CkGm8X7m.js} +1 -1
  130. package/dist/assets/tracing-Clwy4SlW.js +1 -0
  131. package/dist/assets/tracing-panel-BwjLG_aC.js +2 -0
  132. package/dist/assets/{tree-actions-GM5E_bUs.js → tree-actions-BZNTQ12N.js} +1 -1
  133. package/dist/assets/{use-restore-focus-BxEGwIbk.js → use-restore-focus-DdS7Tl0F.js} +1 -1
  134. package/dist/assets/{useAddCell-GR7eEQrT.js → useAddCell-Cjuqk2YR.js} +1 -1
  135. package/dist/assets/useAsyncData-qvoPYHtx.js +1 -0
  136. package/dist/assets/useBoolean-Dd2nWXFJ.js +1 -0
  137. package/dist/assets/useCellActionButton-MoWLvuGe.js +1 -0
  138. package/dist/assets/useDebounce-DwTQ_1Pu.js +1 -0
  139. package/dist/assets/{useDeepCompareMemoize-DFIVN9dn.js → useDeepCompareMemoize-B2DVK2De.js} +1 -1
  140. package/dist/assets/useDelayElapsed-BlsDT4TV.js +1 -0
  141. package/dist/assets/{useDeleteCell-2_bMClHd.js → useDeleteCell-BC-MW7d1.js} +1 -1
  142. package/dist/assets/{useDependencyPanelTab-k3C3M18S.js → useDependencyPanelTab-CZU7i-B3.js} +1 -1
  143. package/dist/assets/{useEvent-Dfcbgjhg.js → useEvent-BDjgaBtz.js} +1 -1
  144. package/dist/assets/{useEventListener-D9cftcHj.js → useEventListener-CjDeLqFC.js} +1 -1
  145. package/dist/assets/{useIframeCapabilities-CwTkczVN.js → useIframeCapabilities-Czr9e4uG.js} +1 -1
  146. package/dist/assets/{useInstallPackage-iX1eufdl.js → useInstallPackage-wcDq9hYT.js} +1 -1
  147. package/dist/assets/{useInterval--x3s1VpI.js → useInterval-DJdQmXnq.js} +1 -1
  148. package/dist/assets/useNotebookActions-wFxVdRXQ.js +1 -0
  149. package/dist/assets/{useRunCells-B2vUhREg.js → useRunCells-ByaWxoIt.js} +1 -1
  150. package/dist/assets/{useSplitCell-DEb03htp.js → useSplitCell-YJIystwv.js} +1 -1
  151. package/dist/assets/useStateForKey-CGMJQT8x.js +1 -0
  152. package/dist/assets/{useTheme-COhSHkCj.js → useTheme-BAA27lh7.js} +1 -1
  153. package/dist/assets/{utils-CaQYvazl.js → utils-DfElfBpz.js} +1 -1
  154. package/dist/assets/vega-component-BeyxAJad.js +1 -0
  155. package/dist/assets/{vega-embed-container-DJPNkHPb.js → vega-embed-container-DtArSJz1.js} +1 -1
  156. package/dist/assets/vega-loader.browser-Bu25TKax.js +6 -0
  157. package/dist/assets/{worker-DTQ7oH2A.js → worker-bSCjCvwz.js} +1 -1
  158. package/dist/assets/{write-secret-modal-DzKAInL2.js → write-secret-modal-BEx-EUlh.js} +1 -1
  159. package/dist/assets/{xychartDiagram-S5SC5T6Z-KbGzG6-u.js → xychartDiagram-S5SC5T6Z-CeRnBS5e.js} +1 -1
  160. package/dist/index.html +80 -79
  161. package/package.json +8 -5
  162. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  163. package/src/__tests__/host-imports.test.ts +101 -0
  164. package/src/__tests__/mount.test.ts +8 -8
  165. package/src/components/app-config/ai-config.tsx +7 -2
  166. package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
  167. package/src/components/chat/acp/__tests__/state.test.ts +2 -2
  168. package/src/components/chat/acp/blocks.tsx +2 -2
  169. package/src/components/chat/chat-components.tsx +2 -0
  170. package/src/components/chat/chat-panel.tsx +9 -8
  171. package/src/components/chat/chat-utils.ts +4 -8
  172. package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
  173. package/src/components/data-table/__tests__/columns.test.tsx +105 -0
  174. package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
  175. package/src/components/data-table/__tests__/context-menu.test.ts +176 -0
  176. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +24 -1
  177. package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
  178. package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
  179. package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
  180. package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
  181. package/src/components/data-table/cell-selection/feature.ts +2 -2
  182. package/src/components/data-table/cell-selection/types.ts +2 -2
  183. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
  184. package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
  185. package/src/components/data-table/charts/charts.tsx +35 -25
  186. package/src/components/data-table/charts/storage.ts +16 -5
  187. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  188. package/src/components/data-table/column-summary/column-summary.tsx +2 -4
  189. package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
  190. package/src/components/data-table/columns.tsx +45 -46
  191. package/src/components/data-table/context-menu.tsx +161 -46
  192. package/src/components/data-table/data-table.tsx +7 -1
  193. package/src/components/data-table/date-filter-inputs.tsx +15 -42
  194. package/src/components/data-table/date-popover.tsx +6 -4
  195. package/src/components/data-table/export-actions.tsx +267 -163
  196. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
  197. package/src/components/data-table/schemas.ts +14 -36
  198. package/src/components/data-table/search-highlight.tsx +79 -0
  199. package/src/components/data-table/types.ts +1 -0
  200. package/src/components/data-table/url-detector.tsx +49 -9
  201. package/src/components/data-table/utils.ts +18 -0
  202. package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
  203. package/src/components/datasources/__tests__/column-preview.test.tsx +63 -2
  204. package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
  205. package/src/components/datasources/column-preview.tsx +8 -2
  206. package/src/components/datasources/datasources.tsx +47 -35
  207. package/src/components/debugger/debugger-code.tsx +5 -0
  208. package/src/components/dependency-graph/panels.tsx +2 -1
  209. package/src/components/editor/Output.tsx +9 -2
  210. package/src/components/editor/SortableCell.tsx +1 -3
  211. package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
  212. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  213. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +210 -34
  214. package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
  215. package/src/components/editor/actions/pair-with-agent-modal.tsx +100 -37
  216. package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
  217. package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
  218. package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
  219. package/src/components/editor/cell/CreateCellButton.tsx +8 -10
  220. package/src/components/editor/cell/DeleteButton.tsx +1 -0
  221. package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
  222. package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
  223. package/src/components/editor/cell/code/cell-editor.tsx +1 -1
  224. package/src/components/editor/cell/code/language-toggle.tsx +8 -7
  225. package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
  226. package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
  227. package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
  228. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  229. package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
  230. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +366 -0
  231. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
  232. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  233. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  234. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
  235. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  236. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
  237. package/src/components/editor/chrome/wrapper/storage.ts +1 -1
  238. package/src/components/editor/connections/components.tsx +1 -1
  239. package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
  240. package/src/components/editor/controls/command-palette.tsx +22 -10
  241. package/src/components/editor/dynamic-favicon.tsx +21 -40
  242. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +537 -0
  243. package/src/components/editor/errors/send-error-report-button.tsx +69 -0
  244. package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
  245. package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
  246. package/src/components/editor/file-tree/file-explorer.tsx +10 -5
  247. package/src/components/editor/file-tree/file-viewer.tsx +4 -2
  248. package/src/components/editor/file-tree/use-file-search.ts +35 -21
  249. package/src/components/editor/notebook-banner.tsx +1 -0
  250. package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
  251. package/src/components/editor/output/MarimoTracebackOutput.tsx +37 -22
  252. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  253. package/src/components/editor/output/ansi-reduce.ts +12 -0
  254. package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
  255. package/src/components/editor/package-alert.tsx +6 -4
  256. package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
  257. package/src/components/home/components.tsx +17 -31
  258. package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
  259. package/src/components/icons/large-spinner.tsx +3 -14
  260. package/src/components/icons/spinner.tsx +1 -1
  261. package/src/components/mcp/mcp-status-indicator.tsx +1 -1
  262. package/src/components/pages/__tests__/home-page.test.tsx +116 -0
  263. package/src/components/pages/home-page.tsx +164 -91
  264. package/src/components/scratchpad/scratchpad.tsx +2 -2
  265. package/src/components/slides/reveal-component.tsx +4 -2
  266. package/src/components/slides/slide-cell-view.tsx +1 -1
  267. package/src/components/slides/slide-form.tsx +1 -0
  268. package/src/components/tracing/tracing.tsx +2 -2
  269. package/src/components/ui/alert-dialog.tsx +1 -1
  270. package/src/core/ai/context/providers/datasource.ts +6 -2
  271. package/src/core/attachments/__tests__/toast.test.tsx +58 -0
  272. package/src/core/attachments/state.ts +18 -0
  273. package/src/core/attachments/toast.ts +15 -0
  274. package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
  275. package/src/core/cells/__tests__/cell.test.ts +39 -0
  276. package/src/core/cells/__tests__/cells.test.ts +55 -1
  277. package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
  278. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  279. package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
  280. package/src/core/cells/__tests__/session.test.ts +50 -1
  281. package/src/core/cells/cell.ts +14 -3
  282. package/src/core/cells/cells.ts +3 -1
  283. package/src/core/cells/error-report.ts +63 -0
  284. package/src/core/cells/pending-delete-service.ts +2 -6
  285. package/src/core/cells/session.ts +29 -2
  286. package/src/core/cells/types.ts +10 -1
  287. package/src/core/codemirror/__tests__/setup.test.ts +1 -1
  288. package/src/core/codemirror/keymaps/vim.ts +2 -6
  289. package/src/core/codemirror/language/languages/markdown.ts +1 -1
  290. package/src/core/codemirror/language/languages/python.ts +3 -3
  291. package/src/core/codemirror/language/panel/panel.tsx +1 -0
  292. package/src/core/config/pair.ts +5 -3
  293. package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
  294. package/src/core/datasets/data-source-discovery.ts +4 -6
  295. package/src/core/document/__tests__/draft.test.ts +322 -0
  296. package/src/core/document/__tests__/fixtures.ts +92 -0
  297. package/src/core/document/__tests__/model.test.ts +483 -0
  298. package/src/core/document/__tests__/state.test.ts +446 -0
  299. package/src/core/document/__tests__/submission.test.ts +346 -0
  300. package/src/core/document/draft.ts +472 -0
  301. package/src/core/document/index.ts +10 -0
  302. package/src/core/document/model.ts +53 -0
  303. package/src/core/document/state.ts +401 -0
  304. package/src/core/document/sync.ts +359 -0
  305. package/src/core/edit-app.tsx +2 -2
  306. package/src/core/host/AGENTS.md +16 -0
  307. package/src/core/host/__tests__/attachments.test.ts +56 -0
  308. package/src/core/host/__tests__/document.test.ts +126 -0
  309. package/src/core/host/__tests__/fixtures.ts +41 -0
  310. package/src/core/host/__tests__/notebook-client.test.ts +570 -0
  311. package/src/core/host/connection.ts +42 -0
  312. package/src/core/host/notebook-client.ts +297 -0
  313. package/src/core/host/resources/attachments.ts +22 -0
  314. package/src/core/host/resources/document.ts +101 -0
  315. package/src/core/hotkeys/actions.ts +2 -4
  316. package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
  317. package/src/core/network/connection.ts +4 -2
  318. package/src/core/network/useConnectionNotice.ts +8 -2
  319. package/src/core/run-app.tsx +2 -2
  320. package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
  321. package/src/core/runtime/__tests__/runtime.test.ts +18 -17
  322. package/src/core/runtime/runtime.ts +26 -30
  323. package/src/core/state/__tests__/jotai.test.tsx +108 -0
  324. package/src/core/state/jotai.ts +1 -1
  325. package/src/core/static/files.ts +2 -0
  326. package/src/core/wasm/bridge.ts +2 -10
  327. package/src/core/wasm/store.ts +3 -3
  328. package/src/core/wasm/worker/bootstrap.ts +1 -1
  329. package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
  330. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -29
  331. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +223 -0
  332. package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +194 -184
  333. package/src/core/websocket/connection-state.ts +60 -0
  334. package/src/core/websocket/state.ts +7 -0
  335. package/src/core/websocket/transports/__tests__/notebook.test.ts +502 -0
  336. package/src/core/websocket/transports/notebook.ts +213 -0
  337. package/src/core/websocket/types.ts +9 -39
  338. package/src/core/websocket/useNotebookClient.ts +89 -0
  339. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +53 -224
  340. package/src/css/globals.css +5 -0
  341. package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
  342. package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
  343. package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
  344. package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
  345. package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
  346. package/src/hooks/__tests__/useScript.test.tsx +77 -0
  347. package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
  348. package/src/hooks/useAsyncData.ts +3 -3
  349. package/src/hooks/useDataSourceDiscovery.ts +6 -4
  350. package/src/hooks/useDebounce.ts +5 -8
  351. package/src/hooks/useDeepCompareMemoize.ts +1 -1
  352. package/src/hooks/useDelayElapsed.ts +14 -10
  353. package/src/hooks/useElapsedTime.ts +3 -3
  354. package/src/hooks/useInputHistory.ts +4 -7
  355. package/src/hooks/useInternalStateWithSync.ts +6 -6
  356. package/src/hooks/useResizeObserver.ts +4 -7
  357. package/src/hooks/useScript.ts +67 -70
  358. package/src/hooks/useStateForKey.ts +93 -0
  359. package/src/mount.tsx +3 -3
  360. package/src/plugins/core/registerReactComponent.tsx +1 -1
  361. package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
  362. package/src/plugins/impl/DataTablePlugin.tsx +14 -15
  363. package/src/plugins/impl/DropdownPlugin.tsx +2 -6
  364. package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
  365. package/src/plugins/impl/FormPlugin.tsx +1 -1
  366. package/src/plugins/impl/RangeSliderPlugin.tsx +0 -1
  367. package/src/plugins/impl/RefreshPlugin.tsx +1 -0
  368. package/src/plugins/impl/SliderPlugin.tsx +6 -8
  369. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +201 -1
  370. package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
  371. package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
  372. package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
  373. package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
  374. package/src/plugins/impl/anywidget/runtime.ts +21 -9
  375. package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
  376. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
  377. package/src/plugins/impl/chat/chat-ui.tsx +12 -10
  378. package/src/plugins/impl/code/any-language-editor.tsx +2 -2
  379. package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
  380. package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
  381. package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
  382. package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
  383. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +0 -1
  384. package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
  385. package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
  386. package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
  387. package/src/plugins/impl/panel/utils.ts +17 -0
  388. package/src/plugins/impl/vega/make-selectable.ts +4 -7
  389. package/src/plugins/layout/RoutesPlugin.tsx +1 -1
  390. package/src/stories/__tests__/editor-story.test.tsx +33 -0
  391. package/src/stories/button.stories.tsx +1 -0
  392. package/src/stories/editor.stories.tsx +24 -17
  393. package/src/stories/form-wrapper.stories.tsx +2 -2
  394. package/src/stories/select.stories.tsx +2 -2
  395. package/src/stories/switchable-multi-select.stories.tsx +1 -1
  396. package/src/utils/__tests__/arrays.test.ts +35 -0
  397. package/src/utils/__tests__/batch-requests.test.ts +3 -3
  398. package/src/utils/__tests__/dates.test.ts +116 -0
  399. package/src/utils/__tests__/numbers.test.ts +48 -1
  400. package/src/utils/__tests__/objects.test.ts +21 -0
  401. package/src/utils/arrays.ts +10 -0
  402. package/src/utils/blob.ts +1 -1
  403. package/src/utils/dates.ts +94 -0
  404. package/src/utils/numbers.ts +28 -17
  405. package/src/utils/objects.ts +3 -7
  406. package/src/utils/once.ts +19 -19
  407. package/src/utils/zod-utils.ts +1 -3
  408. package/dist/assets/CellStatus-BP2mavAq.js +0 -1
  409. package/dist/assets/JsonOutput-D6HIIsCb.js +0 -11
  410. package/dist/assets/MarimoErrorOutput-D60XkCny.js +0 -6
  411. package/dist/assets/RunButton-CK3LhnP5.js +0 -1
  412. package/dist/assets/__vite-browser-external-CYO_P3S8.js +0 -1
  413. package/dist/assets/__vite-browser-external-FeDVGZU8.js +0 -1
  414. package/dist/assets/ai-model-dropdown-B0z6-zhp.js +0 -5
  415. package/dist/assets/any-language-editor-BLCAD3yx.js +0 -1
  416. package/dist/assets/api-BeC_VSGv.js +0 -1
  417. package/dist/assets/arrays-BqUx5USx.js +0 -1
  418. package/dist/assets/cell-editor-ADVfycpN.js +0 -21
  419. package/dist/assets/cell-link-LSth_8hT.js +0 -1
  420. package/dist/assets/chat-panel-DQ6HkWEF.js +0 -4
  421. package/dist/assets/chat-ui-Df8Vcj6n.js +0 -4
  422. package/dist/assets/circle-play-CYIb0qT6.js +0 -1
  423. package/dist/assets/code-visibility-B-_GvwR5.js +0 -1
  424. package/dist/assets/coerce-CpE6kUdX.js +0 -1
  425. package/dist/assets/column-preview-Cm2D6vyX.js +0 -1
  426. package/dist/assets/command-palette-DTSQ7gOC.js +0 -1
  427. package/dist/assets/components-CXfO-F47.js +0 -1
  428. package/dist/assets/config-BOKzA3tF.js +0 -1
  429. package/dist/assets/config-DX8Y2WbJ.js +0 -1
  430. package/dist/assets/config-VPSq73n8.js +0 -1
  431. package/dist/assets/edit-page-ChcSgpke.js +0 -9
  432. package/dist/assets/feedback-button-B2gKC5cn.js +0 -1
  433. package/dist/assets/file-explorer-panel-BEEK5lcl.js +0 -1
  434. package/dist/assets/home-page-DMHnsRxm.js +0 -4
  435. package/dist/assets/index-Bcdzex-3.css +0 -2
  436. package/dist/assets/index-s-M1OXjs.js +0 -29
  437. package/dist/assets/kiosk-mode-BZECfg9k.js +0 -1
  438. package/dist/assets/layout-DmcNEu3P.js +0 -9
  439. package/dist/assets/linear-0eij_aAG.js +0 -1
  440. package/dist/assets/link-NuFVR7G9.js +0 -1
  441. package/dist/assets/links-DkkL9DVa.js +0 -1
  442. package/dist/assets/numbers-Mk3-CcoU.js +0 -1
  443. package/dist/assets/packages-panel-BMGOmZkR.js +0 -1
  444. package/dist/assets/pair-CaQyK6En.js +0 -1
  445. package/dist/assets/panels-CxVG8-v2.js +0 -4
  446. package/dist/assets/precisionRound-Bq_nmf5u.js +0 -1
  447. package/dist/assets/process-output-D0KGq04R.js +0 -1
  448. package/dist/assets/react-vega-C3rYGtP6.js +0 -189
  449. package/dist/assets/requests-CgSY1X4a.js +0 -1
  450. package/dist/assets/run-page-DF3c5cDk.js +0 -1
  451. package/dist/assets/scratchpad-panel-Dwd6W5PE.js +0 -1
  452. package/dist/assets/session-panel-VdDhEBa-.js +0 -1
  453. package/dist/assets/state-BAJYRhmO.js +0 -1
  454. package/dist/assets/state-_Qai_QyX.js +0 -3
  455. package/dist/assets/tooltip-mT3CGest.js +0 -61
  456. package/dist/assets/tracing-Di6pUmX8.js +0 -1
  457. package/dist/assets/tracing-panel-PeRXHD8_.js +0 -2
  458. package/dist/assets/useAsyncData-C7aJJz0Y.js +0 -1
  459. package/dist/assets/useBoolean-BDD6_xQQ.js +0 -1
  460. package/dist/assets/useCellActionButton-B-9eG5z_.js +0 -1
  461. package/dist/assets/useDebounce-B_ccBZTp.js +0 -1
  462. package/dist/assets/useNotebookActions-NHN6A-cN.js +0 -1
  463. package/dist/assets/vega-component-CnadTgoE.js +0 -1
  464. package/dist/assets/vega-loader.browser-CLRkNgm7.js +0 -6
  465. package/dist/assets/ws-BXWY541V.js +0 -22
  466. package/src/core/state/__tests__/jotai.test.ts +0 -25
  467. package/src/core/websocket/__tests__/useWebSocket.test.ts +0 -49
  468. package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
  469. package/src/core/websocket/transports/__tests__/sse.test.ts +0 -338
  470. package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
  471. package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
  472. package/src/core/websocket/transports/basic.ts +0 -102
  473. package/src/core/websocket/transports/sse.ts +0 -308
  474. package/src/core/websocket/transports/transport.ts +0 -61
  475. package/src/core/websocket/transports/ws.ts +0 -98
  476. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -1,338 +0,0 @@
1
- /* Copyright 2026 Marimo. All rights reserved. */
2
-
3
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
- import { SseTransport } from "../sse";
5
- import { MAX_RETRIES, TRANSPORT_EXHAUSTED_REASON } from "../ws";
6
-
7
- const encoder = new TextEncoder();
8
-
9
- function createControllableStream() {
10
- let controller!: ReadableStreamDefaultController<Uint8Array>;
11
- const stream = new ReadableStream<Uint8Array>({
12
- start(c) {
13
- controller = c;
14
- },
15
- });
16
- return {
17
- stream,
18
- push: (text: string) => controller.enqueue(encoder.encode(text)),
19
- end: () => controller.close(),
20
- };
21
- }
22
-
23
- function sseResponse(body: ReadableStream<Uint8Array> | null, status = 200) {
24
- return {
25
- ok: status >= 200 && status < 300,
26
- status,
27
- body,
28
- } as Response;
29
- }
30
-
31
- describe("SseTransport", () => {
32
- let transport: SseTransport;
33
- let fetchMock: ReturnType<typeof vi.fn>;
34
- let opens: Event[];
35
- let messages: MessageEvent[];
36
- let closes: CloseEvent[];
37
- let errors: Event[];
38
-
39
- beforeEach(() => {
40
- fetchMock = vi.fn();
41
- vi.stubGlobal("fetch", fetchMock);
42
- transport = new SseTransport(() => "http://example.invalid/sse");
43
- opens = [];
44
- messages = [];
45
- closes = [];
46
- errors = [];
47
- transport.addEventListener("open", (e) => opens.push(e));
48
- transport.addEventListener("message", (e) => messages.push(e));
49
- transport.addEventListener("close", (e) => closes.push(e));
50
- transport.addEventListener("error", (e) => errors.push(e));
51
- });
52
-
53
- afterEach(() => {
54
- transport.close();
55
- vi.unstubAllGlobals();
56
- vi.useRealTimers();
57
- });
58
-
59
- it("starts closed and connects on reconnect()", async () => {
60
- expect(transport.readyState).toBe(WebSocket.CLOSED);
61
- const { stream } = createControllableStream();
62
- fetchMock.mockResolvedValueOnce(sseResponse(stream));
63
-
64
- transport.reconnect();
65
- expect(transport.readyState).toBe(WebSocket.CONNECTING);
66
- await vi.waitFor(() => expect(opens).toHaveLength(1));
67
- expect(transport.readyState).toBe(WebSocket.OPEN);
68
- expect(fetchMock).toHaveBeenCalledWith(
69
- "http://example.invalid/sse",
70
- expect.objectContaining({
71
- headers: { Accept: "text/event-stream" },
72
- }),
73
- );
74
- });
75
-
76
- it("dispatches message events, joining multi-line data", async () => {
77
- const { stream, push } = createControllableStream();
78
- fetchMock.mockResolvedValueOnce(sseResponse(stream));
79
- transport.reconnect();
80
- await vi.waitFor(() => expect(opens).toHaveLength(1));
81
-
82
- push('data: {"op": "alert"}\n\n');
83
- await vi.waitFor(() => expect(messages).toHaveLength(1));
84
- expect(messages[0].data).toBe('{"op": "alert"}');
85
-
86
- // Multi-line data and records split across chunks
87
- push("data: line1\nda");
88
- push("ta: line2\n\n");
89
- await vi.waitFor(() => expect(messages).toHaveLength(2));
90
- expect(messages[1].data).toBe("line1\nline2");
91
- });
92
-
93
- it("ignores keep-alive comments", async () => {
94
- const { stream, push } = createControllableStream();
95
- fetchMock.mockResolvedValueOnce(sseResponse(stream));
96
- transport.reconnect();
97
- await vi.waitFor(() => expect(opens).toHaveLength(1));
98
-
99
- push(": keep-alive\n\n");
100
- push("data: after\n\n");
101
- await vi.waitFor(() => expect(messages).toHaveLength(1));
102
- expect(messages[0].data).toBe("after");
103
- });
104
-
105
- it("surfaces a server close event with its code and reason", async () => {
106
- const { stream, push } = createControllableStream();
107
- fetchMock.mockResolvedValueOnce(sseResponse(stream));
108
- transport.reconnect();
109
- await vi.waitFor(() => expect(opens).toHaveLength(1));
110
-
111
- // Stop retrying when the consumer treats the close as terminal,
112
- // mirroring useMarimoKernelConnection's terminal path.
113
- transport.addEventListener("close", () => transport.close());
114
- push('event: close\ndata: {"code": 1000, "reason": "MARIMO_SHUTDOWN"}\n\n');
115
- await vi.waitFor(() => expect(closes).toHaveLength(1));
116
- expect(closes[0].code).toBe(1000);
117
- expect(closes[0].reason).toBe("MARIMO_SHUTDOWN");
118
- expect(closes[0].wasClean).toBe(true);
119
- expect(transport.readyState).toBe(WebSocket.CLOSED);
120
- expect(fetchMock).toHaveBeenCalledTimes(1);
121
- });
122
-
123
- it("emits an empty-reason close and retries when the stream ends", async () => {
124
- vi.useFakeTimers();
125
- const first = createControllableStream();
126
- fetchMock.mockResolvedValueOnce(sseResponse(first.stream));
127
- transport.reconnect();
128
- await vi.waitFor(() => expect(opens).toHaveLength(1));
129
-
130
- first.end();
131
- await vi.waitFor(() => expect(closes).toHaveLength(1));
132
- expect(closes[0].reason).toBe("");
133
- expect(closes[0].wasClean).toBe(false);
134
-
135
- // A retry is scheduled and reconnects
136
- const second = createControllableStream();
137
- fetchMock.mockResolvedValueOnce(sseResponse(second.stream));
138
- await vi.advanceTimersByTimeAsync(10_000);
139
- await vi.waitFor(() => expect(opens).toHaveLength(2));
140
- });
141
-
142
- it("does not stack its own retry when the consumer reconnects in onClose", async () => {
143
- vi.useFakeTimers();
144
- const first = createControllableStream();
145
- const second = createControllableStream();
146
- fetchMock
147
- .mockResolvedValueOnce(sseResponse(first.stream))
148
- .mockResolvedValueOnce(sseResponse(second.stream));
149
- // Mirror useMarimoKernelConnection's retry path: reconnect()
150
- // synchronously from inside the close handler.
151
- transport.addEventListener("close", () => transport.reconnect());
152
-
153
- transport.reconnect();
154
- await vi.waitFor(() => expect(opens).toHaveLength(1));
155
-
156
- first.end();
157
- await vi.waitFor(() => expect(opens).toHaveLength(2));
158
-
159
- // The superseded run must not schedule a second connection on top of
160
- // the consumer's reconnect.
161
- await vi.advanceTimersByTimeAsync(60_000);
162
- expect(fetchMock).toHaveBeenCalledTimes(2);
163
-
164
- // The consumer-driven connection is healthy
165
- second.push("data: alive\n\n");
166
- await vi.waitFor(() => expect(messages).toHaveLength(1));
167
- expect(transport.readyState).toBe(WebSocket.OPEN);
168
- });
169
-
170
- it("exhausts the retry budget when the server closes every connection", async () => {
171
- vi.useFakeTimers();
172
- // A server that accepts, immediately sends a close event, and never
173
- // delivers a message must not be retried forever.
174
- fetchMock.mockImplementation(() => {
175
- const { stream, push } = createControllableStream();
176
- push('event: close\ndata: {"code": 3000, "reason": "MARIMO_X"}\n\n');
177
- return Promise.resolve(sseResponse(stream));
178
- });
179
-
180
- transport.reconnect();
181
- for (let i = 0; i < MAX_RETRIES; i++) {
182
- await vi.advanceTimersByTimeAsync(15_000);
183
- }
184
-
185
- expect(closes.length).toBe(MAX_RETRIES);
186
- expect(closes.at(-1)?.reason).toBe(TRANSPORT_EXHAUSTED_REASON);
187
- const fetchCallsWhenExhausted = fetchMock.mock.calls.length;
188
- await vi.advanceTimersByTimeAsync(60_000);
189
- expect(fetchMock.mock.calls.length).toBe(fetchCallsWhenExhausted);
190
- });
191
-
192
- it("a delivered message resets the retry budget", async () => {
193
- vi.useFakeTimers();
194
- const first = createControllableStream();
195
- fetchMock.mockResolvedValueOnce(sseResponse(first.stream));
196
- transport.reconnect();
197
- await vi.waitFor(() => expect(opens).toHaveLength(1));
198
- first.push("data: healthy\n\n");
199
- await vi.waitFor(() => expect(messages).toHaveLength(1));
200
-
201
- first.end();
202
- await vi.waitFor(() => expect(closes).toHaveLength(1));
203
- // Budget was reset by the message, so this close is retry #1, not
204
- // an exhaustion.
205
- expect(closes[0].reason).toBe("");
206
- });
207
-
208
- it("gives up with TRANSPORT_EXHAUSTED_REASON after MAX_RETRIES failures", async () => {
209
- vi.useFakeTimers();
210
- fetchMock.mockRejectedValue(new Error("connection refused"));
211
-
212
- transport.reconnect();
213
- for (let i = 0; i < MAX_RETRIES; i++) {
214
- await vi.advanceTimersByTimeAsync(15_000);
215
- }
216
-
217
- expect(closes.length).toBe(MAX_RETRIES);
218
- expect(closes.at(-1)?.reason).toBe(TRANSPORT_EXHAUSTED_REASON);
219
- expect(errors.length).toBe(MAX_RETRIES);
220
- const fetchCallsWhenExhausted = fetchMock.mock.calls.length;
221
-
222
- // No further retries once exhausted
223
- await vi.advanceTimersByTimeAsync(60_000);
224
- expect(fetchMock.mock.calls.length).toBe(fetchCallsWhenExhausted);
225
-
226
- // A manual reconnect() resets the budget
227
- const { stream } = createControllableStream();
228
- fetchMock.mockResolvedValueOnce(sseResponse(stream));
229
- transport.reconnect();
230
- await vi.waitFor(() => expect(opens).toHaveLength(1));
231
- });
232
-
233
- it("close() aborts the connection and cancels retries", async () => {
234
- vi.useFakeTimers();
235
- const { stream } = createControllableStream();
236
- fetchMock.mockResolvedValueOnce(sseResponse(stream));
237
- transport.reconnect();
238
- await vi.waitFor(() => expect(opens).toHaveLength(1));
239
-
240
- transport.close();
241
- expect(transport.readyState).toBe(WebSocket.CLOSED);
242
- // No close event is dispatched for a user-initiated close, and no
243
- // retry is scheduled.
244
- await vi.advanceTimersByTimeAsync(60_000);
245
- expect(closes).toHaveLength(0);
246
- expect(fetchMock).toHaveBeenCalledTimes(1);
247
- });
248
-
249
- it("treats a non-2xx response as a connection failure", async () => {
250
- vi.useFakeTimers();
251
- fetchMock.mockResolvedValueOnce(sseResponse(null, 500));
252
- transport.reconnect();
253
-
254
- await vi.waitFor(() => expect(closes).toHaveLength(1));
255
- expect(opens).toHaveLength(0);
256
- expect(errors).toHaveLength(1);
257
- expect(closes[0].reason).toBe("");
258
- });
259
-
260
- it("sends provided headers (auth) with the request", async () => {
261
- const withHeaders = new SseTransport(
262
- () => "http://example.invalid/sse",
263
- () => ({ Authorization: "Bearer token123" }),
264
- );
265
- const { stream } = createControllableStream();
266
- fetchMock.mockResolvedValueOnce(sseResponse(stream));
267
- withHeaders.reconnect();
268
- await vi.waitFor(() => expect(fetchMock).toHaveBeenCalled());
269
- withHeaders.close();
270
-
271
- expect(fetchMock).toHaveBeenCalledWith(
272
- "http://example.invalid/sse",
273
- expect.objectContaining({
274
- headers: {
275
- Authorization: "Bearer token123",
276
- Accept: "text/event-stream",
277
- },
278
- }),
279
- );
280
- });
281
-
282
- it("aborts a connection attempt that exceeds the timeout", async () => {
283
- vi.useFakeTimers();
284
- // fetch that only settles when aborted
285
- fetchMock.mockImplementation(
286
- (_url: string, init: RequestInit) =>
287
- new Promise((_, reject) => {
288
- init.signal?.addEventListener("abort", () =>
289
- reject(new Error("aborted")),
290
- );
291
- }),
292
- );
293
-
294
- transport.reconnect();
295
- await vi.advanceTimersByTimeAsync(11_000);
296
- expect(errors).toHaveLength(1);
297
- expect(closes).toHaveLength(1);
298
- expect(closes[0].reason).toBe("");
299
- });
300
-
301
- it("reconnect() mid-stream supersedes the old connection silently", async () => {
302
- const first = createControllableStream();
303
- const second = createControllableStream();
304
- fetchMock
305
- .mockResolvedValueOnce(sseResponse(first.stream))
306
- .mockResolvedValueOnce(sseResponse(second.stream));
307
-
308
- transport.reconnect();
309
- await vi.waitFor(() => expect(opens).toHaveLength(1));
310
-
311
- transport.reconnect();
312
- await vi.waitFor(() => expect(opens).toHaveLength(2));
313
-
314
- // The aborted first connection must not surface close/error events
315
- second.push("data: alive\n\n");
316
- await vi.waitFor(() => expect(messages).toHaveLength(1));
317
- expect(closes).toHaveLength(0);
318
- expect(errors).toHaveLength(0);
319
- });
320
-
321
- it("send() is a no-op", () => {
322
- expect(() => transport.send("data")).not.toThrow();
323
- });
324
-
325
- it("dedupes repeated addEventListener calls", async () => {
326
- const { stream, push } = createControllableStream();
327
- fetchMock.mockResolvedValueOnce(sseResponse(stream));
328
- const onMessage = (e: MessageEvent) => messages.push(e);
329
- transport.addEventListener("message", onMessage);
330
- transport.addEventListener("message", onMessage);
331
- transport.reconnect();
332
- await vi.waitFor(() => expect(opens).toHaveLength(1));
333
-
334
- push("data: once\n\n");
335
- // The beforeEach listener plus the deduped listener = 2 events
336
- await vi.waitFor(() => expect(messages).toHaveLength(2));
337
- });
338
- });
@@ -1,308 +0,0 @@
1
- /* Copyright 2026 Marimo. All rights reserved. */
2
-
3
- import { describe, expect, it, vi } from "vitest";
4
- import { ConnectionSubscriptions } from "../transport";
5
-
6
- describe("ConnectionSubscriptions", () => {
7
- describe("addSubscription", () => {
8
- it("should add a subscription for an event", () => {
9
- const subscriptions = new ConnectionSubscriptions();
10
- const callback = vi.fn();
11
-
12
- subscriptions.addSubscription("open", callback);
13
-
14
- // Verify by notifying
15
- subscriptions.notify("open", new Event("open"));
16
- expect(callback).toHaveBeenCalledTimes(1);
17
- });
18
-
19
- it("should add multiple subscriptions for the same event", () => {
20
- const subscriptions = new ConnectionSubscriptions();
21
- const callback1 = vi.fn();
22
- const callback2 = vi.fn();
23
- const callback3 = vi.fn();
24
-
25
- subscriptions.addSubscription("message", callback1);
26
- subscriptions.addSubscription("message", callback2);
27
- subscriptions.addSubscription("message", callback3);
28
-
29
- const messageEvent = new MessageEvent("message", { data: "test" });
30
- subscriptions.notify("message", messageEvent);
31
-
32
- expect(callback1).toHaveBeenCalledWith(messageEvent);
33
- expect(callback2).toHaveBeenCalledWith(messageEvent);
34
- expect(callback3).toHaveBeenCalledWith(messageEvent);
35
- });
36
-
37
- it("should add subscriptions for different event types", () => {
38
- const subscriptions = new ConnectionSubscriptions();
39
- const openCallback = vi.fn();
40
- const closeCallback = vi.fn();
41
- const messageCallback = vi.fn();
42
-
43
- subscriptions.addSubscription("open", openCallback);
44
- subscriptions.addSubscription("close", closeCallback);
45
- subscriptions.addSubscription("message", messageCallback);
46
-
47
- subscriptions.notify("open", new Event("open"));
48
- expect(openCallback).toHaveBeenCalledTimes(1);
49
- expect(closeCallback).not.toHaveBeenCalled();
50
- expect(messageCallback).not.toHaveBeenCalled();
51
- });
52
-
53
- it("should not add duplicate subscriptions for the same callback", () => {
54
- const subscriptions = new ConnectionSubscriptions();
55
- const callback = vi.fn();
56
-
57
- subscriptions.addSubscription("close", callback);
58
- subscriptions.addSubscription("close", callback);
59
- subscriptions.addSubscription("close", callback);
60
-
61
- subscriptions.notify("close", new Event("close"));
62
-
63
- // Set only stores unique callbacks, so should be called once
64
- expect(callback).toHaveBeenCalledTimes(1);
65
- });
66
- });
67
-
68
- describe("removeSubscription", () => {
69
- it("should remove a subscription", () => {
70
- const subscriptions = new ConnectionSubscriptions();
71
- const callback = vi.fn();
72
-
73
- subscriptions.addSubscription("message", callback);
74
- subscriptions.removeSubscription("message", callback);
75
-
76
- const messageEvent = new MessageEvent("message", { data: "test" });
77
- subscriptions.notify("message", messageEvent);
78
-
79
- expect(callback).not.toHaveBeenCalled();
80
- });
81
-
82
- it("should only remove the specific callback", () => {
83
- const subscriptions = new ConnectionSubscriptions();
84
- const callback1 = vi.fn();
85
- const callback2 = vi.fn();
86
- const callback3 = vi.fn();
87
-
88
- subscriptions.addSubscription("close", callback1);
89
- subscriptions.addSubscription("close", callback2);
90
- subscriptions.addSubscription("close", callback3);
91
-
92
- subscriptions.removeSubscription("close", callback2);
93
-
94
- subscriptions.notify("close", new Event("close"));
95
-
96
- expect(callback1).toHaveBeenCalledTimes(1);
97
- expect(callback2).not.toHaveBeenCalled();
98
- expect(callback3).toHaveBeenCalledTimes(1);
99
- });
100
-
101
- it("should handle removing subscription from non-existent event type", () => {
102
- const subscriptions = new ConnectionSubscriptions();
103
- const callback = vi.fn();
104
-
105
- subscriptions.addSubscription("open", callback);
106
-
107
- expect(() =>
108
- subscriptions.removeSubscription("close", callback),
109
- ).not.toThrow();
110
-
111
- subscriptions.notify("open", new Event("open"));
112
- expect(callback).toHaveBeenCalledTimes(1);
113
- });
114
- });
115
-
116
- describe("notify", () => {
117
- it("should notify all subscribed callbacks for an event", () => {
118
- const subscriptions = new ConnectionSubscriptions();
119
- const callback1 = vi.fn();
120
- const callback2 = vi.fn();
121
-
122
- subscriptions.addSubscription("open", callback1);
123
- subscriptions.addSubscription("open", callback2);
124
-
125
- const openEvent = new Event("open");
126
- subscriptions.notify("open", openEvent);
127
-
128
- expect(callback1).toHaveBeenCalledWith(openEvent);
129
- expect(callback2).toHaveBeenCalledWith(openEvent);
130
- });
131
-
132
- it("should pass the correct data to callbacks", () => {
133
- const subscriptions = new ConnectionSubscriptions();
134
- const callback = vi.fn();
135
-
136
- subscriptions.addSubscription("message", callback);
137
-
138
- const messageEvent = new MessageEvent("message", {
139
- data: "test data",
140
- });
141
- subscriptions.notify("message", messageEvent);
142
-
143
- expect(callback).toHaveBeenCalledWith(messageEvent);
144
- expect(callback.mock.calls[0][0].data).toBe("test data");
145
- });
146
-
147
- it("should not notify callbacks for different event types", () => {
148
- const subscriptions = new ConnectionSubscriptions();
149
- const openCallback = vi.fn();
150
- const closeCallback = vi.fn();
151
-
152
- subscriptions.addSubscription("open", openCallback);
153
- subscriptions.addSubscription("close", closeCallback);
154
-
155
- subscriptions.notify("open", new Event("open"));
156
-
157
- expect(openCallback).toHaveBeenCalledTimes(1);
158
- expect(closeCallback).not.toHaveBeenCalled();
159
- });
160
-
161
- it("should handle exceptions in callbacks without affecting other callbacks", () => {
162
- const subscriptions = new ConnectionSubscriptions();
163
- const throwingCallback = vi.fn(() => {
164
- throw new Error("Callback error");
165
- });
166
- const normalCallback = vi.fn();
167
-
168
- subscriptions.addSubscription("message", throwingCallback);
169
- subscriptions.addSubscription("message", normalCallback);
170
-
171
- const messageEvent = new MessageEvent("message", { data: "test" });
172
-
173
- expect(() => subscriptions.notify("message", messageEvent)).toThrow(
174
- "Callback error",
175
- );
176
-
177
- expect(throwingCallback).toHaveBeenCalledWith(messageEvent);
178
- expect(normalCallback).not.toHaveBeenCalled();
179
- });
180
- });
181
-
182
- describe("Integration scenarios", () => {
183
- it("should handle complete subscription lifecycle", () => {
184
- const subscriptions = new ConnectionSubscriptions();
185
- const callback = vi.fn();
186
-
187
- subscriptions.addSubscription("open", callback);
188
-
189
- subscriptions.notify("open", new Event("open"));
190
- expect(callback).toHaveBeenCalledTimes(1);
191
-
192
- subscriptions.removeSubscription("open", callback);
193
-
194
- subscriptions.notify("open", new Event("open"));
195
- expect(callback).toHaveBeenCalledTimes(1);
196
- });
197
-
198
- it("should handle multiple events with multiple callbacks", () => {
199
- const subscriptions = new ConnectionSubscriptions();
200
-
201
- const openCallback1 = vi.fn();
202
- const openCallback2 = vi.fn();
203
- const closeCallback1 = vi.fn();
204
- const messageCallback = vi.fn();
205
-
206
- subscriptions.addSubscription("open", openCallback1);
207
- subscriptions.addSubscription("open", openCallback2);
208
- subscriptions.addSubscription("close", closeCallback1);
209
- subscriptions.addSubscription("message", messageCallback);
210
-
211
- subscriptions.notify("open", new Event("open"));
212
- expect(openCallback1).toHaveBeenCalledTimes(1);
213
- expect(openCallback2).toHaveBeenCalledTimes(1);
214
- expect(closeCallback1).not.toHaveBeenCalled();
215
- expect(messageCallback).not.toHaveBeenCalled();
216
-
217
- const messageEvent = new MessageEvent("message", { data: "test" });
218
- subscriptions.notify("message", messageEvent);
219
- expect(openCallback1).toHaveBeenCalledTimes(1);
220
- expect(openCallback2).toHaveBeenCalledTimes(1);
221
- expect(closeCallback1).not.toHaveBeenCalled();
222
- expect(messageCallback).toHaveBeenCalledTimes(1);
223
- expect(messageCallback).toHaveBeenCalledWith(messageEvent);
224
-
225
- subscriptions.notify("close", new Event("close"));
226
- expect(openCallback1).toHaveBeenCalledTimes(1);
227
- expect(openCallback2).toHaveBeenCalledTimes(1);
228
- expect(closeCallback1).toHaveBeenCalledTimes(1);
229
- expect(messageCallback).toHaveBeenCalledTimes(1);
230
- });
231
-
232
- it("should handle add and remove operations interleaved", () => {
233
- const subscriptions = new ConnectionSubscriptions();
234
- const callback1 = vi.fn();
235
- const callback2 = vi.fn();
236
- const callback3 = vi.fn();
237
-
238
- subscriptions.addSubscription("message", callback1);
239
- subscriptions.addSubscription("message", callback2);
240
-
241
- const msg1 = new MessageEvent("message", { data: "msg1" });
242
- subscriptions.notify("message", msg1);
243
-
244
- expect(callback1).toHaveBeenCalledWith(msg1);
245
- expect(callback2).toHaveBeenCalledWith(msg1);
246
- expect(callback3).not.toHaveBeenCalled();
247
-
248
- subscriptions.removeSubscription("message", callback1);
249
- subscriptions.addSubscription("message", callback3);
250
-
251
- const msg2 = new MessageEvent("message", { data: "msg2" });
252
- subscriptions.notify("message", msg2);
253
-
254
- expect(callback1).toHaveBeenCalledTimes(1);
255
- expect(callback2).toHaveBeenCalledWith(msg2);
256
- expect(callback3).toHaveBeenCalledWith(msg2);
257
- });
258
-
259
- it("should maintain separate subscription lists per event type", () => {
260
- const subscriptions = new ConnectionSubscriptions();
261
- const callback = vi.fn();
262
-
263
- subscriptions.addSubscription("open", callback);
264
- subscriptions.addSubscription("close", callback);
265
- subscriptions.addSubscription("message", callback);
266
-
267
- subscriptions.removeSubscription("close", callback);
268
-
269
- subscriptions.notify("open", new Event("open"));
270
- expect(callback).toHaveBeenCalledTimes(1);
271
-
272
- subscriptions.notify(
273
- "message",
274
- new MessageEvent("message", { data: "test" }),
275
- );
276
- expect(callback).toHaveBeenCalledTimes(2);
277
-
278
- subscriptions.notify("close", new Event("close"));
279
- expect(callback).toHaveBeenCalledTimes(2);
280
- });
281
- });
282
-
283
- describe("Edge cases", () => {
284
- it("should handle callback that modifies subscriptions", () => {
285
- const subscriptions = new ConnectionSubscriptions();
286
- const callback1 = vi.fn();
287
- const callback2 = vi.fn(() => {
288
- subscriptions.removeSubscription("message", callback1);
289
- });
290
-
291
- subscriptions.addSubscription("message", callback1);
292
- subscriptions.addSubscription("message", callback2);
293
-
294
- const messageEvent = new MessageEvent("message", { data: "test" });
295
- subscriptions.notify("message", messageEvent);
296
-
297
- expect(callback1).toHaveBeenCalledTimes(1);
298
- expect(callback2).toHaveBeenCalledTimes(1);
299
-
300
- callback1.mockClear();
301
- callback2.mockClear();
302
-
303
- subscriptions.notify("message", messageEvent);
304
- expect(callback1).not.toHaveBeenCalled();
305
- expect(callback2).toHaveBeenCalledTimes(1);
306
- });
307
- });
308
- });