@fulgurjs/federation 5.6.0 → 5.7.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.7.1
4
+
5
+ - React 桥接兼容 react-dom/client 的 default-only CJS 命名空间(Vite 6 + React 18),使用同一 renderer 完成真实提交与卸载。
6
+
7
+ - runtime gzip 预算 10240→10496B(实测 10273B):共享屏障及旧冻结内核兼容为必要逻辑增量,继续保留硬门禁。
8
+
9
+ - 修复同步消费拒绝异步 resolveShare 后的迟到 Promise 拒绝,避免额外 unhandledrejection。
10
+ - HTML 入口配置 runtimePlugins 时增加异步协商屏障;远程 expose 执行前准备共享决策,消费方门面保持原有同步形态。异步选择的外部条目与静态/动态实例统一复用;provider 失败后允许重新裁决。
11
+ - 首次消费者接管单例时按真实物理版本登记,不再把 React 18 实例填入标注 React 19 的槽位而绕过 strictVersion;另一版本的单例正在异步加载时拒绝同步接管,避免并发双实例。
12
+ - runtimePlugins 的相对路径以项目 root 解析,避免入口/虚拟模块位置改变后解析错误。
13
+ - 新增 demo/react-versions:React 19 嵌 React 18 隔离、异步 hook 覆盖首次静态导入、严格拒绝与版本对齐远程的同页恢复、Vue 宿主对齐 React 18。新增 Vite 6/8 dev+prod 浏览器 CI。
14
+ - 自定义无 HTML 的入口须先完成动态协商再执行消费者,已求值的静态绑定不能追溯改写。
15
+
16
+ ## 5.7.0
17
+
18
+ - **修复:Vite 8 同步协商门面的共享语义补修(strictVersion 拒绝 + resolveShare 契约)**——5.6.0 引入的同步门面(V8-SYNC-FACADE)存在两处语义缺陷,本轮以 runtime 新决策入口 `loadShareSync` 统一修复,绑定门面/命名空间门面/CJS 垫片三条同步路径全部改走该入口(各引擎一致):
19
+ - **strictVersion 冲突被吞**——5.6.0 门面用 `getLoadedShare`(readyOnly 过滤:只看已就绪实例)+ 空 catch 兜底:非 singleton(webpack 默认 strictVersion=true)下「已注册未加载的本地版本」被排除出候选集 → 误判「无满足版本」抛 MFU-003 → 被 catch 吞掉后静默使用本地副本,README「strictVersion 冲突抛 MFU-003」契约失效。现与异步 loadShare 共用同一选择器(**全部已注册版本**)——已注册未加载的本地版本是合法选中对象(其物理实例就是门面的本地 import,等价异步 `await get()`),strictVersion 冲突(singleton 收养不满足版本 / 真无满足版本)一律抛 MFU-003(保留 shareKey/requiredVersion/提供方细节),不再被本地 fallback 掩盖;拒绝后不 pin、不污染作用域。
20
+ - **resolveShare 首次静态导入被跳过**——现按契约参与:该消费条件的成功快照无条件复用;无快照时**同步调用 hook**(其决策写入快照供后续消费复用);hook 返回 Promise 或选中未就绪的他人条目 → 抛 MFU-004+`details.syncUnsupported`(明确诊断 + 修法,不静默忽略 hook);hook 抛出原样传播。**已知边界(如实)**:异步 resolveShare(返回 Promise)无法在 Vite 8 同步静态导入路径等待——诊断明确给出两条修法(同步返回 picked / 先经一次动态 loadShare 建立快照),不静默执行错误语义。
21
+ - **singleton 首个消费者接管语义**——作用域无任何已加载实例时(如 React 宿主声明 vue 单例但自身不消费),首个消费者(同步或异步)以本地版本接管该单例(MFU-010 告警版本漂移),后续消费者按已加载优先收敛同一实例——webpack「首个消费者定单例」语义;strictVersion 冲突仍拒绝。
22
+ - **新增运行时 API:`loadShareSync(key, opts)`**(内部同步路径专用,未进 `/runtime` 公开入口清单):返回 `{ kind: 'ready', value }`(已就绪实例)或 `{ kind: 'local' }`(使用本地副本,调用方负责 pin);「没有可用共享实例 / 版本冲突 / 尚未就绪 / 决策失败」四态以返回形态与错误码严格区分。
23
+ - **质量门禁**:单测 631 → 647(loadShareSync 决策树 9 例 + 同步门面×真实 runtime 执行级回归 4 例——strictVersion 传播/hook 遵循/静态与动态实例严格相等/B-2/B-8 本地交付);e2e dev 73/73 × vite 6.4.3/8.3.2、prod 33/33 × vite 6.4.3/8.3.2;Jeecg vite 8.3.2 生产五场景 5/5 零错误;gzip 阈值 9216→10240B(loadShareSync 为语义补修的必要增量)。
24
+
3
25
  ## 5.6.0
