@marimo-team/islands 0.25.1-dev4 → 0.25.1-dev41
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-KVhjDMSe.js → ConnectedDataExplorerComponent-CwMXfDQR.js} +3 -3
- package/dist/{ErrorBoundary-pV36lYEK.js → ErrorBoundary-vRGgXmad.js} +1 -1
- package/dist/{ImageComparisonComponent-Dlm5aWnH.js → ImageComparisonComponent-Cpfz30OZ.js} +51 -52
- package/dist/{abnfDiagram-VCTEODGH-CpN0FRda.js → abnfDiagram-VCTEODGH-AFhOQTt8.js} +3 -3
- package/dist/{architectureDiagram-5GKGNRK7-DDyj5SRB.js → architectureDiagram-5GKGNRK7-BngksAAQ.js} +5 -5
- package/dist/{blockDiagram-I7D4REHJ-BbXEXJRu.js → blockDiagram-I7D4REHJ-CvBmppdy.js} +9 -9
- package/dist/{c4Diagram-7LVT6UL2-DcLsbQmR.js → c4Diagram-7LVT6UL2-EceL4_jH.js} +7 -7
- package/dist/{cells-B5-y5EY3.js → cells-Cv93oav_.js} +3 -3
- package/dist/{channel-B5XnTarr.js → channel-z7HZXHwv.js} +1 -1
- package/dist/{chat-ui-HJZsTl7o.js → chat-ui-DLSF8a04.js} +4 -4
- package/dist/{chunk-4HAMMTFA-D2qv8E0X.js → chunk-4HAMMTFA-BPTTOOC5.js} +5 -5
- package/dist/{chunk-75Z2AOVW-5_HsvXwr.js → chunk-75Z2AOVW-C4AOleKf.js} +1 -1
- package/dist/{chunk-DU6HZSFF-BUgAHvnM.js → chunk-DU6HZSFF-1TwWR6gh.js} +1 -1
- package/dist/{chunk-F27PBJKO-DHmYWZXr.js → chunk-F27PBJKO-DMI1PBe4.js} +1 -1
- package/dist/{chunk-GMAD6QVW-Czynz8aA.js → chunk-GMAD6QVW-vStrMz2P.js} +3 -3
- package/dist/{chunk-GVQU2GXP-yQEdulRS.js → chunk-GVQU2GXP-2HwWgs4u.js} +2 -2
- package/dist/{chunk-IMKFNOWR-BfwTebD0.js → chunk-IMKFNOWR-UOVZuZVn.js} +5 -5
- package/dist/{chunk-L3NEJ4N5-DwRWnDDK.js → chunk-L3NEJ4N5-RhC7i0I2.js} +5 -5
- package/dist/{chunk-OSK3NFVY-BBKU_Ign.js → chunk-OSK3NFVY-CZV09N-U.js} +6 -6
- package/dist/{chunk-P2QGCYS3-C0iGU29N.js → chunk-P2QGCYS3-0TAWai9C.js} +1 -1
- package/dist/{chunk-POPQ4Y6H-BCbS3Ozc.js → chunk-POPQ4Y6H-Cc3_56GX.js} +1 -1
- package/dist/{chunk-PWAF6VOD-CTFCL0jI.js → chunk-PWAF6VOD-lNxB9k8z.js} +1 -1
- package/dist/{chunk-SHT3W25Y-BspJEJ5H.js → chunk-SHT3W25Y-CrEPyWy1.js} +7 -7
- package/dist/{chunk-SVP7TREG-DX3rFNTe.js → chunk-SVP7TREG-uIyyySp6.js} +2 -2
- package/dist/{chunk-TICWLB2K-fVF7Uujg.js → chunk-TICWLB2K-DdXM3eg6.js} +5 -5
- package/dist/classDiagram-ZZMXUADV-BiATbBE4.js +30 -0
- package/dist/classDiagram-v2-VYDZK3BY-JnH97GYw.js +30 -0
- package/dist/{code-visibility-CgZS2a3W.js → code-visibility-5wuTKcAD.js} +2 -2
- package/dist/{common-DnMDPPaW.js → common-PL37o7BX.js} +13 -13
- package/dist/{cynefinDiagram-5FMLGOSQ-CFQrqzSX.js → cynefinDiagram-5FMLGOSQ-zknaAsGy.js} +3 -3
- package/dist/{dagre-GXQ25YYZ-xHNAUTyG.js → dagre-GXQ25YYZ-BhMOoMGJ.js} +10 -10
- package/dist/{data-grid-overlay-editor-hHYgD_Sm.js → data-grid-overlay-editor-CWqzAL_P.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-Bt1rPbAx.js → diagram-S7CK7UJ4-V5-Z-EDY.js} +4 -4
- package/dist/{diagram-UQ7AKVKN-D1sq5FoZ.js → diagram-UQ7AKVKN-qw8AjDal.js} +3 -3
- package/dist/{diagram-VSXAHHWV-BKpmG23x.js → diagram-VSXAHHWV-CR7BJygZ.js} +2 -2
- package/dist/{diagram-VX7I27RA-BMwGFVD6.js → diagram-VX7I27RA-DfuqFb1E.js} +5 -5
- package/dist/{diagram-Z3DM3KII--KLDR9Pl.js → diagram-Z3DM3KII-DpWwlYZB.js} +3 -3
- package/dist/{dist-BaHjhkpZ.js → dist-CcyKTBTF.js} +1 -1
- package/dist/{ebnfDiagram-PWID7BFC-WnG4cvSQ.js → ebnfDiagram-PWID7BFC-C20OnNMZ.js} +3 -3
- package/dist/{erDiagram-RLTQ6QDP-DhzKiYHc.js → erDiagram-RLTQ6QDP-DBXLWO9v.js} +12 -12
- package/dist/{error-entries-Bkbpx0T0.js → error-entries-DLl0XqoW.js} +1 -1
- package/dist/{feedback-button-CvKssdG_.js → feedback-button-BwKWcLqp.js} +4 -4
- package/dist/flowDiagram-HODETNUW-DL52nMkt.js +16 -0
- package/dist/{formats-C8rLgZT9.js → formats-Cl_67ghI.js} +1 -1
- package/dist/{ganttDiagram-EL5Y4UJY-DbzEBLbl.js → ganttDiagram-EL5Y4UJY-cRRaDt88.js} +2 -2
- package/dist/{gitGraphDiagram-WWUBYQGX-BMf0iKi1.js → gitGraphDiagram-WWUBYQGX-Pox5k9-Y.js} +2 -2
- package/dist/{glide-data-editor-Ohetlgag.js → glide-data-editor-DdDhX9sS.js} +3 -3
- package/dist/{globals-CPNNcOJI.js → globals-D07l3kcu.js} +1 -1
- package/dist/{html-to-image-BAgJOlSW.js → html-to-image-CGVgroef.js} +2 -2
- package/dist/{infoDiagram-27XIBGKW-BagtCX_5.js → infoDiagram-27XIBGKW-ntpxMfrq.js} +2 -2
- package/dist/{ishikawaDiagram-5VMMS53U-DByDehYy.js → ishikawaDiagram-5VMMS53U-BllIlHE5.js} +3 -3
- package/dist/{journeyDiagram-3NMN7TZE-D2LcKDUO.js → journeyDiagram-3NMN7TZE-EPJDPlt0.js} +2 -2
- package/dist/{kanban-definition-UXKFOSKX-BxwkbBI_.js → kanban-definition-UXKFOSKX-GYtNMjtS.js} +9 -9
- package/dist/{katex-C6D4cX_N.js → katex-BNCmKUol.js} +983 -1120
- package/dist/{loader-D2W4oM-3.js → loader-DEZSzvma.js} +253 -248
- package/dist/main.js +72 -69
- package/dist/{mermaid-DPNuhemn.js → mermaid-Dnzoxjt1.js} +49 -49
- package/dist/{mhchem-DWCF06w9.js → mhchem-BEc7A1or.js} +1 -1
- package/dist/{mindmap-definition-YA3MSWOX-Cgvojw3y.js → mindmap-definition-YA3MSWOX-Cth76xgH.js} +11 -11
- package/dist/{number-overlay-editor-CHWg5gGO.js → number-overlay-editor-DerHT6Rq.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-DahHhYyN.js → pegDiagram-XKGWAZYB-Cu4uOXii.js} +3 -3
- package/dist/{pieDiagram-E7YTZNPT-CCtX0oCK.js → pieDiagram-E7YTZNPT-Bh3eDa9s.js} +3 -3
- package/dist/{process-output-zDX4jbhd.js → process-output-CGfg-JmZ.js} +2 -2
- package/dist/{quadrantDiagram-AXDQQJYC-DftINo6g.js → quadrantDiagram-AXDQQJYC--kqzFC3w.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-Du5NsPUb.js → railroadDiagram-O6MQD6OU-DG-M6Rnc.js} +3 -3
- package/dist/{react-vega-5AcMW8gG.js → react-vega-ewj7q_U5.js} +1 -1
- package/dist/{requirementDiagram-BXWQKSXE-0pmmxL6_.js → requirementDiagram-BXWQKSXE-DCwsThh0.js} +11 -11
- package/dist/{reveal-component-BujcZI2B.js → reveal-component-sfZwts_E.js} +3 -3
- package/dist/{sankeyDiagram-P5KCCOFB-CDKR5TJH.js → sankeyDiagram-P5KCCOFB-BdzhT0BM.js} +1 -1
- package/dist/{sequenceDiagram-WJ2MYXX4-DB-ChWwU.js → sequenceDiagram-WJ2MYXX4-CbXB1_PU.js} +4 -4
- package/dist/{stateDiagram-D77RDMKH-orN6AwMx.js → stateDiagram-D77RDMKH-A-Zf3Sb2.js} +13 -13
- package/dist/stateDiagram-v2-MP3YSRHH-D9YMnUGx.js +30 -0
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-BkSDcqDT.js → swimlanes-42K2YHIH-B_pMf0J4.js} +10 -10
- package/dist/swimlanesDiagram-VR7AAH4N-Ctgy-yGP.js +30 -0
- package/dist/{timeline-definition-24CTP7MA-BuqJUIRZ.js → timeline-definition-24CTP7MA-DTXFjTTM.js} +3 -3
- package/dist/{vega-component-CNf5gcrm.js → vega-component-tFOzgarw.js} +4 -4
- package/dist/{vega-embed-container-CbnTccZi.js → vega-embed-container-sSR5hAsy.js} +2 -2
- package/dist/{vega-loader.browser-D95WfUcj.js → vega-loader.browser-DNST6uVf.js} +2 -2
- package/dist/{vennDiagram-4TSXK5OY-CTUlJ2PK.js → vennDiagram-4TSXK5OY-BPHjZo0R.js} +3 -3
- package/dist/{wardleyDiagram-VM6X3IG4-3evLUb0M.js → wardleyDiagram-VM6X3IG4-6fTLYBuD.js} +3 -3
- package/dist/{xychartDiagram-S5SC5T6Z-DL2E3K8-.js → xychartDiagram-S5SC5T6Z-BYjwjrzE.js} +5 -5
- package/package.json +1 -2
- package/src/__mocks__/common.ts +3 -3
- package/src/__tests__/CellStatus.test.tsx +1 -1
- package/src/__tests__/setup.ts +7 -7
- package/src/components/app-config/ai-config.tsx +2 -0
- package/src/components/app-config/optional-features.tsx +1 -1
- package/src/components/chat/chat-panel.tsx +19 -8
- package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +286 -0
- package/src/components/data-table/charts/charts.tsx +26 -13
- package/src/components/data-table/column-summary/chart-spec-model.tsx +4 -4
- package/src/components/dependency-graph/elements.ts +2 -2
- package/src/components/editor/__tests__/package-alert.test.tsx +140 -0
- package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +107 -0
- package/src/components/editor/actions/pair-with-agent.tsx +119 -0
- package/src/components/editor/actions/useNotebookActions.tsx +2 -1
- package/src/components/editor/ai/add-cell-with-ai.tsx +2 -0
- package/src/components/editor/ai/staged-cell-submission.ts +2 -2
- package/src/components/editor/ai/transport/chat-transport.tsx +1 -1
- package/src/components/editor/chrome/components/contribute-snippet-button.tsx +6 -1
- package/src/components/editor/connections/database/as-code.ts +44 -44
- package/src/components/editor/connections/storage/as-code.ts +3 -3
- package/src/components/editor/file-tree/requesting-tree.tsx +16 -13
- package/src/components/editor/navigation/clipboard.ts +2 -2
- package/src/components/editor/notebook-cell.tsx +2 -2
- package/src/components/editor/output/MarimoTracebackOutput.tsx +1 -1
- package/src/components/editor/output/VideoOutput.tsx +1 -1
- package/src/components/editor/output/ansi-reduce.ts +21 -17
- package/src/components/editor/output/console/ConsoleOutput.tsx +3 -3
- package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +59 -0
- package/src/components/editor/output/console/text-rendering.tsx +3 -1
- package/src/components/editor/package-alert.tsx +20 -7
- package/src/components/home/components.tsx +1 -0
- package/src/components/pages/run-page.tsx +1 -0
- package/src/components/slides/__tests__/minimap-actions.test.tsx +7 -7
- package/src/components/slides/__tests__/minimap.test.ts +12 -12
- package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +8 -8
- package/src/components/static-html/__tests__/share-modal.test.tsx +154 -0
- package/src/components/static-html/__tests__/stage-for-publish.test.ts +111 -0
- package/src/components/static-html/share-modal.tsx +114 -144
- package/src/components/static-html/stage-for-publish.ts +56 -0
- package/src/components/static-html/static-banner.tsx +2 -2
- package/src/components/ui/links.tsx +6 -1
- package/src/components/ui/typography.tsx +16 -8
- package/src/core/ai/context/__tests__/registry.test.ts +31 -28
- package/src/core/ai/context/providers/cell-output.ts +8 -8
- package/src/core/ai/context/providers/datasource.ts +10 -7
- package/src/core/ai/context/providers/error.ts +7 -7
- package/src/core/ai/context/providers/file.ts +8 -8
- package/src/core/ai/context/providers/tables.ts +7 -7
- package/src/core/ai/context/providers/variable.ts +7 -7
- package/src/core/ai/context/registry.ts +18 -18
- package/src/core/ai/ids/ids.ts +5 -5
- package/src/core/ai/model-registry.ts +12 -9
- package/src/core/ai/staged-cells.ts +6 -6
- package/src/core/ai/tools/base.ts +6 -6
- package/src/core/ai/tools/edit-notebook-tool.ts +6 -6
- package/src/core/ai/tools/registry.ts +4 -4
- package/src/core/ai/tools/run-cells-tool.ts +7 -7
- package/src/core/ai/tools/sample-tool.ts +6 -6
- package/src/core/cells/__tests__/cells.test.ts +27 -0
- package/src/core/cells/cells.ts +2 -1
- package/src/core/codemirror/cells/debugger-decorations.ts +13 -7
- package/src/core/codemirror/cells/line-timing-decorations.ts +9 -9
- package/src/core/codemirror/cells/traceback-decorations.ts +7 -4
- package/src/core/codemirror/code-lens/extension.ts +13 -13
- package/src/core/codemirror/copilot/__tests__/language-server.test.ts +6 -6
- package/src/core/codemirror/copilot/__tests__/transport.test.ts +12 -12
- package/src/core/codemirror/copilot/language-server.ts +13 -13
- package/src/core/codemirror/copilot/transport.ts +1 -1
- package/src/core/codemirror/find-replace/search-highlight.ts +5 -5
- package/src/core/codemirror/go-to-definition/underline.ts +3 -3
- package/src/core/codemirror/keymaps/vim.ts +3 -3
- package/src/core/codemirror/language/languages/markdown.ts +8 -8
- package/src/core/codemirror/language/languages/python.ts +7 -7
- package/src/core/codemirror/language/languages/sql/completion-builder.ts +5 -5
- package/src/core/codemirror/language/languages/sql/completion-store.ts +4 -4
- package/src/core/codemirror/language/languages/sql/sql.ts +21 -21
- package/src/core/codemirror/lsp/__tests__/transports.test.ts +10 -10
- package/src/core/codemirror/lsp/federated-lsp.ts +29 -27
- package/src/core/codemirror/lsp/notebook-lsp.ts +21 -21
- package/src/core/codemirror/misc/__tests__/dnd.test.ts +10 -10
- package/src/core/codemirror/misc/__tests__/paste.test.ts +7 -4
- package/src/core/codemirror/reactive-references/extension.ts +3 -3
- package/src/core/codemirror/rtc/loro/awareness.ts +12 -12
- package/src/core/codemirror/rtc/loro/sync.ts +5 -5
- package/src/core/codemirror/vim/cursor-visibility.ts +3 -3
- package/src/core/dom/ui-element.ts +5 -5
- package/src/core/dom/uiregistry.ts +10 -10
- package/src/core/hotkeys/hotkeys.ts +9 -9
- package/src/core/islands/__tests__/bridge.test.ts +4 -4
- package/src/core/islands/__tests__/test-utils.tsx +4 -4
- package/src/core/islands/bridge.ts +85 -82
- package/src/core/islands/components/web-components.tsx +7 -7
- package/src/core/islands/worker/__tests__/controller.test.ts +1 -1
- package/src/core/islands/worker/controller.ts +6 -3
- package/src/core/islands/worker-factory.ts +2 -2
- package/src/core/kernel/RuntimeState.ts +4 -4
- package/src/core/lsp/__tests__/transport.test.ts +19 -19
- package/src/core/lsp/transport.ts +5 -5
- package/src/core/network/CachingRequestRegistry.ts +2 -2
- package/src/core/network/DeferredRequestRegistry.ts +4 -4
- package/src/core/network/__tests__/export-asset-url.test.ts +45 -0
- package/src/core/network/export-asset-url.ts +19 -0
- package/src/core/network/requests-network.ts +2 -7
- package/src/core/runtime/runtime.ts +25 -20
- package/src/core/static/virtual-file-tracker.ts +5 -5
- package/src/core/wasm/__tests__/bridge.test.ts +2 -2
- package/src/core/wasm/bridge.ts +132 -133
- package/src/core/wasm/router.ts +5 -5
- package/src/core/wasm/store.ts +4 -4
- package/src/core/wasm/worker/bootstrap.ts +8 -5
- package/src/core/wasm/worker/message-buffer.ts +3 -3
- package/src/core/websocket/transports/__tests__/ws.test.ts +8 -8
- package/src/core/websocket/transports/basic.ts +15 -8
- package/src/core/websocket/transports/sse.ts +7 -7
- package/src/core/websocket/transports/transport.ts +3 -3
- package/src/core/websocket/transports/ws.ts +9 -7
- package/src/css/globals.css +18 -21
- package/src/css/katex.min.css +260 -241
- package/src/hooks/__tests__/useOverflowDetection.test.tsx +5 -5
- package/src/hooks/__tests__/useResizeObserver.test.tsx +12 -12
- package/src/plugins/core/registerReactComponent.tsx +5 -5
- package/src/plugins/core/sidebar-element.tsx +2 -2
- package/src/plugins/impl/ButtonPlugin.tsx +3 -3
- package/src/plugins/impl/CheckboxPlugin.tsx +3 -3
- package/src/plugins/impl/CodeEditorPlugin.tsx +3 -3
- package/src/plugins/impl/DatePickerPlugin.tsx +3 -3
- package/src/plugins/impl/DateRangePlugin.tsx +3 -3
- package/src/plugins/impl/DateTimePickerPlugin.tsx +3 -3
- package/src/plugins/impl/DictPlugin.tsx +3 -3
- package/src/plugins/impl/DropdownPlugin.tsx +3 -3
- package/src/plugins/impl/FileUploadPlugin.tsx +3 -3
- package/src/plugins/impl/MatrixPlugin.tsx +4 -4
- package/src/plugins/impl/MicrophonePlugin.tsx +3 -3
- package/src/plugins/impl/MultiselectPlugin.tsx +3 -3
- package/src/plugins/impl/NumberPlugin.tsx +3 -3
- package/src/plugins/impl/RadioPlugin.tsx +9 -5
- package/src/plugins/impl/RangeSliderPlugin.tsx +3 -3
- package/src/plugins/impl/RefreshPlugin.tsx +3 -3
- package/src/plugins/impl/SliderPlugin.tsx +3 -3
- package/src/plugins/impl/SwitchPlugin.tsx +3 -3
- package/src/plugins/impl/TabsPlugin.tsx +3 -3
- package/src/plugins/impl/TextAreaPlugin.tsx +3 -3
- package/src/plugins/impl/TextInputPlugin.tsx +3 -3
- package/src/plugins/impl/__tests__/MatrixPlugin.test.tsx +2 -2
- package/src/plugins/impl/anywidget/model.ts +10 -10
- package/src/plugins/impl/anywidget/registry.ts +12 -9
- package/src/plugins/impl/anywidget/runtime.ts +11 -9
- package/src/plugins/impl/anywidget/widget-binding.ts +6 -6
- package/src/plugins/impl/image-comparison/ImageComparisonComponent.tsx +7 -6
- package/src/plugins/impl/matplotlib/MatplotlibPlugin.tsx +3 -3
- package/src/plugins/impl/matplotlib/__tests__/matplotlib-renderer.test.ts +2 -2
- package/src/plugins/impl/matplotlib/matplotlib-renderer.ts +2 -2
- package/src/plugins/impl/mpl-interactive/mpl-websocket-shim.ts +10 -10
- package/src/plugins/impl/panel/utils.ts +5 -5
- package/src/plugins/impl/plotly/PlotlyPlugin.tsx +3 -3
- package/src/plugins/impl/vega/VegaPlugin.tsx +3 -3
- package/src/plugins/impl/vega/__tests__/use-vega-container-remeasure.test.ts +7 -7
- package/src/plugins/impl/vega/__tests__/utils.test.ts +16 -1
- package/src/plugins/impl/vega/__tests__/vega.test.ts +37 -0
- package/src/plugins/impl/vega/loader.ts +23 -1
- package/src/plugins/impl/vega/utils.ts +5 -4
- package/src/plugins/layout/AccordionPlugin.tsx +3 -3
- package/src/plugins/layout/CalloutPlugin.tsx +3 -3
- package/src/plugins/layout/ImageComparisonPlugin.tsx +5 -3
- package/src/plugins/layout/JsonOutputPlugin.tsx +3 -3
- package/src/plugins/layout/MimeRenderPlugin.tsx +3 -3
- package/src/plugins/layout/NavigationMenuPlugin.tsx +3 -3
- package/src/plugins/layout/OutlinePlugin.tsx +3 -3
- package/src/plugins/layout/ProgressPlugin.tsx +3 -3
- package/src/plugins/layout/RoutesPlugin.tsx +6 -8
- package/src/plugins/layout/StatPlugin.tsx +3 -3
- package/src/plugins/layout/TexPlugin.tsx +3 -3
- package/src/plugins/layout/carousel/CarouselPlugin.tsx +4 -4
- package/src/plugins/layout/mermaid/MermaidPlugin.tsx +3 -3
- package/src/utils/Deferred.ts +6 -6
- package/src/utils/async-capture-tracker.ts +10 -7
- package/src/utils/errors.ts +5 -5
- package/src/utils/id-tree.tsx +83 -80
- package/src/utils/lru.ts +1 -1
- package/src/utils/multi-map.ts +14 -12
- package/src/utils/paths.ts +6 -6
- package/src/utils/pluralize.ts +3 -3
- package/src/utils/progress.ts +6 -6
- package/src/utils/python-poet/poet.ts +14 -14
- package/src/utils/routes.ts +4 -2
- package/src/utils/semaphore.ts +6 -6
- package/src/utils/storage/storage.ts +6 -6
- package/src/utils/storage/typed.ts +16 -12
- package/src/utils/time.ts +9 -9
- package/src/utils/timed-cache.ts +4 -4
- package/src/utils/tracer.ts +9 -6
- package/dist/classDiagram-ZZMXUADV-Fe-V14Sc.js +0 -30
- package/dist/classDiagram-v2-VYDZK3BY-xTDg7lOs.js +0 -30
- package/dist/flowDiagram-HODETNUW-6dMdB8pR.js +0 -16
- package/dist/katex-DFBiSucW.js +0 -21840
- package/dist/stateDiagram-v2-MP3YSRHH-o05_rJN8.js +0 -30
- package/dist/swimlanesDiagram-VR7AAH4N-D_nFQfRc.js +0 -30
|
@@ -120,6 +120,65 @@ describe("ConsoleOutput pdb history", () => {
|
|
|
120
120
|
expect(newInput).toHaveValue("next");
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
+
it("submits passwords without making them available to input history", () => {
|
|
124
|
+
const passwordPrompt: WithResponse<OutputMessage> = {
|
|
125
|
+
...stdinPrompt("Password: "),
|
|
126
|
+
mimetype: "text/password",
|
|
127
|
+
};
|
|
128
|
+
const onSubmitDebugger = vi.fn();
|
|
129
|
+
const { rerender } = renderWithProvider(
|
|
130
|
+
<ConsoleOutput
|
|
131
|
+
{...defaultProps}
|
|
132
|
+
consoleOutputs={[stdinPrompt("Name: ")]}
|
|
133
|
+
onSubmitDebugger={onSubmitDebugger}
|
|
134
|
+
/>,
|
|
135
|
+
);
|
|
136
|
+
const nameInput = screen.getByTestId("console-input");
|
|
137
|
+
fireEvent.change(nameInput, { target: { value: "marimo" } });
|
|
138
|
+
fireEvent.keyDown(nameInput, { key: "Enter" });
|
|
139
|
+
|
|
140
|
+
rerender(
|
|
141
|
+
<TooltipProvider>
|
|
142
|
+
<ConsoleOutput
|
|
143
|
+
{...defaultProps}
|
|
144
|
+
consoleOutputs={[stdinPrompt("Name: ", "marimo"), passwordPrompt]}
|
|
145
|
+
onSubmitDebugger={onSubmitDebugger}
|
|
146
|
+
/>
|
|
147
|
+
</TooltipProvider>,
|
|
148
|
+
);
|
|
149
|
+
const input = screen.getByTestId("console-input");
|
|
150
|
+
expect(input).toHaveAttribute("type", "password");
|
|
151
|
+
fireEvent.change(input, { target: { value: "getpass-regression-secret" } });
|
|
152
|
+
fireEvent.keyDown(input, { key: "ArrowUp" });
|
|
153
|
+
expect(input).toHaveValue("getpass-regression-secret");
|
|
154
|
+
fireEvent.keyDown(input, { key: "ArrowDown" });
|
|
155
|
+
expect(input).toHaveValue("getpass-regression-secret");
|
|
156
|
+
fireEvent.keyDown(input, { key: "Enter" });
|
|
157
|
+
expect(onSubmitDebugger).toHaveBeenLastCalledWith(
|
|
158
|
+
"getpass-regression-secret",
|
|
159
|
+
1,
|
|
160
|
+
);
|
|
161
|
+
expect(input).toHaveValue("");
|
|
162
|
+
|
|
163
|
+
rerender(
|
|
164
|
+
<TooltipProvider>
|
|
165
|
+
<ConsoleOutput
|
|
166
|
+
{...defaultProps}
|
|
167
|
+
consoleOutputs={[
|
|
168
|
+
stdinPrompt("Name: ", "marimo"),
|
|
169
|
+
{ ...passwordPrompt, response: "" },
|
|
170
|
+
stdinPrompt("Name: "),
|
|
171
|
+
]}
|
|
172
|
+
/>
|
|
173
|
+
</TooltipProvider>,
|
|
174
|
+
);
|
|
175
|
+
const nextInput = screen.getByTestId("console-input");
|
|
176
|
+
fireEvent.keyDown(nextInput, { key: "ArrowUp" });
|
|
177
|
+
expect(nextInput).toHaveValue("marimo");
|
|
178
|
+
fireEvent.keyDown(nextInput, { key: "ArrowDown" });
|
|
179
|
+
expect(nextInput).toHaveValue("");
|
|
180
|
+
});
|
|
181
|
+
|
|
123
182
|
it("should submit an empty string when Enter is pressed with no input", () => {
|
|
124
183
|
// Many CLIs prompt "Press Enter to continue" and expect "" back.
|
|
125
184
|
const onSubmitDebugger = vi.fn();
|
|
@@ -256,6 +256,7 @@ export const pipInstallReplacer: Replacer = (domNode: DOMNode) => {
|
|
|
256
256
|
);
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
// oxlint-disable-next-line react/jsx-no-useless-fragment -- html-react-parser replacers must return a React element.
|
|
259
260
|
return <>{segments}</>;
|
|
260
261
|
};
|
|
261
262
|
|
|
@@ -283,6 +284,7 @@ export const urlReplacer: Replacer = (domNode: DOMNode) => {
|
|
|
283
284
|
return undefined;
|
|
284
285
|
}
|
|
285
286
|
|
|
287
|
+
// oxlint-disable-next-line react/jsx-no-useless-fragment -- html-react-parser replacers must return a React element.
|
|
286
288
|
return <>{result}</>;
|
|
287
289
|
};
|
|
288
290
|
|
|
@@ -331,5 +333,5 @@ export const RenderTextWithLinks = ({ text }: { text: string }) => {
|
|
|
331
333
|
);
|
|
332
334
|
}, [text]);
|
|
333
335
|
|
|
334
|
-
return
|
|
336
|
+
return content;
|
|
335
337
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
4
|
+
import { useAtomValue } from "jotai";
|
|
4
5
|
import {
|
|
5
6
|
BoxIcon,
|
|
6
7
|
CheckIcon,
|
|
@@ -34,6 +35,7 @@ import {
|
|
|
34
35
|
import { useResolvedMarimoConfig } from "@/core/config/config";
|
|
35
36
|
import type { PackageOperationStatus } from "@/core/kernel/messages";
|
|
36
37
|
import { useRequestClient } from "@/core/network/requests";
|
|
38
|
+
import { sandboxAtom } from "@/core/packages/sandbox-state";
|
|
37
39
|
import { RESTART_REQUIRED_DESCRIPTION } from "@/core/packages/toast-components";
|
|
38
40
|
import { isWasm } from "@/core/wasm/utils";
|
|
39
41
|
import { usePackageMetadata } from "@/hooks/usePackageMetadata";
|
|
@@ -104,6 +106,7 @@ export const PackageAlert: React.FC = () => {
|
|
|
104
106
|
const { packageAlert } = useAlerts();
|
|
105
107
|
const { clearPackageAlert } = useAlertActions();
|
|
106
108
|
const [userConfig] = useResolvedMarimoConfig();
|
|
109
|
+
const sandbox = useAtomValue(sandboxAtom);
|
|
107
110
|
const [desiredPackageVersions, setDesiredPackageVersions] = useState<
|
|
108
111
|
Record<string, string>
|
|
109
112
|
>({});
|
|
@@ -132,8 +135,14 @@ export const PackageAlert: React.FC = () => {
|
|
|
132
135
|
return null;
|
|
133
136
|
}
|
|
134
137
|
|
|
138
|
+
const sandboxPackageManager =
|
|
139
|
+
packageAlert.source === "server" ? null : sandbox?.backend;
|
|
140
|
+
const packageManager =
|
|
141
|
+
sandboxPackageManager ?? userConfig.package_management.manager;
|
|
135
142
|
const doesSupportVersioning =
|
|
136
|
-
|
|
143
|
+
packageAlert.source === "server" ||
|
|
144
|
+
sandbox !== null ||
|
|
145
|
+
packageManager !== "pixi";
|
|
137
146
|
|
|
138
147
|
if (isMissingPackageAlert(packageAlert)) {
|
|
139
148
|
return (
|
|
@@ -207,7 +216,7 @@ export const PackageAlert: React.FC = () => {
|
|
|
207
216
|
{packageAlert.isolated ? (
|
|
208
217
|
<>
|
|
209
218
|
<InstallPackagesButton
|
|
210
|
-
manager={
|
|
219
|
+
manager={packageManager}
|
|
211
220
|
packages={packageAlert.packages.map((pkg) => {
|
|
212
221
|
const parsed = parsePackageSpecifier(pkg);
|
|
213
222
|
const currentExtras =
|
|
@@ -219,10 +228,12 @@ export const PackageAlert: React.FC = () => {
|
|
|
219
228
|
source={packageAlert.source}
|
|
220
229
|
/>
|
|
221
230
|
|
|
222
|
-
{!isWasm() && (
|
|
231
|
+
{!isWasm() && packageAlert.source !== "server" && (
|
|
223
232
|
<>
|
|
224
233
|
<span className="px-2 text-sm">with</span>{" "}
|
|
225
|
-
<PackageManagerForm
|
|
234
|
+
<PackageManagerForm
|
|
235
|
+
fixedManager={sandboxPackageManager ?? undefined}
|
|
236
|
+
/>
|
|
226
237
|
</>
|
|
227
238
|
)}
|
|
228
239
|
</>
|
|
@@ -449,7 +460,9 @@ const InstallPackagesButton = ({
|
|
|
449
460
|
);
|
|
450
461
|
};
|
|
451
462
|
|
|
452
|
-
const PackageManagerForm: React.FC
|
|
463
|
+
const PackageManagerForm: React.FC<{
|
|
464
|
+
fixedManager?: PackageManagerName;
|
|
465
|
+
}> = ({ fixedManager }) => {
|
|
453
466
|
const [config, setConfig] = useResolvedMarimoConfig();
|
|
454
467
|
const { saveUserConfig } = useRequestClient();
|
|
455
468
|
|
|
@@ -490,8 +503,8 @@ const PackageManagerForm: React.FC = () => {
|
|
|
490
503
|
<NativeSelect
|
|
491
504
|
data-testid="install-package-manager-select"
|
|
492
505
|
onChange={(e) => field.onChange(e.target.value)}
|
|
493
|
-
value={field.value}
|
|
494
|
-
disabled={field.disabled}
|
|
506
|
+
value={fixedManager ?? field.value}
|
|
507
|
+
disabled={fixedManager !== undefined || field.disabled}
|
|
495
508
|
className="inline-flex mr-2"
|
|
496
509
|
>
|
|
497
510
|
{PackageManagerNames.map((option) => (
|
|
@@ -170,6 +170,7 @@ export const ResourceLinks: React.FC = () => {
|
|
|
170
170
|
key={resource.title}
|
|
171
171
|
href={resource.url}
|
|
172
172
|
target="_blank"
|
|
173
|
+
rel="noreferrer"
|
|
173
174
|
aria-label={`${resource.title}: ${resource.description}`}
|
|
174
175
|
className="flex items-start gap-3 py-3 px-3 rounded-lg border hover:bg-accent/20 transition-colors shadow-xs"
|
|
175
176
|
>
|
|
@@ -50,6 +50,7 @@ const Watermark = () => {
|
|
|
50
50
|
<a
|
|
51
51
|
href={Constants.githubPage}
|
|
52
52
|
target="_blank"
|
|
53
|
+
rel="noreferrer"
|
|
53
54
|
className="text-sm text-(--grass-11) font-bold tracking-wide transition-colors bg-(--grass-4) hover:bg-(--grass-5) border-t border-l border-(--grass-8) px-3 py-1 rounded-tl-md flex items-center gap-2"
|
|
54
55
|
>
|
|
55
56
|
<span>made with marimo</span>
|
|
@@ -46,21 +46,21 @@ beforeAll(() => {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
global.IntersectionObserver ??= class {
|
|
49
|
-
observe() {
|
|
49
|
+
public observe() {
|
|
50
50
|
/* noop */
|
|
51
51
|
}
|
|
52
|
-
unobserve() {
|
|
52
|
+
public unobserve() {
|
|
53
53
|
/* noop */
|
|
54
54
|
}
|
|
55
|
-
disconnect() {
|
|
55
|
+
public disconnect() {
|
|
56
56
|
/* noop */
|
|
57
57
|
}
|
|
58
|
-
takeRecords() {
|
|
58
|
+
public takeRecords() {
|
|
59
59
|
return [];
|
|
60
60
|
}
|
|
61
|
-
root = null;
|
|
62
|
-
rootMargin = "";
|
|
63
|
-
thresholds = [];
|
|
61
|
+
public root = null;
|
|
62
|
+
public rootMargin = "";
|
|
63
|
+
public thresholds = [];
|
|
64
64
|
} as unknown as typeof IntersectionObserver;
|
|
65
65
|
});
|
|
66
66
|
|
|
@@ -42,28 +42,28 @@ describe("useVisibleCellIds", () => {
|
|
|
42
42
|
mutationObserveSpy = vi.fn();
|
|
43
43
|
|
|
44
44
|
global.IntersectionObserver = class MockIntersectionObserver {
|
|
45
|
-
constructor(
|
|
45
|
+
public constructor(
|
|
46
46
|
callback: IntersectionObserverCallback,
|
|
47
47
|
_options?: IntersectionObserverInit,
|
|
48
48
|
) {
|
|
49
49
|
intersectionCallback = callback;
|
|
50
50
|
}
|
|
51
|
-
observe = observeSpy;
|
|
52
|
-
unobserve = vi.fn();
|
|
53
|
-
disconnect = intersectionDisconnectSpy;
|
|
54
|
-
root = null;
|
|
55
|
-
rootMargin = "";
|
|
56
|
-
thresholds = [0];
|
|
57
|
-
takeRecords = vi.fn(() => []);
|
|
51
|
+
public observe = observeSpy;
|
|
52
|
+
public unobserve = vi.fn();
|
|
53
|
+
public disconnect = intersectionDisconnectSpy;
|
|
54
|
+
public root = null;
|
|
55
|
+
public rootMargin = "";
|
|
56
|
+
public thresholds = [0];
|
|
57
|
+
public takeRecords = vi.fn(() => []);
|
|
58
58
|
} as unknown as typeof IntersectionObserver;
|
|
59
59
|
|
|
60
60
|
global.MutationObserver = class MockMutationObserver {
|
|
61
|
-
constructor(callback: MutationCallback) {
|
|
61
|
+
public constructor(callback: MutationCallback) {
|
|
62
62
|
mutationCallback = callback;
|
|
63
63
|
}
|
|
64
|
-
observe = mutationObserveSpy;
|
|
65
|
-
disconnect = mutationDisconnectSpy;
|
|
66
|
-
takeRecords = vi.fn(() => []);
|
|
64
|
+
public observe = mutationObserveSpy;
|
|
65
|
+
public disconnect = mutationDisconnectSpy;
|
|
66
|
+
public takeRecords = vi.fn(() => []);
|
|
67
67
|
} as unknown as typeof MutationObserver;
|
|
68
68
|
});
|
|
69
69
|
|
|
@@ -76,12 +76,12 @@ describe("SlideScrollContainer", () => {
|
|
|
76
76
|
vi.stubGlobal(
|
|
77
77
|
"ResizeObserver",
|
|
78
78
|
class MockResizeObserver {
|
|
79
|
-
callback: ResizeObserverCallback;
|
|
80
|
-
observe = vi.fn();
|
|
81
|
-
unobserve = vi.fn();
|
|
82
|
-
disconnect = vi.fn();
|
|
79
|
+
public callback: ResizeObserverCallback;
|
|
80
|
+
public observe = vi.fn();
|
|
81
|
+
public unobserve = vi.fn();
|
|
82
|
+
public disconnect = vi.fn();
|
|
83
83
|
|
|
84
|
-
constructor(callback: ResizeObserverCallback) {
|
|
84
|
+
public constructor(callback: ResizeObserverCallback) {
|
|
85
85
|
this.callback = callback;
|
|
86
86
|
resizeObservers.push(this);
|
|
87
87
|
}
|
|
@@ -90,9 +90,9 @@ describe("SlideScrollContainer", () => {
|
|
|
90
90
|
vi.stubGlobal(
|
|
91
91
|
"MutationObserver",
|
|
92
92
|
class MockMutationObserver {
|
|
93
|
-
observe = vi.fn();
|
|
94
|
-
disconnect = vi.fn();
|
|
95
|
-
takeRecords = vi.fn(() => []);
|
|
93
|
+
public observe = vi.fn();
|
|
94
|
+
public disconnect = vi.fn();
|
|
95
|
+
public takeRecords = vi.fn(() => []);
|
|
96
96
|
},
|
|
97
97
|
);
|
|
98
98
|
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import type React from "react";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { Dialog } from "@/components/ui/dialog";
|
|
7
|
+
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
8
|
+
import { ShareStaticNotebookModal } from "../share-modal";
|
|
9
|
+
import { stageForPublish } from "../stage-for-publish";
|
|
10
|
+
|
|
11
|
+
const exportAsHTML = vi.fn();
|
|
12
|
+
|
|
13
|
+
vi.mock("@/core/network/requests", () => ({
|
|
14
|
+
useRequestClient: () => ({ exportAsHTML }),
|
|
15
|
+
}));
|
|
16
|
+
vi.mock("@/core/export/layout", () => ({
|
|
17
|
+
getExportLayout: vi.fn().mockResolvedValue(undefined),
|
|
18
|
+
}));
|
|
19
|
+
vi.mock("@/core/static/virtual-file-tracker", () => ({
|
|
20
|
+
VirtualFileTracker: { INSTANCE: { filenames: () => [] } },
|
|
21
|
+
}));
|
|
22
|
+
vi.mock("../stage-for-publish", () => ({
|
|
23
|
+
stageForPublish: vi.fn(),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
const stageForPublishMock = vi.mocked(stageForPublish);
|
|
27
|
+
const CLAIM_URL = "https://claim.example/abc";
|
|
28
|
+
|
|
29
|
+
function wrapper({ children }: { children: React.ReactNode }) {
|
|
30
|
+
return (
|
|
31
|
+
<TooltipProvider>
|
|
32
|
+
<Dialog open={true}>{children}</Dialog>
|
|
33
|
+
</TooltipProvider>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("ShareStaticNotebookModal", () => {
|
|
38
|
+
const onClose = vi.fn();
|
|
39
|
+
const windowOpen = vi.fn<typeof window.open>();
|
|
40
|
+
|
|
41
|
+
beforeEach(() => {
|
|
42
|
+
vi.clearAllMocks();
|
|
43
|
+
vi.stubGlobal("open", windowOpen);
|
|
44
|
+
exportAsHTML.mockResolvedValue({
|
|
45
|
+
contents: "<html></html>",
|
|
46
|
+
filename: "nb.html",
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
vi.unstubAllGlobals();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function renderModal() {
|
|
55
|
+
return render(<ShareStaticNotebookModal onClose={onClose} />, {
|
|
56
|
+
wrapper,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
it("exports with CDN assets, opens the claim page, and keeps the link in the dialog", async () => {
|
|
61
|
+
stageForPublishMock.mockResolvedValue(CLAIM_URL);
|
|
62
|
+
renderModal();
|
|
63
|
+
|
|
64
|
+
fireEvent.click(screen.getByTestId("share-static-notebook-button"));
|
|
65
|
+
|
|
66
|
+
const link = await screen.findByTestId("open-claim-page-link");
|
|
67
|
+
expect(link).toHaveAttribute("href", CLAIM_URL);
|
|
68
|
+
expect(link).toHaveAttribute("rel", "noopener noreferrer");
|
|
69
|
+
expect(link).toHaveAttribute("target", "_blank");
|
|
70
|
+
|
|
71
|
+
expect(windowOpen).toHaveBeenCalledWith(
|
|
72
|
+
CLAIM_URL,
|
|
73
|
+
"_blank",
|
|
74
|
+
"noopener,noreferrer",
|
|
75
|
+
);
|
|
76
|
+
expect(exportAsHTML).toHaveBeenCalledWith(
|
|
77
|
+
expect.objectContaining({
|
|
78
|
+
includeCode: true,
|
|
79
|
+
download: false,
|
|
80
|
+
assetUrl: expect.stringContaining("cdn.jsdelivr.net"),
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
expect(stageForPublishMock).toHaveBeenCalledWith(
|
|
84
|
+
"nb.html",
|
|
85
|
+
"<html></html>",
|
|
86
|
+
);
|
|
87
|
+
// The dialog stays open so the link survives a blocked popup.
|
|
88
|
+
expect(onClose).not.toHaveBeenCalled();
|
|
89
|
+
expect(
|
|
90
|
+
screen.queryByTestId("share-static-notebook-button"),
|
|
91
|
+
).not.toBeInTheDocument();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("closes from the staged view via Done", async () => {
|
|
95
|
+
stageForPublishMock.mockResolvedValue(CLAIM_URL);
|
|
96
|
+
renderModal();
|
|
97
|
+
|
|
98
|
+
fireEvent.click(screen.getByTestId("share-static-notebook-button"));
|
|
99
|
+
fireEvent.click(
|
|
100
|
+
await screen.findByTestId("done-share-static-notebook-button"),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
expect(onClose).toHaveBeenCalledTimes(1);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("shows the failure in the dialog and allows retrying", async () => {
|
|
107
|
+
stageForPublishMock
|
|
108
|
+
.mockRejectedValueOnce(new Error("Too many uploads, try again later"))
|
|
109
|
+
.mockResolvedValueOnce(CLAIM_URL);
|
|
110
|
+
renderModal();
|
|
111
|
+
|
|
112
|
+
fireEvent.click(screen.getByTestId("share-static-notebook-button"));
|
|
113
|
+
|
|
114
|
+
const error = await screen.findByTestId("share-static-notebook-error");
|
|
115
|
+
expect(error).toHaveTextContent(
|
|
116
|
+
"Publish failed: Too many uploads, try again later",
|
|
117
|
+
);
|
|
118
|
+
expect(windowOpen).not.toHaveBeenCalled();
|
|
119
|
+
expect(onClose).not.toHaveBeenCalled();
|
|
120
|
+
|
|
121
|
+
const retry = screen.getByTestId("share-static-notebook-button");
|
|
122
|
+
expect(retry).toHaveTextContent("Try again");
|
|
123
|
+
expect(retry).toBeEnabled();
|
|
124
|
+
fireEvent.click(retry);
|
|
125
|
+
|
|
126
|
+
expect(await screen.findByTestId("open-claim-page-link")).toHaveAttribute(
|
|
127
|
+
"href",
|
|
128
|
+
CLAIM_URL,
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("disables both buttons while staging", async () => {
|
|
133
|
+
let resolveStage: (url: string) => void = () => undefined;
|
|
134
|
+
stageForPublishMock.mockImplementation(
|
|
135
|
+
() =>
|
|
136
|
+
new Promise<string>((resolve) => {
|
|
137
|
+
resolveStage = resolve;
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
renderModal();
|
|
141
|
+
|
|
142
|
+
fireEvent.click(screen.getByTestId("share-static-notebook-button"));
|
|
143
|
+
|
|
144
|
+
await waitFor(() => {
|
|
145
|
+
expect(screen.getByTestId("share-static-notebook-button")).toBeDisabled();
|
|
146
|
+
});
|
|
147
|
+
expect(
|
|
148
|
+
screen.getByTestId("cancel-share-static-notebook-button"),
|
|
149
|
+
).toBeDisabled();
|
|
150
|
+
|
|
151
|
+
resolveStage(CLAIM_URL);
|
|
152
|
+
await screen.findByTestId("open-claim-page-link");
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { MAX_PUBLISH_BYTES, stageForPublish } from "../stage-for-publish";
|
|
5
|
+
|
|
6
|
+
// Opaque to the client; the shapes here are deliberately not molab's.
|
|
7
|
+
const PENDING = {
|
|
8
|
+
pendingId: "id-1",
|
|
9
|
+
presignedUrl: "https://upload.example/put?token=abc",
|
|
10
|
+
claimUrl: "https://claim.example/abc",
|
|
11
|
+
expiresAt: "2026-01-01T00:00:00Z",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function jsonResponse(body: unknown, status = 200): Response {
|
|
15
|
+
return new Response(JSON.stringify(body), {
|
|
16
|
+
status,
|
|
17
|
+
headers: { "Content-Type": "application/json" },
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("stageForPublish", () => {
|
|
22
|
+
const fetchMock = vi.fn<typeof fetch>();
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
fetchMock.mockReset();
|
|
30
|
+
vi.unstubAllGlobals();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("reserves a pending upload, PUTs the export, and returns the claim URL", async () => {
|
|
34
|
+
fetchMock
|
|
35
|
+
.mockResolvedValueOnce(jsonResponse(PENDING))
|
|
36
|
+
.mockResolvedValueOnce(new Response(null, { status: 200 }));
|
|
37
|
+
|
|
38
|
+
const claimUrl = await stageForPublish("nb.html", "<html>hi</html>");
|
|
39
|
+
|
|
40
|
+
expect(claimUrl).toBe(PENDING.claimUrl);
|
|
41
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
42
|
+
|
|
43
|
+
const [stageUrl, stageInit] = fetchMock.mock.calls[0];
|
|
44
|
+
expect(String(stageUrl)).toMatch(/\/api\/artifacts\/pending$/);
|
|
45
|
+
expect(stageInit?.method).toBe("POST");
|
|
46
|
+
expect(JSON.parse(String(stageInit?.body))).toEqual({
|
|
47
|
+
fileName: "nb.html",
|
|
48
|
+
fileSize: new Blob(["<html>hi</html>"]).size,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const [putUrl, putInit] = fetchMock.mock.calls[1];
|
|
52
|
+
expect(putUrl).toBe(PENDING.presignedUrl);
|
|
53
|
+
expect(putInit?.method).toBe("PUT");
|
|
54
|
+
expect(putInit?.body).toBeInstanceOf(Blob);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("surfaces the server's error message when staging is rejected", async () => {
|
|
58
|
+
fetchMock.mockResolvedValueOnce(
|
|
59
|
+
jsonResponse({ error: "Only .html or .htm files are supported" }, 400),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
await expect(stageForPublish("nb.txt", "x")).rejects.toThrow(
|
|
63
|
+
"Only .html or .htm files are supported",
|
|
64
|
+
);
|
|
65
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("explains a 429 when the body carries no message", async () => {
|
|
69
|
+
fetchMock.mockResolvedValueOnce(new Response("", { status: 429 }));
|
|
70
|
+
|
|
71
|
+
await expect(stageForPublish("nb.html", "x")).rejects.toThrow(
|
|
72
|
+
"Too many uploads, try again later",
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("falls back to a generic message for other bodiless failures", async () => {
|
|
77
|
+
fetchMock.mockResolvedValueOnce(new Response("", { status: 500 }));
|
|
78
|
+
|
|
79
|
+
await expect(stageForPublish("nb.html", "x")).rejects.toThrow(
|
|
80
|
+
"Failed to prepare the upload",
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("fails when the presigned PUT is rejected", async () => {
|
|
85
|
+
fetchMock
|
|
86
|
+
.mockResolvedValueOnce(jsonResponse(PENDING))
|
|
87
|
+
.mockResolvedValueOnce(new Response(null, { status: 403 }));
|
|
88
|
+
|
|
89
|
+
await expect(stageForPublish("nb.html", "x")).rejects.toThrow(
|
|
90
|
+
"Failed to upload the notebook export",
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("rejects exports over the size limit before touching the network", async () => {
|
|
95
|
+
// Stub Blob so the test doesn't allocate 100 MB.
|
|
96
|
+
const RealBlob = globalThis.Blob;
|
|
97
|
+
vi.stubGlobal(
|
|
98
|
+
"Blob",
|
|
99
|
+
class extends RealBlob {
|
|
100
|
+
override get size() {
|
|
101
|
+
return MAX_PUBLISH_BYTES + 1;
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
await expect(stageForPublish("nb.html", "x")).rejects.toThrow(
|
|
107
|
+
"File is too large. Maximum size is 100 MB.",
|
|
108
|
+
);
|
|
109
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
110
|
+
});
|
|
111
|
+
});
|