VS_CODE_MONACO_FIRST_E2E_DEBRIEF_1

VS Code → Monaco E2E: Debrief 1

Date: 2026-04-14 Branch: vs_code_integration (galaxy-tool-util-ts) Companion: VS_CODE_MONACO_FIRST_E2E_PLAN.md Status: Phase-1 landed + 2 Monaco specs authored; boot spec boots live, hover spec blocked on an upstream integration issue.

Where to start next session

Open with: packages/gxwf-ui/src/editor/extensionSource.ts + the extension host iframe (packages/gxwf-ui/public/monaco/webWorkerExtensionHostIframe.html).

The live blocker is that the extension host (running inside the monaco-vscode-api iframe at /monaco/webWorkerExtensionHostIframe.html) cannot fetch the blob: URLs that the vsix: loader produces in the parent window. Console from the last failing run:

ext-host fetch threw blob:http://127.0.0.1:.../<uuid> Failed to fetch
Activating extension 'davelopez.galaxy-workflows' failed: Failed to fetch
Client Galaxy Workflows (galaxyworkflow): connection to server is erroring.

CSP is already permissive enough (we verified — connect-src allows blob: in both the main CSP and the iframe’s meta CSP after our patches). The failure is a blob-lifetime / cross-context issue: blob URLs created via URL.createObjectURL in window A aren’t always retrievable from a worker launched in iframe B, even same-origin. Chrome has been tightening this.

Two candidate fixes, listed in order of preference:

  1. Stop using blob: for extension files. Change loadFromFiles in extensionSource.ts to register files via data: URLs (base64) for small files and a static in-memory cache for larger ones. registerFileUrl(rel, dataUrl) should work regardless of which context does the fetch. Trade-off: data: URLs are larger on the wire but irrelevant for an in-process extension.
  2. Stage the .vsix unpacked. scripts/stage-extension.mjs could unzip the .vsix into public/ext/galaxy-workflows/ and we register files as ordinary HTTP paths. Simplest, highest-fidelity to what the extension was built for, but requires an extra build step and a directory-listing source (already the folder: loader’s job — just need to unify).

Either fix should let monaco-hover.spec.ts go green and re-enable LSP.

Also retry first: monaco-boot.spec.ts wasn’t re-run after the registerFileSystemOverlay fix landed — it should now pass end-to-end. If it does, that’s the evidence the CSP + init work actually unblocks boot; record that before diving into the blob issue.

What landed

Commit on vs_code_integration:

AreaFileChange
CSPpackages/gxwf-web/src/router.tsNew buildMonacoCspHeader for /monaco/* paths (inline+eval scripts allowed); main CSP connect-src now includes blob: + data:
Monaco iframepackages/gxwf-ui/scripts/copy-monaco-iframe.mjsPatches the staged iframe’s meta CSP to add blob: to connect-src
Servicespackages/gxwf-ui/src/editor/fileSystem.tsregisterCustomProviderregisterFileSystemOverlay (post-init-safe)
Routingpackages/gxwf-ui/src/views/FileView.vueReads :path route param, syncs URL ↔ selection
E2E harnesspackages/gxwf-e2e/src/global-setup.tsDetects packages/gxwf-ui/fixtures/galaxy-workflows.vsix; sets VITE_GXWF_MONACO=1 / VITE_GXWF_EXT_SOURCE=vsix:/ext/galaxy-workflows.vsix / VITE_GXWF_EXPOSE_MONACO=1 for the child build; exports GXWF_E2E_MONACO=1 to the test process
E2E helperspackages/gxwf-e2e/src/monaco.ts, src/locators.tswaitForMonaco, getMonacoValue, typeInMonaco, triggerHoverAt, collectCspViolations, openFileViaUrl, MONACO_ENABLED, SKIP_REASON, Monaco locator object
E2E specstests/monaco-boot.spec.ts, tests/monaco-hover.spec.tsSelf-skip via GXWF_E2E_MONACO; boot spec covers Phases 1/2/4.5 (editor count, language id, CSP cleanliness, leak check); hover spec covers Phases 1/2/4 (trigger editor.action.showHover on class:)
Docspackages/gxwf-e2e/README.mdFixture recipe restated

make check green, gxwf-web vitest green (88 tests), existing Playwright e2e suite green (7 tests). Monaco specs self-skip on fresh clones.

Lessons learned

What the plan got right

What the plan underestimated

What went wrong mechanically

Concrete harness improvements for the next session

  1. Have global-setup.ts build gxwf-web too (or fail loudly if gxwf-web/dist/router.js is older than gxwf-web/src/router.ts). The harness currently only builds gxwf-ui.
  2. Add a monaco:probe dev target — a throwaway page that mounts MonacoEditor standalone and dumps window.__gxwfMonaco, loaded extensions, and recent console errors to the DOM. Useful for diagnosing integration issues without running Playwright at all.
  3. Add a vitest integration spec (packages/gxwf-ui/test/editor/mount.test.ts) that mounts MonacoEditor in jsdom with worker/monaco-env shims and asserts the mount-to-ready path. This would have caught the registerCustomProvider regression before the E2E spec surfaced it.
  4. Treat V2 §<phase> “open item” tags as gates on E2E landing order. Don’t write a spec whose dependency is an open item; file the open item as a blocker task first.

Unresolved questions (to flag before next session)

  1. For the blob fetch fix: is the project willing to ship data: URLs (larger bundle, simpler code) or do we want the staged-vsix-extract approach (more code, closer to upstream folder: loader)?
  2. Should gxwf-web’s global-setup now build gxwf-web too, or should the @galaxy-tool-util/gxwf-web package switch to importing src/ directly in dev (no dist step for e2e)?
  3. monaco-fallback.spec.ts — the user already said to skip this; debrief-worthy because the plan’s §109 “second Playwright project” machinery is non-trivial and we’d want to avoid building it in parallel with the blob fix. Defer or drop.
  4. Are the CSP widenings shippable as-is (blob:/data: in main connect-src) or do we want a stricter variant gated behind VITE_GXWF_MONACO=1 at the server level? Current impl is unconditional.