@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,50 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * examples/templates/showcase/vue-host/src/App.vue — 宿主根布局:顶部导航 + 守卫横幅 + RouterView。
4
+ * 「工单列表 / 设置」是直接跳子应用深链的菜单项:宿主 router.push 到 basePath 下的路径。
5
+ */
6
+ import { RouterView, useRouter } from 'vue-router'
7
+ import GuardBanner from './GuardBanner.vue'
8
+ import { logNav } from './demo-log'
9
+
10
+ interface MenuItem {
11
+ label: string
12
+ path: string
13
+ }
14
+
15
+ const router = useRouter()
16
+
17
+ const menus: MenuItem[] = [
18
+ { label: '首页', path: '/' },
19
+ { label: '关于', path: '/about' },
20
+ { label: '桥接演示页', path: '/br-react' },
21
+ { label: '工单列表', path: '/br-react/orders' },
22
+ { label: '设置', path: '/br-react/settings' },
23
+ ]
24
+
25
+ const handleMenu = (item: MenuItem): void => {
26
+ logNav('宿主菜单', `push ${item.path}`)
27
+ void router.push(item.path)
28
+ }
29
+ </script>
30
+
31
+ <template>
32
+ <div class="demo-shell">
33
+ <GuardBanner />
34
+ <nav class="demo-nav" aria-label="宿主导航">
35
+ <button v-for="item in menus" :key="item.path" @click="handleMenu(item)">{{ item.label }}</button>
36
+ </nav>
37
+ <RouterView />
38
+ </div>
39
+ </template>
40
+
41
+ <style>
42
+ /* 演示全局基础样式(非 scoped):宿主与子应用同页渲染,统一观感 */
43
+ body { font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; margin: 0; }
44
+ .demo-shell { padding: 12px; max-width: 960px; margin: 0 auto; }
45
+ .demo-nav { display: flex; gap: 8px; padding: 8px 0; border-bottom: 1px solid #ddd; margin-bottom: 12px; flex-wrap: wrap; }
46
+ .demo-nav button { cursor: pointer; }
47
+ .demo-row { display: flex; gap: 8px; align-items: center; margin: 8px 0; flex-wrap: wrap; }
48
+ .demo-muted { color: #666; font-size: 13px; }
49
+ code { background: #f4f4f5; padding: 1px 4px; border-radius: 3px; }
50
+ </style>
@@ -0,0 +1,24 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * examples/templates/showcase/vue-host/src/GuardBanner.vue — 宿主守卫确认横幅(异步 beforeEach 等待用户选择)。
4
+ * 「取消」= Vue Router 导航失败 → 通道收到 cancelled,URL/子应用位置保持不变。
5
+ */
6
+ import { guardPending, resolveGuard } from './guard'
7
+ </script>
8
+
9
+ <template>
10
+ <div v-if="guardPending" class="demo-guard" role="alertdialog" aria-label="宿主守卫确认">
11
+ <span>宿主守卫:是否允许前往 <code>{{ guardPending.to }}</code>?</span>
12
+ <button @click="resolveGuard(true)">继续</button>
13
+ <button @click="resolveGuard(false)">取消</button>
14
+ </div>
15
+ </template>
16
+
17
+ <style scoped>
18
+ .demo-guard {
19
+ display: flex; gap: 12px; align-items: center;
20
+ background: #fff7e6; border: 1px solid #f0c36d;
21
+ padding: 8px 12px; margin-bottom: 12px; border-radius: 4px;
22
+ }
23
+ .demo-guard button { cursor: pointer; }
24
+ </style>
@@ -0,0 +1,60 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * examples/templates/showcase/vue-host/src/ObsPanel.vue — URL 同步观测台(实时更新)。
4
+ * 展示:宿主当前 URL(location.href)、子应用逻辑位置、history.length、
5
+ * 子应用挂载计数、最近 10 条导航事件(带来源标注)。
6
+ */
7
+ import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
8
+ import { useRoute } from 'vue-router'
9
+ import { navLog, mountCount } from './demo-log'
10
+ import { BRIDGE_BASE_PATH } from './routing'
11
+
12
+ const route = useRoute()
13
+
14
+ const hostHref = ref(window.location.href)
15
+ const historyLength = ref(window.history.length)
16
+
17
+ const childLocation = computed(() => {
18
+ const inPrefix = route.path === BRIDGE_BASE_PATH || route.path.startsWith(`${BRIDGE_BASE_PATH}/`)
19
+ if (!inPrefix) return route.fullPath
20
+ return route.fullPath.slice(BRIDGE_BASE_PATH.length) || '/'
21
+ })
22
+
23
+ const refresh = (): void => {
24
+ hostHref.value = window.location.href
25
+ historyLength.value = window.history.length
26
+ }
27
+
28
+ watch(() => route.fullPath, refresh)
29
+
30
+ const handlePop = (): void => { refresh() }
31
+ onMounted(() => window.addEventListener('popstate', handlePop))
32
+ onUnmounted(() => window.removeEventListener('popstate', handlePop))
33
+ </script>
34
+
35
+ <template>
36
+ <section class="demo-obs">
37
+ <h3 class="demo-obs-title">URL 同步观测台</h3>
38
+ <p>宿主当前 URL:<code data-demo-host-url>{{ hostHref }}</code></p>
39
+ <p>子应用逻辑位置:<code data-demo-child-location>{{ childLocation }}</code></p>
40
+ <p>
41
+ history.length:<code data-demo-history-length>{{ historyLength }}</code>
42
+ |子应用 mount 次数:<code data-demo-mount-count>{{ mountCount }}</code>(路由切换应恒为 1)
43
+ </p>
44
+ <h4>最近导航事件(≤10 条,新在下)</h4>
45
+ <ol class="demo-obs-log">
46
+ <li v-for="entry in navLog" :key="entry.id">
47
+ [{{ entry.time }}] {{ entry.source }} — {{ entry.detail }}
48
+ </li>
49
+ <li v-if="navLog.length === 0" class="demo-muted">(暂无事件)</li>
50
+ </ol>
51
+ </section>
52
+ </template>
53
+
54
+ <style scoped>
55
+ .demo-obs { border: 1px solid #d0e7ff; background: #f5faff; padding: 8px 12px; margin: 12px 0; border-radius: 4px; }
56
+ .demo-obs-title { margin: 6px 0; }
57
+ .demo-obs p { margin: 4px 0; }
58
+ h4 { margin: 6px 0 2px; }
59
+ .demo-obs-log { margin: 4px 0; padding-left: 20px; font-size: 13px; }
60
+ </style>
@@ -0,0 +1,49 @@
1
+ /**
2
+ * examples/templates/showcase/vue-host/src/demo-log.ts — URL 同步观测台数据源(模块级单例,跨宿主页面存活)。
3
+ * 事件来源标注:宿主菜单 / 深链粘贴 / 子应用 Link / 子应用 push·replace / 子应用 go /
4
+ * 子应用初始化 / 浏览器前进后退 / 宿主守卫。最多保留 10 条。
5
+ */
6
+ import { ref } from 'vue'
7
+
8
+ export interface NavLogEntry {
9
+ id: number
10
+ time: string
11
+ source: string
12
+ detail: string
13
+ }
14
+
15
+ export type DemoChildEvent =
16
+ | { type: 'mounted'; mountCount: number }
17
+ | { type: 'nav'; source: string; action: string; detail: string }
18
+
19
+ export const navLog = ref<NavLogEntry[]>([])
20
+ export const mountCount = ref(0)
21
+
22
+ let nextId = 1
23
+
24
+ export function logNav(source: string, detail: string): void {
25
+ const entry: NavLogEntry = { id: nextId, time: new Date().toLocaleTimeString('zh-CN', { hour12: false }), source, detail }
26
+ nextId += 1
27
+ navLog.value = [...navLog.value, entry].slice(-10)
28
+ }
29
+
30
+ /** appProps 稳定回调:子应用经它上报挂载计数与导航事件 */
31
+ export function handleChildEvent(event: DemoChildEvent): void {
32
+ if (event.type === 'mounted') {
33
+ mountCount.value = event.mountCount
34
+ logNav('子应用', `mount #${event.mountCount}`)
35
+ return
36
+ }
37
+ logNav(event.source, `${event.action} ${event.detail}`)
38
+ }
39
+
40
+ let popInstalled = false
41
+
42
+ /** 浏览器前进后退(POP)在 main.ts 安装一次;push/replace 由各自来源显式标注 */
43
+ export function installPopStateLogger(): void {
44
+ if (popInstalled) return
45
+ popInstalled = true
46
+ window.addEventListener('popstate', () => {
47
+ logNav('浏览器前进后退', window.location.pathname + window.location.search)
48
+ })
49
+ }
@@ -0,0 +1,4 @@
1
+ // 精确轨启用方法(把下面片段合入宿主 tsconfig 的 compilerOptions;baseUrl 指向本 tsconfig 所在目录):
2
+ // "paths": { "react-remote/*": ["src/fulgurjs/types/react-remote.d/*"] }
3
+ // 说明:零配置轨(react-remote.d.ts 的 ambient 声明)可解析但导出为宽松类型;
4
+ // 配置 paths 后同形态导入解析到本目录的转发模块,获得远程源码级类型精度。
@@ -0,0 +1,3 @@
1
+ // 自动生成(精确轨):经 tsconfig paths 解析到本文件后获得源码级类型
2
+ export * from "../../../../../react-remote/src/bridge"
3
+ export { default } from "../../../../../react-remote/src/bridge"
@@ -0,0 +1,7 @@
1
+ // 自动生成:fulgurjs-federation dev 类型直连(remote: react-remote,mode: source)
2
+ // 重新生成:重启 host dev server
3
+
4
+ declare module "react-remote/bridge" {
5
+ const __fg_default: any
6
+ export default __fg_default
7
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * examples/templates/showcase/vue-host/src/guard.ts — 宿主真实权限守卫(Vue Router 4)。
3
+ * /br-react/locked 需人工确认:异步 beforeEach 返回 Promise → Vue Router 等待用户选择;
4
+ * 返回 false = 真实取消(NavigationFailure)→ 桥接通道收到 cancelled,URL/历史/子应用位置
5
+ * 全部保持最后确认状态,插件绝不自动重试。
6
+ */
7
+ import { ref } from 'vue'
8
+ import type { Router } from 'vue-router'
9
+ import { logNav } from './demo-log'
10
+ import { BRIDGE_BASE_PATH } from './routing'
11
+
12
+ export interface GuardPending {
13
+ to: string
14
+ }
15
+
16
+ export const guardPending = ref<GuardPending | null>(null)
17
+ let resolver: ((allow: boolean) => void) | undefined
18
+
19
+ /** 守卫横幅按钮回调:放行(true)或取消(false) */
20
+ export function resolveGuard(allow: boolean): void {
21
+ const current = resolver
22
+ resolver = undefined
23
+ current?.(allow)
24
+ }
25
+
26
+ /** 作废当前待确认导航(新导航开始时旧导航已被 vue-router 取消,确认横幅必须随之撤下) */
27
+ function invalidatePending(): void {
28
+ const current = resolver
29
+ if (!current) return
30
+ resolver = undefined
31
+ guardPending.value = null
32
+ current(false)
33
+ }
34
+
35
+ export function installGuard(router: Router): void {
36
+ router.beforeEach(async (to) => {
37
+ // 任何新导航开始都作废旧待确认(含非 locked 目标,如守卫等待期间的 go(1))
38
+ invalidatePending()
39
+ if (!to.path.startsWith(`${BRIDGE_BASE_PATH}/locked`)) return true
40
+ guardPending.value = { to: to.fullPath }
41
+ logNav('宿主守卫', `拦截 → ${to.fullPath}(等待确认)`)
42
+ const allow = await new Promise<boolean>((resolve) => { resolver = resolve })
43
+ guardPending.value = null
44
+ logNav('宿主守卫', allow ? `继续 → ${to.fullPath}` : `取消 → ${to.fullPath}(URL/历史/子应用位置保持不变)`)
45
+ return allow
46
+ })
47
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * examples/templates/showcase/vue-host/src/main.ts — 宿主入口。
3
+ * 宿主 Router 是浏览器历史唯一写入方;catch-all 路由(/br-react/* 单条记录渲染同一组件)
4
+ * 保证子应用内部导航不重挂宿主组件。守卫与 POP 日志在此一次性安装。
5
+ */
6
+ import { createApp } from 'vue'
7
+ import { createRouter, createWebHistory } from 'vue-router'
8
+ import App from './App.vue'
9
+ import HomePage from './pages/HomePage.vue'
10
+ import AboutPage from './pages/AboutPage.vue'
11
+ import BridgeReactPage from './pages/BridgeReactPage.vue'
12
+ import { initBridgeRouting } from './routing'
13
+ import { installGuard } from './guard'
14
+ import { installPopStateLogger } from './demo-log'
15
+
16
+ const router = createRouter({
17
+ history: createWebHistory(),
18
+ routes: [
19
+ { path: '/', component: HomePage },
20
+ { path: '/about', component: AboutPage },
21
+ // catch-all:子应用挂载在 /br-react 前缀下,内部导航全部命中同一条路由记录
22
+ { path: '/br-react/:pathMatch(.*)*', component: BridgeReactPage },
23
+ ],
24
+ })
25
+
26
+ initBridgeRouting(router)
27
+ installGuard(router)
28
+ installPopStateLogger()
29
+
30
+ createApp(App).use(router).mount('#app')
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * examples/templates/showcase/vue-host/src/pages/AboutPage.vue — 关于页:场景与工程说明。
4
+ */
5
+ </script>
6
+
7
+ <template>
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
+ </template>
@@ -0,0 +1,74 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * examples/templates/showcase/vue-host/src/pages/BridgeReactPage.vue — 桥接演示页(Vue 宿主 × React 子应用)。
4
+ * createVueBridgeApp + routing prop(basePath=/br-react + createVueBridgeNavigation 端口);
5
+ * catch-all 路由保证子应用内部导航不重挂本组件;appProps 携带稳定回调供子应用上报事件。
6
+ */
7
+ import { ref } from 'vue'
8
+ import { useRouter } from 'vue-router'
9
+ import { createVueBridgeApp } from '@fulgurjs/federation/bridge/vue'
10
+ import { BRIDGE_BASE_PATH, getBridgeRouting } from '../routing'
11
+ import { handleChildEvent, logNav } from '../demo-log'
12
+ import ObsPanel from '../ObsPanel.vue'
13
+
14
+ const RemoteReactBridge = createVueBridgeApp('react-remote/bridge', { retries: 1 })
15
+ const routing = getBridgeRouting()
16
+ const appProps = { onChildEvent: handleChildEvent }
17
+
18
+ const router = useRouter()
19
+ const deepLink = ref(`${window.location.origin}${BRIDGE_BASE_PATH}/orders?q=网络&page=2`)
20
+ const deepLinkError = ref('')
21
+
22
+ const handleDeepLink = (): void => {
23
+ const raw = deepLink.value.trim()
24
+ let path = raw
25
+ if (/^https?:\/\//i.test(raw)) {
26
+ try {
27
+ const parsed = new URL(raw)
28
+ path = parsed.pathname + parsed.search
29
+ } catch {
30
+ deepLinkError.value = 'URL 无法解析'
31
+ return
32
+ }
33
+ }
34
+ if (path !== BRIDGE_BASE_PATH && !path.startsWith(`${BRIDGE_BASE_PATH}/`)) {
35
+ deepLinkError.value = `路径必须位于 ${BRIDGE_BASE_PATH} 前缀下`
36
+ return
37
+ }
38
+ deepLinkError.value = ''
39
+ logNav('深链粘贴', `push ${path}`)
40
+ void router.push(path)
41
+ }
42
+ </script>
43
+
44
+ <template>
45
+ <section>
46
+ <h2>桥接演示:Vue 宿主 × React 子应用(URL 同步)</h2>
47
+ <p class="demo-muted">
48
+ 子应用挂载在宿主 <code>{{ BRIDGE_BASE_PATH }}</code> 前缀下;观测台实时展示宿主 URL、
49
+ 子应用逻辑位置、history.length 与导航事件来源。
50
+ </p>
51
+ <ObsPanel />
52
+ <section class="demo-deeplink">
53
+ <h3>深链粘贴框(push 侧「刷新直达」复现)</h3>
54
+ <div class="demo-row">
55
+ <input v-model="deepLink" class="demo-deeplink-input" aria-label="深链粘贴框" />
56
+ <button @click="handleDeepLink">push 复现直达</button>
57
+ </div>
58
+ <p v-if="deepLinkError !== ''" class="demo-deeplink-error">{{ deepLinkError }}</p>
59
+ <p class="demo-muted">粘贴带中文 query 的完整 URL 后点击按钮 = 复制地址栏在新窗口打开前的 push 侧动作。</p>
60
+ </section>
61
+ <section class="demo-bridge-area" data-demo-bridge-area>
62
+ <RemoteReactBridge :routing="routing" :app-props="appProps" />
63
+ </section>
64
+ </section>
65
+ </template>
66
+
67
+ <style scoped>
68
+ .demo-deeplink { border: 1px dashed #bbb; padding: 8px 12px; margin: 12px 0; border-radius: 4px; }
69
+ .demo-deeplink h3 { margin: 6px 0; }
70
+ .demo-deeplink-input { flex: 1 1 320px; }
71
+ .demo-deeplink button { cursor: pointer; }
72
+ .demo-deeplink-error { color: #c0392b; margin: 4px 0; }
73
+ .demo-bridge-area { border: 2px solid #61dafb; padding: 8px 12px; margin: 12px 0; border-radius: 4px; }
74
+ </style>
@@ -0,0 +1,13 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * examples/templates/showcase/vue-host/src/pages/HomePage.vue — 宿主首页。
4
+ */
5
+ </script>
6
+
7
+ <template>
8
+ <section>
9
+ <h2>首页</h2>
10
+ <p>双向桥接 URL 同步演示:宿主页级路由(首页 / 关于 / 桥接演示页)+ 子应用受控 memory 路由。</p>
11
+ <p>顶部菜单「桥接演示页 / 工单列表 / 设置」经宿主 router.push 直达子应用深链。</p>
12
+ </section>
13
+ </template>
@@ -0,0 +1,31 @@
1
+ /**
2
+ * examples/templates/showcase/vue-host/src/routing.ts — 桥接宿主导航端口(模块级单例)。
3
+ * main.ts 组装宿主 router 后初始化一次:createVueBridgeNavigation(router)——
4
+ * Vue Router fullPath 已剥离 history base,逻辑路径无需再处理部署前缀;
5
+ * 端口引用保持稳定 → routing 键不变,宿主不重挂、不重复订阅。
6
+ */
7
+ import type { Router } from 'vue-router'
8
+ import { createVueBridgeNavigation } from '@fulgurjs/federation/bridge/router/vue'
9
+
10
+ export const BRIDGE_BASE_PATH = '/br-react'
11
+
12
+ /**
13
+ * 宿主 routing prop 结构(BridgeHostRouting)。
14
+ * 注:5.4.1 发布包未从 /bridge/router/vue 导出 BridgeHostRouting 类型(README §8.3 示例
15
+ * 与发布包类型面不一致,已知问题),这里用端口函数返回值结构化等价替代。
16
+ */
17
+ interface BridgeHostRoutingLike {
18
+ basePath: string
19
+ navigation: ReturnType<typeof createVueBridgeNavigation>
20
+ }
21
+
22
+ let routing: BridgeHostRoutingLike | undefined
23
+
24
+ export function initBridgeRouting(router: Router): void {
25
+ routing = { basePath: BRIDGE_BASE_PATH, navigation: createVueBridgeNavigation(router) }
26
+ }
27
+
28
+ export function getBridgeRouting(): BridgeHostRoutingLike {
29
+ if (!routing) throw new Error('桥接路由端口未初始化:请先在 main.ts 调用 initBridgeRouting(router)')
30
+ return routing
31
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "jsx": "preserve",
8
+ "skipLibCheck": true,
9
+ "noEmit": true,
10
+ "allowImportingTsExtensions": true,
11
+ "types": ["vite/client"]
12
+ },
13
+ "include": ["src", "fulgurjs.config.ts", "vite.config.ts"]
14
+ }
@@ -0,0 +1,17 @@
1
+ import type { FederationOptions } from '@fulgurjs/federation'
2
+
3
+ /**
4
+ * Vue 子应用联邦配置(examples/templates/showcase/vue-remote,端口 5335)。
5
+ * 只暴露 ./bridge 桥接契约;宿主(react-host)启用 URL 同步后经它加载。
6
+ * vue-router 是子应用自己的受控路由库(不进 shared),由消费方自装。
7
+ */
8
+ export default {
9
+ name: 'vue-remote',
10
+ filename: 'fulgurjs-remoteEntry.js',
11
+ exposes: {
12
+ './bridge': './src/bridge.ts',
13
+ },
14
+ shared: {
15
+ vue: { singleton: true },
16
+ },
17
+ } 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>vue-remote(桥接 URL 同步子应用,5335)</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.ts"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "fulgurjs-demo-bridge-vue-remote",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite --port 5335 --strictPort",
8
+ "build": "vite build",
9
+ "preview": "vite preview --port 5335 --strictPort",
10
+ "typecheck": "vue-tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@fulgurjs/federation": "5.9.0",
14
+ "vue": "^3.5.13",
15
+ "vue-router": "^4.5.0"
16
+ },
17
+ "devDependencies": {
18
+ "@vitejs/plugin-vue": "^5.2.1",
19
+ "typescript": "^5.8.0",
20
+ "vite": "^6.3.5",
21
+ "vue-tsc": "^2.2.0"
22
+ }
23
+ }
@@ -0,0 +1,54 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * examples/templates/showcase/vue-remote/src/ChildLayout.vue — 子应用根布局:导航演示按钮 + RouterView。
4
+ * push/replace 走被 connectVueBridgeRouter 接管的 router.push/replace(经通道同步宿主 URL);
5
+ * go 委托宿主浏览器历史,不创建第二条独立历史。
6
+ */
7
+ import { RouterView, useRouter } from 'vue-router'
8
+ import { getMountCount, reportNav } from './child-bus'
9
+
10
+ const router = useRouter()
11
+
12
+ const handlePushDemo = (): void => {
13
+ const target = router.resolve('/orders?q=演示&page=2').fullPath
14
+ reportNav('子应用 push/replace', 'push', target)
15
+ void router.push(target)
16
+ }
17
+
18
+ const handleReplaceDemo = (): void => {
19
+ reportNav('子应用 push/replace', 'replace', '/settings')
20
+ void router.replace('/settings')
21
+ }
22
+
23
+ const handleGoBack = (): void => {
24
+ reportNav('子应用 go', 'go', '-1')
25
+ router.go(-1)
26
+ }
27
+
28
+ const handleGoForward = (): void => {
29
+ reportNav('子应用 go', 'go', '1')
30
+ router.go(1)
31
+ }
32
+ </script>
33
+
34
+ <template>
35
+ <section class="demo-child">
36
+ <p class="demo-child-title">
37
+ <strong>Vue 子应用</strong>(受控 memory 路由)· 本会话挂载次数:{{ getMountCount() }}
38
+ </p>
39
+ <div class="demo-row">
40
+ <button @click="handlePushDemo">子应用 push /orders?q=演示&amp;page=2</button>
41
+ <button @click="handleReplaceDemo">子应用 replace /settings</button>
42
+ <button @click="handleGoBack">子应用 go(-1)</button>
43
+ <button @click="handleGoForward">子应用 go(1)</button>
44
+ </div>
45
+ <RouterView />
46
+ </section>
47
+ </template>
48
+
49
+ <style scoped>
50
+ .demo-child { border-top: 1px dashed #ccc; margin-top: 8px; padding-top: 8px; }
51
+ .demo-child-title { margin: 4px 0; }
52
+ .demo-row { display: flex; gap: 8px; align-items: center; margin: 8px 0; flex-wrap: wrap; }
53
+ .demo-row button { cursor: pointer; }
54
+ </style>
@@ -0,0 +1,48 @@
1
+ /**
2
+ * examples/templates/showcase/vue-remote/src/bridge.ts — Vue 子应用桥接契约(exposes './bridge')。
3
+ * defineBridgeApp(factory, { routing: true }) 声明路由协议(缺失时宿主启用同步即 MFU-031 占位)。
4
+ * 受控路由 = createMemoryHistory + connectVueBridgeRouter;工厂为异步形态——
5
+ * 必须 await conn.ready 后再 app.use(router)(初始 push 落定后再 install)。
6
+ */
7
+ import { createApp } from 'vue'
8
+ import { createMemoryHistory, createRouter, type RouteRecordRaw } from 'vue-router'
9
+ import { defineBridgeApp } from '@fulgurjs/federation/runtime'
10
+ import { connectVueBridgeRouter } from '@fulgurjs/federation/bridge/router/vue'
11
+ import ChildLayout from './ChildLayout.vue'
12
+ import OrderList from './pages/OrderList.vue'
13
+ import OrderDetail from './pages/OrderDetail.vue'
14
+ import Settings from './pages/Settings.vue'
15
+ import Locked from './pages/Locked.vue'
16
+ import { bindReporter, reportNav } from './child-bus'
17
+
18
+ const routes: RouteRecordRaw[] = [
19
+ // 根路径以 replace 规范化到 /orders(不产生额外历史条目)
20
+ { path: '/', redirect: '/orders' },
21
+ { path: '/orders', component: OrderList },
22
+ { path: '/orders/:id', component: OrderDetail },
23
+ { path: '/settings', component: Settings },
24
+ { path: '/locked', component: Locked },
25
+ ]
26
+
27
+ export default defineBridgeApp(async (props, ctx) => {
28
+ if (!ctx?.routing) {
29
+ throw new Error('本契约需要宿主启用 URL 同步(mount 第三参数 routing)')
30
+ }
31
+ bindReporter(props)
32
+ const init = ctx.routing.getLocation()
33
+ const initPath = init.pathname + init.search + init.hash
34
+ const router = createRouter({ history: createMemoryHistory(), routes })
35
+ const conn = connectVueBridgeRouter(ctx.routing, router, { signal: ctx.signal })
36
+ await conn.ready
37
+ const currentPath = router.currentRoute.value.fullPath
38
+ reportNav(
39
+ '子应用初始化',
40
+ 'init',
41
+ currentPath !== initPath
42
+ ? `根路径规范化 replace → ${currentPath}(无新增历史条目)`
43
+ : `初始位置 → ${currentPath}(与宿主深链一致)`,
44
+ )
45
+ const app = createApp(ChildLayout, props)
46
+ app.use(router)
47
+ return app
48
+ }, { routing: true })
@@ -0,0 +1,32 @@
1
+ /**
2
+ * examples/templates/showcase/vue-remote/src/child-bus.ts — 子应用 → 宿主观测台上报总线。
3
+ * 宿主经 appProps 传入稳定回调 onChildEvent;本模块持有该引用并维护挂载计数。
4
+ * 单页单实例假设:同一宿主页同时只挂一个本子应用实例(路由同步前缀互斥的常规形态)。
5
+ */
6
+ export type DemoChildNavAction = 'push' | 'replace' | 'go' | 'init'
7
+
8
+ export type DemoChildEvent =
9
+ | { type: 'mounted'; mountCount: number }
10
+ | { type: 'nav'; source: string; action: DemoChildNavAction; detail: string }
11
+
12
+ export type DemoChildReporter = (event: DemoChildEvent) => void
13
+
14
+ const bus: { report: DemoChildReporter | undefined } = { report: undefined }
15
+ let mountCount = 0
16
+
17
+ /** 桥接工厂内调用:从 props 快照中取出宿主回调,并上报本次挂载计数 */
18
+ export function bindReporter(props: Record<string, unknown> | undefined): void {
19
+ const candidate = (props as { onChildEvent?: DemoChildReporter } | undefined)?.onChildEvent
20
+ bus.report = typeof candidate === 'function' ? candidate : undefined
21
+ mountCount += 1
22
+ bus.report?.({ type: 'mounted', mountCount })
23
+ }
24
+
25
+ /** 导航来源上报(子应用 Link / 子应用 push·replace / 子应用 go / 子应用初始化) */
26
+ export function reportNav(source: string, action: DemoChildNavAction, detail: string): void {
27
+ bus.report?.({ type: 'nav', source, action, detail })
28
+ }
29
+
30
+ export function getMountCount(): number {
31
+ return mountCount
32
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * examples/templates/showcase/vue-remote/src/main.ts — 独立运行入口(仅 dev 占位)。
3
+ * 子应用真实能力经 exposes './bridge' 被宿主加载;直开本页仅作 dev server 健康检查。
4
+ */
5
+ import { createApp, h } from 'vue'
6
+
7
+ createApp({
8
+ render: () =>
9
+ h('div', { style: "font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; padding: 24px" }, [
10
+ h('h1', null, 'vue-remote(联邦子应用,5335)'),
11
+ h('p', null, '本应用通过 expose ./bridge(defineBridgeApp + routing 协议)被宿主加载。'),
12
+ h('p', null, [
13
+ h('a', { href: 'http://localhost:5336/br-vue/orders' }, '打开 React 宿主桥接页(5336)'),
14
+ ]),
15
+ ]),
16
+ }).mount('#app')