@fulgurjs/federation 0.8.4 → 0.9.1-test.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.0(2026-09-21)
4
+
5
+ ### 新增(发布可靠性 + IDE 边界 + 配置面)
6
+
7
+ - **`dts: { mode: 'source' | 'shim' }`**(默认 `source`,行为不变):`shim` 形态的类型声明不引用跨工程源文件(宽松占位),根治 VSCode/Volar 打开 `types/*.d.ts` 时的跨工程诊断红波浪线;取舍为无源码级补全/跳转(README §9.1.5)。
8
+ - **`fulgurjs.config.ts` 新增 `host.prefetch: 'all' | string[] | false`**(默认 `'all'`,行为不变):空闲预载名单成为正式配置面,init 生成 bridge.ts 时注入 `PREFETCH_REMOTES` 常量(README §9.1.3)。
9
+ - **`npm run typecheck:latest`**:用最新 TypeScript + vue-tsc 对 `tests/types-repro/` 典型消费形态做类型回归——根治"工程内旧 TS 绿、用户 IDE(新 TS)红"的盲区(0.8.2 的 EP locale ts2345 即由此暴露)。
10
+ - **CI 流水线**:`.github/workflows/ci.yml`(push/PR:单测 + 双口径 typecheck + build/gzip 门禁)与 `.github/workflows/publish.yml`(GitHub Release 触发 `npm publish --provenance`,Trusted Publishing 迁移见 docs/trusted-publishing-迁移清单.md)。
11
+
12
+ ### 变更
13
+
14
+ - **gzip 门禁真实化**:`scripts/check-gzip.mjs` 接入 build(阈值 ≤6144B,实测基线 5232B);`version.ts` 注释口径修正(原"≤5120 CI 守卫"与事实不符)。
15
+ - **发布物移除 `docs/manual.html`**(早期手册,内容停留在较早形态,避免双源漂移):本 README 为唯一权威文档;仓库内文件保留为历史存档。
16
+
17
+ ### 修复
18
+
19
+ - types-repro 样例集 + `buildShimModule`/`resolveDtsMode` 单测(单测 183 → 188)。
20
+
3
21
  ## 0.8.4(2026-09-21)
4
22
 
5
23
  ### 文档(README / 迁移指南全面清理历史沿革表述,只保留当前形态,零代码变化)
package/README.md CHANGED
@@ -147,7 +147,7 @@ npx fulgurjs doctor --base http://localhost:5173 --apps app-a --dev
147
147
 
148
148
  ## API 参考
149
149
 
150
- 以下覆盖插件的全部公开 API,签名与默认值与源码一致;完整语义细节与实测截图见 [`docs/manual.html`](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/manual.html)。
150
+ 以下覆盖插件的全部公开 API,签名与默认值与源码一致——**本 README 为唯一权威文档**(仓库内 `docs/manual.html` 为早期补充手册,部分内容基于较早形态,仅作参考)。
151
151
 
152
152
  ### 1. `federation(options)` — Vite 插件(宿主/远程同一份 API)
153
153
 
@@ -171,7 +171,7 @@ import { federation } from '@fulgurjs/federation'
171
171
  | `runtimeChunk` | `boolean \| 'single'` | — | 运行时是否拆独立 chunk |
172
172
  | `manifest` | `boolean` | `true` | prod 构建生成 `fulgurjs-manifest.json`(preloadRemote 依赖它) |
173
173
  | `runtimePlugins` | `string[]` | `[]` | 运行时插件模块路径列表(写法见「运行时插件」) |
174
- | `dts` | `boolean \| { dir?: string }` | `true` | dev 下拉取远程 manifest 生成类型声明——宿主写 `import X from 'remote-a/X'` 补全直达远程源码。**产物写入 `src/fulgurjs/types/`(联邦产物集中一个文件夹;无 src 布局回退 `.fulgurjs/types`)**,src 布局项目 tsconfig 零配置即生效;`{ dir }` 可自定义位置 |
174
+ | `dts` | `boolean \| { dir?: string; mode?: 'source' \| 'shim' }` | `true` | dev 下拉取远程 manifest 生成类型声明——宿主写 `import X from 'remote-a/X'` 获得类型。**产物写入 `src/fulgurjs/types/`(联邦产物集中一个文件夹;无 src 布局回退 `.fulgurjs/types`)**,src 布局项目 tsconfig 零配置即生效;`{ dir }` 自定义位置;`mode: 'source'`(默认)跨工程源码直连(补全/跳转直达远程源码,VSCode 打开生成物可能显示工程外文件诊断);`mode: 'shim'` 宽松占位(不引用源文件,IDE 全程干净,无源码级补全——见 §9.1.5) |
175
175
  | `devSharedSelf` | `boolean` | 纯远程 `true`;有 `remotes` 的宿主 `false` | dev 下自身源码(含依赖)是否参与 shared 协商改写。**双向联邦**(既 expose 又消费 remote)的宿主/远程需显式 `true`,否则 prod 双 vue 实例 |
176
176
  | `automaticAsyncBoundary` | — | 恒为 `true` | 接受任意值:TLA 自动异步边界,无需手工 bootstrap |
177
177
  | `dataPrefetch` | — | 恒为 `true` | 接受任意值:`preloadRemote` 始终可用 |
