@yumerijs/core 1.1.0 → 1.1.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/dist/core.d.ts +61 -44
- package/dist/core.js +298 -324
- package/package.json +1 -1
package/dist/core.d.ts
CHANGED
|
@@ -18,6 +18,14 @@ interface PluginLoader {
|
|
|
18
18
|
installPluginDependencies(pluginName: string): Promise<void>;
|
|
19
19
|
logger: Logger;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* 定义插件状态枚举
|
|
23
|
+
*/
|
|
24
|
+
export declare const enum PluginStatus {
|
|
25
|
+
ENABLED = "enabled",// 正常启用
|
|
26
|
+
DISABLED = "disabled",// 已禁用
|
|
27
|
+
PENDING = "pending"
|
|
28
|
+
}
|
|
21
29
|
export declare class Core {
|
|
22
30
|
plugins: {
|
|
23
31
|
[name: string]: Plugin & {
|
|
@@ -45,116 +53,125 @@ export declare class Core {
|
|
|
45
53
|
evttoplu: Record<string, Record<string, ((...args: any[]) => Promise<void>)[]>>;
|
|
46
54
|
mdwtoplu: Record<string, string>;
|
|
47
55
|
plftoplu: Record<string, string>;
|
|
48
|
-
|
|
49
|
-
* 创建Core实例
|
|
50
|
-
* @param pluginLoader 插件加载器
|
|
51
|
-
*/
|
|
56
|
+
pluginStatus: Record<string, PluginStatus>;
|
|
52
57
|
constructor(pluginLoader: PluginLoader);
|
|
53
58
|
/**
|
|
54
59
|
* 加载配置文件
|
|
55
|
-
* @param configPath
|
|
60
|
+
* @param configPath 配置文件的路径
|
|
56
61
|
*/
|
|
57
62
|
loadConfig(configPath: string): Promise<void>;
|
|
58
63
|
/**
|
|
59
|
-
*
|
|
60
|
-
* @param configPath
|
|
64
|
+
* 监听配置文件变化,实现热重载
|
|
65
|
+
* @param configPath 配置文件的路径
|
|
61
66
|
*/
|
|
62
67
|
private watchConfig;
|
|
63
68
|
/**
|
|
64
|
-
*
|
|
69
|
+
* 获取指定插件的配置
|
|
65
70
|
* @param pluginName 插件名称
|
|
66
|
-
* @returns
|
|
71
|
+
* @returns 插件的配置对象
|
|
67
72
|
*/
|
|
68
73
|
getPluginConfig(pluginName: string): Promise<Config>;
|
|
69
74
|
/**
|
|
70
|
-
*
|
|
71
|
-
* @returns Promise<void>
|
|
75
|
+
* 加载所有在配置文件中启用的插件
|
|
72
76
|
*/
|
|
73
77
|
loadPlugins(): Promise<void>;
|
|
74
78
|
/**
|
|
75
|
-
*
|
|
76
|
-
* @param
|
|
77
|
-
* @param
|
|
79
|
+
* 导出的函数:加载单个插件
|
|
80
|
+
* @param pluginName 要加载的插件名
|
|
81
|
+
* @param triggerPendingCheck 是否在加载后触发对其他待定插件的检查,默认为 true
|
|
82
|
+
* @returns Promise<boolean> 是否加载成功
|
|
78
83
|
*/
|
|
79
|
-
|
|
84
|
+
loadSinglePlugin(pluginName: string, triggerPendingCheck?: boolean): Promise<boolean>;
|
|
85
|
+
/**
|
|
86
|
+
* 内部函数:尝试加载所有处于 PENDING 状态的插件
|
|
87
|
+
*/
|
|
88
|
+
private _loadPendingPlugins;
|
|
89
|
+
/**
|
|
90
|
+
* 卸载插件,并递归卸载依赖于它的其他插件
|
|
91
|
+
* @param pluginNameToUnload 要卸载的插件名
|
|
92
|
+
*/
|
|
93
|
+
unloadPlugin(pluginNameToUnload: string): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* 内部函数:执行单个插件的卸载逻辑
|
|
96
|
+
* @param pluginName 插件名
|
|
97
|
+
*/
|
|
98
|
+
private _unloadSinglePlugin;
|
|
80
99
|
/**
|
|
81
100
|
* 重新加载插件
|
|
82
101
|
* @param pluginName 插件名称
|
|
83
|
-
* @param core Core实例
|
|
84
102
|
*/
|
|
85
|
-
reloadPlugin(pluginName: string
|
|
103
|
+
reloadPlugin(pluginName: string): Promise<void>;
|
|
86
104
|
/**
|
|
87
|
-
*
|
|
88
|
-
* @param
|
|
89
|
-
* @param
|
|
105
|
+
* 监听插件文件变化,实现热更新
|
|
106
|
+
* @param core Core 实例
|
|
107
|
+
* @param pluginsDir 插件目录,默认为 'plugins'
|
|
90
108
|
*/
|
|
91
|
-
|
|
109
|
+
watchPlugins(core: Core, pluginsDir?: string): void;
|
|
92
110
|
/**
|
|
93
111
|
* 注册组件
|
|
94
112
|
* @param name 组件名称
|
|
95
|
-
* @param component
|
|
96
|
-
* @returns void
|
|
113
|
+
* @param component 组件实例
|
|
97
114
|
*/
|
|
98
115
|
registerComponent(name: string, component: any): void;
|
|
99
116
|
/**
|
|
100
117
|
* 获取组件
|
|
101
118
|
* @param name 组件名称
|
|
102
|
-
* @returns
|
|
119
|
+
* @returns 组件实例
|
|
103
120
|
*/
|
|
104
121
|
getComponent(name: string): any;
|
|
105
122
|
/**
|
|
106
|
-
*
|
|
123
|
+
* 注销组件
|
|
107
124
|
* @param name 组件名称
|
|
108
125
|
*/
|
|
109
126
|
unregisterComponent(name: string): void;
|
|
110
127
|
/**
|
|
111
|
-
*
|
|
128
|
+
* 注册事件监听器
|
|
112
129
|
* @param event 事件名称
|
|
113
|
-
* @param listener
|
|
130
|
+
* @param listener 事件回调函数
|
|
114
131
|
*/
|
|
115
132
|
on(event: string, listener: (...args: any[]) => Promise<void>): void;
|
|
116
133
|
/**
|
|
117
134
|
* 触发事件
|
|
118
135
|
* @param event 事件名称
|
|
119
|
-
* @param args
|
|
136
|
+
* @param args 传递给事件监听器的参数
|
|
120
137
|
*/
|
|
121
138
|
emit(event: string, ...args: any[]): Promise<void>;
|
|
122
139
|
/**
|
|
123
140
|
* 注册全局中间件
|
|
124
141
|
* @param name 中间件名称
|
|
125
|
-
* @param middleware
|
|
126
|
-
* @returns Core
|
|
142
|
+
* @param middleware 中间件函数
|
|
143
|
+
* @returns Core 实例
|
|
127
144
|
*/
|
|
128
145
|
use(name: string, middleware: Middleware): Core;
|
|
129
146
|
/**
|
|
130
|
-
*
|
|
131
|
-
* @param name
|
|
132
|
-
* @returns
|
|
147
|
+
* 注册命令
|
|
148
|
+
* @param name 命令名称
|
|
149
|
+
* @returns Command 实例
|
|
133
150
|
*/
|
|
134
151
|
command(name: string): Command;
|
|
135
152
|
/**
|
|
136
|
-
*
|
|
137
|
-
* @param name
|
|
138
|
-
* @param session
|
|
139
|
-
* @param args
|
|
140
|
-
* @returns
|
|
153
|
+
* 执行命令
|
|
154
|
+
* @param name 命令名称
|
|
155
|
+
* @param session 会话对象
|
|
156
|
+
* @param args 传递给命令处理函数的参数
|
|
157
|
+
* @returns 会话对象或 null
|
|
141
158
|
*/
|
|
142
159
|
executeCommand(name: string, session: any, ...args: any[]): Promise<Session | null>;
|
|
143
160
|
/**
|
|
144
161
|
* 注册平台
|
|
145
|
-
* @param platform
|
|
146
|
-
* @returns
|
|
162
|
+
* @param platform 平台实例
|
|
163
|
+
* @returns 平台启动结果
|
|
147
164
|
*/
|
|
148
165
|
registerPlatform(platform: Platform): any;
|
|
149
166
|
/**
|
|
150
|
-
*
|
|
167
|
+
* 清理指定插件注册的所有内容
|
|
151
168
|
* @param pluginname 插件名称
|
|
152
169
|
*/
|
|
153
170
|
unregall(pluginname: string): void;
|
|
154
171
|
/**
|
|
155
|
-
*
|
|
156
|
-
* @param name
|
|
157
|
-
* @returns null
|
|
172
|
+
* 获取命令
|
|
173
|
+
* @param name 命令名称
|
|
174
|
+
* @returns Command 实例或 null
|
|
158
175
|
*/
|
|
159
176
|
getCommand(name: string): Command | null;
|
|
160
177
|
}
|
package/dist/core.js
CHANGED
|
@@ -38,8 +38,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.Core = void 0;
|
|
40
40
|
/**
|
|
41
|
-
* @time: 2025/
|
|
42
|
-
* @author: FireGuo
|
|
41
|
+
* @time: 2025/07/12 18:14
|
|
42
|
+
* @author: FireGuo & Manus
|
|
43
43
|
* WindyPear-Team All right reserved
|
|
44
44
|
**/
|
|
45
45
|
const yaml = __importStar(require("js-yaml"));
|
|
@@ -61,43 +61,39 @@ class Core {
|
|
|
61
61
|
logger = new logger_1.Logger('core');
|
|
62
62
|
providedComponents = {};
|
|
63
63
|
pluginModules = {};
|
|
64
|
-
configPath = '';
|
|
65
|
-
globalMiddlewares = {};
|
|
66
|
-
cmdtoplu = {};
|
|
67
|
-
comtoplu = {};
|
|
68
|
-
evttoplu = {};
|
|
69
|
-
mdwtoplu = {};
|
|
70
|
-
plftoplu = {};
|
|
71
|
-
|
|
72
|
-
* 创建Core实例
|
|
73
|
-
* @param pluginLoader 插件加载器
|
|
74
|
-
*/
|
|
64
|
+
configPath = '';
|
|
65
|
+
globalMiddlewares = {};
|
|
66
|
+
cmdtoplu = {};
|
|
67
|
+
comtoplu = {};
|
|
68
|
+
evttoplu = {};
|
|
69
|
+
mdwtoplu = {};
|
|
70
|
+
plftoplu = {};
|
|
71
|
+
pluginStatus = {};
|
|
75
72
|
constructor(pluginLoader) {
|
|
76
73
|
this.pluginLoader = pluginLoader;
|
|
77
74
|
}
|
|
78
75
|
/**
|
|
79
76
|
* 加载配置文件
|
|
80
|
-
* @param configPath
|
|
77
|
+
* @param configPath 配置文件的路径
|
|
81
78
|
*/
|
|
82
79
|
async loadConfig(configPath) {
|
|
83
80
|
try {
|
|
84
|
-
this.configPath = configPath;
|
|
81
|
+
this.configPath = configPath;
|
|
85
82
|
const doc = yaml.load(fs.readFileSync(configPath, 'utf8'));
|
|
86
83
|
this.config = doc;
|
|
87
84
|
this.logger.info('Config loaded.');
|
|
88
|
-
// 开发环境下监听配置文件变化
|
|
89
85
|
if (process.env.NODE_ENV === 'development') {
|
|
90
86
|
this.watchConfig(configPath);
|
|
91
87
|
}
|
|
92
88
|
}
|
|
93
89
|
catch (e) {
|
|
94
90
|
this.logger.error('Failed to load config:', e);
|
|
95
|
-
throw e;
|
|
91
|
+
throw e;
|
|
96
92
|
}
|
|
97
93
|
}
|
|
98
94
|
/**
|
|
99
|
-
*
|
|
100
|
-
* @param configPath
|
|
95
|
+
* 监听配置文件变化,实现热重载
|
|
96
|
+
* @param configPath 配置文件的路径
|
|
101
97
|
*/
|
|
102
98
|
watchConfig(configPath) {
|
|
103
99
|
const watcher = chokidar_1.default.watch(configPath, {
|
|
@@ -109,30 +105,26 @@ class Core {
|
|
|
109
105
|
},
|
|
110
106
|
});
|
|
111
107
|
watcher.on('change', async () => {
|
|
112
|
-
//this.logger.info('Config file changed, reloading...');
|
|
113
108
|
try {
|
|
114
|
-
// 重新加载配置文件
|
|
115
109
|
const doc = yaml.load(fs.readFileSync(configPath, 'utf8'));
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
110
|
+
if (doc && doc.plugins) {
|
|
111
|
+
this.config.plugins = doc.plugins;
|
|
112
|
+
}
|
|
119
113
|
await this.emit('config-changed', this.config);
|
|
120
114
|
}
|
|
121
115
|
catch (error) {
|
|
122
|
-
|
|
116
|
+
this.logger.error('Failed to reload config:', error);
|
|
123
117
|
}
|
|
124
118
|
});
|
|
125
119
|
this.logger.info(`Watching for config changes at ${configPath}`);
|
|
126
120
|
}
|
|
127
121
|
/**
|
|
128
|
-
*
|
|
122
|
+
* 获取指定插件的配置
|
|
129
123
|
* @param pluginName 插件名称
|
|
130
|
-
* @returns
|
|
124
|
+
* @returns 插件的配置对象
|
|
131
125
|
*/
|
|
132
126
|
async getPluginConfig(pluginName) {
|
|
133
|
-
// 如果插件名以~开头,则去掉~前缀获取配置
|
|
134
127
|
const actualPluginName = pluginName.startsWith('~') ? pluginName.substring(1) : pluginName;
|
|
135
|
-
// 如果配置文件路径存在且处于开发模式,每次都重新读取配置文件
|
|
136
128
|
if (this.configPath && process.env.NODE_ENV === 'development') {
|
|
137
129
|
try {
|
|
138
130
|
const doc = yaml.load(fs.readFileSync(this.configPath, 'utf8'));
|
|
@@ -147,129 +139,221 @@ class Core {
|
|
|
147
139
|
if (!this.config.plugins[actualPluginName]) {
|
|
148
140
|
return new config_1.Config(actualPluginName);
|
|
149
141
|
}
|
|
150
|
-
|
|
151
|
-
return config;
|
|
142
|
+
return new config_1.Config(actualPluginName, this.config.plugins[actualPluginName]);
|
|
152
143
|
}
|
|
153
144
|
/**
|
|
154
|
-
*
|
|
155
|
-
* @returns Promise<void>
|
|
145
|
+
* 加载所有在配置文件中启用的插件
|
|
156
146
|
*/
|
|
157
147
|
async loadPlugins() {
|
|
158
|
-
// 检查 plugins 配置是否存在且是对象类型
|
|
159
148
|
if (!this.config || typeof this.config.plugins !== 'object' || this.config.plugins === null) {
|
|
160
|
-
this.logger.info('No plugins configuration found
|
|
149
|
+
this.logger.info('No plugins configuration found. No plugins to load.');
|
|
161
150
|
return;
|
|
162
151
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
152
|
+
const allPluginNames = Object.keys(this.config.plugins);
|
|
153
|
+
this.pluginStatus = {}; // 重置状态
|
|
154
|
+
// 初始化所有插件的状态
|
|
155
|
+
for (const name of allPluginNames) {
|
|
156
|
+
if (name.startsWith('~')) {
|
|
157
|
+
const actualName = name.substring(1);
|
|
158
|
+
this.pluginStatus[actualName] = "disabled" /* PluginStatus.DISABLED */;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
this.pluginStatus[name] = "pending" /* PluginStatus.PENDING */; // 默认为等待加载
|
|
162
|
+
}
|
|
168
163
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
164
|
+
const enabledPlugins = allPluginNames.filter(name => !name.startsWith('~'));
|
|
165
|
+
if (enabledPlugins.length === 0) {
|
|
166
|
+
this.logger.info('No enabled plugins found in configuration.');
|
|
167
|
+
return;
|
|
173
168
|
}
|
|
174
|
-
|
|
175
|
-
//
|
|
176
|
-
let
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
while (remainingPluginNames.length > 0) {
|
|
183
|
-
let loadedInThisPass = false;
|
|
184
|
-
// 用于存储下一轮尝试加载的插件名
|
|
185
|
-
const nextRemainingPluginNames = [];
|
|
186
|
-
// 遍历当前剩余待加载的插件名
|
|
187
|
-
for (const pluginName of remainingPluginNames) {
|
|
188
|
-
// 如果插件已经加载或已尝试加载,则跳过
|
|
189
|
-
if (loadedPluginNames.includes(pluginName) || loadAttempted[`${pluginName}`]) {
|
|
169
|
+
// this.logger.info(`Disabled plugins: ${Object.keys(this.pluginStatus).filter(p => this.pluginStatus[p] === PluginStatus.DISABLED).join(', ') || 'None'}`);
|
|
170
|
+
// 循环加载,直到没有插件可以被加载
|
|
171
|
+
let loadedInLastPass = true;
|
|
172
|
+
while (loadedInLastPass) {
|
|
173
|
+
loadedInLastPass = false;
|
|
174
|
+
for (const pluginName of enabledPlugins) {
|
|
175
|
+
// 如果插件已经是启用状态,则跳过
|
|
176
|
+
if (this.pluginStatus[pluginName] === "enabled" /* PluginStatus.ENABLED */) {
|
|
190
177
|
continue;
|
|
191
178
|
}
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
179
|
+
// 尝试加载单个插件
|
|
180
|
+
const success = await this.loadSinglePlugin(pluginName, false); // 内部加载,不触发后续的pending检查
|
|
181
|
+
if (success) {
|
|
182
|
+
loadedInLastPass = true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// 加载完所有能加载的插件后,统一检查并加载待定插件
|
|
187
|
+
await this._loadPendingPlugins();
|
|
188
|
+
const pendingPlugins = Object.keys(this.pluginStatus).filter(p => this.pluginStatus[p] === "pending" /* PluginStatus.PENDING */);
|
|
189
|
+
if (pendingPlugins.length > 0) {
|
|
190
|
+
// this.logger.warn('Some plugins could not be loaded due to unresolved dependencies:', pendingPlugins);
|
|
191
|
+
}
|
|
192
|
+
if (process.env.NODE_ENV === 'development') {
|
|
193
|
+
this.watchPlugins(this);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* 导出的函数:加载单个插件
|
|
198
|
+
* @param pluginName 要加载的插件名
|
|
199
|
+
* @param triggerPendingCheck 是否在加载后触发对其他待定插件的检查,默认为 true
|
|
200
|
+
* @returns Promise<boolean> 是否加载成功
|
|
201
|
+
*/
|
|
202
|
+
async loadSinglePlugin(pluginName, triggerPendingCheck = true) {
|
|
203
|
+
// 如果插件不存在于状态记录中(例如,动态添加),则设为 PENDING
|
|
204
|
+
if (!this.pluginStatus[pluginName]) {
|
|
205
|
+
this.pluginStatus[pluginName] = "pending" /* PluginStatus.PENDING */;
|
|
206
|
+
}
|
|
207
|
+
// 只有 PENDING 状态的插件才能被加载
|
|
208
|
+
if (this.pluginStatus[pluginName] !== "pending" /* PluginStatus.PENDING */) {
|
|
209
|
+
// this.logger.info(`Plugin "${pluginName}" is not in a pending state (current: ${this.pluginStatus[pluginName]}). Skipping load.`);
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
const pluginInstance = await this.pluginLoader.load(pluginName);
|
|
214
|
+
if (!pluginInstance) {
|
|
215
|
+
throw new Error('Plugin loader returned no instance.');
|
|
216
|
+
}
|
|
217
|
+
const deps = pluginInstance.depend || [];
|
|
218
|
+
const unmetDependencies = deps.filter(dep => !this.providedComponents[dep]);
|
|
219
|
+
if (unmetDependencies.length > 0) {
|
|
220
|
+
// this.logger.info(`Plugin "${pluginName}" has unmet dependencies: [${unmetDependencies.join(', ')}]. Will retry later.`);
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
// 依赖满足,开始加载
|
|
224
|
+
this.plugins[pluginName] = pluginInstance;
|
|
225
|
+
if (pluginInstance.apply) {
|
|
226
|
+
await pluginInstance.apply(new context_1.Context(this, pluginName), await this.getPluginConfig(pluginName));
|
|
227
|
+
this.pluginLoader.logger.info(`Applied plugin "${pluginName}"`);
|
|
228
|
+
}
|
|
229
|
+
// 注册提供的组件
|
|
230
|
+
if (pluginInstance.provide) {
|
|
231
|
+
for (const componentName of pluginInstance.provide) {
|
|
232
|
+
if (this.providedComponents[componentName]) {
|
|
233
|
+
this.logger.error(`Component "${componentName}" is provided by multiple plugins: "${this.providedComponents[componentName]}" and "${pluginName}".`);
|
|
235
234
|
}
|
|
236
235
|
else {
|
|
237
|
-
|
|
236
|
+
this.providedComponents[componentName] = pluginName;
|
|
238
237
|
}
|
|
239
238
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
239
|
+
}
|
|
240
|
+
this.pluginStatus[pluginName] = "enabled" /* PluginStatus.ENABLED */; // 更新状态为已启用
|
|
241
|
+
// this.logger.info(`Plugin "${pluginName}" loaded successfully.`);
|
|
242
|
+
// 加载成功后,检查是否可以加载其他等待中的插件
|
|
243
|
+
if (triggerPendingCheck) {
|
|
244
|
+
await this._loadPendingPlugins();
|
|
245
|
+
}
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
this.pluginLoader.logger.error(`Failed to load plugin "${pluginName}":`, err);
|
|
250
|
+
// 加载失败的插件保持 PENDING 状态,以便后续重试
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* 内部函数:尝试加载所有处于 PENDING 状态的插件
|
|
256
|
+
*/
|
|
257
|
+
async _loadPendingPlugins() {
|
|
258
|
+
const pendingPlugins = Object.keys(this.pluginStatus).filter(p => this.pluginStatus[p] === "pending" /* PluginStatus.PENDING */);
|
|
259
|
+
if (pendingPlugins.length === 0)
|
|
260
|
+
return;
|
|
261
|
+
// this.logger.info('Checking pending plugins...');
|
|
262
|
+
for (const pluginName of pendingPlugins) {
|
|
263
|
+
await this.loadSinglePlugin(pluginName, false); // 递归调用,但不触发顶层的pending检查
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* 卸载插件,并递归卸载依赖于它的其他插件
|
|
268
|
+
* @param pluginNameToUnload 要卸载的插件名
|
|
269
|
+
*/
|
|
270
|
+
async unloadPlugin(pluginNameToUnload) {
|
|
271
|
+
// 1. 找出所有直接或间接依赖于此插件的插件
|
|
272
|
+
const dependents = [];
|
|
273
|
+
const pluginsToCheck = [pluginNameToUnload];
|
|
274
|
+
while (pluginsToCheck.length > 0) {
|
|
275
|
+
const currentPluginName = pluginsToCheck.shift();
|
|
276
|
+
const provided = this.plugins[currentPluginName]?.provide || [];
|
|
277
|
+
if (provided.length === 0)
|
|
278
|
+
continue;
|
|
279
|
+
for (const pluginName in this.plugins) {
|
|
280
|
+
if (dependents.includes(pluginName) || pluginName === pluginNameToUnload)
|
|
281
|
+
continue;
|
|
282
|
+
const deps = this.plugins[pluginName].depend || [];
|
|
283
|
+
if (provided.some(p => deps.includes(p))) {
|
|
284
|
+
if (!dependents.includes(pluginName)) {
|
|
285
|
+
dependents.push(pluginName);
|
|
286
|
+
pluginsToCheck.push(pluginName); // 递归查找
|
|
287
|
+
// this.logger.info(`Plugin "${pluginName}" depends on "${currentPluginName}" and will be unloaded.`);
|
|
288
|
+
}
|
|
243
289
|
}
|
|
244
290
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
291
|
+
}
|
|
292
|
+
// 2. 卸载所有依赖者
|
|
293
|
+
for (const dependentName of dependents) {
|
|
294
|
+
await this._unloadSinglePlugin(dependentName);
|
|
295
|
+
}
|
|
296
|
+
// 3. 最后卸载目标插件
|
|
297
|
+
await this._unloadSinglePlugin(pluginNameToUnload);
|
|
298
|
+
// 4. 卸载完成后,尝试重新加载处于 PENDING 状态的插件
|
|
299
|
+
// await this._loadPendingPlugins();
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* 内部函数:执行单个插件的卸载逻辑
|
|
303
|
+
* @param pluginName 插件名
|
|
304
|
+
*/
|
|
305
|
+
async _unloadSinglePlugin(pluginName) {
|
|
306
|
+
if (this.pluginStatus[pluginName] !== "enabled" /* PluginStatus.ENABLED */) {
|
|
307
|
+
return; // 只卸载已启用的插件
|
|
308
|
+
}
|
|
309
|
+
this.logger.info(`Unloading plugin "${pluginName}"...`);
|
|
310
|
+
try {
|
|
311
|
+
const plugin = this.plugins[pluginName];
|
|
312
|
+
if (plugin && plugin.disable) {
|
|
313
|
+
await plugin.disable(new context_1.Context(this, pluginName));
|
|
250
314
|
}
|
|
251
|
-
|
|
252
|
-
|
|
315
|
+
await this.pluginLoader.unloadPlugin(pluginName);
|
|
316
|
+
this.unregall(pluginName); // 清理该插件注册的所有内容
|
|
317
|
+
delete this.plugins[pluginName];
|
|
318
|
+
delete this.pluginModules[pluginName];
|
|
319
|
+
// 更新状态为 PENDING,因为它的配置仍然是启用的
|
|
320
|
+
// 如果它被其他插件依赖,当那个插件被卸载时,它也会被重新评估
|
|
321
|
+
this.pluginStatus[pluginName] = "pending" /* PluginStatus.PENDING */;
|
|
322
|
+
this.emit('plugin-unloaded', pluginName);
|
|
323
|
+
// this.logger.info(`Plugin "${pluginName}" unloaded.`);
|
|
253
324
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
this.logger.warn('Some plugins could not be fully loaded due to unresolved dependencies or errors:', remainingPluginNames.map(name => name) // 映射回插件名
|
|
257
|
-
);
|
|
325
|
+
catch (error) {
|
|
326
|
+
this.logger.error(`Failed to unload plugin "${pluginName}":`, error);
|
|
258
327
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* 重新加载插件
|
|
331
|
+
* @param pluginName 插件名称
|
|
332
|
+
*/
|
|
333
|
+
async reloadPlugin(pluginName) {
|
|
334
|
+
// this.logger.info(`Reloading plugin "${pluginName}"...`);
|
|
335
|
+
// 1. 递归卸载插件及其依赖者
|
|
336
|
+
await this.unloadPlugin(pluginName);
|
|
337
|
+
// 2. 重新加载该插件
|
|
338
|
+
// unloadPlugin 已经将状态设置为 PENDING,所以 loadSinglePlugin 可以直接工作
|
|
339
|
+
const success = await this.loadSinglePlugin(pluginName);
|
|
340
|
+
if (success) {
|
|
341
|
+
// this.logger.info(`Plugin "${pluginName}" reloaded successfully.`);
|
|
342
|
+
this.emit('plugin-reloaded', pluginName);
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
this.logger.error(`Failed to reload plugin "${pluginName}". It may have unmet dependencies.`);
|
|
262
346
|
}
|
|
263
347
|
}
|
|
264
348
|
/**
|
|
265
|
-
*
|
|
266
|
-
* @param core Core实例
|
|
267
|
-
* @param pluginsDir
|
|
349
|
+
* 监听插件文件变化,实现热更新
|
|
350
|
+
* @param core Core 实例
|
|
351
|
+
* @param pluginsDir 插件目录,默认为 'plugins'
|
|
268
352
|
*/
|
|
269
353
|
watchPlugins(core, pluginsDir = 'plugins') {
|
|
270
354
|
const logger = new logger_1.Logger('hmr');
|
|
271
355
|
const watcher = chokidar_1.default.watch(pluginsDir, {
|
|
272
|
-
ignored: /(^|[/\\])\../,
|
|
356
|
+
ignored: /(^|[/\\])\../,
|
|
273
357
|
persistent: true,
|
|
274
358
|
ignoreInitial: true,
|
|
275
359
|
awaitWriteFinish: {
|
|
@@ -277,167 +361,84 @@ class Core {
|
|
|
277
361
|
pollInterval: 100,
|
|
278
362
|
},
|
|
279
363
|
});
|
|
280
|
-
|
|
364
|
+
const getPluginNameFromPath = (changePath) => {
|
|
281
365
|
if (!changePath.endsWith('.ts') && !changePath.endsWith('.js'))
|
|
282
|
-
return;
|
|
366
|
+
return null;
|
|
283
367
|
const parts = changePath.split(path.sep);
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
368
|
+
return parts.length > 1 ? parts[1] : null;
|
|
369
|
+
};
|
|
370
|
+
watcher.on('change', async (changePath) => {
|
|
371
|
+
const pluginName = getPluginNameFromPath(changePath);
|
|
372
|
+
if (pluginName) {
|
|
373
|
+
logger.info(`Plugin file changed: ${changePath}`);
|
|
374
|
+
await core.reloadPlugin(pluginName);
|
|
375
|
+
}
|
|
289
376
|
});
|
|
290
377
|
watcher.on('add', async (changePath) => {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const pluginName = parts[1];
|
|
297
|
-
this.logger.info(`New plugin file added: ${changePath}`);
|
|
298
|
-
this.reloadPlugin(pluginName, core);
|
|
378
|
+
const pluginName = getPluginNameFromPath(changePath);
|
|
379
|
+
if (pluginName) {
|
|
380
|
+
logger.info(`New plugin file added: ${changePath}`);
|
|
381
|
+
await core.reloadPlugin(pluginName);
|
|
382
|
+
}
|
|
299
383
|
});
|
|
300
384
|
watcher.on('unlink', async (changePath) => {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const pluginName = parts[1];
|
|
307
|
-
this.logger.info(`Plugin file removed: ${changePath}`);
|
|
308
|
-
await this.unloadPluginAndEmit(pluginName, core);
|
|
385
|
+
const pluginName = getPluginNameFromPath(changePath);
|
|
386
|
+
if (pluginName) {
|
|
387
|
+
logger.info(`Plugin file removed: ${changePath}`);
|
|
388
|
+
await core.unloadPlugin(pluginName);
|
|
389
|
+
}
|
|
309
390
|
});
|
|
310
391
|
logger.info(`Watching for plugin changes in ${pluginsDir}`);
|
|
311
392
|
}
|
|
312
|
-
/**
|
|
313
|
-
* 重新加载插件
|
|
314
|
-
* @param pluginName 插件名称
|
|
315
|
-
* @param core Core实例
|
|
316
|
-
*/
|
|
317
|
-
async reloadPlugin(pluginName, core) {
|
|
318
|
-
try {
|
|
319
|
-
// 强制重新读取配置文件,确保获取最新配置
|
|
320
|
-
if (this.configPath && process.env.NODE_ENV === 'development') {
|
|
321
|
-
try {
|
|
322
|
-
const doc = yaml.load(fs.readFileSync(this.configPath, 'utf8'));
|
|
323
|
-
if (doc && doc.plugins) {
|
|
324
|
-
this.config.plugins = doc.plugins;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
catch (error) {
|
|
328
|
-
this.logger.warn('Failed to refresh config for plugin reload:', error);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
// 获取最新的插件配置
|
|
332
|
-
const config = await core.getPluginConfig(pluginName);
|
|
333
|
-
// 加载插件实例
|
|
334
|
-
const plugin = await core.pluginLoader.load(pluginName);
|
|
335
|
-
// 如果插件存在,先禁用旧实例
|
|
336
|
-
if (plugin && plugin.disable) {
|
|
337
|
-
await plugin.disable(new context_1.Context(this, pluginName));
|
|
338
|
-
}
|
|
339
|
-
// 清理插件提供的组件
|
|
340
|
-
// if (plugin.provide) {
|
|
341
|
-
// for (let providedmodules of plugin.provide) {
|
|
342
|
-
// if (this.components[`${providedmodules}`]) {
|
|
343
|
-
// this.unregisterComponent(providedmodules);
|
|
344
|
-
// }
|
|
345
|
-
// }
|
|
346
|
-
// }
|
|
347
|
-
// 卸载插件
|
|
348
|
-
await core.unloadPluginAndEmit(pluginName, core);
|
|
349
|
-
// 应用新的插件实例
|
|
350
|
-
if (plugin && plugin.apply) {
|
|
351
|
-
await plugin.apply(new context_1.Context(this, pluginName), config);
|
|
352
|
-
}
|
|
353
|
-
this.pluginLoader.logger.info(`apply plugin ${pluginName}`);
|
|
354
|
-
// 触发插件重载事件
|
|
355
|
-
core.emit('plugin-reloaded', pluginName);
|
|
356
|
-
}
|
|
357
|
-
catch (error) {
|
|
358
|
-
this.logger.error(`Failed to reload plugin ${pluginName}:`, error);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* 卸载插件并触发事件
|
|
363
|
-
* @param pluginName 插件名称
|
|
364
|
-
* @param core Core实例
|
|
365
|
-
*/
|
|
366
|
-
async unloadPluginAndEmit(pluginName, core) {
|
|
367
|
-
try {
|
|
368
|
-
const plugin = core.plugins[`${pluginName}`];
|
|
369
|
-
if (plugin && plugin.disable) {
|
|
370
|
-
await plugin.disable(new context_1.Context(this, pluginName));
|
|
371
|
-
}
|
|
372
|
-
await core.pluginLoader.unloadPlugin(pluginName);
|
|
373
|
-
delete core.plugins[`${pluginName}`];
|
|
374
|
-
delete this.pluginModules[`${pluginName}`];
|
|
375
|
-
// Remove provided components from this plugin
|
|
376
|
-
// for (const componentName in this.providedComponents) {
|
|
377
|
-
// if (this.providedComponents[`${componentName}`] === pluginName) {
|
|
378
|
-
// delete this.components[`${componentName}`];
|
|
379
|
-
// delete this.providedComponents[`${componentName}`];
|
|
380
|
-
// }
|
|
381
|
-
// }
|
|
382
|
-
this.unregall(pluginName);
|
|
383
|
-
core.emit('plugin-unloaded', pluginName);
|
|
384
|
-
}
|
|
385
|
-
catch (error) {
|
|
386
|
-
this.logger.error(`Failed to unload plugin ${pluginName}:`, error);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
393
|
/**
|
|
390
394
|
* 注册组件
|
|
391
395
|
* @param name 组件名称
|
|
392
|
-
* @param component
|
|
393
|
-
* @returns void
|
|
396
|
+
* @param component 组件实例
|
|
394
397
|
*/
|
|
395
398
|
registerComponent(name, component) {
|
|
396
399
|
if (this.components.hasOwnProperty(name)) {
|
|
397
|
-
this.logger.warn(`Component "${name}" already registered by plugin "${this.providedComponents[
|
|
400
|
+
this.logger.warn(`Component "${name}" already registered by plugin "${this.providedComponents[name]}".`);
|
|
398
401
|
return;
|
|
399
402
|
}
|
|
400
|
-
this.components[
|
|
401
|
-
this.logger.info(`Component "${name}" registered.`);
|
|
403
|
+
this.components[name] = component;
|
|
404
|
+
// this.logger.info(`Component "${name}" registered.`);
|
|
402
405
|
}
|
|
403
406
|
/**
|
|
404
407
|
* 获取组件
|
|
405
408
|
* @param name 组件名称
|
|
406
|
-
* @returns
|
|
409
|
+
* @returns 组件实例
|
|
407
410
|
*/
|
|
408
411
|
getComponent(name) {
|
|
409
|
-
return this.components[
|
|
412
|
+
return this.components[name];
|
|
410
413
|
}
|
|
411
414
|
/**
|
|
412
|
-
*
|
|
415
|
+
* 注销组件
|
|
413
416
|
* @param name 组件名称
|
|
414
417
|
*/
|
|
415
418
|
unregisterComponent(name) {
|
|
416
|
-
delete this.components[
|
|
417
|
-
delete this.providedComponents[
|
|
418
|
-
delete this.comtoplu[
|
|
419
|
+
delete this.components[name];
|
|
420
|
+
delete this.providedComponents[name];
|
|
421
|
+
delete this.comtoplu[name];
|
|
419
422
|
}
|
|
420
423
|
/**
|
|
421
|
-
*
|
|
424
|
+
* 注册事件监听器
|
|
422
425
|
* @param event 事件名称
|
|
423
|
-
* @param listener
|
|
426
|
+
* @param listener 事件回调函数
|
|
424
427
|
*/
|
|
425
428
|
on(event, listener) {
|
|
426
|
-
if (!this.eventListeners[
|
|
427
|
-
this.eventListeners[
|
|
429
|
+
if (!this.eventListeners[event]) {
|
|
430
|
+
this.eventListeners[event] = [];
|
|
428
431
|
}
|
|
429
|
-
this.eventListeners[
|
|
430
|
-
//this.logger.info(`Listener added for event "${event}".`);
|
|
432
|
+
this.eventListeners[event].push(listener);
|
|
431
433
|
}
|
|
432
434
|
/**
|
|
433
435
|
* 触发事件
|
|
434
436
|
* @param event 事件名称
|
|
435
|
-
* @param args
|
|
437
|
+
* @param args 传递给事件监听器的参数
|
|
436
438
|
*/
|
|
437
439
|
async emit(event, ...args) {
|
|
438
|
-
if (this.eventListeners[
|
|
439
|
-
|
|
440
|
-
for (const listener of this.eventListeners[`${event}`]) {
|
|
440
|
+
if (this.eventListeners[event]) {
|
|
441
|
+
for (const listener of this.eventListeners[event]) {
|
|
441
442
|
try {
|
|
442
443
|
await listener(...args);
|
|
443
444
|
}
|
|
@@ -446,136 +447,109 @@ class Core {
|
|
|
446
447
|
}
|
|
447
448
|
}
|
|
448
449
|
}
|
|
449
|
-
else {
|
|
450
|
-
//this.logger.info(`No listeners for event "${event}".`);
|
|
451
|
-
}
|
|
452
450
|
}
|
|
453
451
|
/**
|
|
454
452
|
* 注册全局中间件
|
|
455
453
|
* @param name 中间件名称
|
|
456
|
-
* @param middleware
|
|
457
|
-
* @returns Core
|
|
454
|
+
* @param middleware 中间件函数
|
|
455
|
+
* @returns Core 实例
|
|
458
456
|
*/
|
|
459
457
|
use(name, middleware) {
|
|
460
458
|
this.globalMiddlewares[name] = middleware;
|
|
461
459
|
return this;
|
|
462
460
|
}
|
|
463
461
|
/**
|
|
464
|
-
*
|
|
465
|
-
* @param name
|
|
466
|
-
* @returns
|
|
462
|
+
* 注册命令
|
|
463
|
+
* @param name 命令名称
|
|
464
|
+
* @returns Command 实例
|
|
467
465
|
*/
|
|
468
466
|
command(name) {
|
|
469
|
-
const command = new command_1.Command(this, name);
|
|
470
|
-
this.commands[
|
|
467
|
+
const command = new command_1.Command(this, name);
|
|
468
|
+
this.commands[name] = command;
|
|
471
469
|
return command;
|
|
472
470
|
}
|
|
473
471
|
/**
|
|
474
|
-
*
|
|
475
|
-
* @param name
|
|
476
|
-
* @param session
|
|
477
|
-
* @param args
|
|
478
|
-
* @returns
|
|
472
|
+
* 执行命令
|
|
473
|
+
* @param name 命令名称
|
|
474
|
+
* @param session 会话对象
|
|
475
|
+
* @param args 传递给命令处理函数的参数
|
|
476
|
+
* @returns 会话对象或 null
|
|
479
477
|
*/
|
|
480
478
|
async executeCommand(name, session, ...args) {
|
|
481
|
-
const command = this.commands[
|
|
479
|
+
const command = this.commands[name];
|
|
482
480
|
if (command) {
|
|
483
|
-
// 将 globalMiddlewares 从对象转为数组
|
|
484
481
|
const globalMiddlewareList = Object.values(this.globalMiddlewares || {});
|
|
485
482
|
const commandMiddlewareList = command.middlewares || [];
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
const runner = async () => {
|
|
491
|
-
if (index >= middlewares.length) {
|
|
492
|
-
await command.executeHandler(session, ...args);
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
483
|
+
const middlewares = [...globalMiddlewareList, ...commandMiddlewareList];
|
|
484
|
+
let index = 0;
|
|
485
|
+
const runner = async () => {
|
|
486
|
+
if (index < middlewares.length) {
|
|
495
487
|
const middleware = middlewares[index++];
|
|
496
488
|
await middleware(session, runner);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
await command.executeHandler(session, ...args);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
await runner();
|
|
495
|
+
return session;
|
|
504
496
|
}
|
|
505
497
|
return null;
|
|
506
498
|
}
|
|
507
499
|
/**
|
|
508
500
|
* 注册平台
|
|
509
|
-
* @param platform
|
|
510
|
-
* @returns
|
|
501
|
+
* @param platform 平台实例
|
|
502
|
+
* @returns 平台启动结果
|
|
511
503
|
*/
|
|
512
504
|
registerPlatform(platform) {
|
|
513
505
|
this.platforms.push(platform);
|
|
514
506
|
return platform.startPlatform(this);
|
|
515
507
|
}
|
|
516
508
|
/**
|
|
517
|
-
*
|
|
509
|
+
* 清理指定插件注册的所有内容
|
|
518
510
|
* @param pluginname 插件名称
|
|
519
511
|
*/
|
|
520
512
|
unregall(pluginname) {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
for (const cmd of cmdtodel) {
|
|
513
|
+
// 清理 commands
|
|
514
|
+
Object.keys(this.cmdtoplu)
|
|
515
|
+
.filter(cmd => this.cmdtoplu[cmd] === pluginname)
|
|
516
|
+
.forEach(cmd => {
|
|
528
517
|
delete this.cmdtoplu[cmd];
|
|
529
518
|
delete this.commands[cmd];
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
for (const comp of comptodel) {
|
|
519
|
+
});
|
|
520
|
+
// 清理 components 和 providedComponents
|
|
521
|
+
Object.keys(this.comtoplu)
|
|
522
|
+
.filter(comp => this.comtoplu[comp] === pluginname)
|
|
523
|
+
.forEach(comp => {
|
|
538
524
|
delete this.comtoplu[comp];
|
|
539
525
|
delete this.components[comp];
|
|
540
526
|
delete this.providedComponents[comp];
|
|
541
|
-
}
|
|
542
|
-
|
|
527
|
+
});
|
|
528
|
+
// 清理 event listeners
|
|
543
529
|
for (const evt in this.evttoplu) {
|
|
544
530
|
if (this.evttoplu[evt][pluginname]) {
|
|
545
|
-
// 清除插件在 evttoplu 中的记录
|
|
546
|
-
delete this.evttoplu[evt][pluginname];
|
|
547
|
-
// 如果该事件的插件已经全部清除了,也可以选择删掉整个事件
|
|
548
|
-
if (Object.keys(this.evttoplu[evt]).length === 0) {
|
|
549
|
-
evttodel.push(evt);
|
|
550
|
-
}
|
|
551
|
-
// 同时在 this.eventListeners 中删掉对应 listener
|
|
552
531
|
const pluginListeners = this.evttoplu[evt][pluginname] || [];
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
for (const mdw in this.mdwtoplu) {
|
|
558
|
-
if (this.mdwtoplu[mdw] === pluginname) {
|
|
559
|
-
mdwtodel.push(mdw);
|
|
532
|
+
if (this.eventListeners[evt]) {
|
|
533
|
+
this.eventListeners[evt] = this.eventListeners[evt].filter(l => !pluginListeners.includes(l));
|
|
534
|
+
}
|
|
535
|
+
delete this.evttoplu[evt][pluginname];
|
|
560
536
|
}
|
|
561
537
|
}
|
|
562
|
-
|
|
538
|
+
// 清理 middlewares
|
|
539
|
+
Object.keys(this.mdwtoplu)
|
|
540
|
+
.filter(mdw => this.mdwtoplu[mdw] === pluginname)
|
|
541
|
+
.forEach(mdw => {
|
|
563
542
|
delete this.mdwtoplu[mdw];
|
|
564
543
|
delete this.globalMiddlewares[mdw];
|
|
565
|
-
}
|
|
544
|
+
});
|
|
566
545
|
}
|
|
567
546
|
/**
|
|
568
|
-
*
|
|
569
|
-
* @param name
|
|
570
|
-
* @returns null
|
|
547
|
+
* 获取命令
|
|
548
|
+
* @param name 命令名称
|
|
549
|
+
* @returns Command 实例或 null
|
|
571
550
|
*/
|
|
572
551
|
getCommand(name) {
|
|
573
|
-
|
|
574
|
-
return this.commands[name];
|
|
575
|
-
}
|
|
576
|
-
else {
|
|
577
|
-
return null;
|
|
578
|
-
}
|
|
552
|
+
return this.commands[name] || null;
|
|
579
553
|
}
|
|
580
554
|
}
|
|
581
555
|
exports.Core = Core;
|