@marimo-team/islands 0.25.3-dev6 → 0.25.3-dev61
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.
- package/dist/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-Da5OoEUL.js} +14 -14
- package/dist/{ErrorBoundary-Ca-Sa492.js → ErrorBoundary-BEfUX0zz.js} +8 -8
- package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-Bz3mrprX.js} +2 -2
- package/dist/any-language-editor-k2B8YI3Y.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-Cs5ujb9K.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
- package/dist/{button-CvjkwdSk.js → button-BxB3RDjN.js} +1 -1
- package/dist/{capabilities-CQ5IjhzL.js → capabilities-BgR0osfN.js} +2 -2
- package/dist/{chat-ui-0lKdOoQ3.js → chat-ui-C0_lCzql.js} +1139 -1140
- package/dist/{check-EDwSwP89.js → check-P35dc2dH.js} +1 -1
- package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-CcTSaxZt.js} +1 -1
- package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-DcJcjOaO.js} +1 -1
- package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-BVtDTjaF.js} +1 -1
- package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-BkDFkh8q.js} +1 -1
- package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
- package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-CfAt7HT5.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-CGEjNYa2.js} +2 -2
- package/dist/{code-visibility-NGRjhbtA.js → code-visibility-CA0UOEgk.js} +6 -6
- package/dist/{common-B7_ZSg0P.js → common-BgoPSdwN.js} +4960 -4835
- package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-CPoxUG7G.js} +3 -3
- package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-BW6NmfJi.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-CWg3CTuF.js} +1 -1
- package/dist/{data-grid-overlay-editor-B_cmrkBm.js → data-grid-overlay-editor-CRjm80Ab.js} +5 -5
- package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-BsSJJldB.js → diagram-S7CK7UJ4-BPxtoS-z.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-DA-vJdFL.js} +1 -1
- package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-D6bkR84S.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-m60g4cAT.js} +1 -1
- package/dist/{dialog-CEBFZNyc.js → dialog-C1PcpCqX.js} +3 -3
- package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
- package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-B3eUiVJB.js} +177 -169
- package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-CK99sho2.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-Bryq0tdE.js} +1 -1
- package/dist/{error-banner-D-WCIiMt.js → error-banner-Sl2FlSJ4.js} +2 -2
- package/dist/{error-entries-DSvFWT_M.js → error-entries-CMNtA8NK.js} +959 -959
- package/dist/{feedback-button-CTF2Nj9s.js → feedback-button-CZA7i0Ir.js} +29 -29
- package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-D-3aRpfS.js} +2 -2
- package/dist/{formats-CeOmU1MU.js → formats-6pLWWrY2.js} +3 -3
- package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
- package/dist/{glide-data-editor-BQHF9puC.js → glide-data-editor-Bd52MLtf.js} +370 -368
- package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Bt8_pBa2.js} +16 -16
- package/dist/{globals-Cj8iqMEp.js → globals-BvANmUu6.js} +3 -3
- package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
- package/dist/{html-to-image-B8s5SgGm.js → html-to-image-6xcoTKhC.js} +29 -29
- package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-s1C1kgfU.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-CCsRgolY.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-BrmxDlcc.js} +1 -1
- package/dist/{label-BDKF3Afc.js → label-BRnUC0NX.js} +2 -2
- package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
- package/dist/{loader-BQOfjTIJ.js → loader-Bm8cy3e8.js} +12 -12
- package/dist/main.js +3177 -2980
- package/dist/{mermaid-CHo49Yv-.js → mermaid-DKI0869I.js} +38 -38
- package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
- package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-641xJysF.js} +1 -1
- package/dist/{number-overlay-editor-DBcnm0SY.js → number-overlay-editor-C7ffluCS.js} +3 -3
- package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
- package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-BqvywNJA.js → pegDiagram-XKGWAZYB-keVUAUeS.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-DHktXPO_.js} +1 -1
- package/dist/precisionRound-DhAzeRcm.js +49 -0
- package/dist/{process-output-CKRXyszb.js → process-output-BJRJFobf.js} +4 -4
- package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-BVEF4OV_.js} +2 -2
- package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
- package/dist/{requirementDiagram-BXWQKSXE-a_MxGvxe.js → requirementDiagram-BXWQKSXE-CWo-UAKV.js} +1 -1
- package/dist/{reveal-component-DTBSuGkC.js → reveal-component-Dfuz8zQ_.js} +19 -19
- package/dist/{select-BButFi-d.js → select-B0LRDH0g.js} +4 -4
- package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-CHQvLnq4.js} +1 -1
- package/dist/{spec-BwarBpUJ.js → spec-C-LRKGtT.js} +4 -4
- package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-BOJZgbUJ.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH-C53XXdYH.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-D2o1gOtw.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-BHFzN1Wk.js} +2 -2
- package/dist/{swiper-component-DKntyn8p.js → swiper-component-DfhRvAFi.js} +2 -2
- package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
- package/dist/{timeline-definition-24CTP7MA-D3c46WM5.js → timeline-definition-24CTP7MA-rv7QUa46.js} +1 -1
- package/dist/{toDate-vIXljnsr.js → toDate-CjPuEPou.js} +21 -20
- package/dist/{toast-B9UMEi8u.js → toast-CFWpihJU.js} +4 -4
- package/dist/{tooltip-BO1REbqs.js → tooltip-CKO1pHJo.js} +2 -2
- package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-DBCITLeF.js} +1 -1
- package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-ChgNtdmE.js} +15 -14
- package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-c_yJtkAe.js} +2 -2
- package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-Bet7kSsI.js} +1 -1
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-BkdeVdXC.js} +1 -1
- package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-VVmz_24o.js} +3 -3
- package/dist/{useTheme-CRnjkku0.js → useTheme-DympCsQg.js} +5 -5
- package/dist/{vega-component-DTG6zxaY.js → vega-component-DnfQeoyt.js} +147 -147
- package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
- package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
- package/dist/{vennDiagram-4TSXK5OY-BSZD8JcZ.js → vennDiagram-4TSXK5OY-D36Zk7t-.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-BLBolvyQ.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-CfH57l0v.js} +2 -2
- package/package.json +4 -4
- package/src/__tests__/host-imports.test.ts +1 -0
- package/src/components/app-config/ai-config.tsx +6 -2
- package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
- package/src/components/chat/chat-panel.tsx +3 -7
- package/src/components/chat/chat-utils.ts +4 -8
- package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
- package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
- package/src/components/data-table/charts/charts.tsx +14 -19
- package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
- package/src/components/data-table/columns.tsx +2 -1
- package/src/components/data-table/context-menu.tsx +36 -20
- package/src/components/data-table/date-popover.tsx +6 -4
- package/src/components/data-table/export-actions.tsx +267 -163
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
- package/src/components/data-table/schemas.ts +14 -36
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +24 -0
- package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
- package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
- package/src/components/editor/cell/CreateCellButton.tsx +8 -10
- package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
- package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
- package/src/components/editor/cell/code/cell-editor.tsx +1 -1
- package/src/components/editor/cell/code/language-toggle.tsx +8 -7
- package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
- package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +12 -18
- package/src/components/editor/connections/components.tsx +1 -1
- package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +12 -23
- package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
- package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
- package/src/components/editor/file-tree/file-explorer.tsx +10 -5
- package/src/components/editor/file-tree/file-viewer.tsx +4 -2
- package/src/components/editor/file-tree/use-file-search.ts +35 -21
- package/src/components/editor/output/MarimoTracebackOutput.tsx +1 -1
- package/src/components/editor/output/console/ConsoleOutput.tsx +8 -18
- package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
- package/src/components/icons/large-spinner.tsx +3 -14
- package/src/components/pages/home-page.tsx +4 -4
- package/src/components/slides/slide-cell-view.tsx +1 -1
- package/src/components/tracing/tracing.tsx +2 -2
- package/src/core/attachments/state.ts +5 -8
- package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
- package/src/core/cells/cells.ts +2 -1
- package/src/core/cells/pending-delete-service.ts +2 -6
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
- package/src/core/datasets/data-source-discovery.ts +4 -6
- package/src/core/edit-app.tsx +2 -2
- package/src/core/host/AGENTS.md +11 -2
- package/src/core/host/__tests__/attachments.test.ts +56 -0
- package/src/core/host/__tests__/document.test.ts +126 -0
- package/src/core/host/__tests__/events.test.ts +49 -0
- package/src/core/host/__tests__/fixtures.ts +41 -0
- package/src/core/host/__tests__/notebook-client.test.ts +496 -0
- package/src/core/host/connection.ts +81 -0
- package/src/core/host/events.ts +68 -0
- package/src/core/host/notebook-client.ts +316 -0
- package/src/core/host/resources/attachments.ts +19 -0
- package/src/core/host/resources/document.ts +101 -0
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/islands/__tests__/bootstrap.test.ts +0 -26
- package/src/core/islands/bootstrap.ts +0 -1
- package/src/core/network/connection.ts +4 -2
- package/src/core/network/useConnectionNotice.ts +8 -2
- package/src/core/run-app.tsx +2 -2
- package/src/core/runtime/__tests__/runtime.test.ts +5 -12
- package/src/core/runtime/runtime.ts +5 -5
- package/src/core/static/files.ts +2 -0
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -17
- package/src/core/websocket/__tests__/{useWebSocket.test.ts → createConnectionTransport.test.ts} +1 -1
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +149 -0
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +168 -253
- package/src/core/websocket/connection-state.ts +70 -0
- package/src/core/websocket/state.ts +7 -0
- package/src/core/websocket/transports/__tests__/sse.test.ts +18 -4
- package/src/core/websocket/transports/create.ts +31 -0
- package/src/core/websocket/transports/sse.ts +14 -2
- package/src/core/websocket/transports/transport.ts +12 -23
- package/src/core/websocket/types.ts +9 -39
- package/src/core/websocket/useNotebookClient.ts +78 -0
- package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +53 -233
- package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
- package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
- package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
- package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
- package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
- package/src/hooks/__tests__/useScript.test.tsx +77 -0
- package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
- package/src/hooks/useAsyncData.ts +3 -3
- package/src/hooks/useDataSourceDiscovery.ts +6 -4
- package/src/hooks/useDebounce.ts +5 -8
- package/src/hooks/useDeepCompareMemoize.ts +1 -1
- package/src/hooks/useDelayElapsed.ts +14 -10
- package/src/hooks/useElapsedTime.ts +3 -3
- package/src/hooks/useInputHistory.ts +4 -7
- package/src/hooks/useInternalStateWithSync.ts +6 -6
- package/src/hooks/useResizeObserver.ts +4 -7
- package/src/hooks/useScript.ts +67 -70
- package/src/hooks/useStateForKey.ts +93 -0
- package/src/plugins/core/registerReactComponent.tsx +1 -1
- package/src/plugins/impl/DataTablePlugin.tsx +14 -15
- package/src/plugins/impl/DropdownPlugin.tsx +2 -6
- package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/SliderPlugin.tsx +6 -8
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +201 -1
- package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
- package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
- package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
- package/src/plugins/impl/anywidget/runtime.ts +21 -9
- package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
- package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
- package/src/plugins/impl/chat/chat-ui.tsx +10 -9
- package/src/plugins/impl/code/any-language-editor.tsx +2 -2
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
- package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
- package/src/plugins/impl/panel/PanelPlugin.tsx +15 -16
- package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
- package/src/plugins/impl/panel/utils.ts +17 -0
- package/src/plugins/impl/vega/make-selectable.ts +3 -6
- package/src/stories/__tests__/editor-story.test.tsx +33 -0
- package/src/stories/editor.stories.tsx +24 -17
- package/src/utils/__tests__/arrays.test.ts +35 -0
- package/src/utils/__tests__/numbers.test.ts +48 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +10 -0
- package/src/utils/numbers.ts +28 -17
- package/src/utils/objects.ts +3 -7
- package/src/utils/once.ts +19 -19
- package/dist/any-language-editor-DZ_YTVus.js +0 -76
- package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
- package/dist/precisionRound-DMIVQrt2.js +0 -97
- package/src/core/host/__tests__/protocol.test.ts +0 -214
- package/src/core/host/protocol.ts +0 -1943
- package/src/core/websocket/useWebSocket.tsx +0 -88
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { createPyodideConnection } from "../../wasm/bridge";
|
|
4
|
+
import { isWasm } from "../../wasm/utils";
|
|
5
|
+
import { BasicTransport } from "./basic";
|
|
6
|
+
import { SseTransport } from "./sse";
|
|
7
|
+
import { WsTransport } from "./ws";
|
|
8
|
+
import type { IConnectionTransport } from "./transport";
|
|
9
|
+
|
|
10
|
+
export interface ConnectionTransportOptions {
|
|
11
|
+
url: () => string;
|
|
12
|
+
static: boolean;
|
|
13
|
+
transportType: "websocket" | "sse";
|
|
14
|
+
headers: () => Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createConnectionTransport(
|
|
18
|
+
options: ConnectionTransportOptions,
|
|
19
|
+
): IConnectionTransport {
|
|
20
|
+
if (options.static) {
|
|
21
|
+
return BasicTransport.empty();
|
|
22
|
+
}
|
|
23
|
+
if (isWasm()) {
|
|
24
|
+
return createPyodideConnection();
|
|
25
|
+
}
|
|
26
|
+
// Read the URL lazily to follow runtime redirects.
|
|
27
|
+
if (options.transportType === "sse") {
|
|
28
|
+
return new SseTransport(options.url, options.headers);
|
|
29
|
+
}
|
|
30
|
+
return new WsTransport(options.url);
|
|
31
|
+
}
|
|
@@ -218,16 +218,20 @@ export class SseTransport implements IConnectionTransport {
|
|
|
218
218
|
const decoder = new TextDecoder();
|
|
219
219
|
let buffer = "";
|
|
220
220
|
let eventName: string | null = null;
|
|
221
|
+
let eventId: string | null = null;
|
|
221
222
|
let dataLines: string[] = [];
|
|
222
223
|
|
|
223
224
|
const dispatch = (): ServerClose | null => {
|
|
224
225
|
if (dataLines.length === 0) {
|
|
225
226
|
eventName = null;
|
|
227
|
+
eventId = null;
|
|
226
228
|
return null;
|
|
227
229
|
}
|
|
228
230
|
const data = dataLines.join("\n");
|
|
229
231
|
const name = eventName;
|
|
232
|
+
const id = eventId;
|
|
230
233
|
eventName = null;
|
|
234
|
+
eventId = null;
|
|
231
235
|
dataLines = [];
|
|
232
236
|
if (name === "close") {
|
|
233
237
|
return parseServerClose(data);
|
|
@@ -236,9 +240,15 @@ export class SseTransport implements IConnectionTransport {
|
|
|
236
240
|
// (rather than on open, so a server that accepts and immediately
|
|
237
241
|
// closes cannot retry forever).
|
|
238
242
|
this.retryCount = 0;
|
|
243
|
+
// Normalize SSE fields into the same envelope as host WebSockets.
|
|
244
|
+
// Leave the payload text intact for the client's JSON decoder.
|
|
245
|
+
const envelope =
|
|
246
|
+
name === null
|
|
247
|
+
? data
|
|
248
|
+
: `{"event":${JSON.stringify(name)},"data":${data}${id === null ? "" : `,"id":${JSON.stringify(id)}`}}`;
|
|
239
249
|
this.subscriptions.notify(
|
|
240
250
|
"message",
|
|
241
|
-
new MessageEvent("message", { data }),
|
|
251
|
+
new MessageEvent("message", { data: envelope }),
|
|
242
252
|
);
|
|
243
253
|
return null;
|
|
244
254
|
};
|
|
@@ -284,8 +294,10 @@ export class SseTransport implements IConnectionTransport {
|
|
|
284
294
|
dataLines.push(fieldValue);
|
|
285
295
|
} else if (field === "event") {
|
|
286
296
|
eventName = fieldValue;
|
|
297
|
+
} else if (field === "id") {
|
|
298
|
+
eventId = fieldValue;
|
|
287
299
|
}
|
|
288
|
-
//
|
|
300
|
+
// The host client owns the cursor; `retry` does not override our policy.
|
|
289
301
|
}
|
|
290
302
|
if (cursor > 0) {
|
|
291
303
|
buffer = buffer.slice(cursor);
|
|
@@ -1,30 +1,19 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
export type ConnectionEvent = "open" | "close" | "message" | "error";
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
import type {
|
|
4
|
+
NotebookTransport,
|
|
5
|
+
ConnectionEvent,
|
|
6
|
+
ConnectionTransportCallback,
|
|
7
|
+
IConnectionTransportMap,
|
|
8
|
+
} from "../../host/connection";
|
|
9
|
+
export type {
|
|
10
|
+
ConnectionEvent,
|
|
11
|
+
ConnectionTransportCallback,
|
|
12
|
+
IConnectionTransportMap,
|
|
13
|
+
} from "../../host/connection";
|
|
14
14
|
|
|
15
|
-
export interface IConnectionTransport {
|
|
16
|
-
reconnect(code?: number | undefined, reason?: string | undefined): void;
|
|
17
|
-
close(): void;
|
|
15
|
+
export interface IConnectionTransport extends NotebookTransport {
|
|
18
16
|
send(data: string | ArrayBuffer | Blob | ArrayBufferView<ArrayBuffer>): void;
|
|
19
|
-
addEventListener<T extends ConnectionEvent>(
|
|
20
|
-
event: T,
|
|
21
|
-
callback: ConnectionTransportCallback<T>,
|
|
22
|
-
): void;
|
|
23
|
-
removeEventListener<T extends ConnectionEvent>(
|
|
24
|
-
event: T,
|
|
25
|
-
callback: ConnectionTransportCallback<T>,
|
|
26
|
-
): void;
|
|
27
|
-
readyState: WebSocket["readyState"];
|
|
28
17
|
}
|
|
29
18
|
|
|
30
19
|
export class ConnectionSubscriptions {
|
|
@@ -1,47 +1,17 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
+
import type { ConnectionStatus as HostConnectionStatus } from "../host/connection";
|
|
3
4
|
import type { NotificationMessageData } from "../kernel/messages";
|
|
4
5
|
|
|
6
|
+
export {
|
|
7
|
+
ConnectionState,
|
|
8
|
+
ConnectionClosedReason as WebSocketClosedReason,
|
|
9
|
+
} from "../host/connection";
|
|
10
|
+
|
|
5
11
|
export type ConnectionPhase =
|
|
6
12
|
| NotificationMessageData<"startup-progress">["phase"]
|
|
7
13
|
| "reconnecting";
|
|
8
14
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
OPEN: "OPEN",
|
|
13
|
-
CLOSING: "CLOSING",
|
|
14
|
-
CLOSED: "CLOSED",
|
|
15
|
-
} as const;
|
|
16
|
-
|
|
17
|
-
export type ConnectionState =
|
|
18
|
-
(typeof ConnectionState)[keyof typeof ConnectionState];
|
|
19
|
-
|
|
20
|
-
export const WebSocketClosedReason = {
|
|
21
|
-
KERNEL_DISCONNECTED: "KERNEL_DISCONNECTED",
|
|
22
|
-
KERNEL_STARTUP_ERROR: "KERNEL_STARTUP_ERROR",
|
|
23
|
-
} as const;
|
|
24
|
-
|
|
25
|
-
export type WebSocketClosedReason =
|
|
26
|
-
(typeof WebSocketClosedReason)[keyof typeof WebSocketClosedReason];
|
|
27
|
-
|
|
28
|
-
export type ConnectionStatus =
|
|
29
|
-
| {
|
|
30
|
-
state: typeof ConnectionState.CLOSED;
|
|
31
|
-
code: WebSocketClosedReason;
|
|
32
|
-
/**
|
|
33
|
-
* Human-readable reason for closing the connection.
|
|
34
|
-
*/
|
|
35
|
-
reason: string;
|
|
36
|
-
phase?: ConnectionPhase;
|
|
37
|
-
}
|
|
38
|
-
| {
|
|
39
|
-
state: typeof ConnectionState.CONNECTING;
|
|
40
|
-
phase?: ConnectionPhase;
|
|
41
|
-
}
|
|
42
|
-
| {
|
|
43
|
-
state:
|
|
44
|
-
| typeof ConnectionState.OPEN
|
|
45
|
-
| typeof ConnectionState.CLOSING
|
|
46
|
-
| typeof ConnectionState.NOT_STARTED;
|
|
47
|
-
};
|
|
15
|
+
export type ConnectionStatus = HostConnectionStatus & {
|
|
16
|
+
phase?: ConnectionPhase;
|
|
17
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { useStore } from "jotai";
|
|
4
|
+
import { useEffect, useMemo } from "react";
|
|
5
|
+
import useEvent from "react-use-event-hook";
|
|
6
|
+
import { jsonParseWithSpecialChar } from "@/utils/json/json-parser";
|
|
7
|
+
import type {
|
|
8
|
+
NotificationMessage,
|
|
9
|
+
NotificationPayload,
|
|
10
|
+
} from "../kernel/messages";
|
|
11
|
+
import {
|
|
12
|
+
createNotebookClient,
|
|
13
|
+
type NotebookClient,
|
|
14
|
+
} from "../host/notebook-client";
|
|
15
|
+
import { classifyCloseEvent } from "./connection-state";
|
|
16
|
+
import { notebookClientAtom } from "./state";
|
|
17
|
+
import {
|
|
18
|
+
createConnectionTransport,
|
|
19
|
+
type ConnectionTransportOptions,
|
|
20
|
+
} from "./transports/create";
|
|
21
|
+
|
|
22
|
+
type Options = ConnectionTransportOptions & {
|
|
23
|
+
waitUntilReady: () => Promise<void>;
|
|
24
|
+
checkHealth: () => Promise<boolean>;
|
|
25
|
+
onNotification: (notification: NotificationPayload) => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function useNotebookClient(options: Options): NotebookClient {
|
|
29
|
+
const store = useStore();
|
|
30
|
+
const { static: isStatic, transportType } = options;
|
|
31
|
+
const controller = useMemo(() => createNotebookClient(store), [store]);
|
|
32
|
+
const url = useEvent(options.url);
|
|
33
|
+
const headers = useEvent(options.headers);
|
|
34
|
+
const waitUntilReady = useEvent(options.waitUntilReady);
|
|
35
|
+
const checkHealth = useEvent(options.checkHealth);
|
|
36
|
+
const onNotification = useEvent(options.onNotification);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const lifetime = new AbortController();
|
|
40
|
+
store.set(notebookClientAtom, controller.client);
|
|
41
|
+
controller.connect({
|
|
42
|
+
createTransport: (url) =>
|
|
43
|
+
createConnectionTransport({
|
|
44
|
+
url,
|
|
45
|
+
headers,
|
|
46
|
+
static: isStatic,
|
|
47
|
+
transportType,
|
|
48
|
+
}),
|
|
49
|
+
url,
|
|
50
|
+
signal: lifetime.signal,
|
|
51
|
+
waitUntilReady,
|
|
52
|
+
checkHealth,
|
|
53
|
+
// The host validates the envelope, not the native notification contents.
|
|
54
|
+
onNotification: (notification) =>
|
|
55
|
+
onNotification({ data: notification as NotificationMessage }),
|
|
56
|
+
decodeJson: jsonParseWithSpecialChar,
|
|
57
|
+
classifyClose: classifyCloseEvent,
|
|
58
|
+
});
|
|
59
|
+
return () => {
|
|
60
|
+
lifetime.abort();
|
|
61
|
+
if (store.get(notebookClientAtom) === controller.client) {
|
|
62
|
+
store.set(notebookClientAtom, null);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}, [
|
|
66
|
+
controller,
|
|
67
|
+
store,
|
|
68
|
+
isStatic,
|
|
69
|
+
transportType,
|
|
70
|
+
url,
|
|
71
|
+
headers,
|
|
72
|
+
waitUntilReady,
|
|
73
|
+
checkHealth,
|
|
74
|
+
onNotification,
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
return controller.client;
|
|
78
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { invalidatePackageData } from "@/core/packages/package-data";
|
|
4
|
-
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
5
|
-
import {
|
|
4
|
+
import { useAtom, useAtomValue, useSetAtom, useStore } from "jotai";
|
|
5
|
+
import { useLayoutEffect } from "react";
|
|
6
6
|
import { useErrorBoundary } from "react-error-boundary";
|
|
7
7
|
import { toast } from "@/components/ui/use-toast";
|
|
8
|
-
import { attachmentsAtom } from "@/core/attachments/state";
|
|
9
8
|
import { getNotebook, useCellActions } from "@/core/cells/cells";
|
|
10
9
|
import { applyTransactionChanges } from "@/core/cells/document-changes";
|
|
11
10
|
import { AUTOCOMPLETER } from "@/core/codemirror/completion/Autocompleter";
|
|
@@ -13,8 +12,7 @@ import type {
|
|
|
13
12
|
NotificationMessageData,
|
|
14
13
|
NotificationPayload,
|
|
15
14
|
} from "@/core/kernel/messages";
|
|
16
|
-
import {
|
|
17
|
-
import { useConnectionTransport } from "@/core/websocket/useWebSocket";
|
|
15
|
+
import { useNotebookClient } from "@/core/websocket/useNotebookClient";
|
|
18
16
|
import { renderHTML } from "@/plugins/core/RenderHTML";
|
|
19
17
|
import {
|
|
20
18
|
handleWidgetMessage,
|
|
@@ -22,11 +20,7 @@ import {
|
|
|
22
20
|
} from "@/plugins/impl/anywidget/registry";
|
|
23
21
|
import { logNever } from "@/utils/assertNever";
|
|
24
22
|
import { prettyError } from "@/utils/errors";
|
|
25
|
-
import {
|
|
26
|
-
type JsonString,
|
|
27
|
-
safeExtractSetUIElementMessageBuffers,
|
|
28
|
-
} from "@/utils/json/base64";
|
|
29
|
-
import { jsonParseWithSpecialChar } from "@/utils/json/json-parser";
|
|
23
|
+
import { safeExtractSetUIElementMessageBuffers } from "@/utils/json/base64";
|
|
30
24
|
import { Logger } from "@/utils/Logger";
|
|
31
25
|
import { reloadSafe } from "@/utils/reload-safe";
|
|
32
26
|
import { useAlertActions } from "../alerts/state";
|
|
@@ -82,104 +76,10 @@ import { useStorageActions } from "../storage/state";
|
|
|
82
76
|
import { useVariablesActions } from "../variables/state";
|
|
83
77
|
import type { VariableName } from "../variables/types";
|
|
84
78
|
import { isWasm } from "../wasm/utils";
|
|
85
|
-
import {
|
|
86
|
-
type ConnectionStatus,
|
|
87
|
-
WebSocketClosedReason,
|
|
88
|
-
ConnectionState,
|
|
89
|
-
} from "./types";
|
|
79
|
+
import { WebSocketClosedReason, ConnectionState } from "./types";
|
|
90
80
|
|
|
91
81
|
const SUPPORTS_LAZY_KERNELS = true;
|
|
92
82
|
|
|
93
|
-
// All MARIMO_* reasons except TRANSPORT_EXHAUSTED are emitted by the backend
|
|
94
|
-
// (marimo/_server/api/endpoints/ws_endpoint.py and ws/*.py). Keep in sync with
|
|
95
|
-
// the backend literals. Over the SSE transport these arrive as in-band
|
|
96
|
-
// `close` events rather than WebSocket close frames, so every terminal
|
|
97
|
-
// reason must be classified here — an unclassified reason falls into the
|
|
98
|
-
// retry path.
|
|
99
|
-
export type CloseReason =
|
|
100
|
-
| "MARIMO_NO_FILE_KEY"
|
|
101
|
-
| "MARIMO_NO_SESSION_ID"
|
|
102
|
-
| "MARIMO_NO_SESSION"
|
|
103
|
-
| "MARIMO_SHUTDOWN"
|
|
104
|
-
| "MARIMO_KERNEL_STARTUP_ERROR"
|
|
105
|
-
| "MARIMO_UNAUTHORIZED"
|
|
106
|
-
| "MARIMO_KIOSK_NOT_ALLOWED"
|
|
107
|
-
| typeof TRANSPORT_EXHAUSTED_REASON;
|
|
108
|
-
|
|
109
|
-
export type CloseDecision =
|
|
110
|
-
| { kind: "terminal"; status: ConnectionStatus; closeTransport: boolean }
|
|
111
|
-
| { kind: "gave-up"; status: ConnectionStatus }
|
|
112
|
-
| { kind: "retry"; status: ConnectionStatus };
|
|
113
|
-
|
|
114
|
-
export function classifyCloseEvent(event: { reason?: string }): CloseDecision {
|
|
115
|
-
switch (event.reason as CloseReason | undefined) {
|
|
116
|
-
case TRANSPORT_EXHAUSTED_REASON:
|
|
117
|
-
return {
|
|
118
|
-
kind: "gave-up",
|
|
119
|
-
status: {
|
|
120
|
-
state: ConnectionState.CLOSED,
|
|
121
|
-
code: WebSocketClosedReason.KERNEL_DISCONNECTED,
|
|
122
|
-
reason: "kernel not found",
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
case "MARIMO_NO_FILE_KEY":
|
|
126
|
-
case "MARIMO_NO_SESSION_ID":
|
|
127
|
-
case "MARIMO_NO_SESSION":
|
|
128
|
-
case "MARIMO_SHUTDOWN":
|
|
129
|
-
return {
|
|
130
|
-
kind: "terminal",
|
|
131
|
-
status: {
|
|
132
|
-
state: ConnectionState.CLOSED,
|
|
133
|
-
code: WebSocketClosedReason.KERNEL_DISCONNECTED,
|
|
134
|
-
reason: "kernel not found",
|
|
135
|
-
},
|
|
136
|
-
closeTransport: true,
|
|
137
|
-
};
|
|
138
|
-
case "MARIMO_UNAUTHORIZED":
|
|
139
|
-
return {
|
|
140
|
-
kind: "terminal",
|
|
141
|
-
status: {
|
|
142
|
-
state: ConnectionState.CLOSED,
|
|
143
|
-
code: WebSocketClosedReason.KERNEL_DISCONNECTED,
|
|
144
|
-
reason: "not authorized",
|
|
145
|
-
},
|
|
146
|
-
closeTransport: true,
|
|
147
|
-
};
|
|
148
|
-
case "MARIMO_KIOSK_NOT_ALLOWED":
|
|
149
|
-
return {
|
|
150
|
-
kind: "terminal",
|
|
151
|
-
status: {
|
|
152
|
-
state: ConnectionState.CLOSED,
|
|
153
|
-
code: WebSocketClosedReason.KERNEL_DISCONNECTED,
|
|
154
|
-
reason: "kiosk mode is not available for this session",
|
|
155
|
-
},
|
|
156
|
-
closeTransport: true,
|
|
157
|
-
};
|
|
158
|
-
case "MARIMO_KERNEL_STARTUP_ERROR":
|
|
159
|
-
return {
|
|
160
|
-
kind: "terminal",
|
|
161
|
-
status: {
|
|
162
|
-
state: ConnectionState.CLOSED,
|
|
163
|
-
code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
|
|
164
|
-
reason: "Failed to start kernel sandbox",
|
|
165
|
-
},
|
|
166
|
-
closeTransport: true,
|
|
167
|
-
};
|
|
168
|
-
default:
|
|
169
|
-
// Empty/undefined reasons are normal transient closes. Anything else is
|
|
170
|
-
// an unknown server reason; warn so a new MARIMO_* reason doesn't fall
|
|
171
|
-
// silently into the retry path.
|
|
172
|
-
if (event.reason) {
|
|
173
|
-
logNever(event.reason as never);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
kind: "retry",
|
|
179
|
-
status: { state: ConnectionState.CONNECTING },
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
83
|
function getExistingCells(): CellData[] | undefined {
|
|
184
84
|
if (!SUPPORTS_LAZY_KERNELS) {
|
|
185
85
|
return undefined;
|
|
@@ -191,16 +91,12 @@ function getExistingCells(): CellData[] | undefined {
|
|
|
191
91
|
);
|
|
192
92
|
}
|
|
193
93
|
|
|
194
|
-
/**
|
|
195
|
-
|
|
196
|
-
*/
|
|
197
|
-
export function useMarimoKernelConnection(opts: {
|
|
94
|
+
/** Coordinate session startup and apply notifications to the editor. */
|
|
95
|
+
export function useNotebookSession(opts: {
|
|
198
96
|
sessionId: SessionId;
|
|
199
97
|
autoInstantiate: boolean;
|
|
200
98
|
setCells: (cells: CellData[], layout: LayoutState) => void;
|
|
201
99
|
}) {
|
|
202
|
-
// Track whether we want to try reconnecting.
|
|
203
|
-
const shouldTryReconnecting = useRef<boolean>(true);
|
|
204
100
|
const { autoInstantiate, sessionId, setCells } = opts;
|
|
205
101
|
const { showBoundary } = useErrorBoundary();
|
|
206
102
|
|
|
@@ -237,7 +133,6 @@ export function useMarimoKernelConnection(opts: {
|
|
|
237
133
|
} = useAlertActions();
|
|
238
134
|
const setKioskMode = useSetAtom(kioskModeAtom);
|
|
239
135
|
const setStableSessionId = useSetAtom(stableSessionIdAtom);
|
|
240
|
-
const setAttachments = useSetAtom(attachmentsAtom);
|
|
241
136
|
const setCapabilities = useSetAtom(capabilitiesAtom);
|
|
242
137
|
const runtimeManager = useRuntimeManager();
|
|
243
138
|
const transportType = useAtomValue(connectionTransportTypeAtom);
|
|
@@ -248,15 +143,11 @@ export function useMarimoKernelConnection(opts: {
|
|
|
248
143
|
filterFromVariables: filterStorageFromVariables,
|
|
249
144
|
} = useStorageActions();
|
|
250
145
|
|
|
251
|
-
const
|
|
252
|
-
const msg = jsonParseWithSpecialChar(e.data);
|
|
146
|
+
const handleNotification = (msg: NotificationPayload) => {
|
|
253
147
|
switch (msg.data.op) {
|
|
254
148
|
case "reload":
|
|
255
149
|
reloadSafe();
|
|
256
150
|
return;
|
|
257
|
-
case "attachments":
|
|
258
|
-
setAttachments(msg.data.attachments);
|
|
259
|
-
return;
|
|
260
151
|
case "startup-progress": {
|
|
261
152
|
const { phase } = msg.data;
|
|
262
153
|
updateStartupProgress(msg.data);
|
|
@@ -288,7 +179,6 @@ export function useMarimoKernelConnection(opts: {
|
|
|
288
179
|
existingCells,
|
|
289
180
|
});
|
|
290
181
|
if (!msg.data.resumed) {
|
|
291
|
-
setAttachments([]);
|
|
292
182
|
// A freshly started kernel may expose a different environment
|
|
293
183
|
// (e.g. new env vars); re-run discovery instead of reusing stale
|
|
294
184
|
// suggestions from the previous kernel instance.
|
|
@@ -511,109 +401,54 @@ export function useMarimoKernelConnection(opts: {
|
|
|
511
401
|
}
|
|
512
402
|
};
|
|
513
403
|
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
}
|
|
404
|
+
const store = useStore();
|
|
405
|
+
const onMessageError = (error: unknown) => {
|
|
406
|
+
toast({
|
|
407
|
+
title: "Failed to handle message",
|
|
408
|
+
description: prettyError(error),
|
|
409
|
+
variant: "danger",
|
|
410
|
+
});
|
|
522
411
|
};
|
|
523
|
-
|
|
524
|
-
// Manual reconnect. Probes /health first to fail fast when the runtime
|
|
525
|
-
// is unreachable, instead of waiting on partysocket's retry budget.
|
|
526
|
-
const reconnect = async () => {
|
|
527
|
-
if (
|
|
528
|
-
ws.readyState === WebSocket.OPEN ||
|
|
529
|
-
ws.readyState === WebSocket.CONNECTING
|
|
530
|
-
) {
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
shouldTryReconnecting.current = true;
|
|
534
|
-
setKernelStartupError(null);
|
|
535
|
-
setConnection({ state: ConnectionState.CONNECTING });
|
|
536
|
-
const healthy = await runtimeManager.reconcileFromHealth();
|
|
537
|
-
if (!healthy) {
|
|
538
|
-
shouldTryReconnecting.current = false;
|
|
539
|
-
setConnection({
|
|
540
|
-
state: ConnectionState.CLOSED,
|
|
541
|
-
code: WebSocketClosedReason.KERNEL_DISCONNECTED,
|
|
542
|
-
reason: "kernel not found",
|
|
543
|
-
});
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
ws.reconnect();
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
const ws = useConnectionTransport({
|
|
412
|
+
const notebook = useNotebookClient({
|
|
550
413
|
static: isStaticNotebook(),
|
|
551
414
|
transportType,
|
|
552
|
-
/**
|
|
553
|
-
* Unique URL for this session.
|
|
554
|
-
*/
|
|
555
415
|
url: () =>
|
|
556
416
|
transportType === "sse"
|
|
557
417
|
? runtimeManager.getSseURL(sessionId).toString()
|
|
558
418
|
: runtimeManager.getWsURL(sessionId).toString(),
|
|
559
|
-
|
|
560
|
-
* Auth headers for the SSE transport. WebSockets cannot send headers
|
|
561
|
-
* and instead put the access token in the URL.
|
|
562
|
-
*/
|
|
419
|
+
// SSE uses headers for auth. WebSockets carry the token in the URL.
|
|
563
420
|
headers: () => runtimeManager.headers(),
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
*/
|
|
568
|
-
onOpen: async () => {
|
|
569
|
-
updateStartupProgress(null);
|
|
570
|
-
setAttachments([]);
|
|
571
|
-
// If we are open, we can reset our reconnecting flag.
|
|
572
|
-
shouldTryReconnecting.current = true;
|
|
573
|
-
},
|
|
574
|
-
|
|
575
|
-
/**
|
|
576
|
-
* Wait to connect, in case the remote kernel still starting up.
|
|
577
|
-
*/
|
|
578
|
-
waitToConnect: async () => {
|
|
579
|
-
if (isStaticNotebook()) {
|
|
580
|
-
return;
|
|
421
|
+
waitUntilReady: async () => {
|
|
422
|
+
if (!isStaticNotebook() && !isWasm() && !runtimeManager.isSameOrigin) {
|
|
423
|
+
await runtimeManager.waitForHealthy();
|
|
581
424
|
}
|
|
582
|
-
if (isWasm()) {
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
if (runtimeManager.isSameOrigin) {
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
await runtimeManager.waitForHealthy();
|
|
590
425
|
},
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
* Handle messages sent by the kernel.
|
|
594
|
-
*/
|
|
595
|
-
onMessage: (e) => {
|
|
426
|
+
checkHealth: () => runtimeManager.reconcileFromHealth(),
|
|
427
|
+
onNotification: (notification) => {
|
|
596
428
|
try {
|
|
597
|
-
|
|
429
|
+
handleNotification(notification);
|
|
598
430
|
} catch (error) {
|
|
599
|
-
Logger.error("Failed to handle
|
|
600
|
-
|
|
601
|
-
title: "Failed to handle message",
|
|
602
|
-
description: prettyError(error),
|
|
603
|
-
variant: "danger",
|
|
604
|
-
});
|
|
431
|
+
Logger.error("Failed to handle notification", notification, error);
|
|
432
|
+
onMessageError(error);
|
|
605
433
|
}
|
|
606
434
|
},
|
|
435
|
+
});
|
|
607
436
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
437
|
+
// Subscribe before passive connection setup: static/WASM transports can emit synchronously.
|
|
438
|
+
useLayoutEffect(() => {
|
|
439
|
+
const updateConnection = () => {
|
|
440
|
+
const status = store.get(notebook.connectionAtom);
|
|
441
|
+
if (status.state === ConnectionState.NOT_STARTED) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (status.state === ConnectionState.OPEN) {
|
|
445
|
+
updateStartupProgress(null);
|
|
446
|
+
return; // kernel-ready, not the socket, establishes session readiness.
|
|
447
|
+
}
|
|
448
|
+
if (status.state === ConnectionState.CONNECTING) {
|
|
449
|
+
setKernelStartupError(null);
|
|
450
|
+
}
|
|
615
451
|
setConnection((previous) => {
|
|
616
|
-
const status = decision.status;
|
|
617
452
|
if (
|
|
618
453
|
status.state === ConnectionState.CLOSED &&
|
|
619
454
|
status.code === WebSocketClosedReason.KERNEL_STARTUP_ERROR &&
|
|
@@ -632,32 +467,17 @@ export function useMarimoKernelConnection(opts: {
|
|
|
632
467
|
}
|
|
633
468
|
return status;
|
|
634
469
|
});
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
* When we encounter an error, we should close the connection.
|
|
649
|
-
*/
|
|
650
|
-
onError: (e) => {
|
|
651
|
-
Logger.warn("WebSocket error", e);
|
|
652
|
-
setAttachments([]);
|
|
653
|
-
setConnection({
|
|
654
|
-
state: ConnectionState.CLOSED,
|
|
655
|
-
code: WebSocketClosedReason.KERNEL_DISCONNECTED,
|
|
656
|
-
reason: "kernel not found",
|
|
657
|
-
});
|
|
658
|
-
tryReconnecting();
|
|
659
|
-
},
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
return { connection, reconnect };
|
|
470
|
+
};
|
|
471
|
+
const unsubscribe = store.sub(notebook.connectionAtom, updateConnection);
|
|
472
|
+
updateConnection();
|
|
473
|
+
return unsubscribe;
|
|
474
|
+
}, [
|
|
475
|
+
notebook,
|
|
476
|
+
store,
|
|
477
|
+
setConnection,
|
|
478
|
+
setKernelStartupError,
|
|
479
|
+
updateStartupProgress,
|
|
480
|
+
]);
|
|
481
|
+
|
|
482
|
+
return { notebook, connection, reconnect: notebook.reconnect };
|
|
663
483
|
}
|