web-extend-plugin-vue2 0.2.3 → 0.2.5
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/README.md +223 -105
- package/dist/index.cjs +1580 -1446
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1572 -1447
- package/dist/index.mjs.map +1 -1
- package/index.d.ts +126 -2
- package/package.json +10 -3
package/dist/index.cjs
CHANGED
|
@@ -6,379 +6,492 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
6
6
|
|
|
7
7
|
var Vue__default = /*#__PURE__*/_interopDefault(Vue);
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
/**
|
|
10
|
+
* 对外配置单一入口:`resolveRuntimeOptions` / 文档 / 其它模块的默认值与环境键名均由此引用。
|
|
11
|
+
* 宿主通过 `resolveRuntimeOptions(partial)` 覆盖的对象形状见 `defaultWebExtendPluginRuntime`。
|
|
12
|
+
*/
|
|
13
|
+
/** 与 `package.json` 的 peer 下限一致;`npm run test:peer-min` / CI matrix 须与此保持同步 */
|
|
14
|
+
const peerMinimumVersions = {
|
|
15
|
+
vue: '2.6.14',
|
|
16
|
+
vueRouter: '3.5.4'
|
|
17
|
+
};
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// 协议与品牌(发布契约;与清单 `hostPluginApiVersion` 对齐 semver 主版本,一般不随宿主覆盖)
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
const HOST_PLUGIN_API_VERSION = '1.0.0';
|
|
22
|
+
/** 控制台日志与首次引导横幅使用的短名称 */
|
|
23
|
+
const RUNTIME_CONSOLE_LABEL = 'web-extend-plugin-vue2';
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// 与 `build-env` / `resolveBundledEnv` 配套的键名(单一事实来源,便于检索与文档)
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
const webExtendPluginEnvKeys = {
|
|
28
|
+
manifestBase: 'VITE_FRONTEND_PLUGIN_BASE',
|
|
29
|
+
manifestListPath: 'VITE_WEB_PLUGIN_MANIFEST_PATH',
|
|
30
|
+
implicitDevIds: 'VITE_WEB_PLUGIN_IMPLICIT_DEV_IDS',
|
|
31
|
+
allowedScriptHosts: 'VITE_WEB_PLUGIN_ALLOWED_SCRIPT_HOSTS',
|
|
32
|
+
bridgePrefixes: 'VITE_WEB_PLUGIN_BRIDGE_PREFIXES',
|
|
33
|
+
devFallbackManifestUrl: 'VITE_WEB_PLUGIN_DEV_FALLBACK_MANIFEST_URL',
|
|
34
|
+
manifestMode: 'VITE_WEB_PLUGIN_MANIFEST_MODE',
|
|
35
|
+
staticManifestUrl: 'VITE_WEB_PLUGIN_STATIC_MANIFEST_URL',
|
|
36
|
+
mountPath: 'VITE_WEB_PLUGIN_MOUNT_PATH',
|
|
37
|
+
devEntry: 'VITE_WEB_PLUGIN_DEV_ENTRY',
|
|
38
|
+
devPing: 'VITE_WEB_PLUGIN_DEV_PING_PATH',
|
|
39
|
+
devSse: 'VITE_WEB_PLUGIN_DEV_SSE_PATH',
|
|
40
|
+
devPingTimeout: 'VITE_WEB_PLUGIN_DEV_PING_TIMEOUT_MS',
|
|
41
|
+
manifestCredentials: 'VITE_WEB_PLUGIN_MANIFEST_CREDENTIALS',
|
|
42
|
+
devManifestFallback: 'VITE_WEB_PLUGIN_DEV_MANIFEST_FALLBACK',
|
|
43
|
+
webPluginDevOrigin: 'VITE_WEB_PLUGIN_DEV_ORIGIN',
|
|
44
|
+
webPluginDevIds: 'VITE_WEB_PLUGIN_DEV_IDS',
|
|
45
|
+
webPluginDevMap: 'VITE_WEB_PLUGIN_DEV_MAP',
|
|
46
|
+
pluginsBootstrapSummary: 'VITE_PLUGINS_BOOTSTRAP_SUMMARY',
|
|
47
|
+
/** 清单路径备选:部分宿主单独使用 */
|
|
48
|
+
manifestPathAlt: 'VUE_APP_WEB_PLUGIN_MANIFEST_PATH'
|
|
49
|
+
};
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// `manifestMode` 未配置且环境未指定时的默认值
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
const defaultManifestMode = 'api';
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// `manifest-fetch-composer` 中间件默认(中间件 options 可逐项覆盖)
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
const defaultManifestFetchCache = {
|
|
58
|
+
storageKeyPrefix: 'wep.manifestFetch.v1',
|
|
59
|
+
maxEntries: 50
|
|
60
|
+
};
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// 路由合成名前缀(`createHostApi` 为无 name 的路由生成 `__wep_${pluginId}_${seq}`)
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
const routeSynthNamePrefix = '__wep_';
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// 宿主可通过 `resolveRuntimeOptions` 覆盖的运行时默认值(与 README / index.d.ts 描述一致)
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
const defaultWebExtendPluginRuntime = {
|
|
69
|
+
manifestBase: '/fp-api',
|
|
70
|
+
manifestListPath: '/api/frontend-plugins',
|
|
71
|
+
manifestFetchCredentials: 'include',
|
|
72
|
+
devPingPath: '/__web_plugin_dev_ping',
|
|
73
|
+
devReloadSsePath: '/__web_plugin_reload_stream',
|
|
74
|
+
webPluginDevEntryPath: '/src/plugin-entry.js',
|
|
75
|
+
devPingTimeoutMs: 500,
|
|
76
|
+
defaultImplicitDevPluginIds: [],
|
|
77
|
+
allowedScriptHosts: ['localhost', '127.0.0.1', '::1'],
|
|
78
|
+
bridgeAllowedPathPrefixes: ['/api/'],
|
|
79
|
+
/** 与 `hostLayoutComponent` 同时使用时默认父路由 name */
|
|
80
|
+
pluginRoutesParentName: '__wepPluginHost',
|
|
81
|
+
/** 插件壳路径(与菜单、ensurePluginHostRoute 一致) */
|
|
82
|
+
pluginMountPath: '/plugin',
|
|
83
|
+
/** `manifestMode=api` 且开发环境下,API 失败或 plugins 为空时尝试的静态 JSON(可放于 `public/web-plugins/`) */
|
|
84
|
+
devFallbackStaticManifestUrl: '/web-plugins/plugins.manifest.json'
|
|
23
85
|
};
|
|
24
86
|
|
|
25
87
|
/**
|
|
26
88
|
* 与具体打包器解耦的运行时环境读取:优先显式注入,其次 `globalThis.__WEP_ENV__`,再读 `process.env`。
|
|
27
89
|
* Vite 宿主建议在入口调用 `setWebExtendPluginEnv(import.meta.env)`。
|
|
28
90
|
*/
|
|
29
|
-
|
|
30
|
-
/** @type {Record<string, unknown> | null} */
|
|
31
91
|
let _injected = null;
|
|
32
|
-
|
|
33
92
|
/**
|
|
34
93
|
* 注入与 `import.meta.env` 同形态的对象(键如 `VITE_*`、`DEV`)。
|
|
35
|
-
* @param {Record<string, unknown> | null | undefined} env
|
|
36
94
|
*/
|
|
37
95
|
function setWebExtendPluginEnv(env) {
|
|
38
|
-
|
|
96
|
+
_injected = env && typeof env === 'object' ? env : null;
|
|
39
97
|
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @returns {Record<string, unknown> | null}
|
|
43
|
-
*/
|
|
44
98
|
function getEnvObject() {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
99
|
+
if (_injected) {
|
|
100
|
+
return _injected;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const g = typeof globalThis !== 'undefined' ? globalThis : undefined;
|
|
104
|
+
const raw = g;
|
|
105
|
+
if (raw && raw.__WEP_ENV__ && typeof raw.__WEP_ENV__ === 'object') {
|
|
106
|
+
return raw.__WEP_ENV__;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
/* ignore */
|
|
52
111
|
}
|
|
53
|
-
|
|
54
|
-
/* ignore */
|
|
55
|
-
}
|
|
56
|
-
return null
|
|
112
|
+
return null;
|
|
57
113
|
}
|
|
58
|
-
|
|
59
114
|
/**
|
|
60
115
|
* 读取注入环境中的字符串配置(`VITE_*` / `PLUGIN_*` 等价键由调用方传入)。
|
|
61
|
-
* @param {string} key
|
|
62
|
-
* @returns {string|undefined}
|
|
63
116
|
*/
|
|
64
117
|
function readInjectedEnvKey(key) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
118
|
+
const o = getEnvObject();
|
|
119
|
+
if (!o || !(key in o)) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
const v = o[key];
|
|
123
|
+
if (v === undefined || v === '') {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
return String(v);
|
|
74
127
|
}
|
|
75
|
-
|
|
76
|
-
/** @returns {boolean} */
|
|
77
128
|
function readInjectedEnvDev() {
|
|
78
|
-
|
|
79
|
-
|
|
129
|
+
const o = getEnvObject();
|
|
130
|
+
return !!(o && o.DEV === true);
|
|
80
131
|
}
|
|
81
132
|
|
|
82
133
|
/**
|
|
83
134
|
* 从注入环境与 `process.env` 解析 `VITE_*` / `PLUGIN_*` 键。
|
|
84
135
|
*/
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* @param {string} key
|
|
88
|
-
* @returns {string|undefined}
|
|
89
|
-
*/
|
|
90
136
|
function readProcessEnv(key) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
137
|
+
try {
|
|
138
|
+
if (typeof process !== 'undefined' && process.env && key in process.env) {
|
|
139
|
+
const v = process.env[key];
|
|
140
|
+
if (v !== undefined && v !== '') {
|
|
141
|
+
return String(v);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
97
144
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
145
|
+
catch {
|
|
146
|
+
/* ignore */
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
102
149
|
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* @param {string} viteStyleKey
|
|
106
|
-
* @returns {string|null}
|
|
107
|
-
*/
|
|
108
150
|
function viteKeyToPluginAlternate(viteStyleKey) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
151
|
+
if (typeof viteStyleKey !== 'string' || !viteStyleKey.startsWith('VITE_')) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
return `PLUGIN_${viteStyleKey.slice(5)}`;
|
|
113
155
|
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* @param {string} key
|
|
117
|
-
* @param {string} [fallback='']
|
|
118
|
-
*/
|
|
119
156
|
function resolveBundledEnv(key, fallback = '') {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const first = fromInjected === undefined || fromInjected === null ? fromProcess : fromInjected;
|
|
128
|
-
return first === undefined || first === null ? fallback : first
|
|
157
|
+
const alt = viteKeyToPluginAlternate(key);
|
|
158
|
+
const inj = readInjectedEnvKey(key);
|
|
159
|
+
const fromInjected = inj === undefined || inj === null ? (alt ? readInjectedEnvKey(alt) : undefined) : inj;
|
|
160
|
+
const proc = readProcessEnv(key);
|
|
161
|
+
const fromProcess = proc === undefined || proc === null ? (alt ? readProcessEnv(alt) : undefined) : proc;
|
|
162
|
+
const first = fromInjected === undefined || fromInjected === null ? fromProcess : fromInjected;
|
|
163
|
+
return first === undefined || first === null ? fallback : first;
|
|
129
164
|
}
|
|
130
|
-
|
|
131
|
-
/** @returns {boolean} */
|
|
132
165
|
function resolveBundledIsDev() {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
166
|
+
try {
|
|
167
|
+
if (readInjectedEnvDev()) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
136
170
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
try {
|
|
141
|
-
if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'development') {
|
|
142
|
-
return true
|
|
171
|
+
catch {
|
|
172
|
+
/* ignore */
|
|
143
173
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
174
|
+
try {
|
|
175
|
+
if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'development') {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
/* ignore */
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
148
183
|
}
|
|
149
184
|
|
|
150
185
|
/**
|
|
151
|
-
*
|
|
152
|
-
* @module runtime/path-host-utils
|
|
186
|
+
* 清单模式与静态清单 URL 配置解析(显式 options + VITE_* / PLUGIN_* 环境)。
|
|
153
187
|
*/
|
|
188
|
+
const EK$1 = webExtendPluginEnvKeys;
|
|
189
|
+
function resolveManifestModeFromInputs(userMode) {
|
|
190
|
+
if (userMode !== undefined && userMode !== null && String(userMode).trim() !== '') {
|
|
191
|
+
const s = String(userMode).trim().toLowerCase();
|
|
192
|
+
if (s === 'static' || s === 'api') {
|
|
193
|
+
return s;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const e = resolveBundledEnv(EK$1.manifestMode, '');
|
|
197
|
+
if (e) {
|
|
198
|
+
const s = String(e).trim().toLowerCase();
|
|
199
|
+
if (s === 'static' || s === 'api') {
|
|
200
|
+
return s;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return defaultManifestMode;
|
|
204
|
+
}
|
|
205
|
+
function resolveStaticManifestUrlFromInputs(userUrl) {
|
|
206
|
+
if (userUrl !== undefined && userUrl !== null) {
|
|
207
|
+
const s = String(userUrl).trim();
|
|
208
|
+
if (s !== '') {
|
|
209
|
+
return s;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return String(resolveBundledEnv(EK$1.staticManifestUrl, '') || '').trim();
|
|
213
|
+
}
|
|
154
214
|
|
|
155
215
|
/**
|
|
156
|
-
*
|
|
216
|
+
* 路径与脚本主机名校验工具。
|
|
157
217
|
*/
|
|
158
218
|
function ensureLeadingPath(p) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
219
|
+
const t = String(p || '').trim();
|
|
220
|
+
if (!t) {
|
|
221
|
+
return '/';
|
|
222
|
+
}
|
|
223
|
+
return t.startsWith('/') ? t : `/${t}`;
|
|
164
224
|
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* @param {string} hostname
|
|
168
|
-
*/
|
|
169
225
|
function normalizeHost(hostname) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
226
|
+
if (!hostname) {
|
|
227
|
+
return '';
|
|
228
|
+
}
|
|
229
|
+
const h = hostname.toLowerCase();
|
|
230
|
+
if (h.startsWith('[') && h.endsWith(']')) {
|
|
231
|
+
return h.slice(1, -1);
|
|
232
|
+
}
|
|
233
|
+
return h;
|
|
178
234
|
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* @param {string[]} hostnames
|
|
182
|
-
* @returns {Set<string>}
|
|
183
|
-
*/
|
|
184
235
|
function buildAllowedScriptHostsSet(hostnames) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
236
|
+
const s = new Set();
|
|
237
|
+
for (const h of hostnames) {
|
|
238
|
+
const n = normalizeHost(h);
|
|
239
|
+
if (n) {
|
|
240
|
+
s.add(n);
|
|
241
|
+
}
|
|
190
242
|
}
|
|
191
|
-
|
|
192
|
-
return s
|
|
243
|
+
return s;
|
|
193
244
|
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* @param {string} url
|
|
197
|
-
* @param {Set<string>} hostSet
|
|
198
|
-
*/
|
|
199
245
|
function isScriptHostAllowed(url, hostSet) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
246
|
+
if (typeof window === 'undefined') {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const u = new URL(url, window.location.origin);
|
|
251
|
+
const h = normalizeHost(u.hostname);
|
|
252
|
+
return hostSet.has(h);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
210
257
|
}
|
|
211
258
|
|
|
212
259
|
/**
|
|
213
260
|
* 合并用户、环境与默认配置得到运行时选项。
|
|
214
|
-
* @module runtime/resolve-runtime-options
|
|
215
261
|
*/
|
|
216
|
-
|
|
217
|
-
|
|
218
262
|
const DEF = defaultWebExtendPluginRuntime;
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* @param {string|undefined} userVal
|
|
222
|
-
* @param {string} envKey
|
|
223
|
-
* @param {RequestCredentials} fallback
|
|
224
|
-
*/
|
|
263
|
+
const EK = webExtendPluginEnvKeys;
|
|
225
264
|
function resolveManifestCredentials(userVal, envKey, fallback) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
265
|
+
if (userVal !== undefined) {
|
|
266
|
+
const s = String(userVal);
|
|
267
|
+
if (s === 'include' || s === 'omit' || s === 'same-origin') {
|
|
268
|
+
return s;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const e = resolveBundledEnv(envKey, '');
|
|
272
|
+
if (e === 'include' || e === 'omit' || e === 'same-origin') {
|
|
273
|
+
return e;
|
|
230
274
|
}
|
|
231
|
-
|
|
232
|
-
const e = resolveBundledEnv(envKey, '');
|
|
233
|
-
if (e === 'include' || e === 'omit' || e === 'same-origin') {
|
|
234
|
-
return e
|
|
235
|
-
}
|
|
236
|
-
return fallback
|
|
275
|
+
return fallback;
|
|
237
276
|
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* @param {number|undefined} userVal
|
|
241
|
-
* @param {string} envKey
|
|
242
|
-
* @param {number} fallback
|
|
243
|
-
*/
|
|
244
277
|
function resolvePositiveInt(userVal, envKey, fallback) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
278
|
+
if (typeof userVal === 'number' && Number.isFinite(userVal) && userVal > 0) {
|
|
279
|
+
return Math.floor(userVal);
|
|
280
|
+
}
|
|
281
|
+
const raw = resolveBundledEnv(envKey, '');
|
|
282
|
+
const n = raw ? parseInt(raw, 10) : NaN;
|
|
283
|
+
if (Number.isFinite(n) && n > 0) {
|
|
284
|
+
return n;
|
|
285
|
+
}
|
|
286
|
+
return fallback;
|
|
254
287
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
* 合并用户、环境变量与 `defaultWebExtendPluginRuntime`,得到完整运行时选项(宿主可只传需要覆盖的字段)。
|
|
258
|
-
* @param {WebExtendPluginRuntimeOptions} [user]
|
|
259
|
-
* @returns {object}
|
|
260
|
-
*/
|
|
288
|
+
/** 合并用户、环境变量与 `defaultWebExtendPluginRuntime`,得到完整运行时选项(宿主可只传需要覆盖的字段)。 */
|
|
289
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
261
290
|
function resolveRuntimeOptions$1(user = {}) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
.split(',')
|
|
280
|
-
.map((s) => s.trim())
|
|
281
|
-
.filter(Boolean)
|
|
282
|
-
}
|
|
283
|
-
return [...DEF.defaultImplicitDevPluginIds]
|
|
284
|
-
})();
|
|
285
|
-
|
|
286
|
-
const allowedScriptHosts =
|
|
287
|
-
Array.isArray(user.allowedScriptHosts) && user.allowedScriptHosts.length > 0
|
|
288
|
-
? user.allowedScriptHosts.map((h) => normalizeHost(String(h))).filter(Boolean)
|
|
289
|
-
: (() => {
|
|
290
|
-
const e = resolveBundledEnv('VITE_WEB_PLUGIN_ALLOWED_SCRIPT_HOSTS', '');
|
|
291
|
-
if (e) {
|
|
292
|
-
return e
|
|
293
|
-
.split(',')
|
|
294
|
-
.map((s) => normalizeHost(s.trim()))
|
|
295
|
-
.filter(Boolean)
|
|
296
|
-
}
|
|
297
|
-
return [...DEF.allowedScriptHosts]
|
|
291
|
+
const manifestBaseRaw = user.manifestBase !== undefined && user.manifestBase !== ''
|
|
292
|
+
? String(user.manifestBase)
|
|
293
|
+
: resolveBundledEnv(EK.manifestBase, DEF.manifestBase) || DEF.manifestBase;
|
|
294
|
+
const manifestListPath = ensureLeadingPath(user.manifestListPath !== undefined && user.manifestListPath !== ''
|
|
295
|
+
? user.manifestListPath
|
|
296
|
+
: resolveBundledEnv(EK.manifestListPath, DEF.manifestListPath));
|
|
297
|
+
const defaultImplicitDevPluginIds = Array.isArray(user.defaultImplicitDevPluginIds)
|
|
298
|
+
? user.defaultImplicitDevPluginIds.map(String).filter(Boolean)
|
|
299
|
+
: (() => {
|
|
300
|
+
const e = resolveBundledEnv(EK.implicitDevIds, '');
|
|
301
|
+
if (e) {
|
|
302
|
+
return e
|
|
303
|
+
.split(',')
|
|
304
|
+
.map((s) => s.trim())
|
|
305
|
+
.filter(Boolean);
|
|
306
|
+
}
|
|
307
|
+
return [...DEF.defaultImplicitDevPluginIds];
|
|
298
308
|
})();
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
return [...DEF.bridgeAllowedPathPrefixes]
|
|
309
|
+
const allowedScriptHosts = Array.isArray(user.allowedScriptHosts) && user.allowedScriptHosts.length > 0
|
|
310
|
+
? user.allowedScriptHosts.map((h) => normalizeHost(String(h))).filter(Boolean)
|
|
311
|
+
: (() => {
|
|
312
|
+
const e = resolveBundledEnv(EK.allowedScriptHosts, '');
|
|
313
|
+
if (e) {
|
|
314
|
+
return e
|
|
315
|
+
.split(',')
|
|
316
|
+
.map((s) => normalizeHost(s.trim()))
|
|
317
|
+
.filter(Boolean);
|
|
318
|
+
}
|
|
319
|
+
return [...DEF.allowedScriptHosts];
|
|
312
320
|
})();
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
?
|
|
354
|
-
:
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
321
|
+
const bridgeAllowedPathPrefixes = Array.isArray(user.bridgeAllowedPathPrefixes) && user.bridgeAllowedPathPrefixes.length > 0
|
|
322
|
+
? user.bridgeAllowedPathPrefixes.map((p) => ensureLeadingPath(p)).filter(Boolean)
|
|
323
|
+
: (() => {
|
|
324
|
+
const e = resolveBundledEnv(EK.bridgePrefixes, '');
|
|
325
|
+
if (e) {
|
|
326
|
+
return e
|
|
327
|
+
.split(',')
|
|
328
|
+
.map((s) => ensureLeadingPath(s.trim()))
|
|
329
|
+
.filter(Boolean);
|
|
330
|
+
}
|
|
331
|
+
return [...DEF.bridgeAllowedPathPrefixes];
|
|
332
|
+
})();
|
|
333
|
+
const manifestMode = resolveManifestModeFromInputs(user.manifestMode);
|
|
334
|
+
const staticManifestUrl = resolveStaticManifestUrlFromInputs(user.staticManifestUrl);
|
|
335
|
+
const isDevResolved = user.isDev !== undefined ? user.isDev : resolveBundledIsDev();
|
|
336
|
+
const devFallbackStaticManifestUrl = (() => {
|
|
337
|
+
if (user.devFallbackStaticManifestUrl !== undefined && String(user.devFallbackStaticManifestUrl).trim() !== '') {
|
|
338
|
+
return String(user.devFallbackStaticManifestUrl).trim();
|
|
339
|
+
}
|
|
340
|
+
const e = resolveBundledEnv(EK.devFallbackManifestUrl, '');
|
|
341
|
+
if (e) {
|
|
342
|
+
return e.trim();
|
|
343
|
+
}
|
|
344
|
+
return String(DEF.devFallbackStaticManifestUrl).trim();
|
|
345
|
+
})();
|
|
346
|
+
const hostLayoutComponent = user.hostLayoutComponent;
|
|
347
|
+
const pluginRoutesParentName = (() => {
|
|
348
|
+
if (user.pluginRoutesParentName !== undefined) {
|
|
349
|
+
return String(user.pluginRoutesParentName).trim();
|
|
350
|
+
}
|
|
351
|
+
if (hostLayoutComponent != null) {
|
|
352
|
+
return String(DEF.pluginRoutesParentName).trim();
|
|
353
|
+
}
|
|
354
|
+
return '';
|
|
355
|
+
})();
|
|
356
|
+
const pluginMountRaw = user.pluginMountPath !== undefined && String(user.pluginMountPath).trim() !== ''
|
|
357
|
+
? String(user.pluginMountPath).trim()
|
|
358
|
+
: String(resolveBundledEnv(EK.mountPath, '') || DEF.pluginMountPath).trim();
|
|
359
|
+
const pluginMountPath = ensureLeadingPath(pluginMountRaw || DEF.pluginMountPath);
|
|
360
|
+
const pluginHostRouteMeta = user.pluginHostRouteMeta !== undefined && user.pluginHostRouteMeta !== null
|
|
361
|
+
? user.pluginHostRouteMeta
|
|
362
|
+
: undefined;
|
|
363
|
+
const ensurePluginHostRoute = user.ensurePluginHostRoute !== false;
|
|
364
|
+
const devManifestFallback = (() => {
|
|
365
|
+
if (manifestMode === 'static') {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
if (user.devManifestFallback === false) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
if (user.devManifestFallback === true) {
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
const envFlag = resolveBundledEnv(EK.devManifestFallback, '');
|
|
375
|
+
if (envFlag === '0' || envFlag === 'false') {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
if (envFlag === '1' || envFlag === 'true') {
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
return !!isDevResolved;
|
|
382
|
+
})();
|
|
383
|
+
return {
|
|
384
|
+
manifestBase: manifestBaseRaw.replace(/\/$/, '') || DEF.manifestBase.replace(/\/$/, ''),
|
|
385
|
+
manifestListPath,
|
|
386
|
+
manifestMode,
|
|
387
|
+
staticManifestUrl,
|
|
388
|
+
devManifestFallback,
|
|
389
|
+
devFallbackStaticManifestUrl,
|
|
390
|
+
manifestFetchCredentials: resolveManifestCredentials(user.manifestFetchCredentials, EK.manifestCredentials, DEF.manifestFetchCredentials),
|
|
391
|
+
isDev: isDevResolved,
|
|
392
|
+
webPluginDevOrigin: user.webPluginDevOrigin !== undefined
|
|
393
|
+
? user.webPluginDevOrigin
|
|
394
|
+
: resolveBundledEnv(EK.webPluginDevOrigin, ''),
|
|
395
|
+
webPluginDevIds: user.webPluginDevIds !== undefined ? user.webPluginDevIds : resolveBundledEnv(EK.webPluginDevIds, ''),
|
|
396
|
+
webPluginDevMapJson: user.webPluginDevMapJson !== undefined
|
|
397
|
+
? user.webPluginDevMapJson
|
|
398
|
+
: resolveBundledEnv(EK.webPluginDevMap, ''),
|
|
399
|
+
webPluginDevEntryPath: ensureLeadingPath(user.webPluginDevEntryPath !== undefined && user.webPluginDevEntryPath !== ''
|
|
400
|
+
? user.webPluginDevEntryPath
|
|
401
|
+
: resolveBundledEnv(EK.devEntry, DEF.webPluginDevEntryPath)),
|
|
402
|
+
devPingPath: ensureLeadingPath(user.devPingPath !== undefined && user.devPingPath !== ''
|
|
403
|
+
? user.devPingPath
|
|
404
|
+
: resolveBundledEnv(EK.devPing, DEF.devPingPath)),
|
|
405
|
+
devReloadSsePath: ensureLeadingPath(user.devReloadSsePath !== undefined && user.devReloadSsePath !== ''
|
|
406
|
+
? user.devReloadSsePath
|
|
407
|
+
: resolveBundledEnv(EK.devSse, DEF.devReloadSsePath)),
|
|
408
|
+
devPingTimeoutMs: resolvePositiveInt(user.devPingTimeoutMs, EK.devPingTimeout, DEF.devPingTimeoutMs),
|
|
409
|
+
defaultImplicitDevPluginIds,
|
|
410
|
+
allowedScriptHosts,
|
|
411
|
+
bridgeAllowedPathPrefixes,
|
|
412
|
+
bootstrapSummary: user.bootstrapSummary,
|
|
413
|
+
hostLayoutComponent,
|
|
414
|
+
pluginMountPath,
|
|
415
|
+
pluginHostRouteMeta,
|
|
416
|
+
ensurePluginHostRoute,
|
|
417
|
+
pluginRoutesParentName,
|
|
418
|
+
...(typeof user.fetchManifest === 'function' ? { fetchManifest: user.fetchManifest } : {}),
|
|
419
|
+
...(typeof user.transformRoutes === 'function' ? { transformRoutes: user.transformRoutes } : {}),
|
|
420
|
+
...(typeof user.interceptRegisterRoutes === 'function'
|
|
421
|
+
? { interceptRegisterRoutes: user.interceptRegisterRoutes }
|
|
422
|
+
: {}),
|
|
423
|
+
...(typeof user.adaptRouteDeclarations === 'function'
|
|
424
|
+
? { adaptRouteDeclarations: user.adaptRouteDeclarations }
|
|
425
|
+
: {}),
|
|
426
|
+
...(typeof user.applyPluginMenuItems === 'function'
|
|
427
|
+
? { applyPluginMenuItems: user.applyPluginMenuItems }
|
|
428
|
+
: {}),
|
|
429
|
+
...(typeof user.revokePluginMenuItems === 'function'
|
|
430
|
+
? { revokePluginMenuItems: user.revokePluginMenuItems }
|
|
431
|
+
: {}),
|
|
432
|
+
...(user.hostContext !== undefined &&
|
|
433
|
+
user.hostContext !== null &&
|
|
434
|
+
typeof user.hostContext === 'object' &&
|
|
435
|
+
!Array.isArray(user.hostContext)
|
|
436
|
+
? { hostContext: user.hostContext }
|
|
437
|
+
: {}),
|
|
438
|
+
...(typeof user.onBeforePluginActivate === 'function'
|
|
439
|
+
? { onBeforePluginActivate: user.onBeforePluginActivate }
|
|
440
|
+
: {}),
|
|
441
|
+
...(typeof user.onAfterPluginActivate === 'function'
|
|
442
|
+
? { onAfterPluginActivate: user.onAfterPluginActivate }
|
|
443
|
+
: {}),
|
|
444
|
+
...(typeof user.onPluginActivateError === 'function'
|
|
445
|
+
? { onPluginActivateError: user.onPluginActivateError }
|
|
446
|
+
: {})
|
|
447
|
+
};
|
|
364
448
|
}
|
|
365
449
|
|
|
366
450
|
/**
|
|
367
|
-
*
|
|
368
|
-
* @param {{ manifestUrl: string, credentials: RequestCredentials }} ctx
|
|
451
|
+
* 将裸 `{ plugins }` 与 `{ code, data: { plugins } }` 式响应解包为清单对象。
|
|
369
452
|
*/
|
|
453
|
+
function unwrapNestedManifestBody(body) {
|
|
454
|
+
if (!body || typeof body !== 'object') {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
const o = body;
|
|
458
|
+
if (Array.isArray(o.plugins)) {
|
|
459
|
+
return o;
|
|
460
|
+
}
|
|
461
|
+
const d = o.data;
|
|
462
|
+
if (d && typeof d === 'object') {
|
|
463
|
+
const inner = d;
|
|
464
|
+
if (Array.isArray(inner.plugins)) {
|
|
465
|
+
return inner;
|
|
466
|
+
}
|
|
467
|
+
if ('plugins' in inner) {
|
|
468
|
+
return inner;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return d !== undefined && d !== null && typeof d === 'object' ? d : o;
|
|
472
|
+
}
|
|
473
|
+
|
|
370
474
|
async function defaultFetchWebPluginManifest$1(ctx) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
475
|
+
const { manifestUrl, credentials } = ctx;
|
|
476
|
+
try {
|
|
477
|
+
const res = await fetch(manifestUrl, { credentials });
|
|
478
|
+
if (!res.ok) {
|
|
479
|
+
return { ok: false, status: res.status, data: null };
|
|
480
|
+
}
|
|
481
|
+
const body = await res.json();
|
|
482
|
+
const data = unwrapNestedManifestBody(body);
|
|
483
|
+
if (!data || typeof data !== 'object') {
|
|
484
|
+
return {
|
|
485
|
+
ok: false,
|
|
486
|
+
error: new Error('[wep] invalid manifest response body'),
|
|
487
|
+
data: null
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
return { ok: true, data };
|
|
491
|
+
}
|
|
492
|
+
catch (e) {
|
|
493
|
+
return { ok: false, error: e, data: null };
|
|
376
494
|
}
|
|
377
|
-
const data = await res.json();
|
|
378
|
-
return { ok: true, data }
|
|
379
|
-
} catch (e) {
|
|
380
|
-
return { ok: false, error: e, data: null }
|
|
381
|
-
}
|
|
382
495
|
}
|
|
383
496
|
|
|
384
497
|
function getDefaultExportFromCjs (x) {
|
|
@@ -3106,1336 +3219,1348 @@ function requireSemver () {
|
|
|
3106
3219
|
var semverExports = requireSemver();
|
|
3107
3220
|
var semver = /*@__PURE__*/getDefaultExportFromCjs(semverExports);
|
|
3108
3221
|
|
|
3109
|
-
/** 与清单服务 `hostPluginApiVersion` 对齐,用于 `semver` 校验。 */
|
|
3110
|
-
const HOST_PLUGIN_API_VERSION = '1.0.0';
|
|
3111
|
-
|
|
3112
|
-
/** 控制台日志与横幅使用的短名称。 */
|
|
3113
|
-
const RUNTIME_CONSOLE_LABEL = 'web-extend-plugin-vue2';
|
|
3114
|
-
|
|
3115
3222
|
/**
|
|
3116
|
-
*
|
|
3117
|
-
* @module runtime/dev-map
|
|
3223
|
+
* `disposeWebPlugin` 使用的菜单撤销钩子:在 `bootstrapPlugins` 中用 `resolveRuntimeOptions` 结果注册。
|
|
3118
3224
|
*/
|
|
3225
|
+
let revokePluginMenuItems;
|
|
3226
|
+
function setRevokePluginMenuItems(fn) {
|
|
3227
|
+
revokePluginMenuItems = fn;
|
|
3228
|
+
}
|
|
3229
|
+
function revokePluginMenusIfConfigured(pluginId) {
|
|
3230
|
+
if (typeof revokePluginMenuItems === 'function') {
|
|
3231
|
+
try {
|
|
3232
|
+
revokePluginMenuItems(pluginId);
|
|
3233
|
+
}
|
|
3234
|
+
catch (e) {
|
|
3235
|
+
console.warn('[wep] revokePluginMenuItems failed', pluginId, e);
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3119
3239
|
|
|
3120
3240
|
/**
|
|
3121
|
-
*
|
|
3241
|
+
* 开发模式插件 URL 映射(显式 JSON + 隐式 dev 探测)。
|
|
3122
3242
|
*/
|
|
3123
3243
|
function parseWebPluginDevMapExplicit(opts) {
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3244
|
+
if (!opts.isDev) {
|
|
3245
|
+
return null;
|
|
3246
|
+
}
|
|
3247
|
+
const raw = opts.webPluginDevMapJson;
|
|
3248
|
+
if (raw === undefined || raw === null || String(raw).trim() === '') {
|
|
3249
|
+
return null;
|
|
3250
|
+
}
|
|
3251
|
+
try {
|
|
3252
|
+
const map = JSON.parse(String(raw));
|
|
3253
|
+
return map && typeof map === 'object' ? map : null;
|
|
3254
|
+
}
|
|
3255
|
+
catch {
|
|
3256
|
+
console.warn('[wep] invalid webPluginDevMapJson / VITE_WEB_PLUGIN_DEV_MAP');
|
|
3257
|
+
return null;
|
|
3258
|
+
}
|
|
3138
3259
|
}
|
|
3139
|
-
|
|
3140
|
-
/**
|
|
3141
|
-
* @param {Record<string, string>} implicit
|
|
3142
|
-
* @param {Record<string, string>|null} explicit
|
|
3143
|
-
*/
|
|
3144
3260
|
function mergeDevMaps(implicit, explicit) {
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3261
|
+
const i = implicit && typeof implicit === 'object' ? implicit : {};
|
|
3262
|
+
const e = explicit && typeof explicit === 'object' ? explicit : {};
|
|
3263
|
+
return { ...i, ...e };
|
|
3148
3264
|
}
|
|
3149
|
-
|
|
3150
|
-
/**
|
|
3151
|
-
* @param {object} opts
|
|
3152
|
-
* @param {boolean} opts.isDev
|
|
3153
|
-
* @param {string|undefined|null} opts.webPluginDevOrigin
|
|
3154
|
-
* @param {string|undefined|null} opts.webPluginDevIds
|
|
3155
|
-
* @param {string[]} opts.defaultImplicitDevPluginIds
|
|
3156
|
-
* @param {string} opts.devPingPath
|
|
3157
|
-
* @param {number} opts.devPingTimeoutMs
|
|
3158
|
-
* @param {string} opts.webPluginDevEntryPath
|
|
3159
|
-
* @param {Set<string>} hostSet
|
|
3160
|
-
*/
|
|
3161
3265
|
async function buildImplicitWebPluginDevMap(opts, hostSet) {
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
return {}
|
|
3171
|
-
}
|
|
3172
|
-
if (!isScriptHostAllowed(`${origin}/`, hostSet)) {
|
|
3173
|
-
return {}
|
|
3174
|
-
}
|
|
3175
|
-
|
|
3176
|
-
const idsRaw = opts.webPluginDevIds;
|
|
3177
|
-
const ids =
|
|
3178
|
-
idsRaw !== undefined && idsRaw !== null && String(idsRaw).trim() !== ''
|
|
3179
|
-
? String(idsRaw)
|
|
3180
|
-
.split(',')
|
|
3181
|
-
.map((s) => s.trim())
|
|
3182
|
-
.filter(Boolean)
|
|
3183
|
-
: [...opts.defaultImplicitDevPluginIds];
|
|
3184
|
-
|
|
3185
|
-
if (ids.length === 0) {
|
|
3186
|
-
return {}
|
|
3187
|
-
}
|
|
3188
|
-
|
|
3189
|
-
const base = origin.replace(/\/$/, '');
|
|
3190
|
-
const pingUrl = `${base}${opts.devPingPath}`;
|
|
3191
|
-
try {
|
|
3192
|
-
const ctrl = new AbortController();
|
|
3193
|
-
const timer = setTimeout(() => ctrl.abort(), opts.devPingTimeoutMs);
|
|
3194
|
-
const r = await fetch(pingUrl, {
|
|
3195
|
-
mode: 'cors',
|
|
3196
|
-
cache: 'no-store',
|
|
3197
|
-
signal: ctrl.signal
|
|
3198
|
-
});
|
|
3199
|
-
clearTimeout(timer);
|
|
3200
|
-
if (!r.ok) {
|
|
3201
|
-
return {}
|
|
3266
|
+
if (!opts.isDev) {
|
|
3267
|
+
return {};
|
|
3268
|
+
}
|
|
3269
|
+
const origin = opts.webPluginDevOrigin === undefined || opts.webPluginDevOrigin === null
|
|
3270
|
+
? ''
|
|
3271
|
+
: String(opts.webPluginDevOrigin).trim();
|
|
3272
|
+
if (!origin) {
|
|
3273
|
+
return {};
|
|
3202
3274
|
}
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
return {}
|
|
3275
|
+
if (!isScriptHostAllowed(`${origin}/`, hostSet)) {
|
|
3276
|
+
return {};
|
|
3206
3277
|
}
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3278
|
+
const idsRaw = opts.webPluginDevIds;
|
|
3279
|
+
const ids = idsRaw !== undefined && idsRaw !== null && String(idsRaw).trim() !== ''
|
|
3280
|
+
? String(idsRaw)
|
|
3281
|
+
.split(',')
|
|
3282
|
+
.map((s) => s.trim())
|
|
3283
|
+
.filter(Boolean)
|
|
3284
|
+
: [...opts.defaultImplicitDevPluginIds];
|
|
3285
|
+
if (ids.length === 0) {
|
|
3286
|
+
return {};
|
|
3287
|
+
}
|
|
3288
|
+
const base = origin.replace(/\/$/, '');
|
|
3289
|
+
const pingUrl = `${base}${opts.devPingPath}`;
|
|
3290
|
+
try {
|
|
3291
|
+
const ctrl = new AbortController();
|
|
3292
|
+
const timer = setTimeout(() => ctrl.abort(), opts.devPingTimeoutMs);
|
|
3293
|
+
const r = await fetch(pingUrl, {
|
|
3294
|
+
mode: 'cors',
|
|
3295
|
+
cache: 'no-store',
|
|
3296
|
+
signal: ctrl.signal
|
|
3297
|
+
});
|
|
3298
|
+
clearTimeout(timer);
|
|
3299
|
+
if (!r.ok) {
|
|
3300
|
+
return {};
|
|
3301
|
+
}
|
|
3302
|
+
const body = (await r.text()).trim();
|
|
3303
|
+
if (body !== 'ok') {
|
|
3304
|
+
return {};
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
catch {
|
|
3308
|
+
return {};
|
|
3309
|
+
}
|
|
3310
|
+
const pathPart = opts.webPluginDevEntryPath;
|
|
3311
|
+
const map = {};
|
|
3312
|
+
for (const id of ids) {
|
|
3313
|
+
map[id] = `${base}${pathPart}`;
|
|
3314
|
+
}
|
|
3315
|
+
if (ids.length) {
|
|
3316
|
+
console.info('[wep] plugin dev server', base, '→ implicit entries', pathPart, ids.join(', '));
|
|
3317
|
+
}
|
|
3318
|
+
return map;
|
|
3220
3319
|
}
|
|
3221
3320
|
|
|
3222
3321
|
/**
|
|
3223
3322
|
* 开发模式下插件热更新 SSE(按 dev 映射中的 origin 连接)。
|
|
3224
|
-
* @module runtime/dev-reload-sse
|
|
3225
3323
|
*/
|
|
3226
|
-
|
|
3227
|
-
/** @type {Map<string, EventSource>} */
|
|
3228
3324
|
const pluginDevEventSources = new Map();
|
|
3229
|
-
|
|
3230
3325
|
let pluginDevBeforeUnloadRegistered = false;
|
|
3231
|
-
|
|
3232
3326
|
function closeAllPluginDevEventSources() {
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3327
|
+
for (const es of pluginDevEventSources.values()) {
|
|
3328
|
+
try {
|
|
3329
|
+
es.close();
|
|
3330
|
+
}
|
|
3331
|
+
catch {
|
|
3332
|
+
/* ignore */
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
pluginDevEventSources.clear();
|
|
3239
3336
|
}
|
|
3240
|
-
|
|
3241
3337
|
function ensurePluginDevBeforeUnload() {
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3338
|
+
if (pluginDevBeforeUnloadRegistered || typeof window === 'undefined') {
|
|
3339
|
+
return;
|
|
3340
|
+
}
|
|
3341
|
+
pluginDevBeforeUnloadRegistered = true;
|
|
3342
|
+
window.addEventListener('beforeunload', closeAllPluginDevEventSources);
|
|
3247
3343
|
}
|
|
3248
|
-
|
|
3249
|
-
/**
|
|
3250
|
-
* @param {string} origin
|
|
3251
|
-
* @param {Set<string>} hostSet
|
|
3252
|
-
*/
|
|
3253
3344
|
function isDevOriginAllowedForSse(origin, hostSet) {
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3345
|
+
try {
|
|
3346
|
+
const u = new URL(origin);
|
|
3347
|
+
return hostSet.has(normalizeHost(u.hostname));
|
|
3348
|
+
}
|
|
3349
|
+
catch {
|
|
3350
|
+
return false;
|
|
3351
|
+
}
|
|
3260
3352
|
}
|
|
3261
|
-
|
|
3262
|
-
/**
|
|
3263
|
-
* @param {string} origin
|
|
3264
|
-
* @param {boolean} isDev
|
|
3265
|
-
* @param {Set<string>} hostSet
|
|
3266
|
-
* @param {string} ssePath
|
|
3267
|
-
*/
|
|
3268
3353
|
function startPluginDevReloadSse(origin, isDev, hostSet, ssePath) {
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3354
|
+
if (!isDev || pluginDevEventSources.has(origin)) {
|
|
3355
|
+
return;
|
|
3356
|
+
}
|
|
3357
|
+
if (!isDevOriginAllowedForSse(origin, hostSet)) {
|
|
3358
|
+
return;
|
|
3359
|
+
}
|
|
3360
|
+
ensurePluginDevBeforeUnload();
|
|
3361
|
+
const base = origin.replace(/\/$/, '');
|
|
3362
|
+
const url = `${base}${ssePath}`;
|
|
3363
|
+
try {
|
|
3364
|
+
const es = new EventSource(url);
|
|
3365
|
+
pluginDevEventSources.set(origin, es);
|
|
3366
|
+
es.addEventListener('reload', () => {
|
|
3367
|
+
window.location.reload();
|
|
3368
|
+
});
|
|
3369
|
+
es.onopen = () => {
|
|
3370
|
+
console.info('[wep] dev reload SSE', url);
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
3373
|
+
catch (e) {
|
|
3374
|
+
console.warn('[wep] EventSource failed', url, e);
|
|
3375
|
+
}
|
|
3290
3376
|
}
|
|
3291
|
-
|
|
3292
|
-
/**
|
|
3293
|
-
* @param {Record<string, string>|null|undefined} devMap
|
|
3294
|
-
* @param {boolean} isDev
|
|
3295
|
-
* @param {Set<string>} hostSet
|
|
3296
|
-
* @param {string} ssePath
|
|
3297
|
-
*/
|
|
3298
3377
|
function startPluginDevSseForMap(devMap, isDev, hostSet, ssePath) {
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
}
|
|
3302
|
-
const origins = new Set();
|
|
3303
|
-
for (const entry of Object.values(devMap)) {
|
|
3304
|
-
if (typeof entry !== 'string') {
|
|
3305
|
-
continue
|
|
3378
|
+
if (!isDev || !devMap || typeof window === 'undefined') {
|
|
3379
|
+
return;
|
|
3306
3380
|
}
|
|
3307
|
-
const
|
|
3308
|
-
|
|
3309
|
-
|
|
3381
|
+
const origins = new Set();
|
|
3382
|
+
for (const entry of Object.values(devMap)) {
|
|
3383
|
+
if (typeof entry !== 'string') {
|
|
3384
|
+
continue;
|
|
3385
|
+
}
|
|
3386
|
+
const t = entry.trim();
|
|
3387
|
+
if (!t) {
|
|
3388
|
+
continue;
|
|
3389
|
+
}
|
|
3390
|
+
try {
|
|
3391
|
+
origins.add(new URL(t, window.location.href).origin);
|
|
3392
|
+
}
|
|
3393
|
+
catch {
|
|
3394
|
+
/* skip */
|
|
3395
|
+
}
|
|
3310
3396
|
}
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
} catch {
|
|
3314
|
-
/* skip */
|
|
3397
|
+
for (const o of origins) {
|
|
3398
|
+
startPluginDevReloadSse(o, isDev, hostSet, ssePath);
|
|
3315
3399
|
}
|
|
3316
|
-
}
|
|
3317
|
-
for (const o of origins) {
|
|
3318
|
-
startPluginDevReloadSse(o, isDev, hostSet, ssePath);
|
|
3319
|
-
}
|
|
3320
3400
|
}
|
|
3321
3401
|
|
|
3322
3402
|
/**
|
|
3323
3403
|
* 动态加载脚本(去重与并发合并)。
|
|
3324
|
-
* @module runtime/load-script
|
|
3325
3404
|
*/
|
|
3326
|
-
|
|
3327
3405
|
const loadScriptMemo = new Map();
|
|
3328
|
-
|
|
3329
|
-
/**
|
|
3330
|
-
* @param {string} src
|
|
3331
|
-
* @returns {Promise<void>}
|
|
3332
|
-
*/
|
|
3333
3406
|
function loadScript(src) {
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3407
|
+
if (typeof document === 'undefined') {
|
|
3408
|
+
return Promise.reject(new Error('loadScript: no document'));
|
|
3409
|
+
}
|
|
3410
|
+
if (loadScriptMemo.has(src)) {
|
|
3411
|
+
return loadScriptMemo.get(src);
|
|
3412
|
+
}
|
|
3413
|
+
const p = new Promise((resolve, reject) => {
|
|
3414
|
+
const scripts = document.getElementsByTagName('script');
|
|
3415
|
+
for (let i = 0; i < scripts.length; i++) {
|
|
3416
|
+
const el = scripts[i];
|
|
3417
|
+
if (el.src === src) {
|
|
3418
|
+
if (el.getAttribute('data-wep-loaded') === 'true') {
|
|
3419
|
+
resolve();
|
|
3420
|
+
return;
|
|
3421
|
+
}
|
|
3422
|
+
el.addEventListener('load', () => {
|
|
3423
|
+
el.setAttribute('data-wep-loaded', 'true');
|
|
3424
|
+
resolve();
|
|
3425
|
+
}, { once: true });
|
|
3426
|
+
el.addEventListener('error', () => reject(new Error('loadScript failed: ' + src)), { once: true });
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3348
3429
|
}
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3430
|
+
const s = document.createElement('script');
|
|
3431
|
+
s.async = true;
|
|
3432
|
+
s.src = src;
|
|
3433
|
+
s.onload = () => {
|
|
3434
|
+
s.setAttribute('data-wep-loaded', 'true');
|
|
3353
3435
|
resolve();
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
);
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
const s = document.createElement('script');
|
|
3362
|
-
s.async = true;
|
|
3363
|
-
s.src = src;
|
|
3364
|
-
s.onload = () => {
|
|
3365
|
-
s.setAttribute('data-wep-loaded', 'true');
|
|
3366
|
-
resolve();
|
|
3367
|
-
};
|
|
3368
|
-
s.onerror = () => reject(new Error('loadScript failed: ' + src));
|
|
3369
|
-
document.head.appendChild(s);
|
|
3370
|
-
});
|
|
3371
|
-
loadScriptMemo.set(src, p);
|
|
3372
|
-
p.catch(() => loadScriptMemo.delete(src));
|
|
3373
|
-
return p
|
|
3436
|
+
};
|
|
3437
|
+
s.onerror = () => reject(new Error('loadScript failed: ' + src));
|
|
3438
|
+
document.head.appendChild(s);
|
|
3439
|
+
});
|
|
3440
|
+
loadScriptMemo.set(src, p);
|
|
3441
|
+
p.catch(() => loadScriptMemo.delete(src));
|
|
3442
|
+
return p;
|
|
3374
3443
|
}
|
|
3375
3444
|
|
|
3376
3445
|
let _printed = false;
|
|
3377
|
-
|
|
3378
3446
|
/** 在首次引导插件时打印一行运行时标识(非大块 ASCII art)。 */
|
|
3379
3447
|
function printRuntimeBannerOnce() {
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3448
|
+
if (_printed) {
|
|
3449
|
+
return;
|
|
3450
|
+
}
|
|
3451
|
+
_printed = true;
|
|
3452
|
+
if (typeof console !== 'undefined' && typeof console.info === 'function') {
|
|
3453
|
+
console.info(`[wep] ${RUNTIME_CONSOLE_LABEL} · host API ${HOST_PLUGIN_API_VERSION}`);
|
|
3454
|
+
}
|
|
3387
3455
|
}
|
|
3388
3456
|
|
|
3389
3457
|
/**
|
|
3390
|
-
*
|
|
3458
|
+
* 浏览器 fetch 静态 JSON 清单(与 defaultFetchWebPluginManifest 相同解包规则)。
|
|
3391
3459
|
*/
|
|
3460
|
+
async function fetchStaticManifestViaHttp(ctx) {
|
|
3461
|
+
const { manifestUrl, credentials } = ctx;
|
|
3462
|
+
try {
|
|
3463
|
+
const creds = credentials === 'omit' ? 'omit' : 'same-origin';
|
|
3464
|
+
const res = await fetch(manifestUrl, {
|
|
3465
|
+
method: 'GET',
|
|
3466
|
+
credentials: creds,
|
|
3467
|
+
cache: 'no-store'
|
|
3468
|
+
});
|
|
3469
|
+
if (!res.ok) {
|
|
3470
|
+
return {
|
|
3471
|
+
ok: false,
|
|
3472
|
+
status: res.status,
|
|
3473
|
+
error: new Error('[wep] static manifest HTTP ' + res.status),
|
|
3474
|
+
data: null
|
|
3475
|
+
};
|
|
3476
|
+
}
|
|
3477
|
+
const body = await res.json();
|
|
3478
|
+
const data = unwrapNestedManifestBody(body);
|
|
3479
|
+
if (!data || typeof data !== 'object') {
|
|
3480
|
+
return {
|
|
3481
|
+
ok: false,
|
|
3482
|
+
error: new Error('[wep] invalid static manifest JSON'),
|
|
3483
|
+
data: null
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
return { ok: true, data };
|
|
3487
|
+
}
|
|
3488
|
+
catch (e) {
|
|
3489
|
+
return { ok: false, error: e, data: null };
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3392
3492
|
|
|
3393
3493
|
/**
|
|
3394
|
-
*
|
|
3494
|
+
* 开箱:在未手工配置时,注册 `/plugin` + 宿主 Layout 的命名父路由,供 `router.addRoute(parentName, child)` 挂载插件页。
|
|
3395
3495
|
*/
|
|
3396
|
-
function
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3496
|
+
function routeNameExists(router, name) {
|
|
3497
|
+
if (!name) {
|
|
3498
|
+
return false;
|
|
3499
|
+
}
|
|
3500
|
+
if (typeof router.getRoutes === 'function') {
|
|
3501
|
+
return router.getRoutes().some((r) => r.name === name);
|
|
3502
|
+
}
|
|
3503
|
+
return walkRouteNames(router.options && router.options.routes, name);
|
|
3504
|
+
}
|
|
3505
|
+
function walkRouteNames(routes, name) {
|
|
3506
|
+
if (!Array.isArray(routes)) {
|
|
3507
|
+
return false;
|
|
3508
|
+
}
|
|
3509
|
+
for (const r of routes) {
|
|
3510
|
+
if (r && typeof r === 'object' && r.name === name) {
|
|
3511
|
+
return true;
|
|
3512
|
+
}
|
|
3513
|
+
const ch = r && typeof r === 'object' ? r.children : null;
|
|
3514
|
+
if (walkRouteNames(ch, name)) {
|
|
3515
|
+
return true;
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
return false;
|
|
3519
|
+
}
|
|
3520
|
+
function ensurePluginHostRoute$1(router, opts) {
|
|
3521
|
+
if (opts.ensurePluginHostRoute === false) {
|
|
3522
|
+
return;
|
|
3523
|
+
}
|
|
3524
|
+
if (!router || typeof router.addRoute !== 'function') {
|
|
3525
|
+
return;
|
|
3526
|
+
}
|
|
3527
|
+
const parentName = String(opts.pluginRoutesParentName || '').trim();
|
|
3528
|
+
if (!parentName) {
|
|
3529
|
+
return;
|
|
3530
|
+
}
|
|
3531
|
+
if (routeNameExists(router, parentName)) {
|
|
3532
|
+
return;
|
|
3533
|
+
}
|
|
3534
|
+
const Layout = opts.hostLayoutComponent;
|
|
3535
|
+
if (!Layout) {
|
|
3536
|
+
console.warn('[wep] 缺少 hostLayoutComponent,未自动注册插件壳路由;请传入宿主 Layout,或在路由表中自行配置与 pluginRoutesParentName 一致的父路由');
|
|
3537
|
+
return;
|
|
3538
|
+
}
|
|
3539
|
+
const mountDefault = defaultWebExtendPluginRuntime.pluginMountPath;
|
|
3540
|
+
let pathRaw = String(opts.pluginMountPath || mountDefault).trim().replace(/\/$/, '') || mountDefault;
|
|
3541
|
+
if (!pathRaw.startsWith('/')) {
|
|
3542
|
+
pathRaw = `/${pathRaw}`;
|
|
3543
|
+
}
|
|
3544
|
+
const meta = opts.pluginHostRouteMeta && typeof opts.pluginHostRouteMeta === 'object'
|
|
3545
|
+
? { ...opts.pluginHostRouteMeta }
|
|
3546
|
+
: { requiresConfig: true, hidden: true };
|
|
3547
|
+
router.addRoute({
|
|
3548
|
+
path: pathRaw,
|
|
3549
|
+
name: parentName,
|
|
3550
|
+
component: Layout,
|
|
3551
|
+
redirect: 'noredirect',
|
|
3552
|
+
meta,
|
|
3553
|
+
children: []
|
|
3554
|
+
});
|
|
3411
3555
|
}
|
|
3412
3556
|
|
|
3413
3557
|
/**
|
|
3414
|
-
*
|
|
3415
|
-
*
|
|
3416
|
-
* @param {Record<string, string>|null|undefined} devMap
|
|
3417
|
-
* @param {Set<string>} hostSet
|
|
3558
|
+
* 宿主通过 `resolveRuntimeOptions({ hostContext })` 注入的只读上下文,
|
|
3559
|
+
* 经浅拷贝 + 浅冻结后挂到 `hostApi.hostContext`,供插件访问 store/i18n 等而不污染 HostApi 顶层命名。
|
|
3418
3560
|
*/
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3561
|
+
function freezeShallowHostContext(input) {
|
|
3562
|
+
if (input == null || typeof input !== 'object' || Array.isArray(input)) {
|
|
3563
|
+
return Object.freeze({});
|
|
3564
|
+
}
|
|
3565
|
+
return Object.freeze({ ...input });
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
/**
|
|
3569
|
+
* 将静态清单配置路径解析为用于 fetch 的绝对 URL(浏览器)。
|
|
3570
|
+
*/
|
|
3571
|
+
function resolveStaticManifestUrlForFetch(url, origin) {
|
|
3572
|
+
const u = String(url || '').trim();
|
|
3573
|
+
if (!u) {
|
|
3574
|
+
return '';
|
|
3575
|
+
}
|
|
3576
|
+
if (/^https?:\/\//i.test(u)) {
|
|
3577
|
+
return u;
|
|
3578
|
+
}
|
|
3579
|
+
const o = String(origin || '').trim();
|
|
3580
|
+
if (!o) {
|
|
3581
|
+
return u.startsWith('/') ? u : `/${u}`;
|
|
3425
3582
|
}
|
|
3426
3583
|
try {
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
} catch (e) {
|
|
3433
|
-
console.warn('[wep] dev import failed, trying manifest entryUrl', p.id, e);
|
|
3434
|
-
if (entryUrl && isScriptHostAllowed(entryUrl, hostSet)) {
|
|
3435
|
-
await loadScript(entryUrl);
|
|
3436
|
-
}
|
|
3437
|
-
return
|
|
3584
|
+
const pathPart = u.startsWith('/') ? u : `/${u}`;
|
|
3585
|
+
return new URL(pathPart, o).href;
|
|
3586
|
+
}
|
|
3587
|
+
catch {
|
|
3588
|
+
return u;
|
|
3438
3589
|
}
|
|
3439
|
-
return
|
|
3440
|
-
}
|
|
3441
|
-
if (!entryUrl || !isScriptHostAllowed(entryUrl, hostSet)) {
|
|
3442
|
-
console.warn('[wep] skip (entryUrl not allowed)', p.id, entryUrl);
|
|
3443
|
-
return
|
|
3444
|
-
}
|
|
3445
|
-
await loadScript(entryUrl);
|
|
3446
3590
|
}
|
|
3447
3591
|
|
|
3448
3592
|
/**
|
|
3449
|
-
*
|
|
3450
|
-
* @param {(pluginId: string, router: import('vue-router').default, hostKit?: object) => object} createHostApiFactory
|
|
3451
|
-
* @param {import('./resolve-runtime-options.js').WebExtendPluginRuntimeOptions} [runtimeOptions]
|
|
3593
|
+
* 拉取插件清单、加载入口脚本并调用各插件 `activator`。
|
|
3452
3594
|
*/
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
if (
|
|
3473
|
-
|
|
3595
|
+
function shouldShowBootstrapSummary(opts) {
|
|
3596
|
+
if (opts.bootstrapSummary === true) {
|
|
3597
|
+
return true;
|
|
3598
|
+
}
|
|
3599
|
+
if (opts.bootstrapSummary === false) {
|
|
3600
|
+
return false;
|
|
3601
|
+
}
|
|
3602
|
+
const env = resolveBundledEnv(webExtendPluginEnvKeys.pluginsBootstrapSummary, '');
|
|
3603
|
+
if (env === '0' || env === 'false') {
|
|
3604
|
+
return false;
|
|
3605
|
+
}
|
|
3606
|
+
if (env === '1' || env === 'true') {
|
|
3607
|
+
return true;
|
|
3608
|
+
}
|
|
3609
|
+
return resolveBundledIsDev();
|
|
3610
|
+
}
|
|
3611
|
+
async function loadPluginEntry(p, entryUrl, devMap, hostSet) {
|
|
3612
|
+
const devEntry = devMap && typeof devMap[p.id] === 'string' ? devMap[p.id].trim() : '';
|
|
3613
|
+
if (devEntry) {
|
|
3614
|
+
if (!isScriptHostAllowed(devEntry, hostSet)) {
|
|
3615
|
+
console.warn('[wep] dev entry URL not allowed', p.id, devEntry);
|
|
3616
|
+
return;
|
|
3474
3617
|
}
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
? { pluginRoutesParentName: opts.pluginRoutesParentName }
|
|
3490
|
-
: {}),
|
|
3491
|
-
...(typeof opts.transformRoutes === 'function' ? { transformRoutes: opts.transformRoutes } : {}),
|
|
3492
|
-
...(typeof opts.interceptRegisterRoutes === 'function'
|
|
3493
|
-
? { interceptRegisterRoutes: opts.interceptRegisterRoutes }
|
|
3494
|
-
: {}),
|
|
3495
|
-
...(typeof opts.adaptRouteDeclarations === 'function'
|
|
3496
|
-
? { adaptRouteDeclarations: opts.adaptRouteDeclarations }
|
|
3497
|
-
: {})
|
|
3498
|
-
};
|
|
3499
|
-
|
|
3500
|
-
if (!manifestResult.ok) {
|
|
3501
|
-
if (manifestResult.error) {
|
|
3502
|
-
console.warn('[wep] fetch manifest failed', manifestResult.error);
|
|
3503
|
-
} else {
|
|
3504
|
-
console.warn('[wep] manifest HTTP', manifestResult.status, manifestUrl);
|
|
3618
|
+
try {
|
|
3619
|
+
await import(
|
|
3620
|
+
/* webpackIgnore: true */
|
|
3621
|
+
/* @vite-ignore */
|
|
3622
|
+
devEntry);
|
|
3623
|
+
}
|
|
3624
|
+
catch (e) {
|
|
3625
|
+
console.warn('[wep] dev import failed, trying manifest entryUrl', p.id, e);
|
|
3626
|
+
if (entryUrl && isScriptHostAllowed(entryUrl, hostSet)) {
|
|
3627
|
+
await loadScript(entryUrl);
|
|
3628
|
+
}
|
|
3629
|
+
return;
|
|
3630
|
+
}
|
|
3631
|
+
return;
|
|
3505
3632
|
}
|
|
3506
|
-
if (
|
|
3507
|
-
|
|
3633
|
+
if (!entryUrl || !isScriptHostAllowed(entryUrl, hostSet)) {
|
|
3634
|
+
console.warn('[wep] skip (entryUrl not allowed)', p.id, entryUrl);
|
|
3635
|
+
return;
|
|
3508
3636
|
}
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3637
|
+
await loadScript(entryUrl);
|
|
3638
|
+
}
|
|
3639
|
+
async function bootstrapPlugins$1(
|
|
3640
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3641
|
+
router,
|
|
3642
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3643
|
+
createHostApiFactory, runtimeOptions) {
|
|
3644
|
+
if (typeof window === 'undefined') {
|
|
3645
|
+
console.warn('[wep] bootstrapPlugins skipped (no window)');
|
|
3646
|
+
return;
|
|
3516
3647
|
}
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
const
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3648
|
+
printRuntimeBannerOnce();
|
|
3649
|
+
const opts = resolveRuntimeOptions$1(runtimeOptions || {});
|
|
3650
|
+
setRevokePluginMenuItems(typeof opts.revokePluginMenuItems === 'function' ? opts.revokePluginMenuItems : undefined);
|
|
3651
|
+
ensurePluginHostRoute$1(router, opts);
|
|
3652
|
+
const base = String(opts.manifestBase).replace(/\/$/, '');
|
|
3653
|
+
const isStatic = opts.manifestMode === 'static';
|
|
3654
|
+
let manifestUrl;
|
|
3655
|
+
if (isStatic) {
|
|
3656
|
+
const raw = String(opts.staticManifestUrl || '').trim();
|
|
3657
|
+
if (!raw) {
|
|
3658
|
+
console.warn('[wep] manifestMode=static requires non-empty staticManifestUrl (or env VITE_WEB_PLUGIN_STATIC_MANIFEST_URL)');
|
|
3659
|
+
if (shouldShowBootstrapSummary(opts)) {
|
|
3660
|
+
console.info('[wep] bootstrap_summary', { ok: false, reason: 'static_manifest_url_missing' });
|
|
3661
|
+
}
|
|
3662
|
+
return;
|
|
3663
|
+
}
|
|
3664
|
+
manifestUrl = resolveStaticManifestUrlForFetch(raw, window.location.origin);
|
|
3530
3665
|
}
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
window.__PLUGIN_ACTIVATORS__ = window.__PLUGIN_ACTIVATORS__ || {};
|
|
3534
|
-
|
|
3535
|
-
const plugins = data.plugins || [];
|
|
3536
|
-
if (plugins.length === 0) {
|
|
3537
|
-
console.info('[wep] empty plugin manifest — check backend and URL', manifestUrl);
|
|
3538
|
-
}
|
|
3539
|
-
|
|
3540
|
-
const summary = {
|
|
3541
|
-
manifestCount: plugins.length,
|
|
3542
|
-
activated: 0,
|
|
3543
|
-
skipEngines: 0,
|
|
3544
|
-
skipLoad: 0,
|
|
3545
|
-
skipNoActivator: 0,
|
|
3546
|
-
activateFail: 0
|
|
3547
|
-
};
|
|
3548
|
-
|
|
3549
|
-
for (const p of plugins) {
|
|
3550
|
-
const range = p.engines && p.engines.host;
|
|
3551
|
-
if (range && !semver.satisfies(HOST_PLUGIN_API_VERSION, range, { includePrerelease: true })) {
|
|
3552
|
-
console.warn('[wep] skip plugin (engines.host)', p.id, range);
|
|
3553
|
-
summary.skipEngines++;
|
|
3554
|
-
continue
|
|
3666
|
+
else {
|
|
3667
|
+
manifestUrl = `${base}${opts.manifestListPath}`;
|
|
3555
3668
|
}
|
|
3556
|
-
const
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3669
|
+
const hostSet = buildAllowedScriptHostsSet(opts.allowedScriptHosts);
|
|
3670
|
+
const explicit = parseWebPluginDevMapExplicit(opts);
|
|
3671
|
+
const manifestCtx = {
|
|
3672
|
+
manifestUrl,
|
|
3673
|
+
credentials: opts.manifestFetchCredentials
|
|
3674
|
+
};
|
|
3675
|
+
const [primaryResult, implicit] = await Promise.all([
|
|
3676
|
+
(async () => {
|
|
3677
|
+
try {
|
|
3678
|
+
if (typeof opts.fetchManifest === 'function') {
|
|
3679
|
+
return await opts.fetchManifest(manifestCtx);
|
|
3680
|
+
}
|
|
3681
|
+
return await defaultFetchWebPluginManifest$1(manifestCtx);
|
|
3682
|
+
}
|
|
3683
|
+
catch (e) {
|
|
3684
|
+
return { ok: false, error: e, data: null };
|
|
3685
|
+
}
|
|
3686
|
+
})(),
|
|
3687
|
+
buildImplicitWebPluginDevMap(opts, hostSet)
|
|
3688
|
+
]);
|
|
3689
|
+
let manifestResult = primaryResult;
|
|
3690
|
+
let manifestUrlUsed = manifestUrl;
|
|
3691
|
+
if (!isStatic && opts.devManifestFallback && opts.isDev) {
|
|
3692
|
+
const dataObj = primaryResult.ok && primaryResult.data && typeof primaryResult.data === 'object'
|
|
3693
|
+
? primaryResult.data
|
|
3694
|
+
: null;
|
|
3695
|
+
const plen = dataObj && Array.isArray(dataObj.plugins) ? dataObj.plugins.length : 0;
|
|
3696
|
+
const needFallback = !primaryResult.ok || plen === 0;
|
|
3697
|
+
const fallbackRaw = String(opts.devFallbackStaticManifestUrl || '').trim();
|
|
3698
|
+
if (needFallback && fallbackRaw) {
|
|
3699
|
+
const fallbackUrl = resolveStaticManifestUrlForFetch(fallbackRaw, window.location.origin);
|
|
3700
|
+
const fallbackCtx = {
|
|
3701
|
+
manifestUrl: fallbackUrl,
|
|
3702
|
+
credentials: opts.manifestFetchCredentials
|
|
3703
|
+
};
|
|
3704
|
+
const fr = await fetchStaticManifestViaHttp(fallbackCtx);
|
|
3705
|
+
const fdata = fr.ok && fr.data && typeof fr.data === 'object' ? fr.data : null;
|
|
3706
|
+
const flen = fdata && Array.isArray(fdata.plugins) ? fdata.plugins.length : 0;
|
|
3707
|
+
if (fr.ok && flen > 0) {
|
|
3708
|
+
manifestResult = fr;
|
|
3709
|
+
manifestUrlUsed = fallbackUrl;
|
|
3710
|
+
console.info('[wep] dev manifest fallback', { url: fallbackUrl, plugins: flen });
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3563
3713
|
}
|
|
3564
|
-
const
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3714
|
+
const devMap = mergeDevMaps(implicit, explicit);
|
|
3715
|
+
startPluginDevSseForMap(devMap, opts.isDev, hostSet, opts.devReloadSsePath);
|
|
3716
|
+
const frozenHostContext = freezeShallowHostContext(opts.hostContext !== undefined ? opts.hostContext : undefined);
|
|
3717
|
+
const hostKit = {
|
|
3718
|
+
hostContext: frozenHostContext,
|
|
3719
|
+
bridgeAllowedPathPrefixes: opts.bridgeAllowedPathPrefixes,
|
|
3720
|
+
...(opts.pluginRoutesParentName ? { pluginRoutesParentName: opts.pluginRoutesParentName } : {}),
|
|
3721
|
+
...(typeof opts.transformRoutes === 'function' ? { transformRoutes: opts.transformRoutes } : {}),
|
|
3722
|
+
...(typeof opts.interceptRegisterRoutes === 'function'
|
|
3723
|
+
? { interceptRegisterRoutes: opts.interceptRegisterRoutes }
|
|
3724
|
+
: {}),
|
|
3725
|
+
...(typeof opts.adaptRouteDeclarations === 'function'
|
|
3726
|
+
? { adaptRouteDeclarations: opts.adaptRouteDeclarations }
|
|
3727
|
+
: {}),
|
|
3728
|
+
...(typeof opts.applyPluginMenuItems === 'function'
|
|
3729
|
+
? { applyPluginMenuItems: opts.applyPluginMenuItems }
|
|
3730
|
+
: {}),
|
|
3731
|
+
...(typeof opts.revokePluginMenuItems === 'function'
|
|
3732
|
+
? { revokePluginMenuItems: opts.revokePluginMenuItems }
|
|
3733
|
+
: {})
|
|
3734
|
+
};
|
|
3735
|
+
if (!manifestResult.ok) {
|
|
3736
|
+
if (manifestResult.error) {
|
|
3737
|
+
console.warn('[wep] fetch manifest failed', manifestResult.error);
|
|
3738
|
+
}
|
|
3739
|
+
else {
|
|
3740
|
+
const label = isStatic ? 'static manifest' : 'manifest HTTP';
|
|
3741
|
+
console.warn(`[wep] ${label}`, manifestResult.status, manifestUrlUsed);
|
|
3742
|
+
}
|
|
3743
|
+
if (shouldShowBootstrapSummary(opts)) {
|
|
3744
|
+
console.info('[wep] bootstrap_summary', { ok: false, reason: 'manifest_fetch' });
|
|
3745
|
+
}
|
|
3746
|
+
return;
|
|
3569
3747
|
}
|
|
3570
|
-
const
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3748
|
+
const data = manifestResult.data;
|
|
3749
|
+
if (!data) {
|
|
3750
|
+
if (shouldShowBootstrapSummary(opts)) {
|
|
3751
|
+
console.info('[wep] bootstrap_summary', { ok: false, reason: 'manifest_empty_body' });
|
|
3752
|
+
}
|
|
3753
|
+
return;
|
|
3754
|
+
}
|
|
3755
|
+
const apiVer = data.hostPluginApiVersion;
|
|
3756
|
+
if (apiVer) {
|
|
3757
|
+
const coerced = semver.coerce(apiVer);
|
|
3758
|
+
const maj = coerced ? coerced.major : 0;
|
|
3759
|
+
const range = `^${maj}.0.0`;
|
|
3760
|
+
if (!semver.satisfies(HOST_PLUGIN_API_VERSION, range, { includePrerelease: true })) {
|
|
3761
|
+
console.warn('[wep] host API version mismatch', {
|
|
3762
|
+
host: HOST_PLUGIN_API_VERSION,
|
|
3763
|
+
manifest: apiVer
|
|
3764
|
+
});
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
window.__PLUGIN_ACTIVATORS__ = window.__PLUGIN_ACTIVATORS__ || {};
|
|
3768
|
+
const plugins = data.plugins || [];
|
|
3769
|
+
if (plugins.length === 0) {
|
|
3770
|
+
const hint = isStatic ? 'check static JSON file and plugins[]' : 'check backend and URL';
|
|
3771
|
+
console.info('[wep] empty plugin manifest — ' + hint, manifestUrlUsed);
|
|
3772
|
+
}
|
|
3773
|
+
const summary = {
|
|
3774
|
+
manifestCount: plugins.length,
|
|
3775
|
+
activated: 0,
|
|
3776
|
+
skipEngines: 0,
|
|
3777
|
+
skipLoad: 0,
|
|
3778
|
+
skipNoActivator: 0,
|
|
3779
|
+
activateFail: 0
|
|
3780
|
+
};
|
|
3781
|
+
for (const p of plugins) {
|
|
3782
|
+
const range = p.engines && p.engines.host;
|
|
3783
|
+
if (range && !semver.satisfies(HOST_PLUGIN_API_VERSION, range, { includePrerelease: true })) {
|
|
3784
|
+
console.warn('[wep] skip plugin (engines.host)', p.id, range);
|
|
3785
|
+
summary.skipEngines++;
|
|
3786
|
+
continue;
|
|
3787
|
+
}
|
|
3788
|
+
const entryUrl = p.entryUrl;
|
|
3789
|
+
try {
|
|
3790
|
+
await loadPluginEntry(p, entryUrl, devMap, hostSet);
|
|
3791
|
+
}
|
|
3792
|
+
catch (e) {
|
|
3793
|
+
console.warn('[wep] script load failed', p.id, e);
|
|
3794
|
+
summary.skipLoad++;
|
|
3795
|
+
continue;
|
|
3796
|
+
}
|
|
3797
|
+
const activator = window.__PLUGIN_ACTIVATORS__[p.id];
|
|
3798
|
+
if (typeof activator !== 'function') {
|
|
3799
|
+
console.warn('[wep] no activator for', p.id);
|
|
3800
|
+
summary.skipNoActivator++;
|
|
3801
|
+
continue;
|
|
3802
|
+
}
|
|
3803
|
+
const pluginRecord = Object.freeze({ ...p });
|
|
3804
|
+
try {
|
|
3805
|
+
if (typeof opts.onBeforePluginActivate === 'function') {
|
|
3806
|
+
await Promise.resolve(opts.onBeforePluginActivate({
|
|
3807
|
+
pluginId: p.id,
|
|
3808
|
+
router,
|
|
3809
|
+
pluginRecord
|
|
3810
|
+
}));
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
catch (e) {
|
|
3814
|
+
console.warn('[wep] activate skipped (onBeforePluginActivate)', p.id, e);
|
|
3815
|
+
summary.activateFail++;
|
|
3816
|
+
continue;
|
|
3817
|
+
}
|
|
3818
|
+
const hostApi = createHostApiFactory(p.id, router, hostKit);
|
|
3819
|
+
try {
|
|
3820
|
+
await Promise.resolve(activator(hostApi, { pluginRecord }));
|
|
3821
|
+
summary.activated++;
|
|
3822
|
+
if (typeof opts.onAfterPluginActivate === 'function') {
|
|
3823
|
+
await Promise.resolve(opts.onAfterPluginActivate({
|
|
3824
|
+
pluginId: p.id,
|
|
3825
|
+
router,
|
|
3826
|
+
pluginRecord,
|
|
3827
|
+
hostApi
|
|
3828
|
+
}));
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
catch (e) {
|
|
3832
|
+
console.error('[wep] activate failed', p.id, e);
|
|
3833
|
+
summary.activateFail++;
|
|
3834
|
+
if (typeof opts.onPluginActivateError === 'function') {
|
|
3835
|
+
try {
|
|
3836
|
+
await Promise.resolve(opts.onPluginActivateError({
|
|
3837
|
+
pluginId: p.id,
|
|
3838
|
+
error: e,
|
|
3839
|
+
pluginRecord,
|
|
3840
|
+
hostApi
|
|
3841
|
+
}));
|
|
3842
|
+
}
|
|
3843
|
+
catch (hookErr) {
|
|
3844
|
+
console.warn('[wep] onPluginActivateError hook failed', p.id, hookErr);
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
if (shouldShowBootstrapSummary(opts)) {
|
|
3850
|
+
console.info('[wep] bootstrap_summary', { ok: true, ...summary });
|
|
3578
3851
|
}
|
|
3579
|
-
}
|
|
3580
|
-
|
|
3581
|
-
if (shouldShowBootstrapSummary(opts)) {
|
|
3582
|
-
console.info('[wep] bootstrap_summary', { ok: true, ...summary });
|
|
3583
|
-
}
|
|
3584
3852
|
}
|
|
3585
3853
|
|
|
3586
3854
|
/**
|
|
3587
|
-
* 运行时引导相关 API
|
|
3855
|
+
* 运行时引导相关 API 的聚合导出。
|
|
3588
3856
|
*/
|
|
3589
3857
|
|
|
3590
3858
|
var pluginRuntime = /*#__PURE__*/Object.freeze({
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3859
|
+
__proto__: null,
|
|
3860
|
+
bootstrapPlugins: bootstrapPlugins$1,
|
|
3861
|
+
defaultFetchWebPluginManifest: defaultFetchWebPluginManifest$1,
|
|
3862
|
+
ensurePluginHostRoute: ensurePluginHostRoute$1,
|
|
3863
|
+
resolveRuntimeOptions: resolveRuntimeOptions$1
|
|
3595
3864
|
});
|
|
3596
3865
|
|
|
3597
3866
|
/**
|
|
3598
3867
|
* 清单拉取函数的组合工具:缓存、埋点等以**中间件**形式扩展,不侵入 `bootstrapPlugins` 核心逻辑,
|
|
3599
3868
|
* 可组合的 `fetchManifest` 包装;入参/出参与 `resolveRuntimeOptions({ fetchManifest })` 一致。
|
|
3600
|
-
*
|
|
3601
|
-
* @module runtime/manifest-fetch-composer
|
|
3602
|
-
*/
|
|
3603
|
-
|
|
3604
|
-
/**
|
|
3605
|
-
* @typedef {object} FetchWebPluginManifestContext
|
|
3606
|
-
* @property {string} manifestUrl
|
|
3607
|
-
* @property {RequestCredentials} credentials
|
|
3608
|
-
*/
|
|
3609
|
-
|
|
3610
|
-
/**
|
|
3611
|
-
* @typedef {object} FetchWebPluginManifestResult
|
|
3612
|
-
* @property {boolean} ok
|
|
3613
|
-
* @property {number} [status]
|
|
3614
|
-
* @property {{ hostPluginApiVersion?: string, plugins?: object[] }|null} [data]
|
|
3615
|
-
* @property {unknown} [error]
|
|
3616
|
-
*/
|
|
3617
|
-
|
|
3618
|
-
/**
|
|
3619
|
-
* @callback FetchWebPluginManifestFn
|
|
3620
|
-
* @param {FetchWebPluginManifestContext} ctx
|
|
3621
|
-
* @returns {Promise<FetchWebPluginManifestResult>}
|
|
3622
|
-
*/
|
|
3623
|
-
|
|
3624
|
-
/**
|
|
3625
|
-
* 将内层 `fetchManifest` 包装为带缓存的版本。等价于
|
|
3626
|
-
* `composeManifestFetch(inner, manifestFetchCacheMiddleware(options))`。
|
|
3627
|
-
*
|
|
3628
|
-
* @param {FetchWebPluginManifestFn} inner 内层实现(如预设生成的 `fetchManifest` 或 `defaultFetchWebPluginManifest`)
|
|
3629
|
-
* @param {ManifestFetchCacheOptions} [options]
|
|
3630
|
-
* @returns {FetchWebPluginManifestFn}
|
|
3631
3869
|
*/
|
|
3632
3870
|
function wrapManifestFetchWithCache$1(inner, options = {}) {
|
|
3633
|
-
|
|
3871
|
+
return composeManifestFetch$1(inner, manifestFetchCacheMiddleware$1(options));
|
|
3634
3872
|
}
|
|
3635
|
-
|
|
3636
|
-
/**
|
|
3637
|
-
* @typedef {object} ManifestFetchCacheOptions
|
|
3638
|
-
* @property {number} [ttlMs] 缓存时长(毫秒)。`<= 0` 或未传时**不缓存**(直接透传 `inner`)。
|
|
3639
|
-
* @property {'memory'|'session'|'local'} [storage='memory'] `session`/`local` 依赖 `JSON.stringify`,仅适合可序列化的 `data`。
|
|
3640
|
-
* @property {string} [storageKeyPrefix='wep.manifestFetch.v1'] Web Storage 键前缀(仅 storage 非 memory 时生效)。
|
|
3641
|
-
* @property {(ctx: FetchWebPluginManifestContext) => string} [cacheKey] 默认 `manifestUrl + '\0' + credentials`。
|
|
3642
|
-
* @property {(result: FetchWebPluginManifestResult) => boolean} [shouldCache] 默认:`ok === true` 且 `data` 非空。
|
|
3643
|
-
* @property {number} [maxEntries=50] 仅 `memory`:超过条数时淘汰最久未读条目。
|
|
3644
|
-
* @property {() => number} [now] 测试注入时间戳。
|
|
3645
|
-
*/
|
|
3646
|
-
|
|
3647
|
-
/**
|
|
3648
|
-
* 清单拉取**中间件工厂**:`next` 为内层 `fetchManifest`。
|
|
3649
|
-
*
|
|
3650
|
-
* @param {ManifestFetchCacheOptions} options
|
|
3651
|
-
* @returns {(next: FetchWebPluginManifestFn) => FetchWebPluginManifestFn}
|
|
3652
|
-
*/
|
|
3653
3873
|
function manifestFetchCacheMiddleware$1(options = {}) {
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
}
|
|
3658
|
-
|
|
3659
|
-
const storage = options.storage || 'memory';
|
|
3660
|
-
const prefix = options.storageKeyPrefix || 'wep.manifestFetch.v1';
|
|
3661
|
-
const maxEntries = typeof options.maxEntries === 'number' && options.maxEntries > 0 ? options.maxEntries : 50;
|
|
3662
|
-
const getNow = typeof options.now === 'function' ? options.now : () => Date.now();
|
|
3663
|
-
const cacheKeyFn =
|
|
3664
|
-
typeof options.cacheKey === 'function'
|
|
3665
|
-
? options.cacheKey
|
|
3666
|
-
: (ctx) => `${String(ctx.manifestUrl)}\0${String(ctx.credentials)}`;
|
|
3667
|
-
|
|
3668
|
-
const shouldCache =
|
|
3669
|
-
typeof options.shouldCache === 'function'
|
|
3670
|
-
? options.shouldCache
|
|
3671
|
-
: (r) => !!(r && r.ok === true && r.data != null);
|
|
3672
|
-
|
|
3673
|
-
/** @type {Map<string, { expiresAt: number, result: FetchWebPluginManifestResult, lastRead: number }>} */
|
|
3674
|
-
const memory = new Map();
|
|
3675
|
-
|
|
3676
|
-
function cloneResult(r) {
|
|
3677
|
-
try {
|
|
3678
|
-
if (typeof structuredClone === 'function') {
|
|
3679
|
-
return structuredClone(r)
|
|
3680
|
-
}
|
|
3681
|
-
} catch (_) {}
|
|
3682
|
-
try {
|
|
3683
|
-
return JSON.parse(JSON.stringify(r))
|
|
3684
|
-
} catch (_) {
|
|
3685
|
-
return { ...r, data: r.data }
|
|
3874
|
+
const ttlMs = typeof options.ttlMs === 'number' && Number.isFinite(options.ttlMs) ? options.ttlMs : 0;
|
|
3875
|
+
if (ttlMs <= 0) {
|
|
3876
|
+
return (next) => next;
|
|
3686
3877
|
}
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3878
|
+
const storage = options.storage || 'memory';
|
|
3879
|
+
const prefix = options.storageKeyPrefix || defaultManifestFetchCache.storageKeyPrefix;
|
|
3880
|
+
const maxEntries = typeof options.maxEntries === 'number' && options.maxEntries > 0
|
|
3881
|
+
? options.maxEntries
|
|
3882
|
+
: defaultManifestFetchCache.maxEntries;
|
|
3883
|
+
const getNow = typeof options.now === 'function' ? options.now : () => Date.now();
|
|
3884
|
+
const cacheKeyFn = typeof options.cacheKey === 'function'
|
|
3885
|
+
? options.cacheKey
|
|
3886
|
+
: (ctx) => `${String(ctx.manifestUrl)}\0${String(ctx.credentials)}`;
|
|
3887
|
+
const shouldCache = typeof options.shouldCache === 'function'
|
|
3888
|
+
? options.shouldCache
|
|
3889
|
+
: (r) => !!(r && r.ok === true && r.data != null);
|
|
3890
|
+
const memory = new Map();
|
|
3891
|
+
function cloneResult(r) {
|
|
3892
|
+
try {
|
|
3893
|
+
if (typeof structuredClone === 'function') {
|
|
3894
|
+
return structuredClone(r);
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
catch {
|
|
3898
|
+
/* ignore */
|
|
3899
|
+
}
|
|
3900
|
+
try {
|
|
3901
|
+
return JSON.parse(JSON.stringify(r));
|
|
3902
|
+
}
|
|
3903
|
+
catch {
|
|
3904
|
+
return { ...r, data: r.data };
|
|
3905
|
+
}
|
|
3704
3906
|
}
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
if (!raw) {
|
|
3711
|
-
return null
|
|
3712
|
-
}
|
|
3713
|
-
const o = JSON.parse(raw);
|
|
3714
|
-
if (!o || typeof o !== 'object') {
|
|
3715
|
-
return null
|
|
3716
|
-
}
|
|
3717
|
-
const exp = o.expiresAt;
|
|
3718
|
-
const res = o.result;
|
|
3719
|
-
if (typeof exp !== 'number' || getNow() > exp) {
|
|
3720
|
-
store.removeItem(key);
|
|
3721
|
-
return null
|
|
3722
|
-
}
|
|
3723
|
-
return /** @type {FetchWebPluginManifestResult} */ (res)
|
|
3724
|
-
} catch (_) {
|
|
3725
|
-
return null
|
|
3907
|
+
function touchMemory(key) {
|
|
3908
|
+
const e = memory.get(key);
|
|
3909
|
+
if (e) {
|
|
3910
|
+
e.lastRead = getNow();
|
|
3911
|
+
}
|
|
3726
3912
|
}
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3913
|
+
function pruneMemory() {
|
|
3914
|
+
if (memory.size <= maxEntries) {
|
|
3915
|
+
return;
|
|
3916
|
+
}
|
|
3917
|
+
const entries = [...memory.entries()].sort((a, b) => a[1].lastRead - b[1].lastRead);
|
|
3918
|
+
const drop = memory.size - maxEntries;
|
|
3919
|
+
for (let i = 0; i < drop; i++) {
|
|
3920
|
+
memory.delete(entries[i][0]);
|
|
3921
|
+
}
|
|
3734
3922
|
}
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3923
|
+
function readWebStorage(store, key) {
|
|
3924
|
+
try {
|
|
3925
|
+
const raw = store.getItem(key);
|
|
3926
|
+
if (!raw) {
|
|
3927
|
+
return null;
|
|
3928
|
+
}
|
|
3929
|
+
const o = JSON.parse(raw);
|
|
3930
|
+
if (!o || typeof o !== 'object') {
|
|
3931
|
+
return null;
|
|
3932
|
+
}
|
|
3933
|
+
const exp = o.expiresAt;
|
|
3934
|
+
const res = o.result;
|
|
3935
|
+
if (typeof exp !== 'number' || getNow() > exp) {
|
|
3936
|
+
store.removeItem(key);
|
|
3937
|
+
return null;
|
|
3938
|
+
}
|
|
3939
|
+
return res;
|
|
3940
|
+
}
|
|
3941
|
+
catch {
|
|
3942
|
+
return null;
|
|
3747
3943
|
}
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
return cloneResult(hit)
|
|
3944
|
+
}
|
|
3945
|
+
function writeWebStorage(store, key, result, expiresAt) {
|
|
3946
|
+
try {
|
|
3947
|
+
store.setItem(key, JSON.stringify({ expiresAt, result }));
|
|
3753
3948
|
}
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
const hit = readWebStorage(localStorage, sk);
|
|
3757
|
-
if (hit) {
|
|
3758
|
-
return cloneResult(hit)
|
|
3949
|
+
catch {
|
|
3950
|
+
/* Quota / 不可序列化 */
|
|
3759
3951
|
}
|
|
3760
|
-
}
|
|
3761
|
-
|
|
3762
|
-
const result = await next(ctx);
|
|
3763
|
-
|
|
3764
|
-
if (!shouldCache(result)) {
|
|
3765
|
-
return result
|
|
3766
|
-
}
|
|
3767
|
-
|
|
3768
|
-
const expiresAt = now + ttlMs;
|
|
3769
|
-
const frozen = cloneResult(result);
|
|
3770
|
-
|
|
3771
|
-
if (storage === 'memory') {
|
|
3772
|
-
memory.set(key, { expiresAt, result: frozen, lastRead: now });
|
|
3773
|
-
pruneMemory();
|
|
3774
|
-
} else if (storage === 'session' && typeof sessionStorage !== 'undefined') {
|
|
3775
|
-
writeWebStorage(sessionStorage, `${prefix}:${key}`, frozen, expiresAt);
|
|
3776
|
-
} else if (storage === 'local' && typeof localStorage !== 'undefined') {
|
|
3777
|
-
writeWebStorage(localStorage, `${prefix}:${key}`, frozen, expiresAt);
|
|
3778
|
-
}
|
|
3779
|
-
|
|
3780
|
-
return result
|
|
3781
3952
|
}
|
|
3782
|
-
|
|
3953
|
+
return (next) => {
|
|
3954
|
+
return async (ctx) => {
|
|
3955
|
+
const key = cacheKeyFn(ctx);
|
|
3956
|
+
const now = getNow();
|
|
3957
|
+
if (storage === 'memory') {
|
|
3958
|
+
const hit = memory.get(key);
|
|
3959
|
+
if (hit && hit.expiresAt > now) {
|
|
3960
|
+
touchMemory(key);
|
|
3961
|
+
return cloneResult(hit.result);
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
else if (storage === 'session' && typeof sessionStorage !== 'undefined') {
|
|
3965
|
+
const sk = `${prefix}:${key}`;
|
|
3966
|
+
const hit = readWebStorage(sessionStorage, sk);
|
|
3967
|
+
if (hit) {
|
|
3968
|
+
return cloneResult(hit);
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
else if (storage === 'local' && typeof localStorage !== 'undefined') {
|
|
3972
|
+
const sk = `${prefix}:${key}`;
|
|
3973
|
+
const hit = readWebStorage(localStorage, sk);
|
|
3974
|
+
if (hit) {
|
|
3975
|
+
return cloneResult(hit);
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
const result = await next(ctx);
|
|
3979
|
+
if (!shouldCache(result)) {
|
|
3980
|
+
return result;
|
|
3981
|
+
}
|
|
3982
|
+
const expiresAt = now + ttlMs;
|
|
3983
|
+
const frozen = cloneResult(result);
|
|
3984
|
+
if (storage === 'memory') {
|
|
3985
|
+
memory.set(key, { expiresAt, result: frozen, lastRead: now });
|
|
3986
|
+
pruneMemory();
|
|
3987
|
+
}
|
|
3988
|
+
else if (storage === 'session' && typeof sessionStorage !== 'undefined') {
|
|
3989
|
+
writeWebStorage(sessionStorage, `${prefix}:${key}`, frozen, expiresAt);
|
|
3990
|
+
}
|
|
3991
|
+
else if (storage === 'local' && typeof localStorage !== 'undefined') {
|
|
3992
|
+
writeWebStorage(localStorage, `${prefix}:${key}`, frozen, expiresAt);
|
|
3993
|
+
}
|
|
3994
|
+
return result;
|
|
3995
|
+
};
|
|
3996
|
+
};
|
|
3783
3997
|
}
|
|
3784
|
-
|
|
3785
|
-
/**
|
|
3786
|
-
* 自右向左组合中间件(与 Koa/Redux 习惯一致:`compose(f,g,h)(inner)` = f(g(h(inner))))。
|
|
3787
|
-
*
|
|
3788
|
-
* @param {FetchWebPluginManifestFn} inner 最内层拉取实现
|
|
3789
|
-
* @param {...function(FetchWebPluginManifestFn): FetchWebPluginManifestFn} middlewares 每个元素签名 `(next) => async (ctx) => result`
|
|
3790
|
-
* @returns {FetchWebPluginManifestFn}
|
|
3791
|
-
*/
|
|
3792
3998
|
function composeManifestFetch$1(inner, ...middlewares) {
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
}
|
|
3796
|
-
let f = inner;
|
|
3797
|
-
for (let i = middlewares.length - 1; i >= 0; i--) {
|
|
3798
|
-
const mw = middlewares[i];
|
|
3799
|
-
if (typeof mw !== 'function') {
|
|
3800
|
-
throw new Error('[web-extend-plugin-vue2] composeManifestFetch 中间件须为函数')
|
|
3999
|
+
if (typeof inner !== 'function') {
|
|
4000
|
+
throw new Error('[web-extend-plugin-vue2] composeManifestFetch 需要 inner 为函数');
|
|
3801
4001
|
}
|
|
3802
|
-
f =
|
|
3803
|
-
|
|
3804
|
-
|
|
4002
|
+
let f = inner;
|
|
4003
|
+
for (let i = middlewares.length - 1; i >= 0; i--) {
|
|
4004
|
+
const mw = middlewares[i];
|
|
4005
|
+
if (typeof mw !== 'function') {
|
|
4006
|
+
throw new Error('[web-extend-plugin-vue2] composeManifestFetch 中间件须为函数');
|
|
4007
|
+
}
|
|
4008
|
+
f = mw(f);
|
|
4009
|
+
}
|
|
4010
|
+
return f;
|
|
3805
4011
|
}
|
|
3806
4012
|
|
|
3807
4013
|
var manifestComposer = /*#__PURE__*/Object.freeze({
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
4014
|
+
__proto__: null,
|
|
4015
|
+
composeManifestFetch: composeManifestFetch$1,
|
|
4016
|
+
manifestFetchCacheMiddleware: manifestFetchCacheMiddleware$1,
|
|
4017
|
+
wrapManifestFetchWithCache: wrapManifestFetchWithCache$1
|
|
3812
4018
|
});
|
|
3813
4019
|
|
|
3814
4020
|
/**
|
|
3815
|
-
*
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
/**
|
|
3819
|
-
* @param {{ allowedPathPrefixes?: string[] }} [config]
|
|
3820
|
-
*/
|
|
3821
|
-
function createRequestBridge(config = {}) {
|
|
3822
|
-
const raw =
|
|
3823
|
-
Array.isArray(config.allowedPathPrefixes) && config.allowedPathPrefixes.length > 0
|
|
3824
|
-
? config.allowedPathPrefixes
|
|
3825
|
-
: defaultWebExtendPluginRuntime.bridgeAllowedPathPrefixes;
|
|
3826
|
-
const allowedPathPrefixes = raw.map((p) => ensureLeadingPath(p));
|
|
3827
|
-
|
|
3828
|
-
return {
|
|
3829
|
-
/**
|
|
3830
|
-
* @param {string} path 必须以 `/` 开头,且匹配某一白名单前缀
|
|
3831
|
-
* @param {RequestInit} [init]
|
|
3832
|
-
*/
|
|
3833
|
-
async request(path, init = {}) {
|
|
3834
|
-
if (typeof path !== 'string' || !path.startsWith('/')) {
|
|
3835
|
-
throw new Error('[wep:bridge] path must start with /')
|
|
3836
|
-
}
|
|
3837
|
-
const allowed = allowedPathPrefixes.some((p) => path.startsWith(p));
|
|
3838
|
-
if (!allowed) {
|
|
3839
|
-
throw new Error('[wep:bridge] path not allowed: ' + path)
|
|
3840
|
-
}
|
|
3841
|
-
return fetch(path, {
|
|
3842
|
-
credentials: 'same-origin',
|
|
3843
|
-
...init
|
|
3844
|
-
})
|
|
3845
|
-
}
|
|
3846
|
-
}
|
|
3847
|
-
}
|
|
3848
|
-
|
|
3849
|
-
/**
|
|
3850
|
-
* 宿主侧响应式注册表:插件菜单与扩展点槽位(供布局与 `ExtensionPoint` 消费)。
|
|
3851
|
-
*/
|
|
3852
|
-
|
|
3853
|
-
/**
|
|
3854
|
-
* @type {{
|
|
3855
|
-
* menus: object[],
|
|
3856
|
-
* slots: Record<string, Array<{ pluginId: string, component: import('vue').Component, priority: number, key: string }>>
|
|
3857
|
-
* }}
|
|
4021
|
+
* 宿主侧响应式注册表:扩展点槽位(供布局与 `ExtensionPoint` 消费)。
|
|
4022
|
+
* 菜单数据由宿主在 `applyPluginMenuItems` 中自行并入其路由/菜单 state,框架不维护平行菜单列表。
|
|
3858
4023
|
*/
|
|
3859
4024
|
const registries = Vue__default.default.observable({
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
/** 槽位变更计数;缓解 Vue2 对动态 `Vue.set(slots, key)` 的依赖收集不完整。 */
|
|
3863
|
-
slotRevision: 0
|
|
4025
|
+
slots: {},
|
|
4026
|
+
slotRevision: 0
|
|
3864
4027
|
});
|
|
3865
4028
|
|
|
3866
4029
|
/**
|
|
3867
4030
|
* 按插件 id 收集 `onTeardown` 回调,供 `disposeWebPlugin` 统一执行。
|
|
3868
4031
|
*/
|
|
3869
|
-
|
|
3870
|
-
/** @type {Map<string, Array<() => void>>} */
|
|
3871
4032
|
const _byPlugin = new Map();
|
|
3872
|
-
|
|
3873
|
-
/**
|
|
3874
|
-
* @param {string} pluginId
|
|
3875
|
-
* @param {() => void} fn
|
|
3876
|
-
*/
|
|
3877
4033
|
function registerPluginTeardown(pluginId, fn) {
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
4034
|
+
if (typeof fn !== 'function') {
|
|
4035
|
+
return;
|
|
4036
|
+
}
|
|
4037
|
+
let list = _byPlugin.get(pluginId);
|
|
4038
|
+
if (!list) {
|
|
4039
|
+
list = [];
|
|
4040
|
+
_byPlugin.set(pluginId, list);
|
|
4041
|
+
}
|
|
4042
|
+
list.push(fn);
|
|
4043
|
+
}
|
|
4044
|
+
function runPluginTeardowns(pluginId) {
|
|
4045
|
+
const list = _byPlugin.get(pluginId);
|
|
4046
|
+
if (!list) {
|
|
4047
|
+
return;
|
|
4048
|
+
}
|
|
4049
|
+
_byPlugin.delete(pluginId);
|
|
4050
|
+
for (const fn of list) {
|
|
4051
|
+
try {
|
|
4052
|
+
fn();
|
|
4053
|
+
}
|
|
4054
|
+
catch (e) {
|
|
4055
|
+
console.warn('[wep] teardown failed', pluginId, e);
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
3887
4058
|
}
|
|
3888
4059
|
|
|
3889
4060
|
/**
|
|
3890
|
-
*
|
|
4061
|
+
* 插件访问后端的受控通道:`fetch` 仅允许落在配置的路径前缀下(默认 `/api/`),默认 `credentials: 'same-origin'`。
|
|
3891
4062
|
*/
|
|
3892
|
-
function
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
4063
|
+
function createRequestBridge(config = {}) {
|
|
4064
|
+
const raw = Array.isArray(config.allowedPathPrefixes) && config.allowedPathPrefixes.length > 0
|
|
4065
|
+
? config.allowedPathPrefixes
|
|
4066
|
+
: defaultWebExtendPluginRuntime.bridgeAllowedPathPrefixes;
|
|
4067
|
+
const allowedPathPrefixes = raw.map((p) => ensureLeadingPath(p));
|
|
4068
|
+
return {
|
|
4069
|
+
async request(path, init = {}) {
|
|
4070
|
+
if (typeof path !== 'string' || !path.startsWith('/')) {
|
|
4071
|
+
throw new Error('[wep:bridge] path must start with /');
|
|
4072
|
+
}
|
|
4073
|
+
const allowed = allowedPathPrefixes.some((p) => path.startsWith(p));
|
|
4074
|
+
if (!allowed) {
|
|
4075
|
+
throw new Error('[wep:bridge] path not allowed: ' + path);
|
|
4076
|
+
}
|
|
4077
|
+
return fetch(path, {
|
|
4078
|
+
credentials: 'same-origin',
|
|
4079
|
+
...init
|
|
4080
|
+
});
|
|
4081
|
+
}
|
|
4082
|
+
};
|
|
3905
4083
|
}
|
|
3906
4084
|
|
|
3907
4085
|
/**
|
|
3908
4086
|
* 构造插件 `activator(hostApi)` 使用的宿主 API:路由、菜单、扩展点、资源与受控请求桥。
|
|
3909
4087
|
*/
|
|
3910
|
-
|
|
3911
4088
|
let slotItemKeySeq = 0;
|
|
3912
4089
|
let routeSynthSeq = 0;
|
|
3913
|
-
|
|
3914
|
-
/**
|
|
3915
|
-
* @param {unknown[]} nodes
|
|
3916
|
-
*/
|
|
3917
4090
|
function analyzeRouteInputTree(nodes) {
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
4091
|
+
let hasDecl = false;
|
|
4092
|
+
let hasCfg = false;
|
|
4093
|
+
function walk(r) {
|
|
4094
|
+
if (!r || typeof r !== 'object') {
|
|
4095
|
+
return;
|
|
4096
|
+
}
|
|
4097
|
+
const o = r;
|
|
4098
|
+
const cfg = o.component != null || o.components != null;
|
|
4099
|
+
const decl = typeof o.componentRef === 'string';
|
|
4100
|
+
if (cfg) {
|
|
4101
|
+
hasCfg = true;
|
|
4102
|
+
}
|
|
4103
|
+
else if (decl) {
|
|
4104
|
+
hasDecl = true;
|
|
4105
|
+
}
|
|
4106
|
+
const ch = o.children;
|
|
4107
|
+
if (Array.isArray(ch)) {
|
|
4108
|
+
for (const c of ch) {
|
|
4109
|
+
walk(c);
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
3932
4112
|
}
|
|
3933
|
-
const
|
|
3934
|
-
|
|
3935
|
-
for (const c of ch) {
|
|
3936
|
-
walk(c);
|
|
3937
|
-
}
|
|
4113
|
+
for (const n of nodes) {
|
|
4114
|
+
walk(n);
|
|
3938
4115
|
}
|
|
3939
|
-
|
|
3940
|
-
for (const n of nodes) {
|
|
3941
|
-
walk(n);
|
|
3942
|
-
}
|
|
3943
|
-
return { hasDecl, hasCfg }
|
|
4116
|
+
return { hasDecl, hasCfg };
|
|
3944
4117
|
}
|
|
3945
|
-
|
|
3946
4118
|
/**
|
|
3947
4119
|
* 单插件在宿主侧的 API 句柄。工厂请传 `(id, router, kit) => createHostApi(id, r, kit)` 以便 bridge 白名单等到位。
|
|
3948
|
-
*
|
|
3949
|
-
* @param {string} pluginId
|
|
3950
|
-
* @param {import('vue-router').default} router
|
|
3951
|
-
* @param {Record<string, unknown>} [hostKitOptions] 与 `resolveRuntimeOptions` 中路由/bridge 等字段一致
|
|
3952
4120
|
*/
|
|
4121
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3953
4122
|
function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
name: r.name || `__wep_${pluginId}_${routeSynthSeq++}`,
|
|
3971
|
-
meta: { ...(r.meta || {}), pluginId }
|
|
3972
|
-
}));
|
|
3973
|
-
if (typeof router.addRoute === 'function') {
|
|
3974
|
-
if (parentName) {
|
|
3975
|
-
for (const r of wrapped) {
|
|
3976
|
-
router.addRoute(parentName, r);
|
|
4123
|
+
const bridgePrefixes = Array.isArray(hostKitOptions.bridgeAllowedPathPrefixes) &&
|
|
4124
|
+
hostKitOptions.bridgeAllowedPathPrefixes.length > 0
|
|
4125
|
+
? hostKitOptions.bridgeAllowedPathPrefixes
|
|
4126
|
+
: defaultWebExtendPluginRuntime.bridgeAllowedPathPrefixes;
|
|
4127
|
+
const bridge = createRequestBridge({ allowedPathPrefixes: bridgePrefixes });
|
|
4128
|
+
const parentName = typeof hostKitOptions.pluginRoutesParentName === 'string'
|
|
4129
|
+
? hostKitOptions.pluginRoutesParentName.trim()
|
|
4130
|
+
: '';
|
|
4131
|
+
function applyInternalRegister(rawRouteConfigs) {
|
|
4132
|
+
const wrapped = rawRouteConfigs.map((r) => ({
|
|
4133
|
+
...r,
|
|
4134
|
+
name: r.name || `${routeSynthNamePrefix}${pluginId}_${routeSynthSeq++}`,
|
|
4135
|
+
meta: { ...(r.meta || {}), pluginId }
|
|
4136
|
+
}));
|
|
4137
|
+
if (typeof router.addRoute !== 'function') {
|
|
4138
|
+
throw new Error('[wep] vue-router 3.5+ 必需:请使用 router.addRoute(不再支持 addRoutes)');
|
|
3977
4139
|
}
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
4140
|
+
if (parentName) {
|
|
4141
|
+
for (const r of wrapped) {
|
|
4142
|
+
router.addRoute(parentName, r);
|
|
4143
|
+
}
|
|
3981
4144
|
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
'[wep] pluginRoutesParentName requires vue-router 3.5+ addRoute; falling back to top-level addRoutes'
|
|
3987
|
-
);
|
|
3988
|
-
}
|
|
3989
|
-
router.addRoutes(wrapped);
|
|
3990
|
-
}
|
|
3991
|
-
}
|
|
3992
|
-
|
|
3993
|
-
function injectStylesheet(href) {
|
|
3994
|
-
const link = document.createElement('link');
|
|
3995
|
-
link.rel = 'stylesheet';
|
|
3996
|
-
link.href = href;
|
|
3997
|
-
link.setAttribute('data-plugin-asset', pluginId);
|
|
3998
|
-
document.head.appendChild(link);
|
|
3999
|
-
}
|
|
4000
|
-
|
|
4001
|
-
/** @param {string} src */
|
|
4002
|
-
function injectScript(src) {
|
|
4003
|
-
return new Promise((resolve, reject) => {
|
|
4004
|
-
const s = document.createElement('script');
|
|
4005
|
-
s.async = true;
|
|
4006
|
-
s.src = src;
|
|
4007
|
-
s.setAttribute('data-plugin-asset', pluginId);
|
|
4008
|
-
s.onload = () => resolve();
|
|
4009
|
-
s.onerror = () => reject(new Error('script failed: ' + src));
|
|
4010
|
-
document.head.appendChild(s);
|
|
4011
|
-
})
|
|
4012
|
-
}
|
|
4013
|
-
|
|
4014
|
-
return {
|
|
4015
|
-
hostPluginApiVersion: HOST_PLUGIN_API_VERSION,
|
|
4016
|
-
|
|
4017
|
-
registerRoutes(routes) {
|
|
4018
|
-
const list = Array.isArray(routes) ? routes : [];
|
|
4019
|
-
if (list.length === 0) {
|
|
4020
|
-
return
|
|
4021
|
-
}
|
|
4022
|
-
const { hasDecl, hasCfg } = analyzeRouteInputTree(list);
|
|
4023
|
-
if (hasDecl && hasCfg) {
|
|
4024
|
-
throw new Error(
|
|
4025
|
-
'[wep] registerRoutes: cannot mix RouteDeclaration (componentRef) with RouteConfig (component)'
|
|
4026
|
-
)
|
|
4027
|
-
}
|
|
4028
|
-
let /** @type {import('vue-router').RouteConfig[]} */ configs;
|
|
4029
|
-
if (hasDecl) {
|
|
4030
|
-
const adapt = hostKitOptions.adaptRouteDeclarations;
|
|
4031
|
-
if (typeof adapt !== 'function') {
|
|
4032
|
-
throw new Error(
|
|
4033
|
-
'[wep] registerRoutes: RouteDeclaration (componentRef) requires adaptRouteDeclarations on the host'
|
|
4034
|
-
)
|
|
4145
|
+
else {
|
|
4146
|
+
for (const r of wrapped) {
|
|
4147
|
+
router.addRoute(r);
|
|
4148
|
+
}
|
|
4035
4149
|
}
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
if (typeof hostKitOptions.interceptRegisterRoutes === 'function') {
|
|
4054
|
-
hostKitOptions.interceptRegisterRoutes({
|
|
4055
|
-
pluginId,
|
|
4056
|
-
router,
|
|
4057
|
-
routes: configs,
|
|
4058
|
-
applyInternalRegister
|
|
4059
|
-
});
|
|
4060
|
-
} else {
|
|
4061
|
-
applyInternalRegister(configs);
|
|
4062
|
-
}
|
|
4063
|
-
},
|
|
4064
|
-
|
|
4065
|
-
registerMenuItems(items) {
|
|
4066
|
-
for (const item of items) {
|
|
4067
|
-
registries.menus.push({ ...item, pluginId });
|
|
4068
|
-
}
|
|
4069
|
-
registries.menus.sort(
|
|
4070
|
-
(a, b) => (a.order != null ? a.order : 0) - (b.order != null ? b.order : 0)
|
|
4071
|
-
);
|
|
4072
|
-
},
|
|
4073
|
-
|
|
4074
|
-
registerSlotComponents(pointId, components) {
|
|
4075
|
-
if (!pointId) {
|
|
4076
|
-
return
|
|
4077
|
-
}
|
|
4078
|
-
if (!registries.slots[pointId]) {
|
|
4079
|
-
Vue__default.default.set(registries.slots, pointId, []);
|
|
4080
|
-
}
|
|
4081
|
-
const list = registries.slots[pointId];
|
|
4082
|
-
for (const c of components) {
|
|
4083
|
-
list.push({
|
|
4084
|
-
pluginId,
|
|
4085
|
-
component: c.component,
|
|
4086
|
-
priority: c.priority != null ? c.priority : 0,
|
|
4087
|
-
key: `${pluginId}-${pointId}-${++slotItemKeySeq}`
|
|
4150
|
+
}
|
|
4151
|
+
function injectStylesheet(href) {
|
|
4152
|
+
const link = document.createElement('link');
|
|
4153
|
+
link.rel = 'stylesheet';
|
|
4154
|
+
link.href = href;
|
|
4155
|
+
link.setAttribute('data-plugin-asset', pluginId);
|
|
4156
|
+
document.head.appendChild(link);
|
|
4157
|
+
}
|
|
4158
|
+
function injectScript(src) {
|
|
4159
|
+
return new Promise((resolve, reject) => {
|
|
4160
|
+
const s = document.createElement('script');
|
|
4161
|
+
s.async = true;
|
|
4162
|
+
s.src = src;
|
|
4163
|
+
s.setAttribute('data-plugin-asset', pluginId);
|
|
4164
|
+
s.onload = () => resolve();
|
|
4165
|
+
s.onerror = () => reject(new Error('script failed: ' + src));
|
|
4166
|
+
document.head.appendChild(s);
|
|
4088
4167
|
});
|
|
4089
|
-
}
|
|
4090
|
-
list.sort((a, b) => b.priority - a.priority);
|
|
4091
|
-
registries.slotRevision++;
|
|
4092
|
-
},
|
|
4093
|
-
|
|
4094
|
-
registerStylesheetUrls(urls) {
|
|
4095
|
-
for (const u of urls || []) {
|
|
4096
|
-
if (typeof u === 'string' && u) {
|
|
4097
|
-
injectStylesheet(u);
|
|
4098
|
-
}
|
|
4099
|
-
}
|
|
4100
|
-
},
|
|
4101
|
-
|
|
4102
|
-
registerScriptUrls(urls) {
|
|
4103
|
-
const chain = (urls || []).filter((u) => typeof u === 'string' && u).reduce(
|
|
4104
|
-
(p, u) => p.then(() => injectScript(u)),
|
|
4105
|
-
Promise.resolve()
|
|
4106
|
-
);
|
|
4107
|
-
chain.catch((e) => console.warn('[wep] registerScriptUrls', pluginId, e));
|
|
4108
|
-
},
|
|
4109
|
-
|
|
4110
|
-
registerSanitizedHtmlSnippet() {
|
|
4111
|
-
throw new Error('registerSanitizedHtmlSnippet is not enabled')
|
|
4112
|
-
},
|
|
4113
|
-
|
|
4114
|
-
getBridge: () => bridge,
|
|
4115
|
-
|
|
4116
|
-
onTeardown(_pluginId, fn) {
|
|
4117
|
-
if (typeof fn === 'function') {
|
|
4118
|
-
registerPluginTeardown(pluginId, fn);
|
|
4119
|
-
}
|
|
4120
4168
|
}
|
|
4121
|
-
|
|
4169
|
+
const hostContext = hostKitOptions.hostContext != null && typeof hostKitOptions.hostContext === 'object'
|
|
4170
|
+
? hostKitOptions.hostContext
|
|
4171
|
+
: Object.freeze({});
|
|
4172
|
+
return {
|
|
4173
|
+
hostPluginApiVersion: HOST_PLUGIN_API_VERSION,
|
|
4174
|
+
/** 宿主注入的只读依赖(store、router、开放能力等),见 `resolveRuntimeOptions.hostContext` */
|
|
4175
|
+
hostContext,
|
|
4176
|
+
registerRoutes(routes) {
|
|
4177
|
+
const list = Array.isArray(routes) ? routes : [];
|
|
4178
|
+
if (list.length === 0) {
|
|
4179
|
+
return;
|
|
4180
|
+
}
|
|
4181
|
+
const { hasDecl, hasCfg } = analyzeRouteInputTree(list);
|
|
4182
|
+
if (hasDecl && hasCfg) {
|
|
4183
|
+
throw new Error('[wep] registerRoutes: cannot mix RouteDeclaration (componentRef) with RouteConfig (component)');
|
|
4184
|
+
}
|
|
4185
|
+
let configs;
|
|
4186
|
+
if (hasDecl) {
|
|
4187
|
+
const adapt = hostKitOptions.adaptRouteDeclarations;
|
|
4188
|
+
if (typeof adapt !== 'function') {
|
|
4189
|
+
throw new Error('[wep] registerRoutes: RouteDeclaration (componentRef) requires adaptRouteDeclarations on the host');
|
|
4190
|
+
}
|
|
4191
|
+
configs = adapt({
|
|
4192
|
+
pluginId,
|
|
4193
|
+
router,
|
|
4194
|
+
declarations: list
|
|
4195
|
+
});
|
|
4196
|
+
}
|
|
4197
|
+
else {
|
|
4198
|
+
configs = list;
|
|
4199
|
+
}
|
|
4200
|
+
if (typeof hostKitOptions.transformRoutes === 'function') {
|
|
4201
|
+
configs = hostKitOptions.transformRoutes({
|
|
4202
|
+
pluginId,
|
|
4203
|
+
router,
|
|
4204
|
+
routes: configs
|
|
4205
|
+
});
|
|
4206
|
+
}
|
|
4207
|
+
if (typeof hostKitOptions.interceptRegisterRoutes === 'function') {
|
|
4208
|
+
hostKitOptions.interceptRegisterRoutes({
|
|
4209
|
+
pluginId,
|
|
4210
|
+
router,
|
|
4211
|
+
routes: configs,
|
|
4212
|
+
applyInternalRegister
|
|
4213
|
+
});
|
|
4214
|
+
}
|
|
4215
|
+
else {
|
|
4216
|
+
applyInternalRegister(configs);
|
|
4217
|
+
}
|
|
4218
|
+
},
|
|
4219
|
+
registerMenuItems(items) {
|
|
4220
|
+
const apply = hostKitOptions.applyPluginMenuItems;
|
|
4221
|
+
if (typeof apply !== 'function') {
|
|
4222
|
+
throw new Error('[wep] registerMenuItems 需要宿主在 resolveRuntimeOptions 中提供 applyPluginMenuItems,将菜单数据并入宿主侧栏/目录 state(框架不再维护 registries.menus)');
|
|
4223
|
+
}
|
|
4224
|
+
const list = Array.isArray(items) ? items : [];
|
|
4225
|
+
const enriched = list.map((item) => ({ ...item, pluginId }));
|
|
4226
|
+
enriched.sort((a, b) => (a.order != null ? Number(a.order) : 0) - (b.order != null ? Number(b.order) : 0));
|
|
4227
|
+
apply({ pluginId, items: enriched });
|
|
4228
|
+
},
|
|
4229
|
+
registerSlotComponents(pointId, components) {
|
|
4230
|
+
if (!pointId) {
|
|
4231
|
+
return;
|
|
4232
|
+
}
|
|
4233
|
+
if (!registries.slots[pointId]) {
|
|
4234
|
+
Vue__default.default.set(registries.slots, pointId, []);
|
|
4235
|
+
}
|
|
4236
|
+
const list = registries.slots[pointId];
|
|
4237
|
+
for (const c of components) {
|
|
4238
|
+
list.push({
|
|
4239
|
+
pluginId,
|
|
4240
|
+
component: c.component,
|
|
4241
|
+
priority: c.priority != null ? c.priority : 0,
|
|
4242
|
+
key: `${pluginId}-${pointId}-${++slotItemKeySeq}`
|
|
4243
|
+
});
|
|
4244
|
+
}
|
|
4245
|
+
list.sort((a, b) => b.priority - a.priority);
|
|
4246
|
+
registries.slotRevision++;
|
|
4247
|
+
},
|
|
4248
|
+
registerStylesheetUrls(urls) {
|
|
4249
|
+
for (const u of urls || []) {
|
|
4250
|
+
if (typeof u === 'string' && u) {
|
|
4251
|
+
injectStylesheet(u);
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
},
|
|
4255
|
+
registerScriptUrls(urls) {
|
|
4256
|
+
const chain = (urls || []).filter((u) => typeof u === 'string' && u).reduce((p, u) => p.then(() => injectScript(u)), Promise.resolve());
|
|
4257
|
+
chain.catch((e) => console.warn('[wep] registerScriptUrls', pluginId, e));
|
|
4258
|
+
},
|
|
4259
|
+
registerSanitizedHtmlSnippet() {
|
|
4260
|
+
throw new Error('registerSanitizedHtmlSnippet is not enabled');
|
|
4261
|
+
},
|
|
4262
|
+
getBridge: () => bridge,
|
|
4263
|
+
onTeardown(_pluginId, fn) {
|
|
4264
|
+
if (typeof fn === 'function') {
|
|
4265
|
+
registerPluginTeardown(pluginId, fn);
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
4268
|
+
};
|
|
4122
4269
|
}
|
|
4123
4270
|
|
|
4124
4271
|
/**
|
|
4125
4272
|
* 卸载单个插件:执行 teardown、清理注册表与 activator、移除带 `data-plugin-asset` 的 DOM。
|
|
4126
4273
|
* 注意:Vue Router 3 无公开 `removeRoute`,动态路由通常需整页刷新或宿主自行维护。
|
|
4127
4274
|
*/
|
|
4128
|
-
|
|
4129
|
-
/**
|
|
4130
|
-
* @param {string} pluginId 与 manifest `id` 一致
|
|
4131
|
-
*/
|
|
4132
4275
|
function disposeWebPlugin(pluginId) {
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
}
|
|
4136
|
-
|
|
4137
|
-
runPluginTeardowns(pluginId);
|
|
4138
|
-
|
|
4139
|
-
for (let i = registries.menus.length - 1; i >= 0; i--) {
|
|
4140
|
-
if (registries.menus[i].pluginId === pluginId) {
|
|
4141
|
-
registries.menus.splice(i, 1);
|
|
4276
|
+
if (!pluginId || typeof pluginId !== 'string') {
|
|
4277
|
+
return;
|
|
4142
4278
|
}
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4279
|
+
runPluginTeardowns(pluginId);
|
|
4280
|
+
revokePluginMenusIfConfigured(pluginId);
|
|
4281
|
+
const slots = registries.slots;
|
|
4282
|
+
for (const pointId of Object.keys(slots)) {
|
|
4283
|
+
const list = slots[pointId];
|
|
4284
|
+
if (!Array.isArray(list)) {
|
|
4285
|
+
continue;
|
|
4286
|
+
}
|
|
4287
|
+
const next = list.filter((x) => x.pluginId !== pluginId);
|
|
4288
|
+
if (next.length === 0) {
|
|
4289
|
+
Vue__default.default.delete(slots, pointId);
|
|
4290
|
+
}
|
|
4291
|
+
else if (next.length !== list.length) {
|
|
4292
|
+
Vue__default.default.set(slots, pointId, next);
|
|
4293
|
+
}
|
|
4150
4294
|
}
|
|
4151
|
-
|
|
4152
|
-
if (
|
|
4153
|
-
|
|
4154
|
-
}
|
|
4155
|
-
|
|
4295
|
+
registries.slotRevision++;
|
|
4296
|
+
if (typeof window !== 'undefined' && window.__PLUGIN_ACTIVATORS__) {
|
|
4297
|
+
delete window.__PLUGIN_ACTIVATORS__[pluginId];
|
|
4298
|
+
}
|
|
4299
|
+
if (typeof document !== 'undefined') {
|
|
4300
|
+
document.querySelectorAll('[data-plugin-asset]').forEach((el) => {
|
|
4301
|
+
if (el.getAttribute('data-plugin-asset') === pluginId) {
|
|
4302
|
+
el.remove();
|
|
4303
|
+
}
|
|
4304
|
+
});
|
|
4156
4305
|
}
|
|
4157
|
-
}
|
|
4158
|
-
registries.slotRevision++;
|
|
4159
|
-
|
|
4160
|
-
if (typeof window !== 'undefined' && window.__PLUGIN_ACTIVATORS__) {
|
|
4161
|
-
delete window.__PLUGIN_ACTIVATORS__[pluginId];
|
|
4162
|
-
}
|
|
4163
|
-
|
|
4164
|
-
if (typeof document !== 'undefined') {
|
|
4165
|
-
document.querySelectorAll('[data-plugin-asset]').forEach((el) => {
|
|
4166
|
-
if (el.getAttribute('data-plugin-asset') === pluginId) {
|
|
4167
|
-
el.remove();
|
|
4168
|
-
}
|
|
4169
|
-
});
|
|
4170
|
-
}
|
|
4171
4306
|
}
|
|
4172
4307
|
|
|
4173
4308
|
/**
|
|
4174
4309
|
* 布局中的扩展点占位;插件通过 `hostApi.registerSlotComponents(pointId, ...)` 注入组件。
|
|
4310
|
+
* 样式由宿主全局 CSS 覆盖 `.extension-point` / `.plugin-point-error`。
|
|
4175
4311
|
*/
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
return { error: null }
|
|
4182
|
-
},
|
|
4183
|
-
errorCaptured(err) {
|
|
4184
|
-
this.error = err && err.message ? err.message : String(err);
|
|
4185
|
-
console.error('[wep:ExtensionPoint]', this.label, err);
|
|
4186
|
-
return false
|
|
4187
|
-
},
|
|
4188
|
-
render(h) {
|
|
4189
|
-
if (this.error) {
|
|
4190
|
-
return h(
|
|
4191
|
-
'div',
|
|
4192
|
-
{ class: 'plugin-point-error', style: { color: '#c00', fontSize: '12px' } },
|
|
4193
|
-
`[插件 ${this.label}] 渲染失败`
|
|
4194
|
-
)
|
|
4195
|
-
}
|
|
4196
|
-
const d = this.$slots.default;
|
|
4197
|
-
return d && d[0] ? d[0] : h('span')
|
|
4198
|
-
}
|
|
4199
|
-
};
|
|
4200
|
-
|
|
4201
|
-
var ExtensionPoint = {
|
|
4202
|
-
name: 'ExtensionPoint',
|
|
4203
|
-
components: { SlotErrorBoundary },
|
|
4204
|
-
props: {
|
|
4205
|
-
pointId: { type: String, required: true },
|
|
4206
|
-
slotProps: { type: Object, default: () => ({}) }
|
|
4207
|
-
},
|
|
4208
|
-
computed: {
|
|
4209
|
-
items() {
|
|
4210
|
-
void registries.slotRevision;
|
|
4211
|
-
return registries.slots[this.pointId] || []
|
|
4312
|
+
const SlotErrorBoundary = Vue__default.default.extend({
|
|
4313
|
+
name: 'SlotErrorBoundary',
|
|
4314
|
+
props: { label: String },
|
|
4315
|
+
data() {
|
|
4316
|
+
return { error: null };
|
|
4212
4317
|
},
|
|
4213
|
-
|
|
4214
|
-
|
|
4318
|
+
errorCaptured(err) {
|
|
4319
|
+
this.error = err instanceof Error && err.message ? err.message : String(err);
|
|
4320
|
+
console.error('[wep:ExtensionPoint]', this.label, err);
|
|
4321
|
+
return false;
|
|
4322
|
+
},
|
|
4323
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4324
|
+
render(h) {
|
|
4325
|
+
if (this.error) {
|
|
4326
|
+
return h('div', { class: 'plugin-point-error' }, `[插件 ${this.label}] 渲染失败`);
|
|
4327
|
+
}
|
|
4328
|
+
const d = this.$slots.default;
|
|
4329
|
+
return d && d[0] ? d[0] : h('span');
|
|
4215
4330
|
}
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4331
|
+
});
|
|
4332
|
+
var ExtensionPoint = Vue__default.default.extend({
|
|
4333
|
+
name: 'ExtensionPoint',
|
|
4334
|
+
components: { SlotErrorBoundary },
|
|
4335
|
+
props: {
|
|
4336
|
+
pointId: { type: String, required: true },
|
|
4337
|
+
slotProps: { type: Object, default: () => ({}) }
|
|
4338
|
+
},
|
|
4339
|
+
computed: {
|
|
4340
|
+
items() {
|
|
4341
|
+
void registries.slotRevision;
|
|
4342
|
+
return registries.slots[this.pointId] || [];
|
|
4343
|
+
},
|
|
4344
|
+
forwardProps() {
|
|
4345
|
+
return this.slotProps || {};
|
|
4346
|
+
}
|
|
4347
|
+
},
|
|
4348
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4349
|
+
render(h) {
|
|
4350
|
+
return h('div', {
|
|
4351
|
+
class: 'extension-point',
|
|
4352
|
+
attrs: { 'data-point-id': this.pointId }
|
|
4353
|
+
}, this.items.map((item) => h(SlotErrorBoundary, {
|
|
4229
4354
|
key: item.key,
|
|
4230
4355
|
props: { label: item.pluginId }
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
)
|
|
4235
|
-
)
|
|
4236
|
-
}
|
|
4237
|
-
};
|
|
4356
|
+
}, [h(item.component, { props: this.forwardProps })])));
|
|
4357
|
+
}
|
|
4358
|
+
});
|
|
4238
4359
|
|
|
4239
4360
|
/**
|
|
4240
4361
|
* 注册全局 `ExtensionPoint` 并异步拉取清单、激活插件。
|
|
4241
4362
|
*/
|
|
4242
|
-
|
|
4243
4363
|
/**
|
|
4244
|
-
* @param
|
|
4245
|
-
* @param
|
|
4246
|
-
* @param
|
|
4364
|
+
* @param Vue Vue 构造函数
|
|
4365
|
+
* @param router vue-router 实例
|
|
4366
|
+
* @param options 传给 `resolveRuntimeOptions`;可含 `env` 以读取 `VITE_*`
|
|
4247
4367
|
*/
|
|
4368
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4248
4369
|
function installWebExtendPluginVue2(Vue, router, options) {
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4370
|
+
const opts = options || {};
|
|
4371
|
+
const { env: injectedEnv, ...runtimeUser } = opts;
|
|
4372
|
+
if (injectedEnv && typeof injectedEnv === 'object') {
|
|
4373
|
+
setWebExtendPluginEnv(injectedEnv);
|
|
4374
|
+
}
|
|
4375
|
+
if (Vue && ExtensionPoint) {
|
|
4376
|
+
Vue.component('ExtensionPoint', ExtensionPoint);
|
|
4377
|
+
}
|
|
4378
|
+
const runtime = resolveRuntimeOptions$1(runtimeUser);
|
|
4379
|
+
return bootstrapPlugins$1(router, (id, r, kit) => createHostApi(id, r, kit || {}), runtime);
|
|
4259
4380
|
}
|
|
4260
4381
|
|
|
4261
4382
|
/**
|
|
4262
4383
|
* Vue CLI + 统一 axios(如 RuoYi `utils/request`)场景的 `install` 预设。
|
|
4263
4384
|
*/
|
|
4264
|
-
|
|
4265
|
-
/**
|
|
4266
|
-
* @typedef {object} VueCliAxiosPresetDeps
|
|
4267
|
-
* @property {(config: { url: string, method?: string }) => Promise<unknown>} request
|
|
4268
|
-
*/
|
|
4269
|
-
|
|
4270
|
-
/**
|
|
4271
|
-
* 将完整 manifest URL 转为相对 `apiBase` 的请求 path,供 axios `baseURL` 拼接。
|
|
4272
|
-
* @param {string} manifestUrl
|
|
4273
|
-
* @param {string} [apiBase]
|
|
4274
|
-
*/
|
|
4275
4385
|
function resolveManifestPathUnderApiBase(manifestUrl, apiBase) {
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4386
|
+
const base = String(apiBase !== undefined
|
|
4387
|
+
? apiBase
|
|
4388
|
+
: typeof process !== 'undefined' && process.env && process.env.VUE_APP_BASE_API
|
|
4389
|
+
? String(process.env.VUE_APP_BASE_API)
|
|
4390
|
+
: '').replace(/\/$/, '');
|
|
4391
|
+
if (typeof window === 'undefined') {
|
|
4392
|
+
return '/api/frontend-plugins';
|
|
4393
|
+
}
|
|
4394
|
+
const u = new URL(manifestUrl, window.location.origin);
|
|
4395
|
+
let path = u.pathname + u.search;
|
|
4396
|
+
if (base && path.startsWith(base)) {
|
|
4397
|
+
path = path.slice(base.length) || '/';
|
|
4398
|
+
}
|
|
4399
|
+
return path;
|
|
4290
4400
|
}
|
|
4291
|
-
|
|
4292
4401
|
/**
|
|
4293
|
-
*
|
|
4294
|
-
*
|
|
4295
|
-
* @returns {object|null}
|
|
4402
|
+
* 常见 Java 清单路径:与 `VUE_APP_BASE_API` 拼接为 `${base}/frontend-plugins`。
|
|
4403
|
+
* 若后端使用 `/api/frontend-plugins` 段,请在 extra 中显式传入 `manifestListPath`。
|
|
4296
4404
|
*/
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
return
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
return inner
|
|
4405
|
+
const defaultVueCliJavaManifestListPath = '/frontend-plugins';
|
|
4406
|
+
function bridgePrefixesFromVueCliEnv() {
|
|
4407
|
+
const base = (typeof process !== 'undefined' && process.env && process.env.VUE_APP_BASE_API
|
|
4408
|
+
? String(process.env.VUE_APP_BASE_API)
|
|
4409
|
+
: '').replace(/\/$/, '');
|
|
4410
|
+
const raw = [base ? `${base}/` : '', '/api/', '/dev-api/'].filter(Boolean);
|
|
4411
|
+
return [...new Set(raw)];
|
|
4412
|
+
}
|
|
4413
|
+
function createVueCliAxiosInstallOptions(deps, extra = {}) {
|
|
4414
|
+
const { request } = deps;
|
|
4415
|
+
if (typeof request !== 'function') {
|
|
4416
|
+
throw new Error('[wep] createVueCliAxiosInstallOptions requires deps.request');
|
|
4310
4417
|
}
|
|
4311
|
-
|
|
4312
|
-
|
|
4418
|
+
const { fetchManifest: userFetchManifest, manifestMode: extraManifestMode, staticManifestUrl: extraStaticManifestUrl, ...restExtra } = extra;
|
|
4419
|
+
const envBase = (typeof process !== 'undefined' && process.env && process.env.VUE_APP_BASE_API
|
|
4420
|
+
? String(process.env.VUE_APP_BASE_API)
|
|
4421
|
+
: '').replace(/\/$/, '');
|
|
4422
|
+
const userBase = extra.manifestBase !== undefined && String(extra.manifestBase).trim() !== ''
|
|
4423
|
+
? String(extra.manifestBase).replace(/\/$/, '')
|
|
4424
|
+
: '';
|
|
4425
|
+
const stripBase = userBase || envBase;
|
|
4426
|
+
const manifestMode = resolveManifestModeFromInputs(extraManifestMode);
|
|
4427
|
+
const staticManifestUrl = resolveStaticManifestUrlFromInputs(extraStaticManifestUrl);
|
|
4428
|
+
const fetchManifestApi = async (ctx) => {
|
|
4429
|
+
try {
|
|
4430
|
+
const url = resolveManifestPathUnderApiBase(ctx.manifestUrl, stripBase);
|
|
4431
|
+
const body = await request({
|
|
4432
|
+
url,
|
|
4433
|
+
method: 'get'
|
|
4434
|
+
});
|
|
4435
|
+
const data = unwrapNestedManifestBody(body);
|
|
4436
|
+
if (!data || typeof data !== 'object') {
|
|
4437
|
+
return {
|
|
4438
|
+
ok: false,
|
|
4439
|
+
error: new Error('[wep] invalid manifest response'),
|
|
4440
|
+
data: null
|
|
4441
|
+
};
|
|
4442
|
+
}
|
|
4443
|
+
return { ok: true, data };
|
|
4444
|
+
}
|
|
4445
|
+
catch (e) {
|
|
4446
|
+
return { ok: false, error: e, data: null };
|
|
4447
|
+
}
|
|
4448
|
+
};
|
|
4449
|
+
const fetchManifestStatic = (ctx) => fetchStaticManifestViaHttp(ctx);
|
|
4450
|
+
const fetchManifest = typeof userFetchManifest === 'function'
|
|
4451
|
+
? userFetchManifest
|
|
4452
|
+
: manifestMode === 'static'
|
|
4453
|
+
? fetchManifestStatic
|
|
4454
|
+
: fetchManifestApi;
|
|
4455
|
+
const opts = {
|
|
4456
|
+
manifestBase: stripBase || undefined,
|
|
4457
|
+
bridgeAllowedPathPrefixes: bridgePrefixesFromVueCliEnv(),
|
|
4458
|
+
manifestMode,
|
|
4459
|
+
staticManifestUrl,
|
|
4460
|
+
...restExtra,
|
|
4461
|
+
fetchManifest
|
|
4462
|
+
};
|
|
4463
|
+
const listPath = typeof process !== 'undefined' &&
|
|
4464
|
+
process.env &&
|
|
4465
|
+
process.env[webExtendPluginEnvKeys.manifestPathAlt];
|
|
4466
|
+
if (listPath && opts.manifestListPath === undefined && extra.manifestListPath === undefined) {
|
|
4467
|
+
opts.manifestListPath = String(process.env[webExtendPluginEnvKeys.manifestPathAlt]);
|
|
4313
4468
|
}
|
|
4314
|
-
|
|
4315
|
-
return d !== undefined && d !== null && typeof d === 'object' ? /** @type {object} */ (d) : o
|
|
4469
|
+
return opts;
|
|
4316
4470
|
}
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4471
|
+
/**
|
|
4472
|
+
* 少样板接入:`hostContext` 自动含 `router`(及可选 `store`)、`isDev` 与常见 `manifestListPath` 默认值。
|
|
4473
|
+
* 更多运行时字段仍可通过 `extra` 覆盖。
|
|
4474
|
+
*/
|
|
4475
|
+
function createVueCliAxiosQuickInstallOptions(
|
|
4476
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4477
|
+
router, deps, extra = {}) {
|
|
4478
|
+
const { request, hostLayoutComponent, store, hostContext: depHostContext, applyPluginMenuItems, revokePluginMenuItems } = deps;
|
|
4479
|
+
const { hostContext: extraHostContext, isDev: extraIsDev, manifestListPath: extraListPath, ...restExtra } = extra;
|
|
4480
|
+
const hostContext = {
|
|
4481
|
+
router,
|
|
4482
|
+
...(store !== undefined ? { store } : {}),
|
|
4483
|
+
...(depHostContext && typeof depHostContext === 'object' ? depHostContext : {}),
|
|
4484
|
+
...(extraHostContext && typeof extraHostContext === 'object' ? extraHostContext : {})
|
|
4485
|
+
};
|
|
4486
|
+
const manifestListPath = extraListPath !== undefined && String(extraListPath).trim() !== ''
|
|
4487
|
+
? String(extraListPath)
|
|
4488
|
+
: defaultVueCliJavaManifestListPath;
|
|
4489
|
+
return createVueCliAxiosInstallOptions({ request }, {
|
|
4490
|
+
hostLayoutComponent,
|
|
4491
|
+
hostContext,
|
|
4492
|
+
applyPluginMenuItems,
|
|
4493
|
+
revokePluginMenuItems,
|
|
4494
|
+
isDev: extraIsDev !== undefined ? Boolean(extraIsDev) : resolveBundledIsDev(),
|
|
4495
|
+
manifestListPath,
|
|
4496
|
+
...restExtra
|
|
4497
|
+
});
|
|
4326
4498
|
}
|
|
4499
|
+
const presetVueCliAxios = Object.freeze({
|
|
4500
|
+
id: 'vue-cli-axios',
|
|
4501
|
+
description: 'Vue CLI + axios request for API manifest; optional manifestMode=static uses fetch',
|
|
4502
|
+
createInstallOptions: createVueCliAxiosInstallOptions,
|
|
4503
|
+
createQuickInstallOptions: createVueCliAxiosQuickInstallOptions,
|
|
4504
|
+
defaultJavaManifestListPath: defaultVueCliJavaManifestListPath,
|
|
4505
|
+
manifestPathForApiBase: resolveManifestPathUnderApiBase,
|
|
4506
|
+
unwrapManifestBody: unwrapNestedManifestBody
|
|
4507
|
+
});
|
|
4327
4508
|
|
|
4328
4509
|
/**
|
|
4329
|
-
*
|
|
4330
|
-
* @param {Record<string, unknown>} [extra]
|
|
4510
|
+
* Vue CLI + axios 场景的一键安装:合并 hostContext、默认清单路径与 IIFE 全局 Vue。
|
|
4331
4511
|
*/
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
const envBase = (
|
|
4338
|
-
typeof process !== 'undefined' && process.env && process.env.VUE_APP_BASE_API
|
|
4339
|
-
? String(process.env.VUE_APP_BASE_API)
|
|
4340
|
-
: ''
|
|
4341
|
-
).replace(/\/$/, '');
|
|
4342
|
-
const userBase =
|
|
4343
|
-
extra.manifestBase !== undefined && String(extra.manifestBase).trim() !== ''
|
|
4344
|
-
? String(extra.manifestBase).replace(/\/$/, '')
|
|
4345
|
-
: '';
|
|
4346
|
-
const stripBase = userBase || envBase;
|
|
4347
|
-
|
|
4348
|
-
const fetchManifest = async (ctx) => {
|
|
4349
|
-
try {
|
|
4350
|
-
const url = resolveManifestPathUnderApiBase(ctx.manifestUrl, stripBase);
|
|
4351
|
-
const body = await request({
|
|
4352
|
-
url,
|
|
4353
|
-
method: 'get'
|
|
4354
|
-
});
|
|
4355
|
-
const data = unwrapNestedManifestBody(body);
|
|
4356
|
-
if (!data || typeof data !== 'object') {
|
|
4357
|
-
return {
|
|
4358
|
-
ok: false,
|
|
4359
|
-
error: new Error('[wep] invalid manifest response'),
|
|
4360
|
-
data: null
|
|
4361
|
-
}
|
|
4362
|
-
}
|
|
4363
|
-
return { ok: true, data }
|
|
4364
|
-
} catch (e) {
|
|
4365
|
-
return { ok: false, error: e, data: null }
|
|
4512
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4513
|
+
function installVueCliAxiosWebPlugins(Vue, router, deps, extra) {
|
|
4514
|
+
const exposeGlobalVue = extra?.exposeGlobalVue !== false;
|
|
4515
|
+
if (exposeGlobalVue && typeof window !== 'undefined') {
|
|
4516
|
+
window.Vue = Vue;
|
|
4366
4517
|
}
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
manifestBase: stripBase || undefined,
|
|
4371
|
-
bridgeAllowedPathPrefixes: bridgePrefixesFromVueCliEnv(),
|
|
4372
|
-
fetchManifest,
|
|
4373
|
-
...extra
|
|
4374
|
-
};
|
|
4375
|
-
|
|
4376
|
-
const listPath =
|
|
4377
|
-
typeof process !== 'undefined' && process.env && process.env.VUE_APP_WEB_PLUGIN_MANIFEST_PATH;
|
|
4378
|
-
if (listPath && opts.manifestListPath === undefined && extra.manifestListPath === undefined) {
|
|
4379
|
-
opts.manifestListPath = String(listPath);
|
|
4380
|
-
}
|
|
4381
|
-
|
|
4382
|
-
return opts
|
|
4518
|
+
const { exposeGlobalVue: _skip, ...restExtra } = extra || {};
|
|
4519
|
+
const opts = createVueCliAxiosQuickInstallOptions(router, deps, restExtra);
|
|
4520
|
+
return installWebExtendPluginVue2(Vue, router, opts);
|
|
4383
4521
|
}
|
|
4384
4522
|
|
|
4385
|
-
const presetVueCliAxios = Object.freeze({
|
|
4386
|
-
id: 'vue-cli-axios',
|
|
4387
|
-
description: 'Vue CLI + unified axios request; manifest uses host request()',
|
|
4388
|
-
createInstallOptions: createVueCliAxiosInstallOptions,
|
|
4389
|
-
manifestPathForApiBase: resolveManifestPathUnderApiBase,
|
|
4390
|
-
unwrapManifestBody: unwrapNestedManifestBody
|
|
4391
|
-
});
|
|
4392
|
-
|
|
4393
4523
|
/**
|
|
4394
4524
|
* 包对外稳定导出与 `WebExtendPluginVue2` 命名空间。
|
|
4395
4525
|
*/
|
|
4396
|
-
|
|
4397
|
-
const {
|
|
4398
|
-
bootstrapPlugins,
|
|
4399
|
-
defaultFetchWebPluginManifest,
|
|
4400
|
-
resolveRuntimeOptions
|
|
4401
|
-
} = pluginRuntime;
|
|
4402
|
-
|
|
4403
|
-
const {
|
|
4404
|
-
composeManifestFetch,
|
|
4405
|
-
manifestFetchCacheMiddleware,
|
|
4406
|
-
wrapManifestFetchWithCache
|
|
4407
|
-
} = manifestComposer;
|
|
4408
|
-
|
|
4526
|
+
const { bootstrapPlugins, defaultFetchWebPluginManifest, resolveRuntimeOptions, ensurePluginHostRoute } = pluginRuntime;
|
|
4527
|
+
const { composeManifestFetch, manifestFetchCacheMiddleware, wrapManifestFetchWithCache } = manifestComposer;
|
|
4409
4528
|
const WebExtendPluginVue2 = Object.freeze({
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4529
|
+
install: installWebExtendPluginVue2,
|
|
4530
|
+
runtime: Object.freeze({
|
|
4531
|
+
bootstrapPlugins: bootstrapPlugins$1,
|
|
4532
|
+
resolveRuntimeOptions: resolveRuntimeOptions$1,
|
|
4533
|
+
ensurePluginHostRoute: ensurePluginHostRoute$1,
|
|
4534
|
+
defaultFetchWebPluginManifest: defaultFetchWebPluginManifest$1,
|
|
4535
|
+
composeManifestFetch: composeManifestFetch$1,
|
|
4536
|
+
manifestFetchCacheMiddleware: manifestFetchCacheMiddleware$1,
|
|
4537
|
+
wrapManifestFetchWithCache: wrapManifestFetchWithCache$1
|
|
4538
|
+
}),
|
|
4539
|
+
host: Object.freeze({
|
|
4540
|
+
createHostApi,
|
|
4541
|
+
disposeWebPlugin,
|
|
4542
|
+
createRequestBridge,
|
|
4543
|
+
registries
|
|
4544
|
+
}),
|
|
4545
|
+
config: Object.freeze({
|
|
4546
|
+
defaultWebExtendPluginRuntime,
|
|
4547
|
+
setWebExtendPluginEnv,
|
|
4548
|
+
webExtendPluginEnvKeys,
|
|
4549
|
+
defaultManifestFetchCache,
|
|
4550
|
+
defaultManifestMode,
|
|
4551
|
+
routeSynthNamePrefix,
|
|
4552
|
+
peerMinimumVersions
|
|
4553
|
+
}),
|
|
4554
|
+
constants: Object.freeze({
|
|
4555
|
+
HOST_PLUGIN_API_VERSION,
|
|
4556
|
+
RUNTIME_CONSOLE_LABEL
|
|
4557
|
+
}),
|
|
4558
|
+
components: Object.freeze({
|
|
4559
|
+
ExtensionPoint
|
|
4560
|
+
}),
|
|
4561
|
+
presets: Object.freeze({
|
|
4562
|
+
vueCliAxios: presetVueCliAxios
|
|
4563
|
+
})
|
|
4439
4564
|
});
|
|
4440
4565
|
|
|
4441
4566
|
exports.ExtensionPoint = ExtensionPoint;
|
|
@@ -4447,16 +4572,25 @@ exports.composeManifestFetch = composeManifestFetch;
|
|
|
4447
4572
|
exports.createHostApi = createHostApi;
|
|
4448
4573
|
exports.createRequestBridge = createRequestBridge;
|
|
4449
4574
|
exports.createVueCliAxiosInstallOptions = createVueCliAxiosInstallOptions;
|
|
4575
|
+
exports.createVueCliAxiosQuickInstallOptions = createVueCliAxiosQuickInstallOptions;
|
|
4450
4576
|
exports.defaultFetchWebPluginManifest = defaultFetchWebPluginManifest;
|
|
4577
|
+
exports.defaultManifestFetchCache = defaultManifestFetchCache;
|
|
4578
|
+
exports.defaultManifestMode = defaultManifestMode;
|
|
4579
|
+
exports.defaultVueCliJavaManifestListPath = defaultVueCliJavaManifestListPath;
|
|
4451
4580
|
exports.defaultWebExtendPluginRuntime = defaultWebExtendPluginRuntime;
|
|
4452
4581
|
exports.disposeWebPlugin = disposeWebPlugin;
|
|
4582
|
+
exports.ensurePluginHostRoute = ensurePluginHostRoute;
|
|
4583
|
+
exports.installVueCliAxiosWebPlugins = installVueCliAxiosWebPlugins;
|
|
4453
4584
|
exports.installWebExtendPluginVue2 = installWebExtendPluginVue2;
|
|
4454
4585
|
exports.manifestFetchCacheMiddleware = manifestFetchCacheMiddleware;
|
|
4586
|
+
exports.peerMinimumVersions = peerMinimumVersions;
|
|
4455
4587
|
exports.presetVueCliAxios = presetVueCliAxios;
|
|
4456
4588
|
exports.registries = registries;
|
|
4457
4589
|
exports.resolveManifestPathUnderApiBase = resolveManifestPathUnderApiBase;
|
|
4458
4590
|
exports.resolveRuntimeOptions = resolveRuntimeOptions;
|
|
4591
|
+
exports.routeSynthNamePrefix = routeSynthNamePrefix;
|
|
4459
4592
|
exports.setWebExtendPluginEnv = setWebExtendPluginEnv;
|
|
4460
4593
|
exports.unwrapNestedManifestBody = unwrapNestedManifestBody;
|
|
4594
|
+
exports.webExtendPluginEnvKeys = webExtendPluginEnvKeys;
|
|
4461
4595
|
exports.wrapManifestFetchWithCache = wrapManifestFetchWithCache;
|
|
4462
4596
|
//# sourceMappingURL=index.cjs.map
|