@fulgurjs/federation 5.7.1 → 5.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/CHANGELOG.md +23 -4
  2. package/DESIGN.md +3 -0
  3. package/README.en.md +350 -367
  4. package/README.md +293 -1179
  5. package/dist/cli.js +327 -29
  6. package/dist/index.cjs +72 -11
  7. package/dist/index.js +72 -11
  8. package/dist/runtime.js +1 -1
  9. package/docs/API.en.md +339 -0
  10. package/docs/API.md +914 -0
  11. package/docs/webpack-mf-/345/257/271/347/205/247/344/270/216/347/274/272/345/217/243.md +64 -37
  12. package/examples/README.md +3 -67
  13. package/examples/templates/README.md +88 -0
  14. package/examples/templates/react-host-vue-remote/README.md +18 -0
  15. package/examples/templates/react-host-vue-remote/package.json +11 -0
  16. package/examples/templates/react-host-vue-remote/pnpm-lock.yaml +1627 -0
  17. package/examples/templates/react-host-vue-remote/pnpm-workspace.yaml +10 -0
  18. package/examples/{bridge → templates/react-host-vue-remote}/react-host/README.md +8 -7
  19. package/examples/{bridge → templates/react-host-vue-remote}/react-host/package.json +1 -1
  20. package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote.d/_paths.d.ts +4 -0
  21. package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote.d/bridge.ts +3 -0
  22. package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote.d.ts +9 -0
  23. package/examples/templates/react-host-vue-remote/scripts/dev.config.json +6 -0
  24. package/examples/templates/react-host-vue-remote/scripts/dev.mjs +173 -0
  25. package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/README.md +10 -5
  26. package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/package.json +1 -1
  27. package/examples/{react → templates/react-react}/README.md +9 -13
  28. package/examples/{react → templates/react-react}/host/README.md +6 -8
  29. package/examples/{react → templates/react-react}/host/package.json +1 -1
  30. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/ClickButton.ts +3 -0
  31. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/_paths.d.ts +4 -0
  32. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/pages/DetailPage.ts +3 -0
  33. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/pages/HomePage.ts +3 -0
  34. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/utils.ts +2 -0
  35. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d.ts +26 -0
  36. package/examples/templates/react-react/package.json +11 -0
  37. package/examples/templates/react-react/pnpm-lock.yaml +1537 -0
  38. package/examples/templates/react-react/pnpm-workspace.yaml +10 -0
  39. package/examples/{react → templates/react-react}/remote/README.md +7 -6
  40. package/examples/{react → templates/react-react}/remote/package.json +1 -1
  41. package/examples/templates/react-react/scripts/dev.config.json +6 -0
  42. package/examples/templates/react-react/scripts/dev.mjs +173 -0
  43. package/examples/templates/showcase/README.md +109 -0
  44. package/examples/templates/showcase/package.json +13 -0
  45. package/examples/templates/showcase/pnpm-lock.yaml +1858 -0
  46. package/examples/templates/showcase/pnpm-workspace.yaml +12 -0
  47. package/examples/templates/showcase/react-host/fulgurjs.config.ts +21 -0
  48. package/examples/templates/showcase/react-host/index.html +13 -0
  49. package/examples/templates/showcase/react-host/package.json +26 -0
  50. package/examples/templates/showcase/react-host/src/GuardBanner.tsx +33 -0
  51. package/examples/templates/showcase/react-host/src/Layout.tsx +42 -0
  52. package/examples/templates/showcase/react-host/src/ObsPanel.tsx +44 -0
  53. package/examples/templates/showcase/react-host/src/demo-log.ts +74 -0
  54. package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote.d/_paths.d.ts +4 -0
  55. package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote.d/bridge.ts +3 -0
  56. package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote.d.ts +7 -0
  57. package/examples/templates/showcase/react-host/src/main.tsx +33 -0
  58. package/examples/templates/showcase/react-host/src/pages/AboutPage.tsx +18 -0
  59. package/examples/templates/showcase/react-host/src/pages/BridgeVuePage.tsx +77 -0
  60. package/examples/templates/showcase/react-host/src/pages/HomePage.tsx +14 -0
  61. package/examples/templates/showcase/react-host/src/routing.ts +30 -0
  62. package/examples/templates/showcase/react-host/tsconfig.json +15 -0
  63. package/examples/templates/showcase/react-host/vite.config.ts +8 -0
  64. package/examples/templates/showcase/react-remote/fulgurjs.config.ts +18 -0
  65. package/examples/templates/showcase/react-remote/index.html +13 -0
  66. package/examples/templates/showcase/react-remote/package.json +25 -0
  67. package/examples/templates/showcase/react-remote/src/ChildLayout.tsx +48 -0
  68. package/examples/templates/showcase/react-remote/src/bridge.tsx +62 -0
  69. package/examples/templates/showcase/react-remote/src/child-bus.ts +32 -0
  70. package/examples/templates/showcase/react-remote/src/main.tsx +18 -0
  71. package/examples/templates/showcase/react-remote/src/pages/Locked.tsx +23 -0
  72. package/examples/templates/showcase/react-remote/src/pages/OrderDetail.tsx +36 -0
  73. package/examples/templates/showcase/react-remote/src/pages/OrderList.tsx +110 -0
  74. package/examples/templates/showcase/react-remote/src/pages/Settings.tsx +50 -0
  75. package/examples/templates/showcase/react-remote/src/settings-store.ts +11 -0
  76. package/examples/templates/showcase/react-remote/tsconfig.json +14 -0
  77. package/examples/templates/showcase/react-remote/vite.config.ts +14 -0
  78. package/examples/templates/showcase/scripts/dev.config.json +8 -0
  79. package/examples/templates/showcase/scripts/dev.mjs +173 -0
  80. package/examples/templates/showcase/vue-host/fulgurjs.config.ts +21 -0
  81. package/examples/templates/showcase/vue-host/index.html +13 -0
  82. package/examples/templates/showcase/vue-host/package.json +25 -0
  83. package/examples/templates/showcase/vue-host/src/App.vue +50 -0
  84. package/examples/templates/showcase/vue-host/src/GuardBanner.vue +24 -0
  85. package/examples/templates/showcase/vue-host/src/ObsPanel.vue +60 -0
  86. package/examples/templates/showcase/vue-host/src/demo-log.ts +49 -0
  87. package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote.d/_paths.d.ts +4 -0
  88. package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote.d/bridge.ts +3 -0
  89. package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote.d.ts +7 -0
  90. package/examples/templates/showcase/vue-host/src/guard.ts +47 -0
  91. package/examples/templates/showcase/vue-host/src/main.ts +30 -0
  92. package/examples/templates/showcase/vue-host/src/pages/AboutPage.vue +17 -0
  93. package/examples/templates/showcase/vue-host/src/pages/BridgeReactPage.vue +74 -0
  94. package/examples/templates/showcase/vue-host/src/pages/HomePage.vue +13 -0
  95. package/examples/templates/showcase/vue-host/src/routing.ts +31 -0
  96. package/examples/templates/showcase/vue-host/tsconfig.json +14 -0
  97. package/examples/templates/showcase/vue-remote/fulgurjs.config.ts +17 -0
  98. package/examples/templates/showcase/vue-remote/index.html +13 -0
  99. package/examples/templates/showcase/vue-remote/package.json +23 -0
  100. package/examples/templates/showcase/vue-remote/src/ChildLayout.vue +54 -0
  101. package/examples/templates/showcase/vue-remote/src/bridge.ts +48 -0
  102. package/examples/templates/showcase/vue-remote/src/child-bus.ts +32 -0
  103. package/examples/templates/showcase/vue-remote/src/main.ts +16 -0
  104. package/examples/templates/showcase/vue-remote/src/pages/Locked.vue +28 -0
  105. package/examples/templates/showcase/vue-remote/src/pages/OrderDetail.vue +46 -0
  106. package/examples/templates/showcase/vue-remote/src/pages/OrderList.vue +116 -0
  107. package/examples/templates/showcase/vue-remote/src/pages/Settings.vue +34 -0
  108. package/examples/templates/showcase/vue-remote/src/settings-store.ts +13 -0
  109. package/examples/templates/showcase/vue-remote/tsconfig.json +14 -0
  110. package/examples/templates/showcase/vue-remote/vite.config.ts +14 -0
  111. package/examples/templates/vue-host-react-remote/README.md +18 -0
  112. package/examples/templates/vue-host-react-remote/package.json +11 -0
  113. package/examples/templates/vue-host-react-remote/pnpm-lock.yaml +1663 -0
  114. package/examples/templates/vue-host-react-remote/pnpm-workspace.yaml +10 -0
  115. package/examples/{bridge → templates/vue-host-react-remote}/react-remote/README.md +9 -4
  116. package/examples/{bridge → templates/vue-host-react-remote}/react-remote/package.json +1 -1
  117. package/examples/templates/vue-host-react-remote/scripts/dev.config.json +6 -0
  118. package/examples/templates/vue-host-react-remote/scripts/dev.mjs +173 -0
  119. package/examples/{bridge → templates/vue-host-react-remote}/vue-host/README.md +8 -7
  120. package/examples/{bridge → templates/vue-host-react-remote}/vue-host/package.json +1 -1
  121. package/examples/{bridge → templates/vue-host-react-remote}/vue-host/src/App.vue +1 -1
  122. package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote.d/_paths.d.ts +4 -0
  123. package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote.d/bridge.ts +3 -0
  124. package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote.d.ts +9 -0
  125. package/examples/{vue → templates/vue-vue}/README.md +9 -13
  126. package/examples/{vue → templates/vue-vue}/host/README.md +6 -8
  127. package/examples/{vue → templates/vue-vue}/host/package.json +1 -1
  128. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/ClickButton.ts +4 -0
  129. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/_paths.d.ts +4 -0
  130. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/pages/DetailPage.ts +4 -0
  131. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/pages/HomePage.ts +4 -0
  132. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/utils.ts +2 -0
  133. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d.ts +29 -0
  134. package/examples/templates/vue-vue/host/vite.config.ts +8 -0
  135. package/examples/templates/vue-vue/package.json +11 -0
  136. package/examples/templates/vue-vue/pnpm-lock.yaml +1199 -0
  137. package/examples/templates/vue-vue/pnpm-workspace.yaml +10 -0
  138. package/examples/{vue → templates/vue-vue}/remote/README.md +7 -6
  139. package/examples/{vue → templates/vue-vue}/remote/package.json +1 -1
  140. package/examples/{vue → templates/vue-vue}/remote/src/App.vue +1 -1
  141. package/examples/templates/vue-vue/scripts/dev.config.json +6 -0
  142. package/examples/templates/vue-vue/scripts/dev.mjs +173 -0
  143. package/package.json +8 -6
  144. package/examples/README.en.md +0 -67
  145. package/examples/bridge/README.md +0 -36
  146. package/examples/bridge/react-host/package-lock.json +0 -2466
  147. package/examples/bridge/react-remote/package-lock.json +0 -2391
  148. package/examples/bridge/vue-host/package-lock.json +0 -1894
  149. package/examples/bridge/vue-remote/package-lock.json +0 -1865
  150. package/examples/react/host/package-lock.json +0 -2391
  151. package/examples/react/remote/package-lock.json +0 -2333
  152. package/examples/vue/host/package-lock.json +0 -1865
  153. package/examples/vue/remote/package-lock.json +0 -1843
  154. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/fulgurjs.config.ts +0 -0
  155. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/index.html +0 -0
  156. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/src/env.d.ts +0 -0
  157. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/src/host-session.ts +0 -0
  158. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/src/main.tsx +0 -0
  159. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/tsconfig.json +0 -0
  160. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/vite.config.ts +0 -0
  161. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/fulgurjs.config.ts +0 -0
  162. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/index.html +0 -0
  163. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/src/App.vue +0 -0
  164. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/src/bridge.ts +0 -0
  165. /package/examples/{bridge/react-remote → templates/react-host-vue-remote/vue-remote}/src/env.d.ts +0 -0
  166. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/src/main.ts +0 -0
  167. /package/examples/{bridge/vue-host → templates/react-host-vue-remote/vue-remote}/tsconfig.json +0 -0
  168. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/vite.config.ts +0 -0
  169. /package/examples/{react → templates/react-react}/host/fulgurjs.config.ts +0 -0
  170. /package/examples/{react → templates/react-react}/host/index.html +0 -0
  171. /package/examples/{react → templates/react-react}/host/src/App.tsx +0 -0
  172. /package/examples/{react → templates/react-react}/host/src/main.tsx +0 -0
  173. /package/examples/{react → templates/react-react}/host/src/pages/UtilsDemo.tsx +0 -0
  174. /package/examples/{react → templates/react-react}/host/src/remotePages.tsx +0 -0
  175. /package/examples/{bridge/react-remote → templates/react-react/host}/tsconfig.json +0 -0
  176. /package/examples/{react → templates/react-react}/host/vite.config.ts +0 -0
  177. /package/examples/{react → templates/react-react}/remote/fulgurjs.config.ts +0 -0
  178. /package/examples/{react → templates/react-react}/remote/index.html +0 -0
  179. /package/examples/{react → templates/react-react}/remote/src/App.tsx +0 -0
  180. /package/examples/{react → templates/react-react}/remote/src/exposes/ClickButton.tsx +0 -0
  181. /package/examples/{react → templates/react-react}/remote/src/exposes/pages/DetailPage.tsx +0 -0
  182. /package/examples/{react → templates/react-react}/remote/src/exposes/pages/HomePage.tsx +0 -0
  183. /package/examples/{react → templates/react-react}/remote/src/exposes/utils.ts +0 -0
  184. /package/examples/{react → templates/react-react}/remote/src/main.tsx +0 -0
  185. /package/examples/{react/host → templates/react-react/remote}/tsconfig.json +0 -0
  186. /package/examples/{react → templates/react-react}/remote/vite.config.ts +0 -0
  187. /package/examples/{vue/host → templates/showcase/vue-host}/vite.config.ts +0 -0
  188. /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/fulgurjs.config.ts +0 -0
  189. /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/index.html +0 -0
  190. /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/src/bridge.tsx +0 -0
  191. /package/examples/{bridge/vue-host → templates/vue-host-react-remote/react-remote}/src/env.d.ts +0 -0
  192. /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/src/main.tsx +0 -0
  193. /package/examples/{react/remote → templates/vue-host-react-remote/react-remote}/tsconfig.json +0 -0
  194. /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/vite.config.ts +0 -0
  195. /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/fulgurjs.config.ts +0 -0
  196. /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/index.html +0 -0
  197. /package/examples/{bridge/vue-remote → templates/vue-host-react-remote/vue-host}/src/env.d.ts +0 -0
  198. /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/src/host-session.ts +0 -0
  199. /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/src/main.ts +0 -0
  200. /package/examples/{bridge/vue-remote → templates/vue-host-react-remote/vue-host}/tsconfig.json +0 -0
  201. /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/vite.config.ts +0 -0
  202. /package/examples/{vue → templates/vue-vue}/host/fulgurjs.config.ts +0 -0
  203. /package/examples/{vue → templates/vue-vue}/host/index.html +0 -0
  204. /package/examples/{vue → templates/vue-vue}/host/src/App.vue +0 -0
  205. /package/examples/{vue → templates/vue-vue}/host/src/main.ts +0 -0
  206. /package/examples/{vue → templates/vue-vue}/host/src/pages/HomePage.vue +0 -0
  207. /package/examples/{vue → templates/vue-vue}/host/src/pages/UtilsDemo.vue +0 -0
  208. /package/examples/{vue → templates/vue-vue}/host/tsconfig.json +0 -0
  209. /package/examples/{vue → templates/vue-vue}/remote/fulgurjs.config.ts +0 -0
  210. /package/examples/{vue → templates/vue-vue}/remote/index.html +0 -0
  211. /package/examples/{vue → templates/vue-vue}/remote/src/exposes/ClickButton.vue +0 -0
  212. /package/examples/{vue → templates/vue-vue}/remote/src/exposes/pages/DetailPage.vue +0 -0
  213. /package/examples/{vue → templates/vue-vue}/remote/src/exposes/pages/HomePage.vue +0 -0
  214. /package/examples/{vue → templates/vue-vue}/remote/src/exposes/utils.ts +0 -0
  215. /package/examples/{vue → templates/vue-vue}/remote/src/main.ts +0 -0
  216. /package/examples/{vue → templates/vue-vue}/remote/tsconfig.json +0 -0
  217. /package/examples/{vue → templates/vue-vue}/remote/vite.config.ts +0 -0
