Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
/**
* Worker factory for the Editor AI assistant.
*
* The Editor window owns spawning its agent worker so the URL literal
* stays statically analyzable for Vite's worker bundling.
* The Editor window owns spawning its agent worker. We import the worker
* via `?worker&url` so Vite still runs it through the worker build pipeline
* (applying the `worker.plugins` shims), then hand the URL to
* `createCrossOriginWorker` which tolerates CDN-hosted (cross-origin) scripts.
*/
import editorWorkerUrl from "@/agent/editorWorker.ts?worker&url";
import { createCrossOriginWorker } from "@/utils/createCrossOriginWorker";

export function createEditorAgentWorker(): Worker {
return new Worker(new URL("@/agent/editorWorker.ts", import.meta.url), {
return createCrossOriginWorker(editorWorkerUrl, {
type: "module",
name: "tangle-editor-agent",
});
Expand Down
11 changes: 8 additions & 3 deletions src/routes/v2/pages/RunView/toolBridge/runViewAgentWorker.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
/**
* Worker factory for the Run View AI assistant.
*
* The Run View window owns spawning its agent worker so the URL literal
* stays statically analyzable for Vite's worker bundling.
* The Run View window owns spawning its agent worker. We import the worker
* via `?worker&url` so Vite still runs it through the worker build pipeline
* (applying the `worker.plugins` shims), then hand the URL to
* `createCrossOriginWorker` which tolerates CDN-hosted (cross-origin) scripts.
*/
import runViewWorkerUrl from "@/agent/runViewWorker.ts?worker&url";
import { createCrossOriginWorker } from "@/utils/createCrossOriginWorker";

export function createRunViewAgentWorker(): Worker {
return new Worker(new URL("@/agent/runViewWorker.ts", import.meta.url), {
return createCrossOriginWorker(runViewWorkerUrl, {
type: "module",
name: "tangle-run-view-agent",
});
Expand Down
40 changes: 40 additions & 0 deletions src/utils/createCrossOriginWorker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Constructs a module Web Worker that works even when the worker script is
* served from a different origin than the page (e.g. JS bundles on a CDN
* while the document is on the app origin).
*
* Browsers fetch the top-level worker script in `same-origin` mode, so a
* cross-origin script URL is rejected with a `DOMException` thrown
* synchronously inside `new Worker`. The fix is to build the worker from a
* same-origin `blob:` URL whose only content is an ESM `import` of the real
* (cross-origin) worker script. The blob inherits the page origin, satisfying
* the same-origin check, and the cross-origin module import is permitted by
* CORS. The worker's own relative imports keep resolving against the CDN.
*
* See https://github.com/vitejs/vite/issues/12662 and
* https://github.com/vitejs/vite/issues/13680.
*/
export function createCrossOriginWorker(
workerUrl: string,
options?: WorkerOptions,
): Worker {
const url = new URL(workerUrl, import.meta.url);
const workerOptions: WorkerOptions = { type: "module", ...options };

if (url.origin === self.location.origin) {
return new Worker(url, workerOptions);
}

const source = `import ${JSON.stringify(url.href)};`;
const blob = new Blob([source], { type: "text/javascript" });
const blobUrl = URL.createObjectURL(blob);
const worker = new Worker(blobUrl, workerOptions);

// The blob only needs to live until the worker has fetched it. Revoke on
// error immediately, and best-effort after construction, to avoid leaking
// object URLs.
worker.addEventListener("error", () => URL.revokeObjectURL(blobUrl));
setTimeout(() => URL.revokeObjectURL(blobUrl), 0);

return worker;
}
14 changes: 14 additions & 0 deletions vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ export default defineConfig(({ mode }) => {
: []),
],
base: "/",
experimental: {
// Deploy base is unknown at build time: production serves assets from
// the origin root, tophat from a CDN subpath. Emit in-bundle asset
// URLs (notably the `?worker&url` scripts) as `import.meta.url`-relative
// so they resolve against the chunk location instead of an absolute
// `/assets/...` path that drops the tophat prefix. See
// `src/utils/createCrossOriginWorker.ts`.
renderBuiltUrl(filename, { hostType }) {
if (hostType === "js") {
return { relative: true };
}
return { relative: false };
},
},
build: {
manifest: "assets-registry.json",
sourcemap: "hidden",
Expand Down
Loading