@fulgurjs/federation 6.8.6 → 6.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 (139) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.en.md +3 -0
  3. package/README.md +3 -0
  4. package/dist/application-core-BuhXQmLF.d.cts +16 -0
  5. package/dist/application-core-DWI3eKme.d.ts +16 -0
  6. package/dist/bridge-app-react.cjs +177 -9
  7. package/dist/bridge-app-react.d.cts +15 -3
  8. package/dist/bridge-app-react.d.ts +15 -3
  9. package/dist/bridge-app-react.js +101 -9
  10. package/dist/bridge-app-vue.cjs +138 -12
  11. package/dist/bridge-app-vue.d.cts +20 -5
  12. package/dist/bridge-app-vue.d.ts +20 -5
  13. package/dist/bridge-app-vue.js +62 -12
  14. package/dist/bridge-host-react.d.cts +1 -1
  15. package/dist/bridge-host-react.d.ts +1 -1
  16. package/dist/bridge-host-vue.d.cts +1 -1
  17. package/dist/bridge-host-vue.d.ts +1 -1
  18. package/dist/chunk-6OB47FSR.js +81 -0
  19. package/dist/cli.js +350 -24
  20. package/dist/index.cjs +7 -6
  21. package/dist/index.js +7 -6
  22. package/dist/react-adapter.d.cts +1 -1
  23. package/dist/react-adapter.d.ts +1 -1
  24. package/dist/react.d.ts +29 -4
  25. package/dist/react.js +1 -0
  26. package/dist/{remote-types-xf1B6_pK.d.ts → remote-types-COmBhjRc.d.ts} +1 -1
  27. package/dist/{remote-types-58hdbx6x.d.cts → remote-types-CStGOJwu.d.cts} +1 -1
  28. package/dist/runtime.js +1 -1
  29. package/dist/vue-adapter.d.cts +1 -1
  30. package/dist/vue-adapter.d.ts +1 -1
  31. package/dist/vue-store.cjs +40 -0
  32. package/dist/vue-store.d.cts +4 -0
  33. package/dist/vue-store.d.ts +4 -0
  34. package/dist/vue-store.js +15 -0
  35. package/dist/vue.d.ts +37 -6
  36. package/dist/vue.js +2 -0
  37. package/docs/en/reference/api.md +38 -2
  38. package/docs/en/reference/types.md +27 -0
  39. package/docs/zh/reference/api.md +46 -2
  40. package/docs/zh/reference/types.md +27 -0
  41. package/examples/templates/README.md +3 -0
  42. package/examples/templates/react-host-vue-remote/README.md +24 -0
  43. package/examples/templates/react-host-vue-remote/package.json +2 -1
  44. package/examples/templates/react-host-vue-remote/pnpm-lock.yaml +7 -7
  45. package/examples/templates/react-host-vue-remote/pnpm-workspace.yaml +1 -1
  46. package/examples/templates/react-host-vue-remote/react-host/package.json +1 -1
  47. package/examples/templates/react-host-vue-remote/react-host/src/application.tsx +62 -0
  48. package/examples/templates/react-host-vue-remote/react-host/src/main.tsx +2 -62
  49. package/examples/templates/react-host-vue-remote/scripts/prod.mjs +101 -0
  50. package/examples/templates/react-host-vue-remote/vue-remote/README.md +17 -16
  51. package/examples/templates/react-host-vue-remote/vue-remote/fulgurjs.config.ts +2 -2
  52. package/examples/templates/react-host-vue-remote/vue-remote/package.json +1 -1
  53. package/examples/templates/react-host-vue-remote/vue-remote/src/AboutPage.vue +1 -0
  54. package/examples/templates/react-host-vue-remote/vue-remote/src/App.vue +15 -5
  55. package/examples/templates/react-host-vue-remote/vue-remote/src/HomePage.vue +1 -0
  56. package/examples/templates/react-host-vue-remote/vue-remote/src/application.ts +19 -0
  57. package/examples/templates/react-host-vue-remote/vue-remote/src/main.ts +2 -15
  58. package/examples/templates/react-react/README.md +25 -1
  59. package/examples/templates/react-react/host/package.json +1 -1
  60. package/examples/templates/react-react/host/src/application.tsx +33 -0
  61. package/examples/templates/react-react/host/src/main.tsx +3 -32
  62. package/examples/templates/react-react/package.json +2 -1
  63. package/examples/templates/react-react/pnpm-lock.yaml +7 -7
  64. package/examples/templates/react-react/pnpm-workspace.yaml +1 -1
  65. package/examples/templates/react-react/remote/package.json +1 -1
  66. package/examples/templates/react-react/remote/src/application.tsx +11 -0
  67. package/examples/templates/react-react/remote/src/main.tsx +3 -10
  68. package/examples/templates/react-react/scripts/prod.mjs +101 -0
  69. package/examples/templates/showcase/README.md +35 -7
  70. package/examples/templates/showcase/package.json +2 -1
  71. package/examples/templates/showcase/pnpm-lock.yaml +11 -11
  72. package/examples/templates/showcase/pnpm-workspace.yaml +1 -1
  73. package/examples/templates/showcase/react-host/package.json +1 -1
  74. package/examples/templates/showcase/react-host/src/application.tsx +28 -0
  75. package/examples/templates/showcase/react-host/src/demo-log.ts +5 -8
  76. package/examples/templates/showcase/react-host/src/main.tsx +2 -33
  77. package/examples/templates/showcase/react-host/src/routing.ts +2 -2
  78. package/examples/templates/showcase/react-remote/fulgurjs.config.ts +1 -1
  79. package/examples/templates/showcase/react-remote/package.json +1 -1
  80. package/examples/templates/showcase/react-remote/src/ChildLayout.tsx +3 -2
  81. package/examples/templates/showcase/react-remote/src/{bridge.tsx → application.tsx} +21 -21
  82. package/examples/templates/showcase/react-remote/src/child-bus.ts +23 -23
  83. package/examples/templates/showcase/react-remote/src/main.tsx +2 -18
  84. package/examples/templates/showcase/react-remote/src/pages/Locked.tsx +2 -1
  85. package/examples/templates/showcase/react-remote/src/pages/OrderDetail.tsx +2 -1
  86. package/examples/templates/showcase/react-remote/src/pages/OrderList.tsx +2 -1
  87. package/examples/templates/showcase/scripts/prod.mjs +101 -0
  88. package/examples/templates/showcase/vue-host/package.json +1 -1
  89. package/examples/templates/showcase/vue-host/src/application.ts +28 -0
  90. package/examples/templates/showcase/vue-host/src/demo-log.ts +4 -7
  91. package/examples/templates/showcase/vue-host/src/main.ts +2 -30
  92. package/examples/templates/showcase/vue-host/src/routing.ts +2 -2
  93. package/examples/templates/showcase/vue-remote/fulgurjs.config.ts +1 -1
  94. package/examples/templates/showcase/vue-remote/package.json +1 -1
  95. package/examples/templates/showcase/vue-remote/src/ChildLayout.vue +3 -2
  96. package/examples/templates/showcase/vue-remote/src/{bridge.ts → application.ts} +10 -13
  97. package/examples/templates/showcase/vue-remote/src/child-bus.ts +23 -23
  98. package/examples/templates/showcase/vue-remote/src/main.ts +2 -16
  99. package/examples/templates/showcase/vue-remote/src/pages/Locked.vue +2 -1
  100. package/examples/templates/showcase/vue-remote/src/pages/OrderDetail.vue +2 -1
  101. package/examples/templates/showcase/vue-remote/src/pages/OrderList.vue +2 -1
  102. package/examples/templates/vue-host-react-remote/README.md +24 -0
  103. package/examples/templates/vue-host-react-remote/package.json +2 -1
  104. package/examples/templates/vue-host-react-remote/pnpm-lock.yaml +7 -7
  105. package/examples/templates/vue-host-react-remote/pnpm-workspace.yaml +1 -1
  106. package/examples/templates/vue-host-react-remote/react-remote/README.md +17 -17
  107. package/examples/templates/vue-host-react-remote/react-remote/fulgurjs.config.ts +2 -2
  108. package/examples/templates/vue-host-react-remote/react-remote/package.json +1 -1
  109. package/examples/templates/vue-host-react-remote/react-remote/src/{bridge.tsx → application.tsx} +13 -24
  110. package/examples/templates/vue-host-react-remote/react-remote/src/main.tsx +2 -7
  111. package/examples/templates/vue-host-react-remote/scripts/prod.mjs +101 -0
  112. package/examples/templates/vue-host-react-remote/vue-host/package.json +1 -1
  113. package/examples/templates/vue-host-react-remote/vue-host/src/application.ts +5 -0
  114. package/examples/templates/vue-host-react-remote/vue-host/src/main.ts +2 -4
  115. package/examples/templates/vue-vue/README.md +25 -1
  116. package/examples/templates/vue-vue/host/package.json +1 -1
  117. package/examples/templates/vue-vue/host/src/application.ts +54 -0
  118. package/examples/templates/vue-vue/host/src/main.ts +3 -50
  119. package/examples/templates/vue-vue/package.json +2 -1
  120. package/examples/templates/vue-vue/pnpm-lock.yaml +7 -7
  121. package/examples/templates/vue-vue/pnpm-workspace.yaml +1 -1
  122. package/examples/templates/vue-vue/remote/package.json +1 -1
  123. package/examples/templates/vue-vue/remote/src/application.ts +6 -0
  124. package/examples/templates/vue-vue/remote/src/main.ts +3 -5
  125. package/examples/templates/vue-vue/scripts/prod.mjs +101 -0
  126. package/package.json +14 -9
  127. package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote/metadata.json +0 -15
  128. package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote/modules.d.ts +0 -14
  129. package/examples/templates/react-host-vue-remote/react-host/src/fulgurjs/types/bridge-vue-remote/registry.d.ts +0 -10
  130. package/examples/templates/react-host-vue-remote/vue-remote/src/bridge.ts +0 -57
  131. package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote/metadata.json +0 -16
  132. package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote/modules.d.ts +0 -71
  133. package/examples/templates/showcase/react-host/src/fulgurjs/types/vue-remote/registry.d.ts +0 -10
  134. package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote/metadata.json +0 -16
  135. package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote/modules.d.ts +0 -79
  136. package/examples/templates/showcase/vue-host/src/fulgurjs/types/react-remote/registry.d.ts +0 -10
  137. package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote/metadata.json +0 -16
  138. package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote/modules.d.ts +0 -10
  139. package/examples/templates/vue-host-react-remote/vue-host/src/fulgurjs/types/bridge-react-remote/registry.d.ts +0 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # 当前发布说明
