What happened?
When crossOriginIsolated is true and web-ifc is loaded as an ES module (i.e. through any modern bundler — Vite, Rollup, esbuild, webpack ESM), IfcAPI.Init() never completes the multi-threaded path. Since #1946 it no longer hangs forever, but it always burns the full MT_INIT_TIMEOUT_MS (10 s) and then falls back to single-threaded — so multi-threading is effectively unavailable to ESM consumers.
Root cause — two independent defects
1. The pthread worker receives undefined as its script URL.
The generated bootstrap derives its own script URL from document.currentScript, which is always null for ES modules per spec:
// dist/web-ifc-api.js (0.0.77)
_scriptName = globalThis.document?.currentScript?.src // undefined for ES modules
That produces new Worker(undefined). The browser resolves it to the literal path /undefined, receives the app's HTML 404 page, and the worker dies with:
Uncaught SyntaxError: Unexpected token '<'
2. The pthread worker is not created as a module.
// dist/web-ifc-api.js (0.0.77)
new Worker(pthreadMainJs, { name: "em-pthread" }) // no { type: "module" }
Even with a correct URL it then fails to parse ESM content:
Uncaught SyntaxError: Cannot use import statement outside a module
Defect 1 is fixable in the hand-written TypeScript (PR below). Defect 2 lives in Emscripten-generated glue code and can only be addressed from the build/toolchain side.
Relation to existing tickets
This is not #1702 being left unfixed — #1779 correctly fixed that one. It concerned a separate web-ifc-mt.worker.js resolved through locateFile. Since then the Emscripten output changed: 0.0.77 ships no separate worker file at all. The pthread worker now bootstraps from the main script URL, a path that locateFile does not govern. So the same user-visible symptom returned through a different mechanism.
Possibly relevant: #1779 also dropped --cors.opener-policy same-origin --cors.embedder-policy require-cor from the dev script, so the local dev server no longer runs cross-origin-isolated and the MT path isn't exercised there anymore. Restoring those flags (or a small CI check) would likely catch this class of regression.
Proposed fix for defect 1
Pass mainScriptUrlOrBlob: import.meta.url into the WebIFCWasm({...}) options. The generated bootstrap already prefers mainScriptUrlOrBlob over document.currentScript, and import.meta.url is correctly populated for ES modules.
Reproduction
- Any Vite (or other ESM bundler) app importing
web-ifc.
- Serve it with COOP/COEP headers so that
crossOriginIsolated === true.
- Call
IfcAPI.Init().
- Observe the worker 404 /
SyntaxError in the console, then the 10 s timeout and fallback to single-threaded.
Version
0.0.77 (current npm latest)
What browsers are you seeing the problem on?
Chrome (Windows)
What happened?
When
crossOriginIsolatedistrueandweb-ifcis loaded as an ES module (i.e. through any modern bundler — Vite, Rollup, esbuild, webpack ESM),IfcAPI.Init()never completes the multi-threaded path. Since #1946 it no longer hangs forever, but it always burns the fullMT_INIT_TIMEOUT_MS(10 s) and then falls back to single-threaded — so multi-threading is effectively unavailable to ESM consumers.Root cause — two independent defects
1. The pthread worker receives
undefinedas its script URL.The generated bootstrap derives its own script URL from
document.currentScript, which is alwaysnullfor ES modules per spec:That produces
new Worker(undefined). The browser resolves it to the literal path/undefined, receives the app's HTML 404 page, and the worker dies with:2. The pthread worker is not created as a module.
Even with a correct URL it then fails to parse ESM content:
Defect 1 is fixable in the hand-written TypeScript (PR below). Defect 2 lives in Emscripten-generated glue code and can only be addressed from the build/toolchain side.
Relation to existing tickets
This is not #1702 being left unfixed — #1779 correctly fixed that one. It concerned a separate
web-ifc-mt.worker.jsresolved throughlocateFile. Since then the Emscripten output changed: 0.0.77 ships no separate worker file at all. The pthread worker now bootstraps from the main script URL, a path thatlocateFiledoes not govern. So the same user-visible symptom returned through a different mechanism.document.currentScriptexception, but turned it into a silentundefined.Possibly relevant: #1779 also dropped
--cors.opener-policy same-origin --cors.embedder-policy require-corfrom thedevscript, so the local dev server no longer runs cross-origin-isolated and the MT path isn't exercised there anymore. Restoring those flags (or a small CI check) would likely catch this class of regression.Proposed fix for defect 1
Pass
mainScriptUrlOrBlob: import.meta.urlinto theWebIFCWasm({...})options. The generated bootstrap already prefersmainScriptUrlOrBloboverdocument.currentScript, andimport.meta.urlis correctly populated for ES modules.Reproduction
web-ifc.crossOriginIsolated === true.IfcAPI.Init().SyntaxErrorin the console, then the 10 s timeout and fallback to single-threaded.Version
0.0.77 (current npm latest)
What browsers are you seeing the problem on?
Chrome (Windows)