4
26
 
5
27
  - **修复:Vite 8(rolldown)生产链路五项(V8-SYNC-FACADE 配套)**
package/README.en.md CHANGED
@@ -53,7 +53,7 @@ pnpm add -D @fulgurjs/federation
53
53
  - Vue ≥ 3.2.0 and/or React `>=18.0.0 <20` — all three are **optional peers**; install only the framework you use
54
54
  - Chrome 108+ (native top-level await)
55
55
 
56
- > Vite 8 (rolldown): as of 5.3.3, dev, production builds and production page mounting all pass full acceptance (bidirectional bridge 11-step interaction matrix, see the acceptance report). **5.4.2 boundary note**: in production builds of large apps (e.g. JeecgBoot v3.9.5), the plugin's TLA share facades joining chunk merging can trigger a rolldown codegen bug that merges the app's own async initialization into a sync lazy init and drops the `async` marker (`SyntaxError: Unexpected reserved word`; minify/target/manualChunks variants and a bare-baseline comparison have ruled out other variables) — build such apps for production with Vite 5–7; dev is unaffected. The first page open on a cold dev cache still falls inside the dependency pre-bundling window (DEV-010; it self-recovers via reload). Warm up before acceptance runs or manual judgement, as documented.
56
+ > Vite 8 (rolldown): **fully usable in production as of 5.6.0** — the synchronous negotiation facade (V8-SYNC-FACADE) eliminates the startup mutual-await deadlock between top-level await propagation and application circular dependencies (full JeecgBoot v3.9.5 acceptance: login, A-hosts-B, three-layer deep-link refresh, cross-framework deep links, React host; vite 8.3.2 + rolldown 1.2.12), plus three vite8-specific fixes (remoteEntry failure retry, manifest exposes mapping, dependency-preload negative caching); e2e dev 73/73 + prod 33/33 as a standing CI matrix. The first page open on a cold dev cache still falls inside the dependency pre-bundling window (DEV-010; it self-recovers via reload). Warm up before acceptance runs or manual judgement, as documented. Known cost (vite 8 only): the synchronous facade keeps the local copy chunk reachable by the module graph even when negotiation picks another app's instance (dual-version scenarios fetch the shared library at most twice; runtime identity still converges to a single instance).
57
57
 
58
58
  `@fulgurjs/federation/runtime` and `@fulgurjs/federation/react` are **ESM-only** browser entries (no `require()`). The build-time main entry supports both ESM and CJS.
59
59
 