2
2
 
3
+ ## 6.9.0
4
+
5
+ - Vue/React 统一装配 defineApplication:一个默认导出同时用于独立 start 与子应用桥接,复用业务初始化,按实例 mode 选择路由。取消启动、卸载释放和容器封锁复用现有生命周期合同。
6
+ - Vue bindStoreToApp 显式绑定实际 Pinia 实例,保留 StoreDefinition 类型、$id 和热更新属性,不切换全局 activePinia。桥接 props 接受普通 interface,不要求额外索引签名。
7
+ - doctor --config 离线接入预检:源文件、开发/生产地址、状态归属、声明 glob 与必改/可选文件清单;扫描不完整和人工核查明确标记,不把源码检查当浏览器验收。
8
+ - 公开示例生产挂载、默认端口、入口统一登记 scenarios.json;same-frame 有正式生产启动命令;启动时核对构建入口前缀,固定环境配置不长期缓存。
9
+ - 五个公开模板与同框架 Demo 统一使用 application 装配;独立入口显示相同业务页面,删除多余 bridge 转发与旧生成声明缓存。Vue 本地 memory 路由也可自动安装/等待。公开依赖精确到源码候选版本,未伪造新包 integrity。
10
+ - 新增双语已有应用指南和内部 Vue/React 装配演示。
11
+ - 验收修复:本地路由装配的类型收窄(DTS 构建);doctor --config 配置加载失败按 FAIL 报告而非用法错误;资源导入不降低扫描完整性;cli 包内 esbuild 改运行时解析(ESM bundle 不再崩溃)。新增 46 项回归(装配生命周期/状态绑定/源码预检/CLI 合同)与类型正负断言。
12
+
13
+ ## 6.8.7
14
+
15
+ - 修正 init 接入说明和诊断标题:普通远程组件支持消费方传入必填 props,BLD-003 是人工检查项,不是要求所有组件提供默认值的自动构建限制。中英文指南同步,补 Vue/React CLI 说明回归。
16
+
3
17
  ## 6.8.6
