@fulgurjs/federation 4.0.0 → 4.1.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/dist/config.cjs CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var config_exports = {};
32
32
  __export(config_exports, {
33
33
  defineRepoConfig: () => defineRepoConfig,
34
+ federationOptionsForApp: () => federationOptionsForApp,
34
35
  loadRepoConfig: () => loadRepoConfig
35
36
  });
36
37
  module.exports = __toCommonJS(config_exports);
@@ -44,9 +45,19 @@ async function loadRepoConfig(configPath) {
44
45
  const fs = await import("fs");
45
46
  const os = await import("os");
46
47
  const path = await import("path");
47
- const selfDir = path.dirname(fileURLToPath(import_meta.url));
48
- const selfConfigJs = path.join(selfDir, "config.js");
49
- const SELF_SPEC = fs.existsSync(selfConfigJs) ? pathToFileURL(selfConfigJs).href : "@fulgurjs-federation-config-unavailable";
48
+ const selfDir = await (async () => {
49
+ try {
50
+ return path.dirname(fileURLToPath(import_meta.url));
51
+ } catch {
52
+ }
53
+ try {
54
+ return __dirname;
55
+ } catch {
56
+ }
57
+ return "";
58
+ })();
59
+ const selfConfigJs = selfDir ? path.join(selfDir, "config.js") : "";
60
+ const SELF_SPEC = selfConfigJs && fs.existsSync(selfConfigJs) ? pathToFileURL(selfConfigJs).href : "@fulgurjs-federation-config-unavailable";
50
61
  const rewriteSelfImports = (source) => source.replace(/(['"])@fulgurjs\/federation\/config\1/g, (_m, q) => `${q}${SELF_SPEC}${q}`);
51
62
  const importConfigs = async (cacheKey) => {
52
63
  return await import(`${cacheKey}?t=${Date.now()}`);
@@ -101,14 +112,62 @@ async function loadRepoConfig(configPath) {
101
112
  throw new Error(
102
113
  `[fulgurjs:init] \u5E94\u7528 "${app.path}" \u65E2\u65E0 host \u4E5F\u65E0 remote \u89D2\u8272
103
114
  \u6839\u56E0\uFF1A\u5E94\u7528\u5FC5\u987B\u81F3\u5C11\u58F0\u660E\u4E00\u4E2A\u89D2\u8272
104
- \u4FEE\u6CD5\uFF1A\u5BBF\u4E3B\u8865 host: { pages, remotePrefixes, remotes }\uFF0C\u8FDC\u7A0B\u8865 remote: { exposes }`
115
+ \u4FEE\u6CD5\uFF1A\u5BBF\u4E3B\u8865 host: { remotePrefixes, remotes }\uFF08pages \u53EF\u9009\uFF09\uFF0C\u8FDC\u7A0B\u8865 remote: { exposes }`
105
116
  );
106
117
  }
107
118
  }
108
119
  return cfg;
109
120
  }
121
+ function federationOptionsForApp(config, appPathOrName) {
122
+ const app = config.apps.find(
123
+ (a) => a.path === appPathOrName || a.name === appPathOrName
124
+ );
125
+ if (!app) {
126
+ throw new Error(
127
+ `[fulgurjs] \u5E94\u7528 "${appPathOrName}" \u4E0D\u5728\u4ED3\u5E93\u914D\u7F6E\u4E2D
128
+ \u53EF\u7528\u5E94\u7528: ${config.apps.map((a) => `${a.path}(${a.name})`).join("\u3001")}
129
+ \u4FEE\u6CD5: \u4FEE\u6B63\u5E94\u7528\u76EE\u5F55\u540D\u6216\u5BB9\u5668\u540D\uFF08fulgurjs.config.ts apps \u6570\u7EC4\u5185\u4E8C\u9009\u4E00\u5339\u914D\uFF09`
130
+ );
131
+ }
132
+ if (!app.host && !app.remote) {
133
+ throw new Error(
134
+ `[fulgurjs] \u5E94\u7528 "${app.path}" \u65E2\u65E0 host \u4E5F\u65E0 remote \u89D2\u8272
135
+ \u4FEE\u6CD5: \u5BBF\u4E3B\u8865 host: { remotePrefixes, remotes }\uFF0C\u8FDC\u7A0B\u8865 remote: { exposes }`
136
+ );
137
+ }
138
+ const remotes = {};
139
+ const remoteSource = /* @__PURE__ */ new Map();
140
+ const mergeRemotes = (from, role) => {
141
+ for (const [key, addr] of Object.entries(from ?? {})) {
142
+ const prev = remotes[key];
143
+ if (prev && (prev.dev !== addr.dev || prev.prod !== addr.prod)) {
144
+ throw new Error(
145
+ `[fulgurjs] \u5E94\u7528 "${app.path}" \u7684\u8FDC\u7A0B "${key}" \u5728 ${role} \u4E0E ${remoteSource.get(key)} \u4E24\u5904\u5730\u5740\u4E0D\u4E00\u81F4
146
+ ${remoteSource.get(key)}: dev=${prev.dev} prod=${prev.prod}
147
+ ${role}: dev=${addr.dev} prod=${addr.prod}
148
+ \u4FEE\u6CD5: \u4FEE\u6B63 fulgurjs.config.ts \u4F7F\u4E24\u5904\u4E00\u81F4\uFF08\u540C\u4E00\u8FDC\u7A0B\u5728\u4E00\u4E2A\u5E94\u7528\u5185\u53EA\u80FD\u6709\u4E00\u4E2A\u5730\u5740\uFF09`
149
+ );
150
+ }
151
+ remotes[key] = addr;
152
+ remoteSource.set(key, role);
153
+ }
154
+ };
155
+ mergeRemotes(app.host?.remotes, "host.remotes");
156
+ mergeRemotes(app.remote?.remotes, "remote.remotes");
157
+ const hasExposes = !!app.remote && Object.keys(app.remote.exposes).length > 0;
158
+ const hasRemotes = Object.keys(remotes).length > 0;
159
+ return {
160
+ name: app.name,
161
+ ...hasRemotes ? { remotes } : {},
162
+ ...hasExposes ? { exposes: app.remote.exposes } : {},
163
+ ...app.remote?.setup ? { setup: app.remote.setup } : {},
164
+ ...app.shared ? { shared: app.shared } : {},
165
+ devSharedSelf: app.devSharedSelf ?? (hasExposes || !!app.remote?.setup)
166
+ };
167
+ }
110
168
  // Annotate the CommonJS export names for ESM import in node:
111
169
  0 && (module.exports = {
112
170
  defineRepoConfig,
171
+ federationOptionsForApp,
113
172
  loadRepoConfig
114
173
  });
package/dist/config.d.cts CHANGED
@@ -1,3 +1,5 @@
1
+ import { F as FederationOptions } from './options-CRGpSmVj.cjs';
2
+
1
3
  /**
2
4
  * W1 fulgurjs.config.ts:单配置文件驱动的 schema + 加载器(项目无关,决策见排期文档 §4:
3
5
  * 主包内置 bin + 单配置文件驱动,可入库可复跑)。
@@ -12,14 +14,22 @@ interface PageEntry {
12
14
  spec?: string;
13
15
  /** 页签标题 */
14
16
  title?: string;
17
+ /** 页面保活开关(按页显式开启;默认关闭) */
18
+ keepAlive?: boolean;
15
19
  }
16
20
  interface RemoteAddress {
17
21
  dev: string;
18
22
  prod: string;
19
23
  }
20
24
  interface HostConfig {
21
- /** 页面路由表 */
22
- pages: PageEntry[];
25
+ /**
26
+ * 页面路由表(可选):应用代码的页面表是运行时页面真源(供宿主路由与
27
+ * createHostPages 共用),仓库配置中的副本仅供 init/explain 摘要与核对。
28
+ * 不再强制——缺省时 CLI 摘要按实际提供数量报告。
29
+ */
30
+ pages?: PageEntry[];
31
+ /** 页面 spec 推导规则(与宿主 createHostPages 的 deriveSpec 保持一致;缺省 = 去首段 + 剥 :参 段) */
32
+ deriveSpec?: (route: string) => string;
23
33
  /** 路由前缀 → 远程名 */
24
34
  remotePrefixes: Record<string, string>;
25
35
  /** 消费的远程地址(键 = import 前缀;裸 URL,对象形式不支持 name@ 前缀) */
@@ -28,7 +38,9 @@ interface HostConfig {
28
38
  interface RemoteConfig {
29
39
  /** exposes:./键 → 源文件(独立页) */
30
40
  exposes: Record<string, string>;
31
- /** 反向消费的远程(双向联邦时;dev 下自身源码参与协商需 devSharedSelf: true) */
41
+ /** 远程初始化入口(默认导出 setup(context),可选具名导出 onSession(context)) */
42
+ setup?: string;
43
+ /** 反向消费的远程(双向联邦时) */
32
44
  remotes?: Record<string, RemoteAddress>;
33
45
  }
34
46
  interface AppConfig {
@@ -51,6 +63,8 @@ interface AppConfig {
51
63
  singleton?: boolean;
52
64
  requiredVersion?: string;
53
65
  }>;
66
+ /** 显式覆盖 dev 下自身源码参与 shared 协商改写;缺省按角色推断(见 federationOptionsForApp) */
67
+ devSharedSelf?: boolean;
54
68
  }
55
69
  interface DeployConfig {
56
70
  /** NGINX 站点根(样板输出用,可选) */
@@ -75,5 +89,18 @@ declare function defineRepoConfig(config: UserConfig): UserConfig;
75
89
  * 老版本 Node 回退 esbuild 转译(先试配置工程,再试 CLI 自带依赖树)。
76
90
  */
77
91
  declare function loadRepoConfig(configPath: string): Promise<RepoConfig>;
92
+ /**
93
+ * 仓库配置 → 某应用的 federation() Vite 插件选项(§3.4 单配置驱动)。
94
+ *
95
+ * 转换范围:name、宿主/反向 remotes(两处同键冲突即报错,带两边值)、exposes、
96
+ * 可选 setup、shared、devSharedSelf。build.target/base/端口/代理/插件顺序等仍归各应用
97
+ * vite.config.ts 管理——本函数只生成 federation({...}) 的入参。
98
+ *
99
+ * devSharedSelf 来源(优先级):app.devSharedSelf 显式值 > 角色推断。
100
+ * 推断规则:提供 exposes(含仅 setup)的应用为 true(其源码被宿主消费,需协商到宿主
101
+ * 实例);纯宿主(只消费)为 false。双向联邦漏配该项曾是已知错误配置(§12.4),
102
+ * 推断后不再依赖背诵。
103
+ */
104
+ declare function federationOptionsForApp(config: RepoConfig, appPathOrName: string): FederationOptions;
78
105
 
79
- export { type AppConfig, type DeployConfig, type HostConfig, type PageEntry, type RemoteAddress, type RemoteConfig, type RepoConfig, type UserConfig, defineRepoConfig, loadRepoConfig };
106
+ export { type AppConfig, type DeployConfig, type HostConfig, type PageEntry, type RemoteAddress, type RemoteConfig, type RepoConfig, type UserConfig, defineRepoConfig, federationOptionsForApp, loadRepoConfig };
package/dist/config.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { F as FederationOptions } from './options-CRGpSmVj.js';
2
+
1
3
  /**
2
4
  * W1 fulgurjs.config.ts:单配置文件驱动的 schema + 加载器(项目无关,决策见排期文档 §4:
3
5
  * 主包内置 bin + 单配置文件驱动,可入库可复跑)。
@@ -12,14 +14,22 @@ interface PageEntry {
12
14
  spec?: string;
13
15
  /** 页签标题 */
14
16
  title?: string;
17
+ /** 页面保活开关(按页显式开启;默认关闭) */
18
+ keepAlive?: boolean;
15
19
  }
16
20
  interface RemoteAddress {
17
21
  dev: string;
18
22
  prod: string;
19
23
  }
20
24
  interface HostConfig {
21
- /** 页面路由表 */
22
- pages: PageEntry[];
25
+ /**
26
+ * 页面路由表(可选):应用代码的页面表是运行时页面真源(供宿主路由与
27
+ * createHostPages 共用),仓库配置中的副本仅供 init/explain 摘要与核对。
28
+ * 不再强制——缺省时 CLI 摘要按实际提供数量报告。
29
+ */
30
+ pages?: PageEntry[];
31
+ /** 页面 spec 推导规则(与宿主 createHostPages 的 deriveSpec 保持一致;缺省 = 去首段 + 剥 :参 段) */
32
+ deriveSpec?: (route: string) => string;
23
33
  /** 路由前缀 → 远程名 */
24
34
  remotePrefixes: Record<string, string>;
25
35
  /** 消费的远程地址(键 = import 前缀;裸 URL,对象形式不支持 name@ 前缀) */
@@ -28,7 +38,9 @@ interface HostConfig {
28
38
  interface RemoteConfig {
29
39
  /** exposes:./键 → 源文件(独立页) */
30
40
  exposes: Record<string, string>;
31
- /** 反向消费的远程(双向联邦时;dev 下自身源码参与协商需 devSharedSelf: true) */
41
+ /** 远程初始化入口(默认导出 setup(context),可选具名导出 onSession(context)) */
42
+ setup?: string;
43
+ /** 反向消费的远程(双向联邦时) */
32
44
  remotes?: Record<string, RemoteAddress>;
33
45
  }
34
46
  interface AppConfig {
@@ -51,6 +63,8 @@ interface AppConfig {
51
63
  singleton?: boolean;
52
64
  requiredVersion?: string;
53
65
  }>;
66
+ /** 显式覆盖 dev 下自身源码参与 shared 协商改写;缺省按角色推断(见 federationOptionsForApp) */
67
+ devSharedSelf?: boolean;
54
68
  }
55
69
  interface DeployConfig {
56
70
  /** NGINX 站点根(样板输出用,可选) */
@@ -75,5 +89,18 @@ declare function defineRepoConfig(config: UserConfig): UserConfig;
75
89
  * 老版本 Node 回退 esbuild 转译(先试配置工程,再试 CLI 自带依赖树)。
76
90
  */
77
91
  declare function loadRepoConfig(configPath: string): Promise<RepoConfig>;
92
+ /**
93
+ * 仓库配置 → 某应用的 federation() Vite 插件选项(§3.4 单配置驱动)。
94
+ *
95
+ * 转换范围:name、宿主/反向 remotes(两处同键冲突即报错,带两边值)、exposes、
96
+ * 可选 setup、shared、devSharedSelf。build.target/base/端口/代理/插件顺序等仍归各应用
97
+ * vite.config.ts 管理——本函数只生成 federation({...}) 的入参。
98
+ *
99
+ * devSharedSelf 来源(优先级):app.devSharedSelf 显式值 > 角色推断。
100
+ * 推断规则:提供 exposes(含仅 setup)的应用为 true(其源码被宿主消费,需协商到宿主
101
+ * 实例);纯宿主(只消费)为 false。双向联邦漏配该项曾是已知错误配置(§12.4),
102
+ * 推断后不再依赖背诵。
103
+ */
104
+ declare function federationOptionsForApp(config: RepoConfig, appPathOrName: string): FederationOptions;
78
105
 
79
- export { type AppConfig, type DeployConfig, type HostConfig, type PageEntry, type RemoteAddress, type RemoteConfig, type RepoConfig, type UserConfig, defineRepoConfig, loadRepoConfig };
106
+ export { type AppConfig, type DeployConfig, type HostConfig, type PageEntry, type RemoteAddress, type RemoteConfig, type RepoConfig, type UserConfig, defineRepoConfig, federationOptionsForApp, loadRepoConfig };
package/dist/config.js CHANGED
@@ -8,9 +8,19 @@ async function loadRepoConfig(configPath) {
8
8
  const fs = await import("fs");
9
9
  const os = await import("os");
10
10
  const path = await import("path");
11
- const selfDir = path.dirname(fileURLToPath(import.meta.url));
12
- const selfConfigJs = path.join(selfDir, "config.js");
13
- const SELF_SPEC = fs.existsSync(selfConfigJs) ? pathToFileURL(selfConfigJs).href : "@fulgurjs-federation-config-unavailable";
11
+ const selfDir = await (async () => {
12
+ try {
13
+ return path.dirname(fileURLToPath(import.meta.url));
14
+ } catch {
15
+ }
16
+ try {
17
+ return __dirname;
18
+ } catch {
19
+ }
20
+ return "";
21
+ })();
22
+ const selfConfigJs = selfDir ? path.join(selfDir, "config.js") : "";
23
+ const SELF_SPEC = selfConfigJs && fs.existsSync(selfConfigJs) ? pathToFileURL(selfConfigJs).href : "@fulgurjs-federation-config-unavailable";
14
24
  const rewriteSelfImports = (source) => source.replace(/(['"])@fulgurjs\/federation\/config\1/g, (_m, q) => `${q}${SELF_SPEC}${q}`);
15
25
  const importConfigs = async (cacheKey) => {
16
26
  return await import(`${cacheKey}?t=${Date.now()}`);
@@ -65,13 +75,61 @@ async function loadRepoConfig(configPath) {
65
75
  throw new Error(
66
76
  `[fulgurjs:init] \u5E94\u7528 "${app.path}" \u65E2\u65E0 host \u4E5F\u65E0 remote \u89D2\u8272
67
77
  \u6839\u56E0\uFF1A\u5E94\u7528\u5FC5\u987B\u81F3\u5C11\u58F0\u660E\u4E00\u4E2A\u89D2\u8272
68
- \u4FEE\u6CD5\uFF1A\u5BBF\u4E3B\u8865 host: { pages, remotePrefixes, remotes }\uFF0C\u8FDC\u7A0B\u8865 remote: { exposes }`
78
+ \u4FEE\u6CD5\uFF1A\u5BBF\u4E3B\u8865 host: { remotePrefixes, remotes }\uFF08pages \u53EF\u9009\uFF09\uFF0C\u8FDC\u7A0B\u8865 remote: { exposes }`
69
79
  );
70
80
  }
71
81
  }
72
82
  return cfg;
73
83
  }
84
+ function federationOptionsForApp(config, appPathOrName) {
85
+ const app = config.apps.find(
86
+ (a) => a.path === appPathOrName || a.name === appPathOrName
87
+ );
88
+ if (!app) {
89
+ throw new Error(
90
+ `[fulgurjs] \u5E94\u7528 "${appPathOrName}" \u4E0D\u5728\u4ED3\u5E93\u914D\u7F6E\u4E2D
91
+ \u53EF\u7528\u5E94\u7528: ${config.apps.map((a) => `${a.path}(${a.name})`).join("\u3001")}
92
+ \u4FEE\u6CD5: \u4FEE\u6B63\u5E94\u7528\u76EE\u5F55\u540D\u6216\u5BB9\u5668\u540D\uFF08fulgurjs.config.ts apps \u6570\u7EC4\u5185\u4E8C\u9009\u4E00\u5339\u914D\uFF09`
93
+ );
94
+ }
95
+ if (!app.host && !app.remote) {
96
+ throw new Error(
97
+ `[fulgurjs] \u5E94\u7528 "${app.path}" \u65E2\u65E0 host \u4E5F\u65E0 remote \u89D2\u8272
98
+ \u4FEE\u6CD5: \u5BBF\u4E3B\u8865 host: { remotePrefixes, remotes }\uFF0C\u8FDC\u7A0B\u8865 remote: { exposes }`
99
+ );
100
+ }
101
+ const remotes = {};
102
+ const remoteSource = /* @__PURE__ */ new Map();
103
+ const mergeRemotes = (from, role) => {
104
+ for (const [key, addr] of Object.entries(from ?? {})) {
105
+ const prev = remotes[key];
106
+ if (prev && (prev.dev !== addr.dev || prev.prod !== addr.prod)) {
107
+ throw new Error(
108
+ `[fulgurjs] \u5E94\u7528 "${app.path}" \u7684\u8FDC\u7A0B "${key}" \u5728 ${role} \u4E0E ${remoteSource.get(key)} \u4E24\u5904\u5730\u5740\u4E0D\u4E00\u81F4
109
+ ${remoteSource.get(key)}: dev=${prev.dev} prod=${prev.prod}
110
+ ${role}: dev=${addr.dev} prod=${addr.prod}
111
+ \u4FEE\u6CD5: \u4FEE\u6B63 fulgurjs.config.ts \u4F7F\u4E24\u5904\u4E00\u81F4\uFF08\u540C\u4E00\u8FDC\u7A0B\u5728\u4E00\u4E2A\u5E94\u7528\u5185\u53EA\u80FD\u6709\u4E00\u4E2A\u5730\u5740\uFF09`
112
+ );
113
+ }
114
+ remotes[key] = addr;
115
+ remoteSource.set(key, role);
116
+ }
117
+ };
118
+ mergeRemotes(app.host?.remotes, "host.remotes");
119
+ mergeRemotes(app.remote?.remotes, "remote.remotes");
120
+ const hasExposes = !!app.remote && Object.keys(app.remote.exposes).length > 0;
121
+ const hasRemotes = Object.keys(remotes).length > 0;
122
+ return {
123
+ name: app.name,
124
+ ...hasRemotes ? { remotes } : {},
125
+ ...hasExposes ? { exposes: app.remote.exposes } : {},
126
+ ...app.remote?.setup ? { setup: app.remote.setup } : {},
127
+ ...app.shared ? { shared: app.shared } : {},
128
+ devSharedSelf: app.devSharedSelf ?? (hasExposes || !!app.remote?.setup)
129
+ };
130
+ }
74
131
  export {
75
132
  defineRepoConfig,
133
+ federationOptionsForApp,
76
134
  loadRepoConfig
77
135
  };
package/dist/context.cjs CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var context_exports = {};
22
22
  __export(context_exports, {
23
23
  ContextErrorCodes: () => ContextErrorCodes,
24
+ clearAppContext: () => clearAppContext,
24
25
  getAppContext: () => getAppContext,
25
26
  provideAppContext: () => provideAppContext,
26
27
  requireAppContext: () => requireAppContext
@@ -52,7 +53,7 @@ function requireRuntime() {
52
53
  if (!rt || typeof rt.loadRemote !== "function") {
53
54
  throw new FgError(
54
55
  ContextErrorCodes.CONTEXT_NO_RUNTIME,
55
- "fulgurjs runtime singleton not found on this page (globalThis.__FULGURJS_RUNTIME__ is undefined).\n \u6839\u56E0: \u5F53\u524D\u9875\u9762\u6CA1\u6709\u7ECF\u5BBF\u4E3B\u7684\u8054\u90A6\u8FD0\u884C\u65F6\u52A0\u8F7D\uFF08\u72EC\u7ACB\u76F4\u5F00\u8FDC\u7A0B\u9875\uFF0C\u6216\u5BBF\u4E3B\u6865\u665A\u4E8E\u672C\u8C03\u7528\u6267\u884C\uFF09\u3002\n \u4FEE\u6CD5: \u2460 \u4ECE\u5BBF\u4E3B\u5E94\u7528\u7684\u8054\u90A6\u8DEF\u7531\u6253\u5F00\u672C\u9875\u9762\uFF08\u5BBF\u4E3B bridge \u4F1A\u5148\u52A0\u8F7D\u8FD0\u884C\u65F6\u5E76\u63D0\u4F9B context\uFF09\uFF1B\n \u2461 \u82E5\u4F60\u662F\u5BBF\u4E3B\u6865\u4F5C\u8005\uFF1A\u628A provideAppContext \u653E\u5728 bridge \u521D\u59CB\u5316\u5C3E\u90E8\uFF08\u65F6\u5E8F\u5951\u7EA6 bridge \u2192 federatedBoot \u2192 loadRemote\uFF09\u3002",
56
+ "fulgurjs runtime singleton not found on this page (globalThis.__FULGURJS_RUNTIME__ is undefined).\n \u6839\u56E0: \u5F53\u524D\u9875\u9762\u6CA1\u6709\u7ECF\u5BBF\u4E3B\u7684\u8054\u90A6\u8FD0\u884C\u65F6\u52A0\u8F7D\uFF08\u72EC\u7ACB\u76F4\u5F00\u8FDC\u7A0B\u9875\uFF0C\u6216\u5BBF\u4E3B\u6865\u665A\u4E8E\u672C\u8C03\u7528\u6267\u884C\uFF09\u3002\n \u4FEE\u6CD5: \u2460 \u4ECE\u5BBF\u4E3B\u5E94\u7528\u7684\u8054\u90A6\u8DEF\u7531\u6253\u5F00\u672C\u9875\u9762\uFF08\u5BBF\u4E3B\u6865\u4F1A\u5148\u52A0\u8F7D\u8FD0\u884C\u65F6\u5E76\u63D0\u4F9B context\uFF09\uFF1B\n \u2461 \u82E5\u4F60\u662F\u5BBF\u4E3B\u6865\u4F5C\u8005\uFF1A\u628A provideAppContext \u653E\u5728\u6865\u521D\u59CB\u5316\u5C3E\u90E8\uFF08\u65F6\u5E8F\u5951\u7EA6 bridge \u2192 \u8FDC\u7A0B setup \u2192 \u9875\u9762\u6A21\u5757\uFF09\u3002",
56
57
  { runtime: false }
57
58
  );
58
59
  }
@@ -63,6 +64,14 @@ function provideAppContext(config) {
63
64
  const g = globalThis;
64
65
  g[APP_CONTEXT_STORAGE_KEY] = { ...g[APP_CONTEXT_STORAGE_KEY] ?? {}, ...config };
65
66
  }
67
+ function clearAppContext() {
68
+ const g = globalThis;
69
+ delete g[APP_CONTEXT_STORAGE_KEY];
70
+ try {
71
+ g.__FULGURJS_RUNTIME__?.clearSessionState?.();
72
+ } catch {
73
+ }
74
+ }
66
75
  function getAppContext() {
67
76
  requireRuntime();
68
77
  return globalThis[APP_CONTEXT_STORAGE_KEY] ?? {};
@@ -78,7 +87,7 @@ function requireAppContext(...keys) {
78
87
  got: ${got.length ? got.map((k) => `"${k}"`).join(", ") : "(empty \u2014 \u5BBF\u4E3B\u6865\u4ECE\u672A\u8C03\u7528 provideAppContext?)"}
79
88
  expected: \u5BBF\u4E3B\u6865\u5FC5\u987B\u5728\u4EFB\u4F55\u8FDC\u7A0B\u9875\u9762\u52A0\u8F7D\u524D\u63D0\u4F9B ${missing.map((k) => `"${k}"`).join(" / ")}
80
89
  example: host/src/fulgurjs/host/bridge.ts \u2192 provideAppContext({ ${keys.join(", ")}, ... })
81
- \u4FEE\u6CD5: \u68C0\u67E5\u5BBF\u4E3B\u5E94\u7528 fulgurjs bridge \u662F\u5426\u5347\u7EA7\u5230 0.8.0 context \u5F62\u6001\uFF08\u65F6\u5E8F\u5951\u7EA6 bridge \u2192 federatedBoot \u2192 loadRemote\uFF09`,
90
+ \u4FEE\u6CD5: \u68C0\u67E5\u5BBF\u4E3B\u5E94\u7528\u7684 fulgurjs \u6865\u662F\u5426\u5728 loadRemote \u9875\u9762\u524D\u5B8C\u6210 context provide\uFF08\u65F6\u5E8F\u5951\u7EA6 bridge \u2192 \u8FDC\u7A0B setup \u2192 \u9875\u9762\u6A21\u5757\uFF09`,
82
91
  { missing, got }
83
92
  );
84
93
  throw err;
@@ -86,6 +95,7 @@ function requireAppContext(...keys) {
86
95
  // Annotate the CommonJS export names for ESM import in node:
87
96
  0 && (module.exports = {
88
97
  ContextErrorCodes,
98
+ clearAppContext,
89
99
  getAppContext,
90
100
  provideAppContext,
91
101
  requireAppContext
@@ -30,27 +30,43 @@ interface AppContext {
30
30
  locale?: unknown;
31
31
  /** 事件/方法池:events.main.* 宿主提供、events.bpm.* / events.lowcode.* 子应用反向注册 */
32
32
  events?: Record<string, any>;
33
+ /**
34
+ * 非敏感登录代次 ID:宿主每次成功登录/重新登录生成新值,token 刷新但会话未变时沿用。
35
+ * 远程 onSession 按它去重(同一代次只执行一次)。不是用户 ID、不是 token、不作为授权凭证。
36
+ * 远程声明了 onSession 时必填(缺省即 MFU-013)。
37
+ */
38
+ sessionKey?: string;
33
39
  /** 项目扩展位(formUrl/baseUrl 等自定义键,按需自行提供) */
34
40
  [key: string]: unknown;
35
41
  }
36
42
  /**
37
43
  * 宿主写入跨应用上下文(merge 语义,幂等可多次调用,后写覆盖同键)。
38
44
  *
39
- * 宿主桥(host/src/fulgurjs/host/bridge.ts)在登录完成后调用一次:
40
- * provideAppContext({ user, token, getToken, store, hostApp, locale, events: { main: mainEvents }, ... })
45
+ * 宿主桥(host/src/fulgurjs/host/bridge.ts)在登录完成后调用:
46
+ * provideAppContext({ user, getToken, store, hostApp, locale, sessionKey, events: { main: mainEvents }, ... })
47
+ * 登录态变化(换账号/token 刷新)时再次调用即以最新值覆盖同键——不要用布尔闩锁把桥封成只跑一次。
41
48
  */
42
49
  declare function provideAppContext(config: Partial<AppContext> & Record<string, unknown>): void;
50
+ /**
51
+ * 宿主退出登录时清理:删除整个 context 对象(旧 user/getToken 引用一并失效),并通知
52
+ * 运行时单例作废全部远程的会话信号与 onSession 去重状态(下次登录必须重新执行 onSession)。
53
+ *
54
+ * 边界(契约):只清 AppContext 与会话初始化状态;不重置远程模块缓存、共享模块图与
55
+ * 应用级 setup 注册(同一页面内组件与共享实例继续复用)。页面无运行时单例时静默幂等
56
+ * (退出动作不依赖联邦形态,不得因缺运行时而打断登出流程)。
57
+ */
58
+ declare function clearAppContext(): void;
43
59
  /**
44
60
  * 读上下文快照(传输层快照:顶层 merge 结果 + 嵌套对象引用共享)。
45
61
  * 页面无运行时单例(独立直开远程页)→ CC-002 显式抛错,不静默回空。
46
62
  */
47
63
  declare function getAppContext(): AppContext;
48
64
  /**
49
- * 显式校验读取(远程 boot 消费入口):任一键缺失 → CC-001 三段式。
65
+ * 显式校验读取(远程 setup/boot 消费入口):任一键缺失 → CC-001 三段式。
50
66
  *
51
67
  * 用法:const { store, user, hostApp } = requireAppContext('store', 'user', 'hostApp')
52
- * 时序契约:宿主 bridge 先 provide,远程 federatedBoot 后 require——违反即在 boot 处显式失败。
68
+ * 时序契约:宿主桥先 provide,远程 setup/onSession 后 require——违反即在初始化处显式失败。
53
69
  */
54
70
  declare function requireAppContext(...keys: string[]): AppContext;
55
71
 
56
- export { type AppContext, ContextErrorCodes, getAppContext, provideAppContext, requireAppContext };
72
+ export { type AppContext, ContextErrorCodes, clearAppContext, getAppContext, provideAppContext, requireAppContext };
package/dist/context.d.ts CHANGED
@@ -30,27 +30,43 @@ interface AppContext {
30
30
  locale?: unknown;
31
31
  /** 事件/方法池:events.main.* 宿主提供、events.bpm.* / events.lowcode.* 子应用反向注册 */
32
32
  events?: Record<string, any>;
33
+ /**
34
+ * 非敏感登录代次 ID:宿主每次成功登录/重新登录生成新值,token 刷新但会话未变时沿用。
35
+ * 远程 onSession 按它去重(同一代次只执行一次)。不是用户 ID、不是 token、不作为授权凭证。
36
+ * 远程声明了 onSession 时必填(缺省即 MFU-013)。
37
+ */
38
+ sessionKey?: string;
33
39
  /** 项目扩展位(formUrl/baseUrl 等自定义键,按需自行提供) */
34
40
  [key: string]: unknown;
35
41
  }
36
42
  /**
37
43
  * 宿主写入跨应用上下文(merge 语义,幂等可多次调用,后写覆盖同键)。
38
44
  *
39
- * 宿主桥(host/src/fulgurjs/host/bridge.ts)在登录完成后调用一次:
40
- * provideAppContext({ user, token, getToken, store, hostApp, locale, events: { main: mainEvents }, ... })
45
+ * 宿主桥(host/src/fulgurjs/host/bridge.ts)在登录完成后调用:
46
+ * provideAppContext({ user, getToken, store, hostApp, locale, sessionKey, events: { main: mainEvents }, ... })
47
+ * 登录态变化(换账号/token 刷新)时再次调用即以最新值覆盖同键——不要用布尔闩锁把桥封成只跑一次。
41
48
  */
42
49
  declare function provideAppContext(config: Partial<AppContext> & Record<string, unknown>): void;
50
+ /**
51
+ * 宿主退出登录时清理:删除整个 context 对象(旧 user/getToken 引用一并失效),并通知
52
+ * 运行时单例作废全部远程的会话信号与 onSession 去重状态(下次登录必须重新执行 onSession)。
53
+ *
54
+ * 边界(契约):只清 AppContext 与会话初始化状态;不重置远程模块缓存、共享模块图与
55
+ * 应用级 setup 注册(同一页面内组件与共享实例继续复用)。页面无运行时单例时静默幂等
56
+ * (退出动作不依赖联邦形态,不得因缺运行时而打断登出流程)。
57
+ */
58
+ declare function clearAppContext(): void;
43
59
  /**
44
60
  * 读上下文快照(传输层快照:顶层 merge 结果 + 嵌套对象引用共享)。
45
61
  * 页面无运行时单例(独立直开远程页)→ CC-002 显式抛错,不静默回空。
46
62
  */
47
63
  declare function getAppContext(): AppContext;
48
64
  /**
49
- * 显式校验读取(远程 boot 消费入口):任一键缺失 → CC-001 三段式。
65
+ * 显式校验读取(远程 setup/boot 消费入口):任一键缺失 → CC-001 三段式。
50
66
  *
51
67
  * 用法:const { store, user, hostApp } = requireAppContext('store', 'user', 'hostApp')
52
- * 时序契约:宿主 bridge 先 provide,远程 federatedBoot 后 require——违反即在 boot 处显式失败。
68
+ * 时序契约:宿主桥先 provide,远程 setup/onSession 后 require——违反即在初始化处显式失败。
53
69
  */
54
70
  declare function requireAppContext(...keys: string[]): AppContext;
55
71
 
56
- export { type AppContext, ContextErrorCodes, getAppContext, provideAppContext, requireAppContext };
72
+ export { type AppContext, ContextErrorCodes, clearAppContext, getAppContext, provideAppContext, requireAppContext };
package/dist/context.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  FgError
3
- } from "./chunk-3VBYOVH4.js";
3
+ } from "./chunk-WBCXLR2N.js";
4
4
 
5
5
  // src/context.ts
6
6
  var ContextErrorCodes = {
@@ -15,7 +15,7 @@ function requireRuntime() {
15
15
  if (!rt || typeof rt.loadRemote !== "function") {
16
16
  throw new FgError(
17
17
  ContextErrorCodes.CONTEXT_NO_RUNTIME,
18
- "fulgurjs runtime singleton not found on this page (globalThis.__FULGURJS_RUNTIME__ is undefined).\n \u6839\u56E0: \u5F53\u524D\u9875\u9762\u6CA1\u6709\u7ECF\u5BBF\u4E3B\u7684\u8054\u90A6\u8FD0\u884C\u65F6\u52A0\u8F7D\uFF08\u72EC\u7ACB\u76F4\u5F00\u8FDC\u7A0B\u9875\uFF0C\u6216\u5BBF\u4E3B\u6865\u665A\u4E8E\u672C\u8C03\u7528\u6267\u884C\uFF09\u3002\n \u4FEE\u6CD5: \u2460 \u4ECE\u5BBF\u4E3B\u5E94\u7528\u7684\u8054\u90A6\u8DEF\u7531\u6253\u5F00\u672C\u9875\u9762\uFF08\u5BBF\u4E3B bridge \u4F1A\u5148\u52A0\u8F7D\u8FD0\u884C\u65F6\u5E76\u63D0\u4F9B context\uFF09\uFF1B\n \u2461 \u82E5\u4F60\u662F\u5BBF\u4E3B\u6865\u4F5C\u8005\uFF1A\u628A provideAppContext \u653E\u5728 bridge \u521D\u59CB\u5316\u5C3E\u90E8\uFF08\u65F6\u5E8F\u5951\u7EA6 bridge \u2192 federatedBoot \u2192 loadRemote\uFF09\u3002",
18
+ "fulgurjs runtime singleton not found on this page (globalThis.__FULGURJS_RUNTIME__ is undefined).\n \u6839\u56E0: \u5F53\u524D\u9875\u9762\u6CA1\u6709\u7ECF\u5BBF\u4E3B\u7684\u8054\u90A6\u8FD0\u884C\u65F6\u52A0\u8F7D\uFF08\u72EC\u7ACB\u76F4\u5F00\u8FDC\u7A0B\u9875\uFF0C\u6216\u5BBF\u4E3B\u6865\u665A\u4E8E\u672C\u8C03\u7528\u6267\u884C\uFF09\u3002\n \u4FEE\u6CD5: \u2460 \u4ECE\u5BBF\u4E3B\u5E94\u7528\u7684\u8054\u90A6\u8DEF\u7531\u6253\u5F00\u672C\u9875\u9762\uFF08\u5BBF\u4E3B\u6865\u4F1A\u5148\u52A0\u8F7D\u8FD0\u884C\u65F6\u5E76\u63D0\u4F9B context\uFF09\uFF1B\n \u2461 \u82E5\u4F60\u662F\u5BBF\u4E3B\u6865\u4F5C\u8005\uFF1A\u628A provideAppContext \u653E\u5728\u6865\u521D\u59CB\u5316\u5C3E\u90E8\uFF08\u65F6\u5E8F\u5951\u7EA6 bridge \u2192 \u8FDC\u7A0B setup \u2192 \u9875\u9762\u6A21\u5757\uFF09\u3002",
19
19
  { runtime: false }
20
20
  );
21
21
  }
@@ -26,6 +26,14 @@ function provideAppContext(config) {
26
26
  const g = globalThis;
27
27
  g[APP_CONTEXT_STORAGE_KEY] = { ...g[APP_CONTEXT_STORAGE_KEY] ?? {}, ...config };
28
28
  }
29
+ function clearAppContext() {
30
+ const g = globalThis;
31
+ delete g[APP_CONTEXT_STORAGE_KEY];
32
+ try {
33
+ g.__FULGURJS_RUNTIME__?.clearSessionState?.();
34
+ } catch {
35
+ }
36
+ }
29
37
  function getAppContext() {
30
38
  requireRuntime();
31
39
  return globalThis[APP_CONTEXT_STORAGE_KEY] ?? {};
@@ -41,13 +49,14 @@ function requireAppContext(...keys) {
41
49
  got: ${got.length ? got.map((k) => `"${k}"`).join(", ") : "(empty \u2014 \u5BBF\u4E3B\u6865\u4ECE\u672A\u8C03\u7528 provideAppContext?)"}
42
50
  expected: \u5BBF\u4E3B\u6865\u5FC5\u987B\u5728\u4EFB\u4F55\u8FDC\u7A0B\u9875\u9762\u52A0\u8F7D\u524D\u63D0\u4F9B ${missing.map((k) => `"${k}"`).join(" / ")}
43
51
  example: host/src/fulgurjs/host/bridge.ts \u2192 provideAppContext({ ${keys.join(", ")}, ... })
44
- \u4FEE\u6CD5: \u68C0\u67E5\u5BBF\u4E3B\u5E94\u7528 fulgurjs bridge \u662F\u5426\u5347\u7EA7\u5230 0.8.0 context \u5F62\u6001\uFF08\u65F6\u5E8F\u5951\u7EA6 bridge \u2192 federatedBoot \u2192 loadRemote\uFF09`,
52
+ \u4FEE\u6CD5: \u68C0\u67E5\u5BBF\u4E3B\u5E94\u7528\u7684 fulgurjs \u6865\u662F\u5426\u5728 loadRemote \u9875\u9762\u524D\u5B8C\u6210 context provide\uFF08\u65F6\u5E8F\u5951\u7EA6 bridge \u2192 \u8FDC\u7A0B setup \u2192 \u9875\u9762\u6A21\u5757\uFF09`,
45
53
  { missing, got }
46
54
  );
47
55
  throw err;
48
56
  }
49
57
  export {
50
58
  ContextErrorCodes,
59
+ clearAppContext,
51
60
  getAppContext,
52
61
  provideAppContext,
53
62
  requireAppContext