Imported from ZephyrCloudIO/vinext-mf-ssr (
AGENTS.md). Install upstream withnpx skills add ZephyrCloudIO/vinext-mf-ssr. Copyright stays with the author.
LumenCam — agent contract
Camera ecommerce demo: vinext SSR host + Module Federation remotes + runtime discovery.
Domains
| Package | Domain | Expose |
|---|---|---|
apps/host |
Shell / routing | none |
apps/weather |
Local conditions / forecast | ./LocalWeather (React 17, mode: "island") |
apps/marketing |
Campaigns / hero | ./CampaignHero |
apps/recommendations |
Product recommendations | ./ProductRecommendations |
apps/customer-care |
Service / care offerings | ./ServiceInfo |
Do not name remotes after UI chrome (hero, header, banner).
Layout
apps/ host + remotes
discovery/ serves catalog
emitter/ @lumencam/emitter (platform shell:* events)
vite-ssr-mf/ @vite-ssr-mf/federation-shared-globals (sibling of apps; see vite-ssr-mf/README.md)
frontend-discovery.json sole catalog file (edit this only)
Discovery
- File: repo-root
frontend-discovery.json(served bydiscovery/on:4010) - Host fetches
DISCOVERY_URL(defaulthttp://localhost:4010/frontend-discovery.json) - Each MFE key is an array; host uses the first entry
- Fields:
url(remoteEntry.js),metadata.version,extras.exposed,extras.mode(share|island), optionalextras.reactVersion(frame label) - Discovery is hand-authored and decoupled from remote builds; keep
extras.modealigned with remote vitemf.modeby process - No
mf-manifest.jsonin discovery; host mapsremoteEntry.js→remoteEntry.ssr.js
Modes (share | island)
Same strings on remote vite mf.mode and discovery extras.mode. Default is share.
| Mode | Remote | Host |
|---|---|---|
share |
withSharedGlobals(mf, federation); shared react → globalThis; peers + standaloneModes: ["serve"] |
loadRemote → React child of host |
island |
mode: "island"; component + optional load(); own React bundled |
HTML slot + remote hydrate (not loadRemote into host tree) |
Page authors only use <Remote discoveryKey="…" />. They never call island shell APIs by name.
Plugin: @vite-ssr-mf/federation-shared-globals (full API: vite-ssr-mf/README.md)
import { federation } from "@module-federation/vite";
import { withSharedGlobals } from "@vite-ssr-mf/federation-shared-globals";
plugins: [...withSharedGlobals(mf, federation), react()]
sharedGlobals.scope
See vite-ssr-mf/README.md. Short version: omit for globalThis.React; set a string only for globalThis.__MF_GLOBALS__[scope].*. Not used with mode: "island".
Boundaries
- No cross-remote source imports
- No shared global store across MFEs
- Cross-shell UI (modal, toast): remotes use the injected
emitter(@lumencam/emitter); emitshell:*only; shell listens. Skill Rule 4 in.cursor/skills/reviewing-mfe-boundaries - Remotes own fixture data; host does not pass product lists
- Host must not run
federationSharedGlobalson the host vite config - Skills:
.cursor/skills/understanding-mfe-architecture,.cursor/skills/reviewing-mfe-boundaries
Local runtime
- Host Node SSR executes remotes (ssrEntryLoader) and emits markup in the first HTML
- Browser hydrates via discovery
remoteEntry.js - Cloudflare deploy is a follow-up plan