4
18
 
5
19
  - 五个完整模板与功能演示统一精确依赖正式基础包 6.8.5,全部锁文件与 npm 包内模板同步。插件功能与基础版本一致。
package/README.en.md CHANGED
@@ -6,6 +6,9 @@ For example: a main system loads an independently deployed approval page, a Vue
6
6
 
7
7
  [简体中文](https://github.com/chenmingye/fulgurjs-federation/blob/master/README.md) | **[Documentation center](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/README.md)** (Chinese [docs/zh](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/zh/README.md) · English [docs/en](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/en/README.md))
8
8
 
9
+
10
+ To reduce duplicated standalone/bridge startup code, see [application assembly and preflight](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/en/guide/existing-apps.md).
11
+
9
12
  ## Quick start
10
13
 
11
14
  **New project** (recommended): scaffold a fully runnable federation workspace from a template:
package/README.md CHANGED
@@ -6,6 +6,9 @@
6
6
 
7
7
  [English](https://github.com/chenmingye/fulgurjs-federation/blob/master/README.en.md) | **[完整文档中心](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/README.md)**(中文 [docs/zh](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/zh/README.md) · English [docs/en](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/en/README.md))
8
8
 
9
+
10
+ 已有应用希望减少独立启动/桥接重复代码,参阅[统一装配与接入预检](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/zh/guide/existing-apps.md)。
11
+
9
12
  ## 快速开始
10
13
 
11
14
  **新建工程**(推荐):用 CLI 从完整模板创建可独立运行的联邦工程:
@@ -0,0 +1,16 @@
1
+ import { a as BridgeApp } from './bridge-core-77BT1__I.cjs';
2
+
3
+ /** Shared start/stop bookkeeping; framework contracts retain their own rendering semantics. */
4
+
5
+ interface ApplicationHandle {
6
+ element: HTMLElement;
7
+ /** Only disposes this start generation; an old handle cannot stop a replacement. */
8
+ unmount(): void;
9
+ }
10
+ type Application<P extends object> = BridgeApp & {
11
+ __fgBridgeProps?: P;
12
+ } & {
13
+ start(target: string | HTMLElement, ...props: {} extends P ? [props?: P] : [props: P]): Promise<ApplicationHandle>;
14
+ };
15
+
16
+ export type { Application as A };
@@ -0,0 +1,16 @@
1
+ import { a as BridgeApp } from './bridge-core-CoQRVgnZ.js';
2
+
3
+ /** Shared start/stop bookkeeping; framework contracts retain their own rendering semantics. */
4
+
5
+ interface ApplicationHandle {
6
+ element: HTMLElement;
7
+ /** Only disposes this start generation; an old handle cannot stop a replacement. */
8
+ unmount(): void;
9
+ }
10
+ type Application<P extends object> = BridgeApp & {
11
+ __fgBridgeProps?: P;
12
+ } & {
13
+ start(target: string | HTMLElement, ...props: {} extends P ? [props?: P] : [props: P]): Promise<ApplicationHandle>;
14
+ };
15
+
16
+ export type { Application as A };
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/bridge-app-react.ts
31
31
  var bridge_app_react_exports = {};
32
32
  __export(bridge_app_react_exports, {
33
+ defineApplication: () => defineApplication,
33
34
  defineBridgeApp: () => defineBridgeApp
34
35
  });
35
36
  module.exports = __toCommonJS(bridge_app_react_exports);
@@ -41,7 +42,92 @@ function markBridgeMount(target) {
41
42
  Object.defineProperty(target, BRIDGE_MOUNT_KEY, { value: true });
42
43
  }
43
44
 
45
+ // src/application-core.ts
46
+ function waitForStart(result, signal) {
47
+ return new Promise((resolve, reject) => {
48
+ let settled = false;
49
+ const finish = (failed, error) => {
50
+ if (settled) return;
51
+ settled = true;
52
+ signal.removeEventListener("abort", abort);
53
+ if (failed) reject(error);
54
+ else resolve();
55
+ };
56
+ const abort = () => {
57
+ const error = new Error("\u5E94\u7528\u542F\u52A8\u5DF2\u53D6\u6D88\u3002");
58
+ error.name = "AbortError";
59
+ finish(true, error);
60
+ };
61
+ Promise.resolve(result).then(() => finish(false), (error) => finish(true, error));
62
+ signal.addEventListener("abort", abort, { once: true });
63
+ if (signal.aborted) abort();
64
+ });
65
+ }
66
+ function createApplication(bridge, standalone) {
67
+ const entries = /* @__PURE__ */ new WeakMap();
68
+ const blocked = /* @__PURE__ */ new WeakSet();
69
+ const stop = (el) => {
70
+ const entry = entries.get(el);
71
+ if (!entry) {
72
+ try {
73
+ bridge.unmount(el);
74
+ } catch (error) {
75
+ blocked.add(el);
76
+ throw error;
77
+ }
78
+ return;
79
+ }
80
+ entry.controller.abort();
81
+ try {
82
+ standalone.unmount(el);
83
+ entries.delete(el);
84
+ } catch (error) {
85
+ entry.blocked = true;
86
+ blocked.add(el);
87
+ throw error;
88
+ }
89
+ };
90
+ return {
91
+ ...bridge.routing ? { routing: bridge.routing } : {},
92
+ mount(el, props, options) {
93
+ if (entries.has(el) || blocked.has(el)) throw new Error("\u8BE5\u5BB9\u5668\u5DF2\u6709\u72EC\u7ACB\u5E94\u7528\u6216\u5378\u8F7D\u5931\u8D25\u7684\u5B9E\u4F8B\uFF1B\u5148\u6B63\u5E38\u5378\u8F7D\uFF0C\u5378\u8F7D\u5931\u8D25\u65F6\u6574\u9875\u5237\u65B0\u3002");
94
+ return bridge.mount(el, props, options);
95
+ },
96
+ unmount: stop,
97
+ async start(target, ...args) {
98
+ const el = typeof target === "string" ? document.querySelector(target) : target;
99
+ const ElementClass = el?.ownerDocument.defaultView?.HTMLElement;
100
+ if (!ElementClass || !(el instanceof ElementClass)) throw new Error(`\u5E94\u7528\u6302\u8F7D\u5BB9\u5668\u4E0D\u5B58\u5728\u6216\u4E0D\u662F HTMLElement\uFF1A${typeof target === "string" ? target : "target"}`);
101
+ if (entries.has(el) || blocked.has(el)) throw new Error("\u8BE5\u5BB9\u5668\u5DF2\u6709\u72EC\u7ACB\u5E94\u7528\u6216\u5378\u8F7D\u5931\u8D25\u7684\u5B9E\u4F8B\uFF1B\u4E0D\u80FD\u91CD\u590D start\u3002");
102
+ const entry = { controller: new AbortController(), blocked: false };
103
+ entries.set(el, entry);
104
+ try {
105
+ await waitForStart(standalone.mount(el, { ...args[0] ?? {} }, { signal: entry.controller.signal }), entry.controller.signal);
106
+ if (entry.controller.signal.aborted || entries.get(el) !== entry) {
107
+ const error = new Error("\u5E94\u7528\u542F\u52A8\u5DF2\u53D6\u6D88\uFF0C\u8FDF\u5230\u7684\u521D\u59CB\u5316\u7ED3\u679C\u4E0D\u4F1A\u91CD\u65B0\u6302\u8F7D\u3002");
108
+ error.name = "AbortError";
109
+ throw error;
110
+ }
111
+ } catch (error) {
112
+ if (entries.get(el) === entry && !entry.blocked) entries.delete(el);
113
+ entry.controller.abort();
114
+ throw error;
115
+ }
116
+ return { element: el, unmount() {
117
+ if (entries.get(el) === entry) stop(el);
118
+ } };
119
+ }
120
+ };
121
+ }
122
+
44
123
  // src/bridge-app-react.ts
124
+ var appDisposers = /* @__PURE__ */ new WeakMap();
125
+ function disposeResources(entry) {
126
+ const dispose = entry.dispose;
127
+ entry.dispose = void 0;
128
+ appDisposers.delete(entry.controller.signal);
129
+ dispose?.();
130
+ }
45
131
  var entriesByEl = /* @__PURE__ */ new WeakMap();
46
132
  var FirstCommitGate = class extends import_react.Component {
47
133
  state = { error: null };
@@ -64,32 +150,82 @@ function CommitProbe({ children, onCommit }) {
64
150
  return children ?? null;
65
151
  }
66
152
  function defineBridgeApp(factory, options = {}) {
153
+ return createReactAppContract(factory, options, "bridge");
154
+ }
155
+ function defineApplication(factory, options = {}) {
156
+ const contract = (mode) => createReactAppContract((props, ctx) => {
157
+ const context = { ...ctx, mode, signal: ctx.signal };
158
+ if (mode === "bridge") markBridgeMount(context);
159
+ const result = factory(props, context);
160
+ if (!result || typeof result !== "object") throw new Error("defineApplication \u5DE5\u5382\u5FC5\u987B\u8FD4\u56DE ReactElement \u6216 { element, dispose? }\u3002");
161
+ if ("element" in result) {
162
+ if (result.dispose) appDisposers.set(context.signal, result.dispose);
163
+ return result.element;
164
+ }
165
+ return result;
166
+ }, options, mode);
167
+ return createApplication(contract("bridge"), contract("standalone"));
168
+ }
169
+ function createReactAppContract(factory, options, mode) {
170
+ const owner = /* @__PURE__ */ Symbol("react-application");
67
171
  return {
68
- ...options.routing ? { routing: { protocol: 1 } } : {},
172
+ ...mode === "bridge" && options.routing ? { routing: { protocol: 1 } } : {},
69
173
  mount(el, props, mountOptions) {
70
174
  if (entriesByEl.has(el)) {
71
175
  throw new Error("\u540C\u4E00\u5BB9\u5668 el \u5DF2\u6302\u8F7D\u672C\u6865\u63A5\u5E94\u7528\uFF08\u5BB9\u5668\u5DF2\u88AB\u5360\u7528\uFF09\u3002\u540C\u4E00\u5BB9\u5668\u672A\u5378\u8F7D\u524D\u91CD\u590D mount \u662F\u5951\u7EA6\u8FDD\u4F8B\u2014\u2014\u5148 unmount \u518D mount\u3002");
72
176
  }
73
177
  const snapshot = { ...props ?? {} };
74
- const ctx = { signal: mountOptions?.signal, routing: mountOptions?.routing };
75
- markBridgeMount(ctx);
178
+ const controller = new AbortController();
179
+ const ctx = { signal: controller.signal, routing: mountOptions?.routing };
180
+ if (mode === "bridge") markBridgeMount(ctx);
181
+ const entry = {
182
+ owner,
183
+ status: "pending",
184
+ controller,
185
+ removeAbortListener: () => mountOptions?.signal?.removeEventListener("abort", abort)
186
+ };
187
+ const abort = () => {
188
+ controller.abort();
189
+ entry.cancelCommit?.();
190
+ };
191
+ mountOptions?.signal?.addEventListener("abort", abort, { once: true });
192
+ entriesByEl.set(el, entry);
193
+ if (mountOptions?.signal?.aborted) {
194
+ abort();
195
+ entriesByEl.delete(el);
196
+ entry.removeAbortListener();
197
+ return Promise.resolve();
198
+ }
76
199
  let element;
77
200
  try {
78
201
  element = factory(snapshot, ctx);
202
+ entry.dispose = appDisposers.get(controller.signal);
79
203
  } catch (e) {
204
+ entriesByEl.delete(el);
205
+ entry.removeAbortListener();
206
+ controller.abort();
207
+ try {
208
+ disposeResources(entry);
209
+ } catch {
210
+ }
80
211
  throw e instanceof Error ? e : new Error(String(e));
81
212
  }
82
213
  if (element === null || typeof element !== "object") {
214
+ entriesByEl.delete(el);
215
+ entry.removeAbortListener();
216
+ controller.abort();
217
+ try {
218
+ disposeResources(entry);
219
+ } catch {
220
+ }
83
221
  throw new Error(
84
222
  `defineBridgeApp \u5DE5\u5382\u8FD4\u56DE\u503C\u4E0D\u662F ReactElement\uFF08\u5F53\u524D ${element === null ? "null" : typeof element}\uFF09\u3002\u5DE5\u5382\u5FC5\u987B\u8FD4\u56DE ReactElement\uFF08JSX/ createElement \u7684\u8FD4\u56DE\u503C\uFF09\u3002`
85
223
  );
86
224
  }
87
- const entry = { status: "pending" };
88
- entriesByEl.set(el, entry);
89
225
  const promise = (async () => {
90
226
  const reactDomClient = await import("react-dom/client");
91
227
  const createRoot = reactDomClient.createRoot ?? reactDomClient.default?.createRoot;
92
- if (entry.status === "abort" || entriesByEl.get(el) !== entry) return;
228
+ if (entry.status === "abort" || controller.signal.aborted || entriesByEl.get(el) !== entry) return;
93
229
  if (typeof createRoot !== "function") throw new Error(
94
230
  "react-dom/client \u672A\u5BFC\u51FA createRoot\u3002\u8BF7\u6838\u5BF9 React \u4E0E renderer \u7248\u672C\u53CA\u5171\u4EAB\u5B50\u8DEF\u5F84\u914D\u7F6E\u3002"
95
231
  );
@@ -110,6 +246,15 @@ function defineBridgeApp(factory, options = {}) {
110
246
  phaseRef.current = "committed";
111
247
  resolve();
112
248
  };
249
+ entry.cancelCommit = () => {
250
+ if (settled) return;
251
+ settled = true;
252
+ resolve();
253
+ };
254
+ if (controller.signal.aborted) {
255
+ entry.cancelCommit();
256
+ return;
257
+ }
113
258
  root.render(
114
259
  (0, import_react.createElement)(
115
260
  FirstCommitGate,
@@ -129,28 +274,51 @@ function defineBridgeApp(factory, options = {}) {
129
274
  }
130
275
  }
131
276
  if (entriesByEl.get(el) === entry) entriesByEl.delete(el);
277
+ entry.removeAbortListener();
278
+ controller.abort();
279
+ try {
280
+ disposeResources(entry);
281
+ } catch {
282
+ }
132
283
  throw e;
133
284
  }
134
285
  ).then(() => void 0);
135
286
  },
136
287
  unmount(el) {
137
288
  const entry = entriesByEl.get(el);
138
- if (!entry) return;
289
+ if (!entry || entry.owner !== owner) return;
139
290
  entriesByEl.delete(el);
291
+ entry.removeAbortListener();
292
+ entry.controller.abort();
293
+ entry.cancelCommit?.();
140
294
  if (entry.status === "pending" || !entry.root) {
141
295
  entry.status = "abort";
296
+ disposeResources(entry);
142
297
  return;
143
298
  }
144
299
  entry.status = "abort";
300
+ let failure;
301
+ let failed = false;
145
302
  try {
146
303
  entry.root.unmount();
147
- } catch (e) {
148
- throw e instanceof Error ? e : new Error(String(e));
304
+ } catch (error) {
305
+ failed = true;
306
+ failure = error;
307
+ }
308
+ try {
309
+ disposeResources(entry);
310
+ } catch (error) {
311
+ if (!failed) {
312
+ failed = true;
313
+ failure = error;
314
+ }
149
315
  }
316
+ if (failed) throw failure instanceof Error ? failure : new Error(String(failure));
150
317
  }
151
318
  };
152
319
  }
153
320
  // Annotate the CommonJS export names for ESM import in node:
154
321
  0 && (module.exports = {
322
+ defineApplication,
155
323
  defineBridgeApp
156
324
  });
@@ -1,5 +1,6 @@
1
1
  import { ReactElement } from 'react';
2
2
  import { B as BridgeChildRoute, a as BridgeApp } from './bridge-core-77BT1__I.cjs';
3
+ import { A as Application } from './application-core-BuhXQmLF.cjs';
3
4
  import './context.cjs';
4
5
 
5
6
  /**
@@ -31,7 +32,7 @@ interface ReactBridgeAppContext {
31
32
  /** React 子应用工厂:接收挂载时 props 快照与生命周期上下文,返回 ReactElement */
32
33
  type ReactBridgeAppFactory<P = Record<string, unknown>> = (props: P, ctx?: ReactBridgeAppContext) => ReactElement;
33
34
  /** 桥接 props 的类型标记(phantom,与 Vue 侧同构;见 bridge-app-vue 的说明) */
34
- type FgBridgePropsMarker<P extends Record<string, unknown>> = {
35
+ type FgBridgePropsMarker<P extends object> = {
35
36
  __fgBridgeProps?: P;
36
37
  };
37
38
  interface DefineReactBridgeAppOptions {
@@ -47,6 +48,17 @@ interface DefineReactBridgeAppOptions {
47
48
  * export default defineBridgeApp((props) => <MemoryRouter><App {...props} /></MemoryRouter>)
48
49
  * ```
49
50
  */
50
- declare function defineBridgeApp<P extends Record<string, unknown> = Record<string, unknown>>(factory: ReactBridgeAppFactory<P>, options?: DefineReactBridgeAppOptions): BridgeApp & FgBridgePropsMarker<P>;
51
+ declare function defineBridgeApp<P extends object = Record<string, unknown>>(factory: ReactBridgeAppFactory<P>, options?: DefineReactBridgeAppOptions): BridgeApp & FgBridgePropsMarker<P>;
52
+ interface ReactApplicationContext extends ReactBridgeAppContext {
53
+ mode: 'standalone' | 'bridge';
54
+ signal: AbortSignal;
55
+ }
56
+ interface ReactApplicationSetup {
57
+ element: ReactElement;
58
+ /** Dispose a standalone data router or other instance-owned resources. */
59
+ dispose?: () => void;
60
+ }
61
+ type ReactApplicationFactory<P = Record<string, unknown>> = (props: P, context: ReactApplicationContext) => ReactElement | ReactApplicationSetup;
62
+ declare function defineApplication<P extends object = Record<string, unknown>>(factory: ReactApplicationFactory<P>, options?: DefineReactBridgeAppOptions): Application<P>;
51
63
 
52
- export { BridgeApp, type DefineReactBridgeAppOptions, type FgBridgePropsMarker, type ReactBridgeAppContext, type ReactBridgeAppFactory, defineBridgeApp };
64
+ export { BridgeApp, type DefineReactBridgeAppOptions, type FgBridgePropsMarker, type ReactApplicationContext, type ReactApplicationFactory, type ReactApplicationSetup, type ReactBridgeAppContext, type ReactBridgeAppFactory, defineApplication, defineBridgeApp };
@@ -1,5 +1,6 @@
1
1
  import { ReactElement } from 'react';
2
2
  import { B as BridgeChildRoute, a as BridgeApp } from './bridge-core-CoQRVgnZ.js';
3
+ import { A as Application } from './application-core-DWI3eKme.js';
3
4
  import './context.js';
4
5
 
5
6
  /**
@@ -31,7 +32,7 @@ interface ReactBridgeAppContext {
31
32
  /** React 子应用工厂:接收挂载时 props 快照与生命周期上下文,返回 ReactElement */
32
33
  type ReactBridgeAppFactory<P = Record<string, unknown>> = (props: P, ctx?: ReactBridgeAppContext) => ReactElement;
33
34
  /** 桥接 props 的类型标记(phantom,与 Vue 侧同构;见 bridge-app-vue 的说明) */
34
- type FgBridgePropsMarker<P extends Record<string, unknown>> = {
35
+ type FgBridgePropsMarker<P extends object> = {
35
36
  __fgBridgeProps?: P;
36
37
  };
37
38
  interface DefineReactBridgeAppOptions {
@@ -47,6 +48,17 @@ interface DefineReactBridgeAppOptions {
47
48
  * export default defineBridgeApp((props) => <MemoryRouter><App {...props} /></MemoryRouter>)
48
49
  * ```
49
50
  */
50
- declare function defineBridgeApp<P extends Record<string, unknown> = Record<string, unknown>>(factory: ReactBridgeAppFactory<P>, options?: DefineReactBridgeAppOptions): BridgeApp & FgBridgePropsMarker<P>;
51
+ declare function defineBridgeApp<P extends object = Record<string, unknown>>(factory: ReactBridgeAppFactory<P>, options?: DefineReactBridgeAppOptions): BridgeApp & FgBridgePropsMarker<P>;
52
+ interface ReactApplicationContext extends ReactBridgeAppContext {
53
+ mode: 'standalone' | 'bridge';
54
+ signal: AbortSignal;
55
+ }
56
+ interface ReactApplicationSetup {
57
+ element: ReactElement;
58
+ /** Dispose a standalone data router or other instance-owned resources. */
59
+ dispose?: () => void;
60
+ }
61
+ type ReactApplicationFactory<P = Record<string, unknown>> = (props: P, context: ReactApplicationContext) => ReactElement | ReactApplicationSetup;
62
+ declare function defineApplication<P extends object = Record<string, unknown>>(factory: ReactApplicationFactory<P>, options?: DefineReactBridgeAppOptions): Application<P>;
51
63
 
52
- export { BridgeApp, type DefineReactBridgeAppOptions, type FgBridgePropsMarker, type ReactBridgeAppContext, type ReactBridgeAppFactory, defineBridgeApp };
64
+ export { BridgeApp, type DefineReactBridgeAppOptions, type FgBridgePropsMarker, type ReactApplicationContext, type ReactApplicationFactory, type ReactApplicationSetup, type ReactBridgeAppContext, type ReactBridgeAppFactory, defineApplication, defineBridgeApp };
@@ -1,3 +1,6 @@
1
+ import {
2
+ createApplication
3
+ } from "./chunk-6OB47FSR.js";
1
4
  import {
2
5
  markBridgeMount
3
6
  } from "./chunk-HDRZBSND.js";
@@ -7,6 +10,13 @@ import "./chunk-OF2JCXTZ.js";
7
10
 
8
11
  // src/bridge-app-react.ts
9
12
  import { Component, createElement, useEffect } from "react";
13
+ var appDisposers = /* @__PURE__ */ new WeakMap();
14
+ function disposeResources(entry) {
15
+ const dispose = entry.dispose;
16
+ entry.dispose = void 0;
17
+ appDisposers.delete(entry.controller.signal);
18
+ dispose?.();
19
+ }
10
20
  var entriesByEl = /* @__PURE__ */ new WeakMap();
11
21
  var FirstCommitGate = class extends Component {
12
22
  state = { error: null };
@@ -29,32 +39,82 @@ function CommitProbe({ children, onCommit }) {
29
39
  return children ?? null;
30
40
  }
31
41
  function defineBridgeApp(factory, options = {}) {
42
+ return createReactAppContract(factory, options, "bridge");
43
+ }
44
+ function defineApplication(factory, options = {}) {
45
+ const contract = (mode) => createReactAppContract((props, ctx) => {
46
+ const context = { ...ctx, mode, signal: ctx.signal };
47
+ if (mode === "bridge") markBridgeMount(context);
48
+ const result = factory(props, context);
49
+ if (!result || typeof result !== "object") throw new Error("defineApplication \u5DE5\u5382\u5FC5\u987B\u8FD4\u56DE ReactElement \u6216 { element, dispose? }\u3002");
50
+ if ("element" in result) {
51
+ if (result.dispose) appDisposers.set(context.signal, result.dispose);
52
+ return result.element;
53
+ }
54
+ return result;
55
+ }, options, mode);
56
+ return createApplication(contract("bridge"), contract("standalone"));
57
+ }
58
+ function createReactAppContract(factory, options, mode) {
59
+ const owner = /* @__PURE__ */ Symbol("react-application");
32
60
  return {
33
- ...options.routing ? { routing: { protocol: 1 } } : {},
61
+ ...mode === "bridge" && options.routing ? { routing: { protocol: 1 } } : {},
34
62
  mount(el, props, mountOptions) {
35
63
  if (entriesByEl.has(el)) {
36
64
  throw new Error("\u540C\u4E00\u5BB9\u5668 el \u5DF2\u6302\u8F7D\u672C\u6865\u63A5\u5E94\u7528\uFF08\u5BB9\u5668\u5DF2\u88AB\u5360\u7528\uFF09\u3002\u540C\u4E00\u5BB9\u5668\u672A\u5378\u8F7D\u524D\u91CD\u590D mount \u662F\u5951\u7EA6\u8FDD\u4F8B\u2014\u2014\u5148 unmount \u518D mount\u3002");
37
65
  }
38
66
  const snapshot = { ...props ?? {} };
39
- const ctx = { signal: mountOptions?.signal, routing: mountOptions?.routing };
40
- markBridgeMount(ctx);
67
+ const controller = new AbortController();
68
+ const ctx = { signal: controller.signal, routing: mountOptions?.routing };
69
+ if (mode === "bridge") markBridgeMount(ctx);
70
+ const entry = {
71
+ owner,
72
+ status: "pending",
73
+ controller,
74
+ removeAbortListener: () => mountOptions?.signal?.removeEventListener("abort", abort)
75
+ };
76
+ const abort = () => {
77
+ controller.abort();
78
+ entry.cancelCommit?.();
79
+ };
80
+ mountOptions?.signal?.addEventListener("abort", abort, { once: true });
81
+ entriesByEl.set(el, entry);
82
+ if (mountOptions?.signal?.aborted) {
83
+ abort();
84
+ entriesByEl.delete(el);
85
+ entry.removeAbortListener();
86
+ return Promise.resolve();
87
+ }
41
88
  let element;
42
89
  try {
43
90
  element = factory(snapshot, ctx);
91
+ entry.dispose = appDisposers.get(controller.signal);
44
92
  } catch (e) {
93
+ entriesByEl.delete(el);
94
+ entry.removeAbortListener();
95
+ controller.abort();
96
+ try {
97
+ disposeResources(entry);
98
+ } catch {
99
+ }
45
100
  throw e instanceof Error ? e : new Error(String(e));
46
101
  }
47
102
  if (element === null || typeof element !== "object") {
103
+ entriesByEl.delete(el);
104
+ entry.removeAbortListener();
105
+ controller.abort();
106
+ try {
107
+ disposeResources(entry);
108
+ } catch {
109
+ }
48
110
  throw new Error(
49
111
  `defineBridgeApp \u5DE5\u5382\u8FD4\u56DE\u503C\u4E0D\u662F ReactElement\uFF08\u5F53\u524D ${element === null ? "null" : typeof element}\uFF09\u3002\u5DE5\u5382\u5FC5\u987B\u8FD4\u56DE ReactElement\uFF08JSX/ createElement \u7684\u8FD4\u56DE\u503C\uFF09\u3002`
50
112
  );
51
113
  }
52
- const entry = { status: "pending" };
53
- entriesByEl.set(el, entry);
54
114
  const promise = (async () => {
55
115
  const reactDomClient = await import("react-dom/client");
56
116
  const createRoot = reactDomClient.createRoot ?? reactDomClient.default?.createRoot;
57
- if (entry.status === "abort" || entriesByEl.get(el) !== entry) return;
117
+ if (entry.status === "abort" || controller.signal.aborted || entriesByEl.get(el) !== entry) return;
58
118
  if (typeof createRoot !== "function") throw new Error(
59
119
  "react-dom/client \u672A\u5BFC\u51FA createRoot\u3002\u8BF7\u6838\u5BF9 React \u4E0E renderer \u7248\u672C\u53CA\u5171\u4EAB\u5B50\u8DEF\u5F84\u914D\u7F6E\u3002"
60
120
  );
@@ -75,6 +135,15 @@ function defineBridgeApp(factory, options = {}) {
75
135
  phaseRef.current = "committed";
76
136
  resolve();
77
137
  };
138
+ entry.cancelCommit = () => {
139
+ if (settled) return;
140
+ settled = true;
141
+ resolve();
142
+ };
143
+ if (controller.signal.aborted) {
144
+ entry.cancelCommit();
145
+ return;
146
+ }
78
147
  root.render(
79
148
  createElement(
80
149
  FirstCommitGate,
@@ -94,27 +163,50 @@ function defineBridgeApp(factory, options = {}) {
94
163
  }
95
164
  }
96
165
  if (entriesByEl.get(el) === entry) entriesByEl.delete(el);
166
+ entry.removeAbortListener();
167
+ controller.abort();
168
+ try {
169
+ disposeResources(entry);
170
+ } catch {
171
+ }
97
172
  throw e;
98
173
  }
99
174
  ).then(() => void 0);
100
175
  },
101
176
  unmount(el) {
102
177
  const entry = entriesByEl.get(el);
103
- if (!entry) return;
178
+ if (!entry || entry.owner !== owner) return;
104
179
  entriesByEl.delete(el);
180
+ entry.removeAbortListener();
181
+ entry.controller.abort();
182
+ entry.cancelCommit?.();
105
183
  if (entry.status === "pending" || !entry.root) {
106
184
  entry.status = "abort";
185
+ disposeResources(entry);
107
186
  return;
108
187
  }
109
188
  entry.status = "abort";
189
+ let failure;
190
+ let failed = false;
110
191
  try {
111
192
  entry.root.unmount();
112
- } catch (e) {
113
- throw e instanceof Error ? e : new Error(String(e));
193
+ } catch (error) {
194
+ failed = true;
195
+ failure = error;
196
+ }
197
+ try {
198
+ disposeResources(entry);
199
+ } catch (error) {
200
+ if (!failed) {
201
+ failed = true;
202
+ failure = error;
203
+ }
114
204
  }
205
+ if (failed) throw failure instanceof Error ? failure : new Error(String(failure));
115
206
  }
116
207
  };
117
208
  }
118
209
  export {
210
+ defineApplication,
119
211
  defineBridgeApp
120
212
  };