@fulgurjs/federation 5.8.0 → 5.9.1

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 (216) hide show
  1. package/CHANGELOG.md +19 -4
  2. package/README.en.md +60 -24
  3. package/README.md +60 -24
  4. package/dist/cli.js +327 -29
  5. package/dist/index.cjs +2 -2
  6. package/dist/index.js +2 -2
  7. package/dist/runtime.js +1 -1
  8. package/docs/API.en.md +14 -3
  9. package/docs/API.md +7 -6
  10. package/docs/webpack-mf-/345/257/271/347/205/247/344/270/216/347/274/272/345/217/243.md +2 -2
  11. package/examples/README.md +3 -67
  12. package/examples/templates/README.md +88 -0
  13. package/examples/templates/react-host-vue-remote/README.md +18 -0
  14. package/examples/templates/react-host-vue-remote/package.json +11 -0
  15. package/examples/templates/react-host-vue-remote/pnpm-lock.yaml +1627 -0
  16. package/examples/templates/react-host-vue-remote/pnpm-workspace.yaml +10 -0
  17. package/examples/{bridge → templates/react-host-vue-remote}/react-host/README.md +8 -7
  18. package/examples/{bridge → templates/react-host-vue-remote}/react-host/package.json +1 -1
  19. package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote.d/_paths.d.ts +4 -0
  20. package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote.d/bridge.ts +3 -0
  21. package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote.d.ts +9 -0
  22. package/examples/templates/react-host-vue-remote/scripts/dev.config.json +6 -0
  23. package/examples/templates/react-host-vue-remote/scripts/dev.mjs +173 -0
  24. package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/README.md +10 -5
  25. package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/package.json +1 -1
  26. package/examples/{react → templates/react-react}/README.md +9 -13
  27. package/examples/{react → templates/react-react}/host/README.md +6 -8
  28. package/examples/{react → templates/react-react}/host/package.json +1 -1
  29. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/ClickButton.ts +3 -0
  30. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/_paths.d.ts +4 -0
  31. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/pages/DetailPage.ts +3 -0
  32. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/pages/HomePage.ts +3 -0
  33. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d/utils.ts +2 -0
  34. package/examples/templates/react-react/host/src/fulgurjs/types/react-remote.d.ts +26 -0
  35. package/examples/templates/react-react/package.json +11 -0
  36. package/examples/templates/react-react/pnpm-lock.yaml +1537 -0
  37. package/examples/templates/react-react/pnpm-workspace.yaml +10 -0
  38. package/examples/{react → templates/react-react}/remote/README.md +7 -6
  39. package/examples/{react → templates/react-react}/remote/package.json +1 -1
  40. package/examples/templates/react-react/scripts/dev.config.json +6 -0
  41. package/examples/templates/react-react/scripts/dev.mjs +173 -0
  42. package/examples/templates/showcase/README.md +109 -0
  43. package/examples/templates/showcase/package.json +13 -0
  44. package/examples/templates/showcase/pnpm-lock.yaml +1858 -0
  45. package/examples/templates/showcase/pnpm-workspace.yaml +12 -0
  46. package/examples/templates/showcase/react-host/fulgurjs.config.ts +21 -0
  47. package/examples/templates/showcase/react-host/index.html +13 -0
  48. package/examples/templates/showcase/react-host/package.json +26 -0
  49. package/examples/templates/showcase/react-host/src/GuardBanner.tsx +33 -0
  50. package/examples/templates/showcase/react-host/src/Layout.tsx +42 -0
  51. package/examples/templates/showcase/react-host/src/ObsPanel.tsx +44 -0
  52. package/examples/templates/showcase/react-host/src/demo-log.ts +74 -0
  53. package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote.d/_paths.d.ts +4 -0
  54. package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote.d/bridge.ts +3 -0
  55. package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote.d.ts +7 -0
  56. package/examples/templates/showcase/react-host/src/main.tsx +33 -0
  57. package/examples/templates/showcase/react-host/src/pages/AboutPage.tsx +18 -0
  58. package/examples/templates/showcase/react-host/src/pages/BridgeVuePage.tsx +77 -0
  59. package/examples/templates/showcase/react-host/src/pages/HomePage.tsx +14 -0
  60. package/examples/templates/showcase/react-host/src/routing.ts +30 -0
  61. package/examples/templates/showcase/react-host/tsconfig.json +15 -0
  62. package/examples/templates/showcase/react-host/vite.config.ts +8 -0
  63. package/examples/templates/showcase/react-remote/fulgurjs.config.ts +18 -0
  64. package/examples/templates/showcase/react-remote/index.html +13 -0
  65. package/examples/templates/showcase/react-remote/package.json +25 -0
  66. package/examples/templates/showcase/react-remote/src/ChildLayout.tsx +48 -0
  67. package/examples/templates/showcase/react-remote/src/bridge.tsx +62 -0
  68. package/examples/templates/showcase/react-remote/src/child-bus.ts +32 -0
  69. package/examples/templates/showcase/react-remote/src/main.tsx +18 -0
  70. package/examples/templates/showcase/react-remote/src/pages/Locked.tsx +23 -0
  71. package/examples/templates/showcase/react-remote/src/pages/OrderDetail.tsx +36 -0
  72. package/examples/templates/showcase/react-remote/src/pages/OrderList.tsx +110 -0
  73. package/examples/templates/showcase/react-remote/src/pages/Settings.tsx +50 -0
  74. package/examples/templates/showcase/react-remote/src/settings-store.ts +11 -0
  75. package/examples/templates/showcase/react-remote/tsconfig.json +14 -0
  76. package/examples/templates/showcase/react-remote/vite.config.ts +14 -0
  77. package/examples/templates/showcase/scripts/dev.config.json +8 -0
  78. package/examples/templates/showcase/scripts/dev.mjs +173 -0
  79. package/examples/templates/showcase/vue-host/fulgurjs.config.ts +21 -0
  80. package/examples/templates/showcase/vue-host/index.html +13 -0
  81. package/examples/templates/showcase/vue-host/package.json +25 -0
  82. package/examples/templates/showcase/vue-host/src/App.vue +50 -0
  83. package/examples/templates/showcase/vue-host/src/GuardBanner.vue +24 -0
  84. package/examples/templates/showcase/vue-host/src/ObsPanel.vue +60 -0
  85. package/examples/templates/showcase/vue-host/src/demo-log.ts +49 -0
  86. package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote.d/_paths.d.ts +4 -0
  87. package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote.d/bridge.ts +3 -0
  88. package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote.d.ts +7 -0
  89. package/examples/templates/showcase/vue-host/src/guard.ts +47 -0
  90. package/examples/templates/showcase/vue-host/src/main.ts +30 -0
  91. package/examples/templates/showcase/vue-host/src/pages/AboutPage.vue +17 -0
  92. package/examples/templates/showcase/vue-host/src/pages/BridgeReactPage.vue +74 -0
  93. package/examples/templates/showcase/vue-host/src/pages/HomePage.vue +13 -0
  94. package/examples/templates/showcase/vue-host/src/routing.ts +31 -0
  95. package/examples/templates/showcase/vue-host/tsconfig.json +14 -0
  96. package/examples/templates/showcase/vue-remote/fulgurjs.config.ts +17 -0
  97. package/examples/templates/showcase/vue-remote/index.html +13 -0
  98. package/examples/templates/showcase/vue-remote/package.json +23 -0
  99. package/examples/templates/showcase/vue-remote/src/ChildLayout.vue +54 -0
  100. package/examples/templates/showcase/vue-remote/src/bridge.ts +48 -0
  101. package/examples/templates/showcase/vue-remote/src/child-bus.ts +32 -0
  102. package/examples/templates/showcase/vue-remote/src/main.ts +16 -0
  103. package/examples/templates/showcase/vue-remote/src/pages/Locked.vue +28 -0
  104. package/examples/templates/showcase/vue-remote/src/pages/OrderDetail.vue +46 -0
  105. package/examples/templates/showcase/vue-remote/src/pages/OrderList.vue +116 -0
  106. package/examples/templates/showcase/vue-remote/src/pages/Settings.vue +34 -0
  107. package/examples/templates/showcase/vue-remote/src/settings-store.ts +13 -0
  108. package/examples/templates/showcase/vue-remote/tsconfig.json +14 -0
  109. package/examples/templates/showcase/vue-remote/vite.config.ts +14 -0
  110. package/examples/templates/vue-host-react-remote/README.md +18 -0
  111. package/examples/templates/vue-host-react-remote/package.json +11 -0
  112. package/examples/templates/vue-host-react-remote/pnpm-lock.yaml +1663 -0
  113. package/examples/templates/vue-host-react-remote/pnpm-workspace.yaml +10 -0
  114. package/examples/{bridge → templates/vue-host-react-remote}/react-remote/README.md +9 -4
  115. package/examples/{bridge → templates/vue-host-react-remote}/react-remote/package.json +1 -1
  116. package/examples/templates/vue-host-react-remote/scripts/dev.config.json +6 -0
  117. package/examples/templates/vue-host-react-remote/scripts/dev.mjs +173 -0
  118. package/examples/{bridge → templates/vue-host-react-remote}/vue-host/README.md +8 -7
  119. package/examples/{bridge → templates/vue-host-react-remote}/vue-host/package.json +1 -1
  120. package/examples/{bridge → templates/vue-host-react-remote}/vue-host/src/App.vue +1 -1
  121. package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote.d/_paths.d.ts +4 -0
  122. package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote.d/bridge.ts +3 -0
  123. package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote.d.ts +9 -0
  124. package/examples/{vue → templates/vue-vue}/README.md +9 -13
  125. package/examples/{vue → templates/vue-vue}/host/README.md +6 -8
  126. package/examples/{vue → templates/vue-vue}/host/package.json +1 -1
  127. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/ClickButton.ts +4 -0
  128. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/_paths.d.ts +4 -0
  129. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/pages/DetailPage.ts +4 -0
  130. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/pages/HomePage.ts +4 -0
  131. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d/utils.ts +2 -0
  132. package/examples/templates/vue-vue/host/src/fulgurjs/types/vue-remote.d.ts +29 -0
  133. package/examples/templates/vue-vue/host/vite.config.ts +8 -0
  134. package/examples/templates/vue-vue/package.json +11 -0
  135. package/examples/templates/vue-vue/pnpm-lock.yaml +1199 -0
  136. package/examples/templates/vue-vue/pnpm-workspace.yaml +10 -0
  137. package/examples/{vue → templates/vue-vue}/remote/README.md +7 -6
  138. package/examples/{vue → templates/vue-vue}/remote/package.json +1 -1
  139. package/examples/{vue → templates/vue-vue}/remote/src/App.vue +1 -1
  140. package/examples/templates/vue-vue/scripts/dev.config.json +6 -0
  141. package/examples/templates/vue-vue/scripts/dev.mjs +173 -0
  142. package/package.json +4 -4
  143. package/examples/README.en.md +0 -67
  144. package/examples/bridge/README.md +0 -36
  145. package/examples/bridge/react-host/package-lock.json +0 -2466
  146. package/examples/bridge/react-remote/package-lock.json +0 -2391
  147. package/examples/bridge/vue-host/package-lock.json +0 -1894
  148. package/examples/bridge/vue-remote/package-lock.json +0 -1865
  149. package/examples/react/host/package-lock.json +0 -2391
  150. package/examples/react/remote/package-lock.json +0 -2333
  151. package/examples/vue/host/package-lock.json +0 -1865
  152. package/examples/vue/remote/package-lock.json +0 -1843
  153. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/fulgurjs.config.ts +0 -0
  154. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/index.html +0 -0
  155. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/src/env.d.ts +0 -0
  156. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/src/host-session.ts +0 -0
  157. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/src/main.tsx +0 -0
  158. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/tsconfig.json +0 -0
  159. /package/examples/{bridge → templates/react-host-vue-remote}/react-host/vite.config.ts +0 -0
  160. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/fulgurjs.config.ts +0 -0
  161. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/index.html +0 -0
  162. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/src/App.vue +0 -0
  163. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/src/bridge.ts +0 -0
  164. /package/examples/{bridge/react-remote → templates/react-host-vue-remote/vue-remote}/src/env.d.ts +0 -0
  165. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/src/main.ts +0 -0
  166. /package/examples/{bridge/vue-host → templates/react-host-vue-remote/vue-remote}/tsconfig.json +0 -0
  167. /package/examples/{bridge → templates/react-host-vue-remote}/vue-remote/vite.config.ts +0 -0
  168. /package/examples/{react → templates/react-react}/host/fulgurjs.config.ts +0 -0
  169. /package/examples/{react → templates/react-react}/host/index.html +0 -0
  170. /package/examples/{react → templates/react-react}/host/src/App.tsx +0 -0
  171. /package/examples/{react → templates/react-react}/host/src/main.tsx +0 -0
  172. /package/examples/{react → templates/react-react}/host/src/pages/UtilsDemo.tsx +0 -0
  173. /package/examples/{react → templates/react-react}/host/src/remotePages.tsx +0 -0
  174. /package/examples/{bridge/react-remote → templates/react-react/host}/tsconfig.json +0 -0
  175. /package/examples/{react → templates/react-react}/host/vite.config.ts +0 -0
  176. /package/examples/{react → templates/react-react}/remote/fulgurjs.config.ts +0 -0
  177. /package/examples/{react → templates/react-react}/remote/index.html +0 -0
  178. /package/examples/{react → templates/react-react}/remote/src/App.tsx +0 -0
  179. /package/examples/{react → templates/react-react}/remote/src/exposes/ClickButton.tsx +0 -0
  180. /package/examples/{react → templates/react-react}/remote/src/exposes/pages/DetailPage.tsx +0 -0
  181. /package/examples/{react → templates/react-react}/remote/src/exposes/pages/HomePage.tsx +0 -0
  182. /package/examples/{react → templates/react-react}/remote/src/exposes/utils.ts +0 -0
  183. /package/examples/{react → templates/react-react}/remote/src/main.tsx +0 -0
  184. /package/examples/{react/host → templates/react-react/remote}/tsconfig.json +0 -0
  185. /package/examples/{react → templates/react-react}/remote/vite.config.ts +0 -0
  186. /package/examples/{vue/host → templates/showcase/vue-host}/vite.config.ts +0 -0
  187. /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/fulgurjs.config.ts +0 -0
  188. /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/index.html +0 -0
  189. /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/src/bridge.tsx +0 -0
  190. /package/examples/{bridge/vue-host → templates/vue-host-react-remote/react-remote}/src/env.d.ts +0 -0
  191. /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/src/main.tsx +0 -0
  192. /package/examples/{react/remote → templates/vue-host-react-remote/react-remote}/tsconfig.json +0 -0
  193. /package/examples/{bridge → templates/vue-host-react-remote}/react-remote/vite.config.ts +0 -0
  194. /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/fulgurjs.config.ts +0 -0
  195. /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/index.html +0 -0
  196. /package/examples/{bridge/vue-remote → templates/vue-host-react-remote/vue-host}/src/env.d.ts +0 -0
  197. /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/src/host-session.ts +0 -0
  198. /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/src/main.ts +0 -0
  199. /package/examples/{bridge/vue-remote → templates/vue-host-react-remote/vue-host}/tsconfig.json +0 -0
  200. /package/examples/{bridge → templates/vue-host-react-remote}/vue-host/vite.config.ts +0 -0
  201. /package/examples/{vue → templates/vue-vue}/host/fulgurjs.config.ts +0 -0
  202. /package/examples/{vue → templates/vue-vue}/host/index.html +0 -0
  203. /package/examples/{vue → templates/vue-vue}/host/src/App.vue +0 -0
  204. /package/examples/{vue → templates/vue-vue}/host/src/main.ts +0 -0
  205. /package/examples/{vue → templates/vue-vue}/host/src/pages/HomePage.vue +0 -0
  206. /package/examples/{vue → templates/vue-vue}/host/src/pages/UtilsDemo.vue +0 -0
  207. /package/examples/{vue → templates/vue-vue}/host/tsconfig.json +0 -0
  208. /package/examples/{vue → templates/vue-vue}/remote/fulgurjs.config.ts +0 -0
  209. /package/examples/{vue → templates/vue-vue}/remote/index.html +0 -0
  210. /package/examples/{vue → templates/vue-vue}/remote/src/exposes/ClickButton.vue +0 -0
  211. /package/examples/{vue → templates/vue-vue}/remote/src/exposes/pages/DetailPage.vue +0 -0
  212. /package/examples/{vue → templates/vue-vue}/remote/src/exposes/pages/HomePage.vue +0 -0
  213. /package/examples/{vue → templates/vue-vue}/remote/src/exposes/utils.ts +0 -0
  214. /package/examples/{vue → templates/vue-vue}/remote/src/main.ts +0 -0
  215. /package/examples/{vue → templates/vue-vue}/remote/tsconfig.json +0 -0
  216. /package/examples/{vue → templates/vue-vue}/remote/vite.config.ts +0 -0