package/README.en.md CHANGED
@@ -1,476 +1,459 @@
1
1
  # @fulgurjs/federation
2
2
 
3
- [简体中文](./README.md) | English
4
-
5
- > **fulgurjs** — Latin for "lightning · flash of light".
6
- > A Vite plugin that makes Module Federation work out of the box: **one config shape per project, separate dev & prod engines, semantics aligned with webpack Module Federation**, with first-class browser support for both Vue 3 and React 18/19.
7
-
8
- ![tests](https://img.shields.io/badge/tests-460%20%2B%20e2e-green) ![runtime](https://img.shields.io/badge/runtime%20gzip-%3C%209KB-blue) ![vite](https://img.shields.io/badge/vite-5%20%7C%206%20%7C%207%20%7C%208-purple)
9
-
10
- ---
11
-
12
- ## 1. Why
13
-
14
- | | webpack MF | other vite MF solutions | **@fulgurjs/federation** |
15
- |---|---|---|---|
16
- | dev experience | separate builds required | manual bootstrap usually required | ✅ dual dev-server direct wiring, zero manual async boundaries |
17
- | prod artifacts | ✅ | often missing or degraded | ✅ build-time rewriting, stable remoteEntry filename + manifest |
18
- | semantic parity | 100% | incomplete (version negotiation / singleton / fault tolerance often missing) | ✅ aligned clause-by-clause with webpack semantics, e2e-verified |
19
- | **UMD / CJS-only deps** | DIY | **commonly unusable** | ✅ automatic (dep-optimizer externalization + build-time require shims) |
20
- | remote load failures | raw errors | usually missing | ✅ retry / circuit breaker / timeout built in + explicit `fallbackModule` degradation |
21
- | failure recovery | reload the page | usually missing | ✅ built-in placeholders offer **Retry load** (in-page; failed URLs are varied to penetrate the browser's failed-import cache) and **Refresh page to retry** (a user-initiated full reload for failures the browser caches beyond in-page reach) |
22
- | runtime size | ~40KB+ | varies | **gzip < 9KB** (framework-neutral core; adapters are separate) |
23
- | misconfiguration | hard to debug | cryptic | three-part diagnostics: symptom / cause / fix |
24
-
25
- ## 2. Feature overview
26
-
27
- - **Full exposes / remotes / shared semantics** — `name@url` syntax, key renaming, promise-based remotes, full-semver `requiredVersion`, version negotiation (highest wins), singleton / strictVersion, loaded versions are never replaced, multi-version coexistence, `shareKey` redirection, multiple share scopes
28
- - **UMD / CJS-only deps out of the box** — element-plus, avue and other UMD/CJS-only packages simply go into `optimizeDeps.include`; in dev the plugin re-routes shared keys inside pre-bundled output to negotiation facades (esbuild path on Vite ≤ 7, rolldown plugin on Vite ≥ 8), in build CJS `require(<shared>)` calls are redirected to shims — dual-runtime immune
29
- - **Automatic async boundaries** — top-level await injected automatically (es2022+); no webpack-style manual `import('./bootstrap')`
30
- - **Stable artifacts** — remoteEntry keeps a fixed filename (content changes every build → **must be `no-cache`**; only content-hashed chunks may be cached long); `fulgurjs-manifest.json` asset manifest; one chunk per expose
31
- - **Fault tolerance (webpack MF 2.0 errorLoadRemote aligned)** — retry / circuit breaker / timeout built in; `loadRemote(spec, { retries, fallbackModule })` per-call overrides; on failure the fallback module is returned and the error event is still emitted (**never silent**; without `fallbackModule` the error re-throws)
32
- - **Real failure recovery** — Chromium/Firefox/Safari cache failed dynamic imports per URL, so re-importing the same URL rejects without hitting the network again (verified per browser in this repo's e2e; see MDN import() for the underlying semantics). After a real failure the runtime varies the URL (`fulgurjs_retry=N`) across remote-entry loading, dev container loaders and the prod remoteEntry, so "service recovered → click Retry load" genuinely re-fetches. Successful modules are never re-requested with a varied URL — module identity and singletons are preserved; concurrent failures advance exactly one retry generation (no module-instance split); repeated access to loaded modules issues zero extra requests. **Known boundary**: a failed **static dependency** chunk of an expose cannot recover in-page (the browser caches the dependency URL's failure). The built-in placeholder therefore also offers **Refresh page to retry** — a user-initiated full reload that keeps the current URL (never automatic, no reload loops) — and that is the supported recovery path for this case; the plugin deliberately does not rewrite the whole site dependency graph to work around it
33
- - **Enhancements** — dev type generation (dual-track, see §8.6), manifest-driven `preloadRemote()`, runtime plugin hooks (`beforeLoadRemote` / `afterLoadRemote` / `onRemoteError` / `resolveShare`)
34
- - **Full HMR chain** — remote edits propagate to the host page: component hot swap, state retention, error overlay and recovery
35
- - **Zero-silent-failure discipline** — config problems fail at startup with three-part diagnostics; federation failures throw explicitly (error code + actionable fix); no silent fallback paths
36
- - **CLI** — `fulgurjs init` / `explain` / `check-pages` / `doctor` (see §7)
37
- - **Optional remote init lifecycle** — `federation({ setup })`: `setup(context)` runs once per app, `onSession(context)` runs once per host `sessionKey`; failures are explicit and retryable
38
- - **Host page adapter** — one page table shared by routing and layout: URL resolution, longest-prefix remote attribution, R1–R5 validation, component cache keyed by login generation, skeleton/error placeholders, keep-alive names (Vue only)
39
- - **Cross-app context** — `provideAppContext` / `getAppContext` / `requireAppContext` / `clearAppContext`; transport snapshot + function references (not reactive); account switching carried by `onSession` without page reloads
40
- - **Vue direct rendering** — `remoteComponent('remote/X')` on the runtime entry: `defineAsyncComponent + loadRemote` wrapper with explicit error placeholder; runtime core stays framework-free
41
- - **Full React support (browser)** — dedicated `@fulgurjs/federation/react` entry: `remoteComponent`, `useLoadRemote`, `RemoteErrorBoundary`, `createReactHostPages`; shared `react`/`react-dom` singletons with hooks/StrictMode/Context verified single-instance; mounted components follow `sessionKey` changes without remounting; pure-React projects install zero Vue, pure-Vue projects install zero React
42
- - **CSP friendly** — no `eval` / `new Function` anywhere in loading paths
43
- - **Error-code system (44 codes)** — CFG / DEV / BLD / MFU / CC segments, drift-checked against the code registry (see §11)
44
-
45
- ## 3. Installation & requirements
3
+ **[All templates and demos](examples/README.en.md)**: Example catalog and run guide.
4
+
5
+ [简体中文](README.md) | [English](README.en.md)
6
+
7
+ **Use components, pages and functions from another Vite application.**
8
+
9
+ For example, a main application can load a separately deployed approval page, a Vue host can embed a React sub-app, or several applications can use the same utility module. Each application can live in its own repository and build and deploy separately.
10
+
11
+ This is the usage guide. Examples and templates are written against **5.8.0** (the exact version in each project's `package.json` is what gets installed). Signatures, defaults and execution rules are in the [API reference](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/API.en.md).
12
+
13
+ ## Choose what you need
14
+
15
+ | Goal | Use | Example |
16
+ |---|---|---|
17
+ | Load a Vue component in Vue | `remoteComponent` | [Vue examples](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/templates/vue-vue) |
18
+ | Load a React component in React | `remoteComponent` from `/react` | [React examples](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/templates/react-react) |
19
+ | Call a remote JS/TS function | `loadRemote`; React also has `useLoadRemote` | Quick start below |
20
+ | Map several host routes to remote pages | `createHostPages` (Vue) / `createReactHostPages` (React) | [Page demo](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/demos/pages-cli) |
21
+ | Embed Vue in React, or React in Vue | `defineBridgeApp` + a host bridge component | [Bridge examples](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/templates) |
22
+ | Restore a sub-app detail route after refresh | Enable bridge URL sync | [Router demo](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/templates/showcase) |
23
+ | Provide user data or run remote initialization | `AppContext`, optional `setup`/`onSession` | Initialization below |
24
+ | Run React 18 and 19 on the same page | Separate dependency groups and consumers using `shareScope` | [Version isolation demo](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/demos/react-versions) |
25
+
26
+ Combine these features as needed. **A simple remote component does not require a bridge, page table or login lifecycle.**
27
+
28
+ ## Terms in plain language
29
+
30
+ | Term | Meaning |
31
+ |---|---|
32
+ | Host | The application displaying remote content |
33
+ | Remote | The application providing a module |
34
+ | `exposes` | Files the remote allows other applications to load |
35
+ | `remotes` | The remote names and addresses the host uses |
36
+ | `shared` | Dependencies that participate in sharing, such as Vue or React |
37
+ | `singleton` | Adopt one dependency instance within a share scope; this does not make incompatible major versions compatible |
38
+ | `shareScope` | A group of shared dependencies; separate groups can use separate versions |
39
+ | Bridge | A DOM container in which a sub-app manages its own rendering and cleanup |
40
+ | URL sync | Record the sub-app route in the host URL so refresh, sharing and history navigation can restore it |
41
+
42
+ An application can both expose and consume modules.
43
+
44
+ ## Install
45
+
46
+ Install in every participating Vite project:
46
47
 
47
48
  ```bash
48
49
  pnpm add -D @fulgurjs/federation
50
+ # npm projects: npm install -D @fulgurjs/federation
49
51
  ```
50
52
 
51
- - Vite ≥ 5.1 (tested through 8.x)
52
- - Node ≥ 18
53
- - Vue ≥ 3.2.0 and/or React `>=18.0.0 <20` — all three are **optional peers**; install only the framework you use
54
- - Chrome 108+ (native top-level await)
53
+ - Supports browser applications using Vue 3, React 18/19, and plain JS/TS modules.
54
+ - Supports Vite 5.1+ within the Vite 5/6/7/8 series. Your framework plugins must also support your chosen Vite version.
55
+ - The plugin requires Node.js ≥18, but **Vite 7/8 require Node.js 20.19+ or 22.12+**. Meet both requirements.
56
+ - Set the build target to `es2022` or newer. Chrome 108+ is the browser baseline; other browsers need corresponding ESM, dynamic import and top-level await support.
57
+ - A pure Vue application needs Vue; a pure React application needs React and react-dom. A cross-framework bridge host installs both frameworks as explained below.
58
+
59
+ ## Starting fresh: create a complete project
55
60
 
56
- > Vite 8 (rolldown): **fully usable in production as of 5.6.0** — the synchronous negotiation facade (V8-SYNC-FACADE) eliminates the startup mutual-await deadlock between top-level await propagation and application circular dependencies (full JeecgBoot v3.9.5 acceptance: login, A-hosts-B, three-layer deep-link refresh, cross-framework deep links, React host; vite 8.3.2 + rolldown 1.2.12), plus three vite8-specific fixes (remoteEntry failure retry, manifest exposes mapping, dependency-preload negative caching); e2e dev 73/73 + prod 33/33 as a standing CI matrix. The first page open on a cold dev cache still falls inside the dependency pre-bundling window (DEV-010; it self-recovers via reload). Warm up before acceptance runs or manual judgement, as documented. Known cost (vite 8 only): the synchronous facade keeps the local copy chunk reachable by the module graph even when negotiation picks another app's instance (dual-version scenarios fetch the shared library at most twice; runtime identity still converges to a single instance).
61
+ Without an existing project, scaffold from a complete template with the CLI (Node ≥ 20 and pnpm ≥ 9 required):
57
62
 
58
- `@fulgurjs/federation/runtime` and `@fulgurjs/federation/react` are **ESM-only** browser entries (no `require()`). The build-time main entry supports both ESM and CJS.
63
+ ```bash
64
+ npx @fulgurjs/federation create # interactive; or explicit:
65
+ npx @fulgurjs/federation create vue-vue --dir my-federation
66
+ ```
59
67
 
60
- ## 4. Project shape: two files per app
68
+ Five templates cover Vue×Vue, React×React, a Vue host embedding a React child app, a React host embedding a Vue child app, and the bidirectional bridge + URL sync showcase. `create` copies a runnable workspace (lockfile and startup script included) and runs a frozen install by default, then prints the commands to enter, start and build. For an **existing** project skip this and use the quick start below plus `fulgurjs init`.
61
69
 
62
- Every app root owns one `fulgurjs.config.ts` whose **default export is the federation options object itself**; `vite.config.ts` wires it once:
70
+ Template sources live in [examples/templates/](examples/templates/README.md); no manual wiring is needed unless you change names/ports (fixed checklist in the template guide).
63
71
 
64
- ```ts
65
- // vite.config.ts
66
- import { defineConfig } from 'vite'
67
- import react from '@vitejs/plugin-react' // or @vitejs/plugin-vue
68
- import federation from '@fulgurjs/federation'
69
- import fulgurjsConfig from './fulgurjs.config.ts'
72
+ ## Quick start: two Vue applications
70
73
 
71
- export default defineConfig({ plugins: [react(), federation(fulgurjsConfig)] })
72
- ```
74
+ These steps add federation to **existing Vite + Vue projects**, which retain their own HTML and application entry files.
73
75
 
74
- An optional named export `hostPages = { pages, remotePrefixes }` is read by the CLI only; the same pure-data module feeds the browser adapter. Page-data modules must stay pure data (no framework/router/browser imports) so the CLI can evaluate them.
76
+ ```text
77
+ remote-vue/ Provides a button and add() function; dev port 5174
78
+ host-vue/ Loads them; dev port 5173
79
+ ```
75
80
 
76
- Removed in 5.0.0 and not coming back: the aggregate config chain (`root` + `apps[]`, the `/config` entry, `loadRepoConfig`, `federationOptionsForApp`, CLI `--app`), and the no-op options `remoteType`, `library`, `automaticAsyncBoundary`, `dataPrefetch`, `usedExports`, `ignoreUnusedSharedExports` — any of these now fail with `CFG-011` plus migration hints.
81
+ ### 1. Declare remote files
77
82
 
78
- ## 5. Quick start — React
83
+ `remote-vue/fulgurjs.config.ts`:
79
84
 
80
85
  ```ts
81
- // remote: fulgurjs.config.ts
82
86
  import type { FederationOptions } from '@fulgurjs/federation'
83
87
 
84
88
  export default {
85
- name: 'remote-react',
89
+ name: 'remote-vue',
86
90
  exposes: {
87
- './Button': './src/Button.tsx',
88
- './utils': './src/utils.ts',
89
- './pages/home': './src/pages/Home.tsx',
90
- },
91
- shared: {
92
- react: { singleton: true },
93
- 'react-dom': { singleton: true },
91
+ './Button': './src/Button.vue',
92
+ './math': './src/math.ts',
94
93
  },
94
+ shared: { vue: { singleton: true, strictVersion: true } },
95
95
  } satisfies FederationOptions
96
96
  ```
97
97
 
98
- Host consumption — one import point, three usage shapes:
98
+ `remote-vue/src/Button.vue`:
99
99
 
100
- ```tsx
101
- import { remoteComponent, useLoadRemote, createReactHostPages, remoteSchema } from '@fulgurjs/federation/react'
102
- import { pages, remotePrefixes } from './src/federation/pages.data'
100
+ ```vue
101
+ <script setup lang="ts">
102
+ import { ref } from 'vue'
103
+ defineProps<{ label: string }>()
104
+ const count = ref(0)
105
+ </script>
103
106
 
104
- // ① Component — create the factory at module top level (never inside render).
105
- // Loading starts on first render. retry rebuilds the load attempt.
106
- const RemoteButton = remoteComponent<{ label: string; onClick?: () => void }>('remote-react/Button', {
107
- fallback: <p>Loading remote button…</p>,
108
- })
107
+ <template>
108
+ <button @click="count++">{{ label }}: {{ count }}</button>
109
+ </template>
110
+ ```
109
111
 
110
- // ② Plain module — generation-guarded hook
111
- type Utils = { formatMoney(v: number, currency?: string): string }
112
+ `remote-vue/src/math.ts`:
112
113
 
113
- // ③ Page table — same verb as Vue; render the component from your router
114
- const hp = createReactHostPages({ pages, remotePrefixes, schema: remoteSchema })
115
- const RemoteHome = hp.component('remote-react/pages/home')
114
+ ```ts
115
+ export function add(a: number, b: number): number {
116
+ return a + b
117
+ }
116
118
  ```
117
119
 
118
- **Data flow for host state (context):** the host provides context (`provideAppContext`, including a non-sensitive `sessionKey`) and then triggers its own re-render (React state / router). Mounted remote components and hooks observe the new `sessionKey` on that render and re-run their load lifecycle — A→B account switching works on the same mounted instance without remounting. `AppContext` is a plain snapshot: the plugin does not subscribe to it reactively; the host must trigger the render. `beforeLoad` (page tables) runs before every actual load attempt to refresh context. Logout: call `clearAppContext()` before unmounting authed UI.
120
+ ### 2. Declare the address in the host
121
+
122
+ `host-vue/fulgurjs.config.ts`:
119
123
 
120
- Runnable examples: [`examples/vue/{host,remote}`](./examples) and [`examples/react/{host,remote}`](./examples) — four complete copy-and-run projects installed from the npm registry (see the examples entry page). In-repo e2e fixtures: `fixtures/host-react` / `fixtures/remote-react`.
124
+ ```ts
125
+ import type { FederationOptions } from '@fulgurjs/federation'
121
126
 
122
- ## 6. Quick start — Vue
127
+ export default {
128
+ name: 'host-vue',
129
+ remotes: {
130
+ 'remote-vue': {
131
+ dev: 'http://localhost:5174',
132
+ prod: '/remote-vue',
133
+ },
134
+ },
135
+ shared: { vue: { singleton: true, strictVersion: true } },
136
+ } satisfies FederationOptions
137
+ ```
123
138
 
124
- Three integration paths (plain module / multi-page `createHostPages` / `setup` + `AppContext`) are documented in the Chinese README §快速开始;the API is identical to the tables below, imported from `@fulgurjs/federation/runtime`. The Vue-specific extras are `createHostPages` (with `keepAliveNames`) and the Vue `remoteComponent` options (`loadingComponent` / `errorComponent` / `delay`).
139
+ `dev` is the development URL. `prod` is the deployed URL; `/remote-vue` refers to a path on the host origin, not a local filesystem folder.
125
140
 
126
- Cross-framework **plain TS modules** work in both directions (a Vue host can load a React remote's `utils` and vice versa) — direct Vue↔React component rendering in one tree is out of scope.
141
+ ### 3. Register the plugin in both applications
127
142
 
128
- ## 7. CLI
143
+ Each project's `vite.config.ts` imports its own federation config:
129
144
 
130
- ```bash
131
- npx fulgurjs init # scaffold fulgurjs.config.ts; never rewrites other files
132
- npx fulgurjs explain # interpret the effective federation shape + load chain
133
- npx fulgurjs check-pages \
134
- --manifest remote-a=https://cdn.example.com/remote-a/fulgurjs-manifest.json \
135
- --require-verified # page-table ↔ remote manifest contract check (CI gate)
136
- npx fulgurjs doctor --site https://example.com # deployment health check
137
- ```
145
+ ```ts
146
+ import { defineConfig } from 'vite'
147
+ import vue from '@vitejs/plugin-vue'
148
+ import federation from '@fulgurjs/federation'
149
+ import fulgurjsConfig from './fulgurjs.config'
138
150
 
139
- `check-pages`: "confirmed missing" (error, non-zero) is distinct from "unverifiable" (source unreachable — honestly reported, non-zero with `--require-verified`); no fallback to stale local dist output.
151
+ export default defineConfig({
152
+ plugins: [vue(), federation(fulgurjsConfig)],
153
+ build: { target: 'es2022' },
154
+ })
155
+ ```
140
156
 
141
- ## 8. API reference
157
+ Keep existing aliases, proxies and other settings. Install compatible Vue versions in both applications; `strictVersion` rejects incompatible shared versions.
142
158
 
143
- ### 8.1 `@fulgurjs/federation/react` — React entry
159
+ ### 4. Display and call the remote modules
144
160
 
145
- Re-exports the common runtime API of §8.2 **except** the Vue-only items (`remoteComponent` Vue options form, `createHostPages`, `keepAliveNames`), plus:
161
+ `host-vue/src/App.vue`:
146
162
 
147
- #### `remoteComponent<Props>(spec, options?)` → `ComponentType<Props & { ref? }>`
163
+ ```vue
164
+ <script setup lang="ts">
165
+ import { ref } from 'vue'
166
+ import { loadRemote, remoteComponent } from '@fulgurjs/federation/runtime'
148
167
 
149
- | Option | Type / default | Semantics |
150
- |---|---|---|
151
- | `fallback` | `ReactNode`, default `null` | placeholder while this load is pending (distinct from the failure placeholder) |
152
- | `error` | `ReactNode` or `(error, retry) => ReactNode`, default built-in Chinese placeholder | shown on load failure **or** subtree render error; the function receives the real error and a working retry |
153
- | `retries` | `number` (integer 0–10), default follows `loadRemote` (2) | passthrough; invalid values throw at factory call |
154
- | `timeout` | `number` (ms), default none | adapter-level wait cap for this component load; does **not** cancel the issued shared request; late results never overwrite the settled state and produce no unhandled rejections |
168
+ const RemoteButton = remoteComponent('remote-vue/Button')
169
+ const result = ref('Not calculated yet')
155
170
 
156
- - Factory creation and page-table declaration have **zero load side effects**; loading starts on first render via `loadRemote` (container negotiation + optional setup/onSession)
157
- - Not built on `React.lazy`: a lazy instance caches its failed promise and an error-boundary reset alone cannot recover; this implementation's retry rebuilds the load attempt (already-cached successful modules are not re-downloaded)
158
- - Export validation: the default export must be a function/class/`memo`/`forwardRef` component; strings/numbers/empty namespaces fail explicitly
159
- - `ref` passthrough works for `forwardRef` exports (verified on React 18 and 19)
160
- - Render exceptions are caught by the built-in boundary and reported separately from network/export errors; the boundary does not catch event-handler or async-callback errors (React semantics)
161
- - **Session switching:** mounted instances read the current `AppContext.sessionKey` on every render; when the host provides new context and re-renders, the load lifecycle re-runs for the new session on the same instance (no remount, no second React). Same-session re-renders do not reload
162
- - The built-in placeholder shows error code + real cause + fix + a working 重试 (retry) button
171
+ async function calculate() {
172
+ try {
173
+ const math = await loadRemote<{ add(a: number, b: number): number }>('remote-vue/math')
174
+ result.value = String(math.add(1, 2))
175
+ } catch (error) {
176
+ result.value = error instanceof Error ? error.message : String(error)
177
+ }
178
+ }
179
+ </script>
163
180
 
164
- #### `useLoadRemote<Module>(spec, options?)` → `{ data, error, loading, reload }`
181
+ <template>
182
+ <RemoteButton label="Remote button" />
183
+ <button @click="calculate">Call remote add()</button>
184
+ <p>{{ result }}</p>
185
+ </template>
186
+ ```
165
187
 
166
- - `data: Module | undefined`, `error: unknown` (always `undefined` when no error), `loading: boolean`, `reload: () => Promise<void>`
167
- - Options: `shareScope`, `retries`, `fallbackModule` (explicit degradation — failures return the fallback value instead of writing `error`)
168
- - Uniform state contract: first load, spec/option/session change and explicit `reload` all enter `data=undefined, error=undefined, loading=true`; the current attempt writes `data` on success or `error` on failure and clears `loading`; stale attempts never write
169
- - Generation guards: fast A→B switching, late slow responses, consecutive reloads, unmount-during-flight and StrictMode double effects can only write from the latest valid request
170
- - `reload` clears old data and re-runs the lifecycle (onSession dedup by generation) but never re-downloads cached successful modules; resolves normally (failures surface in `error`, never an unhandled rejection). Unmount invalidates pending effects and reloads; calling a saved reload after unmount starts no request
171
- - Session-aware: re-runs when `sessionKey` changes; same-session re-renders don't
188
+ In `remote-vue/Button`, `remote-vue` matches the host's `remotes` key and `Button` matches the remote's `./Button` expose key. The `./` can be omitted when loading it.
172
189
 
173
- #### `RemoteErrorBoundary`
190
+ `loadRemote` returns module exports. You still need to call `math.add()` to perform the calculation.
174
191
 
175
- Standalone page-level boundary. Props: `children`, `fallback` (node or `({ error, reset }) => ReactNode`), `onError(error, info)`, `resetKeys` (reset when any entry changes). `reset` only clears boundary state; if a child holds a failed cache (e.g. your own `React.lazy`), rebuilding the attempt is the caller's job — `remoteComponent`'s built-in retry already does both. It never sees errors already consumed by `remoteComponent`'s inner boundary.
192
+ ### 5. Run both applications
176
193
 
177
- #### `createReactHostPages(options)` → `{ pages, resolve(path), component(spec) }`
194
+ ```bash
195
+ # Terminal one, inside remote-vue
196
+ npm run dev -- --port 5174 --strictPort
178
197
 
179
- - Data options (identical to Vue): `pages`, `remotePrefixes`, `deriveSpec`, `schema`, `strict`, `base`
180
- - Display options (same semantics as `remoteComponent`): `fallback`, `error`, `retries`, `timeout`; plus `beforeLoad: () => void | Promise<void>` — runs before **every actual load attempt** (including retries) so the host can refresh context; never at table creation
181
- - `component<P>(spec)` returns a React component type; the component cache is keyed by spec + login generation (rebuilt only on a new non-empty `sessionKey`; logout → `undefined` does not rebuild). Module-level caching of `component(spec)` results is supported — mounted pages still follow session changes
182
- - No `keepAliveNames` / no keep-alive promise (Vue-specific); routing is not a runtime dependency — render `component(spec)` output from your router (React Router examples in `examples/react/host`; route params reach remote pages as props)
183
- - Cross-framework Context: host and remote get the **same Context object** through the same expose instance; the plugin does not auto-bridge arbitrary React Contexts
198
+ # Terminal two, inside host-vue
199
+ npm run dev -- --port 5173 --strictPort
200
+ ```
184
201
 
185
- ### Async shared decisions and React version isolation (5.7.1)
202
+ Open `http://localhost:5173`. The remote button should count clicks, and the calculation should display `3`. pnpm projects can use `pnpm dev` instead.
186
203
 
187
- HTML entries configured with `runtimePlugins` negotiate and load shared dependencies before dynamically executing the application. Remote containers prepare shared decisions before executing exposes. Sync facades reuse the same decision and instance, including async hooks selecting a lower version or an external entry. Vite 8 consumer facades remain synchronous; the bootstrap boundary keeps negotiation waits out of consumer dependency cycles.
204
+ Complete projects and deployment configuration: [Vue examples](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/templates/vue-vue).
188
205
 
189
- Library/custom entries without HTML, and policies registered after application startup, must `await loadShare(name, opts)` before dynamically importing new consumers. Existing evaluated static bindings cannot be changed retroactively. An unprepared synchronous consumer still reports MFU-004 with `details.syncUnsupported`; late hook rejections are handled rather than becoming additional unhandled rejections. Local singleton adoption records the instance under its actual version, preserving strict version rejection. To run React 18 and 19 together, isolate React and its renderer in separate share scopes and bridge plain props/callbacks, not React elements or Context objects. See [the runnable version isolation demo](demo/react-versions/README.md).
206
+ ## React setup
190
207
 
191
- ### 8.2 Runtime API — `@fulgurjs/federation/runtime` (Vue apps) and common functions on `/react`
208
+ Use the same configuration structure with these changes:
192
209
 
193
- | Function | Signature | Semantics |
194
- |---|---|---|
195
- | `loadRemote` | `<T = Record<string, any>>(spec: string, opts?: { shareScope?: string; retries?: number; fallbackModule?: () => any }) => Promise<T>` | spec = `<remote>/<expose-key-without-./>`. Goes through container negotiation and the optional setup/onSession lifecycle. Modules cached per `remote@scope#module`; failures uncached and retryable. `fallbackModule` returns your module on failure **and** still emits the error event |
196
- | `loadShare` | `(name: string, opts?: LoadShareOptions) => Promise<any>` | shared-deps negotiation: `requiredVersion` (semver or `false`), `singleton`, `strictVersion`, `shareKey`, `shareScope`, `fallback: () => Promise<any>`. Highest satisfying version wins; loaded versions never replaced; singleton keeps one instance (warns MFU-010 when the reused version doesn't satisfy `requiredVersion`; throws MFU-003 with `strictVersion`) |
197
- | `initSharing` | `(scopeName?: string) => ShareScopeMap` (default `'default'`) | creates/returns the share scope map (usually called for you by the injected init) |
198
- | `registerShare` | `(scopeName, name, version, get: () => Promise<any>, opts?: { from?, eager?, loaded? }) => void` | register a provided shared module at runtime; first registration of a version wins |
199
- | `registerRemote` / `registerRemotes` | `(config: RemoteConfig) => void` / `((list: RemoteConfig[]) => void)` | runtime registration: `{ name, entry, shareScope?, timeout?, retries?, fallback?, breaker?, promise? }`. Promise-based remotes pass `promise: () => Promise<container>` |
200
- | `registerPlugins` | `(plugins: RuntimePlugin[]) => void` | register runtime plugins; each `init(hooks)` may set `resolveShare`, `beforeLoadRemote({ remote, module })`, `afterLoadRemote({ remote, module, module_ns })`, `onRemoteError({ remote, error })`. Observer-hook failures warn but never break loading |
201
- | `preloadRemote` | `(spec: string, opts?: { mode?: 'preload' \| 'prefetch' }) => Promise<void>` | manifest-driven preload of entry + expose chunks + CSS; `'prefetch'` = low priority. No lifecycle side effects (setup/onSession are NOT run) |
202
- | `getContainer` | `(name: string) => Promise<any>` | acquire the initialized container |
203
- | `getRuntime` | `() => FgRuntime` | the page-level runtime singleton (`globalThis.__FULGURJS_RUNTIME__`) |
204
- | `parseSpec` | `(spec: string) => { remote, module }` | synchronous spec parsing |
205
- | `shareScopeMap` | `ShareScopeMap` | live registry (debug surface: `window.__FULGURJS_SCOPE__`) |
206
- | `unwrapDefault` | `(ns: any) => any` | ESM/CJS default-interop helper |
207
- | `version` | `string` | plugin/runtime version |
208
- | `clearSessionState` | `() => void` | invalidate all remotes' session signals and onSession dedup state (called by `clearAppContext`) |
209
-
210
- Remote-registration config fields: `timeout` (ms, default 15000 — ends the caller's wait, never cancels the issued import), `retries` (0–10, default 2), `fallback: string[]` (spare entry URLs), `breaker: { threshold, resetMs }` (default 5 / 30s).
211
-
212
- ### 8.3 Plugin options — `federation(options)`
213
-
214
- | Option | Type / default | Notes |
215
- |---|---|---|
216
- | `name` | `string`, **required** | container name; unique per page; `/^[a-zA-Z][\w.-]*$/` |
217
- | `exposes` | `Record<string, string \| { import, name? }>` | key normalized to `./Key`; stable chunk name optional |
218
- | `remotes` | `Record<string, string \| RemoteEntryConfig \| (() => Promise<any>)>` | string = url or `name@url`; object = `{ external?, dev?, prod?, timeout?, retries?, fallback?, breaker?, shareScope? }`; function = promise-based remote (runtime-register instead) |
219
- | `shared` | `string[]` or `Record<string, string \| SharedHint>` | see below |
220
- | `setup` | `string` | module path; must default-export `setup(context)`, optional named `onSession(context)` |
221
- | `shareScope` | `string`, default `'default'` | default scope for provides |
222
- | `filename` | `string`, default `'fulgurjs-remoteEntry.js'` | fixed remoteEntry filename |
223
- | `manifest` | `boolean`, default `true` | emit `fulgurjs-manifest.json` |
224
- | `dts` | `boolean \| { dir?, mode?: 'source' \| 'shim' }`, default `true` | dev type generation (see §8.6) |
225
- | `devSharedSelf` | `boolean`, default inferred | pure remotes & dual-role apps: `true` (dev shared rewriting); pure hosts: `false` |
226
- | `devCorsOrigins` | `'*'` or `string[]` | dev endpoints + server.cors share the policy; explicit user `server.cors` wins |
227
- | `devFsRoot` | `boolean`, default `true` | dev manifest carries local fsRoot for type direct-connect; `false` → host falls back to `any` stubs |
228
- | `runtimePlugins` | `string[]` | modules default-exporting a `RuntimePlugin` |
229
-
230
- `SharedHint` fields: `import` (local specifier or `false` = pure consumer), `packageName` (infer `requiredVersion` from a different package name), `requiredVersion` (semver or `false`), `singleton`, `strictVersion` (default: `true` when a local fallback exists and not singleton, webpack-aligned), `shareKey`, `shareScope`, `eager`, `version`.
231
-
232
- ### 8.4 Lifecycle — `setup` / `onSession`
210
+ 1. Use `@vitejs/plugin-react` in `vite.config.ts`, followed by `federation(fulgurjsConfig)`.
211
+ 2. Expose `./Button` from `./src/Button.tsx`; configure the remote's address in the host.
212
+ 3. Both applications use compatible React/renderer versions and share:
233
213
 
234
214
  ```ts
235
- // federation({ setup: './src/fulgurjs/setup.ts' })
236
- export default async function setup(ctx: { appContext: Record<string, any>; sessionKey?: string; signal: AbortSignal }) {
237
- // app-level: once per app, before the first business module is returned
238
- }
239
- export async function onSession(ctx: { appContext: any; sessionKey: string; signal: AbortSignal }) {
240
- // session-level: once per host sessionKey (login generation); re-login re-runs, logout invalidates
215
+ shared: {
216
+ react: { singleton: true, strictVersion: true },
217
+ 'react-dom': { singleton: true, strictVersion: true },
241
218
  }
242
219
  ```
243
220
 
244
- - Failures reject the triggering `loadRemote` (MFU-011/012) and are retryable; already-succeeded stages are not re-run
245
- - `signal` aborts on logout/session change — check `signal.aborted` before writing async results
246
- - `preloadRemote` / `getContainer` never trigger the lifecycle
247
- - Remote declares `onSession` → the host **must** provide a non-empty `sessionKey` (MFU-013); never use a token as sessionKey
248
- - No-setup remotes (plain public components) load normally without any context
249
-
250
- ### 8.5 AppContext — cross-app values
251
-
252
- - `provideAppContext(partial)` — merge-write the page-level singleton (idempotent; later writes win). Host bridge calls it after login and re-calls on account change; then triggers its own re-render
253
- - `getAppContext()` — read the snapshot (`CC-002` if loaded outside the host federation)
254
- - `requireAppContext(...keys)` — validated read; missing keys → `CC-001` with got/expected/example
255
- - `clearAppContext()` — delete context + invalidate session signals/dedup (module and share caches, and completed app-level setup, are preserved). Logout must call it before unmounting authed UI
256
- - Standard fields: `user`, `getToken()`, `store` (host pinia), `hostApp` (host Vue app), `locale`, `events`, `sessionKey` — plus arbitrary extension keys. Transport snapshot + function references; not reactive
221
+ Remote `src/Button.tsx`:
257
222
 
258
- ### 8.6 Dev types (dual-track)
259
-
260
- - Zero config: ambient declarations per expose — imports resolve, exports typed `any`; setup entry never generates declarations
261
- - Precise track: add `"paths": { "<remote>/*": ["<typesDir>/<remote>.d/*"] }` to the app's **effective TS context** — `tsconfig.json` itself, its `extends` chain, or a referenced sub-project whose `include` covers the app source / types output dir. Standalone `tsconfig.test.json`, `tsconfig.node.json` (vite.config only) and other unrelated configs do not affect the decision; imports then resolve through forwarder modules to **source-level types** (wrong props/arguments fail compilation). Remotes covered by paths automatically skip their loose declaration to avoid shadowing
223
+ ```tsx
224
+ import { useState } from 'react'
262
225
 
263
- Type generation supports string or array `extends` (later entries override earlier entries) and directory `references`; inherited paths retain their declaring directory. `baseUrl` and `paths` inherit independently. If application contexts disagree on remote `paths`, the plugin keeps loose declarations and reports a diagnostic; align application mappings to enable precise types. Lifecycle errors (`MFU-012`) retain the original setup/onSession exception in `cause`.
264
- - `devFsRoot: false` or unreachable source: degrades to resolvable `any` declarations and cleans stale precise-track files (precise → degrade → restore cycles compile cleanly)
265
- - `dts: false` stops generation without deleting existing output; `dts.dir` relocates; `mode: 'shim'` gives loose IDE-clean placeholders
266
- - Precise track requires the host and remote to share a filesystem (same-machine dev); verified bounds: React 18.0.0–19.x with matching @types
226
+ export default function Button({ label }: { label: string }) {
227
+ const [count, setCount] = useState(0)
228
+ return <button onClick={() => setCount(count + 1)}>{label}: {count}</button>
229
+ }
230
+ ```
267
231
 
268
- ### 8.7 Cross-framework bridge — `/bridge` (sub-app-level Vue↔React, 5.3.0+)
232
+ Host `src/App.tsx`, with a remote configured as `remote-react`:
269
233
 
270
- **Scope**: whole-app mount/unmount embedding both ways — a Vue 3 host mounts a React 18/19 sub-app, and a React host mounts a Vue 3 sub-app. Component-level conversion, Angular, SSR/RSC, JS sandbox, CSS isolation are out of scope (§12). Sub-app internal route ↔ browser URL sync is available since 5.4.0 (§8.8).
234
+ ```tsx
235
+ import { remoteComponent } from '@fulgurjs/federation/react'
271
236
 
272
- #### Entries & import graph
237
+ // Create once at module scope, not on every render.
238
+ const RemoteButton = remoteComponent<{ label: string }>('remote-react/Button', {
239
+ fallback: <p>Loading…</p>,
240
+ })
273
241
 
274
- ```text
275
- build @fulgurjs/federation -> the Vite plugin (unchanged)
276
- Vue sub-app @fulgurjs/federation/runtime -> defineBridgeApp (zero React)
277
- React sub-app @fulgurjs/federation/react -> defineBridgeApp (zero Vue; react-dom/client loads at mount time)
278
- bridge host @fulgurjs/federation/bridge/vue -> createVueBridgeApp (recommended for Vue hosts; zero React)
279
- @fulgurjs/federation/bridge/react -> createReactBridgeApp (recommended for React hosts; zero Vue)
280
- @fulgurjs/federation/bridge -> aggregate (kept for compatibility; dev native ESM executes both host adapters)
242
+ export default function App() {
243
+ return <RemoteButton label="Remote React button" />
244
+ }
281
245
  ```
282
246
 
283
- **The split entries are the recommended usage**: a Vue host that only uses `createVueBridgeApp` never executes the React host adapter — in dev native ESM and in the production bundle (asserted by e2e request graphs). The aggregate `/bridge` tree-shakes in production but has no such guarantee in dev.
247
+ React also imports `loadRemote` and `useLoadRemote` from `/react` for ordinary modules. Complete projects: [React examples](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/templates/react-react).
284
248
 
285
- **Dual-framework install contract (required)**: the bridge host installs `vue` + `react` + `react-dom` and configures all three as `singleton: true` in `shared`. Sub-apps install and share only their own framework. Pure single-framework projects are unaffected. Missing singletons is a usage violation — the plugin runs the negotiation mechanism honestly (double-instance symptoms such as Invalid hook call are documented, not intercepted).
249
+ ## Embed Vue and React in each other
286
250
 
287
- #### Sub-app side: `defineBridgeApp` (`/runtime` and `/react`, same name)
251
+ **A bridge embeds a sub-app with its own component tree. It does not convert a React component into a Vue component.**
288
252
 
289
- The remote's `./bridge` expose module **default-exports** the contract object; the plugin validates that `mount`/`unmount` are functions (`MFU-015` otherwise).
253
+ For a Vue host embedding React:
290
254
 
291
- ```ts
292
- // Vue sub-app src/bridge.ts
293
- import { createApp } from 'vue'
294
- import { createMemoryHistory, createRouter } from 'vue-router'
295
- import { defineBridgeApp } from '@fulgurjs/federation/runtime'
296
- export default defineBridgeApp((props) => {
297
- const app = createApp(App, props)
298
- app.use(createRouter({ history: createMemoryHistory(), routes }))
299
- return app
300
- })
301
- ```
255
+ 1. React remote `src/bridge.tsx`:
302
256
 
303
257
  ```tsx
304
- // React sub-app src/bridge.tsx
305
- import { MemoryRouter } from 'react-router-dom'
306
258
  import { defineBridgeApp } from '@fulgurjs/federation/react'
307
- export default defineBridgeApp((props) => <MemoryRouter><App {...props} /></MemoryRouter>)
308
- ```
309
259
 
310
- Contract semantics (`BridgeApp`):
311
- - `mount(el, props?): void | Promise<void>` — returning `void` means the first root commit completed synchronously (Vue); a Promise keeps the host pending until the first root commit completes (React uses a built-in commit probe; `root.render()` returning does **not** count as success). Failure before the first commit must throw/reject (host turns it into `MFU-016`, `details.phase: 'mount'`) after cleaning up any created root.
312
- - `unmount(el): void` — synchronously invalidates the current generation for that container and cleans up; unknown containers are a no-op. Unmounting while pending immediately invalidates the in-flight generation: late results must not revive DOM, overwrite host state, or produce unhandled rejections. An `unmount` throw is reported as `MFU-016` (`phase: 'unmount'`); the container's cleanup state is uncertain, and the plugin **permanently blocks that container**: neither the in-page retry nor a session change will mount a new instance there (the default placeholder removes its retry button), so a full page reload is the only recovery; audit leftover resources (subscriptions/timers/global side effects) honestly.
313
- - Contract instances are keyed **per container element**; double-mount on the same container is rejected (`MFU-016`).
314
- - Errors inside the sub-app after the first commit belong to **the sub-app's own error boundary** — host boundaries cannot catch cross-root render errors.
260
+ export default defineBridgeApp((props) => (
261
+ <section>React sub-app: {String(props.message ?? '')}</section>
262
+ ))
263
+ ```
315
264
 
316
- #### Host side: `createVueBridgeApp` / `createReactBridgeApp`
265
+ 2. Add `exposes: { './bridge': './src/bridge.tsx' }` to the remote config.
266
+ 3. Configure the remote address in the Vue host, then use:
317
267
 
318
- ```ts
319
- // Vue host
268
+ ```vue
269
+ <script setup lang="ts">
320
270
  import { createVueBridgeApp } from '@fulgurjs/federation/bridge/vue'
321
- const RemoteReactApp = createVueBridgeApp('bridge-react-remote/bridge', {
322
- retries: 1,
323
- getContext: () => getLatestHostContext(), // your own synchronous pure getter
324
- })
325
- // <RemoteReactApp :session-key="loginKey" :app-props="{ userId, onReady }" />
326
- ```
271
+ const RemoteApp = createVueBridgeApp<{ message: string }>('remote-react/bridge')
272
+ </script>
327
273
 
328
- ```tsx
329
- // React host
330
- import { createReactBridgeApp } from '@fulgurjs/federation/bridge/react'
331
- const RemoteVueApp = createReactBridgeApp('bridge-vue-remote/bridge', { getContext: () => getLatestHostContext() })
332
- // <RemoteVueApp sessionKey={loginKey} appProps={{ userId, onReady }} />
274
+ <template>
275
+ <RemoteApp :app-props="{ message: 'From Vue host' }" />
276
+ </template>
333
277
  ```
334
278
 
335
- | Item | `createVueBridgeApp` | `createReactBridgeApp` |
279
+ A cross-framework host installs and shares `vue`, `react` and `react-dom`. The child installs and shares its own framework. React and react-dom must be compatible; multiple React majors need separate dependency groups and consumers, as shown in the isolation demo.
280
+
281
+ In the other direction, use `createReactBridgeApp` in the React host. The Vue child uses `defineBridgeApp` from `/runtime` and returns a `createApp(...)` application.
282
+
283
+ ### The shortest bridge mental model
284
+
285
+ | Question | Answer |
286
+ |---|---|
287
+ | Which frameworks does each side install? | The bridge host installs and shares `vue` + `react` + `react-dom` (all three `singleton: true`); the child installs and shares only its own framework. This contract is mandatory — a missing key produces double instances (Invalid hook call / broken state) |
288
+ | What do `shared` / `singleton` do here? | They make host and child use the **same** framework instance; `singleton` converges instances but does not make incompatible majors compatible |
289
+ | How does the child export its entry? | The `./bridge` expose file **default-exports** the value returned by `defineBridgeApp(...)`; missing `mount`/`unmount` fails with MFU-015 |
290
+ | How does the host mount it? | `createVueBridgeApp('remote/bridge')` / `createReactBridgeApp(...)` return a component: render to mount, remove to unmount; pass `appProps` and optional `sessionKey` |
291
+ | When is URL sync needed? | Only when refresh/share/back-forward must restore the child's internal page (`routing` + `basePath`, configured on both ends). Without it, child navigation not touching the host URL is normal behavior |
292
+ | How do sessions and unmount work? | Login generations use `sessionKey` (new generation re-runs `onSession`; logout → `null` unmounts and empties). Unmount is driven by the host component lifecycle; a child cleanup throw blocks that container until a full page reload |
293
+
294
+ ### `appProps` is a mount-time snapshot, not reactive props
295
+
296
+ Top-level fields are shallow-copied at mount; later host-side replacements do **not** update the child. Three channels for live data:
297
+
298
+ | Situation | Use | Cost |
336
299
  |---|---|---|
337
- | Factory options | `loadingComponent?` `errorComponent?` (receives `error`; full takeover) `retries?` (0–10) `timeout?` `getContext?` | `fallback?` `error?` (node or `(error, retry) => ReactNode`) `retries?` `timeout?` `getContext?` |
338
- | Component props | `appProps: P` + `sessionKey?: string \| null` (control prop, never mixed into business props) | same |
339
- | Error placeholder | Chinese diagnostic (code + root cause + fix) with retry / full-reload buttons | same |
300
+ | Child needs current host values (token, user name…) | Pass a **stable callback** (`getToken: () => store.token`) — calls read the latest value | No remount; good for "reads" |
301
+ | Both sides share one state | Pass the host store instance via `appProps` or AppContext; both subscribe to the same instance | Reactivity does not cross roots; the child subscribes explicitly |
302
+ | Must re-initialize with new props | Change the bridge component's `key` to remount explicitly (full unload → reload chain) | All state resets; do not trigger frequently |
303
+
304
+ This differs from ordinary component props on purpose — it is a structural property of cross-root mounting, not a bug. Separate component trees do not inherit Context, provide/inject or routers. Pass or install what is needed explicitly. Use `remoteComponent` for a same-framework component; use a bridge for a sub-app.
305
+
306
+ Complete bidirectional setup and login/cleanup flows: [bridge examples](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/templates).
340
307
 
341
- - **`appProps` snapshot**: shallow-copied top-level fields at mount time; nested objects, reactive stores and functions keep their original references. Later top-level replacements are not tracked (use `:key` / React `key` to remount). Cross-root inheritance (Vue provide/inject, Pinia, React Context, routers) does not happen — pass what is needed explicitly.
342
- - **`getContext`**: a synchronous, side-effect-free getter called before each actual load (first load, retry, session switch). Non-object/thenable returns → `MFU-016` (`phase: 'getContext'`). The bridge validates the snapshot's `sessionKey` against the controlled value (`MFU-017` on mismatch, without writing global state), then writes `provideAppContext` itself. On generation change the bridge clears the previous account context first (zero residue).
343
- - **Controlled `sessionKey`**: accepts `undefined` (no controlled validation) / `null` (logged out: unmount immediately, keep the container empty, stop loading) / non-empty string (login generation). Illegal values → `MFU-017`.
344
- - **Multi-instance**: several same-spec instances coexist (per-el keying); `AppContext` is a page-level singleton — all controlled instances on a page must share the same session (`MFU-017` otherwise). React StrictMode double-effect is safe. Vue `<KeepAlive>` deactivation is **not** an unmount. Late results from invalidated generations are dropped by generation guards; a remote `onSession` must honor the existing `signal.aborted` contract.
308
+ ## Keep child routes in the browser URL
345
309
 
346
- ### 8.8 Bridge URL sync — `/bridge/router/*` (sub-app internal routes ↔ browser URL, 5.4.0+)
310
+ Bridging does not change the host URL by default. Enable URL sync to map:
347
311
 
348
- The bridge defaults to memory routing: internal navigation does not touch the browser URL and refresh cannot restore the sub-app's internal page. URL sync makes the **host URL express the sub-app's internal location** — deep links, refresh, bookmarks, back/forward and host-menu navigation all agree. It is opt-in; **default is off** (5.3.x behavior and legacy contracts unchanged).
312
+ ```text
313
+ Host /approval/list → Child /list
314
+ Host /approval/detail/42 → Child /detail/42
315
+ ```
349
316
 
350
- **Architecture**: the host router is the only writer of browser history; the sub-app uses a controlled memory router; both sides communicate over a dedicated routing channel (not appProps/Context); path/search/hash changes within an instance **do not remount the root, do not rebuild stores, do not reload the remote**.
317
+ Configure both sides:
351
318
 
352
- **Host (Vue Router 4, history or hash mode)**: declare a suffix route (`/approval/:pathMatch(.*)*` — without it detail navigation unmounts the sub-app), add real guards (`beforeEach` rejecting → the channel receives `cancelled`, URL/history/sub-app position unchanged), then `createVueBridgeNavigation(router)` (Vue Router already removes its history base) and pass `routing={{ basePath: '/approval', navigation }}` to the bridge component.
319
+ 1. The host router must handle all child paths under `/approval` without unmounting the child on each detail navigation.
320
+ 2. Pass `routing` to the host bridge component, including `basePath: '/approval'` and the host navigation adapter.
321
+ 3. The child declares `defineBridgeApp(..., { routing: true })` and connects a controlled memory router.
353
322
 
354
- **Sub-app**: declare the protocol and wire a controlled router —
355
- Vue: `defineBridgeApp(async (props, ctx) => { const router = createRouter({ history: createMemoryHistory(), routes }); await connectVueBridgeRouter(ctx.routing!, router).ready; ... app.use(router); return app }, { routing: true })` (await ready BEFORE `app.use(router)` — the install-time initial navigation would otherwise override the deep-link location).
356
- React: `createReactBridgeRouter(ctx.routing!, routes).element` — `createMemoryRouter`-based; `Link`/`useNavigate` work unmodified.
323
+ Vue uses `createVueBridgeNavigation` / `connectVueBridgeRouter`; React uses `createReactBridgeNavigation` / `createReactBridgeRouter`. React hosts need a data router (`createBrowserRouter` or `createHashRouter`), not `BrowserRouter`. Built-in adapters support Vue Router 4 and React Router ≥6.11.
357
324
 
358
- **Host (React Router)**: data routers only (`createBrowserRouter`/`createHashRouter` + `RouterProvider`); `createReactBridgeNavigation(router, { basename, canNavigate })`. `canNavigate` is an optional early rejection policy. The adapter also observes the real blocker: it waits for `reset()` (cancelled) or a committed navigation after `proceed()`. Resolving `router.navigate()` alone does not imply a commit. Declarative `BrowserRouter` has no cancellation semantics and is not supported. Requires react-router ≥ 6.11.
325
+ Refresh, shared links and browser history restore the route, **not form contents or business data**. See [routing API](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/API.en.md#url-sync) and the runnable [router demo](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/templates/showcase).
359
326
 
360
- **Lifecycle and navigation**: both child connectors accept an optional third argument `{ signal?: AbortSignal }`; pass `ctx.signal` to dispose on session invalidation, or call `connection.dispose()` yourself. Push and replace retain their history action; numeric navigation delegates to the host history. Concurrent requests are serialized and superseded requests are invalidated. Navigation errors reject with MFU-033 and preserve `cause`; they are not reported as cancellation. Custom host ports receive an optional third argument `{ signal }` and must check it before asynchronous commits.
327
+ ## User data and remote initialization
361
328
 
362
- **Contract highlights**: `basePath` is a static absolute path from the host-router perspective (segment-matched; conflicting/overlapping prefixes rejected, `MFU-030`); location is compared and preserved as three raw strings (duplicate query keys, encoding, fragments survive without re-encoding); cancellation never auto-retries; session switch (`sessionKey→null`) invalidates the old channel — late navigations are rejected and never write the URL; KeepAlive-cached instances pause routing writes; enabling sync against a contract without `{ routing: true }` shows `MFU-031` instead of silently falling back to memory; escaping targets and illegal `go` arguments → `MFU-032`; redirect loops beyond 5 internal replaces → `MFU-033` with the chain attached. Router libraries are optional peers consumed only through the two opt-in entries (`/bridge/router/vue`, `/bridge/router/react`, each gated ≤ 4096B gzip); the default entries never load a router library. Not promised: SSR/RSC, cross-window, nested multi-level bridge routing proxies, TanStack Router and other libraries (extend via the `BridgeHostNavigation`/`BridgeChildRoute` ports).
329
+ These features are optional. A plain button or utility module does not need them.
363
330
 
364
- ## 9. Artifacts, endpoints & caching
331
+ | Need | API | When |
332
+ |---|---|---|
333
+ | Provide user, token getter, store, etc. | `provideAppContext` | Host supplies them before loading business modules |
334
+ | Read host values | `getAppContext` / `requireAppContext` | Called by remote business code |
335
+ | Initialize a remote once | Default export in configured `setup` file | Before the first business `loadRemote('remote/module')` returns |
336
+ | Synchronize permissions after login/account changes | Named `onSession` export in the same file | Deduplicated by `sessionKey` |
337
+ | Clear account context on logout | `clearAppContext` | Host logout flow; host also removes private pages/caches |
365
338
 
366
- | Artifact | Cache policy |
367
- |---|---|
368
- | `fulgurjs-remoteEntry.js` (fixed filename, content changes every build) | **`no-cache`** |
369
- | content-hashed chunks / CSS | `immutable` long cache |
370
- | `fulgurjs-manifest.json` | `no-cache` (consumed by `preloadRemote` / `check-pages` / `doctor`) |
371
- | dev endpoints `/@fulgurjs-entry.js` / `/@fulgurjs-manifest.json` | `no-cache`, CORS per `devCorsOrigins` |
339
+ `sessionKey` identifies a login attempt; it is **not a token or authorization credential**. Generate a new value on login/account change; token refresh alone retains it.
340
+
341
+ A bridge can read current data using `getContext`. Controlled `sessionKey: null` means logged out: unmount and stop loading. Omitting the key disables controlled session switching.
342
+
343
+ Only a configured `setup` file participates in initialization. `preloadRemote` fetches resources without running setup/onSession. Async initialization must check `context.signal.aborted` before writing state, so late responses do not restore old-account data.
344
+
345
+ See the [API reference](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/API.en.md#context).
346
+
347
+ ## Several remote pages
348
+
349
+ Maintain a page table and pass it to `createHostPages` (Vue) or `createReactHostPages` (React). These helpers resolve modules, cache loading components and provide loading/error states. **They do not create your host Router.**
350
+
351
+ The table records the host `route` and the remote expose `spec` (omit `./` and do not repeat the remote name); `remotePrefixes` selects the remote. For example, `/shop/home`, `spec: 'pages/Home'` and `remotePrefixes: { '/shop': 'shop' }` resolve to `shop/pages/Home`. Vue can use KeepAlive for component state; React has no equivalent keep-alive promise here.
352
+
353
+ See [page API](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/API.en.md#pages) and [page demo](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/demos/pages-cli).
372
354
 
373
- Lazy-loading measurement layers: ① nothing until first render of a remote component/page; ② container entry + shared metadata on first load; ③ the expose chunk; ④ shared-dependency bodies (negotiated, possibly already loaded). `preloadRemote(spec)` fetches ②③④ without executing lifecycle code. Verify with real network records — count URLs and transferred bytes per layer, cold cache vs revisit.
355
+ ## Build and deploy
374
356
 
375
- ## 10. Debugging surfaces
357
+ Build each application separately with its own `npm run build`. The remote produces `fulgurjs-remoteEntry.js` and `fulgurjs-manifest.json` by default. The host locates them through `prod`.
376
358
 
377
- - `window.__FULGURJS_SCOPE__` — live share-scope registry
378
- - `window.__FULGURJS_INFO__` — per-remote status/latency/errors + `errors` log
379
- - `DEBUG=fulgurjs:*` — controlled pipeline diagnostics (off by default)
380
- - Runtime diagnostics are emitted in Chinese by design (language policy); codes are stable identifiers listed below
359
+ Check these settings:
381
360
 
382
- ## 11. Error codes (48)
361
+ - Remote deployment `/remote-vue/` → remote Vite `base: '/remote-vue/'` and host `prod: '/remote-vue'`.
362
+ - HTML, remoteEntry and manifest use `Cache-Control: no-cache`; content-hashed chunks can use long-lived caching.
363
+ - SPA routes support refresh; missing resource URLs return 404 rather than HTML.
364
+ - Cross-origin deployments need production CORS headers; dev settings do not configure the production server.
365
+ - Keep chunks still referenced by old pages available during releases, or use a deployment flow that avoids mixed versions.
383
366
 
384
- | Segment | Code | Meaning |
367
+ Deployment examples: [Vue](https://github.com/chenmingye/fulgurjs-federation/blob/master/examples/templates/vue-vue/README.md) / [React](https://github.com/chenmingye/fulgurjs-federation/blob/master/examples/templates/react-react/README.md).
368
+
369
+ ## Handle failures
370
+
371
+ | Symptom | Check | Recovery |
385
372
  |---|---|---|
386
- | CFG | `CFG-001` | name missing or invalid |
387
- | | `CFG-002` | exposes shape invalid |
388
- | | `CFG-003` | remotes shape invalid / illegal key characters |
389
- | | `CFG-004` | shared shape invalid |
390
- | | `CFG-005` | remotes key collides with a shared key |
391
- | | `CFG-006` | island config (neither provides nor consumes) |
392
- | | `CFG-007` | `name@` prefix misuse in object-form remotes |
393
- | | `CFG-008` | shared illegal combo (eager+import:false / duplicate shareKey) |
394
- | | `CFG-009` | remote runtime params invalid (timeout/retries/breaker) |
395
- | | `CFG-010` | devCorsOrigins invalid |
396
- | | `CFG-011` | removed no-op option (any value errors with migration hints) |
397
- | | `CFG-012` | setup config invalid / reserved expose key squatted |
398
- | DEV | `DEV-001` | remote dev server unreachable (manifest fetch failed) |
399
- | | `DEV-002` | remote dev manifest empty or unrecognized |
400
- | | `DEV-004` | known UMD-only dep missing from optimizeDeps.include |
401
- | | `DEV-005` | remotes dev URL port not listening |
402
- | | `DEV-006` | host/remote plugin version mismatch |
403
- | | `DEV-009` | facade/virtual module 404 (.vite cache drift — clear and restart) |
404
- | | `DEV-010` | dev cold-start pre-bundle window notice (transient) |
405
- | | `DEV-011` | non-loopback host + wildcard dev CORS reminder |
406
- | | `DEV-012` | non-loopback host + fsRoot disclosure reminder |
407
- | BLD | `BLD-001` | expose source resolution failed |
408
- | | `BLD-002` | build target below es2022 |
409
- | | `BLD-003` | expose target declares required props (documented checklist) |
410
- | | `BLD-006` | array-form output prevents automatic facade chunk isolation |
411
- | MFU | `MFU-001` | remote container/module load failure (network / timeout / retries exhausted / breaker) |
412
- | | `MFU-002` | remoteEntry self-reported name mismatch |
413
- | | `MFU-003` | strictVersion requirement not satisfied |
414
- | | `MFU-004` | shared module missing with no local fallback |
415
- | | `MFU-005` | same container re-initialized with a different share scope |
416
- | | `MFU-006` | requested module not exposed by the remote |
417
- | | `MFU-007` | preload failed (non-blocking) |
418
- | | `MFU-008` | unknown remote |
419
- | | `MFU-009` | loaded module has no exports at all |
420
- | | `MFU-010` | reused singleton version doesn't satisfy the consumer requirement (warn-once) |
421
- | | `MFU-011` | setup entry export shape invalid |
422
- | | `MFU-012` | setup/onSession threw (retryable; only the failed stage resets) |
423
- | | `MFU-013` | onSession declared but host sessionKey missing |
424
- | | `MFU-014` | setup/onSession synchronously re-loading the same remote (deadlock guard) |
425
- | | `MFU-015` | bridge contract invalid (`./bridge` default export missing non-function mount/unmount; fix points to `defineBridgeApp`) |
426
- | | `MFU-016` | bridge preparation or lifecycle failure (`details.phase` = getContext/mount/unmount; cause keeps the sub-app's original error) |
427
- | | `MFU-017` | bridge session mismatch (controlled sessionKey vs AppContext / illegal value / page-level single-session conflict) |
428
- | MFU | `MFU-030` | Bridge URL-sync config invalid / prefix conflict (illegal basePath: empty, root, query/hash/wildcard; overlapping active prefixes) |
429
- | MFU | `MFU-031` | Bridge routing protocol missing / channel destroyed (sub-app not declared with `{ routing: true }`; disposed channel reused) |
430
- | MFU | `MFU-032` | Bridge illegal navigation (target escaping its own prefix, illegal `go` argument, request on a dead channel) |
431
- | MFU | `MFU-033` | Bridge routing preparation/sync failed (redirect limit or navigation exception, chain/cause attached; no silent fallback to memory) |
432
- | CC | `CC-001` | AppContext required key missing (got/expected/example) |
433
- | | `CC-002` | runtime singleton unavailable (standalone remote page) |
434
-
435
- ## 12. Boundaries (explicitly not supported)
436
-
437
- - Support covers **browser-client** federation for Vue 3 and React 18–19. Not supported: SSR, React Server Components, Next.js full-stack, React Native, Node-side remote loading. **Cross-framework boundary (5.3.0+)**: sub-app-level embedding is supported (§8.7 `/bridge`); direct component-level Vue↔React rendering in one tree is not (that is the product of framework-conversion libraries). Pure single-framework projects keep zero cross-dependency
438
- - **Bridge isolation boundary (declared honestly in §8.7)**: bridging isolates only the mount/unmount edge of the two component trees — no browser realm isolation. Remote global CSS, `body`/`html` styles, global variables, and DOM rendered outside the container via React Portal / Vue Teleport still affect the host; `unmount` cannot revoke CSS the browser already loaded. Sub-app internal errors do not bubble into host error boundaries (cross-root). Sub-app routing defaults to memory mode; explicit URL sync exists since 5.4.0 (§8.8) — when it is not enabled, refreshing does not restore the sub-app's internal path
439
- - React side does not promise component keep-alive (`keepAliveNames` is Vue-only); re-opened pages still reuse downloaded modules
440
- - Cross-origin Fast Refresh: remote React components update via the remote dev server's HMR push; after a cold start the first round often needs a host refresh — component-state retention across the federation boundary is not promised
441
- - Not compatible with originjs `virtual:__federation__` legacy imports
442
- - No browser DevTools extension (the `window.__FULGURJS_*` surfaces serve debugging)
443
-
444
- ## 13. Documentation & examples
445
-
446
- - [Migration guide (Chinese)](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/迁移指南.md) — a real qiankun → federation migration (seven steps + acceptance checklist)
447
- - [webpack MF comparison & gaps (Chinese)](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/webpack-mf-对照与缺口.md)
448
- - [Sandbox boundary audit (Chinese)](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/沙箱边界审计.md)
449
- - [`DESIGN.md`](https://github.com/chenmingye/fulgurjs-federation/blob/master/DESIGN.md) — architecture and alignment tables
450
- - Examples: [`examples/vue/{host,remote}`](./examples) + [`examples/react/{host,remote}`](./examples) + [`examples/bridge/*`](./examples) — copy-and-run projects, registry-installable (see the examples entry page)
451
-
452
- ## 14. Development & testing
373
+ | Remote unavailable | Server, address, CORS | Timeout/retry/error UI; optional backup entry or fallback module |
374
+ | Module missing | remotes name and exposes key | Fix the name and retry |
375
+ | Shared version incompatible | Installed versions, requiredVersion, strictVersion, scope | Align or isolate versions |
376
+ | Static dependency remains failed after service recovery | Browser may retain the failed dependency URL | User-initiated refresh preserves the current address |
377
+ | Child unmount fails | Child cleanup, timers and subscriptions | Container stays blocked; refresh and fix cleanup |
378
+
379
+ `remoteComponent` and bridge components provide default error UI. Direct `loadRemote` calls and React `useLoadRemote` require application error handling. An explicit `fallbackModule` does not repair the original remote.
380
+
381
+ Errors include a code, cause and fix. See [error codes](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/API.en.md#error-codes).
382
+
383
+ ## Vite 8 and support boundaries
384
+
385
+ **Supports Vite 8 development and production. The earlier large-application startup hang has been fixed and relevant regression tests pass.**
386
+
387
+ Two practical details:
388
+
389
+ - A first dev visit may reload while Vite prepares newly discovered dependencies. Wait for optimization before judging stable behavior. This is not a production behavior on every visit.
390
+ - Some shared scenarios fetch an unused local library copy. One singleton scope still uses one instance; explicitly isolated React 18/19 scopes may use one each. Downloaded file count and active instance count are different.
391
+
392
+ Not provided: SSR/RSC, Node-side federation, React Native, automatic JS/CSS isolation, webpack `script/var` artifact interoperability, component-type conversion, automatic multi-level bridge routing proxies or cross-window route sync. Global CSS/variables can affect the host; children need their own internal error handling.
393
+
394
+ See the [capability comparison](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/webpack-mf-对照与缺口.md) for detailed boundaries and differences from webpack.
395
+
396
+ ## Debugging, types and CLI
397
+
398
+ Run in the application directory:
453
399
 
454
400
  ```bash
455
- pnpm --dir packages/plugin install && pnpm --dir packages/plugin build
456
- for app in fixtures/host-vue fixtures/remote-a fixtures/remote-b fixtures/remote-auto fixtures/host-auto fixtures/remote-react fixtures/host-react e2e; do pnpm --dir "$app" install; done
457
- pnpm --dir e2e exec playwright install chromium
458
-
459
- pnpm test:unit # full unit suite
460
- pnpm test:dev # Vue + React: dev and fault (four projects)
461
- pnpm test:prod # Vue + React: prod (two projects), isolated NGINX; cleans up after tests
462
- pnpm test # unit + all dev/fault + all prod projects
463
- pnpm --dir e2e exec playwright test --list # inspect unique cases and project ownership
464
- node e2e/scripts/pack-smoke.mjs # local tarball consumer checks; not registry acceptance
465
- node e2e/scripts/react-types-check.mjs # dual-track dev types + negative matrix
466
- node e2e/scripts/react-negative-check.mjs # N08/N10/N11 negative checks
467
- bash e2e/scripts/prod-setup.sh # build all fixtures + isolated NGINX
401
+ # No existing project: create a complete project from a template (see "Starting fresh")
402
+ npx @fulgurjs/federation create
403
+
404
+ # Existing project: generate a federation config starter file
405
+ npx fulgurjs init # --template <path> sets the output path (a path, not a template id)
406
+ npx fulgurjs explain
407
+
408
+ # Optional: validate a configured host page table
409
+ npx fulgurjs check-pages --site http://localhost:5173
410
+
411
+ # After deployment under /remote-vue/, substitute your actual site:
412
+ npx fulgurjs doctor --base https://your-site.example --apps remote-vue
468
413
  ```
469
414
 
470
- CI: unit + dual typecheck + build gates (gzip, error-code consistency); e2e Vue+React suites across Vite 6.4.3 / 7.3.6 / 8.3.0; scheduled Vite 5.1 floor job; prod-e2e; tarball consumer smoke (Vue + React).
415
+ For `doctor`, `--base` is the site URL and `--apps` lists **deployment subdirectories** (a remote deployed under `/remote-vue/` is `remote-vue`): the example checks `https://your-site.example/remote-vue/`. It does not infer a different development port from a container name.
416
+
417
+ `init` creates a federation config template, not a full application, router or Nginx configuration (use `create` for a new complete project). `check-pages` compares the page table with remote exposes; an unreachable remote is reported as unverified.
418
+
419
+ Remote dev types are generated by default. Accessible source provides more precise mapping; inaccessible source produces `any` declarations without precise checks/completion. Set `dts: false` to disable generation. See the reference for details.
420
+
421
+ Advanced diagnostics use `window.__FULGURJS_SCOPE__`, `window.__FULGURJS_INFO__` and `FULGURJS_DEBUG`. Normal integration does not require editing these objects.
422
+
423
+ ## API reference
424
+
425
+ Use the current reference rather than guessing signatures from old task documents:
426
+
427
+ - [Plugin options and defaults](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/API.en.md#plugin-options)
428
+ - [Runtime loading, registration and hooks](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/API.en.md#runtime)
429
+ - [Bridge props, sessions and cleanup](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/API.en.md#bridge)
430
+ - [URL sync and navigation](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/API.en.md#url-sync)
431
+ - [Chinese API reference](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/API.md)
432
+
433
+ ### When an AI implements your integration
434
+
435
+ Specify the framework, whether you need a component or sub-app, remote URLs/expose names, and whether login switching or URL sync is required. Have it read the guide and relevant API section first, preserve the existing Vite configuration, check installed versions and use the correct browser entry. It should not invent configuration fields. Verify mounting, interaction and error handling; URL sync also needs deep-link refresh, history and cancellation checks.
436
+
437
+ ## Documentation
438
+
439
+ - [Demo catalog](https://github.com/chenmingye/fulgurjs-federation/blob/master/examples/demos/README.md): setup and runnable scenarios.
440
+ - [Copy-and-run templates](https://github.com/chenmingye/fulgurjs-federation/tree/master/examples/templates): five pnpm-workspace examples/templates (Vue×Vue, React×React, both cross-framework bridge directions, and a full showcase). Copy a folder, then `pnpm install && pnpm dev`.
441
+ - [Migration guide](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/迁移指南.md).
442
+ - [CHANGELOG](https://github.com/chenmingye/fulgurjs-federation/blob/master/CHANGELOG.md): changes and migration requirements.
443
+ - [Acceptance records](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/整夜全量验收报告-20261004.md): overnight acceptance on two real MES business projects (fresh SVN copies), covering dev, production, fault recovery and HMR, plus production-build notes for large Vite 6 apps (that round required disabling `manualChunks`; **fixed in 5.8.0 — keep your own `manualChunks`, shared bodies are isolated into `fulgurjs-provider-*` chunks automatically**). Historical record: [20261002 demo acceptance](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/完整Demo展示与全面复测-验收报告-20261002.md) — historical results are not a substitute for testing your application.
444
+
445
+ ## Development and testing
446
+
447
+ These commands develop **this plugin repository**; ordinary consumers do not need them:
448
+
449
+ ```bash
450
+ pnpm --dir packages/plugin install
451
+ pnpm --dir packages/plugin build
452
+ pnpm test:unit
453
+ ```
471
454
 
472
- The known dual-client error-overlay case is skipped only on the reproduced Vite 5.1.4 version and reported as skipped, never passed. Other Vite 5 versions still execute it. Fixture tests do not replace final registry-package testing in a real application's development and production environments.
455
+ See [CONTRIBUTING](https://github.com/chenmingye/fulgurjs-federation/blob/master/CONTRIBUTING.md) for fixture installation and browser test prerequisites. CI checks builds, types, unit tests, installed packages and browser scenarios across multiple Vite versions. Counts come from the corresponding run.
473
456
 
474
457
  ## License
475
458
 
476
- [MIT](./LICENSE) © chenmingye (Jason)
459
+ [MIT](LICENSE) © chenmingye (Jason)