@marimo-team/frontend 0.24.3-dev55 → 0.24.3-dev58
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/assets/{ConnectedDataExplorerComponent-COS9QB8y.js → ConnectedDataExplorerComponent-BaunPY14.js} +1 -1
- package/dist/assets/{ErrorBoundary-B0WULAu7.js → ErrorBoundary-B7tf_7B1.js} +2 -2
- package/dist/assets/{LazyAnyLanguageCodeMirror-BBJUf-Np.js → LazyAnyLanguageCodeMirror-C3FCflY3.js} +2 -2
- package/dist/assets/{RunButton-BfJ-ltdu.js → RunButton-CE8Rn7A2.js} +1 -1
- package/dist/assets/__vite-browser-external-BKSyd8Ok.js +1 -0
- package/dist/assets/__vite-browser-external-ZTY8EBdt.js +1 -0
- package/dist/assets/{abnfDiagram-VCTEODGH-DboXQG6R.js → abnfDiagram-VCTEODGH-CgLkNDl-.js} +1 -1
- package/dist/assets/{add-cell-with-ai-COKLzuEj.js → add-cell-with-ai-DZpYEFLT.js} +1 -1
- package/dist/assets/{agent-panel-BhikzzeA.js → agent-panel-Blde_5Nw.js} +1 -1
- package/dist/assets/{ai-model-dropdown-CDPQlQb4.js → ai-model-dropdown-BAV2ckFD.js} +1 -1
- package/dist/assets/{any-language-editor-JD5Y88Ry.js → any-language-editor-Br1nAB4H.js} +1 -1
- package/dist/assets/{app-config-button-B0d_lhhw.js → app-config-button-CAcrPkSw.js} +1 -1
- package/dist/assets/{architectureDiagram-5GKGNRK7-BvWNI_5q.js → architectureDiagram-5GKGNRK7--6i_ophu.js} +1 -1
- package/dist/assets/{blockDiagram-I7D4REHJ-Dlsb_LDd.js → blockDiagram-I7D4REHJ-8Kt9Qfvn.js} +1 -1
- package/dist/assets/{cache-panel-TpjG4mLz.js → cache-panel-B7eXY-wH.js} +1 -1
- package/dist/assets/{chat-display-Blm598xg.js → chat-display-Cm9AooD6.js} +1 -1
- package/dist/assets/{chat-panel-IDQSw-2o.js → chat-panel-BJulebpm.js} +1 -1
- package/dist/assets/{chat-ui-BCSAa-3N.js → chat-ui-CbvVmHX5.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-mx4yRsyK.js → chunk-IMKFNOWR-LOXuyKpq.js} +1 -1
- package/dist/assets/{chunk-SHT3W25Y--1D03dcz.js → chunk-SHT3W25Y-DSHCLgMi.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-D-8rbb0u.js → chunk-SVP7TREG-DBEt2afY.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-BS0UFo-m.js → chunk-TICWLB2K-HWt9N6bw.js} +1 -1
- package/dist/assets/{classDiagram-ZZMXUADV-D-Yj3iaX.js → classDiagram-ZZMXUADV-_R63PS_W.js} +1 -1
- package/dist/assets/{classDiagram-v2-VYDZK3BY-D-Yj3iaX.js → classDiagram-v2-VYDZK3BY-_R63PS_W.js} +1 -1
- package/dist/assets/{code-visibility-CtcGTxtC.js → code-visibility-BAomF5-u.js} +1 -1
- package/dist/assets/{command-palette-BdrVdLjt.js → command-palette-ls_CWFw2.js} +1 -1
- package/dist/assets/{common-Ck3N5kYX.js → common-CFEYk4ZD.js} +1 -1
- package/dist/assets/{components-D4Ld_zGl.js → components-B19v8LfN.js} +1 -1
- package/dist/assets/{components-CZyPJUeR.js → components-CoCDuD3v.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-CmSj7gh3.js → cynefinDiagram-5FMLGOSQ-DzvzfeBu.js} +1 -1
- package/dist/assets/{dagre-B7KPVDPi.js → dagre-Du9KcfwW.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-CWQaKLCd.js → dagre-GXQ25YYZ-Cn_twVWm.js} +1 -1
- package/dist/assets/{data-grid-overlay-editor-APdEQBzl.js → data-grid-overlay-editor-BybtNCnw.js} +1 -1
- package/dist/assets/{dependency-graph-panel-BN7HnK2P.js → dependency-graph-panel-bkpt5uV5.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-9eNHnvs_.js → diagram-S7CK7UJ4-r38unUVd.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-BRuPC1Wd.js → diagram-UQ7AKVKN-B4nKOViW.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-8hfdpbGT.js → diagram-VX7I27RA-BWKnMlwu.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-DRSErXLE.js → diagram-Z3DM3KII-DhLOR-V2.js} +1 -1
- package/dist/assets/{documentation-panel-DLoR1lNz.js → documentation-panel-CT9lUKBw.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC--5KxiZWp.js → ebnfDiagram-PWID7BFC-DeT9RQwz.js} +1 -1
- package/dist/assets/{edit-page-DEA_9yzH.js → edit-page-D_JZxdDI.js} +4 -4
- package/dist/assets/{erDiagram-RLTQ6QDP-CvEz2ZIr.js → erDiagram-RLTQ6QDP-BRjuZ28D.js} +1 -1
- package/dist/assets/{error-panel-DiprKIxB.js → error-panel-BKzymMJt.js} +1 -1
- package/dist/assets/{feedback-button-BbkK__l4.js → feedback-button-BZ4eWgUl.js} +1 -1
- package/dist/assets/{file-explorer-panel-Bb4Apx-N.js → file-explorer-panel-CFSRfg8a.js} +1 -1
- package/dist/assets/{file-name-input-Dv-bmt0C.js → file-name-input-DCYH4Kqg.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-jk_3qz7x.js → flowDiagram-HODETNUW-B930yEc1.js} +1 -1
- package/dist/assets/{gallery-page-BtIDlcO9.js → gallery-page-C6msmdvq.js} +1 -1
- package/dist/assets/{glide-data-editor-DTuLqk5M.js → glide-data-editor-BsfOFrfe.js} +4 -4
- package/dist/assets/{graphlib-BQHSCXhd.js → graphlib-D47HZg_Z.js} +1 -1
- package/dist/assets/has-DkxB0Eh3.js +1 -0
- package/dist/assets/{home-page-DcLD2SYq.js → home-page-BbcGWO0y.js} +1 -1
- package/dist/assets/index-nH9dQ13U.js +62 -0
- package/dist/assets/{infoDiagram-27XIBGKW-BvTlbZly.js → infoDiagram-27XIBGKW-C1-xZcQU.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-Bd0JPSkJ.js → ishikawaDiagram-5VMMS53U-B60ECldo.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-DnWi5lN9.js → kanban-definition-UXKFOSKX-DK3xk3aO.js} +1 -1
- package/dist/assets/{logs-panel-WXuCywjT.js → logs-panel-Dk5nRS20.js} +1 -1
- package/dist/assets/{mermaid-FMOV29Gw.js → mermaid-CeSZtPQ7.js} +4 -4
- package/dist/assets/{mindmap-definition-YA3MSWOX-CE7bu4uQ.js → mindmap-definition-YA3MSWOX-BB-BWlMB.js} +1 -1
- package/dist/assets/{number-overlay-editor-qnMbBZBS.js → number-overlay-editor-BBlzp0N9.js} +1 -1
- package/dist/assets/{outline-panel-Ra4ZfPaK.js → outline-panel-B4nxCpLH.js} +1 -1
- package/dist/assets/{packages-panel-Bx507J_e.js → packages-panel-CfPZKjPk.js} +1 -1
- package/dist/assets/{panels-WxJBrHDO.js → panels-BWU5O17j.js} +3 -3
- package/dist/assets/{pegDiagram-XKGWAZYB-Di8y4ZKK.js → pegDiagram-XKGWAZYB-C1RBmCCz.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-D2WKj0zg.js → pieDiagram-E7YTZNPT-BVo3j1st.js} +1 -1
- package/dist/assets/{process-output-BvPR1InQ.js → process-output-CPvGqY7r.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-BSD9hFAB.js → railroadDiagram-O6MQD6OU-CKqTw0oi.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-C4Is-Eo9.js → requirementDiagram-BXWQKSXE-DnjSRQN8.js} +1 -1
- package/dist/assets/{reveal-component-CLf7EQgk.js → reveal-component-CbEXjUJn.js} +1 -1
- package/dist/assets/{run-page-CjJJvVx_.js → run-page-txqyXqLW.js} +1 -1
- package/dist/assets/save-worker-CMa6Cvnx.js +113 -0
- package/dist/assets/{scratchpad-panel-u5F0VTl4.js → scratchpad-panel-BirgND3s.js} +1 -1
- package/dist/assets/{secrets-panel-DUFKRVaH.js → secrets-panel-Dy_oak_1.js} +1 -1
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-BHyxbrPL.js → sequenceDiagram-WJ2MYXX4-CZVbl-1N.js} +1 -1
- package/dist/assets/{session-panel-BY4S5P_s.js → session-panel-C2dlC4QT.js} +1 -1
- package/dist/assets/{snippets-panel-Cq4K4hBV.js → snippets-panel-BmO3Y6z8.js} +1 -1
- package/dist/assets/{stateDiagram-D77RDMKH-Cv5gcew4.js → stateDiagram-D77RDMKH-BHcKHZmR.js} +1 -1
- package/dist/assets/{stateDiagram-v2-MP3YSRHH-DkKFuvSs.js → stateDiagram-v2-MP3YSRHH-CmqQh4T2.js} +1 -1
- package/dist/assets/{swimlanes-42K2YHIH-BHKDxubG.js → swimlanes-42K2YHIH-WuvP6CDS.js} +1 -1
- package/dist/assets/{swimlanesDiagram-VR7AAH4N-Bh4LMjQ2.js → swimlanesDiagram-VR7AAH4N-B4c5y_p2.js} +1 -1
- package/dist/assets/{swiper-component-CcIKtHf1.js → swiper-component-BR-epejw.js} +1 -1
- package/dist/assets/{terminal-BjVPspc3.js → terminal-DrvDl_d5.js} +1 -1
- package/dist/assets/{timeline-definition-24CTP7MA-DwkTapoq.js → timeline-definition-24CTP7MA-DKn76-RQ.js} +1 -1
- package/dist/assets/{tracing-Caq9OuIL.js → tracing-BXDOsNGf.js} +1 -1
- package/dist/assets/{tracing-panel-CtPZXpUL.js → tracing-panel-V_ZVLq68.js} +2 -2
- package/dist/assets/{useCellActionButton-OUIwiRu0.js → useCellActionButton-BeczgoeM.js} +1 -1
- package/dist/assets/{vega-component-D-72a839.js → vega-component-B8gGF1Ew.js} +1 -1
- package/dist/assets/{vega-embed-container-BBdTcDp3.js → vega-embed-container-CXv4_JzG.js} +1 -1
- package/dist/assets/{vennDiagram-4TSXK5OY-KTiFwgD1.js → vennDiagram-4TSXK5OY-nQpUsTlH.js} +1 -1
- package/dist/assets/{wardleyDiagram-VM6X3IG4-Bfwv76R-.js → wardleyDiagram-VM6X3IG4-BQk9IGjL.js} +1 -1
- package/dist/assets/{worker-BXMsnprR.js → worker-B8c-1v-F.js} +5 -5
- package/dist/assets/{xychartDiagram-S5SC5T6Z-kFI-x2jp.js → xychartDiagram-S5SC5T6Z-FkkMK-fP.js} +1 -1
- package/dist/index.html +3 -3
- package/package.json +1 -1
- package/src/core/islands/worker-factory.ts +2 -12
- package/src/core/wasm/__tests__/bootstrap.test.ts +63 -0
- package/src/core/wasm/__tests__/bridge.test.ts +8 -0
- package/src/core/wasm/__tests__/fs.test.ts +22 -0
- package/src/core/wasm/__tests__/runtime-config.test.ts +58 -0
- package/src/core/wasm/bridge.ts +18 -16
- package/src/core/wasm/rpc.ts +2 -0
- package/src/core/wasm/runtime-config.ts +27 -0
- package/src/core/wasm/worker/bootstrap.ts +21 -11
- package/src/core/wasm/worker/fs.ts +4 -0
- package/src/core/wasm/worker/save-worker.ts +7 -0
- package/src/core/wasm/worker/types.ts +7 -4
- package/src/core/wasm/worker/worker.ts +6 -0
- package/src/utils/__tests__/worker.test.ts +81 -0
- package/src/utils/worker.ts +22 -0
- package/dist/assets/__vite-browser-external-BzcgEWaD.js +0 -1
- package/dist/assets/__vite-browser-external-D87rEcrz.js +0 -1
- package/dist/assets/has-DZ-sm-ft.js +0 -1
- package/dist/assets/index-DKfkO1Lu.js +0 -62
- package/dist/assets/save-worker-BstmibH4.js +0 -113
package/src/core/wasm/bridge.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { throwNotImplemented } from "@/utils/functions";
|
|
|
9
9
|
import { Logger } from "@/utils/Logger";
|
|
10
10
|
import { reloadSafe } from "@/utils/reload-safe";
|
|
11
11
|
import { generateUUID } from "@/utils/uuid";
|
|
12
|
+
import { createModuleWorker } from "@/utils/worker";
|
|
12
13
|
import { notebookIsRunningAtom } from "../cells/cells";
|
|
13
14
|
import type { CommandMessage } from "../kernel/messages";
|
|
14
15
|
import { getMarimoVersion } from "../meta/globals";
|
|
@@ -41,10 +42,13 @@ import { BasicTransport } from "../websocket/transports/basic";
|
|
|
41
42
|
import type { IConnectionTransport } from "../websocket/transports/transport";
|
|
42
43
|
import { PyodideRouter } from "./router";
|
|
43
44
|
import { getWorkerRPC } from "./rpc";
|
|
45
|
+
import { getWasmRuntimeConfig } from "./runtime-config";
|
|
44
46
|
import { createShareableLink } from "./share";
|
|
45
47
|
import { wasmInitStateAtom } from "./state";
|
|
46
48
|
import { fallbackFileStore, notebookFileStore } from "./store";
|
|
47
49
|
import { isWasm } from "./utils";
|
|
50
|
+
import saveWorkerUrl from "./worker/save-worker.ts?worker&url";
|
|
51
|
+
import workerUrl from "./worker/worker.ts?worker&url";
|
|
48
52
|
import type { SaveWorkerSchema } from "./worker/save-worker";
|
|
49
53
|
import type { WorkerSchema } from "./worker/worker";
|
|
50
54
|
|
|
@@ -81,19 +85,20 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
81
85
|
};
|
|
82
86
|
}
|
|
83
87
|
|
|
88
|
+
const runtimeConfig = getWasmRuntimeConfig();
|
|
89
|
+
|
|
84
90
|
// Create save worker
|
|
85
|
-
const saveWorker =
|
|
86
|
-
|
|
87
|
-
new URL("./worker/save-worker.ts", import.meta.url),
|
|
91
|
+
const saveWorker = createModuleWorker(
|
|
92
|
+
new URL(saveWorkerUrl, import.meta.url),
|
|
88
93
|
{
|
|
89
|
-
type: "module",
|
|
90
94
|
// Pass the version (and optional capability suffix) to the worker
|
|
91
|
-
/* @vite-ignore */
|
|
92
95
|
name: getWasmWorkerName(),
|
|
93
96
|
},
|
|
94
97
|
);
|
|
95
98
|
|
|
96
|
-
|
|
99
|
+
const rpc = getWorkerRPC<SaveWorkerSchema>(saveWorker);
|
|
100
|
+
rpc.send.bootstrap(runtimeConfig);
|
|
101
|
+
return rpc.proxy.request;
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
private constructor() {
|
|
@@ -101,20 +106,17 @@ export class PyodideBridge implements RunRequests, EditRequests {
|
|
|
101
106
|
return;
|
|
102
107
|
}
|
|
103
108
|
|
|
109
|
+
const runtimeConfig = getWasmRuntimeConfig();
|
|
110
|
+
|
|
104
111
|
// Create a worker
|
|
105
|
-
const worker = new
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
type: "module",
|
|
110
|
-
// Pass the version (and optional capability suffix) to the worker
|
|
111
|
-
/* @vite-ignore */
|
|
112
|
-
name: getWasmWorkerName(),
|
|
113
|
-
},
|
|
114
|
-
);
|
|
112
|
+
const worker = createModuleWorker(new URL(workerUrl, import.meta.url), {
|
|
113
|
+
// Pass the version (and optional capability suffix) to the worker
|
|
114
|
+
name: getWasmWorkerName(),
|
|
115
|
+
});
|
|
115
116
|
|
|
116
117
|
// Create the RPC
|
|
117
118
|
this.rpc = getWorkerRPC<WorkerSchema>(worker);
|
|
119
|
+
this.rpc.send.bootstrap(runtimeConfig);
|
|
118
120
|
|
|
119
121
|
// Listeners
|
|
120
122
|
this.rpc.addMessageListener("ready", () => {
|
package/src/core/wasm/rpc.ts
CHANGED
|
@@ -6,11 +6,13 @@ import {
|
|
|
6
6
|
type RPCSchema,
|
|
7
7
|
} from "rpc-anywhere";
|
|
8
8
|
import { Logger } from "@/utils/Logger";
|
|
9
|
+
import type { WasmRuntimeConfig } from "./runtime-config";
|
|
9
10
|
import { TRANSPORT_ID } from "./worker/constants";
|
|
10
11
|
|
|
11
12
|
export type ParentSchema = RPCSchema<{
|
|
12
13
|
messages: {
|
|
13
14
|
consumerReady: {};
|
|
15
|
+
bootstrap: WasmRuntimeConfig;
|
|
14
16
|
};
|
|
15
17
|
}>;
|
|
16
18
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
export interface WasmRuntimeConfig {
|
|
4
|
+
pyodideIndexUrl?: string;
|
|
5
|
+
pyodideLockfileUrl?: string;
|
|
6
|
+
pypiIndexUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getWasmRuntimeConfig(): WasmRuntimeConfig {
|
|
10
|
+
const element = document.querySelector<HTMLElement>("marimo-wasm");
|
|
11
|
+
const resolve = (url: string | undefined) => {
|
|
12
|
+
try {
|
|
13
|
+
return url ? new URL(url, document.baseURI).href : undefined;
|
|
14
|
+
} catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
pyodideIndexUrl: resolve(element?.dataset.pyodideIndexUrl),
|
|
20
|
+
pyodideLockfileUrl: resolve(element?.dataset.pyodideLockfileUrl),
|
|
21
|
+
// URL serialization escapes the placeholder understood by micropip.
|
|
22
|
+
pypiIndexUrl: resolve(element?.dataset.pypiIndexUrl)?.replaceAll(
|
|
23
|
+
"%7Bpackage_name%7D",
|
|
24
|
+
"{package_name}",
|
|
25
|
+
),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -41,10 +41,9 @@ export class DefaultWasmController implements WasmController {
|
|
|
41
41
|
return this.pyodide;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
async bootstrap(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}): Promise<PyodideInterface> {
|
|
44
|
+
async bootstrap(
|
|
45
|
+
opts: Parameters<WasmController["bootstrap"]>[0],
|
|
46
|
+
): Promise<PyodideInterface> {
|
|
48
47
|
const pyodide = await this.loadPyodideAndPackages(opts);
|
|
49
48
|
|
|
50
49
|
if (MAKE_SNAPSHOT) {
|
|
@@ -55,10 +54,9 @@ export class DefaultWasmController implements WasmController {
|
|
|
55
54
|
return pyodide;
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
private async loadPyodideAndPackages(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}): Promise<PyodideInterface> {
|
|
57
|
+
private async loadPyodideAndPackages(
|
|
58
|
+
opts: Parameters<WasmController["bootstrap"]>[0],
|
|
59
|
+
): Promise<PyodideInterface> {
|
|
62
60
|
if (!loadPyodide) {
|
|
63
61
|
throw new Error("loadPyodide is not defined");
|
|
64
62
|
}
|
|
@@ -68,7 +66,9 @@ export class DefaultWasmController implements WasmController {
|
|
|
68
66
|
// Without this, this fails in Firefox with
|
|
69
67
|
// `Could not extract indexURL path from pyodide module`
|
|
70
68
|
// This fixes for Firefox and does not break Chrome/others
|
|
71
|
-
const indexURL =
|
|
69
|
+
const indexURL =
|
|
70
|
+
opts.pyodideIndexUrl ??
|
|
71
|
+
`https://cdn.jsdelivr.net/pyodide/${opts.pyodideVersion}/full/`;
|
|
72
72
|
const pyodide = await loadPyodide({
|
|
73
73
|
// Perf: These get loaded while pyodide is being bootstrapped
|
|
74
74
|
packages: [
|
|
@@ -81,16 +81,26 @@ export class DefaultWasmController implements WasmController {
|
|
|
81
81
|
"packaging",
|
|
82
82
|
],
|
|
83
83
|
_makeSnapshot: MAKE_SNAPSHOT,
|
|
84
|
-
lockFileURL:
|
|
84
|
+
lockFileURL:
|
|
85
|
+
opts.pyodideLockfileUrl ??
|
|
86
|
+
`https://wasm.marimo.app/pyodide-lock.json?v=${opts.version}&pyodide=${opts.pyodideVersion}`,
|
|
85
87
|
indexURL,
|
|
86
88
|
// Since Pyodide 0.28.0, when lockFileURL is set, the package base URL
|
|
87
89
|
// defaults to the lockfile's URL (wasm.marimo.app) instead of indexURL.
|
|
88
90
|
// Unlike Node, browsers get no CDN fallback on a failed fetch, so we
|
|
89
|
-
// should pin packageBaseUrl
|
|
91
|
+
// should pin packageBaseUrl to indexURL to restore
|
|
90
92
|
// the resolution akin to pre-0.28.
|
|
91
93
|
packageBaseUrl: indexURL,
|
|
92
94
|
convertNullToNone: true,
|
|
93
95
|
});
|
|
96
|
+
if (opts.pypiIndexUrl) {
|
|
97
|
+
const micropip = pyodide.pyimport("micropip");
|
|
98
|
+
try {
|
|
99
|
+
micropip.set_index_urls(opts.pypiIndexUrl);
|
|
100
|
+
} finally {
|
|
101
|
+
micropip.destroy();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
94
104
|
this.pyodide = pyodide;
|
|
95
105
|
span.end("ok");
|
|
96
106
|
return pyodide;
|
|
@@ -20,6 +20,10 @@ export const WasmFileSystem = {
|
|
|
20
20
|
FS.chdir(HOME_DIR);
|
|
21
21
|
},
|
|
22
22
|
mountFS: (pyodide: PyodideInterface) => {
|
|
23
|
+
// Opaque origins (single-file exports) use the in-memory filesystem.
|
|
24
|
+
if (globalThis.origin === "null") {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
23
27
|
const FS = getFS(pyodide);
|
|
24
28
|
// Mount the filesystem
|
|
25
29
|
FS.mount(pyodide.FS.filesystems.IDBFS, { root: "." }, HOME_DIR);
|
|
@@ -9,9 +9,11 @@ import {
|
|
|
9
9
|
} from "rpc-anywhere";
|
|
10
10
|
import type { SaveNotebookRequest } from "@/core/network/types";
|
|
11
11
|
import { decodeUtf8 } from "@/utils/strings";
|
|
12
|
+
import { Deferred } from "../../../utils/Deferred";
|
|
12
13
|
import { prettyError } from "../../../utils/errors";
|
|
13
14
|
import { Logger } from "../../../utils/Logger";
|
|
14
15
|
import type { ParentSchema } from "../rpc";
|
|
16
|
+
import type { WasmRuntimeConfig } from "../runtime-config";
|
|
15
17
|
import { TRANSPORT_ID } from "./constants";
|
|
16
18
|
import { WasmFileSystem } from "./fs";
|
|
17
19
|
import { getController } from "./getController";
|
|
@@ -25,6 +27,8 @@ declare const self: Window & {
|
|
|
25
27
|
pyodide: PyodideInterface;
|
|
26
28
|
};
|
|
27
29
|
|
|
30
|
+
const runtimeConfig = new Deferred<WasmRuntimeConfig>();
|
|
31
|
+
|
|
28
32
|
// Initialize
|
|
29
33
|
async function loadPyodideAndPackages() {
|
|
30
34
|
try {
|
|
@@ -36,6 +40,7 @@ async function loadPyodideAndPackages() {
|
|
|
36
40
|
const controller = await getController(marimoVersion);
|
|
37
41
|
self.controller = controller;
|
|
38
42
|
self.pyodide = await controller.bootstrap({
|
|
43
|
+
...(await runtimeConfig.promise),
|
|
39
44
|
version: marimoVersion,
|
|
40
45
|
pyodideVersion: pyodideVersion,
|
|
41
46
|
});
|
|
@@ -100,6 +105,8 @@ const rpc = createRPC<SaveWorkerSchema, ParentSchema>({
|
|
|
100
105
|
requestHandler,
|
|
101
106
|
});
|
|
102
107
|
|
|
108
|
+
rpc.addMessageListener("bootstrap", (config) => runtimeConfig.resolve(config));
|
|
109
|
+
|
|
103
110
|
rpc.send("ready", {});
|
|
104
111
|
|
|
105
112
|
function getMarimoVersion() {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
import type { PyodideInterface } from "pyodide";
|
|
3
|
+
import type { WasmRuntimeConfig } from "../runtime-config";
|
|
3
4
|
import type { UserConfig } from "@/core/config/config-schema";
|
|
4
5
|
import type {
|
|
5
6
|
CommandMessage,
|
|
@@ -43,10 +44,12 @@ export interface WasmController {
|
|
|
43
44
|
* Prepare the wasm environment
|
|
44
45
|
* @param opts.version - The marimo version
|
|
45
46
|
*/
|
|
46
|
-
bootstrap(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
bootstrap(
|
|
48
|
+
opts: WasmRuntimeConfig & {
|
|
49
|
+
version: string;
|
|
50
|
+
pyodideVersion: string;
|
|
51
|
+
},
|
|
52
|
+
): Promise<PyodideInterface>;
|
|
50
53
|
/**
|
|
51
54
|
* Mount the filesystem
|
|
52
55
|
* @param opts.code - The code to mount
|
|
@@ -22,6 +22,7 @@ import { prettyError } from "../../../utils/errors";
|
|
|
22
22
|
import { invariant } from "../../../utils/invariant";
|
|
23
23
|
import { Logger } from "../../../utils/Logger";
|
|
24
24
|
import type { ParentSchema } from "../rpc";
|
|
25
|
+
import type { WasmRuntimeConfig } from "../runtime-config";
|
|
25
26
|
import { TRANSPORT_ID } from "./constants";
|
|
26
27
|
import { WasmFileSystem } from "./fs";
|
|
27
28
|
import { getController } from "./getController";
|
|
@@ -47,6 +48,8 @@ declare const self: Window & {
|
|
|
47
48
|
|
|
48
49
|
const workerInitSpan = t.startSpan("worker:init");
|
|
49
50
|
|
|
51
|
+
const runtimeConfig = new Deferred<WasmRuntimeConfig>();
|
|
52
|
+
|
|
50
53
|
// Initialize pyodide
|
|
51
54
|
async function loadPyodideAndPackages() {
|
|
52
55
|
try {
|
|
@@ -58,6 +61,7 @@ async function loadPyodideAndPackages() {
|
|
|
58
61
|
message: "Loading marimo...",
|
|
59
62
|
});
|
|
60
63
|
self.pyodide = await t.wrapAsync(controller.bootstrap.bind(controller))({
|
|
64
|
+
...(await runtimeConfig.promise),
|
|
61
65
|
version: marimoVersion,
|
|
62
66
|
pyodideVersion: pyodideVersion,
|
|
63
67
|
});
|
|
@@ -354,6 +358,8 @@ const rpc = createRPC<WorkerSchema, ParentSchema>({
|
|
|
354
358
|
requestHandler,
|
|
355
359
|
});
|
|
356
360
|
|
|
361
|
+
rpc.addMessageListener("bootstrap", (config) => runtimeConfig.resolve(config));
|
|
362
|
+
|
|
357
363
|
rpc.send("ready", {});
|
|
358
364
|
|
|
359
365
|
/// Listeners
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { createModuleWorker } from "../worker";
|
|
4
|
+
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
vi.unstubAllGlobals();
|
|
7
|
+
vi.restoreAllMocks();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe("createModuleWorker", () => {
|
|
11
|
+
it.each(["http", "blob"])(
|
|
12
|
+
"preserves caller-owned %s worker URLs",
|
|
13
|
+
(scheme) => {
|
|
14
|
+
const Worker = vi.fn(function (
|
|
15
|
+
_url: string | URL,
|
|
16
|
+
_options: WorkerOptions,
|
|
17
|
+
) {
|
|
18
|
+
return {};
|
|
19
|
+
});
|
|
20
|
+
vi.stubGlobal("Worker", Worker);
|
|
21
|
+
const url = new URL(
|
|
22
|
+
scheme === "blob"
|
|
23
|
+
? `blob:${location.origin}/worker`
|
|
24
|
+
: new URL("/worker.js", location.href),
|
|
25
|
+
);
|
|
26
|
+
const revoke = vi.spyOn(URL, "revokeObjectURL");
|
|
27
|
+
createModuleWorker(url, { name: "marimo" });
|
|
28
|
+
expect(Worker).toHaveBeenCalledWith(url.href, {
|
|
29
|
+
type: "module",
|
|
30
|
+
name: "marimo",
|
|
31
|
+
});
|
|
32
|
+
expect(revoke).not.toHaveBeenCalled();
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
it("preserves the page origin for hosted CDN workers", () => {
|
|
37
|
+
const Worker = vi.fn(function (
|
|
38
|
+
_url: string | URL,
|
|
39
|
+
_options: WorkerOptions,
|
|
40
|
+
) {
|
|
41
|
+
return {};
|
|
42
|
+
});
|
|
43
|
+
vi.stubGlobal("Worker", Worker);
|
|
44
|
+
const create = vi
|
|
45
|
+
.spyOn(URL, "createObjectURL")
|
|
46
|
+
.mockReturnValue("blob:worker");
|
|
47
|
+
const revoke = vi
|
|
48
|
+
.spyOn(URL, "revokeObjectURL")
|
|
49
|
+
.mockImplementation(() => undefined);
|
|
50
|
+
createModuleWorker(new URL("https://cdn.example.com/worker.js"), {
|
|
51
|
+
name: "islands",
|
|
52
|
+
});
|
|
53
|
+
expect(create).toHaveBeenCalledWith(expect.any(Blob));
|
|
54
|
+
expect(Worker).toHaveBeenCalledWith("blob:worker", {
|
|
55
|
+
type: "module",
|
|
56
|
+
name: "islands",
|
|
57
|
+
});
|
|
58
|
+
expect(revoke).toHaveBeenCalledWith("blob:worker");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("imports a CDN worker from an opaque origin", () => {
|
|
62
|
+
const Worker = vi.fn(function (
|
|
63
|
+
_url: string | URL,
|
|
64
|
+
_options: WorkerOptions,
|
|
65
|
+
) {
|
|
66
|
+
return {};
|
|
67
|
+
});
|
|
68
|
+
vi.stubGlobal("Worker", Worker);
|
|
69
|
+
vi.stubGlobal("origin", "null");
|
|
70
|
+
const url = new URL("https://cdn.example.com/worker.js");
|
|
71
|
+
createModuleWorker(url, { name: "0.24.2::controller" });
|
|
72
|
+
const [source, options] = Worker.mock.calls[0];
|
|
73
|
+
expect(decodeURIComponent(String(source))).toBe(
|
|
74
|
+
'data:application/javascript,import "https://cdn.example.com/worker.js"',
|
|
75
|
+
);
|
|
76
|
+
expect(options).toEqual({
|
|
77
|
+
type: "module",
|
|
78
|
+
name: "0.24.2::controller",
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/** Load a module worker across origins, including from file:// pages. */
|
|
4
|
+
export function createModuleWorker(url: URL, options: WorkerOptions): Worker {
|
|
5
|
+
if (url.origin === globalThis.origin) {
|
|
6
|
+
return new Worker(url.href, { ...options, type: "module" });
|
|
7
|
+
}
|
|
8
|
+
const js = `import ${JSON.stringify(url.href)}`;
|
|
9
|
+
// Blob workers preserve the page origin, but file:// needs a data URL.
|
|
10
|
+
const source =
|
|
11
|
+
globalThis.origin === "null"
|
|
12
|
+
? `data:application/javascript,${encodeURIComponent(js)}`
|
|
13
|
+
: URL.createObjectURL(new Blob([js], { type: "application/javascript" }));
|
|
14
|
+
try {
|
|
15
|
+
return new Worker(source, { ...options, type: "module" });
|
|
16
|
+
} finally {
|
|
17
|
+
if (source.startsWith("blob:")) {
|
|
18
|
+
// Worker() captures the Blob before returning, even while imports load.
|
|
19
|
+
URL.revokeObjectURL(source);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./worker-BXMsnprR.js";var t=e(((e,t)=>{t.exports={}}));export default t();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./save-worker-BstmibH4.js";var t=e(((e,t)=>{t.exports={}}));export default t();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{Ka as e}from"./index-DKfkO1Lu.js";var t=Object.prototype.hasOwnProperty;function n(e,n){return e!=null&&t.call(e,n)}function r(t,r){return t!=null&&e(t,r,n)}export{r as t};
|