@@ -0,0 +1,12 @@
1
+ packages:
2
+ - vue-host
3
+ - vue-remote
4
+ - react-host
5
+ - react-remote
6
+ minimumReleaseAgeExclude:
7
+ - '@fulgurjs/federation@5.7.1 || 5.8.0 || 5.9.0'
8
+ # pnpm >=12 构建脚本批准(12 起弃用 onlyBuiltDependencies;pnpm 9-11 忽略本键:
9
+ # esbuild >=0.16 二进制经 optionalDependencies 平台包分发,postinstall 仅校验,不阻塞安装)
10
+ allowBuilds:
11
+ esbuild: true
12
+ vue-demi: true
@@ -0,0 +1,21 @@
1
+ import type { FederationOptions } from '@fulgurjs/federation'
2
+
3
+ /**
4
+ * React 宿主联邦配置(examples/templates/showcase/react-host,端口 5336)。
5
+ * 桥接宿主双框架安装合同:react + react-dom + vue 三键全部 singleton——
6
+ * Vue 子应用的 shared vue 实例经此协商单例,缺失会表现为双实例(MFU-010)。
7
+ */
8
+ export default {
9
+ name: 'react-host',
10
+ remotes: {
11
+ 'vue-remote': {
12
+ dev: 'http://localhost:5335',
13
+ prod: '/vue-remote',
14
+ },
15
+ },
16
+ shared: {
17
+ react: { singleton: true },
18
+ 'react-dom': { singleton: true },
19
+ vue: { singleton: true },
20
+ },
21
+ } satisfies FederationOptions
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="icon" href="data:," />
7
+ <title>react-host(桥接 URL 同步宿主 × Vue 子应用,5336)</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "fulgurjs-demo-bridge-react-host",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite --port 5336 --strictPort",
8
+ "build": "vite build",
9
+ "preview": "vite preview --port 5336 --strictPort",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@fulgurjs/federation": "5.9.0",
14
+ "react": "^19.1.0",
15
+ "react-dom": "^19.1.0",
16
+ "react-router-dom": "^7.1.0",
17
+ "vue": "^3.5.13"
18
+ },
19
+ "devDependencies": {
20
+ "@types/react": "^19.1.0",
21
+ "@types/react-dom": "^19.1.0",
22
+ "@vitejs/plugin-react": "^5.0.0",
23
+ "typescript": "^5.8.0",
24
+ "vite": "^6.3.5"
25
+ }
26
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * examples/templates/showcase/react-host/src/GuardBanner.tsx — 宿主守卫横幅(useBlocker 原生守卫)。
3
+ * /br-vue/locked 需人工确认:blocked 时渲染「继续/取消」;proceed() 放行提交,
4
+ * reset() 取消 → 桥接端口观察到真实 blocker 状态,返回 cancelled,URL/子应用位置保持不变。
5
+ */
6
+ import type { ReactElement } from 'react'
7
+ import { useBlocker } from 'react-router-dom'
8
+ import { logNav } from './demo-log'
9
+ import { BRIDGE_BASE_PATH } from './routing'
10
+
11
+ export default function GuardBanner(): ReactElement | null {
12
+ const blocker = useBlocker(({ nextLocation }) => nextLocation.pathname.startsWith(`${BRIDGE_BASE_PATH}/locked`))
13
+ if (blocker.state !== 'blocked') return null
14
+ const target = blocker.location?.pathname ?? ''
15
+
16
+ const handleProceed = (): void => {
17
+ logNav('宿主守卫', `继续 → ${target}`)
18
+ blocker.proceed?.()
19
+ }
20
+
21
+ const handleReset = (): void => {
22
+ logNav('宿主守卫', `取消 → ${target}(URL/历史/子应用位置保持不变)`)
23
+ blocker.reset?.()
24
+ }
25
+
26
+ return (
27
+ <div role="alertdialog" aria-label="宿主守卫确认" style={{ display: 'flex', gap: 12, alignItems: 'center', background: '#fff7e6', border: '1px solid #f0c36d', padding: '8px 12px', marginBottom: 12, borderRadius: 4 }}>
28
+ <span>宿主守卫(useBlocker):是否允许前往 <code>{target}</code>?</span>
29
+ <button onClick={handleProceed}>继续</button>
30
+ <button onClick={handleReset}>取消</button>
31
+ </div>
32
+ )
33
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * examples/templates/showcase/react-host/src/Layout.tsx — 宿主根布局:顶部导航 + 守卫横幅 + Outlet。
3
+ * 「工单列表 / 设置」是直接跳子应用深链的菜单项:宿主 navigate 到 basePath 下的路径。
4
+ */
5
+ import { Outlet, useNavigate } from 'react-router-dom'
6
+ import type { ReactElement } from 'react'
7
+ import GuardBanner from './GuardBanner'
8
+ import { logNav } from './demo-log'
9
+
10
+ interface MenuItem {
11
+ label: string
12
+ path: string
13
+ }
14
+
15
+ const MENUS: MenuItem[] = [
16
+ { label: '首页', path: '/' },
17
+ { label: '关于', path: '/about' },
18
+ { label: '桥接演示页', path: '/br-vue' },
19
+ { label: '工单列表', path: '/br-vue/orders' },
20
+ { label: '设置', path: '/br-vue/settings' },
21
+ ]
22
+
23
+ export default function Layout(): ReactElement {
24
+ const navigate = useNavigate()
25
+
26
+ const handleMenu = (item: MenuItem): void => {
27
+ logNav('宿主菜单', `push ${item.path}`)
28
+ void navigate(item.path)
29
+ }
30
+
31
+ return (
32
+ <div style={{ padding: 12, maxWidth: 960, margin: '0 auto', fontFamily: "'PingFang SC', 'Microsoft YaHei', sans-serif" }}>
33
+ <GuardBanner />
34
+ <nav aria-label="宿主导航" style={{ display: 'flex', gap: 8, padding: '8px 0', borderBottom: '1px solid #ddd', marginBottom: 12, flexWrap: 'wrap' }}>
35
+ {MENUS.map((item) => (
36
+ <button key={item.path} onClick={() => handleMenu(item)}>{item.label}</button>
37
+ ))}
38
+ </nav>
39
+ <Outlet />
40
+ </div>
41
+ )
42
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * examples/templates/showcase/react-host/src/ObsPanel.tsx — URL 同步观测台(实时更新)。
3
+ * 展示:宿主当前 URL(location.href)、子应用逻辑位置、history.length、
4
+ * 子应用挂载计数、最近 10 条导航事件(带来源标注)。
5
+ */
6
+ import { useEffect, useState, useSyncExternalStore } from 'react'
7
+ import type { ReactElement } from 'react'
8
+ import { useLocation } from 'react-router-dom'
9
+ import { demoLog } from './demo-log'
10
+ import { BRIDGE_BASE_PATH } from './routing'
11
+
12
+ export default function ObsPanel(): ReactElement {
13
+ const snapshot = useSyncExternalStore(demoLog.subscribe, demoLog.getSnapshot)
14
+ const location = useLocation()
15
+ const [historyLength, setHistoryLength] = useState(window.history.length)
16
+
17
+ useEffect(() => {
18
+ setHistoryLength(window.history.length)
19
+ }, [location.key, snapshot])
20
+
21
+ const inPrefix = location.pathname === BRIDGE_BASE_PATH || location.pathname.startsWith(`${BRIDGE_BASE_PATH}/`)
22
+ const childLocation = inPrefix
23
+ ? (location.pathname.slice(BRIDGE_BASE_PATH.length) || '/') + location.search + location.hash
24
+ : location.pathname + location.search
25
+
26
+ return (
27
+ <section style={{ border: '1px solid #d0e7ff', background: '#f5faff', padding: '8px 12px', margin: '12px 0', borderRadius: 4 }}>
28
+ <h3 style={{ margin: '6px 0' }}>URL 同步观测台</h3>
29
+ <p style={{ margin: '4px 0' }}>宿主当前 URL:<code data-demo-host-url>{window.location.href}</code></p>
30
+ <p style={{ margin: '4px 0' }}>子应用逻辑位置:<code data-demo-child-location>{childLocation}</code></p>
31
+ <p style={{ margin: '4px 0' }}>
32
+ history.length:<code data-demo-history-length>{historyLength}</code>
33
+ |子应用 mount 次数:<code data-demo-mount-count>{snapshot.mountCount}</code>(路由切换应恒为 1)
34
+ </p>
35
+ <h4 style={{ margin: '6px 0 2px' }}>最近导航事件(≤10 条,新在下)</h4>
36
+ <ol style={{ margin: '4px 0', paddingLeft: 20, fontSize: 13 }}>
37
+ {snapshot.entries.map((entry) => (
38
+ <li key={entry.id}>[{entry.time}] {entry.source} — {entry.detail}</li>
39
+ ))}
40
+ {snapshot.entries.length === 0 && <li style={{ color: '#666' }}>(暂无事件)</li>}
41
+ </ol>
42
+ </section>
43
+ )
44
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * examples/templates/showcase/react-host/src/demo-log.ts — URL 同步观测台数据源(模块级单例,跨宿主页面存活)。
3
+ * 事件来源标注:宿主菜单 / 深链粘贴 / 子应用 Link / 子应用 push·replace / 子应用 go /
4
+ * 子应用初始化 / 浏览器前进后退 / 宿主守卫。最多保留 10 条。
5
+ * 外部经 useSyncExternalStore(demoLog.subscribe, demoLog.getSnapshot) 订阅。
6
+ */
7
+ export interface NavLogEntry {
8
+ id: number
9
+ time: string
10
+ source: string
11
+ detail: string
12
+ }
13
+
14
+ export type DemoChildEvent =
15
+ | { type: 'mounted'; mountCount: number }
16
+ | { type: 'nav'; source: string; action: string; detail: string }
17
+
18
+ export interface DemoLogSnapshot {
19
+ entries: NavLogEntry[]
20
+ mountCount: number
21
+ }
22
+
23
+ let state: DemoLogSnapshot = { entries: [], mountCount: 0 }
24
+ let nextId = 1
25
+ const listeners = new Set<() => void>()
26
+
27
+ function emitChange(): void {
28
+ for (const listener of [...listeners]) listener()
29
+ }
30
+
31
+ function log(source: string, detail: string): void {
32
+ const entry: NavLogEntry = { id: nextId, time: new Date().toLocaleTimeString('zh-CN', { hour12: false }), source, detail }
33
+ nextId += 1
34
+ state = { ...state, entries: [...state.entries, entry].slice(-10) }
35
+ emitChange()
36
+ }
37
+
38
+ export const demoLog = {
39
+ subscribe(listener: () => void): () => void {
40
+ listeners.add(listener)
41
+ return () => { listeners.delete(listener) }
42
+ },
43
+ getSnapshot(): DemoLogSnapshot {
44
+ return state
45
+ },
46
+ log,
47
+ }
48
+
49
+ /** 宿主侧显式标注的导航事件(宿主菜单 / 深链粘贴 / 宿主守卫 / 浏览器前进后退) */
50
+ export function logNav(source: string, detail: string): void {
51
+ log(source, detail)
52
+ }
53
+
54
+ /** appProps 稳定回调:子应用经它上报挂载计数与导航事件 */
55
+ export function handleChildEvent(event: DemoChildEvent): void {
56
+ if (event.type === 'mounted') {
57
+ state = { ...state, mountCount: event.mountCount }
58
+ emitChange()
59
+ log('子应用', `mount #${event.mountCount}`)
60
+ return
61
+ }
62
+ log(event.source, `${event.action} ${event.detail}`)
63
+ }
64
+
65
+ let popInstalled = false
66
+
67
+ /** 浏览器前进后退(POP)在 main.tsx 安装一次;push/replace 由各自来源显式标注 */
68
+ export function installPopStateLogger(): void {
69
+ if (popInstalled) return
70
+ popInstalled = true
71
+ window.addEventListener('popstate', () => {
72
+ log('浏览器前进后退', window.location.pathname + window.location.search)
73
+ })
74
+ }
@@ -0,0 +1,4 @@
1
+ // 精确轨启用方法(把下面片段合入宿主 tsconfig 的 compilerOptions;baseUrl 指向本 tsconfig 所在目录):
2
+ // "paths": { "vue-remote/*": ["src/fulgurjs/types/vue-remote.d/*"] }
3
+ // 说明:零配置轨(vue-remote.d.ts 的 ambient 声明)可解析但导出为宽松类型;
4
+ // 配置 paths 后同形态导入解析到本目录的转发模块,获得远程源码级类型精度。
@@ -0,0 +1,3 @@
1
+ // 自动生成(精确轨):经 tsconfig paths 解析到本文件后获得源码级类型
2
+ export * from "../../../../../vue-remote/src/bridge"
3
+ export { default } from "../../../../../vue-remote/src/bridge"
@@ -0,0 +1,7 @@
1
+ // 自动生成:fulgurjs-federation dev 类型直连(remote: vue-remote,mode: source)
2
+ // 重新生成:重启 host dev server
3
+
4
+ declare module "vue-remote/bridge" {
5
+ const __fg_default: any
6
+ export default __fg_default
7
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * examples/templates/showcase/react-host/src/main.tsx — 宿主入口。
3
+ * data router(createBrowserRouter + RouterProvider)是 URL 同步的前提(declarative 模式无取消语义);
4
+ * catch-all 路由(/br-vue/* 单条记录渲染同一组件)保证子应用内部导航不重挂宿主组件。
5
+ * 注意:刻意不包 StrictMode——桥接契约工厂在每次 mount 时执行,StrictMode 的双 effect
6
+ * 会让子应用挂载计数翻倍,干扰「mount 次数恒为 1」演示(生产语义不受影响)。
7
+ */
8
+ import { createRoot } from 'react-dom/client'
9
+ import { createBrowserRouter, RouterProvider } from 'react-router-dom'
10
+ import Layout from './Layout'
11
+ import HomePage from './pages/HomePage'
12
+ import AboutPage from './pages/AboutPage'
13
+ import BridgeVuePage from './pages/BridgeVuePage'
14
+ import { initBridgeRouting } from './routing'
15
+ import { installPopStateLogger } from './demo-log'
16
+
17
+ const router = createBrowserRouter([
18
+ {
19
+ path: '/',
20
+ element: <Layout />,
21
+ children: [
22
+ { index: true, element: <HomePage /> },
23
+ { path: 'about', element: <AboutPage /> },
24
+ // catch-all:子应用挂载在 /br-vue 前缀下,内部导航全部命中同一条路由记录
25
+ { path: 'br-vue/*', element: <BridgeVuePage /> },
26
+ ],
27
+ },
28
+ ])
29
+
30
+ initBridgeRouting(router)
31
+ installPopStateLogger()
32
+
33
+ createRoot(document.getElementById('root')!).render(<RouterProvider router={router} />)
@@ -0,0 +1,18 @@
1
+ /**
2
+ * examples/templates/showcase/react-host/src/pages/AboutPage.tsx — 关于页:场景与工程说明。
3
+ */
4
+ import type { ReactElement } from 'react'
5
+
6
+ export default function AboutPage(): ReactElement {
7
+ return (
8
+ <section>
9
+ <h2>关于</h2>
10
+ <p>examples/templates/showcase:一个 pnpm 模板中的 4 个应用 —— react-remote(5333)、vue-host(5334)、vue-remote(5335)、react-host(5336)。</p>
11
+ <ul>
12
+ <li>vue-host(:5334)套 react-remote(:5333),桥接前缀 basePath=/br-react</li>
13
+ <li>react-host(:5336)套 vue-remote(:5335),桥接前缀 basePath=/br-vue</li>
14
+ </ul>
15
+ <p>API 与源码对照、演示操作清单见 examples/templates/showcase/README.md。</p>
16
+ </section>
17
+ )
18
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * examples/templates/showcase/react-host/src/pages/BridgeVuePage.tsx — 桥接演示页(React 宿主 × Vue 子应用)。
3
+ * createReactBridgeApp + routing prop(basePath=/br-vue + createReactBridgeNavigation 端口);
4
+ * catch-all 路由(/br-vue/* 单条记录)保证子应用内部导航不重挂本组件;
5
+ * appProps 携带稳定回调供子应用上报事件。
6
+ */
7
+ import { useState } from 'react'
8
+ import type { ChangeEvent, ReactElement } from 'react'
9
+ import { useLocation, useNavigate } from 'react-router-dom'
10
+ import { createReactBridgeApp } from '@fulgurjs/federation/bridge/react'
11
+ import { BRIDGE_BASE_PATH, getBridgeRouting } from '../routing'
12
+ import { handleChildEvent, logNav } from '../demo-log'
13
+ import ObsPanel from '../ObsPanel'
14
+
15
+ const RemoteVueBridge = createReactBridgeApp('vue-remote/bridge', { retries: 1 })
16
+ const appProps = { onChildEvent: handleChildEvent }
17
+
18
+ export default function BridgeVuePage(): ReactElement {
19
+ // 端口在 main.tsx 的 initBridgeRouting(router) 之后才可用——必须在组件渲染期取,
20
+ // 不能放到模块顶层(main.tsx 静态 import 本模块早于 initBridgeRouting 执行)。
21
+ // getBridgeRouting 返回模块级单例,引用跨重渲染稳定 → routing 键不变,子应用不重挂。
22
+ const routing = getBridgeRouting()
23
+ const navigate = useNavigate()
24
+ const location = useLocation()
25
+ const [deepLink, setDeepLink] = useState(`${window.location.origin}${BRIDGE_BASE_PATH}/orders?q=网络&page=2`)
26
+ const [deepLinkError, setDeepLinkError] = useState('')
27
+
28
+ const handleDeepLinkChange = (event: ChangeEvent<HTMLInputElement>): void => {
29
+ setDeepLink(event.target.value)
30
+ }
31
+
32
+ const handleDeepLink = (): void => {
33
+ const raw = deepLink.trim()
34
+ let path = raw
35
+ if (/^https?:\/\//i.test(raw)) {
36
+ try {
37
+ const parsed = new URL(raw)
38
+ path = parsed.pathname + parsed.search
39
+ } catch {
40
+ setDeepLinkError('URL 无法解析')
41
+ return
42
+ }
43
+ }
44
+ if (path !== BRIDGE_BASE_PATH && !path.startsWith(`${BRIDGE_BASE_PATH}/`)) {
45
+ setDeepLinkError(`路径必须位于 ${BRIDGE_BASE_PATH} 前缀下`)
46
+ return
47
+ }
48
+ setDeepLinkError('')
49
+ logNav('深链粘贴', `push ${path}`)
50
+ void navigate(path)
51
+ }
52
+
53
+ return (
54
+ <section>
55
+ <h2>桥接演示:React 宿主 × Vue 子应用(URL 同步)</h2>
56
+ <p style={{ color: '#666', fontSize: 13, margin: '4px 0' }}>
57
+ 子应用挂载在宿主 <code>{BRIDGE_BASE_PATH}</code> 前缀下;当前宿主路径:
58
+ <code>{location.pathname + location.search}</code>
59
+ </p>
60
+ <ObsPanel />
61
+ <section style={{ border: '1px dashed #bbb', padding: '8px 12px', margin: '12px 0', borderRadius: 4 }}>
62
+ <h3 style={{ margin: '6px 0' }}>深链粘贴框(push 侧「刷新直达」复现)</h3>
63
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
64
+ <input value={deepLink} onChange={handleDeepLinkChange} style={{ flex: '1 1 320px' }} aria-label="深链粘贴框" />
65
+ <button onClick={handleDeepLink}>push 复现直达</button>
66
+ </div>
67
+ {deepLinkError !== '' && <p style={{ color: '#c0392b' }}>{deepLinkError}</p>}
68
+ <p style={{ color: '#666', fontSize: 13, margin: '4px 0' }}>
69
+ 粘贴带中文 query 的完整 URL 后点击按钮 = 复制地址栏在新窗口打开前的 push 侧动作。
70
+ </p>
71
+ </section>
72
+ <section data-demo-bridge-area style={{ border: '2px solid #42b883', padding: '8px 12px', margin: '12px 0', borderRadius: 4 }}>
73
+ <RemoteVueBridge routing={routing} appProps={appProps} />
74
+ </section>
75
+ </section>
76
+ )
77
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * examples/templates/showcase/react-host/src/pages/HomePage.tsx — 宿主首页。
3
+ */
4
+ import type { ReactElement } from 'react'
5
+
6
+ export default function HomePage(): ReactElement {
7
+ return (
8
+ <section>
9
+ <h2>首页</h2>
10
+ <p>双向桥接 URL 同步演示:宿主页级路由(首页 / 关于 / 桥接演示页)+ 子应用受控 memory 路由。</p>
11
+ <p>顶部菜单「桥接演示页 / 工单列表 / 设置」经宿主 navigate 直达子应用深链。</p>
12
+ </section>
13
+ )
14
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * examples/templates/showcase/react-host/src/routing.ts — 桥接宿主导航端口(模块级单例)。
3
+ * createReactBridgeNavigation 仅支持 data router 模式(createBrowserRouter/createHashRouter);
4
+ * dev 下 Vite base 为 '/' 无需 basename;子目录部署时与 createBrowserRouter 同源传 basename。
5
+ * 端口引用保持稳定 → routing 键不变,宿主不重挂、不重复订阅。
6
+ */
7
+ import { createReactBridgeNavigation } from '@fulgurjs/federation/bridge/router/react'
8
+
9
+ export const BRIDGE_BASE_PATH = '/br-vue'
10
+
11
+ /**
12
+ * 宿主 routing prop 结构(BridgeHostRouting)。
13
+ * 注:5.4.1 发布包未从 /bridge/router/react 导出 BridgeHostRouting 类型(README §8.3 示例
14
+ * 与发布包类型面不一致,已知问题),这里用端口函数返回值结构化等价替代。
15
+ */
16
+ interface BridgeHostRoutingLike {
17
+ basePath: string
18
+ navigation: ReturnType<typeof createReactBridgeNavigation>
19
+ }
20
+
21
+ let routing: BridgeHostRoutingLike | undefined
22
+
23
+ export function initBridgeRouting(router: Parameters<typeof createReactBridgeNavigation>[0]): void {
24
+ routing = { basePath: BRIDGE_BASE_PATH, navigation: createReactBridgeNavigation(router) }
25
+ }
26
+
27
+ export function getBridgeRouting(): BridgeHostRoutingLike {
28
+ if (!routing) throw new Error('桥接路由端口未初始化:请先在 main.tsx 调用 initBridgeRouting(router)')
29
+ return routing
30
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "jsx": "react-jsx",
8
+ "skipLibCheck": true,
9
+ "noEmit": true,
10
+ "allowImportingTsExtensions": true,
11
+ "types": ["vite/client"]
12
+ },
13
+ "include": ["src", "fulgurjs.config.ts", "vite.config.ts"],
14
+ "exclude": ["src/fulgurjs/types/vue-remote.d"]
15
+ }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import { federation } from '@fulgurjs/federation'
4
+ import fulgurjsConfig from './fulgurjs.config.ts'
5
+
6
+ export default defineConfig({
7
+ plugins: [react(), federation(fulgurjsConfig)],
8
+ })
@@ -0,0 +1,18 @@
1
+ import type { FederationOptions } from '@fulgurjs/federation'
2
+
3
+ /**
4
+ * React 子应用联邦配置(examples/templates/showcase/react-remote,端口 5333)。
5
+ * 只暴露 ./bridge 桥接契约;宿主(vue-host)启用 URL 同步后经它加载。
6
+ * react-router-dom 是子应用自己的受控路由库(不进 shared),由消费方自装。
7
+ */
8
+ export default {
9
+ name: 'react-remote',
10
+ filename: 'fulgurjs-remoteEntry.js',
11
+ exposes: {
12
+ './bridge': './src/bridge.tsx',
13
+ },
14
+ shared: {
15
+ react: { singleton: true },
16
+ 'react-dom': { singleton: true },
17
+ },
18
+ } satisfies FederationOptions
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <link rel="icon" href="data:," />
7
+ <title>react-remote(桥接 URL 同步子应用,5333)</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "fulgurjs-demo-bridge-react-remote",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite --port 5333 --strictPort",
8
+ "build": "vite build",
9
+ "preview": "vite preview --port 5333 --strictPort",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@fulgurjs/federation": "5.9.0",
14
+ "react": "^19.1.0",
15
+ "react-dom": "^19.1.0",
16
+ "react-router-dom": "^7.1.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/react": "^19.1.0",
20
+ "@types/react-dom": "^19.1.0",
21
+ "@vitejs/plugin-react": "^5.0.0",
22
+ "typescript": "^5.8.0",
23
+ "vite": "^6.3.5"
24
+ }
25
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * examples/templates/showcase/react-remote/src/ChildLayout.tsx — 子应用根布局:导航演示按钮 + Outlet。
3
+ * push/replace 走被 createReactBridgeRouter 接管的 router.navigate(经通道同步宿主 URL);
4
+ * 数字 navigate 委托宿主浏览器历史(go),不创建第二条独立历史。
5
+ */
6
+ import { Outlet, useNavigate } from 'react-router-dom'
7
+ import type { ReactElement } from 'react'
8
+ import { getMountCount, reportNav } from './child-bus'
9
+
10
+ export default function ChildLayout(): ReactElement {
11
+ const navigate = useNavigate()
12
+
13
+ const handlePushDemo = (): void => {
14
+ const target = `/orders?page=2&q=${encodeURIComponent('演示')}`
15
+ reportNav('子应用 push/replace', 'push', target)
16
+ void navigate(target)
17
+ }
18
+
19
+ const handleReplaceDemo = (): void => {
20
+ reportNav('子应用 push/replace', 'replace', '/settings')
21
+ void navigate('/settings', { replace: true })
22
+ }
23
+
24
+ const handleGoBack = (): void => {
25
+ reportNav('子应用 go', 'go', '-1')
26
+ navigate(-1)
27
+ }
28
+
29
+ const handleGoForward = (): void => {
30
+ reportNav('子应用 go', 'go', '1')
31
+ navigate(1)
32
+ }
33
+
34
+ return (
35
+ <div style={{ borderTop: '1px dashed #ccc', marginTop: 8, paddingTop: 8 }}>
36
+ <p style={{ margin: '4px 0' }}>
37
+ <strong>React 子应用</strong>(受控 memory 路由)· 本会话挂载次数:{getMountCount()}
38
+ </p>
39
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', margin: '8px 0' }}>
40
+ <button onClick={handlePushDemo}>子应用 push /orders?q=演示&amp;page=2</button>
41
+ <button onClick={handleReplaceDemo}>子应用 replace /settings</button>
42
+ <button onClick={handleGoBack}>子应用 go(-1)</button>
43
+ <button onClick={handleGoForward}>子应用 go(1)</button>
44
+ </div>
45
+ <Outlet />
46
+ </div>
47
+ )
48
+ }