@@ -182,6 +182,12 @@ Standalone page-level boundary. Props: `children`, `fallback` (node or `({ error
182
182
  - No `keepAliveNames` / no keep-alive promise (Vue-specific); routing is not a runtime dependency — render `component(spec)` output from your router (React Router examples in `examples/react/host`; route params reach remote pages as props)
183
183
  - Cross-framework Context: host and remote get the **same Context object** through the same expose instance; the plugin does not auto-bridge arbitrary React Contexts
184
184
 
185
+ ### Async shared decisions and React version isolation (5.7.1)
186
+
187
+ HTML entries configured with `runtimePlugins` negotiate and load shared dependencies before dynamically executing the application. Remote containers prepare shared decisions before executing exposes. Sync facades reuse the same decision and instance, including async hooks selecting a lower version or an external entry. Vite 8 consumer facades remain synchronous; the bootstrap boundary keeps negotiation waits out of consumer dependency cycles.
188
+
189
+ Library/custom entries without HTML, and policies registered after application startup, must `await loadShare(name, opts)` before dynamically importing new consumers. Existing evaluated static bindings cannot be changed retroactively. An unprepared synchronous consumer still reports MFU-004 with `details.syncUnsupported`; late hook rejections are handled rather than becoming additional unhandled rejections. Local singleton adoption records the instance under its actual version, preserving strict version rejection. To run React 18 and 19 together, isolate React and its renderer in separate share scopes and bridge plain props/callbacks, not React elements or Context objects. See [the runnable version isolation demo](demo/react-versions/README.md).
190
+
185
191
  ### 8.2 Runtime API — `@fulgurjs/federation/runtime` (Vue apps) and common functions on `/react`
186
192
 
187
193
  | Function | Signature | Semantics |
package/README.md CHANGED
@@ -51,7 +51,7 @@ pnpm add -D @fulgurjs/federation
51
51
 
52
52
  要求:Vite ≥ 5.1(实测至 8.x)、Node ≥ 18、Vue 3 和/或 React 18–19(均为可选 peer——按所用框架安装)、浏览器 Chrome 108+(TLA 原生支持)。
53
53
 
54
- > Vite 8(rolldown):5.3.3 起 dev 与生产构建、生产页面挂载均已通过完整验收(双向桥接 11 步交互矩阵,见验收报告)。**5.4.2 补充边界**:大型应用(如 JeecgBoot v3.9.5)生产构建下,插件的 TLA 共享门面参与 chunk 合并时可触发 rolldown 将应用 async 初始化语句合并丢 `async` 标记(`SyntaxError: Unexpected reserved word`,minify/target/manualChunks 变体与裸基线对照已排除其他变量)——此类应用生产构建请用 Vite 5–7,开发态不受影响。dev 冷启动首开仍受依赖预构建窗口影响(DEV-010,首轮打开自动恢复),验收与人工判断请按文档先预热。
54
+ > Vite 8(rolldown):**5.6.0 起生产链路完整可用**——同步协商门面(V8-SYNC-FACADE)根除了「TLA 顶层 await × 应用循环依赖」的启动互等死锁(JeecgBoot v3.9.5 全场景验收:登录/A套B/三层深链刷新直达/跨框架深链/React 宿主,vite 8.3.2 + rolldown 1.2.12),并修复 remoteEntry 失败重试、manifest exposes 缺项、依赖预载负缓存三项 vite8 专属缺陷;e2e dev 73/73 + prod 33/33(CI 常驻矩阵)。dev 冷启动首开仍受依赖预构建窗口影响(DEV-010,首轮打开自动恢复),验收与人工判断请按文档先预热。已知代价(仅 vite 8):同步门面使协商命中他方实例时本地副本 chunk 仍会被模块图拉取(双版本场景共享库网络副本 ≤2,运行时身份仍收敛单一实例)。
55
55
 
56
56
  ## 快速开始(React 应用)
57
57
 
@@ -508,6 +508,10 @@ const Panel = await loadRemote('shop/Panel', {
508
508
  (`runtimePlugins: ['./src/fulgurjsPlugin.ts']`)
509
509
 
510
510
  > hook 错误契约:`beforeLoadRemote` / `afterLoadRemote` 是**观测 hook**——自身抛错只告警、不改写加载结果;`resolveShare` 是**决策 hook**——显式抛错向调用方传播(绝不静默回退到另一份共享依赖)。
511
+ >
512
+ > **resolveShare 与消费路径(5.7.1 起)**:配置了 `runtimePlugins` 的 HTML 入口在执行应用前完成共享裁决与加载;远程容器也会在执行 expose 前完成异步裁决。同步门面复用同一消费条件的决策与实例,异步 hook 可以选择低版本或原表之外的条目,不会被本地副本覆盖。应用与 provider 之间保留动态导入边界,Vite 8 的消费方门面仍无 TLA,避免把协商等待传入消费方循环依赖。
513
+ >
514
+ > **入口边界**:没有 HTML 入口的 library/自定义入口,或应用运行后才调用 `registerPlugins` 更改策略,需要先 `await loadShare(name, opts)`,再动态导入新的消费者;已经求值的静态绑定无法追溯改写。未准备的同步消费者遇到异步 hook 仍给出 `MFU-004`(`details.syncUnsupported: true`),并接管其迟到拒绝,避免额外 `unhandledrejection`。`strictVersion` 冲突给出 MFU-003;本地接管的实例按真实版本登记,不能借用另一版本槽位绕过检查。需要同时使用 React 18/19 时,为整组 React、renderer 及其消费方设置独立 `shareScope`,通过桥接传普通 props/回调,不跨 renderer 传 ReactElement 或 Context。可运行示例见 [React 版本隔离与恢复](demo/react-versions/README.md)。
511
515
 
512
516
  ```ts
513
517
  import type { RuntimePlugin } from '@fulgurjs/federation/runtime'
@@ -79,8 +79,12 @@ function defineBridgeApp(factory, options = {}) {
79
79
  entriesByEl.set(el, entry);
80
80
  const promise = (async () => {
81
81
  const reactDomClient = await import("react-dom/client");
82
+ const createRoot = reactDomClient.createRoot ?? reactDomClient.default?.createRoot;
82
83
  if (entry.status === "abort" || entriesByEl.get(el) !== entry) return;
83
- const root = reactDomClient.createRoot(el);
84
+ if (typeof createRoot !== "function") throw new Error(
85
+ "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"
86
+ );
87
+ const root = createRoot(el);
84
88
  entry.root = root;
85
89
  entry.status = "live";
86
90
  const phaseRef = { current: "pending" };
@@ -45,8 +45,12 @@ function defineBridgeApp(factory, options = {}) {
45
45
  entriesByEl.set(el, entry);
46
46
  const promise = (async () => {
47
47
  const reactDomClient = await import("react-dom/client");
48
+ const createRoot = reactDomClient.createRoot ?? reactDomClient.default?.createRoot;
48
49
  if (entry.status === "abort" || entriesByEl.get(el) !== entry) return;
49
- const root = reactDomClient.createRoot(el);
50
+ if (typeof createRoot !== "function") throw new Error(
51
+ "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"
52
+ );
53
+ const root = createRoot(el);
50
54
  entry.root = root;
51
55
  entry.status = "live";
52
56
  const phaseRef = { current: "pending" };
@@ -546,9 +546,19 @@ function createRuntime() {
546
546
  if (byName && Object.keys(byName).length > 0) {
547
547
  try {
548
548
  const pick = selectShareEntry(name, opts);
549
- if (pick && pick.value === void 0 && pick.version === localVersion) {
550
- pick.value = instance;
551
- pick.loaded = true;
549
+ if (pick && pick.value === void 0) {
550
+ if (pick.version === localVersion) {
551
+ pick.value = instance;
552
+ pick.loaded = true;
553
+ } else if (opts.singleton && !pick.pendingGet) {
554
+ registerShare2(scopeName, key, localVersion, () => Promise.resolve(instance), {
555
+ from: "fulgurjs:local-fallback",
556
+ loaded: true
557
+ });
558
+ const local = scope[key][localVersion];
559
+ local.value = instance;
560
+ local.loaded = true;
561
+ }
552
562
  }
553
563
  } catch {
554
564
  }
@@ -565,7 +575,9 @@ function createRuntime() {
565
575
  const scopeName = opts.shareScope || "default";
566
576
  const shareKey = opts.shareKey || name;
567
577
  const byName = getScope(scopeName)[shareKey] || {};
568
- let entry = selectShareEntry(name, opts);
578
+ const requestKey = shareRequestKey(name, opts);
579
+ const snapshot = hooks.resolveShare ? resolvedShareSnapshots.get(requestKey) : void 0;
580
+ let entry = snapshot || selectShareEntry(name, opts);
569
581
  if (!entry) {
570
582
  if (opts.fallback) {
571
583
  const instance2 = await opts.fallback();
@@ -582,7 +594,7 @@ function createRuntime() {
582
594
  emitError({ remote: shareKey, error: err });
583
595
  throw err;
584
596
  }
585
- if (hooks.resolveShare) {
597
+ if (hooks.resolveShare && !snapshot) {
586
598
  const picked = await hooks.resolveShare({
587
599
  shareKey,
588
600
  shareScope: scopeName,
@@ -591,6 +603,7 @@ function createRuntime() {
591
603
  available: Object.values(byName)
592
604
  });
593
605
  if (picked) entry = picked;
606
+ resolvedShareSnapshots.set(requestKey, entry);
594
607
  }
595
608
  if (entry.value !== void 0) {
596
609
  resolvedShareSnapshots.set(shareRequestKey(name, opts), entry);
@@ -614,6 +627,7 @@ function createRuntime() {
614
627
  return entry.value;
615
628
  }
616
629
  entry.loaded = false;
630
+ if (resolvedShareSnapshots.get(requestKey) === entry) resolvedShareSnapshots.delete(requestKey);
617
631
  throw e;
618
632
  } finally {
619
633
  if (entry.pendingGet === pending) entry.pendingGet = void 0;
@@ -629,6 +643,92 @@ function createRuntime() {
629
643
  function pinLoadedShare2(name, opts, localVersion, instance) {
630
644
  registerFallbackInstance(name, { ...opts, localVersion }, instance);
631
645
  }
646
+ function loadShareSync2(name, opts) {
647
+ const scopeName = opts.shareScope || "default";
648
+ const shareKey = opts.shareKey || name;
649
+ if (hooks.resolveShare) {
650
+ const snapKey = shareRequestKey(name, opts);
651
+ const snap = resolvedShareSnapshots.get(snapKey);
652
+ if (snap && snap.value !== void 0) return { kind: "ready", value: snap.value };
653
+ const pickedRaw = snap || hooks.resolveShare({
654
+ shareKey,
655
+ shareScope: scopeName,
656
+ requiredVersion: opts.requiredVersion,
657
+ picked: selectShareEntry(name, opts),
658
+ available: Object.values(getScope(scopeName)[shareKey] || {})
659
+ });
660
+ if (pickedRaw && typeof pickedRaw.then === "function") {
661
+ void Promise.resolve(pickedRaw).catch(() => {
662
+ });
663
+ throw new FgError(
664
+ ErrorCodes.SHARE_NOT_AVAILABLE,
665
+ `\u73B0\u8C61\uFF1AresolveShare \u5BF9\u5171\u4EAB "${shareKey}" \u8FD4\u56DE\u4E86 Promise\uFF0C\u4F46\u8BE5\u5171\u4EAB\u6B63\u88AB\u540C\u6B65\u9759\u6001\u5BFC\u5165\u6D88\u8D39\uFF08Vite 8 \u540C\u6B65\u534F\u5546\u95E8\u9762\uFF09\u3002
666
+ \u539F\u56E0\uFF1A\u6A21\u5757\u6C42\u503C\u671F\u65E0\u6CD5\u7B49\u5F85\u5F02\u6B65\u51B3\u7B56\uFF1B\u9759\u6001\u5BFC\u5165\u4E0D\u80FD\u9759\u9ED8\u5FFD\u7565 hook \u51B3\u7B56\u3002
667
+ \u4FEE\u6CD5\uFF1AVite 8 HTML \u5165\u53E3\u4F1A\u5148\u5B8C\u6210\u5F02\u6B65\u534F\u5546\uFF1B\u5176\u4ED6\u5165\u53E3\u8BF7\u5148\u52A8\u6001 loadShare \u5EFA\u7ACB\u5FEB\u7167\uFF0C\u518D\u5BFC\u5165\u6D88\u8D39\u8005\uFF0C\u6216\u8BA9\u8BE5\u952E\u540C\u6B65\u8FD4\u56DE picked\u3002`,
668
+ { shareKey, requiredVersion: opts.requiredVersion, syncUnsupported: true }
669
+ );
670
+ }
671
+ const picked = pickedRaw;
672
+ if (picked) {
673
+ if (picked.value !== void 0) {
674
+ resolvedShareSnapshots.set(shareRequestKey(name, opts), picked);
675
+ return { kind: "ready", value: picked.value };
676
+ }
677
+ if (picked.version !== void 0 && picked.version === opts.localVersion) return { kind: "local" };
678
+ throw new FgError(
679
+ ErrorCodes.SHARE_NOT_AVAILABLE,
680
+ `\u73B0\u8C61\uFF1AresolveShare \u4E3A "${shareKey}" \u9009\u62E9\u4E86\u672A\u5C31\u7EEA\u7684 ${picked.version ?? "\u6761\u76EE"}\u3002
681
+ \u539F\u56E0\uFF1A\u540C\u6B65\u6D88\u8D39\u4E0D\u80FD\u7B49\u5F85\u52A0\u8F7D\u3002
682
+ \u4FEE\u6CD5\uFF1A\u5148\u52A8\u6001 loadShare \u518D\u5BFC\u5165\u6D88\u8D39\u8005\uFF0C\u6216\u9009\u62E9\u5DF2\u5C31\u7EEA\u5B9E\u4F8B/\u672C\u5730\u7248\u672C ${opts.localVersion ?? "\u672A\u58F0\u660E"}\u3002`,
683
+ { shareKey, requiredVersion: opts.requiredVersion, syncUnsupported: true }
684
+ );
685
+ }
686
+ }
687
+ const entry = selectShareEntry(name, opts);
688
+ if (!entry) return { kind: "local" };
689
+ if (entry.value !== void 0) return { kind: "ready", value: entry.value };
690
+ if (entry.version !== void 0 && entry.version === opts.localVersion) return { kind: "local" };
691
+ if (opts.singleton || opts.strictVersion && opts.requiredVersion && (!opts.localVersion || !satisfies(opts.localVersion, opts.requiredVersion))) {
692
+ if (opts.strictVersion || entry.pendingGet) {
693
+ throw new FgError(
694
+ ErrorCodes.SHARE_NOT_AVAILABLE,
695
+ `\u73B0\u8C61\uFF1A\u5171\u4EAB "${shareKey}" \u9009\u4E2D\u4E86 ${entry.from} \u63D0\u4F9B\u7684 ${entry.version}\uFF08\u5C1A\u672A\u52A0\u8F7D\uFF09\uFF0C\u540C\u6B65\u9759\u6001\u5BFC\u5165\u65E0\u6CD5\u7B49\u5F85\u5176\u5B9E\u4F8B\u5C31\u7EEA\u3002
696
+ \u539F\u56E0\uFF1A\u4E0D\u80FD\u6539\u7528\u4E0D\u517C\u5BB9\u7684\u672C\u5730\u526F\u672C\u6216\u5206\u88C2\u5355\u4F8B\u3002
697
+ \u4FEE\u6CD5\uFF1A\u8BA9\u8BE5\u5171\u4EAB\u7684\u9996\u6B21\u6D88\u8D39\u5148\u7ECF\u4E00\u6B21\u52A8\u6001 loadShare \u5B8C\u6210\u52A0\u8F7D\uFF0C\u6216\u5C06\u672C\u5E94\u7528\u7248\u672C\u4E0E\u4F5C\u7528\u57DF\u63D0\u4F9B\u7248\u672C\u5BF9\u9F50\u3002`,
698
+ { shareKey, requiredVersion: opts.requiredVersion, syncUnsupported: true }
699
+ );
700
+ }
701
+ const warnKey = `${scopeName}|${shareKey}|${entry.version}|${opts.localVersion}`;
702
+ if (!reportedSingletonSkews.has(warnKey)) {
703
+ reportedSingletonSkews.add(warnKey);
704
+ console.warn(
705
+ `[fulgurjs:MFU-010] \u5171\u4EAB\u5355\u4F8B "${shareKey}"\uFF1A\u4F5C\u7528\u57DF\u4E2D ${entry.from} \u63D0\u4F9B\u7684 ${entry.version} \u5C1A\u672A\u52A0\u8F7D\uFF0C\u9996\u4E2A\u6D88\u8D39\u8005\u4EE5\u672C\u5730\u7248\u672C ${opts.localVersion ?? ""} \u63A5\u7BA1\uFF08\u540E\u7EED\u6D88\u8D39\u8005\u6309\u5DF2\u52A0\u8F7D\u4F18\u5148\u6536\u655B\u5230\u8BE5\u5B9E\u4F8B\uFF09\u3002`
706
+ );
707
+ }
708
+ return { kind: "local" };
709
+ }
710
+ console.warn(
711
+ `[fulgurjs] \u5171\u4EAB "${shareKey}"\uFF1A\u975E\u5355\u4F8B\u540C\u6B65\u6D88\u8D39\u4F7F\u7528\u672C\u5730 ${opts.localVersion ?? ""}\uFF0C\u8FDC\u7A0B ${entry.version} \u5C1A\u672A\u5C31\u7EEA\u3002`
712
+ );
713
+ return { kind: "local" };
714
+ }
715
+ async function prepareShares2(requests) {
716
+ if (!hooks.resolveShare) return;
717
+ for (const { name, opts } of requests) {
718
+ const key = shareRequestKey(name, opts);
719
+ if (resolvedShareSnapshots.has(key)) continue;
720
+ const entry = selectShareEntry(name, opts);
721
+ const picked = await hooks.resolveShare({
722
+ shareKey: opts.shareKey || name,
723
+ shareScope: opts.shareScope || "default",
724
+ requiredVersion: opts.requiredVersion,
725
+ picked: entry,
726
+ available: Object.values(getScope(opts.shareScope || "default")[opts.shareKey || name] || {})
727
+ }) || entry;
728
+ if (picked) resolvedShareSnapshots.set(key, picked);
729
+ }
730
+ for (const { name, opts } of requests) await loadShare2(name, opts);
731
+ }
632
732
  function sanitizeUrl(u) {
633
733
  try {
634
734
  const x = new URL(u, typeof location < "u" ? location.href : "http://f.invalid");
@@ -1063,6 +1163,8 @@ ${hints.join("\n")}`,
1063
1163
  registerRemote: registerRemote2,
1064
1164
  registerPlugins: registerPlugins2,
1065
1165
  loadShare: loadShare2,
1166
+ loadShareSync: loadShareSync2,
1167
+ prepareShares: prepareShares2,
1066
1168
  getLoadedShare: getLoadedShare2,
1067
1169
  pinLoadedShare: pinLoadedShare2,
1068
1170
  loadRemote: loadRemote2,
@@ -1086,6 +1188,10 @@ var registerRemote = runtime.registerRemote;
1086
1188
  var registerPlugins = runtime.registerPlugins;
1087
1189
  var loadShare = runtime.loadShare;
1088
1190
  var getLoadedShare = runtime.getLoadedShare;
1191
+ var prepareShares = runtime.prepareShares || (async (requests) => {
1192
+ for (const { name, opts } of requests) await runtime.loadShare(name, opts);
1193
+ });
1194
+ var loadShareSync = runtime.loadShareSync;
1089
1195
  var pinLoadedShare = runtime.pinLoadedShare;
1090
1196
  var loadRemote = runtime.loadRemote;
1091
1197
  var getContainer = runtime.getContainer;
@@ -10,7 +10,7 @@ import {
10
10
  releaseBridgeSession,
11
11
  resolveBridgeContext,
12
12
  withBridgeTimeout
13
- } from "./chunk-CI54BRPO.js";
13
+ } from "./chunk-GLASM5EX.js";
14
14
 
15
15
  // src/bridge-host-vue.ts
16
16
  import { defineComponent, h, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, shallowRef, toRaw, watch } from "vue";
@@ -10,7 +10,7 @@ import {
10
10
  releaseBridgeSession,
11
11
  resolveBridgeContext,
12
12
  withBridgeTimeout
13
- } from "./chunk-CI54BRPO.js";
13
+ } from "./chunk-GLASM5EX.js";
14
14
 
15
15
  // src/bridge-host-react.ts
16
16
  import {