feat: 插件性能护栏 + 公共组件库——激活/装载限时兜底、renderer 装载三态呈现、@aso/plugin-sdk/ui、withTimeout/withRetry

This commit is contained in:
Rick Ye
2026-09-11 09:56:26 +08:00
parent 4ed31a34fc
commit f77942ee2b
7 changed files with 473 additions and 12 deletions
+20 -4
View File
@@ -34,6 +34,7 @@ import type {
StatusItem,
ToolbarButtonDef
} from '@aso/plugin-sdk'
import { withTimeout } from '@aso/plugin-sdk'
import type { PluginInfo, PluginStatusEntry, PluginToolbarEntry, PluginsUiState } from '@shared/types'
import { BUILTIN_PLUGINS, type BuiltinPlugin } from './builtin'
import { getLogger } from '../logger'
@@ -44,6 +45,9 @@ const pluginLog = getLogger('plugin')
/** harness 插件契约版本(manifest.minContract 与之比对;破坏性变更时提升) */
export const HARNESS_CONTRACT_VERSION = '0.2.0'
/** 插件激活限时(异步工厂的兜底超时):挂死的插件不得阻塞 runtime 重建主流程 */
export const PLUGIN_ACTIVATION_TIMEOUT_MS = 10_000
/** 外置插件装载开关(build 级):开发版开启、用户版关闭;ASO_EXTERNAL_PLUGINS=1 强制开启 */
export const EXTERNAL_PLUGINS_ENABLED = !app.isPackaged || process.env.ASO_EXTERNAL_PLUGINS === '1'
@@ -597,13 +601,25 @@ class PluginHost {
this.resetSettingsCallbacks(plugin.manifest.id)
try {
const result = factory(this.buildApi(plugin, pi))
log.info('plugin activated', { pluginId: plugin.manifest.id, source: plugin.source })
const pluginId = plugin.manifest.id
if (typeof result === 'function') {
disposers.push(result)
log.info('plugin activated', { pluginId, source: plugin.source })
} else if (result && typeof (result as Promise<unknown>).then === 'function') {
void (result as Promise<void | (() => void)>).then((d) => {
if (typeof d === 'function') disposers.push(d)
})
// 异步工厂:限时兜底(挂死不拖垮 runtime 重建),reject 必须接住(不得成为 unhandled rejection)
void withTimeout(result as Promise<void | (() => void)>, PLUGIN_ACTIVATION_TIMEOUT_MS, `插件 ${pluginId} 激活`)
.then((d) => {
if (typeof d === 'function') disposers.push(d)
log.info('plugin activated', { pluginId, source: plugin.source, async: true })
})
.catch((err: unknown) => {
plugin.error = `激活失败: ${err instanceof Error ? err.message : String(err)}`
log.error('plugin activation failed', { pluginId, err })
this.clearPluginUi(pluginId)
this.send('plugins:listChanged', null)
})
} else {
log.info('plugin activated', { pluginId, source: plugin.source })
}
} catch (err) {
plugin.error = `激活失败: ${err instanceof Error ? err.message : String(err)}`