Skip to content

Fix Monaco workers failing in cross-origin deployments #4963

Merged
jurgenwerk merged 5 commits into
mainfrom
cs-11095-monaco-workers-fail-in-deployments-due-to-cross-origin-host
May 27, 2026
Merged

Fix Monaco workers failing in cross-origin deployments #4963
jurgenwerk merged 5 commits into
mainfrom
cs-11095-monaco-workers-fail-in-deployments-due-to-cross-origin-host

Conversation

@jurgenwerk
Copy link
Copy Markdown
Contributor

@jurgenwerk jurgenwerk commented May 26, 2026

image

Summary

In deployed environments the host bundle (e.g. boxel-host.stack.cards) is served from a different origin than the realm-server page that boots it (realms.stack.cards). Vite's ?worker import emits a Worker constructor pointed at an absolute cross-origin URL, and new Worker(crossOriginUrl) is forbidden by the browser. As a result Monaco's JSON, TS, CSS, and HTML language services fail to spin up — silently degrading code-mode (no JSON diagnostics on .json card-instance files, no TS IntelliSense on .gts, no formatting) and surfacing a TypeError: Cannot read properties of undefined (reading 'toUrl') from Monaco's same-thread fallback path. Dev/CI is on localhost for both origins so the bug only manifests on staging/prod.

Fix

packages/host/app/services/monaco-service.ts:

  • Switch the 5 Monaco worker imports from ?worker (Worker constructor) to ?worker&url (URL string).
  • Route them through a new makeMonacoWorker(url) helper:
    • Same-origin: new Worker(url) as before — keeps dev/CI behavior identical.
    • Cross-origin: wrap the URL in a same-origin Blob that immediately importScripts(...) the real worker code. Workers spawned from a Blob URL adopt the page's origin, and importScripts is allowed to fetch cross-origin without CORS, so the bundled worker code still loads.

No vite-config or realm-server changes required — the existing experimental.renderBuiltUrl still resolves the worker URL against __boxelAssetsURL, and the helper handles the cross-origin case from that.

Linear: https://linear.app/cardstack/issue/CS-11095/monaco-workers-fail-in-deployments-due-to-cross-origin-host-bundle

Test plan

  • pnpm run lint:types clean (verified locally).
  • Deploy to staging and open a .json card-instance file in code mode.
  • Browser console: no Failed to construct 'Worker' warnings, no FileAccessImpl.toUri TypeErrors.
  • JSON diagnostics fire on a .json card instance, TS IntelliSense fires on a .gts file, formatting works.
  • Dev/CI behavior unchanged (same-origin path — verify host tests still pass).

🤖 Generated with Claude Code

jurgenwerk and others added 2 commits May 26, 2026 12:40
In deployed environments the host bundle is served from a different
origin than the realm-server page that boots it, so the worker URLs
emitted by Vite's `?worker` import are cross-origin. `new Worker(url)`
rejects cross-origin URLs, which knocks out Monaco's JSON, TS, CSS, and
HTML language services and surfaces a `FileAccessImpl.toUri` TypeError
from the same-thread fallback.

Switch the 5 Monaco worker imports to `?worker&url` and route them
through a `makeMonacoWorker` helper. Same-origin (dev/CI) constructs
the Worker directly; cross-origin wraps the URL in a same-origin Blob
that `importScripts` the real worker code — workers spawned from a
Blob URL adopt the page's origin, and `importScripts` is allowed to
fetch cross-origin without CORS.

No vite-config or realm-server changes required.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jurgenwerk jurgenwerk marked this pull request as ready for review May 26, 2026 10:54
@jurgenwerk jurgenwerk marked this pull request as draft May 26, 2026 10:54
@jurgenwerk jurgenwerk closed this May 26, 2026
@jurgenwerk jurgenwerk reopened this May 26, 2026
@jurgenwerk jurgenwerk changed the title Fix Monaco workers failing in cross-origin deployments (CS-11095) Fix Monaco workers failing in cross-origin deployments May 26, 2026
@jurgenwerk jurgenwerk marked this pull request as ready for review May 26, 2026 13:02
@jurgenwerk jurgenwerk marked this pull request as draft May 26, 2026 13:02
@jurgenwerk jurgenwerk closed this May 26, 2026
@jurgenwerk jurgenwerk reopened this May 26, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 79b57991c4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

});
return new Worker(URL.createObjectURL(blob));
}
return new Worker(absolute.href);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Instantiate same-origin Monaco workers as module workers

This branch now creates workers with new Worker(absolute.href) (classic by default), but these Monaco worker entries are imported via Vite’s worker pipeline and are expected to run with module semantics in dev/same-origin environments. Losing the worker type: 'module' can cause the worker script to fail parsing/booting (for example when ESM syntax is present), which disables Monaco language services locally and in CI while this code path is taken.

Useful? React with 👍 / 👎.

let blob = new Blob([`importScripts(${JSON.stringify(absolute.href)});`], {
type: 'text/javascript',
});
return new Worker(URL.createObjectURL(blob));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Revoke Blob URL after creating cross-origin worker

Each cross-origin worker creation allocates a new blob URL via URL.createObjectURL(blob) but never revokes it. If Monaco workers are recreated repeatedly (editor reloads, route transitions, hot reload), these unreleased object URLs accumulate for the lifetime of the page and increase memory usage. Capture the blob URL, construct the worker, then call URL.revokeObjectURL(...) once the worker is created.

Useful? React with 👍 / 👎.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 26, 2026

Preview deployments

Host Test Results

    1 files      1 suites   1h 37m 19s ⏱️
2 754 tests 2 739 ✅ 15 💤 0 ❌
2 773 runs  2 758 ✅ 15 💤 0 ❌

Results for commit 4907fde.

Realm Server Test Results

    1 files  ±0      1 suites  ±0   10m 22s ⏱️ +34s
1 500 tests +9  1 500 ✅ +9  0 💤 ±0  0 ❌ ±0 
1 591 runs  +9  1 591 ✅ +9  0 💤 ±0  0 ❌ ±0 

Results for commit 4907fde. ± Comparison against earlier commit 2bc7eb8.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses Monaco language-service workers failing to start when the host bundle is served from a different origin than the realm-server page (cross-origin deployments), which breaks JSON/TS/CSS/HTML diagnostics and IntelliSense in code mode.

Changes:

  • Switched Monaco worker imports from Vite’s ?worker (constructor) to ?worker&url (URL string).
  • Added a makeMonacoWorker(url) helper that creates workers directly for same-origin URLs and uses a same-origin Blob shim (with importScripts) for cross-origin worker URLs.
  • Updated MonacoEnvironment.getWorker to use makeMonacoWorker for all Monaco worker labels.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/host/app/services/monaco-service.ts Outdated
jurgenwerk and others added 2 commits May 26, 2026 15:47
…hese object URLs can accumulate and leak memory for the lifetime of the page

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@jurgenwerk
Copy link
Copy Markdown
Contributor Author

jurgenwerk commented May 27, 2026

After deploying this branch to staging- I confirm this is fixed and the error is gone.

@jurgenwerk jurgenwerk marked this pull request as ready for review May 27, 2026 08:20
@jurgenwerk jurgenwerk requested a review from a team May 27, 2026 08:20
@jurgenwerk jurgenwerk merged commit 295100e into main May 27, 2026
126 of 128 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants