92 lines
2.5 KiB
TypeScript
92 lines
2.5 KiB
TypeScript
/**
|
|
* metona-sqlark Plugin — 插件系统
|
|
* @module plugin
|
|
*
|
|
* 管理插件的注册、生命周期和钩子调度。
|
|
*/
|
|
|
|
import type { MetonaPlugin, HookName } from '../constants';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Hook 回调类型
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export type HookCallback = (...args: unknown[]) => void | Promise<void>;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// PluginManager
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export class PluginManager {
|
|
private plugins: MetonaPlugin[] = [];
|
|
private hooks: Map<HookName, HookCallback[]> = new Map();
|
|
|
|
/** 注册插件 */
|
|
register(plugin: MetonaPlugin, db?: unknown): void {
|
|
// 按优先级插入
|
|
const priority = plugin.priority ?? 0;
|
|
const insertIndex = this.plugins.findIndex(
|
|
(p) => (p.priority ?? 0) < priority,
|
|
);
|
|
if (insertIndex === -1) {
|
|
this.plugins.push(plugin);
|
|
} else {
|
|
this.plugins.splice(insertIndex, 0, plugin);
|
|
}
|
|
// 安装(传入 db 实例)
|
|
plugin.install(db);
|
|
}
|
|
|
|
/** 卸载插件 */
|
|
unregister(pluginName: string): void {
|
|
const idx = this.plugins.findIndex((p) => p.name === pluginName);
|
|
if (idx !== -1) {
|
|
this.plugins[idx].destroy();
|
|
this.plugins.splice(idx, 1);
|
|
}
|
|
}
|
|
|
|
/** 获取所有已注册插件 */
|
|
getPlugins(): MetonaPlugin[] {
|
|
return [...this.plugins];
|
|
}
|
|
|
|
/** 添加钩子回调 */
|
|
on(hook: HookName, callback: HookCallback): void {
|
|
const callbacks = this.hooks.get(hook) ?? [];
|
|
callbacks.push(callback);
|
|
this.hooks.set(hook, callbacks);
|
|
}
|
|
|
|
/** 移除钩子回调 */
|
|
off(hook: HookName, callback: HookCallback): void {
|
|
const callbacks = this.hooks.get(hook);
|
|
if (callbacks) {
|
|
const idx = callbacks.indexOf(callback);
|
|
if (idx !== -1) callbacks.splice(idx, 1);
|
|
}
|
|
}
|
|
|
|
/** 触发钩子 */
|
|
async trigger(hook: HookName, ...args: unknown[]): Promise<void> {
|
|
const callbacks = this.hooks.get(hook);
|
|
if (callbacks) {
|
|
for (const cb of callbacks) {
|
|
await cb(...args);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** 销毁所有插件 */
|
|
destroy(): void {
|
|
for (const plugin of this.plugins) {
|
|
try { plugin.destroy(); } catch (e) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn(`[metona-sqlark] Plugin "${plugin.name}" destroy error:`, e);
|
|
}
|
|
}
|
|
this.plugins = [];
|
|
this.hooks.clear();
|
|
}
|
|
}
|