This is an automated email from the ASF dual-hosted git repository.
vatsrahul1001 pushed a commit to branch v3-2-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 04d5f2e599034b02222b95c0797fd3b70df90960
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon May 25 19:06:40 2026 +0530
[v3-2-test] UI: Load Monaco workers via a same-origin Blob shim (#67352)
(#67469)
In dev mode the SPA shell is served by the airflow api-server while
Vite serves assets on a different origin. Module workers cannot be
constructed cross-origin even when CORS allows the fetch — Chrome
refuses with 'Script at <vite url> cannot be accessed from origin
<api-server>'. Monaco then falls back to 'main-thread workers', whose
`$loadForeignModule` path tries to resolve module IDs through the
non-existent AMD `require.toUrl`, flooding the console with
`Cannot read properties of undefined (reading 'toUrl')` errors.
Swap the `?worker` constructor imports for `?url` imports and
construct workers from a same-origin Blob shim that simply re-imports
the resolved URL. The Blob lives on the page origin so the Worker
constructor accepts it, and the inner cross-origin `import` is
permitted by Vite's CORS response.
Closes #67342.
(cherry picked from commit 68629ffd9fc63978e0d39dde16cb4f3890d06af3)
Co-authored-by: Pierre Jeambrun <[email protected]>
Co-authored-by: Rahul Vats <[email protected]>
---
.../src/components/MonacoEditor/configureMonaco.ts | 30 ++++++++++++++++------
1 file changed, 22 insertions(+), 8 deletions(-)
diff --git
a/airflow-core/src/airflow/ui/src/components/MonacoEditor/configureMonaco.ts
b/airflow-core/src/airflow/ui/src/components/MonacoEditor/configureMonaco.ts
index de8b8b0ebc6..2ad53eb09a6 100644
--- a/airflow-core/src/airflow/ui/src/components/MonacoEditor/configureMonaco.ts
+++ b/airflow-core/src/airflow/ui/src/components/MonacoEditor/configureMonaco.ts
@@ -35,9 +35,15 @@ const loadMonacoModules = async () => {
import("monaco-editor/esm/vs/base/browser/ui/codicons/codiconStyles"),
]).then(([api]) => api);
- const workerConstructors = Promise.all([
- import("monaco-editor/esm/vs/editor/editor.worker?worker").then((module)
=> module.default),
-
import("monaco-editor/esm/vs/language/json/json.worker?worker").then((module)
=> module.default),
+ // Resolve the workers as plain URLs (not Vite `?worker` constructors). In
dev mode
+ // the SPA shell is served by the airflow api-server while Vite serves
assets on a
+ // different origin, and `new Worker(crossOriginUrl, { type: "module" })` is
rejected
+ // by the browser. Wrapping the cross-origin URL in a same-origin Blob shim
that just
+ // re-imports it sidesteps the restriction (CORS still permits the inner
import). In
+ // production the worker is same-origin and the shim is harmless.
+ const workerUrls = Promise.all([
+ import("monaco-editor/esm/vs/editor/editor.worker.js?url").then((module)
=> module.default),
+
import("monaco-editor/esm/vs/language/json/json.worker.js?url").then((module)
=> module.default),
]);
const languageContributions = Promise.all([
@@ -45,13 +51,21 @@ const loadMonacoModules = async () => {
import("monaco-editor/esm/vs/language/json/monaco.contribution"),
]);
- const [monaco, [editorWorker, jsonWorker]] = await Promise.all([
+ const [monaco, [editorWorkerUrl, jsonWorkerUrl]] = await Promise.all([
monacoApi,
- workerConstructors,
+ workerUrls,
languageContributions,
]);
- return { editorWorker, jsonWorker, monaco };
+ return { editorWorkerUrl, jsonWorkerUrl, monaco };
+};
+
+const createWorkerFromUrl = (workerUrl: string): Worker => {
+ const absoluteUrl = new URL(workerUrl, import.meta.url).href;
+ const shim = `import ${JSON.stringify(absoluteUrl)};`;
+ const blobUrl = URL.createObjectURL(new Blob([shim], { type:
"text/javascript" }));
+
+ return new Worker(blobUrl, { type: "module" });
};
export const configureMonaco = () => {
@@ -60,10 +74,10 @@ export const configureMonaco = () => {
}
configurationPromise = loadMonacoModules()
- .then(({ editorWorker, jsonWorker, monaco }) => {
+ .then(({ editorWorkerUrl, jsonWorkerUrl, monaco }) => {
Reflect.set(globalThis, "MonacoEnvironment", {
getWorker: (_moduleId: string, label: string) =>
- label === "json" ? new jsonWorker() : new editorWorker(),
+ createWorkerFromUrl(label === "json" ? jsonWorkerUrl :
editorWorkerUrl),
} satisfies MonacoEnvironment);
loader.config({ monaco });