fix(client): static import map fixes Firefox 150 bundle load failures#175
Merged
Merged
Conversation
Production Firefox 150 fails to load every Bifrost app with "bare specifier was not remapped" because Firefox 150 ships multiple-import-maps support gated behind dom.multiple_import_maps.enabled (default off), enforcing the legacy single-map rule strictly. Any runtime <script type="importmap"> registered after the module loader has started is rejected. main.tsx's top-level imports run before initReactShim(), so the runtime map (post-jackmusick#165 single map; pre-jackmusick#165 two maps) is always too late in Firefox 150. Chrome 133+ ships the same multi-map feature on, which is why it merged the late map silently. Move the platform import map from runtime JS injection to a static <script type="importmap"> in client/index.html. The map URLs point to small ESM stub files generated by a Vite plugin (client/src/build-plugins/bifrost-module-stubs.ts) that re-export from globalThis.__bifrost_* keys populated by initReactShim() at boot. User-declared deps continue to be resolved at runtime, but only when an app actually has them. In that case we lazy-load es-module-shims in shim mode (where late importmap-shim registration is allowed). Apps with no user deps load with zero polyfill cost. A drift test (client/src/lib/platform-modules.test.ts) pins the contract between the registry, the shim, the static HTML map, and the stub-generator so future additions can't drift silently. App bundler is unchanged. Existing S3 bundles still load. No SCHEMA_VERSION bump, no cache invalidation, no risk to deployed apps. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolved conflict in BundledAppShell.tsx by keeping main's `setDefaultAppScope` import (added by jackmusick#184) and dropping the no-longer-used `platformScope` import (this branch removed the `setPlatformScope()` call entirely in favor of populating globalThis.__bifrost_* via initReactShim() at startup). No semantic changes — just import-list reconciliation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jackmusick
approved these changes
May 4, 2026
Owner
jackmusick
left a comment
There was a problem hiding this comment.
APPROVE — static import map for Firefox 150.
What I checked
- Drift test (
platform-modules.test.ts) — 15/15 pass. The test pins all four corners of the contract: registry validity,stubUrlForfilename stability, static importmap completeness + ordering before any module script, and the shim assigning every globalKey. This is the right safety net for a fix whose failure mode is silent (specifier resolution falling back). - Production build — green; 7 stub assets emitted under
dist/__bifrost_modules/(one per platform module). Stub bodies look correct:globalThis.__bifrost_react→export default m.default ?? m; export const X = m["X"];with a clear error if the global is unpopulated. - Full vitest — 943/943 pass on the merged tip.
- Conflict resolution on merge with main —
BundledAppShell.tsxhad an import-list conflict with #184'ssetDefaultAppScopeaddition. Resolved by keeping #184's import and dropping the no-longer-usedplatformScopeimport (this PR removedsetPlatformScope()entirely in favor of populatingglobalThis.__bifrost_*at boot viainitReactShim()). Pushed as commit 313e03e on your branch. - Architectural fit — moving from runtime blob-URL maps to static-HTML map is the right answer here. Apps with no user deps now pay zero polyfill cost. User-dep apps lazy-load es-module-shims and use shim mode for late importmap registration; the user-dep map duplicates the platform keys so shim-mode modules can resolve them too. Same import-map behavior cross-browser (Firefox 150 strict mode and Chrome multi-map both work).
Notes
- The
REACT_EXTERNALSconstant continues to keep esm.sh from bundling its own React, preserving the single-instance invariant. registeredUserDepMapsSet guards against re-registering the same shim-mode map for the same dep set.
Merging.
4 tasks
jackmusick
added a commit
that referenced
this pull request
May 4, 2026
…tag) (#190) ## Summary - `https://ga.jspm.io/npm:es-module-shims@2/dist/es-module-shims.js` returns **HTTP 404** — JSPM's CDN doesn't resolve floating-tag specifiers, only exact versions. - Every app with user-declared deps (the `hasUserDeps` branch added in #175) failed to load with `Failed to load es-module-shims from ...`. - Switched to jsDelivr — the CDN the es-module-shims docs themselves use — pinned to `2.8.0` for reproducible loads. ## Test plan - [x] `npm run tsc` clean - [x] `eslint` clean on changed file - [ ] In Firefox, open a Bifrost app with at least one user-declared dependency and confirm the bundle loads without the prior error - [ ] In Chrome, confirm apps with no user deps still take the native-import path (no shim fetch) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jackmusick
added a commit
that referenced
this pull request
May 6, 2026
Pulls in the post-#177 main commits since the first merge: external MCP client (#177), pagination tiebreaker (#189), CLI --json flag position (#189), Firefox bundle load fix (#175), browser table SDK polish (#184), es-module-shims jsDelivr fix (#190), drop app.yaml (#191). Conflicts resolved (all parallel-add — keep both sides): - api/src/main.py — both branches added new routers; concatenated - api/src/routers/__init__.py — same parallel router exports - client/src/lib/v1.d.ts — regenerated from merged OpenAPI surface - client/tsconfig.app.tsbuildinfo — build artifact, kept ours Alembic merge migration updated: 20260504_merge_main_chat_v2 now points to (20260503_agent_mcp_grants, 20260428_chat_v2_m2) since main's tip moved past 20260504_backfill_table_access via 20260502_external_mcp.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Production Firefox 150 fails to load every Bifrost app with "bare specifier was not remapped" because Firefox 150 ships multiple-import-maps support gated behind dom.multiple_import_maps.enabled (default off), enforcing the legacy single-map rule strictly. Any runtime <script type="importmap"> registered after the module loader has started is rejected. main.tsx's top-level imports run before initReactShim(), so the runtime map (post-#165 single map; pre-#165 two maps) is always too late in Firefox 150. Chrome 133+ ships the same multi-map feature on, which is why it merged the late map silently.
Move the platform import map from runtime JS injection to a static <script type="importmap"> in client/index.html. The map URLs point to small ESM stub files generated by a Vite plugin
(client/src/build-plugins/bifrost-module-stubs.ts) that re-export from globalThis._bifrost* keys populated by initReactShim() at boot.
User-declared deps continue to be resolved at runtime, but only when an app actually has them. In that case we lazy-load es-module-shims in shim mode (where late importmap-shim registration is allowed). Apps with no user deps load with zero polyfill cost.
A drift test (client/src/lib/platform-modules.test.ts) pins the contract between the registry, the shim, the static HTML map, and the stub-generator so future additions can't drift silently.
App bundler is unchanged. Existing S3 bundles still load. No SCHEMA_VERSION bump, no cache invalidation, no risk to deployed apps.