@marimo-team/islands 0.25.3-dev17 → 0.25.3-dev170
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-BbSXV-Xa.js} +21 -19
- package/dist/{ErrorBoundary-jaGao4LA.js → ErrorBoundary-NE86qEWZ.js} +18 -18
- package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-D7n07nXf.js} +2 -2
- package/dist/any-language-editor-3E24SnCV.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-D6CjRib5.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
- package/dist/assets/__vite-browser-external-DVeFf2e7.js +1 -0
- package/dist/assets/worker-C1pfNBsx.js +113 -0
- package/dist/{button-CvjkwdSk.js → button-BQRdqQi1.js} +199 -192
- package/dist/{capabilities-CQ5IjhzL.js → capabilities-CI4xUPsp.js} +2 -2
- package/dist/{chat-ui-DaHynk8g.js → chat-ui-CC7GekpG.js} +1144 -1143
- package/dist/{check-EDwSwP89.js → check-SpOMoCLP.js} +1 -1
- package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-6dtPz7uS.js} +1 -1
- package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-BwnFNKDA.js} +1 -1
- package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-D2eV4vBJ.js} +1 -1
- package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-BDs4-_-r.js} +1 -1
- package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
- package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-DCJnf_8v.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-BOmSzPBD.js} +2 -2
- package/dist/{code-visibility-Bub8tnaA.js → code-visibility-Bzvc6ajl.js} +25 -25
- package/dist/{common-CdT0nyN1.js → common-Ciw4zkh2.js} +6933 -6754
- package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-C1trdvVt.js} +3 -3
- package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-Bln_Soky.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-D-nf7WzW.js} +1 -1
- package/dist/{data-grid-overlay-editor-Dbcss6_2.js → data-grid-overlay-editor-AQREmxI7.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-PsgSMkY2.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-DfwfUuSw.js} +1 -1
- package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-C9_e-oMc.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-CWLa5tuY.js} +1 -1
- package/dist/{dialog-CEBFZNyc.js → dialog-DgeqyrEZ.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-B6ftyzOg.js} +1277 -1229
- package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-B1QRM58v.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-DJK_FqvW.js} +1 -1
- package/dist/{error-banner-D-WCIiMt.js → error-banner-D40pmfQQ.js} +83 -83
- package/dist/{error-entries-Bv6bZPF3.js → error-entries-DgK3wUJR.js} +5728 -5638
- package/dist/{feedback-button-B3Wrnsf_.js → feedback-button-8a6SETmT.js} +23 -23
- package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-Uxc25XSJ.js} +2 -2
- package/dist/{formats-27CW_UP4.js → formats-DQCyPd5Z.js} +3 -3
- package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
- package/dist/{glide-data-editor-BDYKJdqK.js → glide-data-editor-B70oke_v.js} +370 -368
- package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Dz_F4pAY.js} +16 -16
- package/dist/{globals-DGTE1S0_.js → globals-Cxd5hNlI.js} +3 -3
- package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
- package/dist/{html-to-image-Bf8QMGi4.js → html-to-image-D4yG5Ru0.js} +631 -594
- package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-DmjxqwL0.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-NgCLHqQi.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-CZaG-iam.js} +1 -1
- package/dist/{label-BDKF3Afc.js → label-D9_-zSg-.js} +3 -3
- package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
- package/dist/{loader-BQOfjTIJ.js → loader-DOVB0Syl.js} +12 -12
- package/dist/main.js +7689 -7342
- package/dist/{mermaid-CHo49Yv-.js → mermaid-CSvw7c71.js} +38 -38
- package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
- package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-DJYQBDH_.js} +1 -1
- package/dist/{number-overlay-editor-S8UJY0i5.js → number-overlay-editor-ChK-SKUk.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-CHKO_dAe.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-DVevbMAo.js} +1 -1
- package/dist/precisionRound-DhAzeRcm.js +49 -0
- package/dist/{process-output-BZg56CCb.js → process-output-DxvBAeL3.js} +4 -4
- package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-Detj1kcB.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-BLFD0T_Z.js} +1 -1
- package/dist/{reveal-component-De4Pvf4V.js → reveal-component-CXMXeDZD.js} +14 -14
- package/dist/{select-BButFi-d.js → select-CkF_GBxO.js} +537 -458
- package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-BZBgatQV.js} +1 -1
- package/dist/{spec-BwarBpUJ.js → spec-Mnkn3XEr.js} +41 -11
- package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-4BZW9-6_.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH-C4sKC8Iq.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-SL5yeGe2.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-DqPmAYUv.js} +2 -2
- package/dist/{swiper-component-DKntyn8p.js → swiper-component-BkCm-kLg.js} +2 -2
- package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
- package/dist/{timeline-definition-24CTP7MA-D3c46WM5.js → timeline-definition-24CTP7MA-BfXUMWkq.js} +1 -1
- package/dist/{toDate-UtiwmnH2.js → toDate-TMubKIVL.js} +37 -36
- package/dist/{toast-B9UMEi8u.js → toast-Btbve3Lm.js} +231 -213
- package/dist/{tooltip-BO1REbqs.js → tooltip-CJ4Rqasp.js} +327 -303
- package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-CmGwRNId.js} +1482 -1433
- package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-DjMGFzFy.js} +15 -14
- package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-eCL_SdeG.js} +2 -2
- package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-BQR5TJot.js} +1 -1
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-CSdWn6NP.js} +1 -1
- package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-C_GvKWbQ.js} +28 -25
- package/dist/{useTheme-CRnjkku0.js → useTheme-Bns2gJZ6.js} +224 -220
- package/dist/{vega-component-BeF9dfyO.js → vega-component-BsVptuhz.js} +148 -148
- 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-CH65znGa.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-z9pWKb2_.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-MxyaWjjq.js} +2 -2
- package/package.json +16 -16
- package/src/__mocks__/requests.ts +0 -1
- package/src/__tests__/module-entry.test.ts +44 -0
- package/src/__tests__/mount.test.ts +1 -21
- package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
- package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
- package/src/components/ai/ai-model-dropdown.tsx +11 -6
- package/src/components/app-config/ai-config.tsx +7 -2
- package/src/components/app-config/user-config-form.tsx +35 -0
- package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
- package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
- package/src/components/chat/acp/__tests__/state.test.ts +2 -2
- package/src/components/chat/acp/blocks.tsx +3 -2
- package/src/components/chat/acp/session-tabs.tsx +8 -3
- package/src/components/chat/chat-components.tsx +2 -0
- package/src/components/chat/chat-panel.tsx +9 -8
- package/src/components/chat/chat-utils.ts +4 -8
- package/src/components/data-table/TableBottomBar.tsx +4 -1
- package/src/components/data-table/TableTopBar.tsx +2 -0
- package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
- package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
- package/src/components/data-table/__tests__/columns.test.tsx +105 -0
- package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
- package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
- package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
- package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
- package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
- package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
- package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
- package/src/components/data-table/cell-selection/feature.ts +2 -2
- package/src/components/data-table/cell-selection/types.ts +2 -2
- package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
- package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
- package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
- package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
- package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
- package/src/components/data-table/charts/charts.tsx +40 -30
- package/src/components/data-table/charts/storage.ts +16 -5
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
- package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
- package/src/components/data-table/column-summary/column-summary.tsx +2 -4
- package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
- package/src/components/data-table/columns.tsx +45 -46
- package/src/components/data-table/context-menu.tsx +36 -20
- package/src/components/data-table/data-table.tsx +17 -1
- package/src/components/data-table/date-popover.tsx +6 -4
- package/src/components/data-table/export-actions.tsx +76 -48
- package/src/components/data-table/pagination.tsx +44 -33
- package/src/components/data-table/range-focus/atoms.ts +4 -6
- package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
- package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
- package/src/components/data-table/renderers.tsx +16 -7
- package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
- package/src/components/data-table/search-highlight.tsx +79 -0
- package/src/components/data-table/types.ts +1 -0
- package/src/components/data-table/url-detector.tsx +49 -9
- package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
- package/src/components/datasources/__tests__/column-preview.test.tsx +98 -5
- package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
- package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
- package/src/components/datasources/__tests__/utils.test.ts +57 -6
- package/src/components/datasources/column-preview.tsx +13 -4
- package/src/components/datasources/datasources.tsx +51 -39
- package/src/components/datasources/utils.ts +32 -23
- package/src/components/debugger/debugger-code.tsx +5 -0
- package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
- package/src/components/dependency-graph/elements.ts +24 -21
- package/src/components/dependency-graph/panels.tsx +2 -1
- package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
- package/src/components/editor/SortableCell.tsx +1 -3
- package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
- package/src/components/editor/__tests__/data-attributes.test.tsx +6 -2
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
- 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/actions/useRestartKernel.tsx +1 -1
- package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
- package/src/components/editor/ai/ai-completion-editor.tsx +1 -0
- package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
- package/src/components/editor/alerts/connection-notice.tsx +1 -1
- package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
- package/src/components/editor/app-container.tsx +1 -1
- package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
- package/src/components/editor/cell/CreateCellButton.tsx +10 -12
- package/src/components/editor/cell/DeleteButton.tsx +3 -2
- package/src/components/editor/cell/RunButton.tsx +2 -2
- package/src/components/editor/cell/StagedAICell.tsx +10 -2
- package/src/components/editor/cell/StopButton.tsx +2 -2
- 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 +9 -16
- package/src/components/editor/cell/code/language-toggle.tsx +8 -7
- package/src/components/editor/cell/collapse.tsx +5 -3
- package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
- package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
- package/src/components/editor/chrome/components/feedback-button.tsx +1 -1
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
- package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
- package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
- package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
- package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
- package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
- package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
- package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/snippets-panel.tsx +5 -1
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +9 -18
- package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
- package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +58 -1
- package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
- package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
- package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
- package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
- package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +12 -2
- package/src/components/editor/chrome/wrapper/storage.ts +1 -1
- package/src/components/editor/connections/components.tsx +1 -1
- package/src/components/editor/controls/Controls.tsx +2 -2
- package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
- package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
- package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +1 -1
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +9 -23
- package/src/components/editor/errors/send-error-report-button.tsx +3 -6
- package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
- package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
- package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
- package/src/components/editor/file-tree/file-explorer.tsx +45 -8
- package/src/components/editor/file-tree/file-name-input.tsx +1 -0
- 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/header/__tests__/status.test.tsx +5 -3
- package/src/components/editor/header/app-header.tsx +1 -1
- package/src/components/editor/header/status.tsx +40 -20
- package/src/components/editor/links/__tests__/cell-link.test.tsx +146 -0
- package/src/components/editor/links/cell-link.tsx +14 -5
- package/src/components/editor/navigation/focus-utils.ts +4 -4
- package/src/components/editor/notebook-banner.tsx +1 -0
- package/src/components/editor/notebook-cell.tsx +12 -3
- package/src/components/editor/output/MarimoErrorOutput.tsx +2 -1
- package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
- package/src/components/editor/output/Outputs.css +8 -0
- package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
- package/src/components/editor/output/ansi-reduce.ts +33 -20
- package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
- package/src/components/editor/package-alert.tsx +14 -7
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +12 -6
- package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
- package/src/components/home/components.tsx +17 -31
- package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
- package/src/components/icons/large-spinner.tsx +3 -14
- package/src/components/icons/spinner.tsx +1 -1
- package/src/components/mcp/mcp-status-indicator.tsx +1 -1
- package/src/components/pages/__tests__/home-page.test.tsx +116 -0
- package/src/components/pages/home-page.tsx +164 -91
- package/src/components/scratchpad/scratchpad.test.tsx +95 -0
- package/src/components/scratchpad/scratchpad.tsx +16 -3
- package/src/components/slides/reveal-component.tsx +4 -2
- package/src/components/slides/slide-cell-view.tsx +1 -1
- package/src/components/slides/slide-form.tsx +1 -0
- package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
- package/src/components/tracing/tracing.tsx +45 -41
- package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
- package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
- package/src/components/ui/alert-dialog.tsx +1 -1
- package/src/components/ui/draggable-popover.tsx +24 -2
- package/src/components/ui/range-slider.tsx +22 -9
- package/src/core/ai/__tests__/staged-cells.test.ts +8 -5
- package/src/core/ai/context/providers/datasource.ts +6 -2
- package/src/core/ai/staged-cells.ts +7 -2
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +3 -15
- package/src/core/ai/tools/base.ts +16 -7
- package/src/core/ai/tools/edit-notebook-tool.ts +61 -48
- package/src/core/ai/tools/registry.ts +13 -8
- package/src/core/ai/tools/sample-tool.ts +6 -7
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -0
- package/src/core/cells/__tests__/cell.test.ts +39 -0
- package/src/core/cells/__tests__/cells.test.ts +3 -298
- package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
- package/src/core/cells/__tests__/document.test.ts +410 -0
- package/src/core/cells/__tests__/flush-document.test.ts +40 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +108 -0
- package/src/core/cells/__tests__/session.test.ts +117 -36
- package/src/core/cells/__tests__/with-document-save.test.ts +67 -0
- package/src/core/cells/cell.ts +14 -3
- package/src/core/cells/cells.ts +30 -159
- package/src/core/cells/columns.ts +38 -0
- package/src/core/cells/document.ts +360 -0
- package/src/core/cells/pending-delete-service.ts +2 -6
- package/src/core/cells/session.ts +1 -1
- package/src/core/cells/types.ts +22 -1
- package/src/core/codemirror/__tests__/setup.test.ts +29 -29
- package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
- package/src/core/codemirror/ai/resources.ts +1 -1
- package/src/core/codemirror/cells/__tests__/extensions.test.ts +42 -1
- package/src/core/codemirror/cells/extensions.ts +21 -3
- package/src/core/codemirror/format.ts +6 -5
- package/src/core/codemirror/keymaps/vim.ts +2 -6
- package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
- package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
- package/src/core/codemirror/language/cell-code.ts +114 -0
- package/src/core/codemirror/language/extension.ts +52 -35
- package/src/core/codemirror/language/languages/markdown.ts +8 -6
- package/src/core/codemirror/language/languages/python.ts +16 -10
- package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
- package/src/core/codemirror/language/panel/panel.tsx +1 -0
- package/src/core/codemirror/language/types.ts +7 -7
- package/src/core/codemirror/language/utils.ts +9 -39
- package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
- package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
- package/src/core/codemirror/readonly/extension.ts +2 -2
- package/src/core/codemirror/rtc/extension.ts +24 -13
- package/src/core/codemirror/rtc/loro/awareness.ts +34 -19
- package/src/core/codemirror/rtc/loro/sync.ts +1 -0
- package/src/core/codemirror/utils.ts +1 -0
- package/src/core/config/__tests__/config-schema.test.ts +5 -3
- package/src/core/config/config-schema.ts +3 -0
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
- package/src/core/datasets/data-source-discovery.ts +4 -6
- package/src/core/edit-app.tsx +6 -5
- package/src/core/errors/error-entries.ts +3 -0
- package/src/core/export/hooks.ts +1 -1
- package/src/core/export/layout.ts +2 -2
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/islands/__tests__/bootstrap.test.ts +16 -42
- package/src/core/islands/__tests__/bridge.test.ts +31 -26
- package/src/core/islands/bootstrap.ts +22 -52
- package/src/core/islands/bridge.ts +31 -21
- package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
- package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
- package/src/core/islands/worker/controller.ts +4 -10
- package/src/core/islands/worker/worker.tsx +33 -33
- package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
- package/src/core/network/__tests__/connection.test.ts +1 -1
- package/src/core/network/connection-notice.ts +1 -1
- package/src/core/network/connection.ts +5 -3
- package/src/core/network/requests-lazy.ts +1 -2
- package/src/core/network/requests-network.ts +0 -9
- package/src/core/network/requests-static.ts +0 -1
- package/src/core/network/requests-toasting.tsx +0 -1
- package/src/core/network/types.ts +0 -5
- package/src/core/network/useConnectionNotice.ts +8 -2
- package/src/core/notebook/attachments.ts +38 -0
- package/src/core/notebook/document/__tests__/codemirror.test.ts +547 -0
- package/src/core/notebook/document/__tests__/draft.test.ts +322 -0
- package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
- package/src/core/notebook/document/__tests__/hold.test.ts +88 -0
- package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
- package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
- package/src/core/notebook/document/__tests__/model.test.ts +247 -0
- package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
- package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
- package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
- package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
- package/src/core/notebook/document/__tests__/state.test.ts +438 -0
- package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
- package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
- package/src/core/notebook/document/__tests__/test-host.ts +247 -0
- package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
- package/src/core/notebook/document/__tests__/user.ts +61 -0
- package/src/core/notebook/document/codemirror.ts +216 -0
- package/src/core/notebook/document/draft.ts +472 -0
- package/src/core/notebook/document/hooks.tsx +118 -0
- package/src/core/notebook/document/index.ts +23 -0
- package/src/core/notebook/document/model.ts +105 -0
- package/src/core/notebook/document/select.ts +41 -0
- package/src/core/notebook/document/state.ts +374 -0
- package/src/core/notebook/document/sync.ts +418 -0
- package/src/core/notebook/index.ts +18 -0
- package/src/core/notebook/replica/AGENTS.md +11 -0
- package/src/core/notebook/replica/__tests__/attachments.test.ts +56 -0
- package/src/core/notebook/replica/__tests__/document.test.ts +126 -0
- package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
- package/src/core/notebook/replica/__tests__/replica.test.ts +550 -0
- package/src/core/notebook/replica/backend.ts +19 -0
- package/src/core/notebook/replica/connection.ts +42 -0
- package/src/core/notebook/replica/index.ts +13 -0
- package/src/core/notebook/replica/replica.ts +286 -0
- package/src/core/notebook/replica/resources/attachments.ts +22 -0
- package/src/core/notebook/replica/resources/document.ts +101 -0
- package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
- package/src/core/packages/useSandboxController.ts +1 -1
- package/src/core/run-app.tsx +47 -44
- package/src/core/runtime/__tests__/adapter.test.ts +1 -1
- package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
- package/src/core/runtime/__tests__/runtime.test.ts +32 -58
- package/src/core/runtime/adapter.ts +1 -1
- package/src/core/runtime/config.ts +2 -2
- package/src/core/runtime/runtime.ts +38 -42
- package/src/core/runtime/types.ts +0 -2
- package/src/core/saving/filename.ts +1 -1
- package/src/core/saving/save-component.tsx +10 -4
- package/src/core/saving/useAutoSave.ts +1 -1
- package/src/core/session/__tests__/backend.test.ts +235 -0
- package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +183 -0
- package/src/core/session/__tests__/useNotebookSession.hook.test.tsx +669 -0
- package/src/core/session/active-notebook.ts +11 -0
- package/src/core/session/backend.ts +125 -0
- package/src/core/{websocket → session}/types.ts +2 -2
- package/src/core/session/useNotebookReplica.ts +72 -0
- package/src/core/{websocket → session}/useNotebookSession.tsx +56 -43
- package/src/core/state/__tests__/jotai.test.tsx +108 -0
- package/src/core/state/jotai.ts +1 -1
- package/src/core/static/__tests__/files.test.ts +38 -10
- package/src/core/static/files.ts +2 -0
- package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
- package/src/core/transports/__tests__/server.test.ts +408 -0
- package/src/core/transports/__tests__/static.test.ts +175 -0
- package/src/core/transports/__tests__/wasm.test.ts +166 -0
- package/src/core/{websocket → transports}/connection-state.ts +9 -16
- package/src/core/transports/server.ts +152 -0
- package/src/core/transports/static.ts +84 -0
- package/src/core/transports/wasm.ts +97 -0
- package/src/core/wasm/__tests__/bridge.test.ts +213 -50
- package/src/core/wasm/__tests__/worker.test.ts +187 -0
- package/src/core/wasm/bridge.ts +75 -53
- package/src/core/wasm/rpc.ts +15 -3
- package/src/core/wasm/store.ts +3 -3
- package/src/core/wasm/worker/bootstrap.ts +55 -51
- package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
- package/src/core/wasm/worker/save-worker.ts +9 -10
- package/src/core/wasm/worker/types.ts +29 -18
- package/src/core/wasm/worker/worker.ts +88 -44
- package/src/css/globals.css +10 -0
- 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 +6 -10
- package/src/hooks/useDeepCompareMemoize.ts +1 -1
- package/src/hooks/useDelayElapsed.ts +14 -10
- package/src/hooks/useElapsedTime.ts +3 -3
- package/src/hooks/useEventListener.ts +1 -2
- 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/mount.tsx +1 -5
- package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
- package/src/plugins/core/registerReactComponent.tsx +1 -1
- package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataTablePlugin.tsx +69 -37
- 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/RangeSliderPlugin.tsx +16 -2
- package/src/plugins/impl/RefreshPlugin.tsx +1 -0
- package/src/plugins/impl/SliderPlugin.tsx +6 -8
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +387 -1
- package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
- package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
- package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
- package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
- package/src/plugins/impl/anywidget/model.ts +3 -1
- package/src/plugins/impl/anywidget/registry.ts +18 -14
- 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 +14 -11
- 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/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
- package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
- package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
- package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
- package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
- package/src/plugins/impl/data-frames/panel.tsx +43 -16
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
- package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
- package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
- package/src/plugins/impl/panel/utils.ts +17 -0
- package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
- package/src/plugins/impl/plotly/selection.ts +25 -19
- package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
- package/src/plugins/impl/vega/make-selectable.ts +4 -7
- package/src/plugins/layout/RoutesPlugin.tsx +1 -1
- package/src/stories/__tests__/editor-story.test.tsx +33 -0
- package/src/stories/button.stories.tsx +1 -0
- package/src/stories/cell.stories.tsx +1 -1
- package/src/stories/data-table.stories.tsx +31 -0
- package/src/stories/editor.stories.tsx +24 -17
- package/src/stories/form-wrapper.stories.tsx +2 -2
- package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
- package/src/stories/runtime-story.tsx +1 -1
- package/src/stories/select.stories.tsx +2 -2
- package/src/stories/switchable-multi-select.stories.tsx +1 -1
- package/src/utils/__tests__/arrays.test.ts +35 -0
- package/src/utils/__tests__/batch-requests.test.ts +3 -3
- package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +29 -0
- package/src/utils/__tests__/numbers.test.ts +105 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +11 -2
- package/src/utils/blob.ts +1 -1
- package/src/utils/createReducer.ts +53 -26
- package/src/utils/maps.ts +0 -3
- package/src/utils/numbers.ts +79 -18
- package/src/utils/objects.ts +4 -9
- package/src/utils/once.ts +19 -19
- package/src/utils/tracer.ts +9 -5
- package/src/utils/zod-utils.ts +1 -3
- package/dist/any-language-editor-DZ_YTVus.js +0 -76
- package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
- package/dist/assets/worker-HPY9fbhz.js +0 -114
- package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
- package/dist/precisionRound-DMIVQrt2.js +0 -97
- package/src/core/attachments/state.ts +0 -21
- package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
- package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
- package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
- package/src/core/cells/document-changes.ts +0 -944
- package/src/core/host/AGENTS.md +0 -6
- package/src/core/host/__tests__/events.test.ts +0 -46
- package/src/core/host/__tests__/notebook-client.test.ts +0 -191
- package/src/core/host/__tests__/protocol.test.ts +0 -214
- package/src/core/host/connection.ts +0 -81
- package/src/core/host/events.ts +0 -60
- package/src/core/host/notebook-client.ts +0 -226
- package/src/core/host/protocol.ts +0 -1943
- package/src/core/state/__tests__/jotai.test.ts +0 -25
- package/src/core/wasm/worker/message-buffer.ts +0 -33
- package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -140
- package/src/core/websocket/__tests__/useNotebookSession.hook.test.tsx +0 -883
- package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
- package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
- package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
- package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
- package/src/core/websocket/transports/basic.ts +0 -102
- package/src/core/websocket/transports/create.ts +0 -31
- package/src/core/websocket/transports/sse.ts +0 -308
- package/src/core/websocket/transports/transport.ts +0 -50
- package/src/core/websocket/transports/ws.ts +0 -98
- package/src/core/websocket/useNotebookClient.ts +0 -80
- /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
- /package/src/core/{websocket → session}/connection-utils.ts +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/** The backend the editor follows, chosen once for its mode. */
|
|
4
|
+
|
|
5
|
+
import { NotebookClient, StreamStopped } from "@marimo-team/client";
|
|
6
|
+
import type { NotebookId } from "@marimo-team/client/protocol";
|
|
7
|
+
import type { NotebookBackend } from "../notebook/replica";
|
|
8
|
+
import { PyodideBridge } from "../wasm/bridge";
|
|
9
|
+
import { isWasm } from "../wasm/utils";
|
|
10
|
+
import { sessionSse, websocket, withHeaders } from "../transports/server";
|
|
11
|
+
import { idle } from "../transports/static";
|
|
12
|
+
|
|
13
|
+
/** How the editor follows its notebook. */
|
|
14
|
+
export interface EditorBackendOptions {
|
|
15
|
+
static: boolean;
|
|
16
|
+
transportType: "websocket" | "sse";
|
|
17
|
+
/** The session route, read before each connection. */
|
|
18
|
+
url: () => string;
|
|
19
|
+
/** The runtime's headers, read before each request. */
|
|
20
|
+
headers: () => Record<string, string>;
|
|
21
|
+
/** The host URL of a notebook the stream reported. */
|
|
22
|
+
notebookUrl: (notebookId: NotebookId) => URL;
|
|
23
|
+
/** Wait until the host can accept the first connection. */
|
|
24
|
+
waitUntilReady: () => Promise<void>;
|
|
25
|
+
/** Whether the host is reachable before an explicit reconnect. */
|
|
26
|
+
checkHealth: () => Promise<boolean>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Read the session route once the host has started.
|
|
31
|
+
*
|
|
32
|
+
* Every connection waits for the same startup. Once it settles, even by
|
|
33
|
+
* giving up, connections read the route directly. An explicit reconnect
|
|
34
|
+
* checks the host's health instead.
|
|
35
|
+
*/
|
|
36
|
+
function afterStartup({
|
|
37
|
+
url,
|
|
38
|
+
waitUntilReady,
|
|
39
|
+
}: Pick<EditorBackendOptions, "url" | "waitUntilReady">) {
|
|
40
|
+
let startup: Promise<void> | undefined;
|
|
41
|
+
let settled = false;
|
|
42
|
+
return async () => {
|
|
43
|
+
if (!settled) {
|
|
44
|
+
startup ??= waitUntilReady().finally(() => {
|
|
45
|
+
settled = true;
|
|
46
|
+
});
|
|
47
|
+
try {
|
|
48
|
+
await startup;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
throw new StreamStopped("Healthy connection never made", {
|
|
51
|
+
cause: error,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return url();
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Follow the notebook running in the WASM worker.
|
|
61
|
+
*
|
|
62
|
+
* The worker's backend exists once the worker has started, so the first
|
|
63
|
+
* connection creates it. Commands are only for a notebook its stream reported.
|
|
64
|
+
*/
|
|
65
|
+
function wasm(): NotebookBackend {
|
|
66
|
+
let backend: Promise<NotebookBackend> | undefined;
|
|
67
|
+
let started: NotebookBackend | undefined;
|
|
68
|
+
return {
|
|
69
|
+
transport: async (attempt, handlers) => {
|
|
70
|
+
backend ??= PyodideBridge.INSTANCE.createNotebookBackend();
|
|
71
|
+
try {
|
|
72
|
+
started = await backend;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw new StreamStopped("The WASM notebook did not start", {
|
|
75
|
+
cause: error,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return started.transport(attempt, handlers);
|
|
79
|
+
},
|
|
80
|
+
notebook: (id) => {
|
|
81
|
+
if (started === undefined) {
|
|
82
|
+
throw new Error("The WASM notebook has not started");
|
|
83
|
+
}
|
|
84
|
+
return started.notebook(id);
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Commands for a notebook the editor cannot change through its backend. */
|
|
90
|
+
function uneditable(): ReturnType<NotebookBackend["notebook"]> {
|
|
91
|
+
const unsupported: NotebookClient["save"] = async () => ({
|
|
92
|
+
ok: false,
|
|
93
|
+
status: 501,
|
|
94
|
+
problem: {
|
|
95
|
+
type: "tag:marimo.io,2026:host/unsupported-operation",
|
|
96
|
+
title: "Document mutations are unavailable on this connection",
|
|
97
|
+
status: 501,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
return { edit: () => unsupported(), save: unsupported };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Choose the backend for the editor's notebook. */
|
|
104
|
+
export function createEditorBackend(
|
|
105
|
+
options: EditorBackendOptions,
|
|
106
|
+
): NotebookBackend {
|
|
107
|
+
if (options.static) {
|
|
108
|
+
return { transport: idle, notebook: uneditable };
|
|
109
|
+
}
|
|
110
|
+
if (isWasm()) {
|
|
111
|
+
return wasm();
|
|
112
|
+
}
|
|
113
|
+
const url = afterStartup(options);
|
|
114
|
+
return {
|
|
115
|
+
transport:
|
|
116
|
+
options.transportType === "sse"
|
|
117
|
+
? sessionSse(url, options.headers)
|
|
118
|
+
: websocket(url),
|
|
119
|
+
notebook: (id) =>
|
|
120
|
+
new NotebookClient(options.notebookUrl(id), {
|
|
121
|
+
fetch: withHeaders(options.headers),
|
|
122
|
+
}),
|
|
123
|
+
checkHealth: options.checkHealth,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import type { ConnectionStatus as HostConnectionStatus } from "../
|
|
3
|
+
import type { ConnectionStatus as HostConnectionStatus } from "../notebook/replica";
|
|
4
4
|
import type { NotificationMessageData } from "../kernel/messages";
|
|
5
5
|
|
|
6
6
|
export {
|
|
7
7
|
ConnectionState,
|
|
8
8
|
ConnectionClosedReason as WebSocketClosedReason,
|
|
9
|
-
} from "../
|
|
9
|
+
} from "../notebook/replica";
|
|
10
10
|
|
|
11
11
|
export type ConnectionPhase =
|
|
12
12
|
| NotificationMessageData<"startup-progress">["phase"]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/** The editor session's replica, following its backend while mounted. */
|
|
4
|
+
|
|
5
|
+
import { useStore } from "jotai";
|
|
6
|
+
import { useEffect, useMemo } from "react";
|
|
7
|
+
import useEvent from "react-use-event-hook";
|
|
8
|
+
import type {
|
|
9
|
+
NotificationMessage,
|
|
10
|
+
NotificationPayload,
|
|
11
|
+
} from "../kernel/messages";
|
|
12
|
+
import {
|
|
13
|
+
createNotebookReplica,
|
|
14
|
+
type NotebookReplica,
|
|
15
|
+
} from "../notebook/replica";
|
|
16
|
+
import { activeNotebookReplicaAtom } from "./active-notebook";
|
|
17
|
+
import { createEditorBackend, type EditorBackendOptions } from "./backend";
|
|
18
|
+
|
|
19
|
+
type Options = EditorBackendOptions & {
|
|
20
|
+
onNotification: (notification: NotificationPayload) => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function useNotebookReplica(options: Options): NotebookReplica {
|
|
24
|
+
const store = useStore();
|
|
25
|
+
const { static: isStatic, transportType } = options;
|
|
26
|
+
const replica = useMemo(() => createNotebookReplica(store), [store]);
|
|
27
|
+
const url = useEvent(options.url);
|
|
28
|
+
const headers = useEvent(options.headers);
|
|
29
|
+
const notebookUrl = useEvent(options.notebookUrl);
|
|
30
|
+
const waitUntilReady = useEvent(options.waitUntilReady);
|
|
31
|
+
const checkHealth = useEvent(options.checkHealth);
|
|
32
|
+
const onNotification = useEvent(options.onNotification);
|
|
33
|
+
// Opens nothing until the replica follows it.
|
|
34
|
+
const backend = useMemo(
|
|
35
|
+
() =>
|
|
36
|
+
createEditorBackend({
|
|
37
|
+
static: isStatic,
|
|
38
|
+
transportType,
|
|
39
|
+
url,
|
|
40
|
+
headers,
|
|
41
|
+
notebookUrl,
|
|
42
|
+
waitUntilReady,
|
|
43
|
+
checkHealth,
|
|
44
|
+
}),
|
|
45
|
+
[
|
|
46
|
+
isStatic,
|
|
47
|
+
transportType,
|
|
48
|
+
url,
|
|
49
|
+
headers,
|
|
50
|
+
notebookUrl,
|
|
51
|
+
waitUntilReady,
|
|
52
|
+
checkHealth,
|
|
53
|
+
],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
store.set(activeNotebookReplicaAtom, replica);
|
|
58
|
+
const disconnect = replica.connect(backend, {
|
|
59
|
+
// The host validates the envelope, not the native notification contents.
|
|
60
|
+
onNotification: (notification) =>
|
|
61
|
+
onNotification({ data: notification as NotificationMessage }),
|
|
62
|
+
});
|
|
63
|
+
return () => {
|
|
64
|
+
disconnect();
|
|
65
|
+
if (store.get(activeNotebookReplicaAtom) === replica) {
|
|
66
|
+
store.set(activeNotebookReplicaAtom, null);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}, [replica, backend, store, onNotification]);
|
|
70
|
+
|
|
71
|
+
return replica;
|
|
72
|
+
}
|
|
@@ -2,18 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import { invalidatePackageData } from "@/core/packages/package-data";
|
|
4
4
|
import { useAtom, useAtomValue, useSetAtom, useStore } from "jotai";
|
|
5
|
-
import { useLayoutEffect } from "react";
|
|
5
|
+
import { useEffect, useLayoutEffect, useMemo } 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
|
-
import { applyTransactionChanges } from "@/core/cells/document-changes";
|
|
11
9
|
import { AUTOCOMPLETER } from "@/core/codemirror/completion/Autocompleter";
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from "@/core/kernel/messages";
|
|
16
|
-
import { useNotebookClient } from "@/core/websocket/useNotebookClient";
|
|
10
|
+
import { createDocumentModel } from "@/core/notebook/document";
|
|
11
|
+
import type { NotificationPayload } from "@/core/kernel/messages";
|
|
12
|
+
import { useNotebookReplica } from "@/core/session/useNotebookReplica";
|
|
17
13
|
import { renderHTML } from "@/plugins/core/RenderHTML";
|
|
18
14
|
import {
|
|
19
15
|
handleWidgetMessage,
|
|
@@ -66,7 +62,7 @@ import { type LayoutState, useLayoutActions } from "../layout/state";
|
|
|
66
62
|
import { kioskModeAtom } from "../mode";
|
|
67
63
|
import { connectionAtom, startupProgressAtom } from "../network/connection";
|
|
68
64
|
import type { RequestId } from "../network/DeferredRequestRegistry";
|
|
69
|
-
import {
|
|
65
|
+
import { useRuntimeManager } from "../runtime/config";
|
|
70
66
|
import { SECRETS_REGISTRY } from "../secrets/request-registry";
|
|
71
67
|
import { isStaticNotebook } from "../static/static-state";
|
|
72
68
|
import {
|
|
@@ -76,7 +72,7 @@ import {
|
|
|
76
72
|
import { useStorageActions } from "../storage/state";
|
|
77
73
|
import { useVariablesActions } from "../variables/state";
|
|
78
74
|
import type { VariableName } from "../variables/types";
|
|
79
|
-
import {
|
|
75
|
+
import { activeDocumentModelAtom } from "./active-notebook";
|
|
80
76
|
import { WebSocketClosedReason, ConnectionState } from "./types";
|
|
81
77
|
|
|
82
78
|
const SUPPORTS_LAZY_KERNELS = true;
|
|
@@ -102,17 +98,7 @@ export function useNotebookSession(opts: {
|
|
|
102
98
|
const { showBoundary } = useErrorBoundary();
|
|
103
99
|
|
|
104
100
|
const { handleCellMessage } = useCellActions();
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
const handleDocumentTransaction = (
|
|
108
|
-
transaction: NotificationMessageData<"notebook-document-transaction">["transaction"],
|
|
109
|
-
) => {
|
|
110
|
-
applyTransactionChanges(
|
|
111
|
-
transaction.changes,
|
|
112
|
-
actionsWithoutMiddleware,
|
|
113
|
-
() => getNotebook().cellIds.inOrderIds,
|
|
114
|
-
);
|
|
115
|
-
};
|
|
101
|
+
const { reconcileDocument } = useCellActions({ skipMiddleware: true });
|
|
116
102
|
const { addCellNotification } = useRunsActions();
|
|
117
103
|
const setKernelState = useSetAtom(kernelStateAtom);
|
|
118
104
|
const setInitialRunCompleted = useSetAtom(initialRunCompletedAtom);
|
|
@@ -136,11 +122,9 @@ export function useNotebookSession(opts: {
|
|
|
136
122
|
const setStableSessionId = useSetAtom(stableSessionIdAtom);
|
|
137
123
|
const setCapabilities = useSetAtom(capabilitiesAtom);
|
|
138
124
|
const runtimeManager = useRuntimeManager();
|
|
139
|
-
const { notebookId } = useAtomValue(runtimeConfigAtom);
|
|
140
125
|
const transportType = useAtomValue(connectionTransportTypeAtom);
|
|
141
126
|
const setCacheInfo = useSetAtom(cacheInfoAtom);
|
|
142
127
|
const setKernelStartupError = useSetAtom(kernelStartupErrorAtom);
|
|
143
|
-
const setAttachments = useSetAtom(attachmentsAtom);
|
|
144
128
|
const {
|
|
145
129
|
setNamespaces: setStorageNamespaces,
|
|
146
130
|
filterFromVariables: filterStorageFromVariables,
|
|
@@ -148,9 +132,6 @@ export function useNotebookSession(opts: {
|
|
|
148
132
|
|
|
149
133
|
const handleNotification = (msg: NotificationPayload) => {
|
|
150
134
|
switch (msg.data.op) {
|
|
151
|
-
case "attachments":
|
|
152
|
-
setAttachments(msg.data.attachments);
|
|
153
|
-
return;
|
|
154
135
|
case "reload":
|
|
155
136
|
reloadSafe();
|
|
156
137
|
return;
|
|
@@ -185,7 +166,6 @@ export function useNotebookSession(opts: {
|
|
|
185
166
|
existingCells,
|
|
186
167
|
});
|
|
187
168
|
if (!msg.data.resumed) {
|
|
188
|
-
setAttachments([]);
|
|
189
169
|
// A freshly started kernel may expose a different environment
|
|
190
170
|
// (e.g. new env vars); re-run discovery instead of reusing stale
|
|
191
171
|
// suggestions from the previous kernel instance.
|
|
@@ -271,13 +251,18 @@ export function useNotebookSession(opts: {
|
|
|
271
251
|
filterStorageFromVariables(variableNames);
|
|
272
252
|
return;
|
|
273
253
|
case "variable-values":
|
|
274
|
-
setMetadata(
|
|
275
|
-
msg.data.
|
|
254
|
+
setMetadata([
|
|
255
|
+
...(msg.data.removed ?? []).map((v) => ({
|
|
256
|
+
name: v.name as VariableName,
|
|
257
|
+
dataType: undefined,
|
|
258
|
+
value: undefined,
|
|
259
|
+
})),
|
|
260
|
+
...msg.data.variables.map((v) => ({
|
|
276
261
|
name: v.name as VariableName,
|
|
277
262
|
dataType: v.datatype,
|
|
278
263
|
value: v.value,
|
|
279
264
|
})),
|
|
280
|
-
);
|
|
265
|
+
]);
|
|
281
266
|
return;
|
|
282
267
|
case "alert":
|
|
283
268
|
toast({
|
|
@@ -398,7 +383,7 @@ export function useNotebookSession(opts: {
|
|
|
398
383
|
);
|
|
399
384
|
return;
|
|
400
385
|
case "notebook-document-transaction":
|
|
401
|
-
|
|
386
|
+
// Edits reach the notebook through the host document.
|
|
402
387
|
return;
|
|
403
388
|
case "consumer-capabilities":
|
|
404
389
|
setKioskMode(!msg.data.consumer_capabilities.edit);
|
|
@@ -416,25 +401,23 @@ export function useNotebookSession(opts: {
|
|
|
416
401
|
variant: "danger",
|
|
417
402
|
});
|
|
418
403
|
};
|
|
419
|
-
const
|
|
420
|
-
sessionId,
|
|
404
|
+
const replica = useNotebookReplica({
|
|
421
405
|
static: isStaticNotebook(),
|
|
422
406
|
transportType,
|
|
423
|
-
messageFormat:
|
|
424
|
-
notebookId && transportType === "websocket" ? "host" : "session",
|
|
425
407
|
url: () =>
|
|
426
408
|
transportType === "sse"
|
|
427
409
|
? runtimeManager.getSseURL(sessionId).toString()
|
|
428
410
|
: runtimeManager.getWsURL(sessionId).toString(),
|
|
429
411
|
// SSE uses headers for auth. WebSockets carry the token in the URL.
|
|
430
412
|
headers: () => runtimeManager.headers(),
|
|
413
|
+
notebookUrl: (notebookId) => runtimeManager.getNotebookURL(notebookId),
|
|
414
|
+
// Only the host's transports read the URL, so static and WASM never wait.
|
|
431
415
|
waitUntilReady: async () => {
|
|
432
|
-
if (!
|
|
416
|
+
if (!runtimeManager.isSameOrigin) {
|
|
433
417
|
await runtimeManager.waitForHealthy();
|
|
434
418
|
}
|
|
435
419
|
},
|
|
436
420
|
checkHealth: () => runtimeManager.reconcileFromHealth(),
|
|
437
|
-
onMessageError,
|
|
438
421
|
onNotification: (notification) => {
|
|
439
422
|
try {
|
|
440
423
|
handleNotification(notification);
|
|
@@ -445,14 +428,45 @@ export function useNotebookSession(opts: {
|
|
|
445
428
|
},
|
|
446
429
|
});
|
|
447
430
|
|
|
431
|
+
const documentModel = useMemo(
|
|
432
|
+
() =>
|
|
433
|
+
createDocumentModel({
|
|
434
|
+
store,
|
|
435
|
+
documentAtom: replica.documentAtom,
|
|
436
|
+
backendAtom: replica.backendAtom,
|
|
437
|
+
}),
|
|
438
|
+
[store, replica],
|
|
439
|
+
);
|
|
440
|
+
// Edits outlive reconnects and end with the session. The notebook's cells
|
|
441
|
+
// follow the document for as long as the session is mounted.
|
|
442
|
+
useEffect(() => {
|
|
443
|
+
const controller = new AbortController();
|
|
444
|
+
documentModel.start({ signal: controller.signal });
|
|
445
|
+
store.set(activeDocumentModelAtom, documentModel);
|
|
446
|
+
const follow = () => {
|
|
447
|
+
const document = store.get(documentModel.documentAtom);
|
|
448
|
+
if (document !== null) {
|
|
449
|
+
reconcileDocument(document);
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
const unsubscribe = store.sub(documentModel.documentAtom, follow);
|
|
453
|
+
follow();
|
|
454
|
+
return () => {
|
|
455
|
+
unsubscribe();
|
|
456
|
+
controller.abort();
|
|
457
|
+
if (store.get(activeDocumentModelAtom) === documentModel) {
|
|
458
|
+
store.set(activeDocumentModelAtom, null);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
}, [documentModel, store, reconcileDocument]);
|
|
462
|
+
|
|
448
463
|
// Subscribe before passive connection setup: static/WASM transports can emit synchronously.
|
|
449
464
|
useLayoutEffect(() => {
|
|
450
465
|
const updateConnection = () => {
|
|
451
|
-
const status = store.get(
|
|
466
|
+
const status = store.get(replica.connectionAtom);
|
|
452
467
|
if (status.state === ConnectionState.NOT_STARTED) {
|
|
453
468
|
return;
|
|
454
469
|
}
|
|
455
|
-
setAttachments([]);
|
|
456
470
|
if (status.state === ConnectionState.OPEN) {
|
|
457
471
|
updateStartupProgress(null);
|
|
458
472
|
return; // kernel-ready, not the socket, establishes session readiness.
|
|
@@ -480,17 +494,16 @@ export function useNotebookSession(opts: {
|
|
|
480
494
|
return status;
|
|
481
495
|
});
|
|
482
496
|
};
|
|
483
|
-
const unsubscribe = store.sub(
|
|
497
|
+
const unsubscribe = store.sub(replica.connectionAtom, updateConnection);
|
|
484
498
|
updateConnection();
|
|
485
499
|
return unsubscribe;
|
|
486
500
|
}, [
|
|
487
|
-
|
|
501
|
+
replica,
|
|
488
502
|
store,
|
|
489
503
|
setConnection,
|
|
490
|
-
setAttachments,
|
|
491
504
|
setKernelStartupError,
|
|
492
505
|
updateStartupProgress,
|
|
493
506
|
]);
|
|
494
507
|
|
|
495
|
-
return {
|
|
508
|
+
return { replica, documentModel, connection, reconnect: replica.reconnect };
|
|
496
509
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { act, renderHook } from "@testing-library/react";
|
|
4
|
+
import { atom, Provider } from "jotai";
|
|
5
|
+
import type React from "react";
|
|
6
|
+
import { describe, expect, it, vi } from "vitest";
|
|
7
|
+
import { store, useJotaiEffect, waitFor } from "../jotai";
|
|
8
|
+
|
|
9
|
+
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
10
|
+
<Provider store={store}>{children}</Provider>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
describe("waitFor function", () => {
|
|
14
|
+
it("should resolve when the atom satisfies the predicate", async () => {
|
|
15
|
+
const testAtom = atom(0);
|
|
16
|
+
store.set(testAtom, 10);
|
|
17
|
+
|
|
18
|
+
const result = await waitFor(testAtom, (value) => value === 10);
|
|
19
|
+
expect(result).toBe(10);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should resolve when the atom changes to satisfy the predicate", async () => {
|
|
23
|
+
const testAtom = atom(0);
|
|
24
|
+
|
|
25
|
+
setTimeout(() => store.set(testAtom, 15), 5);
|
|
26
|
+
setTimeout(() => store.set(testAtom, 20), 10);
|
|
27
|
+
|
|
28
|
+
const result = await waitFor(testAtom, (value) => value === 20);
|
|
29
|
+
expect(result).toBe(20);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("useJotaiEffect", () => {
|
|
34
|
+
it("calls the effect with the new and previous values", () => {
|
|
35
|
+
const testAtom = atom(0);
|
|
36
|
+
const effect = vi.fn();
|
|
37
|
+
renderHook(() => useJotaiEffect(testAtom, effect), { wrapper });
|
|
38
|
+
|
|
39
|
+
act(() => store.set(testAtom, 1));
|
|
40
|
+
act(() => store.set(testAtom, 2));
|
|
41
|
+
|
|
42
|
+
expect(effect.mock.calls).toEqual([
|
|
43
|
+
[1, 0],
|
|
44
|
+
[2, 1],
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("calls only the latest callback after a rerender", () => {
|
|
49
|
+
const testAtom = atom(0);
|
|
50
|
+
const first = vi.fn();
|
|
51
|
+
const second = vi.fn();
|
|
52
|
+
const { rerender } = renderHook(
|
|
53
|
+
({ effect }) => useJotaiEffect(testAtom, effect),
|
|
54
|
+
{ initialProps: { effect: first }, wrapper },
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
rerender({ effect: second });
|
|
58
|
+
act(() => store.set(testAtom, 1));
|
|
59
|
+
|
|
60
|
+
expect(first).not.toHaveBeenCalled();
|
|
61
|
+
expect(second).toHaveBeenCalledTimes(1);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("subscribes once per rerender with an inline callback", () => {
|
|
65
|
+
const testAtom = atom(0);
|
|
66
|
+
const calls: number[] = [];
|
|
67
|
+
const { rerender } = renderHook(
|
|
68
|
+
() => useJotaiEffect(testAtom, (value) => calls.push(value)),
|
|
69
|
+
{ wrapper },
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
rerender();
|
|
73
|
+
rerender();
|
|
74
|
+
act(() => store.set(testAtom, 1));
|
|
75
|
+
|
|
76
|
+
expect(calls).toEqual([1]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("stops listening to the previous atom when the atom changes", () => {
|
|
80
|
+
const firstAtom = atom(0);
|
|
81
|
+
const secondAtom = atom(0);
|
|
82
|
+
const effect = vi.fn();
|
|
83
|
+
const { rerender } = renderHook(
|
|
84
|
+
({ target }) => useJotaiEffect(target, effect),
|
|
85
|
+
{ initialProps: { target: firstAtom }, wrapper },
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
rerender({ target: secondAtom });
|
|
89
|
+
act(() => store.set(firstAtom, 1));
|
|
90
|
+
expect(effect).not.toHaveBeenCalled();
|
|
91
|
+
|
|
92
|
+
act(() => store.set(secondAtom, 1));
|
|
93
|
+
expect(effect).toHaveBeenCalledTimes(1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("unsubscribes on unmount", () => {
|
|
97
|
+
const testAtom = atom(0);
|
|
98
|
+
const effect = vi.fn();
|
|
99
|
+
const { unmount } = renderHook(() => useJotaiEffect(testAtom, effect), {
|
|
100
|
+
wrapper,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
unmount();
|
|
104
|
+
act(() => store.set(testAtom, 1));
|
|
105
|
+
|
|
106
|
+
expect(effect).not.toHaveBeenCalled();
|
|
107
|
+
});
|
|
108
|
+
});
|
package/src/core/state/jotai.ts
CHANGED
|
@@ -42,7 +42,7 @@ export function useJotaiEffect<T>(
|
|
|
42
42
|
const store = useStore();
|
|
43
43
|
useEffect(() => {
|
|
44
44
|
let prevValue = store.get(atom);
|
|
45
|
-
store.sub(atom, () => {
|
|
45
|
+
return store.sub(atom, () => {
|
|
46
46
|
const value = store.get(atom);
|
|
47
47
|
effect(value, prevValue);
|
|
48
48
|
prevValue = value;
|
|
@@ -654,19 +654,47 @@ describe("error handling and edge cases", () => {
|
|
|
654
654
|
unpatch();
|
|
655
655
|
});
|
|
656
656
|
|
|
657
|
-
it("
|
|
658
|
-
const
|
|
659
|
-
const
|
|
657
|
+
it("forwards relative URLs and options to the original fetch", async () => {
|
|
658
|
+
const response = new Response("relative content");
|
|
659
|
+
const originalFetch = vi.spyOn(window, "fetch").mockResolvedValue(response);
|
|
660
|
+
const unpatch = patchFetch({});
|
|
661
|
+
const options = { headers: { Accept: "text/plain" } };
|
|
662
|
+
|
|
663
|
+
try {
|
|
664
|
+
expect(await window.fetch("relative-file.txt", options)).toBe(response);
|
|
665
|
+
expect(originalFetch).toHaveBeenCalledExactlyOnceWith(
|
|
666
|
+
"relative-file.txt",
|
|
667
|
+
options,
|
|
668
|
+
);
|
|
669
|
+
} finally {
|
|
670
|
+
unpatch();
|
|
671
|
+
originalFetch.mockRestore();
|
|
672
|
+
}
|
|
673
|
+
});
|
|
660
674
|
|
|
661
|
-
|
|
675
|
+
it("preserves the original fetch rejection for malformed URLs", async () => {
|
|
676
|
+
const error = new TypeError("Invalid URL");
|
|
677
|
+
const originalFetch = vi.spyOn(window, "fetch").mockRejectedValue(error);
|
|
662
678
|
const loggerSpy = vi
|
|
663
679
|
.spyOn(console, "error")
|
|
664
680
|
.mockImplementation(Functions.NOOP);
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
681
|
+
const unpatch = patchFetch({});
|
|
682
|
+
const options = { headers: { Accept: "text/plain" } };
|
|
683
|
+
|
|
684
|
+
try {
|
|
685
|
+
await expect(window.fetch("http://[", options)).rejects.toBe(error);
|
|
686
|
+
expect(originalFetch).toHaveBeenCalledExactlyOnceWith(
|
|
687
|
+
"http://[",
|
|
688
|
+
options,
|
|
689
|
+
);
|
|
690
|
+
expect(loggerSpy).toHaveBeenCalledWith(
|
|
691
|
+
"Error parsing URL",
|
|
692
|
+
expect.any(TypeError),
|
|
693
|
+
);
|
|
694
|
+
} finally {
|
|
695
|
+
unpatch();
|
|
696
|
+
originalFetch.mockRestore();
|
|
697
|
+
loggerSpy.mockRestore();
|
|
698
|
+
}
|
|
671
699
|
});
|
|
672
700
|
});
|
package/src/core/static/files.ts
CHANGED
|
@@ -125,6 +125,8 @@ function withoutLeadingDot(path: string): string {
|
|
|
125
125
|
/**
|
|
126
126
|
* Resolve a URL to a blob URL if it's a virtual file, for use with dynamic import().
|
|
127
127
|
* Unlike fetch, import() can't be patched, so we need to convert data URLs to blob URLs.
|
|
128
|
+
* The caller owns a returned URL that differs from the input and must revoke it
|
|
129
|
+
* once no module consumers or their cleanup callbacks need it.
|
|
128
130
|
*
|
|
129
131
|
* @returns The original URL if not a virtual file, or a blob URL if it is
|
|
130
132
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
4
|
import { Logger } from "@/utils/Logger";
|
|
5
|
-
import { WebSocketClosedReason, ConnectionState } from "
|
|
5
|
+
import { WebSocketClosedReason, ConnectionState } from "../../session/types";
|
|
6
6
|
import { classifyCloseEvent } from "../connection-state";
|
|
7
7
|
|
|
8
8
|
function classify(reason: string | undefined) {
|
|
@@ -64,16 +64,4 @@ describe("classifyCloseEvent", () => {
|
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
66
|
});
|
|
67
|
-
|
|
68
|
-
describe("transport exhaustion", () => {
|
|
69
|
-
it("MARIMO_TRANSPORT_EXHAUSTED → gave-up with KERNEL_DISCONNECTED", () => {
|
|
70
|
-
const decision = classify("MARIMO_TRANSPORT_EXHAUSTED");
|
|
71
|
-
expect(decision.kind).toBe("gave-up");
|
|
72
|
-
expect(decision.status).toEqual({
|
|
73
|
-
state: ConnectionState.CLOSED,
|
|
74
|
-
code: WebSocketClosedReason.KERNEL_DISCONNECTED,
|
|
75
|
-
reason: "kernel not found",
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
67
|
});
|