web-extend-plugin-vue2 0.3.0 → 0.3.2
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 +73 -200
- package/dist/index.cjs +540 -456
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +539 -449
- package/dist/index.mjs.map +1 -1
- package/index.d.ts +18 -180
- package/package.json +3 -6
package/dist/index.mjs
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
import Vue from 'vue';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Public constants and runtime defaults consumed by resolveRuntimeOptions and
|
|
5
|
+
* other runtime modules.
|
|
6
6
|
*/
|
|
7
|
-
/** 与 `package.json` 的 peer 下限一致;`npm run test:peer-min` / CI matrix 须与此保持同步 */
|
|
8
7
|
const peerMinimumVersions = {
|
|
9
|
-
vue: '2.6.
|
|
10
|
-
vueRouter: '3.5.
|
|
8
|
+
vue: '2.6.0',
|
|
9
|
+
vueRouter: '3.5.0'
|
|
11
10
|
};
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
// 协议与品牌(发布契约;与清单 `hostPluginApiVersion` 对齐 semver 主版本,一般不随宿主覆盖)
|
|
14
|
-
// ---------------------------------------------------------------------------
|
|
15
11
|
const HOST_PLUGIN_API_VERSION = '1.0.0';
|
|
16
|
-
/** 控制台日志与首次引导横幅使用的短名称 */
|
|
17
12
|
const RUNTIME_CONSOLE_LABEL = 'web-extend-plugin-vue2';
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// 与 `build-env` / `resolveBundledEnv` 配套的键名(单一事实来源,便于检索与文档)
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
13
|
const webExtendPluginEnvKeys = {
|
|
22
14
|
manifestBase: 'VITE_FRONTEND_PLUGIN_BASE',
|
|
23
15
|
manifestListPath: 'VITE_WEB_PLUGIN_MANIFEST_PATH',
|
|
@@ -38,27 +30,14 @@ const webExtendPluginEnvKeys = {
|
|
|
38
30
|
webPluginDevIds: 'VITE_WEB_PLUGIN_DEV_IDS',
|
|
39
31
|
webPluginDevMap: 'VITE_WEB_PLUGIN_DEV_MAP',
|
|
40
32
|
pluginsBootstrapSummary: 'VITE_PLUGINS_BOOTSTRAP_SUMMARY',
|
|
41
|
-
/** 清单路径备选:部分宿主单独使用 */
|
|
42
33
|
manifestPathAlt: 'VUE_APP_WEB_PLUGIN_MANIFEST_PATH'
|
|
43
34
|
};
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
// `manifestMode` 未配置且环境未指定时的默认值
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
35
|
const defaultManifestMode = 'api';
|
|
48
|
-
// ---------------------------------------------------------------------------
|
|
49
|
-
// `manifest-fetch-composer` 中间件默认(中间件 options 可逐项覆盖)
|
|
50
|
-
// ---------------------------------------------------------------------------
|
|
51
36
|
const defaultManifestFetchCache = {
|
|
52
37
|
storageKeyPrefix: 'wep.manifestFetch.v1',
|
|
53
38
|
maxEntries: 50
|
|
54
39
|
};
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
// 路由合成名前缀(`createHostApi` 为无 name 的路由生成 `__wep_${pluginId}_${seq}`)
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
40
|
const routeSynthNamePrefix = '__wep_';
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
// 宿主可通过 `resolveRuntimeOptions` 覆盖的运行时默认值(与 README / index.d.ts 描述一致)
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
41
|
const defaultWebExtendPluginRuntime = {
|
|
63
42
|
manifestBase: '/fp-api',
|
|
64
43
|
manifestListPath: '/api/frontend-plugins',
|
|
@@ -70,11 +49,7 @@ const defaultWebExtendPluginRuntime = {
|
|
|
70
49
|
defaultImplicitDevPluginIds: [],
|
|
71
50
|
allowedScriptHosts: ['localhost', '127.0.0.1', '::1'],
|
|
72
51
|
bridgeAllowedPathPrefixes: ['/api/'],
|
|
73
|
-
/** 与 `hostLayoutComponent` 同时使用时默认父路由 name */
|
|
74
|
-
pluginRoutesParentName: '__wepPluginHost',
|
|
75
|
-
/** 插件壳路径(与菜单、ensurePluginHostRoute 一致) */
|
|
76
52
|
pluginMountPath: '/plugin',
|
|
77
|
-
/** `manifestMode=api` 且开发环境下,API 失败或 plugins 为空时尝试的静态 JSON(可放于 `public/web-plugins/`) */
|
|
78
53
|
devFallbackStaticManifestUrl: '/web-plugins/plugins.manifest.json'
|
|
79
54
|
};
|
|
80
55
|
|
|
@@ -411,6 +386,9 @@ function resolveRuntimeOptions$1(user = {}) {
|
|
|
411
386
|
...(typeof user.adaptRouteDeclarations === 'function'
|
|
412
387
|
? { adaptRouteDeclarations: user.adaptRouteDeclarations }
|
|
413
388
|
: {}),
|
|
389
|
+
...(typeof user.onPluginRoutesContributed === 'function'
|
|
390
|
+
? { onPluginRoutesContributed: user.onPluginRoutesContributed }
|
|
391
|
+
: {}),
|
|
414
392
|
...(user.hostContext !== undefined &&
|
|
415
393
|
user.hostContext !== null &&
|
|
416
394
|
typeof user.hostContext === 'object' &&
|
|
@@ -3202,7 +3180,7 @@ var semverExports = requireSemver();
|
|
|
3202
3180
|
var semver = /*@__PURE__*/getDefaultExportFromCjs(semverExports);
|
|
3203
3181
|
|
|
3204
3182
|
/**
|
|
3205
|
-
* 引导时注入的 router 引用,供 disposeWebPlugin
|
|
3183
|
+
* 引导时注入的 router 引用,供 disposeWebPlugin 卸载插件动态路由。
|
|
3206
3184
|
*/
|
|
3207
3185
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3208
3186
|
let bootstrapRouter;
|
|
@@ -3216,6 +3194,223 @@ function getPluginBootstrapRouter() {
|
|
|
3216
3194
|
return bootstrapRouter;
|
|
3217
3195
|
}
|
|
3218
3196
|
|
|
3197
|
+
/**
|
|
3198
|
+
* 记录各插件通过 registerRoutes 挂到 router 上的顶层路由,
|
|
3199
|
+
* 优先使用官方 `addRoute()` 返回的 disposer 做卸载;仅在 disposer 不可用时回退到 name。
|
|
3200
|
+
*/
|
|
3201
|
+
const pluginIdToTopRoutes = new Map();
|
|
3202
|
+
function recordPluginTopRoutes(pluginId, routes) {
|
|
3203
|
+
if (!pluginId || routes.length === 0) {
|
|
3204
|
+
return;
|
|
3205
|
+
}
|
|
3206
|
+
const normalized = routes.filter((route) => route && route.name);
|
|
3207
|
+
if (normalized.length === 0) {
|
|
3208
|
+
return;
|
|
3209
|
+
}
|
|
3210
|
+
const cur = pluginIdToTopRoutes.get(pluginId) || [];
|
|
3211
|
+
pluginIdToTopRoutes.set(pluginId, cur.concat(normalized));
|
|
3212
|
+
}
|
|
3213
|
+
function tryRemoveRouteByName(
|
|
3214
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3215
|
+
router, pluginId, name) {
|
|
3216
|
+
if (!router || typeof router.removeRoute !== 'function') {
|
|
3217
|
+
console.warn('[wep] route disposer unavailable,且 router.removeRoute 不可用,动态路由可能残留', pluginId, name);
|
|
3218
|
+
return;
|
|
3219
|
+
}
|
|
3220
|
+
try {
|
|
3221
|
+
router.removeRoute(name);
|
|
3222
|
+
}
|
|
3223
|
+
catch (e) {
|
|
3224
|
+
console.warn('[wep] removeRoute fallback failed', name, e);
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
/**
|
|
3228
|
+
* 从 matcher 移除该插件登记过的顶层路由(含其子树)。
|
|
3229
|
+
* 优先走 `router.addRoute()` 返回的 disposer;仅在 disposer 不可用时回退 `router.removeRoute(name)`。
|
|
3230
|
+
*/
|
|
3231
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3232
|
+
function removeRegisteredRoutesForPlugin(router, pluginId) {
|
|
3233
|
+
const routes = pluginIdToTopRoutes.get(pluginId);
|
|
3234
|
+
if (!routes || routes.length === 0) {
|
|
3235
|
+
return;
|
|
3236
|
+
}
|
|
3237
|
+
for (let i = routes.length - 1; i >= 0; i--) {
|
|
3238
|
+
const route = routes[i];
|
|
3239
|
+
if (typeof route.dispose === 'function') {
|
|
3240
|
+
try {
|
|
3241
|
+
route.dispose();
|
|
3242
|
+
continue;
|
|
3243
|
+
}
|
|
3244
|
+
catch (e) {
|
|
3245
|
+
console.warn('[wep] route disposer failed', route.name, e);
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
tryRemoveRouteByName(router, pluginId, route.name);
|
|
3249
|
+
}
|
|
3250
|
+
pluginIdToTopRoutes.delete(pluginId);
|
|
3251
|
+
}
|
|
3252
|
+
/** 调试或宿主高级场景:查询已登记 name(勿依赖顺序语义) */
|
|
3253
|
+
function getRegisteredTopRouteNamesForPlugin(pluginId) {
|
|
3254
|
+
return (pluginIdToTopRoutes.get(pluginId) || []).map((route) => route.name);
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
/**
|
|
3258
|
+
* Host-side reactive registries for extension-point slot components.
|
|
3259
|
+
* Menus/sidebar mapping stays on the host and is not duplicated here.
|
|
3260
|
+
*/
|
|
3261
|
+
const registries = {
|
|
3262
|
+
slots: {},
|
|
3263
|
+
slotRevision: 0
|
|
3264
|
+
};
|
|
3265
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3266
|
+
function ensureRegistriesReactive(VueLike) {
|
|
3267
|
+
if (!VueLike || typeof VueLike.observable !== 'function') {
|
|
3268
|
+
return registries;
|
|
3269
|
+
}
|
|
3270
|
+
if (registries.__wepObservedBy__ === VueLike) {
|
|
3271
|
+
return registries;
|
|
3272
|
+
}
|
|
3273
|
+
VueLike.observable(registries);
|
|
3274
|
+
Object.defineProperty(registries, '__wepObservedBy__', {
|
|
3275
|
+
value: VueLike,
|
|
3276
|
+
configurable: true,
|
|
3277
|
+
enumerable: false,
|
|
3278
|
+
writable: true
|
|
3279
|
+
});
|
|
3280
|
+
return registries;
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3283
|
+
/**
|
|
3284
|
+
* 按插件 id 收集 `onTeardown` 回调,供 `disposeWebPlugin` 统一执行。
|
|
3285
|
+
*/
|
|
3286
|
+
const _byPlugin = new Map();
|
|
3287
|
+
function registerPluginTeardown(pluginId, fn) {
|
|
3288
|
+
if (typeof fn !== 'function') {
|
|
3289
|
+
return;
|
|
3290
|
+
}
|
|
3291
|
+
let list = _byPlugin.get(pluginId);
|
|
3292
|
+
if (!list) {
|
|
3293
|
+
list = [];
|
|
3294
|
+
_byPlugin.set(pluginId, list);
|
|
3295
|
+
}
|
|
3296
|
+
list.push(fn);
|
|
3297
|
+
}
|
|
3298
|
+
function runPluginTeardowns(pluginId) {
|
|
3299
|
+
const list = _byPlugin.get(pluginId);
|
|
3300
|
+
if (!list) {
|
|
3301
|
+
return;
|
|
3302
|
+
}
|
|
3303
|
+
_byPlugin.delete(pluginId);
|
|
3304
|
+
for (const fn of list) {
|
|
3305
|
+
try {
|
|
3306
|
+
fn();
|
|
3307
|
+
}
|
|
3308
|
+
catch (e) {
|
|
3309
|
+
console.warn('[wep] teardown failed', pluginId, e);
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
/**
|
|
3315
|
+
* 动态加载脚本(去重与并发合并)。
|
|
3316
|
+
*/
|
|
3317
|
+
const loadScriptMemo = new Map();
|
|
3318
|
+
function markScriptOwnership(script, pluginId) {
|
|
3319
|
+
if (pluginId) {
|
|
3320
|
+
script.setAttribute('data-plugin-asset', pluginId);
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
function clearLoadedScriptMemo(src) {
|
|
3324
|
+
if (typeof src === 'string' && src) {
|
|
3325
|
+
loadScriptMemo.delete(src);
|
|
3326
|
+
return;
|
|
3327
|
+
}
|
|
3328
|
+
loadScriptMemo.clear();
|
|
3329
|
+
}
|
|
3330
|
+
function loadScript(src, pluginId) {
|
|
3331
|
+
if (typeof document === 'undefined') {
|
|
3332
|
+
return Promise.reject(new Error('loadScript: no document'));
|
|
3333
|
+
}
|
|
3334
|
+
if (loadScriptMemo.has(src)) {
|
|
3335
|
+
return loadScriptMemo.get(src);
|
|
3336
|
+
}
|
|
3337
|
+
const p = new Promise((resolve, reject) => {
|
|
3338
|
+
const scripts = document.getElementsByTagName('script');
|
|
3339
|
+
for (let i = 0; i < scripts.length; i++) {
|
|
3340
|
+
const el = scripts[i];
|
|
3341
|
+
if (el.src === src) {
|
|
3342
|
+
markScriptOwnership(el, pluginId);
|
|
3343
|
+
if (el.getAttribute('data-wep-loaded') === 'true') {
|
|
3344
|
+
resolve();
|
|
3345
|
+
return;
|
|
3346
|
+
}
|
|
3347
|
+
el.addEventListener('load', () => {
|
|
3348
|
+
el.setAttribute('data-wep-loaded', 'true');
|
|
3349
|
+
resolve();
|
|
3350
|
+
}, { once: true });
|
|
3351
|
+
el.addEventListener('error', () => reject(new Error('loadScript failed: ' + src)), { once: true });
|
|
3352
|
+
return;
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
const s = document.createElement('script');
|
|
3356
|
+
s.async = true;
|
|
3357
|
+
s.src = src;
|
|
3358
|
+
markScriptOwnership(s, pluginId);
|
|
3359
|
+
s.onload = () => {
|
|
3360
|
+
s.setAttribute('data-wep-loaded', 'true');
|
|
3361
|
+
resolve();
|
|
3362
|
+
};
|
|
3363
|
+
s.onerror = () => reject(new Error('loadScript failed: ' + src));
|
|
3364
|
+
document.head.appendChild(s);
|
|
3365
|
+
});
|
|
3366
|
+
loadScriptMemo.set(src, p);
|
|
3367
|
+
p.catch(() => loadScriptMemo.delete(src));
|
|
3368
|
+
return p;
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3371
|
+
/**
|
|
3372
|
+
* 卸载单个插件:执行 teardown、移除该插件登记的动态路由、清理注册表与 activator、移除带 `data-plugin-asset` 的 DOM。
|
|
3373
|
+
* 优先使用 `addRoute()` 返回的 disposer;必要时回退 `router.removeRoute(name)`。
|
|
3374
|
+
*/
|
|
3375
|
+
function disposeWebPlugin(pluginId) {
|
|
3376
|
+
if (!pluginId || typeof pluginId !== 'string') {
|
|
3377
|
+
return;
|
|
3378
|
+
}
|
|
3379
|
+
runPluginTeardowns(pluginId);
|
|
3380
|
+
removeRegisteredRoutesForPlugin(getPluginBootstrapRouter(), pluginId);
|
|
3381
|
+
const slots = registries.slots;
|
|
3382
|
+
for (const pointId of Object.keys(slots)) {
|
|
3383
|
+
const list = slots[pointId];
|
|
3384
|
+
if (!Array.isArray(list)) {
|
|
3385
|
+
continue;
|
|
3386
|
+
}
|
|
3387
|
+
const next = list.filter((x) => x.pluginId !== pluginId);
|
|
3388
|
+
if (next.length === 0) {
|
|
3389
|
+
Vue.delete(slots, pointId);
|
|
3390
|
+
}
|
|
3391
|
+
else if (next.length !== list.length) {
|
|
3392
|
+
Vue.set(slots, pointId, next);
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
registries.slotRevision++;
|
|
3396
|
+
if (typeof window !== 'undefined' && window.__PLUGIN_ACTIVATORS__) {
|
|
3397
|
+
delete window.__PLUGIN_ACTIVATORS__[pluginId];
|
|
3398
|
+
}
|
|
3399
|
+
if (typeof document !== 'undefined') {
|
|
3400
|
+
document.querySelectorAll('[data-plugin-asset]').forEach((el) => {
|
|
3401
|
+
if (el.getAttribute('data-plugin-asset') === pluginId) {
|
|
3402
|
+
if (el.tagName === 'SCRIPT') {
|
|
3403
|
+
const src = el.src;
|
|
3404
|
+
if (src) {
|
|
3405
|
+
clearLoadedScriptMemo(src);
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
el.remove();
|
|
3409
|
+
}
|
|
3410
|
+
});
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3219
3414
|
/**
|
|
3220
3415
|
* 开发模式插件 URL 映射(显式 JSON + 隐式 dev 探测)。
|
|
3221
3416
|
*/
|
|
@@ -3378,49 +3573,6 @@ function startPluginDevSseForMap(devMap, isDev, hostSet, ssePath) {
|
|
|
3378
3573
|
}
|
|
3379
3574
|
}
|
|
3380
3575
|
|
|
3381
|
-
/**
|
|
3382
|
-
* 动态加载脚本(去重与并发合并)。
|
|
3383
|
-
*/
|
|
3384
|
-
const loadScriptMemo = new Map();
|
|
3385
|
-
function loadScript(src) {
|
|
3386
|
-
if (typeof document === 'undefined') {
|
|
3387
|
-
return Promise.reject(new Error('loadScript: no document'));
|
|
3388
|
-
}
|
|
3389
|
-
if (loadScriptMemo.has(src)) {
|
|
3390
|
-
return loadScriptMemo.get(src);
|
|
3391
|
-
}
|
|
3392
|
-
const p = new Promise((resolve, reject) => {
|
|
3393
|
-
const scripts = document.getElementsByTagName('script');
|
|
3394
|
-
for (let i = 0; i < scripts.length; i++) {
|
|
3395
|
-
const el = scripts[i];
|
|
3396
|
-
if (el.src === src) {
|
|
3397
|
-
if (el.getAttribute('data-wep-loaded') === 'true') {
|
|
3398
|
-
resolve();
|
|
3399
|
-
return;
|
|
3400
|
-
}
|
|
3401
|
-
el.addEventListener('load', () => {
|
|
3402
|
-
el.setAttribute('data-wep-loaded', 'true');
|
|
3403
|
-
resolve();
|
|
3404
|
-
}, { once: true });
|
|
3405
|
-
el.addEventListener('error', () => reject(new Error('loadScript failed: ' + src)), { once: true });
|
|
3406
|
-
return;
|
|
3407
|
-
}
|
|
3408
|
-
}
|
|
3409
|
-
const s = document.createElement('script');
|
|
3410
|
-
s.async = true;
|
|
3411
|
-
s.src = src;
|
|
3412
|
-
s.onload = () => {
|
|
3413
|
-
s.setAttribute('data-wep-loaded', 'true');
|
|
3414
|
-
resolve();
|
|
3415
|
-
};
|
|
3416
|
-
s.onerror = () => reject(new Error('loadScript failed: ' + src));
|
|
3417
|
-
document.head.appendChild(s);
|
|
3418
|
-
});
|
|
3419
|
-
loadScriptMemo.set(src, p);
|
|
3420
|
-
p.catch(() => loadScriptMemo.delete(src));
|
|
3421
|
-
return p;
|
|
3422
|
-
}
|
|
3423
|
-
|
|
3424
3576
|
let _printed = false;
|
|
3425
3577
|
/** 在首次引导插件时打印一行运行时标识(非大块 ASCII art)。 */
|
|
3426
3578
|
function printRuntimeBannerOnce() {
|
|
@@ -3569,6 +3721,29 @@ function resolveStaticManifestUrlForFetch(url, origin) {
|
|
|
3569
3721
|
}
|
|
3570
3722
|
}
|
|
3571
3723
|
|
|
3724
|
+
/**
|
|
3725
|
+
* 记录当前已成功激活的插件 id,供宿主做只读观测。
|
|
3726
|
+
*/
|
|
3727
|
+
const activatedPluginIds = new Set();
|
|
3728
|
+
function clearActivatedPluginIds() {
|
|
3729
|
+
activatedPluginIds.clear();
|
|
3730
|
+
}
|
|
3731
|
+
function markPluginActivated(pluginId) {
|
|
3732
|
+
if (!pluginId) {
|
|
3733
|
+
return;
|
|
3734
|
+
}
|
|
3735
|
+
activatedPluginIds.add(pluginId);
|
|
3736
|
+
}
|
|
3737
|
+
function markPluginDeactivated(pluginId) {
|
|
3738
|
+
if (!pluginId) {
|
|
3739
|
+
return;
|
|
3740
|
+
}
|
|
3741
|
+
activatedPluginIds.delete(pluginId);
|
|
3742
|
+
}
|
|
3743
|
+
function getActivatedPluginIds$1() {
|
|
3744
|
+
return [...activatedPluginIds];
|
|
3745
|
+
}
|
|
3746
|
+
|
|
3572
3747
|
/**
|
|
3573
3748
|
* 拉取插件清单、加载入口脚本并调用各插件 `activator`。
|
|
3574
3749
|
*/
|
|
@@ -3604,7 +3779,7 @@ async function loadPluginEntry(p, entryUrl, devMap, hostSet) {
|
|
|
3604
3779
|
catch (e) {
|
|
3605
3780
|
console.warn('[wep] dev import failed, trying manifest entryUrl', p.id, e);
|
|
3606
3781
|
if (entryUrl && isScriptHostAllowed(entryUrl, hostSet)) {
|
|
3607
|
-
await loadScript(entryUrl);
|
|
3782
|
+
await loadScript(entryUrl, p.id);
|
|
3608
3783
|
}
|
|
3609
3784
|
return;
|
|
3610
3785
|
}
|
|
@@ -3614,7 +3789,40 @@ async function loadPluginEntry(p, entryUrl, devMap, hostSet) {
|
|
|
3614
3789
|
console.warn('[wep] skip (entryUrl not allowed)', p.id, entryUrl);
|
|
3615
3790
|
return;
|
|
3616
3791
|
}
|
|
3617
|
-
await loadScript(entryUrl);
|
|
3792
|
+
await loadScript(entryUrl, p.id);
|
|
3793
|
+
}
|
|
3794
|
+
function coercePriority(value) {
|
|
3795
|
+
if (value === null || value === undefined) {
|
|
3796
|
+
return null;
|
|
3797
|
+
}
|
|
3798
|
+
const n = Number(value);
|
|
3799
|
+
return Number.isFinite(n) ? n : null;
|
|
3800
|
+
}
|
|
3801
|
+
function sortByPriority(plugins) {
|
|
3802
|
+
return plugins
|
|
3803
|
+
.map((entry, index) => ({
|
|
3804
|
+
entry,
|
|
3805
|
+
priority: coercePriority(entry.priority),
|
|
3806
|
+
index
|
|
3807
|
+
}))
|
|
3808
|
+
.sort((a, b) => {
|
|
3809
|
+
const aHas = a.priority !== null;
|
|
3810
|
+
const bHas = b.priority !== null;
|
|
3811
|
+
if (!aHas && !bHas) {
|
|
3812
|
+
return a.index - b.index;
|
|
3813
|
+
}
|
|
3814
|
+
if (!aHas) {
|
|
3815
|
+
return 1;
|
|
3816
|
+
}
|
|
3817
|
+
if (!bHas) {
|
|
3818
|
+
return -1;
|
|
3819
|
+
}
|
|
3820
|
+
if (a.priority !== b.priority) {
|
|
3821
|
+
return a.priority - b.priority;
|
|
3822
|
+
}
|
|
3823
|
+
return a.index - b.index;
|
|
3824
|
+
})
|
|
3825
|
+
.map((decorated) => decorated.entry);
|
|
3618
3826
|
}
|
|
3619
3827
|
async function bootstrapPlugins$1(
|
|
3620
3828
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -3626,6 +3834,7 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3626
3834
|
return;
|
|
3627
3835
|
}
|
|
3628
3836
|
printRuntimeBannerOnce();
|
|
3837
|
+
clearActivatedPluginIds();
|
|
3629
3838
|
const opts = resolveRuntimeOptions$1(runtimeOptions || {});
|
|
3630
3839
|
setPluginBootstrapRouter(router);
|
|
3631
3840
|
ensurePluginHostRoute$1(router, opts);
|
|
@@ -3704,6 +3913,9 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3704
3913
|
: {}),
|
|
3705
3914
|
...(typeof opts.adaptRouteDeclarations === 'function'
|
|
3706
3915
|
? { adaptRouteDeclarations: opts.adaptRouteDeclarations }
|
|
3916
|
+
: {}),
|
|
3917
|
+
...(typeof opts.onPluginRoutesContributed === 'function'
|
|
3918
|
+
? { onPluginRoutesContributed: opts.onPluginRoutesContributed }
|
|
3707
3919
|
: {})
|
|
3708
3920
|
};
|
|
3709
3921
|
if (!manifestResult.ok) {
|
|
@@ -3732,22 +3944,30 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3732
3944
|
const maj = coerced ? coerced.major : 0;
|
|
3733
3945
|
const range = `^${maj}.0.0`;
|
|
3734
3946
|
if (!semver.satisfies(HOST_PLUGIN_API_VERSION, range, { includePrerelease: true })) {
|
|
3735
|
-
console.warn('[wep] host API version mismatch', {
|
|
3947
|
+
console.warn('[wep] manifest host API version mismatch; skip bootstrap', {
|
|
3736
3948
|
host: HOST_PLUGIN_API_VERSION,
|
|
3737
3949
|
manifest: apiVer
|
|
3738
3950
|
});
|
|
3951
|
+
if (shouldShowBootstrapSummary(opts)) {
|
|
3952
|
+
console.info('[wep] bootstrap_summary', {
|
|
3953
|
+
ok: false,
|
|
3954
|
+
reason: 'manifest_host_api_version_mismatch'
|
|
3955
|
+
});
|
|
3956
|
+
}
|
|
3957
|
+
return;
|
|
3739
3958
|
}
|
|
3740
3959
|
}
|
|
3741
3960
|
window.__PLUGIN_ACTIVATORS__ = window.__PLUGIN_ACTIVATORS__ || {};
|
|
3742
|
-
const
|
|
3961
|
+
const originalPlugins = data.plugins || [];
|
|
3962
|
+
const plugins = sortByPriority(originalPlugins);
|
|
3743
3963
|
if (plugins.length === 0) {
|
|
3744
3964
|
const hint = isStatic ? 'check static JSON file and plugins[]' : 'check backend and URL';
|
|
3745
3965
|
console.info('[wep] empty plugin manifest — ' + hint, manifestUrlUsed);
|
|
3746
3966
|
}
|
|
3747
3967
|
const summary = {
|
|
3748
|
-
manifestCount:
|
|
3968
|
+
manifestCount: originalPlugins.length,
|
|
3749
3969
|
activated: 0,
|
|
3750
|
-
|
|
3970
|
+
skipApiVersion: 0,
|
|
3751
3971
|
skipLoad: 0,
|
|
3752
3972
|
skipNoActivator: 0,
|
|
3753
3973
|
activateFail: 0
|
|
@@ -3755,8 +3975,10 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3755
3975
|
for (const p of plugins) {
|
|
3756
3976
|
const range = p.engines && p.engines.host;
|
|
3757
3977
|
if (range && !semver.satisfies(HOST_PLUGIN_API_VERSION, range, { includePrerelease: true })) {
|
|
3758
|
-
console.warn('[wep] skip plugin (engines.host)', p.id, range
|
|
3759
|
-
|
|
3978
|
+
console.warn('[wep] skip plugin (engines.host)', p.id, range, {
|
|
3979
|
+
host: HOST_PLUGIN_API_VERSION
|
|
3980
|
+
});
|
|
3981
|
+
summary.skipApiVersion++;
|
|
3760
3982
|
continue;
|
|
3761
3983
|
}
|
|
3762
3984
|
const entryUrl = p.entryUrl;
|
|
@@ -3792,6 +4014,11 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3792
4014
|
const hostApi = createHostApiFactory(p.id, router, hostKit);
|
|
3793
4015
|
try {
|
|
3794
4016
|
await Promise.resolve(activator(hostApi, { pluginRecord }));
|
|
4017
|
+
markPluginActivated(p.id);
|
|
4018
|
+
const teardownCapableHostApi = hostApi;
|
|
4019
|
+
if (typeof teardownCapableHostApi.onTeardown === 'function') {
|
|
4020
|
+
teardownCapableHostApi.onTeardown(p.id, () => markPluginDeactivated(p.id));
|
|
4021
|
+
}
|
|
3795
4022
|
summary.activated++;
|
|
3796
4023
|
if (typeof opts.onAfterPluginActivate === 'function') {
|
|
3797
4024
|
await Promise.resolve(opts.onAfterPluginActivate({
|
|
@@ -3804,6 +4031,12 @@ createHostApiFactory, runtimeOptions) {
|
|
|
3804
4031
|
}
|
|
3805
4032
|
catch (e) {
|
|
3806
4033
|
console.error('[wep] activate failed', p.id, e);
|
|
4034
|
+
try {
|
|
4035
|
+
disposeWebPlugin(p.id);
|
|
4036
|
+
}
|
|
4037
|
+
catch (disposeErr) {
|
|
4038
|
+
console.warn('[wep] rollback failed after activation error', p.id, disposeErr);
|
|
4039
|
+
}
|
|
3807
4040
|
summary.activateFail++;
|
|
3808
4041
|
if (typeof opts.onPluginActivateError === 'function') {
|
|
3809
4042
|
try {
|
|
@@ -3834,6 +4067,7 @@ var pluginRuntime = /*#__PURE__*/Object.freeze({
|
|
|
3834
4067
|
bootstrapPlugins: bootstrapPlugins$1,
|
|
3835
4068
|
defaultFetchWebPluginManifest: defaultFetchWebPluginManifest$1,
|
|
3836
4069
|
ensurePluginHostRoute: ensurePluginHostRoute$1,
|
|
4070
|
+
getActivatedPluginIds: getActivatedPluginIds$1,
|
|
3837
4071
|
resolveRuntimeOptions: resolveRuntimeOptions$1
|
|
3838
4072
|
});
|
|
3839
4073
|
|
|
@@ -3992,48 +4226,40 @@ var manifestComposer = /*#__PURE__*/Object.freeze({
|
|
|
3992
4226
|
});
|
|
3993
4227
|
|
|
3994
4228
|
/**
|
|
3995
|
-
*
|
|
3996
|
-
* 菜单由宿主从路由 `meta` 推导(见 `buildMenuDescriptorsFromRoutes`),框架不维护平行菜单列表。
|
|
3997
|
-
*/
|
|
3998
|
-
const registries = Vue.observable({
|
|
3999
|
-
slots: {},
|
|
4000
|
-
slotRevision: 0
|
|
4001
|
-
});
|
|
4002
|
-
|
|
4003
|
-
/**
|
|
4004
|
-
* 按插件 id 收集 `onTeardown` 回调,供 `disposeWebPlugin` 统一执行。
|
|
4229
|
+
* 插件访问后端的受控通道:`fetch` 仅允许落在配置的路径前缀下(默认 `/api/`),默认 `credentials: 'same-origin'`。
|
|
4005
4230
|
*/
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
return;
|
|
4231
|
+
function normalizeBridgePath(input) {
|
|
4232
|
+
if (typeof window === 'undefined') {
|
|
4233
|
+
throw new Error('[wep:bridge] window is unavailable');
|
|
4010
4234
|
}
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
list = [];
|
|
4014
|
-
_byPlugin.set(pluginId, list);
|
|
4235
|
+
if (typeof input !== 'string' || !input.startsWith('/')) {
|
|
4236
|
+
throw new Error('[wep:bridge] path must start with /');
|
|
4015
4237
|
}
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
function runPluginTeardowns(pluginId) {
|
|
4019
|
-
const list = _byPlugin.get(pluginId);
|
|
4020
|
-
if (!list) {
|
|
4021
|
-
return;
|
|
4238
|
+
if (input.includes('\\')) {
|
|
4239
|
+
throw new Error('[wep:bridge] path must not contain backslashes');
|
|
4022
4240
|
}
|
|
4023
|
-
|
|
4024
|
-
|
|
4241
|
+
const url = new URL(input, window.location.origin);
|
|
4242
|
+
if (url.origin !== window.location.origin) {
|
|
4243
|
+
throw new Error('[wep:bridge] cross-origin path is not allowed');
|
|
4244
|
+
}
|
|
4245
|
+
const normalized = url.pathname;
|
|
4246
|
+
const decodedPath = (() => {
|
|
4025
4247
|
try {
|
|
4026
|
-
|
|
4248
|
+
return decodeURIComponent(normalized);
|
|
4027
4249
|
}
|
|
4028
|
-
catch
|
|
4029
|
-
|
|
4250
|
+
catch {
|
|
4251
|
+
return normalized;
|
|
4030
4252
|
}
|
|
4031
|
-
}
|
|
4253
|
+
})();
|
|
4254
|
+
const hasTraversalSegment = decodedPath
|
|
4255
|
+
.split('/')
|
|
4256
|
+
.filter(Boolean)
|
|
4257
|
+
.some((segment) => segment === '.' || segment === '..');
|
|
4258
|
+
if (hasTraversalSegment) {
|
|
4259
|
+
throw new Error('[wep:bridge] path traversal is not allowed');
|
|
4260
|
+
}
|
|
4261
|
+
return normalized + url.search;
|
|
4032
4262
|
}
|
|
4033
|
-
|
|
4034
|
-
/**
|
|
4035
|
-
* 插件访问后端的受控通道:`fetch` 仅允许落在配置的路径前缀下(默认 `/api/`),默认 `credentials: 'same-origin'`。
|
|
4036
|
-
*/
|
|
4037
4263
|
function createRequestBridge(config = {}) {
|
|
4038
4264
|
const raw = Array.isArray(config.allowedPathPrefixes) && config.allowedPathPrefixes.length > 0
|
|
4039
4265
|
? config.allowedPathPrefixes
|
|
@@ -4041,14 +4267,13 @@ function createRequestBridge(config = {}) {
|
|
|
4041
4267
|
const allowedPathPrefixes = raw.map((p) => ensureLeadingPath(p));
|
|
4042
4268
|
return {
|
|
4043
4269
|
async request(path, init = {}) {
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
}
|
|
4047
|
-
const allowed = allowedPathPrefixes.some((p) => path.startsWith(p));
|
|
4270
|
+
const normalizedPath = normalizeBridgePath(path);
|
|
4271
|
+
const pathnameOnly = normalizedPath.split('?')[0];
|
|
4272
|
+
const allowed = allowedPathPrefixes.some((p) => pathnameOnly === p || pathnameOnly.startsWith(`${p.replace(/\/$/, '')}/`));
|
|
4048
4273
|
if (!allowed) {
|
|
4049
|
-
throw new Error('[wep:bridge] path not allowed: ' +
|
|
4274
|
+
throw new Error('[wep:bridge] path not allowed: ' + normalizedPath);
|
|
4050
4275
|
}
|
|
4051
|
-
return fetch(
|
|
4276
|
+
return fetch(normalizedPath, {
|
|
4052
4277
|
credentials: 'same-origin',
|
|
4053
4278
|
...init
|
|
4054
4279
|
});
|
|
@@ -4057,51 +4282,83 @@ function createRequestBridge(config = {}) {
|
|
|
4057
4282
|
}
|
|
4058
4283
|
|
|
4059
4284
|
/**
|
|
4060
|
-
*
|
|
4285
|
+
* 按插件记录已贡献路由的可序列化快照,避免宿主依赖 router 内部 matcher 形态。
|
|
4061
4286
|
*/
|
|
4062
|
-
const
|
|
4063
|
-
function
|
|
4064
|
-
if (
|
|
4065
|
-
return;
|
|
4287
|
+
const contributedRoutesByPlugin = new Map();
|
|
4288
|
+
function sanitizeValue(value) {
|
|
4289
|
+
if (value == null) {
|
|
4290
|
+
return value;
|
|
4066
4291
|
}
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
}
|
|
4070
|
-
/**
|
|
4071
|
-
* 从 matcher 移除该插件登记过的顶层路由(含其子树)。
|
|
4072
|
-
* 需 vue-router ≥3.5 的 removeRoute;否则静默跳过。
|
|
4073
|
-
*/
|
|
4074
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4075
|
-
function removeRegisteredRoutesForPlugin(router, pluginId) {
|
|
4076
|
-
const names = pluginIdToRouteNames.get(pluginId);
|
|
4077
|
-
if (!names || names.length === 0) {
|
|
4078
|
-
return;
|
|
4292
|
+
if (typeof value === 'function') {
|
|
4293
|
+
return undefined;
|
|
4079
4294
|
}
|
|
4080
|
-
if (
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4295
|
+
if (Array.isArray(value)) {
|
|
4296
|
+
return value
|
|
4297
|
+
.map((item) => sanitizeValue(item))
|
|
4298
|
+
.filter((item) => item !== undefined);
|
|
4084
4299
|
}
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4300
|
+
if (typeof value !== 'object') {
|
|
4301
|
+
return value;
|
|
4302
|
+
}
|
|
4303
|
+
const out = {};
|
|
4304
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
4305
|
+
if (key === 'component' || key === 'components') {
|
|
4306
|
+
continue;
|
|
4088
4307
|
}
|
|
4089
|
-
|
|
4090
|
-
|
|
4308
|
+
const sanitized = sanitizeValue(raw);
|
|
4309
|
+
if (sanitized !== undefined) {
|
|
4310
|
+
out[key] = sanitized;
|
|
4091
4311
|
}
|
|
4092
4312
|
}
|
|
4093
|
-
|
|
4313
|
+
return out;
|
|
4094
4314
|
}
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4315
|
+
function sanitizeContributedRoutes(routes) {
|
|
4316
|
+
return routes
|
|
4317
|
+
.map((route) => sanitizeValue(route))
|
|
4318
|
+
.filter((route) => !!route && typeof route === 'object');
|
|
4319
|
+
}
|
|
4320
|
+
function recordContributedRoutesForPlugin(pluginId, routes) {
|
|
4321
|
+
const sanitized = sanitizeContributedRoutes(routes);
|
|
4322
|
+
const current = contributedRoutesByPlugin.get(pluginId) || [];
|
|
4323
|
+
const next = current.concat(sanitized);
|
|
4324
|
+
contributedRoutesByPlugin.set(pluginId, next);
|
|
4325
|
+
return next.map((route) => sanitizeValue(route));
|
|
4326
|
+
}
|
|
4327
|
+
function getContributedRoutesForPlugin(pluginId) {
|
|
4328
|
+
const routes = contributedRoutesByPlugin.get(pluginId) || [];
|
|
4329
|
+
return routes.map((route) => sanitizeValue(route));
|
|
4330
|
+
}
|
|
4331
|
+
function clearContributedRoutesForPlugin(pluginId) {
|
|
4332
|
+
contributedRoutesByPlugin.delete(pluginId);
|
|
4098
4333
|
}
|
|
4099
4334
|
|
|
4100
4335
|
/**
|
|
4101
|
-
* 构造插件 `activator(hostApi)` 使用的宿主 API
|
|
4336
|
+
* 构造插件 `activator(hostApi)` 使用的宿主 API:路由、扩展点、资源与受控请求桥。
|
|
4102
4337
|
*/
|
|
4103
4338
|
let slotItemKeySeq = 0;
|
|
4104
4339
|
let routeSynthSeq = 0;
|
|
4340
|
+
function decorateRouteTreeWithPluginMeta(pluginId, route) {
|
|
4341
|
+
const meta = route.meta && typeof route.meta === 'object' && !Array.isArray(route.meta)
|
|
4342
|
+
? route.meta
|
|
4343
|
+
: {};
|
|
4344
|
+
const wepMeta = meta.__wep && typeof meta.__wep === 'object' && !Array.isArray(meta.__wep)
|
|
4345
|
+
? meta.__wep
|
|
4346
|
+
: {};
|
|
4347
|
+
const nextChildren = Array.isArray(route.children)
|
|
4348
|
+
? route.children.map((child) => decorateRouteTreeWithPluginMeta(pluginId, child))
|
|
4349
|
+
: route.children;
|
|
4350
|
+
return {
|
|
4351
|
+
...route,
|
|
4352
|
+
meta: {
|
|
4353
|
+
...meta,
|
|
4354
|
+
__wep: {
|
|
4355
|
+
...wepMeta,
|
|
4356
|
+
pluginId
|
|
4357
|
+
}
|
|
4358
|
+
},
|
|
4359
|
+
...(nextChildren ? { children: nextChildren } : {})
|
|
4360
|
+
};
|
|
4361
|
+
}
|
|
4105
4362
|
function analyzeRouteInputTree(nodes) {
|
|
4106
4363
|
let hasDecl = false;
|
|
4107
4364
|
let hasCfg = false;
|
|
@@ -4143,26 +4400,71 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
|
4143
4400
|
const parentName = typeof hostKitOptions.pluginRoutesParentName === 'string'
|
|
4144
4401
|
? hostKitOptions.pluginRoutesParentName.trim()
|
|
4145
4402
|
: '';
|
|
4403
|
+
function rollbackRegisteredTopRoutes(routes) {
|
|
4404
|
+
for (let i = routes.length - 1; i >= 0; i--) {
|
|
4405
|
+
const route = routes[i];
|
|
4406
|
+
if (typeof route.dispose === 'function') {
|
|
4407
|
+
try {
|
|
4408
|
+
route.dispose();
|
|
4409
|
+
continue;
|
|
4410
|
+
}
|
|
4411
|
+
catch (e) {
|
|
4412
|
+
console.warn('[wep] rollback route disposer failed', route.name, e);
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
if (typeof router.removeRoute === 'function') {
|
|
4416
|
+
try {
|
|
4417
|
+
router.removeRoute(route.name);
|
|
4418
|
+
}
|
|
4419
|
+
catch (e) {
|
|
4420
|
+
console.warn('[wep] rollback removeRoute failed', route.name, e);
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
}
|
|
4424
|
+
}
|
|
4146
4425
|
function applyInternalRegister(rawRouteConfigs) {
|
|
4147
4426
|
const wrapped = rawRouteConfigs.map((r) => ({
|
|
4148
|
-
...r,
|
|
4149
|
-
name: r.name || `${routeSynthNamePrefix}${pluginId}_${routeSynthSeq++}
|
|
4150
|
-
meta: { ...(r.meta || {}), pluginId }
|
|
4427
|
+
...decorateRouteTreeWithPluginMeta(pluginId, r),
|
|
4428
|
+
name: r.name || `${routeSynthNamePrefix}${pluginId}_${routeSynthSeq++}`
|
|
4151
4429
|
}));
|
|
4152
4430
|
if (typeof router.addRoute !== 'function') {
|
|
4153
4431
|
throw new Error('[wep] vue-router 3.5+ 必需:请使用 router.addRoute(不再支持 addRoutes)');
|
|
4154
4432
|
}
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4433
|
+
const registeredTopRoutes = [];
|
|
4434
|
+
try {
|
|
4435
|
+
if (parentName) {
|
|
4436
|
+
for (const r of wrapped) {
|
|
4437
|
+
const dispose = router.addRoute(parentName, r);
|
|
4438
|
+
registeredTopRoutes.push({
|
|
4439
|
+
name: String(r.name),
|
|
4440
|
+
dispose: typeof dispose === 'function' ? dispose : undefined
|
|
4441
|
+
});
|
|
4442
|
+
}
|
|
4159
4443
|
}
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4444
|
+
else {
|
|
4445
|
+
for (const r of wrapped) {
|
|
4446
|
+
const dispose = router.addRoute(r);
|
|
4447
|
+
registeredTopRoutes.push({
|
|
4448
|
+
name: String(r.name),
|
|
4449
|
+
dispose: typeof dispose === 'function' ? dispose : undefined
|
|
4450
|
+
});
|
|
4451
|
+
}
|
|
4164
4452
|
}
|
|
4165
4453
|
}
|
|
4454
|
+
catch (e) {
|
|
4455
|
+
rollbackRegisteredTopRoutes(registeredTopRoutes);
|
|
4456
|
+
throw e;
|
|
4457
|
+
}
|
|
4458
|
+
recordPluginTopRoutes(pluginId, registeredTopRoutes);
|
|
4459
|
+
const contributedRoutes = recordContributedRoutesForPlugin(pluginId, wrapped);
|
|
4460
|
+
if (contributedRoutes.length > 0 && typeof hostKitOptions.onPluginRoutesContributed === 'function') {
|
|
4461
|
+
hostKitOptions.onPluginRoutesContributed({
|
|
4462
|
+
pluginId,
|
|
4463
|
+
router,
|
|
4464
|
+
routes: wrapped,
|
|
4465
|
+
contributedRoutes
|
|
4466
|
+
});
|
|
4467
|
+
}
|
|
4166
4468
|
}
|
|
4167
4469
|
function injectStylesheet(href) {
|
|
4168
4470
|
const link = document.createElement('link');
|
|
@@ -4185,6 +4487,12 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
|
4185
4487
|
const hostContext = hostKitOptions.hostContext != null && typeof hostKitOptions.hostContext === 'object'
|
|
4186
4488
|
? hostKitOptions.hostContext
|
|
4187
4489
|
: Object.freeze({});
|
|
4490
|
+
const hostVue = hostContext && hostContext.Vue;
|
|
4491
|
+
const VueRuntime = hostVue || Vue;
|
|
4492
|
+
ensureRegistriesReactive(VueRuntime);
|
|
4493
|
+
registerPluginTeardown(pluginId, () => {
|
|
4494
|
+
clearContributedRoutesForPlugin(pluginId);
|
|
4495
|
+
});
|
|
4188
4496
|
return {
|
|
4189
4497
|
hostPluginApiVersion: HOST_PLUGIN_API_VERSION,
|
|
4190
4498
|
/** 宿主注入的只读依赖(store、router、开放能力等),见 `resolveRuntimeOptions.hostContext` */
|
|
@@ -4237,7 +4545,7 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
|
4237
4545
|
return;
|
|
4238
4546
|
}
|
|
4239
4547
|
if (!registries.slots[pointId]) {
|
|
4240
|
-
|
|
4548
|
+
VueRuntime.set(registries.slots, pointId, []);
|
|
4241
4549
|
}
|
|
4242
4550
|
const list = registries.slots[pointId];
|
|
4243
4551
|
for (const c of components) {
|
|
@@ -4265,6 +4573,7 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
|
4265
4573
|
registerSanitizedHtmlSnippet() {
|
|
4266
4574
|
throw new Error('registerSanitizedHtmlSnippet is not enabled');
|
|
4267
4575
|
},
|
|
4576
|
+
getContributedRoutes: () => getContributedRoutesForPlugin(pluginId),
|
|
4268
4577
|
getBridge: () => bridge,
|
|
4269
4578
|
onTeardown(_pluginId, fn) {
|
|
4270
4579
|
if (typeof fn === 'function') {
|
|
@@ -4274,189 +4583,69 @@ function createHostApi(pluginId, router, hostKitOptions = {}) {
|
|
|
4274
4583
|
};
|
|
4275
4584
|
}
|
|
4276
4585
|
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
Vue.delete(slots, pointId);
|
|
4296
|
-
}
|
|
4297
|
-
else if (next.length !== list.length) {
|
|
4298
|
-
Vue.set(slots, pointId, next);
|
|
4299
|
-
}
|
|
4300
|
-
}
|
|
4301
|
-
registries.slotRevision++;
|
|
4302
|
-
if (typeof window !== 'undefined' && window.__PLUGIN_ACTIVATORS__) {
|
|
4303
|
-
delete window.__PLUGIN_ACTIVATORS__[pluginId];
|
|
4304
|
-
}
|
|
4305
|
-
if (typeof document !== 'undefined') {
|
|
4306
|
-
document.querySelectorAll('[data-plugin-asset]').forEach((el) => {
|
|
4307
|
-
if (el.getAttribute('data-plugin-asset') === pluginId) {
|
|
4308
|
-
el.remove();
|
|
4586
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4587
|
+
function createExtensionPointComponent(VueLike = Vue) {
|
|
4588
|
+
ensureRegistriesReactive(VueLike);
|
|
4589
|
+
const SlotErrorBoundary = VueLike.extend({
|
|
4590
|
+
name: 'SlotErrorBoundary',
|
|
4591
|
+
props: { label: String },
|
|
4592
|
+
data() {
|
|
4593
|
+
return { error: null };
|
|
4594
|
+
},
|
|
4595
|
+
errorCaptured(err) {
|
|
4596
|
+
this.error = err instanceof Error && err.message ? err.message : String(err);
|
|
4597
|
+
console.error('[wep:ExtensionPoint]', this.label, err);
|
|
4598
|
+
return false;
|
|
4599
|
+
},
|
|
4600
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4601
|
+
render(h) {
|
|
4602
|
+
if (this.error) {
|
|
4603
|
+
return h('div', { class: 'plugin-point-error' }, `[插件 ${this.label}] 渲染失败`);
|
|
4309
4604
|
}
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
}
|
|
4313
|
-
|
|
4314
|
-
/**
|
|
4315
|
-
* 从路由配置(含 meta)推导侧栏/目录用菜单描述,与 registerRoutes 入参同源。
|
|
4316
|
-
*
|
|
4317
|
-
* 约定:
|
|
4318
|
-
* - `meta.menu === false`:该节点及其子树不参与菜单
|
|
4319
|
-
* - 节点出现条件:`meta.title` 或 `meta.menuTitle` 非空字符串,或存在非空的子菜单列表
|
|
4320
|
-
* - `meta.order` 数字越小越靠前;缺省为 0
|
|
4321
|
-
* - 可选:`meta.icon`、`meta.permission`、`meta.hidden`、`meta.external`
|
|
4322
|
-
*/
|
|
4323
|
-
function pickTitle(meta, route) {
|
|
4324
|
-
if (typeof meta.menuTitle === 'string' && meta.menuTitle) {
|
|
4325
|
-
return meta.menuTitle;
|
|
4326
|
-
}
|
|
4327
|
-
if (typeof meta.title === 'string' && meta.title) {
|
|
4328
|
-
return meta.title;
|
|
4329
|
-
}
|
|
4330
|
-
if (route.name != null && String(route.name)) {
|
|
4331
|
-
return String(route.name);
|
|
4332
|
-
}
|
|
4333
|
-
return String(route.path || '');
|
|
4334
|
-
}
|
|
4335
|
-
function sortDescriptors(items) {
|
|
4336
|
-
items.sort((a, b) => a.order - b.order || a.path.localeCompare(b.path));
|
|
4337
|
-
for (const x of items) {
|
|
4338
|
-
if (x.children && x.children.length) {
|
|
4339
|
-
sortDescriptors(x.children);
|
|
4340
|
-
}
|
|
4341
|
-
}
|
|
4342
|
-
}
|
|
4343
|
-
/**
|
|
4344
|
-
* 从插件侧 RouteConfig 树构建菜单描述(不含 component,便于并入宿主菜单 state)。
|
|
4345
|
-
*/
|
|
4346
|
-
function buildMenuDescriptorsFromRoutes(routes, pluginId) {
|
|
4347
|
-
const out = [];
|
|
4348
|
-
function walk(route) {
|
|
4349
|
-
const meta = (route.meta || {});
|
|
4350
|
-
if (meta.menu === false) {
|
|
4351
|
-
return null;
|
|
4352
|
-
}
|
|
4353
|
-
const chIn = Array.isArray(route.children) ? route.children : [];
|
|
4354
|
-
const childMenus = chIn.map(walk).filter(Boolean);
|
|
4355
|
-
const hasTitle = typeof meta.title === 'string' || typeof meta.menuTitle === 'string';
|
|
4356
|
-
if (!hasTitle && childMenus.length === 0) {
|
|
4357
|
-
return null;
|
|
4358
|
-
}
|
|
4359
|
-
const item = {
|
|
4360
|
-
path: String(route.path || ''),
|
|
4361
|
-
name: route.name,
|
|
4362
|
-
title: pickTitle(meta, route),
|
|
4363
|
-
order: meta.order != null ? Number(meta.order) : 0,
|
|
4364
|
-
...(pluginId ? { pluginId } : {}),
|
|
4365
|
-
...(meta.icon !== undefined ? { icon: meta.icon } : {}),
|
|
4366
|
-
...(meta.permission !== undefined ? { permission: meta.permission } : {}),
|
|
4367
|
-
...(meta.hidden === true ? { hidden: true } : {}),
|
|
4368
|
-
...(meta.external === true ? { external: true } : {}),
|
|
4369
|
-
...(childMenus.length ? { children: childMenus } : {})
|
|
4370
|
-
};
|
|
4371
|
-
return item;
|
|
4372
|
-
}
|
|
4373
|
-
for (const r of routes) {
|
|
4374
|
-
const m = walk(r);
|
|
4375
|
-
if (m) {
|
|
4376
|
-
out.push(m);
|
|
4605
|
+
const d = this.$slots.default;
|
|
4606
|
+
return d && d[0] ? d[0] : h('span');
|
|
4377
4607
|
}
|
|
4378
|
-
}
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
}
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
* 样式由宿主全局 CSS 覆盖 `.extension-point` / `.plugin-point-error`。
|
|
4386
|
-
*/
|
|
4387
|
-
const SlotErrorBoundary = Vue.extend({
|
|
4388
|
-
name: 'SlotErrorBoundary',
|
|
4389
|
-
props: { label: String },
|
|
4390
|
-
data() {
|
|
4391
|
-
return { error: null };
|
|
4392
|
-
},
|
|
4393
|
-
errorCaptured(err) {
|
|
4394
|
-
this.error = err instanceof Error && err.message ? err.message : String(err);
|
|
4395
|
-
console.error('[wep:ExtensionPoint]', this.label, err);
|
|
4396
|
-
return false;
|
|
4397
|
-
},
|
|
4398
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4399
|
-
render(h) {
|
|
4400
|
-
if (this.error) {
|
|
4401
|
-
return h('div', { class: 'plugin-point-error' }, `[插件 ${this.label}] 渲染失败`);
|
|
4402
|
-
}
|
|
4403
|
-
const d = this.$slots.default;
|
|
4404
|
-
return d && d[0] ? d[0] : h('span');
|
|
4405
|
-
}
|
|
4406
|
-
});
|
|
4407
|
-
var ExtensionPoint = Vue.extend({
|
|
4408
|
-
name: 'ExtensionPoint',
|
|
4409
|
-
components: { SlotErrorBoundary },
|
|
4410
|
-
props: {
|
|
4411
|
-
pointId: { type: String, required: true },
|
|
4412
|
-
slotProps: { type: Object, default: () => ({}) }
|
|
4413
|
-
},
|
|
4414
|
-
computed: {
|
|
4415
|
-
items() {
|
|
4416
|
-
void registries.slotRevision;
|
|
4417
|
-
return registries.slots[this.pointId] || [];
|
|
4608
|
+
});
|
|
4609
|
+
return VueLike.extend({
|
|
4610
|
+
name: 'ExtensionPoint',
|
|
4611
|
+
components: { SlotErrorBoundary },
|
|
4612
|
+
props: {
|
|
4613
|
+
pointId: { type: String, required: true },
|
|
4614
|
+
slotProps: { type: Object, default: () => ({}) }
|
|
4418
4615
|
},
|
|
4419
|
-
|
|
4420
|
-
|
|
4616
|
+
computed: {
|
|
4617
|
+
items() {
|
|
4618
|
+
void registries.slotRevision;
|
|
4619
|
+
return registries.slots[this.pointId] || [];
|
|
4620
|
+
},
|
|
4621
|
+
forwardProps() {
|
|
4622
|
+
return this.slotProps || {};
|
|
4623
|
+
}
|
|
4624
|
+
},
|
|
4625
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4626
|
+
render(h) {
|
|
4627
|
+
return h('div', {
|
|
4628
|
+
class: 'extension-point',
|
|
4629
|
+
attrs: { 'data-point-id': this.pointId }
|
|
4630
|
+
}, this.items.map((item) => h(SlotErrorBoundary, {
|
|
4631
|
+
key: item.key,
|
|
4632
|
+
props: { label: item.pluginId }
|
|
4633
|
+
}, [h(item.component, { props: this.forwardProps })])));
|
|
4421
4634
|
}
|
|
4422
|
-
}
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
return h('div', {
|
|
4426
|
-
class: 'extension-point',
|
|
4427
|
-
attrs: { 'data-point-id': this.pointId }
|
|
4428
|
-
}, this.items.map((item) => h(SlotErrorBoundary, {
|
|
4429
|
-
key: item.key,
|
|
4430
|
-
props: { label: item.pluginId }
|
|
4431
|
-
}, [h(item.component, { props: this.forwardProps })])));
|
|
4432
|
-
}
|
|
4433
|
-
});
|
|
4635
|
+
});
|
|
4636
|
+
}
|
|
4637
|
+
var ExtensionPoint = createExtensionPointComponent();
|
|
4434
4638
|
|
|
4435
|
-
/**
|
|
4436
|
-
* 注册全局 `ExtensionPoint` 并异步拉取清单、激活插件。
|
|
4437
|
-
*/
|
|
4438
|
-
/**
|
|
4439
|
-
* @param Vue Vue 构造函数
|
|
4440
|
-
* @param router vue-router 实例
|
|
4441
|
-
* @param options 传给 `resolveRuntimeOptions`;可含 `env` 以读取 `VITE_*`
|
|
4442
|
-
*/
|
|
4443
4639
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4444
4640
|
function installWebExtendPluginVue2(Vue, router, options) {
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
setWebExtendPluginEnv(injectedEnv);
|
|
4641
|
+
if (Vue) {
|
|
4642
|
+
ensureRegistriesReactive(Vue);
|
|
4643
|
+
Vue.component('ExtensionPoint', createExtensionPointComponent(Vue));
|
|
4449
4644
|
}
|
|
4450
|
-
|
|
4451
|
-
Vue.component('ExtensionPoint', ExtensionPoint);
|
|
4452
|
-
}
|
|
4453
|
-
const runtime = resolveRuntimeOptions$1(runtimeUser);
|
|
4645
|
+
const runtime = resolveRuntimeOptions$1(options || {});
|
|
4454
4646
|
return bootstrapPlugins$1(router, (id, r, kit) => createHostApi(id, r, kit || {}), runtime);
|
|
4455
4647
|
}
|
|
4456
4648
|
|
|
4457
|
-
/**
|
|
4458
|
-
* Vue CLI + 统一 axios(如 RuoYi `utils/request`)场景的 `install` 预设。
|
|
4459
|
-
*/
|
|
4460
4649
|
function resolveManifestPathUnderApiBase(manifestUrl, apiBase) {
|
|
4461
4650
|
const base = String(apiBase !== undefined
|
|
4462
4651
|
? apiBase
|
|
@@ -4466,18 +4655,13 @@ function resolveManifestPathUnderApiBase(manifestUrl, apiBase) {
|
|
|
4466
4655
|
if (typeof window === 'undefined') {
|
|
4467
4656
|
return '/api/frontend-plugins';
|
|
4468
4657
|
}
|
|
4469
|
-
const
|
|
4470
|
-
let path =
|
|
4658
|
+
const url = new URL(manifestUrl, window.location.origin);
|
|
4659
|
+
let path = url.pathname + url.search;
|
|
4471
4660
|
if (base && path.startsWith(base)) {
|
|
4472
4661
|
path = path.slice(base.length) || '/';
|
|
4473
4662
|
}
|
|
4474
4663
|
return path;
|
|
4475
4664
|
}
|
|
4476
|
-
/**
|
|
4477
|
-
* 常见 Java 清单路径:与 `VUE_APP_BASE_API` 拼接为 `${base}/frontend-plugins`。
|
|
4478
|
-
* 若后端使用 `/api/frontend-plugins` 段,请在 extra 中显式传入 `manifestListPath`。
|
|
4479
|
-
*/
|
|
4480
|
-
const defaultVueCliJavaManifestListPath = '/frontend-plugins';
|
|
4481
4665
|
function bridgePrefixesFromVueCliEnv() {
|
|
4482
4666
|
const base = (typeof process !== 'undefined' && process.env && process.env.VUE_APP_BASE_API
|
|
4483
4667
|
? String(process.env.VUE_APP_BASE_API)
|
|
@@ -4498,13 +4682,10 @@ function createVueCliAxiosInstallOptions(deps, extra = {}) {
|
|
|
4498
4682
|
? String(extra.manifestBase).replace(/\/$/, '')
|
|
4499
4683
|
: '';
|
|
4500
4684
|
const stripBase = userBase || envBase;
|
|
4501
|
-
const manifestMode = resolveManifestModeFromInputs(extraManifestMode);
|
|
4502
|
-
const staticManifestUrl = resolveStaticManifestUrlFromInputs(extraStaticManifestUrl);
|
|
4503
4685
|
const fetchManifestApi = async (ctx) => {
|
|
4504
4686
|
try {
|
|
4505
|
-
const url = resolveManifestPathUnderApiBase(ctx.manifestUrl, stripBase);
|
|
4506
4687
|
const body = await request({
|
|
4507
|
-
url,
|
|
4688
|
+
url: resolveManifestPathUnderApiBase(ctx.manifestUrl, stripBase),
|
|
4508
4689
|
method: 'get'
|
|
4509
4690
|
});
|
|
4510
4691
|
const data = unwrapNestedManifestBody(body);
|
|
@@ -4517,17 +4698,18 @@ function createVueCliAxiosInstallOptions(deps, extra = {}) {
|
|
|
4517
4698
|
}
|
|
4518
4699
|
return { ok: true, data };
|
|
4519
4700
|
}
|
|
4520
|
-
catch (
|
|
4521
|
-
return { ok: false, error
|
|
4701
|
+
catch (error) {
|
|
4702
|
+
return { ok: false, error, data: null };
|
|
4522
4703
|
}
|
|
4523
4704
|
};
|
|
4524
|
-
const
|
|
4705
|
+
const manifestMode = resolveManifestModeFromInputs(extraManifestMode);
|
|
4706
|
+
const staticManifestUrl = resolveStaticManifestUrlFromInputs(extraStaticManifestUrl);
|
|
4525
4707
|
const fetchManifest = typeof userFetchManifest === 'function'
|
|
4526
4708
|
? userFetchManifest
|
|
4527
4709
|
: manifestMode === 'static'
|
|
4528
|
-
?
|
|
4710
|
+
? fetchStaticManifestViaHttp
|
|
4529
4711
|
: fetchManifestApi;
|
|
4530
|
-
const
|
|
4712
|
+
const options = {
|
|
4531
4713
|
manifestBase: stripBase || undefined,
|
|
4532
4714
|
bridgeAllowedPathPrefixes: bridgePrefixesFromVueCliEnv(),
|
|
4533
4715
|
manifestMode,
|
|
@@ -4538,106 +4720,14 @@ function createVueCliAxiosInstallOptions(deps, extra = {}) {
|
|
|
4538
4720
|
const listPath = typeof process !== 'undefined' &&
|
|
4539
4721
|
process.env &&
|
|
4540
4722
|
process.env[webExtendPluginEnvKeys.manifestPathAlt];
|
|
4541
|
-
if (listPath &&
|
|
4542
|
-
|
|
4723
|
+
if (listPath && options.manifestListPath === undefined && extra.manifestListPath === undefined) {
|
|
4724
|
+
options.manifestListPath = String(process.env[webExtendPluginEnvKeys.manifestPathAlt]);
|
|
4543
4725
|
}
|
|
4544
|
-
return
|
|
4545
|
-
}
|
|
4546
|
-
/**
|
|
4547
|
-
* 少样板接入:`hostContext` 自动含 `router`(及可选 `store`)、`isDev` 与常见 `manifestListPath` 默认值。
|
|
4548
|
-
* 更多运行时字段仍可通过 `extra` 覆盖。
|
|
4549
|
-
*/
|
|
4550
|
-
function createVueCliAxiosQuickInstallOptions(
|
|
4551
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4552
|
-
router, deps, extra = {}) {
|
|
4553
|
-
const { request, hostLayoutComponent, store, hostContext: depHostContext } = deps;
|
|
4554
|
-
const { hostContext: extraHostContext, isDev: extraIsDev, manifestListPath: extraListPath, ...restExtra } = extra;
|
|
4555
|
-
const hostContext = {
|
|
4556
|
-
router,
|
|
4557
|
-
...(store !== undefined ? { store } : {}),
|
|
4558
|
-
...(depHostContext && typeof depHostContext === 'object' ? depHostContext : {}),
|
|
4559
|
-
...(extraHostContext && typeof extraHostContext === 'object' ? extraHostContext : {})
|
|
4560
|
-
};
|
|
4561
|
-
const manifestListPath = extraListPath !== undefined && String(extraListPath).trim() !== ''
|
|
4562
|
-
? String(extraListPath)
|
|
4563
|
-
: defaultVueCliJavaManifestListPath;
|
|
4564
|
-
return createVueCliAxiosInstallOptions({ request }, {
|
|
4565
|
-
hostLayoutComponent,
|
|
4566
|
-
hostContext,
|
|
4567
|
-
isDev: extraIsDev !== undefined ? Boolean(extraIsDev) : resolveBundledIsDev(),
|
|
4568
|
-
manifestListPath,
|
|
4569
|
-
...restExtra
|
|
4570
|
-
});
|
|
4726
|
+
return options;
|
|
4571
4727
|
}
|
|
4572
|
-
const presetVueCliAxios = Object.freeze({
|
|
4573
|
-
id: 'vue-cli-axios',
|
|
4574
|
-
description: 'Vue CLI + axios request for API manifest; optional manifestMode=static uses fetch',
|
|
4575
|
-
createInstallOptions: createVueCliAxiosInstallOptions,
|
|
4576
|
-
createQuickInstallOptions: createVueCliAxiosQuickInstallOptions,
|
|
4577
|
-
defaultJavaManifestListPath: defaultVueCliJavaManifestListPath,
|
|
4578
|
-
manifestPathForApiBase: resolveManifestPathUnderApiBase,
|
|
4579
|
-
unwrapManifestBody: unwrapNestedManifestBody
|
|
4580
|
-
});
|
|
4581
4728
|
|
|
4582
|
-
|
|
4583
|
-
* Vue CLI + axios 场景的一键安装:合并 hostContext、默认清单路径与 IIFE 全局 Vue。
|
|
4584
|
-
*/
|
|
4585
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4586
|
-
function installVueCliAxiosWebPlugins(Vue, router, deps, extra) {
|
|
4587
|
-
/** 避免输出 `?.`:Vue CLI 4 / Webpack 4 默认不转译 node_modules */
|
|
4588
|
-
const exposeGlobalVue = extra == null || extra.exposeGlobalVue !== false;
|
|
4589
|
-
if (exposeGlobalVue && typeof window !== 'undefined') {
|
|
4590
|
-
window.Vue = Vue;
|
|
4591
|
-
}
|
|
4592
|
-
const { exposeGlobalVue: _skip, ...restExtra } = extra || {};
|
|
4593
|
-
const opts = createVueCliAxiosQuickInstallOptions(router, deps, restExtra);
|
|
4594
|
-
return installWebExtendPluginVue2(Vue, router, opts);
|
|
4595
|
-
}
|
|
4596
|
-
|
|
4597
|
-
/**
|
|
4598
|
-
* 包对外稳定导出与 `WebExtendPluginVue2` 命名空间。
|
|
4599
|
-
*/
|
|
4600
|
-
const { bootstrapPlugins, defaultFetchWebPluginManifest, resolveRuntimeOptions, ensurePluginHostRoute } = pluginRuntime;
|
|
4729
|
+
const { bootstrapPlugins, defaultFetchWebPluginManifest, resolveRuntimeOptions, ensurePluginHostRoute, getActivatedPluginIds } = pluginRuntime;
|
|
4601
4730
|
const { composeManifestFetch, manifestFetchCacheMiddleware, wrapManifestFetchWithCache } = manifestComposer;
|
|
4602
|
-
const WebExtendPluginVue2 = Object.freeze({
|
|
4603
|
-
install: installWebExtendPluginVue2,
|
|
4604
|
-
runtime: Object.freeze({
|
|
4605
|
-
bootstrapPlugins: bootstrapPlugins$1,
|
|
4606
|
-
resolveRuntimeOptions: resolveRuntimeOptions$1,
|
|
4607
|
-
ensurePluginHostRoute: ensurePluginHostRoute$1,
|
|
4608
|
-
defaultFetchWebPluginManifest: defaultFetchWebPluginManifest$1,
|
|
4609
|
-
composeManifestFetch: composeManifestFetch$1,
|
|
4610
|
-
manifestFetchCacheMiddleware: manifestFetchCacheMiddleware$1,
|
|
4611
|
-
wrapManifestFetchWithCache: wrapManifestFetchWithCache$1
|
|
4612
|
-
}),
|
|
4613
|
-
host: Object.freeze({
|
|
4614
|
-
createHostApi,
|
|
4615
|
-
disposeWebPlugin,
|
|
4616
|
-
createRequestBridge,
|
|
4617
|
-
registries,
|
|
4618
|
-
buildMenuDescriptorsFromRoutes,
|
|
4619
|
-
getRegisteredTopRouteNamesForPlugin
|
|
4620
|
-
}),
|
|
4621
|
-
config: Object.freeze({
|
|
4622
|
-
defaultWebExtendPluginRuntime,
|
|
4623
|
-
setWebExtendPluginEnv,
|
|
4624
|
-
webExtendPluginEnvKeys,
|
|
4625
|
-
defaultManifestFetchCache,
|
|
4626
|
-
defaultManifestMode,
|
|
4627
|
-
routeSynthNamePrefix,
|
|
4628
|
-
peerMinimumVersions
|
|
4629
|
-
}),
|
|
4630
|
-
constants: Object.freeze({
|
|
4631
|
-
HOST_PLUGIN_API_VERSION,
|
|
4632
|
-
RUNTIME_CONSOLE_LABEL
|
|
4633
|
-
}),
|
|
4634
|
-
components: Object.freeze({
|
|
4635
|
-
ExtensionPoint
|
|
4636
|
-
}),
|
|
4637
|
-
presets: Object.freeze({
|
|
4638
|
-
vueCliAxios: presetVueCliAxios
|
|
4639
|
-
})
|
|
4640
|
-
});
|
|
4641
4731
|
|
|
4642
|
-
export { ExtensionPoint, HOST_PLUGIN_API_VERSION, RUNTIME_CONSOLE_LABEL,
|
|
4732
|
+
export { ExtensionPoint, HOST_PLUGIN_API_VERSION, RUNTIME_CONSOLE_LABEL, bootstrapPlugins, composeManifestFetch, createHostApi, createRequestBridge, createVueCliAxiosInstallOptions, defaultFetchWebPluginManifest, defaultManifestFetchCache, defaultManifestMode, defaultWebExtendPluginRuntime, disposeWebPlugin, ensurePluginHostRoute, getActivatedPluginIds, getContributedRoutesForPlugin, getRegisteredTopRouteNamesForPlugin, installWebExtendPluginVue2, manifestFetchCacheMiddleware, peerMinimumVersions, registries, resolveRuntimeOptions, routeSynthNamePrefix, setWebExtendPluginEnv, webExtendPluginEnvKeys, wrapManifestFetchWithCache };
|
|
4643
4733
|
//# sourceMappingURL=index.mjs.map
|