@@ -403,7 +403,7 @@ export default defineFulgurjsConfig({
403
403
  | CC 跨应用上下文 | `CC-001` | AppContext 必需字段缺失(三段式:got/expected/example,修法指向宿主桥 `provideFulgurjsAppContext`) |
404
404
  | | `CC-002` | 运行时单例不可用(独立直开远程页;修法 = 经宿主联邦加载,时序契约 bridge → federatedBoot → loadRemote) |
405
405
 
406
- 每个码的完整排查文案见 [`docs/manual.html`](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/manual.html) §8;`fulgurjs doctor` 可提前把部署面的 MFU-001 类问题拦在上线前。
406
+ 错误排查三段式文案与「配置出错?报错看得懂」一节(下文)为准;`fulgurjs doctor` 可提前把部署面的 MFU-001 类问题拦在上线前。
407
407
 
408
408
  ### 7. 产物与端点约定
409
409
 
@@ -414,7 +414,7 @@ export default defineFulgurjsConfig({
414
414
  | prod | `/<base>/fulgurjs-remoteEntry.js` | 固定文件名容器入口(内容每次构建变——**必须 no-cache**) |
415
415
  | prod | `/<base>/fulgurjs-manifest.json` | expose chunk/CSS 清单(preloadRemote 消费,**no-cache**) |
416
416
 
417
- NGINX 部署模板(no-cache 规则 + 深链回退)用 `fulgurjs init --config` 自动生成,样例见 [`docs/manual.html`](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/manual.html)。
417
+ NGINX 部署模板(no-cache 规则 + 深链回退)用 `fulgurjs init --config` 自动生成。
418
418
 
419
419
  ### 8. `remoteComponent` — Vue 远程组件直渲染(`@fulgurjs/federation/vue`)
420
420
 
@@ -578,13 +578,17 @@ const res = await getDictItems('sex')
578
578
 
579
579
  | 属性 | 类型 | 默认值 | 说明 |
580
580
  |---|---|---|---|
581
- | `PREFETCH_REMOTES` | `string[]` | 全部 remotes 键 | 参与空闲预载的 remote 名单(与 vite.config `remotes` 键一致) |
581
+ | `host.prefetch`(fulgurjs.config.ts,集成器项目) | `'all' \| string[] \| false` | `'all'` | `'all'` = 预载全部 remotes 键;`string[]` = 指定名单;`false` = 关闭。init 生成 bridge.ts 时注入 |
582
+ | `PREFETCH_REMOTES`(手工项目,bridge.ts 顶部常量) | `string[]` | 全部 remotes 键 | 与 vite.config `remotes` 键一致 |
582
583
 
583
584
  ```ts
584
- // src/fulgurjs/host/bridge.ts 顶部
585
+ // 集成器项目:fulgurjs.config.ts → apps[].host.prefetch(改后删已生成 bridge.ts 重跑 fulgurjs init)
586
+ prefetch: 'all' // 默认:全部 remote
587
+ prefetch: ['mes-bpm'] // 部分 remote
588
+ prefetch: false // 关闭
585
589
 
586
- // 默认:预载全部 remote(init 集成器按 fulgurjs.config.ts 的 remotes 自动生成)
587
- const PREFETCH_REMOTES: string[] = ['mes-bpm', 'mes-lowcode']
590
+ // 手工项目:src/fulgurjs/host/bridge.ts 顶部常量
591
+ const PREFETCH_REMOTES: string[] = ['mes-bpm', 'mes-lowcode'] // 默认(init 生成形态)
588
592
 
589
593
  // 只预载部分 remote
590
594
  const PREFETCH_REMOTES: string[] = ['mes-bpm']
@@ -616,6 +620,7 @@ const PREFETCH_REMOTES: string[] = []
616
620
 
617
621
  - `types/` 下的 `*.d.ts` 是**插件每次 dev 自动生成**的类型直连声明(勿手改):内部 `export * from '../../../cku-mes-xxx/src/***.vue'` 指向**兄弟工程的源码**。命令行 `vue-tsc --noEmit`(走本应用 tsconfig,skipLibCheck 生效)为 **0 错误**;但 **VSCode/Volar 在打开这些 d.ts 时**可能把工程外 .vue 用推断项目(inferred project,无 tsconfig 上下文)展开检查,显示大片"找不到模块 '@/...'"——**仅编辑器显示问题,不影响命令行检查与构建**,不打开 `types/` 生成物即无感。
618
622
  - 升级插件版本后若 `import '@fulgurjs/federation/context'` 报 ts(2307):是 IDE 的 TS 服务缓存了旧包——`Restart TS Server`(⌘⇧P)或重开窗口即可。
623
+ - **根治红波浪线**:`federation({ dts: { mode: 'shim' } })` —— 生成物不再引用跨工程源文件(宽松占位形态),IDE 全程干净;取舍是失去"跳转直达远程源码"的补全能力(默认 `source` 不变,按项目偏好选择)。
619
624
 
620
625
  ## ⚠️ 首次使用避坑指南(真实迁移项目踩坑实录)
621
626
 
@@ -700,7 +705,7 @@ const Panel = await loadRemote('shop/Panel', {
700
705
 
701
706
  ## 文档
702
707
 
703
- - [`docs/manual.html`](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/manual.html) — 完整使用手册:webpack 逐项对齐总表、每个功能的配置代码 + dev/prod 实测截图、错误码排查、NGINX 部署样例
708
+ - [`docs/manual.html`](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/manual.html) — 早期使用手册(历史存档,不再随 npm 包发布):webpack 逐项对齐总表与部分实测截图;**当前形态以本 README 为准**
704
709
  - [`docs/迁移指南.md`](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/迁移指南.md) — qiankun 微前端 → 联邦的真实迁移案例(七步法 + 验收清单)
705
710
  - [`docs/webpack-mf-对照与缺口.md`](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/webpack-mf-对照与缺口.md) — webpack MF 逐项对照与明确不支持清单
706
711
  - [`docs/沙箱边界审计.md`](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/沙箱边界审计.md) — CSS / 全局变量 / 公共依赖三维度互扰实测
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var RUNTIME_VERSION = "0.8.4";
2
+ var RUNTIME_VERSION = "0.9.1-test.0";
3
3
 
4
4
  export {
5
5
  RUNTIME_VERSION
package/dist/index.cjs CHANGED
@@ -43,7 +43,7 @@ var import_node_fs = __toESM(require("fs"), 1);
43
43
  var import_node_path = __toESM(require("path"), 1);
44
44
 
45
45
  // src/version.ts
46
- var RUNTIME_VERSION = "0.8.4";
46
+ var RUNTIME_VERSION = "0.9.1-test.0";
47
47
 
48
48
  // src/options.ts
49
49
  var DEFAULT_FILENAME = "fulgurjs-remoteEntry.js";
@@ -1093,9 +1093,36 @@ function resolveDtsDir(dtsOpt, rootHasSrc) {
1093
1093
  if (dtsOpt === false) return "";
1094
1094
  return (typeof dtsOpt === "object" ? dtsOpt.dir : void 0) ?? (rootHasSrc ? "src/fulgurjs/types" : ".fulgurjs/types");
1095
1095
  }
1096
+ function resolveDtsMode(dtsOpt) {
1097
+ if (dtsOpt === false) return "source";
1098
+ return (typeof dtsOpt === "object" ? dtsOpt.mode : void 0) ?? "source";
1099
+ }
1100
+ function buildShimModule(abs, moduleSpecifier) {
1101
+ const lines = [`declare module '${moduleSpecifier}' {`];
1102
+ if (abs.endsWith(".vue")) {
1103
+ lines.push(` import type { DefineComponent } from 'vue'`);
1104
+ lines.push(` const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, any>`);
1105
+ lines.push(` export default component`);
1106
+ } else {
1107
+ const text = import_node_fs2.default.readFileSync(abs, "utf8");
1108
+ for (const name of extractTsExportNames(text)) {
1109
+ lines.push(` export const ${name}: any`);
1110
+ }
1111
+ if (sourceHasDefaultExport(abs)) {
1112
+ lines.push(` const _default: any`);
1113
+ lines.push(` export default _default`);
1114
+ }
1115
+ if (!import_node_fs2.default.existsSync(abs)) {
1116
+ lines.push(` export const __module: any`);
1117
+ }
1118
+ }
1119
+ lines.push(`}`);
1120
+ return lines.join("\n");
1121
+ }
1096
1122
  async function generateDevTypes(options, _server) {
1097
1123
  const dtsOpt = options.dts === void 0 ? true : options.dts;
1098
1124
  if (dtsOpt === false) return;
1125
+ const mode = resolveDtsMode(dtsOpt);
1099
1126
  const dir = resolveDtsDir(dtsOpt, import_node_fs2.default.existsSync(import_node_path3.default.join(options.root, "src")));
1100
1127
  const outDir = import_node_path3.default.join(options.root, dir);
1101
1128
  import_node_fs2.default.mkdirSync(outDir, { recursive: true });
@@ -1115,7 +1142,7 @@ async function generateDevTypes(options, _server) {
1115
1142
  continue;
1116
1143
  }
1117
1144
  const lines = [
1118
- `// \u81EA\u52A8\u751F\u6210\uFF1Afulgurjs-federation dev \u7C7B\u578B\u76F4\u8FDE\uFF08remote: ${remote.name}\uFF09`,
1145
+ `// \u81EA\u52A8\u751F\u6210\uFF1Afulgurjs-federation dev \u7C7B\u578B\u76F4\u8FDE\uFF08remote: ${remote.name}\uFF0Cmode: ${mode}\uFF09`,
1119
1146
  `// \u91CD\u65B0\u751F\u6210\uFF1A\u91CD\u542F host dev server`
1120
1147
  ];
1121
1148
  for (const expose of manifest.exposes ?? []) {
@@ -1125,6 +1152,10 @@ async function generateDevTypes(options, _server) {
1125
1152
  const importPath = abs.endsWith(".vue") ? sourceImportPath(rel) : stripTsExtension(sourceImportPath(rel));
1126
1153
  const moduleSpecifier = `${remote.key}/${expose.name.replace(/^\.\//, "")}`;
1127
1154
  lines.push("");
1155
+ if (mode === "shim") {
1156
+ lines.push(buildShimModule(abs, moduleSpecifier));
1157
+ continue;
1158
+ }
1128
1159
  if (abs.endsWith(".vue")) {
1129
1160
  lines.push(`declare module '${moduleSpecifier}' {`);
1130
1161
  lines.push(` import type { DefineComponent } from 'vue'`);
@@ -1147,7 +1178,7 @@ async function generateDevTypes(options, _server) {
1147
1178
  import_node_fs2.default.writeFileSync(import_node_path3.default.join(outDir, `${remote.key}.d.ts`), `${lines.join("\n")}
1148
1179
  `);
1149
1180
  console.log(
1150
- `[fulgurjs] dts: generated ${dir}/${remote.key}.d.ts (${manifest.exposes?.length ?? 0} exposes). \u786E\u4FDD tsconfig include \u5305\u542B "${dir}" \u4EE5\u83B7\u5F97\u6E90\u7801\u7EA7\u7C7B\u578B\u8865\u5168\u3002`
1181
+ `[fulgurjs] dts: generated ${dir}/${remote.key}.d.ts (${manifest.exposes?.length ?? 0} exposes, mode=${mode}). \u786E\u4FDD tsconfig include \u5305\u542B "${dir}" \u4EE5\u83B7\u5F97\u7C7B\u578B\u8865\u5168\u3002`
1151
1182
  );
1152
1183
  }
1153
1184
  }
@@ -1274,7 +1305,7 @@ function syncViteCacheMarker(root, pluginVersion, log) {
1274
1305
  }
1275
1306
 
1276
1307
  // src/runtime-code.gen.ts
1277
- var runtime_code_gen_default = 'var Q=/^v?(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z.-]+)?$/;function O(t){let n=Q.exec(String(t).trim());return n?{major:Number(n[1]),minor:Number(n[2]??0),patch:Number(n[3]??0),pre:n[4]?n[4].split("."):[]}:null}function ee(t,n){if(t.length===0&&n.length===0)return 0;if(t.length===0)return 1;if(n.length===0)return-1;let o=Math.max(t.length,n.length);for(let r=0;r<o;r++){let l=t[r],m=n[r];if(l===void 0)return-1;if(m===void 0)return 1;let y=/^\\d+$/.test(l),w=/^\\d+$/.test(m);if(y&&w){let $=Number(l)-Number(m);if($!==0)return $}else{if(y!==w)return y?-1:1;if(l!==m)return l<m?-1:1}}return 0}function v(t,n){let o=O(t),r=O(n);return!o||!r?t<n?-1:t>n?1:0:o.major!==r.major?o.major-r.major:o.minor!==r.minor?o.minor-r.minor:o.patch!==r.patch?o.patch-r.patch:ee(o.pre,r.pre)}var re=/^[a-z]+[+:-]/i;function k(t,n){if(n===!1||n==null||n==="")return!0;let o=String(n).trim();return!o||o==="*"||o==="latest"||re.test(o)?!0:o.split("||").some(r=>te(t,r.trim()))}function te(t,n){if(!n||n==="*")return!O(t)?.pre.length;let o=n.split(/\\s+-\\s+/);if(o.length===2){let m=o[0].trim(),y=o[1].trim();return!(!k(t,`>=${m}`)||!k(t,`<=${y}`))}let r=O(t);if(!r)return!1;let l=ne(n);if(l===null)return!0;for(let m of l)if(!ue(r,t,m))return!1;return!(r.pre.length>0&&!l.some(m=>m.ver.pre.length>0)&&!l.some(y=>y.ver.major===r.major&&y.ver.minor===r.minor&&y.ver.patch===r.patch))}function ne(t){let n=[],o=t.trim().split(/\\s+/).filter(Boolean);if(o.length===0)return null;for(let r of o){let l=/^(>=|<=|>|<|=|\\^|~)?(.*)$/.exec(r);if(!l)return null;let m=l[1],y=l[2],w=oe(m,y);if(w===null)return null;n.push(...w)}return n}function oe(t,n){let o=ae(n);if(!o)return null;let{ver:r,partial:l}=o;switch(t){case"^":{let m=ie(r);return m?[{op:">=",ver:r},{op:"<",ver:m}]:null}case"~":{let m=l>=2?{major:r.major,minor:r.minor+1,patch:0,pre:[]}:{major:r.major+1,minor:0,patch:0,pre:[]};return[{op:">=",ver:r},{op:"<",ver:m}]}case">=":return[{op:">=",ver:r}];case">":return l===1?[{op:">=",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:">",ver:r}];case"<":return l===1?[{op:"<",ver:{major:r.major,minor:0,patch:0,pre:[]}}]:l===2?[{op:"<",ver:{major:r.major,minor:r.minor,patch:0,pre:[]}}]:[{op:"<",ver:r}];case"<=":return[{op:"<=",ver:r}];case"=":case void 0:return l===1?[{op:">=",ver:r},{op:"<",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:r},{op:"<",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:"=",ver:r}];default:return null}}function ie(t){return t.major>0?{major:t.major+1,minor:0,patch:0,pre:[]}:t.minor>0?{major:0,minor:t.minor+1,patch:0,pre:[]}:{major:0,minor:0,patch:t.patch+1,pre:[]}}function ae(t){let n=t.replace(/^v/,"").trim();if(!n||n==="*"||n==="x"||n==="X")return{ver:{major:0,minor:0,patch:0,pre:[]},partial:0};let o=n.split("."),r=[],l=0;for(let w of o.slice(0,3))if(/^\\d+$/.test(w))r.push(Number(w)),l++;else{if(w==="x"||w==="X"||w==="*")break;break}let m=n.split("-"),y=m.length>1?m.slice(1).join("-").split("+")[0].split("."):[];return l===0&&y.length===0?{ver:{major:0,minor:0,patch:0,pre:[]},partial:0}:{ver:{major:r[0]??0,minor:r[1]??0,patch:r[2]??0,pre:y},partial:l}}function se(t,n){switch(n){case">=":return t>=0;case">":return t>0;case"<":return t<0;case"<=":return t<=0;case"=":return t===0}}function ue(t,n,o){let r=`${t.major}.${t.minor}.${t.patch}`+(t.pre.length?`-${t.pre.join(".")}`:"");if(o.upper)return!(!(v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:""))>=0)||!(v(r,`${o.upper.major}.${o.upper.minor}.${o.upper.patch}`)<0));let l=v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:"")),m=se(l,o.op);return o.ver.pre.length>0&&t.pre.length===0&&(o.op===">"||o.op==="<")?!1:m}var g=class extends Error{code;details;constructor(n,o,r){super(`[fulgurjs:${n}] ${o}`),this.name="FulgurjsError",this.code=n,this.details=r}},R={REMOTE_LOAD_FAILED:"MFU-001",REMOTE_NAME_MISMATCH:"MFU-002",SHARE_STRICT_VERSION:"MFU-003",SHARE_NOT_AVAILABLE:"MFU-004",CONTAINER_REINIT_CONFLICT:"MFU-005",MODULE_NOT_EXPOSED:"MFU-006",PRELOAD_FAILED:"MFU-007",REMOTE_UNKNOWN:"MFU-008",EMPTY_EXPORTS:"MFU-009",SINGLETON_SKEW:"MFU-010"};var F="0.8.4";var U=15e3,P=2,ce=5,le=3e4;function fe(){let t={},n=new Map,o=[],r=new Map,l=new WeakMap,m={};function y(e){m={...m,...e},typeof globalThis<"u"&&(globalThis.__FULGURJS_APP_CONFIG__=m)}function w(){return m}let $={},D=()=>{for(let e of o)try{e.init?.($)}catch(i){console.warn("[fulgurjs] runtimePlugin init failed:",e.name,i)}},N=e=>t[e]??={};function j(e){try{$.onRemoteError?.(e)}catch{}typeof window<"u"&&window.dispatchEvent(new CustomEvent("fulgurjs:error",{detail:e}))}function H(e="default"){return N(e),t}function K(e,i,a,u,E={}){let s=N(e||"default"),c=s[i]??={};c[a]||(c[a]={version:a,get:u,from:E.from??"unknown",eager:!!E.eager,loaded:!!E.loaded})}function x(e){for(let i of e){let a=n.get(i.name);n.set(i.name,{timeout:U,retries:P,...a,...i,state:a?.state??"idle",breakerState:a?.breakerState??{fails:0,openUntil:0},debug:a?.debug??{entry:i.entry,status:"idle"}})}}function q(e){x([e])}function G(e){o.push(...e),D()}async function z(e,i={}){let a=i.shareScope||"default",u=i.shareKey||e,s=N(a)[u]||{},c=Object.keys(s),d=i.requiredVersion,f=c.filter(p=>d===void 0||d===!1||k(p,d)),b;if(i.singleton&&c.length>0){let p=c.filter(h=>s[h].loaded);if(b=[...p.length?p:c].sort(v).pop(),c.length>1||!f.includes(b)){let h=s[b],T=`singleton skew "${u}": req "${d??"any"}" use ${b} from ${h.from}`;if(i.strictVersion){let M=new g(R.SHARE_STRICT_VERSION,T,{shareKey:u,requiredVersion:d});throw j({remote:h.from,error:M}),M}console.warn(`[fulgurjs:MFU-010] ${T}`)}}else if(b=[...f].sort(v).pop(),!b){if(i.strictVersion){let h=new g(R.SHARE_STRICT_VERSION,`no satisfying version for "${u}" (required "${d}") in share scope "${a}"`);throw j({remote:u,error:h}),h}if(i.fallback)return i.fallback();let p=new g(R.SHARE_NOT_AVAILABLE,`shared "${u}" (${d??"any"}) unavailable in scope "${a}" (no local fallback)`,{shareKey:u,requiredVersion:d});throw j({remote:u,error:p}),p}let _=s[b];if($.resolveShare){let p=await $.resolveShare({shareKey:u,shareScope:a,requiredVersion:d,picked:_,available:Object.values(s)});p&&(_=p)}return _.loaded=!0,_.get()}function J(e,i,a){return new Promise((u,E)=>{let s=setTimeout(()=>E(new Error(`timeout after ${i}ms: ${a}`)),i);e.then(c=>{clearTimeout(s),u(c)},c=>{clearTimeout(s),E(c)})})}let W=e=>new Promise(i=>setTimeout(i,e));function I(e,i){if(!e||typeof e.get!="function")throw new Error(`remoteEntry of "${i.name}" does not export a container interface (get/init)`);if(e.name&&i.name&&e.name!==i.name){let a=`remoteEntry self-reported name "${e.name}" mismatches configured name "${i.name}"`;if(i.promise)console.warn(`[fulgurjs] ${a} (promise-based remote, continuing)`);else throw Object.assign(new Error(a),{code:R.REMOTE_NAME_MISMATCH})}}async function L(e,i){return J(import(e),i.timeout??U,`load remoteEntry ${e}`)}async function C(e,i){let a=e.shareScope||"default";if(e.container){let s=l.get(e.container);if(s&&s!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${s}", cannot re-init with "${a}"`);return e.container}if(e.containerPromise)return e.containerPromise;let u=e.breakerState;if(u.openUntil>Date.now()){let s=new g(R.REMOTE_LOAD_FAILED,`circuit breaker open for remote "${e.name}" until ${new Date(u.openUntil).toISOString()}`,{remote:e.name});throw j({remote:e.name,error:s}),s}e.state="loading",e.debug.status="loading";let E=(async()=>{let s=[];if(e.promise)s.push(async()=>{let p=await e.promise(),h=typeof p=="string"?await L(p,e):p;return I(h,e),h});else for(let p of[e.entry,...e.fallback??[]])s.push(async()=>{let h=await L(p,e);return I(h,e),h});let c,d,f=!1,b=i?.retries??e.retries??P;for(let p of s){for(let h=0;h<=b;h++){let T=performance.now();try{c=await p(),e.lastLoadMs=Math.round(performance.now()-T),e.debug.lastLoadMs=e.lastLoadMs,f=!0;break}catch(M){if(d=M,M?.code)throw M;h<b&&await W(200*2**h)}}if(f)break}if(!f||!c){let p=[];throw/^https?:\\/\\//.test(e.entry)&&typeof location<"u"?(p.push(`1. is the remote dev server running? try opening ${e.entry} in the browser \\u2014 it must return JS, not HTML/an error`),p.push("2. is the URL correct in federation({ remotes })? dev and prod entries can differ"),p.push("3. CORS: the remote dev server must allow cross-origin requests (fulgurjs-federation enables server.cors automatically; a custom server config may have disabled it)")):(p.push(`1. is the remote deployed and reachable? try opening ${e.entry} in the browser \\u2014 it must return JS`),p.push("2. NGINX/CDN routing: the entry path must serve the remoteEntry JS file (check try_files / fallback rules)")),new g(R.REMOTE_LOAD_FAILED,`failed to load remote "${e.name}" from ${e.entry}: ${String(d?.message??d)}\n`+p.join(`\n`),{remote:e.name})}I(c,e);let _=l.get(c);if(_&&_!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${_}", cannot re-init with "${a}"`);return typeof c.init=="function"&&await c.init(t),l.set(c,a),e.container=c,e.state="loaded",e.debug.status="loaded",u.fails=0,c})();e.containerPromise=E;try{return await E}catch(s){if(e.containerPromise=void 0,e.state="failed",e.debug.status="failed",e.debug.error=String(s?.message??s),u.fails+=1,u.fails>=ce&&(u.openUntil=Date.now()+le,u.fails=0),s instanceof g)throw j({remote:e.name,error:s}),s;if(s?.code)throw j({remote:e.name,error:s}),s;let c=new g(R.REMOTE_LOAD_FAILED,String(s?.message??s),{remote:e.name});throw j({remote:e.name,error:c}),c}}function A(e){let i=e.indexOf("/");return i===-1?{remote:e,module:""}:{remote:e.slice(0,i),module:B(e.slice(i+1))}}function B(e){return e?e.startsWith(".")?e:`./${e}`:""}async function X(e,i){let{remote:a,module:u}=A(e);$.beforeLoadRemote?.({remote:a,module:u});let E=n.get(a);if(!E)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}". Register it via federation({ remotes }) or registerRemote().`,{remote:a});let s;try{s=await C(E,{retries:i?.retries})}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\\u663E\\u5F0F\\u964D\\u7EA7\\uFF0C\\u9519\\u8BEF\\u5DF2\\u900F\\u51FA)`,f),j({remote:a,error:f}),await i.fallbackModule();throw f}if(!u)return s;let c=`${a}@${E.shareScope||"default"}#${u}`;r.has(c)||r.set(c,s.get(u).catch(f=>{throw r.delete(c),f instanceof g||f?.code?f:new g(R.REMOTE_LOAD_FAILED,`failed to load module "${u}" from remote "${a}": ${String(f?.message??f)}`,{remote:a,module:u})}));let d;try{d=await r.get(c)}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\\u663E\\u5F0F\\u964D\\u7EA7\\uFF0C\\u9519\\u8BEF\\u5DF2\\u900F\\u51FA)`,f),j({remote:a,error:f}),await i.fallbackModule();throw f}if($.afterLoadRemote?.({remote:a,module:u,module_ns:d}),d&&typeof d=="object"&&Object.keys(d).length===0){let f=new g(R.EMPTY_EXPORTS,`no exports: ${u} @ ${a}`,{remote:a,module:u});j({remote:a,error:f}),console.error(`[fulgurjs:MFU-009] ${f.message}`)}return d}function Y(e){let i=n.get(e);if(!i)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${e}"`,{remote:e});return C(i)}async function Z(e,i={}){let{remote:a}=A(e),u=n.get(a);if(!u)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}"`,{remote:a});let E=i.mode??"preload",s=(c,d)=>{if(typeof document>"u"||document.querySelector(`link[href="${c}"]`))return;let f=document.createElement("link");f.rel=d==="style"?"stylesheet":"modulepreload",f.href=c,E==="prefetch"&&(f.fetchPriority="low"),document.head.appendChild(f)};try{if(u.manifestUrl){let c=await fetch(u.manifestUrl,{cache:"no-cache"});if(c.ok){let d=await c.json();s(u.entry,"modulepreload");let f=d.exposes??{};for(let b of Object.keys(f)){let _=f[b];_?.file&&s(new URL(_.file,u.entry).href,"modulepreload");for(let p of _?.css??[])s(new URL(p,u.entry).href,"style")}return}}s(u.entry,"modulepreload")}catch(c){j({remote:a,error:new g(R.PRELOAD_FAILED,String(c?.message??c),{remote:a})})}}return typeof window>"u"||(window.__FULGURJS_SCOPE__=t,window.__FULGURJS_INFO__={get remotes(){let e={};for(let[i,a]of n)e[i]={...a.debug,entry:a.entry};return e},errors:[]}),{shareScopeMap:t,initSharing:H,registerShare:K,registerRemotes:x,registerRemote:q,registerPlugins:G,loadShare:z,loadRemote:X,getContainer:Y,preloadRemote:Z,parseSpec:A,provideFulgurjsAppConfig:y,getFulgurjsAppConfig:w}}var V=globalThis,S=V.__FULGURJS_RUNTIME__??fe();V.__FULGURJS_RUNTIME__=S;try{Object.freeze(S)}catch{}var Re=F;function Se(){return S}var we=S.initSharing,be=S.registerShare,_e=S.registerRemotes,je=S.registerRemote,$e=S.registerPlugins,Me=S.loadShare,ve=S.loadRemote,Te=S.getContainer,Oe=S.preloadRemote,ke=S.shareScopeMap,Ne=S.provideFulgurjsAppConfig,Ie=S.getFulgurjsAppConfig;function Ae(t){if(t&&typeof t=="object"&&"default"in t){let n=t.default;return n!==void 0?n:t}return t}var xe=S;export{xe as default,Te as getContainer,Ie as getFulgurjsAppConfig,Se as getRuntime,we as initSharing,ve as loadRemote,Me as loadShare,Oe as preloadRemote,Ne as provideFulgurjsAppConfig,$e as registerPlugins,je as registerRemote,_e as registerRemotes,be as registerShare,S as runtime,ke as shareScopeMap,Ae as unwrapDefault,Re as version};\n';
1308
+ var runtime_code_gen_default = 'var Q=/^v?(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z.-]+)?$/;function O(t){let n=Q.exec(String(t).trim());return n?{major:Number(n[1]),minor:Number(n[2]??0),patch:Number(n[3]??0),pre:n[4]?n[4].split("."):[]}:null}function ee(t,n){if(t.length===0&&n.length===0)return 0;if(t.length===0)return 1;if(n.length===0)return-1;let o=Math.max(t.length,n.length);for(let r=0;r<o;r++){let l=t[r],m=n[r];if(l===void 0)return-1;if(m===void 0)return 1;let y=/^\\d+$/.test(l),w=/^\\d+$/.test(m);if(y&&w){let $=Number(l)-Number(m);if($!==0)return $}else{if(y!==w)return y?-1:1;if(l!==m)return l<m?-1:1}}return 0}function v(t,n){let o=O(t),r=O(n);return!o||!r?t<n?-1:t>n?1:0:o.major!==r.major?o.major-r.major:o.minor!==r.minor?o.minor-r.minor:o.patch!==r.patch?o.patch-r.patch:ee(o.pre,r.pre)}var re=/^[a-z]+[+:-]/i;function k(t,n){if(n===!1||n==null||n==="")return!0;let o=String(n).trim();return!o||o==="*"||o==="latest"||re.test(o)?!0:o.split("||").some(r=>te(t,r.trim()))}function te(t,n){if(!n||n==="*")return!O(t)?.pre.length;let o=n.split(/\\s+-\\s+/);if(o.length===2){let m=o[0].trim(),y=o[1].trim();return!(!k(t,`>=${m}`)||!k(t,`<=${y}`))}let r=O(t);if(!r)return!1;let l=ne(n);if(l===null)return!0;for(let m of l)if(!ue(r,t,m))return!1;return!(r.pre.length>0&&!l.some(m=>m.ver.pre.length>0)&&!l.some(y=>y.ver.major===r.major&&y.ver.minor===r.minor&&y.ver.patch===r.patch))}function ne(t){let n=[],o=t.trim().split(/\\s+/).filter(Boolean);if(o.length===0)return null;for(let r of o){let l=/^(>=|<=|>|<|=|\\^|~)?(.*)$/.exec(r);if(!l)return null;let m=l[1],y=l[2],w=oe(m,y);if(w===null)return null;n.push(...w)}return n}function oe(t,n){let o=ae(n);if(!o)return null;let{ver:r,partial:l}=o;switch(t){case"^":{let m=ie(r);return m?[{op:">=",ver:r},{op:"<",ver:m}]:null}case"~":{let m=l>=2?{major:r.major,minor:r.minor+1,patch:0,pre:[]}:{major:r.major+1,minor:0,patch:0,pre:[]};return[{op:">=",ver:r},{op:"<",ver:m}]}case">=":return[{op:">=",ver:r}];case">":return l===1?[{op:">=",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:">",ver:r}];case"<":return l===1?[{op:"<",ver:{major:r.major,minor:0,patch:0,pre:[]}}]:l===2?[{op:"<",ver:{major:r.major,minor:r.minor,patch:0,pre:[]}}]:[{op:"<",ver:r}];case"<=":return[{op:"<=",ver:r}];case"=":case void 0:return l===1?[{op:">=",ver:r},{op:"<",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:r},{op:"<",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:"=",ver:r}];default:return null}}function ie(t){return t.major>0?{major:t.major+1,minor:0,patch:0,pre:[]}:t.minor>0?{major:0,minor:t.minor+1,patch:0,pre:[]}:{major:0,minor:0,patch:t.patch+1,pre:[]}}function ae(t){let n=t.replace(/^v/,"").trim();if(!n||n==="*"||n==="x"||n==="X")return{ver:{major:0,minor:0,patch:0,pre:[]},partial:0};let o=n.split("."),r=[],l=0;for(let w of o.slice(0,3))if(/^\\d+$/.test(w))r.push(Number(w)),l++;else{if(w==="x"||w==="X"||w==="*")break;break}let m=n.split("-"),y=m.length>1?m.slice(1).join("-").split("+")[0].split("."):[];return l===0&&y.length===0?{ver:{major:0,minor:0,patch:0,pre:[]},partial:0}:{ver:{major:r[0]??0,minor:r[1]??0,patch:r[2]??0,pre:y},partial:l}}function se(t,n){switch(n){case">=":return t>=0;case">":return t>0;case"<":return t<0;case"<=":return t<=0;case"=":return t===0}}function ue(t,n,o){let r=`${t.major}.${t.minor}.${t.patch}`+(t.pre.length?`-${t.pre.join(".")}`:"");if(o.upper)return!(!(v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:""))>=0)||!(v(r,`${o.upper.major}.${o.upper.minor}.${o.upper.patch}`)<0));let l=v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:"")),m=se(l,o.op);return o.ver.pre.length>0&&t.pre.length===0&&(o.op===">"||o.op==="<")?!1:m}var g=class extends Error{code;details;constructor(n,o,r){super(`[fulgurjs:${n}] ${o}`),this.name="FulgurjsError",this.code=n,this.details=r}},R={REMOTE_LOAD_FAILED:"MFU-001",REMOTE_NAME_MISMATCH:"MFU-002",SHARE_STRICT_VERSION:"MFU-003",SHARE_NOT_AVAILABLE:"MFU-004",CONTAINER_REINIT_CONFLICT:"MFU-005",MODULE_NOT_EXPOSED:"MFU-006",PRELOAD_FAILED:"MFU-007",REMOTE_UNKNOWN:"MFU-008",EMPTY_EXPORTS:"MFU-009",SINGLETON_SKEW:"MFU-010"};var F="0.9.1-test.0";var U=15e3,P=2,ce=5,le=3e4;function fe(){let t={},n=new Map,o=[],r=new Map,l=new WeakMap,m={};function y(e){m={...m,...e},typeof globalThis<"u"&&(globalThis.__FULGURJS_APP_CONFIG__=m)}function w(){return m}let $={},D=()=>{for(let e of o)try{e.init?.($)}catch(i){console.warn("[fulgurjs] runtimePlugin init failed:",e.name,i)}},N=e=>t[e]??={};function j(e){try{$.onRemoteError?.(e)}catch{}typeof window<"u"&&window.dispatchEvent(new CustomEvent("fulgurjs:error",{detail:e}))}function H(e="default"){return N(e),t}function K(e,i,a,u,E={}){let s=N(e||"default"),c=s[i]??={};c[a]||(c[a]={version:a,get:u,from:E.from??"unknown",eager:!!E.eager,loaded:!!E.loaded})}function x(e){for(let i of e){let a=n.get(i.name);n.set(i.name,{timeout:U,retries:P,...a,...i,state:a?.state??"idle",breakerState:a?.breakerState??{fails:0,openUntil:0},debug:a?.debug??{entry:i.entry,status:"idle"}})}}function q(e){x([e])}function G(e){o.push(...e),D()}async function z(e,i={}){let a=i.shareScope||"default",u=i.shareKey||e,s=N(a)[u]||{},c=Object.keys(s),d=i.requiredVersion,f=c.filter(p=>d===void 0||d===!1||k(p,d)),b;if(i.singleton&&c.length>0){let p=c.filter(h=>s[h].loaded);if(b=[...p.length?p:c].sort(v).pop(),c.length>1||!f.includes(b)){let h=s[b],T=`singleton skew "${u}": req "${d??"any"}" use ${b} from ${h.from}`;if(i.strictVersion){let M=new g(R.SHARE_STRICT_VERSION,T,{shareKey:u,requiredVersion:d});throw j({remote:h.from,error:M}),M}console.warn(`[fulgurjs:MFU-010] ${T}`)}}else if(b=[...f].sort(v).pop(),!b){if(i.strictVersion){let h=new g(R.SHARE_STRICT_VERSION,`no satisfying version for "${u}" (required "${d}") in share scope "${a}"`);throw j({remote:u,error:h}),h}if(i.fallback)return i.fallback();let p=new g(R.SHARE_NOT_AVAILABLE,`shared "${u}" (${d??"any"}) unavailable in scope "${a}" (no local fallback)`,{shareKey:u,requiredVersion:d});throw j({remote:u,error:p}),p}let _=s[b];if($.resolveShare){let p=await $.resolveShare({shareKey:u,shareScope:a,requiredVersion:d,picked:_,available:Object.values(s)});p&&(_=p)}return _.loaded=!0,_.get()}function J(e,i,a){return new Promise((u,E)=>{let s=setTimeout(()=>E(new Error(`timeout after ${i}ms: ${a}`)),i);e.then(c=>{clearTimeout(s),u(c)},c=>{clearTimeout(s),E(c)})})}let W=e=>new Promise(i=>setTimeout(i,e));function I(e,i){if(!e||typeof e.get!="function")throw new Error(`remoteEntry of "${i.name}" does not export a container interface (get/init)`);if(e.name&&i.name&&e.name!==i.name){let a=`remoteEntry self-reported name "${e.name}" mismatches configured name "${i.name}"`;if(i.promise)console.warn(`[fulgurjs] ${a} (promise-based remote, continuing)`);else throw Object.assign(new Error(a),{code:R.REMOTE_NAME_MISMATCH})}}async function L(e,i){return J(import(e),i.timeout??U,`load remoteEntry ${e}`)}async function C(e,i){let a=e.shareScope||"default";if(e.container){let s=l.get(e.container);if(s&&s!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${s}", cannot re-init with "${a}"`);return e.container}if(e.containerPromise)return e.containerPromise;let u=e.breakerState;if(u.openUntil>Date.now()){let s=new g(R.REMOTE_LOAD_FAILED,`circuit breaker open for remote "${e.name}" until ${new Date(u.openUntil).toISOString()}`,{remote:e.name});throw j({remote:e.name,error:s}),s}e.state="loading",e.debug.status="loading";let E=(async()=>{let s=[];if(e.promise)s.push(async()=>{let p=await e.promise(),h=typeof p=="string"?await L(p,e):p;return I(h,e),h});else for(let p of[e.entry,...e.fallback??[]])s.push(async()=>{let h=await L(p,e);return I(h,e),h});let c,d,f=!1,b=i?.retries??e.retries??P;for(let p of s){for(let h=0;h<=b;h++){let T=performance.now();try{c=await p(),e.lastLoadMs=Math.round(performance.now()-T),e.debug.lastLoadMs=e.lastLoadMs,f=!0;break}catch(M){if(d=M,M?.code)throw M;h<b&&await W(200*2**h)}}if(f)break}if(!f||!c){let p=[];throw/^https?:\\/\\//.test(e.entry)&&typeof location<"u"?(p.push(`1. is the remote dev server running? try opening ${e.entry} in the browser \\u2014 it must return JS, not HTML/an error`),p.push("2. is the URL correct in federation({ remotes })? dev and prod entries can differ"),p.push("3. CORS: the remote dev server must allow cross-origin requests (fulgurjs-federation enables server.cors automatically; a custom server config may have disabled it)")):(p.push(`1. is the remote deployed and reachable? try opening ${e.entry} in the browser \\u2014 it must return JS`),p.push("2. NGINX/CDN routing: the entry path must serve the remoteEntry JS file (check try_files / fallback rules)")),new g(R.REMOTE_LOAD_FAILED,`failed to load remote "${e.name}" from ${e.entry}: ${String(d?.message??d)}\n`+p.join(`\n`),{remote:e.name})}I(c,e);let _=l.get(c);if(_&&_!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${_}", cannot re-init with "${a}"`);return typeof c.init=="function"&&await c.init(t),l.set(c,a),e.container=c,e.state="loaded",e.debug.status="loaded",u.fails=0,c})();e.containerPromise=E;try{return await E}catch(s){if(e.containerPromise=void 0,e.state="failed",e.debug.status="failed",e.debug.error=String(s?.message??s),u.fails+=1,u.fails>=ce&&(u.openUntil=Date.now()+le,u.fails=0),s instanceof g)throw j({remote:e.name,error:s}),s;if(s?.code)throw j({remote:e.name,error:s}),s;let c=new g(R.REMOTE_LOAD_FAILED,String(s?.message??s),{remote:e.name});throw j({remote:e.name,error:c}),c}}function A(e){let i=e.indexOf("/");return i===-1?{remote:e,module:""}:{remote:e.slice(0,i),module:B(e.slice(i+1))}}function B(e){return e?e.startsWith(".")?e:`./${e}`:""}async function X(e,i){let{remote:a,module:u}=A(e);$.beforeLoadRemote?.({remote:a,module:u});let E=n.get(a);if(!E)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}". Register it via federation({ remotes }) or registerRemote().`,{remote:a});let s;try{s=await C(E,{retries:i?.retries})}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\\u663E\\u5F0F\\u964D\\u7EA7\\uFF0C\\u9519\\u8BEF\\u5DF2\\u900F\\u51FA)`,f),j({remote:a,error:f}),await i.fallbackModule();throw f}if(!u)return s;let c=`${a}@${E.shareScope||"default"}#${u}`;r.has(c)||r.set(c,s.get(u).catch(f=>{throw r.delete(c),f instanceof g||f?.code?f:new g(R.REMOTE_LOAD_FAILED,`failed to load module "${u}" from remote "${a}": ${String(f?.message??f)}`,{remote:a,module:u})}));let d;try{d=await r.get(c)}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\\u663E\\u5F0F\\u964D\\u7EA7\\uFF0C\\u9519\\u8BEF\\u5DF2\\u900F\\u51FA)`,f),j({remote:a,error:f}),await i.fallbackModule();throw f}if($.afterLoadRemote?.({remote:a,module:u,module_ns:d}),d&&typeof d=="object"&&Object.keys(d).length===0){let f=new g(R.EMPTY_EXPORTS,`no exports: ${u} @ ${a}`,{remote:a,module:u});j({remote:a,error:f}),console.error(`[fulgurjs:MFU-009] ${f.message}`)}return d}function Y(e){let i=n.get(e);if(!i)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${e}"`,{remote:e});return C(i)}async function Z(e,i={}){let{remote:a}=A(e),u=n.get(a);if(!u)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}"`,{remote:a});let E=i.mode??"preload",s=(c,d)=>{if(typeof document>"u"||document.querySelector(`link[href="${c}"]`))return;let f=document.createElement("link");f.rel=d==="style"?"stylesheet":"modulepreload",f.href=c,E==="prefetch"&&(f.fetchPriority="low"),document.head.appendChild(f)};try{if(u.manifestUrl){let c=await fetch(u.manifestUrl,{cache:"no-cache"});if(c.ok){let d=await c.json();s(u.entry,"modulepreload");let f=d.exposes??{};for(let b of Object.keys(f)){let _=f[b];_?.file&&s(new URL(_.file,u.entry).href,"modulepreload");for(let p of _?.css??[])s(new URL(p,u.entry).href,"style")}return}}s(u.entry,"modulepreload")}catch(c){j({remote:a,error:new g(R.PRELOAD_FAILED,String(c?.message??c),{remote:a})})}}return typeof window>"u"||(window.__FULGURJS_SCOPE__=t,window.__FULGURJS_INFO__={get remotes(){let e={};for(let[i,a]of n)e[i]={...a.debug,entry:a.entry};return e},errors:[]}),{shareScopeMap:t,initSharing:H,registerShare:K,registerRemotes:x,registerRemote:q,registerPlugins:G,loadShare:z,loadRemote:X,getContainer:Y,preloadRemote:Z,parseSpec:A,provideFulgurjsAppConfig:y,getFulgurjsAppConfig:w}}var V=globalThis,S=V.__FULGURJS_RUNTIME__??fe();V.__FULGURJS_RUNTIME__=S;try{Object.freeze(S)}catch{}var Re=F;function Se(){return S}var we=S.initSharing,be=S.registerShare,_e=S.registerRemotes,je=S.registerRemote,$e=S.registerPlugins,Me=S.loadShare,ve=S.loadRemote,Te=S.getContainer,Oe=S.preloadRemote,ke=S.shareScopeMap,Ne=S.provideFulgurjsAppConfig,Ie=S.getFulgurjsAppConfig;function Ae(t){if(t&&typeof t=="object"&&"default"in t){let n=t.default;return n!==void 0?n:t}return t}var xe=S;export{xe as default,Te as getContainer,Ie as getFulgurjsAppConfig,Se as getRuntime,we as initSharing,ve as loadRemote,Me as loadShare,Oe as preloadRemote,Ne as provideFulgurjsAppConfig,$e as registerPlugins,je as registerRemote,_e as registerRemotes,be as registerShare,S as runtime,ke as shareScopeMap,Ae as unwrapDefault,Re as version};\n';
1278
1309
 
1279
1310
  // src/index.ts
1280
1311
  function readRuntimeCode() {
package/dist/index.d.cts CHANGED
@@ -60,6 +60,7 @@ interface FulgurjsOptions {
60
60
  runtimePlugins?: string[];
61
61
  dts?: boolean | {
62
62
  dir?: string;
63
+ mode?: 'source' | 'shim';
63
64
  };
64
65
  /** 接受并恒为 true:TLA 天然异步边界,无需手工 bootstrap(比 webpack 更进一步) */
65
66
  automaticAsyncBoundary?: boolean;
package/dist/index.d.ts CHANGED
@@ -60,6 +60,7 @@ interface FulgurjsOptions {
60
60
  runtimePlugins?: string[];
61
61
  dts?: boolean | {
62
62
  dir?: string;
63
+ mode?: 'source' | 'shim';
63
64
  };
64
65
  /** 接受并恒为 true:TLA 天然异步边界,无需手工 bootstrap(比 webpack 更进一步) */
65
66
  automaticAsyncBoundary?: boolean;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  RUNTIME_VERSION
3
- } from "./chunk-WX5CMQP5.js";
3
+ } from "./chunk-2VPHH4XD.js";
4
4
 
5
5
  // src/index.ts
6
6
  import path5 from "path";
@@ -1057,9 +1057,36 @@ function resolveDtsDir(dtsOpt, rootHasSrc) {
1057
1057
  if (dtsOpt === false) return "";
1058
1058
  return (typeof dtsOpt === "object" ? dtsOpt.dir : void 0) ?? (rootHasSrc ? "src/fulgurjs/types" : ".fulgurjs/types");
1059
1059
  }
1060
+ function resolveDtsMode(dtsOpt) {
1061
+ if (dtsOpt === false) return "source";
1062
+ return (typeof dtsOpt === "object" ? dtsOpt.mode : void 0) ?? "source";
1063
+ }
1064
+ function buildShimModule(abs, moduleSpecifier) {
1065
+ const lines = [`declare module '${moduleSpecifier}' {`];
1066
+ if (abs.endsWith(".vue")) {
1067
+ lines.push(` import type { DefineComponent } from 'vue'`);
1068
+ lines.push(` const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, any>`);
1069
+ lines.push(` export default component`);
1070
+ } else {
1071
+ const text = fs2.readFileSync(abs, "utf8");
1072
+ for (const name of extractTsExportNames(text)) {
1073
+ lines.push(` export const ${name}: any`);
1074
+ }
1075
+ if (sourceHasDefaultExport(abs)) {
1076
+ lines.push(` const _default: any`);
1077
+ lines.push(` export default _default`);
1078
+ }
1079
+ if (!fs2.existsSync(abs)) {
1080
+ lines.push(` export const __module: any`);
1081
+ }
1082
+ }
1083
+ lines.push(`}`);
1084
+ return lines.join("\n");
1085
+ }
1060
1086
  async function generateDevTypes(options, _server) {
1061
1087
  const dtsOpt = options.dts === void 0 ? true : options.dts;
1062
1088
  if (dtsOpt === false) return;
1089
+ const mode = resolveDtsMode(dtsOpt);
1063
1090
  const dir = resolveDtsDir(dtsOpt, fs2.existsSync(path3.join(options.root, "src")));
1064
1091
  const outDir = path3.join(options.root, dir);
1065
1092
  fs2.mkdirSync(outDir, { recursive: true });
@@ -1079,7 +1106,7 @@ async function generateDevTypes(options, _server) {
1079
1106
  continue;
1080
1107
  }
1081
1108
  const lines = [
1082
- `// \u81EA\u52A8\u751F\u6210\uFF1Afulgurjs-federation dev \u7C7B\u578B\u76F4\u8FDE\uFF08remote: ${remote.name}\uFF09`,
1109
+ `// \u81EA\u52A8\u751F\u6210\uFF1Afulgurjs-federation dev \u7C7B\u578B\u76F4\u8FDE\uFF08remote: ${remote.name}\uFF0Cmode: ${mode}\uFF09`,
1083
1110
  `// \u91CD\u65B0\u751F\u6210\uFF1A\u91CD\u542F host dev server`
1084
1111
  ];
1085
1112
  for (const expose of manifest.exposes ?? []) {
@@ -1089,6 +1116,10 @@ async function generateDevTypes(options, _server) {
1089
1116
  const importPath = abs.endsWith(".vue") ? sourceImportPath(rel) : stripTsExtension(sourceImportPath(rel));
1090
1117
  const moduleSpecifier = `${remote.key}/${expose.name.replace(/^\.\//, "")}`;
1091
1118
  lines.push("");
1119
+ if (mode === "shim") {
1120
+ lines.push(buildShimModule(abs, moduleSpecifier));
1121
+ continue;
1122
+ }
1092
1123
  if (abs.endsWith(".vue")) {
1093
1124
  lines.push(`declare module '${moduleSpecifier}' {`);
1094
1125
  lines.push(` import type { DefineComponent } from 'vue'`);
@@ -1111,7 +1142,7 @@ async function generateDevTypes(options, _server) {
1111
1142
  fs2.writeFileSync(path3.join(outDir, `${remote.key}.d.ts`), `${lines.join("\n")}
1112
1143
  `);
1113
1144
  console.log(
1114
- `[fulgurjs] dts: generated ${dir}/${remote.key}.d.ts (${manifest.exposes?.length ?? 0} exposes). \u786E\u4FDD tsconfig include \u5305\u542B "${dir}" \u4EE5\u83B7\u5F97\u6E90\u7801\u7EA7\u7C7B\u578B\u8865\u5168\u3002`
1145
+ `[fulgurjs] dts: generated ${dir}/${remote.key}.d.ts (${manifest.exposes?.length ?? 0} exposes, mode=${mode}). \u786E\u4FDD tsconfig include \u5305\u542B "${dir}" \u4EE5\u83B7\u5F97\u7C7B\u578B\u8865\u5168\u3002`
1115
1146
  );
1116
1147
  }
1117
1148
  }
@@ -1238,7 +1269,7 @@ function syncViteCacheMarker(root, pluginVersion, log) {
1238
1269
  }
1239
1270
 
1240
1271
  // src/runtime-code.gen.ts
1241
- var runtime_code_gen_default = 'var Q=/^v?(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z.-]+)?$/;function O(t){let n=Q.exec(String(t).trim());return n?{major:Number(n[1]),minor:Number(n[2]??0),patch:Number(n[3]??0),pre:n[4]?n[4].split("."):[]}:null}function ee(t,n){if(t.length===0&&n.length===0)return 0;if(t.length===0)return 1;if(n.length===0)return-1;let o=Math.max(t.length,n.length);for(let r=0;r<o;r++){let l=t[r],m=n[r];if(l===void 0)return-1;if(m===void 0)return 1;let y=/^\\d+$/.test(l),w=/^\\d+$/.test(m);if(y&&w){let $=Number(l)-Number(m);if($!==0)return $}else{if(y!==w)return y?-1:1;if(l!==m)return l<m?-1:1}}return 0}function v(t,n){let o=O(t),r=O(n);return!o||!r?t<n?-1:t>n?1:0:o.major!==r.major?o.major-r.major:o.minor!==r.minor?o.minor-r.minor:o.patch!==r.patch?o.patch-r.patch:ee(o.pre,r.pre)}var re=/^[a-z]+[+:-]/i;function k(t,n){if(n===!1||n==null||n==="")return!0;let o=String(n).trim();return!o||o==="*"||o==="latest"||re.test(o)?!0:o.split("||").some(r=>te(t,r.trim()))}function te(t,n){if(!n||n==="*")return!O(t)?.pre.length;let o=n.split(/\\s+-\\s+/);if(o.length===2){let m=o[0].trim(),y=o[1].trim();return!(!k(t,`>=${m}`)||!k(t,`<=${y}`))}let r=O(t);if(!r)return!1;let l=ne(n);if(l===null)return!0;for(let m of l)if(!ue(r,t,m))return!1;return!(r.pre.length>0&&!l.some(m=>m.ver.pre.length>0)&&!l.some(y=>y.ver.major===r.major&&y.ver.minor===r.minor&&y.ver.patch===r.patch))}function ne(t){let n=[],o=t.trim().split(/\\s+/).filter(Boolean);if(o.length===0)return null;for(let r of o){let l=/^(>=|<=|>|<|=|\\^|~)?(.*)$/.exec(r);if(!l)return null;let m=l[1],y=l[2],w=oe(m,y);if(w===null)return null;n.push(...w)}return n}function oe(t,n){let o=ae(n);if(!o)return null;let{ver:r,partial:l}=o;switch(t){case"^":{let m=ie(r);return m?[{op:">=",ver:r},{op:"<",ver:m}]:null}case"~":{let m=l>=2?{major:r.major,minor:r.minor+1,patch:0,pre:[]}:{major:r.major+1,minor:0,patch:0,pre:[]};return[{op:">=",ver:r},{op:"<",ver:m}]}case">=":return[{op:">=",ver:r}];case">":return l===1?[{op:">=",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:">",ver:r}];case"<":return l===1?[{op:"<",ver:{major:r.major,minor:0,patch:0,pre:[]}}]:l===2?[{op:"<",ver:{major:r.major,minor:r.minor,patch:0,pre:[]}}]:[{op:"<",ver:r}];case"<=":return[{op:"<=",ver:r}];case"=":case void 0:return l===1?[{op:">=",ver:r},{op:"<",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:r},{op:"<",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:"=",ver:r}];default:return null}}function ie(t){return t.major>0?{major:t.major+1,minor:0,patch:0,pre:[]}:t.minor>0?{major:0,minor:t.minor+1,patch:0,pre:[]}:{major:0,minor:0,patch:t.patch+1,pre:[]}}function ae(t){let n=t.replace(/^v/,"").trim();if(!n||n==="*"||n==="x"||n==="X")return{ver:{major:0,minor:0,patch:0,pre:[]},partial:0};let o=n.split("."),r=[],l=0;for(let w of o.slice(0,3))if(/^\\d+$/.test(w))r.push(Number(w)),l++;else{if(w==="x"||w==="X"||w==="*")break;break}let m=n.split("-"),y=m.length>1?m.slice(1).join("-").split("+")[0].split("."):[];return l===0&&y.length===0?{ver:{major:0,minor:0,patch:0,pre:[]},partial:0}:{ver:{major:r[0]??0,minor:r[1]??0,patch:r[2]??0,pre:y},partial:l}}function se(t,n){switch(n){case">=":return t>=0;case">":return t>0;case"<":return t<0;case"<=":return t<=0;case"=":return t===0}}function ue(t,n,o){let r=`${t.major}.${t.minor}.${t.patch}`+(t.pre.length?`-${t.pre.join(".")}`:"");if(o.upper)return!(!(v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:""))>=0)||!(v(r,`${o.upper.major}.${o.upper.minor}.${o.upper.patch}`)<0));let l=v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:"")),m=se(l,o.op);return o.ver.pre.length>0&&t.pre.length===0&&(o.op===">"||o.op==="<")?!1:m}var g=class extends Error{code;details;constructor(n,o,r){super(`[fulgurjs:${n}] ${o}`),this.name="FulgurjsError",this.code=n,this.details=r}},R={REMOTE_LOAD_FAILED:"MFU-001",REMOTE_NAME_MISMATCH:"MFU-002",SHARE_STRICT_VERSION:"MFU-003",SHARE_NOT_AVAILABLE:"MFU-004",CONTAINER_REINIT_CONFLICT:"MFU-005",MODULE_NOT_EXPOSED:"MFU-006",PRELOAD_FAILED:"MFU-007",REMOTE_UNKNOWN:"MFU-008",EMPTY_EXPORTS:"MFU-009",SINGLETON_SKEW:"MFU-010"};var F="0.8.4";var U=15e3,P=2,ce=5,le=3e4;function fe(){let t={},n=new Map,o=[],r=new Map,l=new WeakMap,m={};function y(e){m={...m,...e},typeof globalThis<"u"&&(globalThis.__FULGURJS_APP_CONFIG__=m)}function w(){return m}let $={},D=()=>{for(let e of o)try{e.init?.($)}catch(i){console.warn("[fulgurjs] runtimePlugin init failed:",e.name,i)}},N=e=>t[e]??={};function j(e){try{$.onRemoteError?.(e)}catch{}typeof window<"u"&&window.dispatchEvent(new CustomEvent("fulgurjs:error",{detail:e}))}function H(e="default"){return N(e),t}function K(e,i,a,u,E={}){let s=N(e||"default"),c=s[i]??={};c[a]||(c[a]={version:a,get:u,from:E.from??"unknown",eager:!!E.eager,loaded:!!E.loaded})}function x(e){for(let i of e){let a=n.get(i.name);n.set(i.name,{timeout:U,retries:P,...a,...i,state:a?.state??"idle",breakerState:a?.breakerState??{fails:0,openUntil:0},debug:a?.debug??{entry:i.entry,status:"idle"}})}}function q(e){x([e])}function G(e){o.push(...e),D()}async function z(e,i={}){let a=i.shareScope||"default",u=i.shareKey||e,s=N(a)[u]||{},c=Object.keys(s),d=i.requiredVersion,f=c.filter(p=>d===void 0||d===!1||k(p,d)),b;if(i.singleton&&c.length>0){let p=c.filter(h=>s[h].loaded);if(b=[...p.length?p:c].sort(v).pop(),c.length>1||!f.includes(b)){let h=s[b],T=`singleton skew "${u}": req "${d??"any"}" use ${b} from ${h.from}`;if(i.strictVersion){let M=new g(R.SHARE_STRICT_VERSION,T,{shareKey:u,requiredVersion:d});throw j({remote:h.from,error:M}),M}console.warn(`[fulgurjs:MFU-010] ${T}`)}}else if(b=[...f].sort(v).pop(),!b){if(i.strictVersion){let h=new g(R.SHARE_STRICT_VERSION,`no satisfying version for "${u}" (required "${d}") in share scope "${a}"`);throw j({remote:u,error:h}),h}if(i.fallback)return i.fallback();let p=new g(R.SHARE_NOT_AVAILABLE,`shared "${u}" (${d??"any"}) unavailable in scope "${a}" (no local fallback)`,{shareKey:u,requiredVersion:d});throw j({remote:u,error:p}),p}let _=s[b];if($.resolveShare){let p=await $.resolveShare({shareKey:u,shareScope:a,requiredVersion:d,picked:_,available:Object.values(s)});p&&(_=p)}return _.loaded=!0,_.get()}function J(e,i,a){return new Promise((u,E)=>{let s=setTimeout(()=>E(new Error(`timeout after ${i}ms: ${a}`)),i);e.then(c=>{clearTimeout(s),u(c)},c=>{clearTimeout(s),E(c)})})}let W=e=>new Promise(i=>setTimeout(i,e));function I(e,i){if(!e||typeof e.get!="function")throw new Error(`remoteEntry of "${i.name}" does not export a container interface (get/init)`);if(e.name&&i.name&&e.name!==i.name){let a=`remoteEntry self-reported name "${e.name}" mismatches configured name "${i.name}"`;if(i.promise)console.warn(`[fulgurjs] ${a} (promise-based remote, continuing)`);else throw Object.assign(new Error(a),{code:R.REMOTE_NAME_MISMATCH})}}async function L(e,i){return J(import(e),i.timeout??U,`load remoteEntry ${e}`)}async function C(e,i){let a=e.shareScope||"default";if(e.container){let s=l.get(e.container);if(s&&s!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${s}", cannot re-init with "${a}"`);return e.container}if(e.containerPromise)return e.containerPromise;let u=e.breakerState;if(u.openUntil>Date.now()){let s=new g(R.REMOTE_LOAD_FAILED,`circuit breaker open for remote "${e.name}" until ${new Date(u.openUntil).toISOString()}`,{remote:e.name});throw j({remote:e.name,error:s}),s}e.state="loading",e.debug.status="loading";let E=(async()=>{let s=[];if(e.promise)s.push(async()=>{let p=await e.promise(),h=typeof p=="string"?await L(p,e):p;return I(h,e),h});else for(let p of[e.entry,...e.fallback??[]])s.push(async()=>{let h=await L(p,e);return I(h,e),h});let c,d,f=!1,b=i?.retries??e.retries??P;for(let p of s){for(let h=0;h<=b;h++){let T=performance.now();try{c=await p(),e.lastLoadMs=Math.round(performance.now()-T),e.debug.lastLoadMs=e.lastLoadMs,f=!0;break}catch(M){if(d=M,M?.code)throw M;h<b&&await W(200*2**h)}}if(f)break}if(!f||!c){let p=[];throw/^https?:\\/\\//.test(e.entry)&&typeof location<"u"?(p.push(`1. is the remote dev server running? try opening ${e.entry} in the browser \\u2014 it must return JS, not HTML/an error`),p.push("2. is the URL correct in federation({ remotes })? dev and prod entries can differ"),p.push("3. CORS: the remote dev server must allow cross-origin requests (fulgurjs-federation enables server.cors automatically; a custom server config may have disabled it)")):(p.push(`1. is the remote deployed and reachable? try opening ${e.entry} in the browser \\u2014 it must return JS`),p.push("2. NGINX/CDN routing: the entry path must serve the remoteEntry JS file (check try_files / fallback rules)")),new g(R.REMOTE_LOAD_FAILED,`failed to load remote "${e.name}" from ${e.entry}: ${String(d?.message??d)}\n`+p.join(`\n`),{remote:e.name})}I(c,e);let _=l.get(c);if(_&&_!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${_}", cannot re-init with "${a}"`);return typeof c.init=="function"&&await c.init(t),l.set(c,a),e.container=c,e.state="loaded",e.debug.status="loaded",u.fails=0,c})();e.containerPromise=E;try{return await E}catch(s){if(e.containerPromise=void 0,e.state="failed",e.debug.status="failed",e.debug.error=String(s?.message??s),u.fails+=1,u.fails>=ce&&(u.openUntil=Date.now()+le,u.fails=0),s instanceof g)throw j({remote:e.name,error:s}),s;if(s?.code)throw j({remote:e.name,error:s}),s;let c=new g(R.REMOTE_LOAD_FAILED,String(s?.message??s),{remote:e.name});throw j({remote:e.name,error:c}),c}}function A(e){let i=e.indexOf("/");return i===-1?{remote:e,module:""}:{remote:e.slice(0,i),module:B(e.slice(i+1))}}function B(e){return e?e.startsWith(".")?e:`./${e}`:""}async function X(e,i){let{remote:a,module:u}=A(e);$.beforeLoadRemote?.({remote:a,module:u});let E=n.get(a);if(!E)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}". Register it via federation({ remotes }) or registerRemote().`,{remote:a});let s;try{s=await C(E,{retries:i?.retries})}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\\u663E\\u5F0F\\u964D\\u7EA7\\uFF0C\\u9519\\u8BEF\\u5DF2\\u900F\\u51FA)`,f),j({remote:a,error:f}),await i.fallbackModule();throw f}if(!u)return s;let c=`${a}@${E.shareScope||"default"}#${u}`;r.has(c)||r.set(c,s.get(u).catch(f=>{throw r.delete(c),f instanceof g||f?.code?f:new g(R.REMOTE_LOAD_FAILED,`failed to load module "${u}" from remote "${a}": ${String(f?.message??f)}`,{remote:a,module:u})}));let d;try{d=await r.get(c)}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\\u663E\\u5F0F\\u964D\\u7EA7\\uFF0C\\u9519\\u8BEF\\u5DF2\\u900F\\u51FA)`,f),j({remote:a,error:f}),await i.fallbackModule();throw f}if($.afterLoadRemote?.({remote:a,module:u,module_ns:d}),d&&typeof d=="object"&&Object.keys(d).length===0){let f=new g(R.EMPTY_EXPORTS,`no exports: ${u} @ ${a}`,{remote:a,module:u});j({remote:a,error:f}),console.error(`[fulgurjs:MFU-009] ${f.message}`)}return d}function Y(e){let i=n.get(e);if(!i)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${e}"`,{remote:e});return C(i)}async function Z(e,i={}){let{remote:a}=A(e),u=n.get(a);if(!u)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}"`,{remote:a});let E=i.mode??"preload",s=(c,d)=>{if(typeof document>"u"||document.querySelector(`link[href="${c}"]`))return;let f=document.createElement("link");f.rel=d==="style"?"stylesheet":"modulepreload",f.href=c,E==="prefetch"&&(f.fetchPriority="low"),document.head.appendChild(f)};try{if(u.manifestUrl){let c=await fetch(u.manifestUrl,{cache:"no-cache"});if(c.ok){let d=await c.json();s(u.entry,"modulepreload");let f=d.exposes??{};for(let b of Object.keys(f)){let _=f[b];_?.file&&s(new URL(_.file,u.entry).href,"modulepreload");for(let p of _?.css??[])s(new URL(p,u.entry).href,"style")}return}}s(u.entry,"modulepreload")}catch(c){j({remote:a,error:new g(R.PRELOAD_FAILED,String(c?.message??c),{remote:a})})}}return typeof window>"u"||(window.__FULGURJS_SCOPE__=t,window.__FULGURJS_INFO__={get remotes(){let e={};for(let[i,a]of n)e[i]={...a.debug,entry:a.entry};return e},errors:[]}),{shareScopeMap:t,initSharing:H,registerShare:K,registerRemotes:x,registerRemote:q,registerPlugins:G,loadShare:z,loadRemote:X,getContainer:Y,preloadRemote:Z,parseSpec:A,provideFulgurjsAppConfig:y,getFulgurjsAppConfig:w}}var V=globalThis,S=V.__FULGURJS_RUNTIME__??fe();V.__FULGURJS_RUNTIME__=S;try{Object.freeze(S)}catch{}var Re=F;function Se(){return S}var we=S.initSharing,be=S.registerShare,_e=S.registerRemotes,je=S.registerRemote,$e=S.registerPlugins,Me=S.loadShare,ve=S.loadRemote,Te=S.getContainer,Oe=S.preloadRemote,ke=S.shareScopeMap,Ne=S.provideFulgurjsAppConfig,Ie=S.getFulgurjsAppConfig;function Ae(t){if(t&&typeof t=="object"&&"default"in t){let n=t.default;return n!==void 0?n:t}return t}var xe=S;export{xe as default,Te as getContainer,Ie as getFulgurjsAppConfig,Se as getRuntime,we as initSharing,ve as loadRemote,Me as loadShare,Oe as preloadRemote,Ne as provideFulgurjsAppConfig,$e as registerPlugins,je as registerRemote,_e as registerRemotes,be as registerShare,S as runtime,ke as shareScopeMap,Ae as unwrapDefault,Re as version};\n';
1272
+ var runtime_code_gen_default = 'var Q=/^v?(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z.-]+)?$/;function O(t){let n=Q.exec(String(t).trim());return n?{major:Number(n[1]),minor:Number(n[2]??0),patch:Number(n[3]??0),pre:n[4]?n[4].split("."):[]}:null}function ee(t,n){if(t.length===0&&n.length===0)return 0;if(t.length===0)return 1;if(n.length===0)return-1;let o=Math.max(t.length,n.length);for(let r=0;r<o;r++){let l=t[r],m=n[r];if(l===void 0)return-1;if(m===void 0)return 1;let y=/^\\d+$/.test(l),w=/^\\d+$/.test(m);if(y&&w){let $=Number(l)-Number(m);if($!==0)return $}else{if(y!==w)return y?-1:1;if(l!==m)return l<m?-1:1}}return 0}function v(t,n){let o=O(t),r=O(n);return!o||!r?t<n?-1:t>n?1:0:o.major!==r.major?o.major-r.major:o.minor!==r.minor?o.minor-r.minor:o.patch!==r.patch?o.patch-r.patch:ee(o.pre,r.pre)}var re=/^[a-z]+[+:-]/i;function k(t,n){if(n===!1||n==null||n==="")return!0;let o=String(n).trim();return!o||o==="*"||o==="latest"||re.test(o)?!0:o.split("||").some(r=>te(t,r.trim()))}function te(t,n){if(!n||n==="*")return!O(t)?.pre.length;let o=n.split(/\\s+-\\s+/);if(o.length===2){let m=o[0].trim(),y=o[1].trim();return!(!k(t,`>=${m}`)||!k(t,`<=${y}`))}let r=O(t);if(!r)return!1;let l=ne(n);if(l===null)return!0;for(let m of l)if(!ue(r,t,m))return!1;return!(r.pre.length>0&&!l.some(m=>m.ver.pre.length>0)&&!l.some(y=>y.ver.major===r.major&&y.ver.minor===r.minor&&y.ver.patch===r.patch))}function ne(t){let n=[],o=t.trim().split(/\\s+/).filter(Boolean);if(o.length===0)return null;for(let r of o){let l=/^(>=|<=|>|<|=|\\^|~)?(.*)$/.exec(r);if(!l)return null;let m=l[1],y=l[2],w=oe(m,y);if(w===null)return null;n.push(...w)}return n}function oe(t,n){let o=ae(n);if(!o)return null;let{ver:r,partial:l}=o;switch(t){case"^":{let m=ie(r);return m?[{op:">=",ver:r},{op:"<",ver:m}]:null}case"~":{let m=l>=2?{major:r.major,minor:r.minor+1,patch:0,pre:[]}:{major:r.major+1,minor:0,patch:0,pre:[]};return[{op:">=",ver:r},{op:"<",ver:m}]}case">=":return[{op:">=",ver:r}];case">":return l===1?[{op:">=",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:">",ver:r}];case"<":return l===1?[{op:"<",ver:{major:r.major,minor:0,patch:0,pre:[]}}]:l===2?[{op:"<",ver:{major:r.major,minor:r.minor,patch:0,pre:[]}}]:[{op:"<",ver:r}];case"<=":return[{op:"<=",ver:r}];case"=":case void 0:return l===1?[{op:">=",ver:r},{op:"<",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:r},{op:"<",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:"=",ver:r}];default:return null}}function ie(t){return t.major>0?{major:t.major+1,minor:0,patch:0,pre:[]}:t.minor>0?{major:0,minor:t.minor+1,patch:0,pre:[]}:{major:0,minor:0,patch:t.patch+1,pre:[]}}function ae(t){let n=t.replace(/^v/,"").trim();if(!n||n==="*"||n==="x"||n==="X")return{ver:{major:0,minor:0,patch:0,pre:[]},partial:0};let o=n.split("."),r=[],l=0;for(let w of o.slice(0,3))if(/^\\d+$/.test(w))r.push(Number(w)),l++;else{if(w==="x"||w==="X"||w==="*")break;break}let m=n.split("-"),y=m.length>1?m.slice(1).join("-").split("+")[0].split("."):[];return l===0&&y.length===0?{ver:{major:0,minor:0,patch:0,pre:[]},partial:0}:{ver:{major:r[0]??0,minor:r[1]??0,patch:r[2]??0,pre:y},partial:l}}function se(t,n){switch(n){case">=":return t>=0;case">":return t>0;case"<":return t<0;case"<=":return t<=0;case"=":return t===0}}function ue(t,n,o){let r=`${t.major}.${t.minor}.${t.patch}`+(t.pre.length?`-${t.pre.join(".")}`:"");if(o.upper)return!(!(v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:""))>=0)||!(v(r,`${o.upper.major}.${o.upper.minor}.${o.upper.patch}`)<0));let l=v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:"")),m=se(l,o.op);return o.ver.pre.length>0&&t.pre.length===0&&(o.op===">"||o.op==="<")?!1:m}var g=class extends Error{code;details;constructor(n,o,r){super(`[fulgurjs:${n}] ${o}`),this.name="FulgurjsError",this.code=n,this.details=r}},R={REMOTE_LOAD_FAILED:"MFU-001",REMOTE_NAME_MISMATCH:"MFU-002",SHARE_STRICT_VERSION:"MFU-003",SHARE_NOT_AVAILABLE:"MFU-004",CONTAINER_REINIT_CONFLICT:"MFU-005",MODULE_NOT_EXPOSED:"MFU-006",PRELOAD_FAILED:"MFU-007",REMOTE_UNKNOWN:"MFU-008",EMPTY_EXPORTS:"MFU-009",SINGLETON_SKEW:"MFU-010"};var F="0.9.1-test.0";var U=15e3,P=2,ce=5,le=3e4;function fe(){let t={},n=new Map,o=[],r=new Map,l=new WeakMap,m={};function y(e){m={...m,...e},typeof globalThis<"u"&&(globalThis.__FULGURJS_APP_CONFIG__=m)}function w(){return m}let $={},D=()=>{for(let e of o)try{e.init?.($)}catch(i){console.warn("[fulgurjs] runtimePlugin init failed:",e.name,i)}},N=e=>t[e]??={};function j(e){try{$.onRemoteError?.(e)}catch{}typeof window<"u"&&window.dispatchEvent(new CustomEvent("fulgurjs:error",{detail:e}))}function H(e="default"){return N(e),t}function K(e,i,a,u,E={}){let s=N(e||"default"),c=s[i]??={};c[a]||(c[a]={version:a,get:u,from:E.from??"unknown",eager:!!E.eager,loaded:!!E.loaded})}function x(e){for(let i of e){let a=n.get(i.name);n.set(i.name,{timeout:U,retries:P,...a,...i,state:a?.state??"idle",breakerState:a?.breakerState??{fails:0,openUntil:0},debug:a?.debug??{entry:i.entry,status:"idle"}})}}function q(e){x([e])}function G(e){o.push(...e),D()}async function z(e,i={}){let a=i.shareScope||"default",u=i.shareKey||e,s=N(a)[u]||{},c=Object.keys(s),d=i.requiredVersion,f=c.filter(p=>d===void 0||d===!1||k(p,d)),b;if(i.singleton&&c.length>0){let p=c.filter(h=>s[h].loaded);if(b=[...p.length?p:c].sort(v).pop(),c.length>1||!f.includes(b)){let h=s[b],T=`singleton skew "${u}": req "${d??"any"}" use ${b} from ${h.from}`;if(i.strictVersion){let M=new g(R.SHARE_STRICT_VERSION,T,{shareKey:u,requiredVersion:d});throw j({remote:h.from,error:M}),M}console.warn(`[fulgurjs:MFU-010] ${T}`)}}else if(b=[...f].sort(v).pop(),!b){if(i.strictVersion){let h=new g(R.SHARE_STRICT_VERSION,`no satisfying version for "${u}" (required "${d}") in share scope "${a}"`);throw j({remote:u,error:h}),h}if(i.fallback)return i.fallback();let p=new g(R.SHARE_NOT_AVAILABLE,`shared "${u}" (${d??"any"}) unavailable in scope "${a}" (no local fallback)`,{shareKey:u,requiredVersion:d});throw j({remote:u,error:p}),p}let _=s[b];if($.resolveShare){let p=await $.resolveShare({shareKey:u,shareScope:a,requiredVersion:d,picked:_,available:Object.values(s)});p&&(_=p)}return _.loaded=!0,_.get()}function J(e,i,a){return new Promise((u,E)=>{let s=setTimeout(()=>E(new Error(`timeout after ${i}ms: ${a}`)),i);e.then(c=>{clearTimeout(s),u(c)},c=>{clearTimeout(s),E(c)})})}let W=e=>new Promise(i=>setTimeout(i,e));function I(e,i){if(!e||typeof e.get!="function")throw new Error(`remoteEntry of "${i.name}" does not export a container interface (get/init)`);if(e.name&&i.name&&e.name!==i.name){let a=`remoteEntry self-reported name "${e.name}" mismatches configured name "${i.name}"`;if(i.promise)console.warn(`[fulgurjs] ${a} (promise-based remote, continuing)`);else throw Object.assign(new Error(a),{code:R.REMOTE_NAME_MISMATCH})}}async function L(e,i){return J(import(e),i.timeout??U,`load remoteEntry ${e}`)}async function C(e,i){let a=e.shareScope||"default";if(e.container){let s=l.get(e.container);if(s&&s!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${s}", cannot re-init with "${a}"`);return e.container}if(e.containerPromise)return e.containerPromise;let u=e.breakerState;if(u.openUntil>Date.now()){let s=new g(R.REMOTE_LOAD_FAILED,`circuit breaker open for remote "${e.name}" until ${new Date(u.openUntil).toISOString()}`,{remote:e.name});throw j({remote:e.name,error:s}),s}e.state="loading",e.debug.status="loading";let E=(async()=>{let s=[];if(e.promise)s.push(async()=>{let p=await e.promise(),h=typeof p=="string"?await L(p,e):p;return I(h,e),h});else for(let p of[e.entry,...e.fallback??[]])s.push(async()=>{let h=await L(p,e);return I(h,e),h});let c,d,f=!1,b=i?.retries??e.retries??P;for(let p of s){for(let h=0;h<=b;h++){let T=performance.now();try{c=await p(),e.lastLoadMs=Math.round(performance.now()-T),e.debug.lastLoadMs=e.lastLoadMs,f=!0;break}catch(M){if(d=M,M?.code)throw M;h<b&&await W(200*2**h)}}if(f)break}if(!f||!c){let p=[];throw/^https?:\\/\\//.test(e.entry)&&typeof location<"u"?(p.push(`1. is the remote dev server running? try opening ${e.entry} in the browser \\u2014 it must return JS, not HTML/an error`),p.push("2. is the URL correct in federation({ remotes })? dev and prod entries can differ"),p.push("3. CORS: the remote dev server must allow cross-origin requests (fulgurjs-federation enables server.cors automatically; a custom server config may have disabled it)")):(p.push(`1. is the remote deployed and reachable? try opening ${e.entry} in the browser \\u2014 it must return JS`),p.push("2. NGINX/CDN routing: the entry path must serve the remoteEntry JS file (check try_files / fallback rules)")),new g(R.REMOTE_LOAD_FAILED,`failed to load remote "${e.name}" from ${e.entry}: ${String(d?.message??d)}\n`+p.join(`\n`),{remote:e.name})}I(c,e);let _=l.get(c);if(_&&_!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${_}", cannot re-init with "${a}"`);return typeof c.init=="function"&&await c.init(t),l.set(c,a),e.container=c,e.state="loaded",e.debug.status="loaded",u.fails=0,c})();e.containerPromise=E;try{return await E}catch(s){if(e.containerPromise=void 0,e.state="failed",e.debug.status="failed",e.debug.error=String(s?.message??s),u.fails+=1,u.fails>=ce&&(u.openUntil=Date.now()+le,u.fails=0),s instanceof g)throw j({remote:e.name,error:s}),s;if(s?.code)throw j({remote:e.name,error:s}),s;let c=new g(R.REMOTE_LOAD_FAILED,String(s?.message??s),{remote:e.name});throw j({remote:e.name,error:c}),c}}function A(e){let i=e.indexOf("/");return i===-1?{remote:e,module:""}:{remote:e.slice(0,i),module:B(e.slice(i+1))}}function B(e){return e?e.startsWith(".")?e:`./${e}`:""}async function X(e,i){let{remote:a,module:u}=A(e);$.beforeLoadRemote?.({remote:a,module:u});let E=n.get(a);if(!E)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}". Register it via federation({ remotes }) or registerRemote().`,{remote:a});let s;try{s=await C(E,{retries:i?.retries})}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\\u663E\\u5F0F\\u964D\\u7EA7\\uFF0C\\u9519\\u8BEF\\u5DF2\\u900F\\u51FA)`,f),j({remote:a,error:f}),await i.fallbackModule();throw f}if(!u)return s;let c=`${a}@${E.shareScope||"default"}#${u}`;r.has(c)||r.set(c,s.get(u).catch(f=>{throw r.delete(c),f instanceof g||f?.code?f:new g(R.REMOTE_LOAD_FAILED,`failed to load module "${u}" from remote "${a}": ${String(f?.message??f)}`,{remote:a,module:u})}));let d;try{d=await r.get(c)}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\\u663E\\u5F0F\\u964D\\u7EA7\\uFF0C\\u9519\\u8BEF\\u5DF2\\u900F\\u51FA)`,f),j({remote:a,error:f}),await i.fallbackModule();throw f}if($.afterLoadRemote?.({remote:a,module:u,module_ns:d}),d&&typeof d=="object"&&Object.keys(d).length===0){let f=new g(R.EMPTY_EXPORTS,`no exports: ${u} @ ${a}`,{remote:a,module:u});j({remote:a,error:f}),console.error(`[fulgurjs:MFU-009] ${f.message}`)}return d}function Y(e){let i=n.get(e);if(!i)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${e}"`,{remote:e});return C(i)}async function Z(e,i={}){let{remote:a}=A(e),u=n.get(a);if(!u)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}"`,{remote:a});let E=i.mode??"preload",s=(c,d)=>{if(typeof document>"u"||document.querySelector(`link[href="${c}"]`))return;let f=document.createElement("link");f.rel=d==="style"?"stylesheet":"modulepreload",f.href=c,E==="prefetch"&&(f.fetchPriority="low"),document.head.appendChild(f)};try{if(u.manifestUrl){let c=await fetch(u.manifestUrl,{cache:"no-cache"});if(c.ok){let d=await c.json();s(u.entry,"modulepreload");let f=d.exposes??{};for(let b of Object.keys(f)){let _=f[b];_?.file&&s(new URL(_.file,u.entry).href,"modulepreload");for(let p of _?.css??[])s(new URL(p,u.entry).href,"style")}return}}s(u.entry,"modulepreload")}catch(c){j({remote:a,error:new g(R.PRELOAD_FAILED,String(c?.message??c),{remote:a})})}}return typeof window>"u"||(window.__FULGURJS_SCOPE__=t,window.__FULGURJS_INFO__={get remotes(){let e={};for(let[i,a]of n)e[i]={...a.debug,entry:a.entry};return e},errors:[]}),{shareScopeMap:t,initSharing:H,registerShare:K,registerRemotes:x,registerRemote:q,registerPlugins:G,loadShare:z,loadRemote:X,getContainer:Y,preloadRemote:Z,parseSpec:A,provideFulgurjsAppConfig:y,getFulgurjsAppConfig:w}}var V=globalThis,S=V.__FULGURJS_RUNTIME__??fe();V.__FULGURJS_RUNTIME__=S;try{Object.freeze(S)}catch{}var Re=F;function Se(){return S}var we=S.initSharing,be=S.registerShare,_e=S.registerRemotes,je=S.registerRemote,$e=S.registerPlugins,Me=S.loadShare,ve=S.loadRemote,Te=S.getContainer,Oe=S.preloadRemote,ke=S.shareScopeMap,Ne=S.provideFulgurjsAppConfig,Ie=S.getFulgurjsAppConfig;function Ae(t){if(t&&typeof t=="object"&&"default"in t){let n=t.default;return n!==void 0?n:t}return t}var xe=S;export{xe as default,Te as getContainer,Ie as getFulgurjsAppConfig,Se as getRuntime,we as initSharing,ve as loadRemote,Me as loadShare,Oe as preloadRemote,Ne as provideFulgurjsAppConfig,$e as registerPlugins,je as registerRemote,_e as registerRemotes,be as registerShare,S as runtime,ke as shareScopeMap,Ae as unwrapDefault,Re as version};\n';
1242
1273
 
1243
1274
  // src/index.ts
1244
1275
  function readRuntimeCode() {
package/dist/runtime.js CHANGED
@@ -1,3 +1,3 @@
1
- var Q=/^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/;function O(t){let n=Q.exec(String(t).trim());return n?{major:Number(n[1]),minor:Number(n[2]??0),patch:Number(n[3]??0),pre:n[4]?n[4].split("."):[]}:null}function ee(t,n){if(t.length===0&&n.length===0)return 0;if(t.length===0)return 1;if(n.length===0)return-1;let o=Math.max(t.length,n.length);for(let r=0;r<o;r++){let l=t[r],m=n[r];if(l===void 0)return-1;if(m===void 0)return 1;let y=/^\d+$/.test(l),w=/^\d+$/.test(m);if(y&&w){let $=Number(l)-Number(m);if($!==0)return $}else{if(y!==w)return y?-1:1;if(l!==m)return l<m?-1:1}}return 0}function v(t,n){let o=O(t),r=O(n);return!o||!r?t<n?-1:t>n?1:0:o.major!==r.major?o.major-r.major:o.minor!==r.minor?o.minor-r.minor:o.patch!==r.patch?o.patch-r.patch:ee(o.pre,r.pre)}var re=/^[a-z]+[+:-]/i;function k(t,n){if(n===!1||n==null||n==="")return!0;let o=String(n).trim();return!o||o==="*"||o==="latest"||re.test(o)?!0:o.split("||").some(r=>te(t,r.trim()))}function te(t,n){if(!n||n==="*")return!O(t)?.pre.length;let o=n.split(/\s+-\s+/);if(o.length===2){let m=o[0].trim(),y=o[1].trim();return!(!k(t,`>=${m}`)||!k(t,`<=${y}`))}let r=O(t);if(!r)return!1;let l=ne(n);if(l===null)return!0;for(let m of l)if(!ue(r,t,m))return!1;return!(r.pre.length>0&&!l.some(m=>m.ver.pre.length>0)&&!l.some(y=>y.ver.major===r.major&&y.ver.minor===r.minor&&y.ver.patch===r.patch))}function ne(t){let n=[],o=t.trim().split(/\s+/).filter(Boolean);if(o.length===0)return null;for(let r of o){let l=/^(>=|<=|>|<|=|\^|~)?(.*)$/.exec(r);if(!l)return null;let m=l[1],y=l[2],w=oe(m,y);if(w===null)return null;n.push(...w)}return n}function oe(t,n){let o=ae(n);if(!o)return null;let{ver:r,partial:l}=o;switch(t){case"^":{let m=ie(r);return m?[{op:">=",ver:r},{op:"<",ver:m}]:null}case"~":{let m=l>=2?{major:r.major,minor:r.minor+1,patch:0,pre:[]}:{major:r.major+1,minor:0,patch:0,pre:[]};return[{op:">=",ver:r},{op:"<",ver:m}]}case">=":return[{op:">=",ver:r}];case">":return l===1?[{op:">=",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:">",ver:r}];case"<":return l===1?[{op:"<",ver:{major:r.major,minor:0,patch:0,pre:[]}}]:l===2?[{op:"<",ver:{major:r.major,minor:r.minor,patch:0,pre:[]}}]:[{op:"<",ver:r}];case"<=":return[{op:"<=",ver:r}];case"=":case void 0:return l===1?[{op:">=",ver:r},{op:"<",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:r},{op:"<",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:"=",ver:r}];default:return null}}function ie(t){return t.major>0?{major:t.major+1,minor:0,patch:0,pre:[]}:t.minor>0?{major:0,minor:t.minor+1,patch:0,pre:[]}:{major:0,minor:0,patch:t.patch+1,pre:[]}}function ae(t){let n=t.replace(/^v/,"").trim();if(!n||n==="*"||n==="x"||n==="X")return{ver:{major:0,minor:0,patch:0,pre:[]},partial:0};let o=n.split("."),r=[],l=0;for(let w of o.slice(0,3))if(/^\d+$/.test(w))r.push(Number(w)),l++;else{if(w==="x"||w==="X"||w==="*")break;break}let m=n.split("-"),y=m.length>1?m.slice(1).join("-").split("+")[0].split("."):[];return l===0&&y.length===0?{ver:{major:0,minor:0,patch:0,pre:[]},partial:0}:{ver:{major:r[0]??0,minor:r[1]??0,patch:r[2]??0,pre:y},partial:l}}function se(t,n){switch(n){case">=":return t>=0;case">":return t>0;case"<":return t<0;case"<=":return t<=0;case"=":return t===0}}function ue(t,n,o){let r=`${t.major}.${t.minor}.${t.patch}`+(t.pre.length?`-${t.pre.join(".")}`:"");if(o.upper)return!(!(v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:""))>=0)||!(v(r,`${o.upper.major}.${o.upper.minor}.${o.upper.patch}`)<0));let l=v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:"")),m=se(l,o.op);return o.ver.pre.length>0&&t.pre.length===0&&(o.op===">"||o.op==="<")?!1:m}var g=class extends Error{code;details;constructor(n,o,r){super(`[fulgurjs:${n}] ${o}`),this.name="FulgurjsError",this.code=n,this.details=r}},R={REMOTE_LOAD_FAILED:"MFU-001",REMOTE_NAME_MISMATCH:"MFU-002",SHARE_STRICT_VERSION:"MFU-003",SHARE_NOT_AVAILABLE:"MFU-004",CONTAINER_REINIT_CONFLICT:"MFU-005",MODULE_NOT_EXPOSED:"MFU-006",PRELOAD_FAILED:"MFU-007",REMOTE_UNKNOWN:"MFU-008",EMPTY_EXPORTS:"MFU-009",SINGLETON_SKEW:"MFU-010"};var F="0.8.4";var U=15e3,P=2,ce=5,le=3e4;function fe(){let t={},n=new Map,o=[],r=new Map,l=new WeakMap,m={};function y(e){m={...m,...e},typeof globalThis<"u"&&(globalThis.__FULGURJS_APP_CONFIG__=m)}function w(){return m}let $={},D=()=>{for(let e of o)try{e.init?.($)}catch(i){console.warn("[fulgurjs] runtimePlugin init failed:",e.name,i)}},N=e=>t[e]??={};function j(e){try{$.onRemoteError?.(e)}catch{}typeof window<"u"&&window.dispatchEvent(new CustomEvent("fulgurjs:error",{detail:e}))}function H(e="default"){return N(e),t}function K(e,i,a,u,E={}){let s=N(e||"default"),c=s[i]??={};c[a]||(c[a]={version:a,get:u,from:E.from??"unknown",eager:!!E.eager,loaded:!!E.loaded})}function x(e){for(let i of e){let a=n.get(i.name);n.set(i.name,{timeout:U,retries:P,...a,...i,state:a?.state??"idle",breakerState:a?.breakerState??{fails:0,openUntil:0},debug:a?.debug??{entry:i.entry,status:"idle"}})}}function q(e){x([e])}function G(e){o.push(...e),D()}async function z(e,i={}){let a=i.shareScope||"default",u=i.shareKey||e,s=N(a)[u]||{},c=Object.keys(s),d=i.requiredVersion,f=c.filter(p=>d===void 0||d===!1||k(p,d)),b;if(i.singleton&&c.length>0){let p=c.filter(h=>s[h].loaded);if(b=[...p.length?p:c].sort(v).pop(),c.length>1||!f.includes(b)){let h=s[b],T=`singleton skew "${u}": req "${d??"any"}" use ${b} from ${h.from}`;if(i.strictVersion){let M=new g(R.SHARE_STRICT_VERSION,T,{shareKey:u,requiredVersion:d});throw j({remote:h.from,error:M}),M}console.warn(`[fulgurjs:MFU-010] ${T}`)}}else if(b=[...f].sort(v).pop(),!b){if(i.strictVersion){let h=new g(R.SHARE_STRICT_VERSION,`no satisfying version for "${u}" (required "${d}") in share scope "${a}"`);throw j({remote:u,error:h}),h}if(i.fallback)return i.fallback();let p=new g(R.SHARE_NOT_AVAILABLE,`shared "${u}" (${d??"any"}) unavailable in scope "${a}" (no local fallback)`,{shareKey:u,requiredVersion:d});throw j({remote:u,error:p}),p}let _=s[b];if($.resolveShare){let p=await $.resolveShare({shareKey:u,shareScope:a,requiredVersion:d,picked:_,available:Object.values(s)});p&&(_=p)}return _.loaded=!0,_.get()}function J(e,i,a){return new Promise((u,E)=>{let s=setTimeout(()=>E(new Error(`timeout after ${i}ms: ${a}`)),i);e.then(c=>{clearTimeout(s),u(c)},c=>{clearTimeout(s),E(c)})})}let W=e=>new Promise(i=>setTimeout(i,e));function I(e,i){if(!e||typeof e.get!="function")throw new Error(`remoteEntry of "${i.name}" does not export a container interface (get/init)`);if(e.name&&i.name&&e.name!==i.name){let a=`remoteEntry self-reported name "${e.name}" mismatches configured name "${i.name}"`;if(i.promise)console.warn(`[fulgurjs] ${a} (promise-based remote, continuing)`);else throw Object.assign(new Error(a),{code:R.REMOTE_NAME_MISMATCH})}}async function L(e,i){return J(import(e),i.timeout??U,`load remoteEntry ${e}`)}async function C(e,i){let a=e.shareScope||"default";if(e.container){let s=l.get(e.container);if(s&&s!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${s}", cannot re-init with "${a}"`);return e.container}if(e.containerPromise)return e.containerPromise;let u=e.breakerState;if(u.openUntil>Date.now()){let s=new g(R.REMOTE_LOAD_FAILED,`circuit breaker open for remote "${e.name}" until ${new Date(u.openUntil).toISOString()}`,{remote:e.name});throw j({remote:e.name,error:s}),s}e.state="loading",e.debug.status="loading";let E=(async()=>{let s=[];if(e.promise)s.push(async()=>{let p=await e.promise(),h=typeof p=="string"?await L(p,e):p;return I(h,e),h});else for(let p of[e.entry,...e.fallback??[]])s.push(async()=>{let h=await L(p,e);return I(h,e),h});let c,d,f=!1,b=i?.retries??e.retries??P;for(let p of s){for(let h=0;h<=b;h++){let T=performance.now();try{c=await p(),e.lastLoadMs=Math.round(performance.now()-T),e.debug.lastLoadMs=e.lastLoadMs,f=!0;break}catch(M){if(d=M,M?.code)throw M;h<b&&await W(200*2**h)}}if(f)break}if(!f||!c){let p=[];throw/^https?:\/\//.test(e.entry)&&typeof location<"u"?(p.push(`1. is the remote dev server running? try opening ${e.entry} in the browser \u2014 it must return JS, not HTML/an error`),p.push("2. is the URL correct in federation({ remotes })? dev and prod entries can differ"),p.push("3. CORS: the remote dev server must allow cross-origin requests (fulgurjs-federation enables server.cors automatically; a custom server config may have disabled it)")):(p.push(`1. is the remote deployed and reachable? try opening ${e.entry} in the browser \u2014 it must return JS`),p.push("2. NGINX/CDN routing: the entry path must serve the remoteEntry JS file (check try_files / fallback rules)")),new g(R.REMOTE_LOAD_FAILED,`failed to load remote "${e.name}" from ${e.entry}: ${String(d?.message??d)}
1
+ var Q=/^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/;function O(t){let n=Q.exec(String(t).trim());return n?{major:Number(n[1]),minor:Number(n[2]??0),patch:Number(n[3]??0),pre:n[4]?n[4].split("."):[]}:null}function ee(t,n){if(t.length===0&&n.length===0)return 0;if(t.length===0)return 1;if(n.length===0)return-1;let o=Math.max(t.length,n.length);for(let r=0;r<o;r++){let l=t[r],m=n[r];if(l===void 0)return-1;if(m===void 0)return 1;let y=/^\d+$/.test(l),w=/^\d+$/.test(m);if(y&&w){let $=Number(l)-Number(m);if($!==0)return $}else{if(y!==w)return y?-1:1;if(l!==m)return l<m?-1:1}}return 0}function v(t,n){let o=O(t),r=O(n);return!o||!r?t<n?-1:t>n?1:0:o.major!==r.major?o.major-r.major:o.minor!==r.minor?o.minor-r.minor:o.patch!==r.patch?o.patch-r.patch:ee(o.pre,r.pre)}var re=/^[a-z]+[+:-]/i;function k(t,n){if(n===!1||n==null||n==="")return!0;let o=String(n).trim();return!o||o==="*"||o==="latest"||re.test(o)?!0:o.split("||").some(r=>te(t,r.trim()))}function te(t,n){if(!n||n==="*")return!O(t)?.pre.length;let o=n.split(/\s+-\s+/);if(o.length===2){let m=o[0].trim(),y=o[1].trim();return!(!k(t,`>=${m}`)||!k(t,`<=${y}`))}let r=O(t);if(!r)return!1;let l=ne(n);if(l===null)return!0;for(let m of l)if(!ue(r,t,m))return!1;return!(r.pre.length>0&&!l.some(m=>m.ver.pre.length>0)&&!l.some(y=>y.ver.major===r.major&&y.ver.minor===r.minor&&y.ver.patch===r.patch))}function ne(t){let n=[],o=t.trim().split(/\s+/).filter(Boolean);if(o.length===0)return null;for(let r of o){let l=/^(>=|<=|>|<|=|\^|~)?(.*)$/.exec(r);if(!l)return null;let m=l[1],y=l[2],w=oe(m,y);if(w===null)return null;n.push(...w)}return n}function oe(t,n){let o=ae(n);if(!o)return null;let{ver:r,partial:l}=o;switch(t){case"^":{let m=ie(r);return m?[{op:">=",ver:r},{op:"<",ver:m}]:null}case"~":{let m=l>=2?{major:r.major,minor:r.minor+1,patch:0,pre:[]}:{major:r.major+1,minor:0,patch:0,pre:[]};return[{op:">=",ver:r},{op:"<",ver:m}]}case">=":return[{op:">=",ver:r}];case">":return l===1?[{op:">=",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:">",ver:r}];case"<":return l===1?[{op:"<",ver:{major:r.major,minor:0,patch:0,pre:[]}}]:l===2?[{op:"<",ver:{major:r.major,minor:r.minor,patch:0,pre:[]}}]:[{op:"<",ver:r}];case"<=":return[{op:"<=",ver:r}];case"=":case void 0:return l===1?[{op:">=",ver:r},{op:"<",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:r},{op:"<",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:"=",ver:r}];default:return null}}function ie(t){return t.major>0?{major:t.major+1,minor:0,patch:0,pre:[]}:t.minor>0?{major:0,minor:t.minor+1,patch:0,pre:[]}:{major:0,minor:0,patch:t.patch+1,pre:[]}}function ae(t){let n=t.replace(/^v/,"").trim();if(!n||n==="*"||n==="x"||n==="X")return{ver:{major:0,minor:0,patch:0,pre:[]},partial:0};let o=n.split("."),r=[],l=0;for(let w of o.slice(0,3))if(/^\d+$/.test(w))r.push(Number(w)),l++;else{if(w==="x"||w==="X"||w==="*")break;break}let m=n.split("-"),y=m.length>1?m.slice(1).join("-").split("+")[0].split("."):[];return l===0&&y.length===0?{ver:{major:0,minor:0,patch:0,pre:[]},partial:0}:{ver:{major:r[0]??0,minor:r[1]??0,patch:r[2]??0,pre:y},partial:l}}function se(t,n){switch(n){case">=":return t>=0;case">":return t>0;case"<":return t<0;case"<=":return t<=0;case"=":return t===0}}function ue(t,n,o){let r=`${t.major}.${t.minor}.${t.patch}`+(t.pre.length?`-${t.pre.join(".")}`:"");if(o.upper)return!(!(v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:""))>=0)||!(v(r,`${o.upper.major}.${o.upper.minor}.${o.upper.patch}`)<0));let l=v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:"")),m=se(l,o.op);return o.ver.pre.length>0&&t.pre.length===0&&(o.op===">"||o.op==="<")?!1:m}var g=class extends Error{code;details;constructor(n,o,r){super(`[fulgurjs:${n}] ${o}`),this.name="FulgurjsError",this.code=n,this.details=r}},R={REMOTE_LOAD_FAILED:"MFU-001",REMOTE_NAME_MISMATCH:"MFU-002",SHARE_STRICT_VERSION:"MFU-003",SHARE_NOT_AVAILABLE:"MFU-004",CONTAINER_REINIT_CONFLICT:"MFU-005",MODULE_NOT_EXPOSED:"MFU-006",PRELOAD_FAILED:"MFU-007",REMOTE_UNKNOWN:"MFU-008",EMPTY_EXPORTS:"MFU-009",SINGLETON_SKEW:"MFU-010"};var F="0.9.1-test.0";var U=15e3,P=2,ce=5,le=3e4;function fe(){let t={},n=new Map,o=[],r=new Map,l=new WeakMap,m={};function y(e){m={...m,...e},typeof globalThis<"u"&&(globalThis.__FULGURJS_APP_CONFIG__=m)}function w(){return m}let $={},D=()=>{for(let e of o)try{e.init?.($)}catch(i){console.warn("[fulgurjs] runtimePlugin init failed:",e.name,i)}},N=e=>t[e]??={};function j(e){try{$.onRemoteError?.(e)}catch{}typeof window<"u"&&window.dispatchEvent(new CustomEvent("fulgurjs:error",{detail:e}))}function H(e="default"){return N(e),t}function K(e,i,a,u,E={}){let s=N(e||"default"),c=s[i]??={};c[a]||(c[a]={version:a,get:u,from:E.from??"unknown",eager:!!E.eager,loaded:!!E.loaded})}function x(e){for(let i of e){let a=n.get(i.name);n.set(i.name,{timeout:U,retries:P,...a,...i,state:a?.state??"idle",breakerState:a?.breakerState??{fails:0,openUntil:0},debug:a?.debug??{entry:i.entry,status:"idle"}})}}function q(e){x([e])}function G(e){o.push(...e),D()}async function z(e,i={}){let a=i.shareScope||"default",u=i.shareKey||e,s=N(a)[u]||{},c=Object.keys(s),d=i.requiredVersion,f=c.filter(p=>d===void 0||d===!1||k(p,d)),b;if(i.singleton&&c.length>0){let p=c.filter(h=>s[h].loaded);if(b=[...p.length?p:c].sort(v).pop(),c.length>1||!f.includes(b)){let h=s[b],T=`singleton skew "${u}": req "${d??"any"}" use ${b} from ${h.from}`;if(i.strictVersion){let M=new g(R.SHARE_STRICT_VERSION,T,{shareKey:u,requiredVersion:d});throw j({remote:h.from,error:M}),M}console.warn(`[fulgurjs:MFU-010] ${T}`)}}else if(b=[...f].sort(v).pop(),!b){if(i.strictVersion){let h=new g(R.SHARE_STRICT_VERSION,`no satisfying version for "${u}" (required "${d}") in share scope "${a}"`);throw j({remote:u,error:h}),h}if(i.fallback)return i.fallback();let p=new g(R.SHARE_NOT_AVAILABLE,`shared "${u}" (${d??"any"}) unavailable in scope "${a}" (no local fallback)`,{shareKey:u,requiredVersion:d});throw j({remote:u,error:p}),p}let _=s[b];if($.resolveShare){let p=await $.resolveShare({shareKey:u,shareScope:a,requiredVersion:d,picked:_,available:Object.values(s)});p&&(_=p)}return _.loaded=!0,_.get()}function J(e,i,a){return new Promise((u,E)=>{let s=setTimeout(()=>E(new Error(`timeout after ${i}ms: ${a}`)),i);e.then(c=>{clearTimeout(s),u(c)},c=>{clearTimeout(s),E(c)})})}let W=e=>new Promise(i=>setTimeout(i,e));function I(e,i){if(!e||typeof e.get!="function")throw new Error(`remoteEntry of "${i.name}" does not export a container interface (get/init)`);if(e.name&&i.name&&e.name!==i.name){let a=`remoteEntry self-reported name "${e.name}" mismatches configured name "${i.name}"`;if(i.promise)console.warn(`[fulgurjs] ${a} (promise-based remote, continuing)`);else throw Object.assign(new Error(a),{code:R.REMOTE_NAME_MISMATCH})}}async function L(e,i){return J(import(e),i.timeout??U,`load remoteEntry ${e}`)}async function C(e,i){let a=e.shareScope||"default";if(e.container){let s=l.get(e.container);if(s&&s!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${s}", cannot re-init with "${a}"`);return e.container}if(e.containerPromise)return e.containerPromise;let u=e.breakerState;if(u.openUntil>Date.now()){let s=new g(R.REMOTE_LOAD_FAILED,`circuit breaker open for remote "${e.name}" until ${new Date(u.openUntil).toISOString()}`,{remote:e.name});throw j({remote:e.name,error:s}),s}e.state="loading",e.debug.status="loading";let E=(async()=>{let s=[];if(e.promise)s.push(async()=>{let p=await e.promise(),h=typeof p=="string"?await L(p,e):p;return I(h,e),h});else for(let p of[e.entry,...e.fallback??[]])s.push(async()=>{let h=await L(p,e);return I(h,e),h});let c,d,f=!1,b=i?.retries??e.retries??P;for(let p of s){for(let h=0;h<=b;h++){let T=performance.now();try{c=await p(),e.lastLoadMs=Math.round(performance.now()-T),e.debug.lastLoadMs=e.lastLoadMs,f=!0;break}catch(M){if(d=M,M?.code)throw M;h<b&&await W(200*2**h)}}if(f)break}if(!f||!c){let p=[];throw/^https?:\/\//.test(e.entry)&&typeof location<"u"?(p.push(`1. is the remote dev server running? try opening ${e.entry} in the browser \u2014 it must return JS, not HTML/an error`),p.push("2. is the URL correct in federation({ remotes })? dev and prod entries can differ"),p.push("3. CORS: the remote dev server must allow cross-origin requests (fulgurjs-federation enables server.cors automatically; a custom server config may have disabled it)")):(p.push(`1. is the remote deployed and reachable? try opening ${e.entry} in the browser \u2014 it must return JS`),p.push("2. NGINX/CDN routing: the entry path must serve the remoteEntry JS file (check try_files / fallback rules)")),new g(R.REMOTE_LOAD_FAILED,`failed to load remote "${e.name}" from ${e.entry}: ${String(d?.message??d)}
2
2
  `+p.join(`
3
3
  `),{remote:e.name})}I(c,e);let _=l.get(c);if(_&&_!==a)throw new g(R.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${_}", cannot re-init with "${a}"`);return typeof c.init=="function"&&await c.init(t),l.set(c,a),e.container=c,e.state="loaded",e.debug.status="loaded",u.fails=0,c})();e.containerPromise=E;try{return await E}catch(s){if(e.containerPromise=void 0,e.state="failed",e.debug.status="failed",e.debug.error=String(s?.message??s),u.fails+=1,u.fails>=ce&&(u.openUntil=Date.now()+le,u.fails=0),s instanceof g)throw j({remote:e.name,error:s}),s;if(s?.code)throw j({remote:e.name,error:s}),s;let c=new g(R.REMOTE_LOAD_FAILED,String(s?.message??s),{remote:e.name});throw j({remote:e.name,error:c}),c}}function A(e){let i=e.indexOf("/");return i===-1?{remote:e,module:""}:{remote:e.slice(0,i),module:B(e.slice(i+1))}}function B(e){return e?e.startsWith(".")?e:`./${e}`:""}async function X(e,i){let{remote:a,module:u}=A(e);$.beforeLoadRemote?.({remote:a,module:u});let E=n.get(a);if(!E)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}". Register it via federation({ remotes }) or registerRemote().`,{remote:a});let s;try{s=await C(E,{retries:i?.retries})}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`,f),j({remote:a,error:f}),await i.fallbackModule();throw f}if(!u)return s;let c=`${a}@${E.shareScope||"default"}#${u}`;r.has(c)||r.set(c,s.get(u).catch(f=>{throw r.delete(c),f instanceof g||f?.code?f:new g(R.REMOTE_LOAD_FAILED,`failed to load module "${u}" from remote "${a}": ${String(f?.message??f)}`,{remote:a,module:u})}));let d;try{d=await r.get(c)}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`,f),j({remote:a,error:f}),await i.fallbackModule();throw f}if($.afterLoadRemote?.({remote:a,module:u,module_ns:d}),d&&typeof d=="object"&&Object.keys(d).length===0){let f=new g(R.EMPTY_EXPORTS,`no exports: ${u} @ ${a}`,{remote:a,module:u});j({remote:a,error:f}),console.error(`[fulgurjs:MFU-009] ${f.message}`)}return d}function Y(e){let i=n.get(e);if(!i)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${e}"`,{remote:e});return C(i)}async function Z(e,i={}){let{remote:a}=A(e),u=n.get(a);if(!u)throw new g(R.REMOTE_UNKNOWN,`unknown remote "${a}"`,{remote:a});let E=i.mode??"preload",s=(c,d)=>{if(typeof document>"u"||document.querySelector(`link[href="${c}"]`))return;let f=document.createElement("link");f.rel=d==="style"?"stylesheet":"modulepreload",f.href=c,E==="prefetch"&&(f.fetchPriority="low"),document.head.appendChild(f)};try{if(u.manifestUrl){let c=await fetch(u.manifestUrl,{cache:"no-cache"});if(c.ok){let d=await c.json();s(u.entry,"modulepreload");let f=d.exposes??{};for(let b of Object.keys(f)){let _=f[b];_?.file&&s(new URL(_.file,u.entry).href,"modulepreload");for(let p of _?.css??[])s(new URL(p,u.entry).href,"style")}return}}s(u.entry,"modulepreload")}catch(c){j({remote:a,error:new g(R.PRELOAD_FAILED,String(c?.message??c),{remote:a})})}}return typeof window>"u"||(window.__FULGURJS_SCOPE__=t,window.__FULGURJS_INFO__={get remotes(){let e={};for(let[i,a]of n)e[i]={...a.debug,entry:a.entry};return e},errors:[]}),{shareScopeMap:t,initSharing:H,registerShare:K,registerRemotes:x,registerRemote:q,registerPlugins:G,loadShare:z,loadRemote:X,getContainer:Y,preloadRemote:Z,parseSpec:A,provideFulgurjsAppConfig:y,getFulgurjsAppConfig:w}}var V=globalThis,S=V.__FULGURJS_RUNTIME__??fe();V.__FULGURJS_RUNTIME__=S;try{Object.freeze(S)}catch{}var Re=F;function Se(){return S}var we=S.initSharing,be=S.registerShare,_e=S.registerRemotes,je=S.registerRemote,$e=S.registerPlugins,Me=S.loadShare,ve=S.loadRemote,Te=S.getContainer,Oe=S.preloadRemote,ke=S.shareScopeMap,Ne=S.provideFulgurjsAppConfig,Ie=S.getFulgurjsAppConfig;function Ae(t){if(t&&typeof t=="object"&&"default"in t){let n=t.default;return n!==void 0?n:t}return t}var xe=S;export{xe as default,Te as getContainer,Ie as getFulgurjsAppConfig,Se as getRuntime,we as initSharing,ve as loadRemote,Me as loadShare,Oe as preloadRemote,Ne as provideFulgurjsAppConfig,$e as registerPlugins,je as registerRemote,_e as registerRemotes,be as registerShare,S as runtime,ke as shareScopeMap,Ae as unwrapDefault,Re as version};
package/dist/vue.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  ErrorCodes,
3
3
  FulgurjsError
4
4
  } from "./chunk-CK42RK2O.js";
5
- import "./chunk-WX5CMQP5.js";
5
+ import "./chunk-2VPHH4XD.js";
6
6
 
7
7
  // src/vue.ts
8
8
  import { defineAsyncComponent, defineComponent, h } from "vue";
@@ -137,7 +137,7 @@ onUnmounted(() => {
137
137
  |---|---|---|---|---|
138
138
  | 页面保活(多 tab 状态保留) | `keepAlive` | `boolean` | `false` | 页面路由表条目(`src/fulgurjs/host/pages.ts`;集成器项目改 `fulgurjs.config.ts` 后**删已生成产物**重跑 init) |
139
139
  | 页面加载骨架屏 | —(内置,无配置项) | — | delay 200ms 的骨架占位 | 页面工厂内置(`pages.ts`) |
140
- | 空闲预载 | `PREFETCH_REMOTES` | `string[]` | 全部 remotes 键 | `src/fulgurjs/host/bridge.ts` 顶部常量 |
140
+ | 空闲预载 | `host.prefetch`(集成器项目)/ `PREFETCH_REMOTES`(手工项目常量) | `'all' \| string[] \| false` | `'all'`(全部 remotes 键) | `fulgurjs.config.ts` / `bridge.ts` 顶部 |
141
141
  | 联邦诊断面板 | —(内置页面) | — | 常驻 | 路由 `/fulgurjs-demo`(prod 为 `/main/fulgurjs-demo`) |
142
142
 
143
143
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fulgurjs/federation",
3
- "version": "0.8.4",
3
+ "version": "0.9.1-test.0",
4
4
  "type": "module",
5
5
  "description": "Vite Module Federation with full Webpack MF parity. Dev & production, no compromise.",
6
6
  "license": "MIT",
@@ -62,18 +62,18 @@
62
62
  "CHANGELOG.md",
63
63
  "DESIGN.md",
64
64
  "examples",
65
- "docs/manual.html",
66
- "docs/迁移指南.md",
67
- "docs/webpack-mf-对照与缺口.md",
68
- "docs/沙箱边界审计.md",
69
- "docs/P5-vite7-8兼容矩阵.md",
65
+ "docs/\u8fc1\u79fb\u6307\u5357.md",
66
+ "docs/webpack-mf-\u5bf9\u7167\u4e0e\u7f3a\u53e3.md",
67
+ "docs/\u6c99\u7bb1\u8fb9\u754c\u5ba1\u8ba1.md",
68
+ "docs/P5-vite7-8\u517c\u5bb9\u77e9\u9635.md",
70
69
  "docs/vite-upstream-issue-irregexp.md"
71
70
  ],
72
71
  "scripts": {
73
- "build": "cp ../../README.md README.md && mkdir -p docs && cp ../../docs/manual.html ../../docs/迁移指南.md ../../docs/webpack-mf-对照与缺口.md ../../docs/沙箱边界审计.md ../../docs/P5-vite7-8兼容矩阵.md ../../docs/vite-upstream-issue-irregexp.md docs/ && cp ../../CHANGELOG.md CHANGELOG.md && cp ../../DESIGN.md DESIGN.md && rm -rf examples && cp -r ../../examples examples && esbuild src/runtime/index.ts --bundle --format=esm --target=es2022 --minify --outfile=dist/runtime.js && node scripts/gen-runtime.mjs && tsup src/index.ts src/pages.ts src/config.ts src/vue.ts src/context.ts --format esm,cjs --dts --external vite --target node18 --clean && esbuild src/cli.ts --bundle --format=esm --target=node18 --platform=node --outfile=dist/cli.js && esbuild src/runtime/index.ts --bundle --format=esm --target=es2022 --minify --outfile=dist/runtime.js",
72
+ "build": "cp ../../README.md README.md && mkdir -p docs && cp ../../docs/\u8fc1\u79fb\u6307\u5357.md ../../docs/webpack-mf-\u5bf9\u7167\u4e0e\u7f3a\u53e3.md ../../docs/\u6c99\u7bb1\u8fb9\u754c\u5ba1\u8ba1.md ../../docs/P5-vite7-8\u517c\u5bb9\u77e9\u9635.md ../../docs/vite-upstream-issue-irregexp.md docs/ && cp ../../CHANGELOG.md CHANGELOG.md && cp ../../DESIGN.md DESIGN.md && rm -rf examples && cp -r ../../examples examples && esbuild src/runtime/index.ts --bundle --format=esm --target=es2022 --minify --outfile=dist/runtime.js && node scripts/gen-runtime.mjs && tsup src/index.ts src/pages.ts src/config.ts src/vue.ts src/context.ts --format esm,cjs --dts --external vite --target node18 --clean && esbuild src/cli.ts --bundle --format=esm --target=node18 --platform=node --outfile=dist/cli.js && esbuild src/runtime/index.ts --bundle --format=esm --target=es2022 --minify --outfile=dist/runtime.js && node scripts/check-gzip.mjs",
74
73
  "dev": "tsup --watch",
75
74
  "test": "vitest run",
76
- "typecheck": "tsc --noEmit"
75
+ "typecheck": "tsc --noEmit",
76
+ "typecheck:latest": "node scripts/typecheck-latest.mjs"
77
77
  },
78
78
  "dependencies": {
79
79
  "es-module-lexer": "^1.6.0",
package/docs/manual.html DELETED
@@ -1,467 +0,0 @@
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">
6
- <title>@fulgurjs/federation 使用手册 · Vite 模块联邦(全量对齐 Webpack MF)</title>
7
- <style>
8
- :root { --blue:#1677ff; --bg:#f6f8fa; --border:#e4e7ed; --text:#24292f; }
9
- * { box-sizing: border-box; }
10
- body { font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif; color: var(--text); margin: 0; background: #fff; line-height: 1.7; }
11
- .wrap { max-width: 1080px; margin: 0 auto; padding: 24px 32px 96px; }
12
- h1 { font-size: 28px; border-bottom: 2px solid var(--blue); padding-bottom: 12px; }
13
- h2 { font-size: 22px; margin-top: 48px; border-left: 5px solid var(--blue); padding-left: 10px; }
14
- h3 { font-size: 17px; margin-top: 28px; }
15
- code, pre { font-family: "SF Mono", Menlo, Consolas, monospace; font-size: 13px; }
16
- pre { background: #0d1117; color: #e6edf3; padding: 14px 16px; border-radius: 8px; overflow-x: auto; line-height: 1.5; }
17
- code:not(pre code) { background: var(--bg); border: 1px solid var(--border); border-radius: 4px; padding: 1px 6px; }
18
- table { border-collapse: collapse; width: 100%; margin: 12px 0; font-size: 13.5px; }
19
- th, td { border: 1px solid var(--border); padding: 7px 10px; text-align: left; vertical-align: top; }
20
- th { background: var(--bg); }
21
- .ok { color: #1a7f37; font-weight: 600; }
22
- .warn { color: #9a6700; font-weight: 600; }
23
- .shot { max-width: 100%; border: 1px solid var(--border); border-radius: 8px; margin: 8px 0; box-shadow: 0 2px 8px rgba(0,0,0,.06); }
24
- .shot-row { display: flex; gap: 12px; flex-wrap: wrap; }
25
- .shot-row figure { margin: 0; flex: 1 1 320px; }
26
- figcaption { font-size: 12.5px; color: #57606a; margin-top: 4px; }
27
- .badge { display: inline-block; background: var(--blue); color: #fff; border-radius: 10px; padding: 0 10px; font-size: 12px; margin-left: 6px; }
28
- nav.toc { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 14px 20px; columns: 2; font-size: 14px; }
29
- nav.toc a { text-decoration: none; color: var(--blue); }
30
- .note { background: #fff8e6; border: 1px solid #f0d47a; border-radius: 8px; padding: 10px 14px; margin: 10px 0; font-size: 14px; }
31
- .danger { background: #fff1f0; border: 1px solid #ffa39e; border-radius: 8px; padding: 10px 14px; margin: 10px 0; font-size: 14px; }
32
- </style>
33
- </head>
34
- <body>
35
- <div class="wrap">
36
-
37
- <h1>@fulgurjs/federation 使用手册 <span class="badge">v0.1.0</span></h1>
38
- <p><b>Vite 模块联邦插件:开发环境与生产环境都能用,配置面与运行时行为全量对齐 Webpack Module Federation。</b>
39
- 本手册中的全部截图均为真实浏览器测试(Playwright + Chromium)自动留证,测试基座包含
40
- <a href="#tb">示例工程 真实项目副本</a>(qiankun 微前端仓库)与独立 fixture 工程。</p>
41
-
42
- <nav class="toc">
43
- <b>目录</b><br>
44
- <a href="#compare">1. 与 Webpack 原版对齐总表</a><br>
45
- <a href="#quick">2. 快速开始</a><br>
46
- <a href="#arch">3. 工作原理(dev / prod)</a><br>
47
- <a href="#feat">4. 功能章节(含实测截图)</a><br>
48
- <a href="#tb">5. 示例工程 真实项目实战</a><br>
49
- <a href="#hmr">6. HMR 全链路(L1/L2/L3)</a><br>
50
- <a href="#limit">7. 已知差异与限制(诚实版)</a><br>
51
- <a href="#faq">8. 故障排查(错误码 MFU-0xx)</a><br>
52
- <a href="#test">9. 测试覆盖与复现方式</a>
53
- </nav>
54
-
55
- <h2 id="compare">1. 与 Webpack 原版对齐总表</h2>
56
- <p>结论先行:<b>在 Vite↔Vite 场景下,配置面与运行时行为 100% 对齐 webpack ModuleFederationPlugin</b>;
57
- 唯二例外为 <code>remoteType: 'script'/'var'</code>(跨打包器互操作,P3 里程碑)与 Chrome DevTools 扩展(用
58
- <code>window.__FULGURJS_SCOPE__</code> 调试出口替代)。</p>
59
-
60
- <h3>1.1 配置面(webpack 官方全选项)</h3>
61
- <table>
62
- <tr><th>webpack 选项</th><th>fulgurjs</th><th>说明</th></tr>
63
- <tr><td><code>name</code> / <code>filename</code></td><td class="ok">✅ 一致</td><td>容器名 + remoteEntry 稳定文件名(默认 <code>fulgurjs-remoteEntry.js</code>)</td></tr>
64
- <tr><td><code>exposes</code> 字符串 / 对象 <code>{import, name}</code></td><td class="ok">✅ 一致</td><td>对象形式 name = 稳定 chunk 名</td></tr>
65
- <tr><td><code>remotes</code> 的 <code>name@url</code> 语法</td><td class="ok">✅ 一致</td><td>@ 前自报名校验;键重命名(checkout: shop@…)</td></tr>
66
- <tr><td><code>remotes</code> 对象 <code>{external, shareScope}</code></td><td class="ok">✅ 一致</td><td>另支持 <code>dev</code>/<code>prod</code> 地址拆分(webpack 没有的增强)</td></tr>
67
- <tr><td>promise-based remote</td><td class="ok">✅ 一致</td><td>函数形式无法静态序列化,按 webpack 语义在运行时 <code>registerRemote()</code> 注册</td></tr>
68
- <tr><td><code>shared</code> 数组 / semver 简写 / 完整 hint</td><td class="ok">✅ 一致</td><td>9 个 hint 全支持:eager / import / packageName / requiredVersion / shareKey / shareScope / singleton / strictVersion / version</td></tr>
69
- <tr><td><code>requiredVersion</code> 完整 semver 语法</td><td class="ok">✅ 一致</td><td>精确/部分/^/~/比较器/连字符/||/URL 形式(URL 视为接受任意)</td></tr>
70
- <tr><td><code>strictVersion</code> 默认值</td><td class="ok">✅ 一致</td><td>有本地副本且非 singleton → true</td></tr>
71
- <tr><td><code>shareScope</code> 多作用域</td><td class="ok">✅ 一致</td><td>命名空间互相隔离</td></tr>
72
- <tr><td><code>eager</code></td><td class="ok">✅ 一致</td><td>初始 chunk 可用 + 总被下载</td></tr>
73
- <tr><td><code>runtime</code> / <code>runtimeChunk</code></td><td class="ok">✅ 一致</td><td>运行时与 init 独立产物、稳定命名</td></tr>
74
- <tr><td><code>manifest</code></td><td class="ok">✅</td><td>fulgurjs-manifest.json(部署回滚 = 切 manifest 指针)</td></tr>
75
- <tr><td><code>runtimePlugins</code></td><td class="ok">✅</td><td>resolveShare / beforeLoadRemote / afterLoadRemote / onRemoteError 钩子</td></tr>
76
- <tr><td><code>dts</code></td><td class="ok">✅</td><td>dev 源码级类型直连</td></tr>
77
- <tr><td><code>automaticAsyncBoundary</code> / <code>dataPrefetch</code> / <code>usedExports</code></td><td class="warn">🔶 增强</td><td>TLA 自动异步边界(无需手工 bootstrap)、preloadRemote 常驻、Rollup 原生 tree-shaking</td></tr>
78
- <tr><td><code>remoteType: 'script'/'var'</code></td><td class="warn">🔶 P3</td><td>跨打包器互操作(webpack 宿主消费 Vite remote),首发不承诺</td></tr>
79
- <tr><td>Chrome DevTools 扩展</td><td class="warn">🔶 替代</td><td><code>window.__FULGURJS_SCOPE__ / __FULGURJS_INFO__</code> 调试出口</td></tr>
80
- </table>
81
-
82
- <h3>1.2 运行时行为语义(18 条逐条 e2e 验收)</h3>
83
- <table>
84
- <tr><th>#</th><th>webpack 行为</th><th>fulgurjs</th></tr>
85
- <tr><td>1</td><td>协商取满足 requiredVersion 的<b>最高版本</b></td><td class="ok">✅</td></tr>
86
- <tr><td>2</td><td>非 singleton 多版本共存</td><td class="ok">✅</td></tr>
87
- <tr><td>3</td><td>已注册/已加载版本永不替换(first-wins)</td><td class="ok">✅</td></tr>
88
- <tr><td>4</td><td>同版本按 uniqueName 决胜</td><td class="ok">✅</td></tr>
89
- <tr><td>5</td><td>init() 收养语义:双向供给、兄弟 remote 互享、循环守卫</td><td class="ok">✅</td></tr>
90
- <tr><td>6</td><td>同一容器换 scope 重复 init → 抛错</td><td class="ok">✅</td></tr>
91
- <tr><td>7</td><td>容器 API <code>get/init</code> 可手写协议直用</td><td class="ok">✅</td></tr>
92
- <tr><td>8</td><td>singleton 冲突 → warning + 用已注册唯一实例</td><td class="ok">✅</td></tr>
93
- <tr><td>9</td><td>strictVersion 不满足 → 抛错(MFU-003)</td><td class="ok">✅</td></tr>
94
- <tr><td>10</td><td><code>import: false</code> 无匹配 → 抛错(MFU-004);有 fallback → 用本地副本</td><td class="ok">✅</td></tr>
95
- <tr><td>11</td><td>shareKey 重定向(lodash-es ↔ lodash)</td><td class="ok">✅</td></tr>
96
- <tr><td>12</td><td>多 shareScope 隔离</td><td class="ok">✅</td></tr>
97
- <tr><td>13</td><td>eager:初始 chunk 可用 + 总被下载</td><td class="ok">✅</td></tr>
98
- <tr><td>14</td><td>动态 remote(运行时注册)+ promise remote</td><td class="ok">✅</td></tr>
99
- <tr><td>15</td><td>静态 remote 自动加载;失败 → MFU-001 错误码体系</td><td class="ok">✅</td></tr>
100
- <tr><td>16</td><td><code>import('app/Button')</code> 语法 + default/named unwrap</td><td class="ok">✅</td></tr>
101
- <tr><td>17</td><td>exposed 模块样式自动注入(dev style / prod css chunk)</td><td class="ok">✅</td></tr>
102
- <tr><td>18</td><td>异步边界:TLA 自动化(比 webpack 手工 bootstrap 更进一步)</td><td class="ok">✅</td></tr>
103
- </table>
104
-
105
- <h2 id="quick">2. 快速开始</h2>
106
- <h3>2.1 安装</h3>
107
- <pre>pnpm add -D @fulgurjs/federation</pre>
108
-
109
- <h3>2.2 远程提供方(remote)</h3>
110
- <pre>// vite.config.ts
111
- import { defineConfig } from 'vite'
112
- import vue from '@vitejs/plugin-vue'
113
- import { federation } from '@fulgurjs/federation'
114
-
115
- export default defineConfig({
116
- plugins: [
117
- vue(),
118
- federation({
119
- name: 'remote-a',
120
- filename: 'fulgurjs-remoteEntry.js',
121
- exposes: {
122
- './Button': './src/exposes/Button.vue',
123
- './utils': './src/exposes/utils.ts',
124
- },
125
- shared: {
126
- vue: { singleton: true, requiredVersion: '^3.4.0' },
127
- pinia: { singleton: true },
128
- },
129
- }),
130
- ],
131
- })</pre>
132
-
133
- <h3>2.3 宿主(host)</h3>
134
- <pre>federation({
135
- name: 'host-app',
136
- remotes: {
137
- // 单地址:dev 自动拼 @fulgurjs-entry.js,prod 自动拼 remoteEntry 文件名
138
- 'remote-a': 'http://localhost:5101',
139
- // 显式拆分(推荐用于部署)
140
- 'remote-b': { dev: 'http://localhost:5102', prod: 'https://cdn.example.com/remote-b/' },
141
- // webpack 语法 + 键重命名
142
- shop: 'remote-a@http://localhost:5101',
143
- },
144
- shared: { vue: { singleton: true }, pinia: { singleton: true } },
145
- })</pre>
146
-
147
- <h3>2.4 业务代码(与 Webpack 完全同款)</h3>
148
- <pre>// 动态导入(推荐)
149
- const mod = await import('remote-a/Button')
150
- const Btn = mod.default
151
-
152
- // 命名/默认/别名
153
- const { formatDate } = await import('remote-a/utils')
154
-
155
- // 静态导入(编译为 TLA 自动异步边界,无需手工 bootstrap.js)
156
- import Btn from 'remote-a/Button'</pre>
157
-
158
- <h2 id="arch">3. 工作原理(dev / prod 双引擎)</h2>
159
- <table>
160
- <tr><th>环境</th><th>引擎</th><th>机制</th></tr>
161
- <tr><td><b>dev</b></td><td>双 dev-server 协作</td>
162
- <td>remote 的 dev server 暴露 <code>/@fulgurjs-entry.js</code>(自包含容器)与 <code>/@fulgurjs-manifest.json</code>;
163
- 宿主页面经运行时 <code>container.init(shareScopeMap)</code> 收养共享作用域;remote 模块的 shared 导入经
164
- <b>绑定门面</b>(按需生成的虚拟模块)协商到宿主实例;HMR 通过 remote 的 vite client 直连宿主页面。</td></tr>
165
- <tr><td><b>prod</b></td><td>构建期改写(Rollup/Rolldown)</td>
166
- <td>每个 expose 生成虚拟入口 → 独立 chunk;shared 经动态导入边界自动剥离;产出稳定文件名的
167
- <code>fulgurjs-remoteEntry.js</code> + <code>fulgurjs-manifest.json</code>;宿主入口前置 init import 注册 remotes/provides。</td></tr>
168
- </table>
169
- <div class="note">运行时内核 gzip 约 <b>5.0KB</b>(对标 @module-federation/runtime 的 40KB+),页面级单例:
170
- 多应用各自打包的运行时副本经 <code>globalThis.__FULGURJS_RUNTIME__</code> 合一。
171
- 跨源取运行时的官方入口:<code>getRuntime()</code>(独立产物导出,与全局单例同一实例),
172
- 单例带 <code>version</code> 字段(与插件包版本同源)用于副本一致性诊断;方法面已冻结防意外覆写。
173
- 注意:exposes 目标文件(远程页面)禁止静态导入 <code>virtual:fulgurjs-runtime</code>,插件 dev 下会显式报错(见 §8)。</div>
174
-
175
- <h2 id="feat">4. 功能章节(含实测截图)</h2>
176
-
177
- <h3>4.1 远程组件加载(webpack 同款 import 用法)</h3>
178
- <pre>const mod = await import('remote-a/Button')</pre>
179
- <div class="shot-row">
180
- <figure><img class="shot" src="screenshots/dev-load-remote-button.png"><figcaption>dev:宿主页面加载 remote-a/Button 并渲染</figcaption></figure>
181
- <figure><img class="shot" src="screenshots/prod-load-remote-button.png"><figcaption>prod(NGINX 8662/8999):同一段代码</figcaption></figure>
182
- </div>
183
-
184
- <h3>4.2 工具模块 default / named / 常量语义</h3>
185
- <div class="shot-row">
186
- <figure><img class="shot" src="screenshots/dev-remote-utils-semantics.png"><figcaption>dev</figcaption></figure>
187
- <figure><img class="shot" src="screenshots/prod-remote-utils-semantics.png"><figcaption>prod</figcaption></figure>
188
- </div>
189
-
190
- <h3>4.3 双 vue 版本共存 + shared 单例</h3>
191
- <p>remote-a 用 vue 3.5.x,remote-b 以 <code>vue34</code> 别名提供 vue 3.4.38(shareKey 均为 vue)。
192
- 两卡片同时渲染,且 <code>isReactive(host 的 reactive 对象) === true</code> 证明单实例;网络面板 vue 预构建产物全页仅一次请求。</p>
193
- <div class="shot-row">
194
- <figure><img class="shot" src="screenshots/dev-multi-dual-version-shared-singleton.png"><figcaption>dev:双版本共存 + 单例</figcaption></figure>
195
- <figure><img class="shot" src="screenshots/prod-multi-dual-version-shared-singleton.png"><figcaption>prod</figcaption></figure>
196
- </div>
197
-
198
- <h3>4.4 Share Scope 协商可视化(调试出口)</h3>
199
- <p>宿主任意页面可用 <code>window.__FULGURJS_SCOPE__</code> / <code>__FULGURJS_INFO__</code> 查看协商结果与加载状态。</p>
200
- <img class="shot" src="screenshots/dev-scope-debug-panel.png">
201
-
202
- <h3>4.5 remotes 键重命名(checkout: shop@ 语法)</h3>
203
- <div class="shot-row">
204
- <figure><img class="shot" src="screenshots/dev-remote-key-rename.png"><figcaption>dev</figcaption></figure>
205
- <figure><img class="shot" src="screenshots/prod-remote-key-rename.png"><figcaption>prod</figcaption></figure>
206
- </div>
207
-
208
- <h3>4.6 promise-based remote(运行时注册)</h3>
209
- <pre>// 构建时未知 URL:运行时注册(webpack 'promise new Promise' 的等价能力)
210
- rt.registerRemote({
211
- name: 'promise-remote',
212
- promise: async () => ({ name: 'promise-remote', init: async (map) => {...}, get: async (m) => {...} }),
213
- })
214
- const mod = await import('promise-remote/Button')</pre>
215
- <img class="shot" src="screenshots/dev-promise-based-remote.png">
216
-
217
- <h3>4.7 eager shared(初始 chunk 可用)</h3>
218
- <pre>shared: { pinia: { singleton: true, eager: true } }</pre>
219
- <img class="shot" src="screenshots/prod-eager-pinia-initial-download.png">
220
- <figcaption>prod:pinia chunk 出现在首屏 HTML 的 modulepreload 中(未做任何交互)</figcaption>
221
-
222
- <h3>4.8 错误码与错误边界(MFU-006)</h3>
223
- <div class="shot-row">
224
- <figure><img class="shot" src="screenshots/dev-mfu006-module-not-exposed.png"><figcaption>dev:CAUGHT MFU-006</figcaption></figure>
225
- <figure><img class="shot" src="screenshots/prod-mfu006-module-not-exposed.png"><figcaption>prod:行为一致</figcaption></figure>
226
- </div>
227
-
228
- <h3>4.9 远程组件样式注入</h3>
229
- <div class="shot-row">
230
- <figure><img class="shot" src="screenshots/dev-remote-css-injection.png"><figcaption>dev:style 注入</figcaption></figure>
231
- <figure><img class="shot" src="screenshots/prod-remote-css-injection.png"><figcaption>prod:css chunk 注入</figcaption></figure>
232
- </div>
233
-
234
- <h3>4.10 容错:kill remote → MFU-001 → 恢复</h3>
235
- <img class="shot" src="screenshots/dev-fault-remote-killed-mfu001.png">
236
- <img class="shot" src="screenshots/dev-fault-recovered.png">
237
-
238
- <h3>4.11 pinia 共享单例</h3>
239
- <img class="shot" src="screenshots/dev-pinia-shared-singleton.png">
240
-
241
- <h3>4.12 dts 类型直连(dev 源码级补全)</h3>
242
- <p>宿主 dev server 启动时自动拉取各远程的 dev manifest,为每个 exposes 生成 <code>declare module</code> 声明,
243
- 映射到远程本机源码——补全与跳转都是源码级。产物写入 <code>src/fulgurjs-types/&lt;remote&gt;.d.ts</code>,
244
- 确保 tsconfig include 包含该目录即可。</p>
245
- <pre>declare module 'remote-a/Button' {
246
- import type { DefineComponent } from 'vue'
247
- const component: DefineComponent&lt;Record&lt;string, unknown&gt;, Record&lt;string, unknown&gt;, unknown&gt;
248
- export default component
249
- export * from '../../../remote-a/src/exposes/Button.vue'
250
- }</pre>
251
- <img class="shot" src="screenshots/dev-dts-type-link.png">
252
- <figcaption>实测:host-vue 启动后自动生成 remote-a / remote-b / shop 三份类型声明(5 + 2 + 5 个 exposes)</figcaption>
253
-
254
- <h3>4.13 preloadRemote(manifest 驱动精确预载)</h3>
255
- <pre>import { preloadRemote } from 'virtual:fulgurjs-runtime' // 插件已全局注入
256
- await preloadRemote('remote-a') // 按 manifest 精确预载全部 exposes chunk + CSS
257
- await preloadRemote('remote-a', { mode: 'prefetch' }) // 低优先级预取</pre>
258
- <p>实测注入的 modulepreload 链接(dev):容器入口 + 全部 5 个 exposes 的源模块——
259
- 不做盲猜,全部来自 manifest 清单。</p>
260
- <img class="shot" src="screenshots/dev-preload-remote.png">
261
-
262
- <h2 id="tb">5. 示例工程 真实项目实战(qiankun 仓库 + 模块联邦平行通道)</h2>
263
- <p>复制 示例工程(三应用 monorepo) 到插件 testbed,保持 <b>qiankun 集成零改动</b>,新增联邦平行通道:
264
- 示例宿主应用(Vite 6.4.3)作为宿主,示例 bpm 子应用(Vite 5.1.4,/flowable)与 示例 lowcode 子应用(Vite 5.2.12,/lowcode)
265
- 作为远程。免登录演示路由 <code>/main/fulgurjs-demo</code>(meta.ignoreAuth)。</p>
266
- <pre>// admin remotes(dev/prod 自动切换,prod 走 NGINX 同源路径)
267
- 'bpm-app': { dev: 'http://localhost:5102/bpm', prod: '/bpm' },
268
- 'lowcode-app': { dev: 'http://localhost:5103/lowcode', prod: '/lowcode' },
269
- shared: { vue: {singleton:true, requiredVersion:'^3.4.0'}, pinia: {singleton:true} }</pre>
270
- <pre>&lt;!-- 推荐用法:远程组件纳入宿主组件树 --&gt;
271
- const TaskCard = defineAsyncComponent(() =&gt; import('mes-bpm/TaskCard').then(m =&gt; m.default))</pre>
272
- <img class="shot" src="screenshots/tb-dev-demo-page.png">
273
- <img class="shot" src="screenshots/tb-dev-remotes-loaded.png">
274
- <p>协商结果(dev 实测):admin 提供 vue@3.5.40 / pinia@2.1.7;lowcode 带来 vue@3.5.22、bpm 带来 pinia@2.3.1
275
- ——多版本条目并存,消费方按 requiredVersion 取最高满足版本。</p>
276
- <img class="shot" src="screenshots/tb-dev-scope-negotiation.png">
277
-
278
- <h3>5.1 生产环境(NGINX 8662):真实远程产物 + 最小宿主</h3>
279
- <p>bpm/lowcode 生产构建产物(fulgurjs-remoteEntry.js + fulgurjs-manifest.json)部署到 NGINX 8662,
280
- 最小宿主页用<b>纯运行时容器 API</b>(无构建步骤)直接消费真实产物:</p>
281
- <pre>import { initSharing, registerRemotes, loadRemote, loadShare } from '/fulgurjs-runtime.js'
282
- initSharing('default')
283
- registerRemotes([
284
- { name: 'mes-bpm', entry: '/flowable/fulgurjs-remoteEntry.js' },
285
- { name: 'mes-lowcode', entry: '/lowcode/fulgurjs-remoteEntry.js' },
286
- ])
287
- const TaskCard = (await loadRemote('mes-bpm/./TaskCard')).default
288
- const vueMod = await loadShare('vue', { requiredVersion: '^3.4.0', singleton: true })</pre>
289
- <img class="shot" src="screenshots/tb-prod-minimal-host.png">
290
- <figcaption>prod 实测:双远程组件渲染、vue/pinia 双版本协商(vue@3.5.40 ← mes-bpm、vue@3.5.22 ← mes-lowcode;pinia 2.3.1 ← mes-bpm、2.1.7 ← mes-lowcode;宿主 loadShare 实取 3.5.40)</figcaption>
291
-
292
- <h2 id="cases">5.2 线上环境案例清单(后台菜单配置的 27 个子应用页面)</h2>
293
- <p>以下页面由后台动态菜单配置,当前经 qiankun 按 <code>/flowable/</code>、<code>/lowcode/</code> 前缀路由到子应用加载。
294
- 数据来自后台菜单接口(<code>sys/permission/getUserPermissionByToken</code>)实测返回。迁移到模块联邦后,
295
- 这些页面将全部以"页面组件 exposes + defineAsyncComponent 按需加载"方式引用,qiankun 不再参与。</p>
296
-
297
- <h3>mes-bpm(flowable)21 个页面</h3>
298
- <table>
299
- <tr><th>后台菜单路由</th><th>业务</th><th>迁移后的 expose 条目</th></tr>
300
- <tr><td>/bpmTask/flowable/bpm/task/todo</td><td>待办任务</td><td>./pages/bpm/task/todo</td></tr>
301
- <tr><td>/bpmTask/flowable/bpm/task/done</td><td>已办任务</td><td>./pages/bpm/task/done</td></tr>
302
- <tr><td>/bpmTask/flowable/bpm/task/my</td><td>我发起的</td><td>./pages/bpm/task/my</td></tr>
303
- <tr><td>/bpmTask/flowable/bpm/task/copy</td><td>抄送我的</td><td>./pages/bpm/task/copy</td></tr>
304
- <tr><td>/bpmTask/flowable/bpm/task/create</td><td>发起流程</td><td>./pages/bpm/task/create</td></tr>
305
- <tr><td>/bpmTask/flowable/bpm/process-instance/detail</td><td>流程实例详情</td><td>./pages/bpm/process-instance/detail</td></tr>
306
- <tr><td>/bpmTask/flowable/bpm/manager/action</td><td><b>流程审批操作页</b>(iframe 弹窗的嵌入目标,迁移核心收益点)</td><td>./pages/bpm/manager/action</td></tr>
307
- <tr><td>/baseConfig/manager/flowable/bpm/manager/model</td><td>流程模型管理</td><td>./pages/bpm/manager/model</td></tr>
308
- <tr><td>/baseConfig/manager/flowable/bpm/manager/model/create</td><td>新建模型</td><td>./pages/bpm/manager/model/create</td></tr>
309
- <tr><td>/baseConfig/manager/flowable/bpm/manager/model/update/:id</td><td>编辑模型</td><td>./pages/bpm/manager/model/update</td></tr>
310
- <tr><td>/baseConfig/manager/flowable/bpm/manager/model/copy/:id</td><td>复制模型</td><td>./pages/bpm/manager/model/copy</td></tr>
311
- <tr><td>/baseConfig/manager/flowable/bpm/manager/form</td><td>表单管理</td><td>./pages/bpm/manager/form</td></tr>
312
- <tr><td>/baseConfig/manager/flowable/bpm/manager/form/edit</td><td>编辑表单</td><td>./pages/bpm/manager/form/edit</td></tr>
313
- <tr><td>/baseConfig/manager/flowable/bpm/manager/category</td><td>流程分类</td><td>./pages/bpm/manager/category</td></tr>
314
- <tr><td>/baseConfig/manager/flowable/bpm/manager/user-group</td><td>用户组</td><td>./pages/bpm/manager/user-group</td></tr>
315
- <tr><td>/baseConfig/manager/flowable/bpm/manager/process-listener</td><td>流程监听器</td><td>./pages/bpm/manager/process-listener</td></tr>
316
- <tr><td>/baseConfig/manager/flowable/bpm/manager/process-expression</td><td>流程表达式</td><td>./pages/bpm/manager/process-expression</td></tr>
317
- <tr><td>/baseConfig/manager/flowable/bpm/manager/process-instance/manager</td><td>流程实例管理</td><td>./pages/bpm/manager/process-instance/manager</td></tr>
318
- <tr><td>/baseConfig/manager/flowable/bpm/manager/definition</td><td>流程定义</td><td>./pages/bpm/manager/definition</td></tr>
319
- <tr><td>/baseConfig/manager/flowable/bpm/process-instance/report</td><td>流程报表</td><td>./pages/bpm/process-instance/report</td></tr>
320
- </table>
321
-
322
- <h3>mes-lowcode 6 个页面</h3>
323
- <table>
324
- <tr><th>后台菜单路由</th><th>业务</th><th>迁移后的 expose 条目</th></tr>
325
- <tr><td>/online/lowcode/lowdev/formDesign</td><td>表单设计</td><td>./pages/lowdev/formDesign</td></tr>
326
- <tr><td>/online/lowcode/lowdev/reportDesign</td><td>报表设计</td><td>./pages/lowdev/reportDesign</td></tr>
327
- <tr><td>/online/lowcode/lowdev/graphReportDesign</td><td>图形报表设计</td><td>./pages/lowdev/graphReportDesign</td></tr>
328
- <tr><td>/online/lowcode/lowdev/moduleDesign</td><td>模块设计</td><td>./pages/lowdev/moduleDesign</td></tr>
329
- <tr><td>/online/lowcode/lowdev/reportTest/:code</td><td>报表测试(带参)</td><td>./pages/lowdev/reportTest</td></tr>
330
- <tr><td>/online/lowcode/form/external/:type/:id</td><td>外部表单(带参)</td><td>./pages/form/external</td></tr>
331
- </table>
332
-
333
- <div class="note">线上环境页面截图:需要后台真实登录账号(表单默认 admin/123456 在该部署上无效,已验证标准登录流程返回"用户名或密码错误")。
334
- 拿到账号后将在 8661(当前生产环境)逐页点开截图留证,作为 qiankun→模块联邦迁移前后的对比基线保存。</div>
335
-
336
- <h2 id="hmr">6. HMR 全链路(L1/L2/L3)</h2>
337
- <table>
338
- <tr><th>档位</th><th>验收</th><th>结果</th></tr>
339
- <tr><td>L1</td><td>remote 改代码 → 宿主页面组件热替换(无整页刷新)</td><td class="ok">✅</td></tr>
340
- <tr><td>L2</td><td>组件内状态保留(模板级变更走 rerender)</td><td class="ok">✅</td></tr>
341
- <tr><td>L3</td><td>编译报错覆盖层 → 修复自动恢复</td><td class="ok">✅</td></tr>
342
- </table>
343
- <div class="shot-row">
344
- <figure><img class="shot" src="screenshots/tb-dev-hmr-before.png"><figcaption>testbed:HMR 前(进度 70%)</figcaption></figure>
345
- <figure><img class="shot" src="screenshots/tb-dev-hmr-crossapp.png"><figcaption>testbed:改 bpm 源码 → admin 页面热替换(状态 70% 保留)</figcaption></figure>
346
- </div>
347
- <div class="shot-row">
348
- <figure><img class="shot" src="screenshots/dev-hmr-l3-error-overlay.png"><figcaption>L3:remote 编译错误覆盖层</figcaption></figure>
349
- <figure><img class="shot" src="screenshots/dev-hmr-l3-recovered.png"><figcaption>L3:修复后自动恢复</figcaption></figure>
350
- </div>
351
-
352
- <h2 id="limit">7. 已知差异与限制(诚实版)</h2>
353
- <table>
354
- <tr><th>#</th><th>项</th><th>说明</th></tr>
355
- <tr><td>1</td><td><code>remoteType: 'script'/'var'</code></td><td>webpack 宿主消费 Vite remote 的跨打包器互操作 → P3 里程碑,配置时告警并按 module 处理</td></tr>
356
- <tr><td>2</td><td><code>export * from '共享包'</code></td><td>ESM 无法动态生成导出绑定,构建期明确报错;请用命名 re-export</td></tr>
357
- <tr><td>3</td><td>构建目标 ≥ es2022</td><td>TLA(自动异步边界)要求;插件自动提升默认 target,显式低版本会告警</td></tr>
358
- <tr><td>4</td><td>大型遗留宿主中远程组件的<b>交互响应式</b></td><td>实测 示例工程 admin(Vite 6.4.3 + qiankun 插件 + 3 万模块)中,远程组件内联事件可触发、
359
- 数据写入成功,但组件渲染效果偶发不随动(根因:多副本 prebundle chunk 图跨域组合的求值顺序)。<br>
360
- <b>规避:</b>① exposes 组件保持纯 Vue、避免依赖宿主 UI 库的 prebundle;② UI 库跨小版本共享需谨慎
361
- (EP 2.9.1/2.10.2/2.14.3 混用实测存在 Illegal constructor 风险);③ 疑难场景可改用 qiankun 原通道。</td></tr>
362
- <tr><td>5</td><td>SSR</td><td>本版本不支持(hook 自动禁用并告警)</td></tr>
363
- <tr><td>7</td><td>jeecg 规模宿主的 <b>admin 生产构建</b></td><td>示例工程 admin(3 万+模块、Vite 6.4.3、node 24)构建触发
364
- vite build-import-analysis 在 3MB 的 xlsx.mjs vendor chunk(纯第三方库,与联邦代码无关)上的 V8 栈溢出;伴随 16GB 堆 OOM、
365
- vite-plugin-top-level-await 1.6.0 的 swc "missing field type" 连锁问题。已验证:与 shared 开关无关(最小联邦配置同样触发);
366
- <b>dev 模式完全正常,bpm/lowcode 的生产构建正常</b>。规避:关闭 inline sourcemap / vendor 分包 / 升级 vite;根治需等 vite 上游修复。
367
- <b>已实测可行组合(2026-09-16,testbed admin prod 构建通过并部署 8662 验证)</b>:<code>build.sourcemap: false</code> + 停用
368
- <code>vite-plugin-top-level-await</code> + <code>build.target: 'es2022'</code> 三件套(缺一即崩,勿回退)。</td></tr>
369
- <tr><td>6</td><td>跨端口 HMR 的 ws 地址</td><td>remote 的 vite client 以页面 hostname + remote 端口连 ws;本机 localhost 联调无碍,跨主机名需反代</td></tr>
370
- </table>
371
-
372
- <h2 id="faq">8. 故障排查(错误码 MFU-0xx)</h2>
373
- <table>
374
- <tr><th>错误码</th><th>含义</th><th>处置</th></tr>
375
- <tr><td>MFU-001</td><td>remote 容器/模块加载失败(网络、超时、重试耗尽、熔断)</td><td>检查 remoteEntry 地址与 CORS;熔断默认 5 次失败后 30s 快速失败,可调 <code>retries/timeout/fallback/breaker</code></td></tr>
376
- <tr><td>MFU-002</td><td>remoteEntry 自报名与配置名不一致</td><td>核对 <code>name@url</code> 的 name 与 remote 应用 <code>federation({ name })</code>(promise remote 仅告警)</td></tr>
377
- <tr><td>MFU-003</td><td>strictVersion 版本不满足</td><td>调整 requiredVersion 或关闭 strictVersion</td></tr>
378
- <tr><td>MFU-004</td><td>共享模块缺失且无本地副本</td><td>去掉 <code>import: false</code> 或补齐提供方</td></tr>
379
- <tr><td>MFU-005</td><td>容器被第二个 share scope 重复 init</td><td>检查是否存在同名 remote 配置了不同 shareScope</td></tr>
380
- <tr><td>MFU-006</td><td>模块未被该远程 exposes</td><td>核对 exposes 键名(自动补 ./)</td></tr>
381
- <tr><td>MFU-007</td><td>预加载失败</td><td>仅上报不阻断;检查 manifest 可达性</td></tr>
382
- <tr><td>MFU-008</td><td>未知远程</td><td>在 <code>remotes</code> 配置或运行时 <code>registerRemote()</code> 注册</td></tr>
383
- <tr><td>MFU-009</td><td>加载到的模块没有任何导出</td><td>核对远程 exposes 是否指向了正确的文件(MFU-006 语义),或该文件是否缺少 export</td></tr>
384
- <tr><td>MFU-010</td><td>singleton 共享版本漂移(消费方要求与作用域实际提供不一致,使用作用域版本)</td><td>核对双方 shared requiredVersion;确需锁版本用 strictVersion(升级为 MFU-003 抛错)</td></tr>
385
- </table>
386
-
387
- <h3>构建/开发期错误(非 MFU 运行时码)</h3>
388
- <table>
389
- <tr><th>错误码</th><th>含义</th><th>处置</th></tr>
390
- <tr><td><code>CFG-001</code></td><td>name 缺失或含非法字符</td><td>按报错示例修正 federation name</td></tr>
391
- <tr><td><code>CFG-002</code></td><td>exposes 配置形状错误</td><td>{ "./Module": "./src/path" }</td></tr>
392
- <tr><td><code>CFG-003</code></td><td>remotes 配置形状错误 / 键含非法字符</td><td>{ name: url | { dev, prod } | () =&gt; Promise }</td></tr>
393
- <tr><td><code>CFG-004</code></td><td>shared 配置形状错误</td><td>["vue"] 或 { vue: { singleton: true } }</td></tr>
394
- <tr><td><code>CFG-005</code></td><td>remotes 键与 shared 键同名冲突</td><td>改名(webpack 同款互斥约束)</td></tr>
395
- <tr><td><code>CFG-006</code></td><td>孤岛配置(既无 exposes 也无 remotes/shared)</td><td>核对 federation 配置意图</td></tr>
396
- <tr><td><code>CFG-007</code></td><td>remotes 对象形式误用 name@ 前缀(dev/prod 槽位整串当 URL 拼接成坏地址,运行时表现为无关的 MFU-001)</td>
397
- <td>对象形式不支持 name@ 前缀:改用键名(自报名默认取键)或字符串形式 <code>'shop@http://host'</code>(重命名语义)</td></tr>
398
- <tr><td><code>CFG-008</code></td><td>shared 非法组合:eager + import:false(eager 需本地副本打进初始 chunk);同 shareKey+shareScope 重复声明(版本裁决歧义)</td>
399
- <td>eager 项去掉 import:false;合并重复声明或改用不同 shareKey</td></tr>
400
- <tr><td><code>DEV-001</code></td><td>remote dev server 不可达(dev manifest 拉取失败)</td>
401
- <td>启动对应 remote 的 dev server;核对宿主 remotes[].dev URL 与 remote 实际 VITE_PORT/base</td></tr>
402
- <tr><td><code>DEV-002</code></td><td>dev manifest 可达但 exposes 为空</td>
403
- <td>核对 remote 的 federation exposes 配置;确认插件版本与宿主一致(DEV-006)</td></tr>
404
- <tr><td><code>DEV-003</code></td><td>shared 键被 optimizeDeps.exclude(dev 裸 CJS 无 interop 风险)</td>
405
- <td>从 exclude 移除;确需移出预构建的库用 dev 专用别名兜 CJS 子路径(迁移指南避坑 #3/#4)</td></tr>
406
- <tr><td><code>DEV-004</code></td><td>已知 UMD-only 依赖(如 @smallwei/avue)不在 optimizeDeps.include</td>
407
- <td>加入 include: ['@smallwei/avue']</td></tr>
408
- <tr><td><code>DEV-005</code></td><td>remotes dev URL 端口无监听</td>
409
- <td>remote 未启动或端口错位(server.origin 同款),核对 VITE_PORT 与 remotes[].dev</td></tr>
410
- <tr><td><code>DEV-006</code></td><td>宿主/远程插件版本不一致</td>
411
- <td>统一各应用 @fulgurjs/federation 版本并重启 dev server</td></tr>
412
- <tr><td><code>DEV-008</code></td><td>exposes 目标文件(远程页面)静态导入 virtual:fulgurjs-runtime——宿主跨源加载时
413
- 在远程模块图内求值第二份副本链,破坏渲染上下文。<strong>0.4.1 起插件自动把该导入改写为惰性单例委托
414
- (求值期零副作用、调用期转发页面级单例),不再报错</strong>;本码保留用于历史排查与极端场景说明</td>
415
- <td>无需任何处理——任何文件直接 <code>import { loadRemote } from 'virtual:fulgurjs-runtime'</code> 即可;
416
- 调试时可绕过代理直取 <code>(globalThis as any).__FULGURJS_RUNTIME__</code></td></tr>
417
- <tr><td><code>DEV-009</code></td><td>联邦虚拟模块/门面请求 404(.vite 缓存漂移)——<strong>0.4.1 起插件在 dev server
418
- 启动时自动检测插件版本变化并清除 .vite 缓存</strong></td>
419
- <td>重启 dev server 即可(缓存自清);若自动清理失败按提示手工 <code>rm -rf node_modules/.vite</code>;
420
- 浏览器侧换全新 profile</td></tr>
421
- <tr><td><code>DEV-010</code></td><td>dev 冷启动预构建窗口提示:首次启动/清 .vite 后首轮 30~60s 内联邦请求瞬时 504 / "ce" / Outdated Optimize Dep(预构建暂态,非回归)</td>
422
- <td>先真实打开一次页面预热(等网络空闲)再做验收断言;重复出现才按 DEV-009 清缓存排查</td></tr>
423
- <tr><td><code>BLD-001</code></td><td>expose 源文件解析失败</td><td>核对 exposes 指向的文件路径存在且可编译</td></tr>
424
- <tr><td><code>BLD-002</code></td><td>构建目标低于 es2022(TLA 需要)</td><td>提升 build.target(插件会自动提升并告警)</td></tr>
425
- <tr><td><code>BLD-003</code></td><td>expose 目标组件含必填 props(文档化核对项,不自动告警)——联邦直挂无法像父组件传 props,必填 props 缺失会渲染崩溃(审批操作页误挂子组件事故类)。自动发射已按实测降级:宿主路由以 props 回调透传 params/query 时必填 props 可由 query 供给(流程详情页 id 实例),插件无法感知该通道</td>
426
- <td>expose 指向独立页(自带取参逻辑)或给 props 默认值;接入前人工核对 expose 目标的 defineProps 声明与取参通道</td></tr>
427
- </table>
428
-
429
- <h3>NGINX 生产样例(同源部署 + 反代后端)</h3>
430
- <pre>server {
431
- listen 8662;
432
- root "/path/to/dist"; # 含 main/ flowable/ lowcode/
433
- location /flowable { try_files $uri $uri/ /flowable/index.html; }
434
- location /lowcode { try_files $uri $uri/ /lowcode/index.html; }
435
- location /main { try_files $uri $uri/ /main/index.html; }
436
- location /api {
437
- proxy_pass http://YOUR-BACKEND-HOST:8080; # 替换为你的后端地址
438
- proxy_http_version 1.1;
439
- proxy_set_header Upgrade $http_upgrade;
440
- proxy_set_header Connection "upgrade";
441
- }
442
- }</pre>
443
-
444
- <h3>常见问题</h3>
445
- <ul>
446
- <li><b>This package is ESM only but it was tried to load by `require`</b> —— 已在 v0.1.0+ 提供 CJS 双格式产物,升级插件后重启 dev server。</li>
447
- <li><b>Top-level await is not available in the configured target</b> —— 插件默认自动提升 <code>build.target</code> 到 es2022;若显式配置了更低版本会告警。</li>
448
- <li><b>Failed to resolve import "vue"</b>(remote 侧)—— 检查是否用 <code>packageName</code> 把 UI 库注入的 helper import 纳入共享别名。</li>
449
- <li><b>端口被占/strictPort 退出</b> —— 旧进程未退干净:<code>kill $(lsof -tiTCP:端口 -sTCP:LISTEN)</code>。</li>
450
- <li><b>pnpm 报 ERR_PNPM_IGNORED_BUILDS</b> —— 在 <code>pnpm-workspace.yaml</code> 写 <code>allowBuilds: { esbuild: true, vue-demi: true }</code>。</li>
451
- </ul>
452
-
453
- <h2 id="test">9. 测试覆盖与复现方式</h2>
454
- <table>
455
- <tr><th>层</th><th>内容</th><th>命令</th></tr>
456
- <tr><td>单测</td><td>67 用例:semver 全语法、协商全分支、AST 改写快照、容器容错/熔断</td><td><code>pnpm test:unit</code></td></tr>
457
- <tr><td>e2e dev</td><td>10 用例:加载/语义/双版本/单例/重命名/promise/错误码/pinia/CSS/HMR</td><td><code>pnpm test:dev</code></td></tr>
458
- <tr><td>e2e 容错</td><td>2 用例:kill remote → MFU-001 → 恢复;HMR L3</td><td><code>npx playwright test --project=fault</code></td></tr>
459
- <tr><td>e2e prod</td><td>8 用例(隔离 NGINX 8999):产物断言/渲染/单例/CSS/eager/容错</td><td><code>pnpm test:prod</code></td></tr>
460
- <tr><td>体积基准</td><td>runtime gzip 4.4KB(红线 5KB)</td><td><code>gzip -c dist/runtime.js | wc -c</code></td></tr>
461
- </table>
462
- <p>全部截图由 e2e 自动产出并归档于 <code>docs/screenshots/</code>;本手册引用的 36 张截图即上述测试的真实产物
463
- (含 §5.2 线上案例清单:后台菜单接口实测解析的 27 个子应用页面)。</p>
464
-
465
- </div>
466
- </body>
467
- </html>