mm_machine 2.9.3 → 2.9.4

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.
Files changed (4) hide show
  1. package/drive.js +668 -490
  2. package/index.js +1141 -921
  3. package/mod.js +742 -711
  4. package/package.json +2 -2
package/index.js CHANGED
@@ -1,678 +1,1220 @@
1
- const { Mod } = require('./mod');
2
- const { Drive } = require('./drive.js');
1
+ /**
2
+ * Manager 管理器类
3
+ * 负责模块注册、检索、执行,支持注册表协同和多种执行模式
4
+ * @class Manager
5
+ * @augments Drive
6
+ */
7
+ var path = require('path');
8
+ var { Drive } = require('./drive');
9
+ var { Registry } = require('./registry');
3
10
 
4
11
  /**
5
- * 管理器类
12
+ * 生命周期状态优先级
13
+ * 用于 reload 时状态恢复
6
14
  */
7
- class Manager extends Mod {
8
- /**
9
- * 配置项
10
- * @type {object}
11
- */
12
- static config = {
13
- /**
14
- * 管理器名称
15
- * @type {string}
16
- */
17
- name: 'manager',
18
- /**
19
- * 管理器标题
20
- * @type {string}
21
- */
22
- title: '管理器',
23
- /**
24
- * 管理器描述
25
- * @type {string}
26
- */
27
- description: '用于管理驱动',
28
- /**
29
- * 检索文件名
30
- * @type {string}
31
- */
32
- filename: 'manager.json',
33
- /**
34
- * 模板目录
35
- * @type {string}
36
- */
37
- tpl_dir: __dirname,
38
- /**
39
- * 基础目录,加载模块内置资源
40
- * @type {string}
41
- */
42
- base_dir: '', // '../common/manager'.fullname(__dirname),
43
- /**
44
- * 自定义目录,加载项目自定义资源
45
- * @type {string}
46
- */
47
- dir: '', // './ai/manager'.fullname(),
48
- /**
49
- * 搜索模式 dir按目录搜索 | file按文件名搜索
50
- * @type {string}
51
- */
52
- search_way: 'file',
53
- /**
54
- * 类型 script脚本 | json配置 | drive驱动
55
- * @type {string}
56
- */
57
- mod_type: 'drive',
58
- /**
59
- * 是否懒加载
60
- * @type {boolean}
61
- */
62
- lazy_load: true,
63
- /**
64
- * 模式
65
- * 1.生产模式,改变文件不会重新加载
66
- * 2.热更新模式,改变配置文件会重新加载配置,不重新加载脚本
67
- * 3.热重载模式,改变配置文件都会加载配置和脚本
68
- * 4.热更新+重载模式,改变配置文件重新加载配置和脚本,执行完后重新加载脚本
69
- * 5.重载模式,执行完后重新加载脚本,避免变量污染
70
- * @type {number}
71
- */
72
- mode: 3,
73
- /**
74
- * 排序项
75
- * @type {string}
76
- */
77
- sort_key: 'sort',
78
- /**
79
- * 作用域
80
- * @type {string}
81
- */
82
- scope: $.val && $.val.scope ? $.val.scope + '' : 'server',
83
- /**
84
- * 是否启用快照加速
85
- * @type {boolean}
86
- */
87
- use_snapshot: true,
88
- /**
89
- * 快照文件路径,留空则自动生成到 cache/snapshot/ 目录下
90
- * @type {string}
91
- */
92
- snapshot_file: ''
93
- };
15
+ var statePriority = {
16
+ created: 0,
17
+ loaded: 1,
18
+ inited: 2,
19
+ started: 3,
20
+ paused: 4,
21
+ ended: 5,
22
+ destroyed: 6
23
+ };
94
24
 
25
+ /**
26
+ * 静态配置默认值
27
+ */
28
+ var defaultConfig = {
29
+ name: 'manager',
30
+ title: '管理器',
31
+ description: '模块管理器',
32
+ dir: '',
33
+ use_registry: false,
34
+ registry_file: 'cache/registry.json',
35
+ lazy_load: 'auto',
36
+ exec_mode: 1,
37
+ sort: 100
38
+ };
39
+
40
+ class Manager extends Drive {
95
41
  /**
96
42
  * 构造函数
97
- * @param {object} config 配置项
98
- * @param {object} parent 父项对象
99
- * @param {object} mods 模块对象列表
100
- * @param {object} Drive 驱动对象
43
+ * @param {object} cfg 配置参数
44
+ * @param {string} [cfg.dir] 模块存放目录
45
+ * @param {boolean} [cfg.use_registry=false] 是否启用注册表
46
+ * @param {string} [cfg.registry_file='cache/registry.json'] 注册表文件路径
47
+ * @param {string|boolean} [cfg.lazy_load='auto'] 全局懒加载策略
48
+ * @param {number} [cfg.exec_mode=1] 事件执行模式
49
+ * @param parent
50
+ * @param mods
51
+ * @param DriveClass
101
52
  */
102
- constructor(config, parent, mods = {}, Drive) {
103
- super(
104
- {
105
- ...Manager.config,
106
- ...config
107
- },
108
- parent
109
- );
110
-
111
- // 模块信息列表 { name: '', sort: 0, available: true, enabled: true }
112
- this.infos = [];
113
- // 模块
114
- this.mods = mods;
115
- if (Drive) {
116
- this.Drive = Drive;
53
+ constructor(cfg, parent, mods, DriveClass) {
54
+ // 兼容旧签名:new Manager(config, parent, mods, Drive)
55
+ if (parent !== undefined || mods !== undefined) {
56
+ var config = { ...defaultConfig, ...cfg };
57
+ // 合并旧 config 中的静态配置
58
+ if (Manager.config) {
59
+ config = { ...Manager.config, ...config };
60
+ }
61
+ super(config, parent);
62
+ this._mods = mods || {};
63
+ this._infos = [];
64
+ this.registry = null;
65
+ this.dir = config.dir || '';
66
+ if (DriveClass) {
67
+ this.Drive = DriveClass;
68
+ }
69
+ } else {
70
+ var config = { ...defaultConfig, ...cfg };
71
+ super(config);
72
+ this._mods = {};
73
+ this._infos = [];
74
+ this.registry = null;
75
+ this.dir = config.dir || '';
117
76
  }
118
77
  }
119
-
120
- get list() {
121
- return this.getList();
122
- }
123
78
  }
124
79
 
80
+ // ==================== 注册表管理 ====================
81
+
125
82
  /**
126
- * 获取事件触发器
127
- * @returns {object} 事件触发器
83
+ * 关联注册表实例
84
+ * @param {object} registry Registry 实例
128
85
  */
129
- Manager.prototype.getEventer = function () {
130
- return $.eventer;
86
+ Manager.prototype.setRegistry = function (registry) {
87
+ this.registry = registry;
131
88
  };
132
89
 
133
90
  /**
134
- * 获取所有模块信息
135
- * @returns {object[]} 模块信息数组
91
+ * 获取注册表实例
92
+ * @returns {object|null} Registry 实例
136
93
  */
137
- Manager.prototype.getInfos = function () {
138
- return this.infos;
94
+ Manager.prototype.getRegistry = function () {
95
+ return this.registry;
139
96
  };
140
97
 
98
+ // ==================== 模块查询 ====================
99
+
141
100
  /**
142
- * 创建模块状态模型
143
- * @param {string} name 模块名
144
- * @param {number} sort 排序值
145
- * @param {boolean} state 状态
146
- * @param {string} file 文件名
147
- * @returns {object} 模块状态模型
101
+ * 获取模块
102
+ * @param {string} name 模块名称
103
+ * @returns {object|null} 模块实例,不存在返回 null
148
104
  */
149
- Manager.prototype.newInfo = function (name, sort, state, file = '') {
150
- return {
151
- name,
152
- sort,
153
- state,
154
- enabled: true,
155
- file: file || '',
156
- get available() {
157
- return this.state == true && this.enabled;
158
- }
159
- };
105
+ Manager.prototype.getMod = function (name) {
106
+ return this._mods[name];
160
107
  };
161
108
 
162
109
  /**
163
- * 设置模块信息
164
- * @param {object} info 模块信息
110
+ * 获取所有模块
111
+ * @returns {object} 模块对象 { name: Drive }
165
112
  */
166
- Manager.prototype.setInfo = function (info) {
167
- let has = false;
168
- for (let i = 0; i < this.infos.length; i++) {
169
- let o = this.infos[i];
170
- if (o.name == info.name) {
171
- this.infos[i] = info;
172
- has = true;
173
- break;
174
- }
175
- }
176
- if (!has) {
177
- this.infos.push(info);
178
- }
113
+ Manager.prototype.getMods = function () {
114
+ return this._mods;
179
115
  };
180
116
 
181
117
  /**
182
- * 获取所有模块对象
183
- * @param {string} keyword 查询关键词,判断nametitle、description之一是否包含关键词,包含的才返回
184
- * @returns {object[]} 模块对象数组
118
+ * 获取模块列表
119
+ * @param {string} [keyword] 模糊匹配关键词,匹配 nametitle
120
+ * @returns {Array<object>} 模块实例数组
185
121
  */
186
122
  Manager.prototype.getList = function (keyword) {
187
- let list = [];
188
- for (let i = 0; i < this.infos.length; i++) {
189
- let info = this.infos[i];
190
- let mod = this.getMod(info.name);
123
+ var list = [];
124
+ for (var i = 0; i < this._infos.length; i++) {
125
+ var info = this._infos[i];
126
+ var mod = this.getMod(info.name);
191
127
  if (!mod) {
192
128
  continue;
193
129
  }
130
+ // 合并状态信息
194
131
  mod.config.state = info.state;
195
132
  list.push(mod);
196
133
  }
197
134
  if (keyword) {
198
- list = list.filter((o) => o.name.includes(keyword) || o.title.includes(keyword) || o.description.includes(keyword));
135
+ list = list.filter((o) => {
136
+ return (o.config.name && o.config.name.indexOf(keyword) !== -1) ||
137
+ (o.config.title && o.config.title.indexOf(keyword) !== -1) ||
138
+ (o.config.description && o.config.description.indexOf(keyword) !== -1);
139
+ });
199
140
  }
200
141
  return list;
201
142
  };
202
143
 
203
144
  /**
204
- * 获取基础目录
205
- * @returns {string} 基础目录
145
+ * 获取模块信息列表
146
+ * @returns {Array<object>} 模块信息数组
206
147
  */
207
- Manager.prototype.getBaseDir = function () {
208
- return this.config.base_dir || __dirname;
148
+ Manager.prototype.getInfos = function () {
149
+ return this._infos;
209
150
  };
210
151
 
211
152
  /**
212
- * 获取模板目录
213
- * @returns {string} 模板目录
153
+ * 设置模块
154
+ * @param {string} name 模块名称
155
+ * @param {object} mod 模块实例
156
+ * @returns {object|null} 设置的模块实例
214
157
  */
215
- Manager.prototype.getTplDir = function () {
216
- return this.config.tpl_dir || __dirname;
158
+ Manager.prototype.setMod = function (name, mod) {
159
+ this._mods[name] = mod;
160
+ return this.getMod(name);
217
161
  };
218
162
 
219
163
  /**
220
- * 默认驱动
164
+ * 更新模块信息
165
+ * 部分合并,只覆盖 info 中存在的字段
166
+ * @param {string} name 模块名称
167
+ * @param {object} info 要更新的信息字段
221
168
  */
222
- Manager.prototype.Drive = Drive;
169
+ Manager.prototype.setInfo = function (name, info) {
170
+ // 兼容旧签名:setInfo({ name, sort, state, ... })
171
+ var info_name = name;
172
+ var info_data = info;
173
+ if (typeof name === 'object' && name !== null) {
174
+ info_data = name;
175
+ info_name = info_data.name;
176
+ }
177
+ if (!info_data || !info_name) {
178
+ return;
179
+ }
180
+ for (var i = 0; i < this._infos.length; i++) {
181
+ if (this._infos[i].name === info_name) {
182
+ for (var k in info_data) {
183
+ this._infos[i][k] = info_data[k];
184
+ }
185
+ return;
186
+ }
187
+ }
188
+ // 不存在则追加
189
+ this._infos.push(info_data);
190
+ };
223
191
 
224
192
  /**
225
- * 更新模块信息
193
+ * 删除模块(仅内存,不删除文件)
194
+ * @param {string} name 模块名称
195
+ * @returns {object|null} 删除的模块实例
226
196
  */
227
- Manager.prototype.updateInfo = function () {
228
- this.infos = [];
229
- let mods = this.getMods();
230
- for (let name in mods) {
231
- let o = mods[name];
232
- let info = this.newInfo(name, o.config.sort, o.config.state, o.config_file);
233
- this.infos.push(info);
197
+ Manager.prototype.delMod = function (name) {
198
+ var mod = this._mods[name];
199
+ if (mod) {
200
+ delete this._mods[name];
234
201
  }
235
- this.sort();
202
+ return mod;
236
203
  };
237
204
 
238
205
  /**
239
- * 排序
206
+ * 清除所有模块
207
+ * 先遍历调用所有模块的 end() 和 destroy() 清理,再清空内存
240
208
  */
241
- Manager.prototype._sort = function () {
242
- var sort_key = this.config.sort_key || 'sort';
243
- this.infos.sort((o1, o2) => {
244
- let p1 = o1[sort_key] || 100;
245
- let p2 = o2[sort_key] || 100;
246
- return p1 - p2;
247
- });
209
+ Manager.prototype.clearMods = function () {
210
+ for (var name in this._mods) {
211
+ var mod = this._mods[name];
212
+ try {
213
+ if (mod.lifecycle_state === 'started' || mod.lifecycle_state === 'paused') {
214
+ mod.end();
215
+ }
216
+ if (mod.lifecycle_state === 'ended') {
217
+ mod.destroy();
218
+ }
219
+ } catch {
220
+ // 清理异常不影响其他模块
221
+ }
222
+ }
223
+ this._mods = {};
224
+ this._infos = [];
248
225
  };
249
226
 
227
+ // ==================== 模块注册/创建 ====================
228
+
250
229
  /**
251
- * 排序
230
+ * 创建并注册模块
231
+ * 根据 type 决定实例化类
232
+ * @param {string} config_file 配置文件路径
233
+ * @param {object} [cfg] 配置对象,不传则从文件读取
234
+ * @param {object|string} [script] 脚本内容,不传则从文件读取
235
+ * @returns {object} 模块实例
252
236
  */
253
- Manager.prototype.sort = function () {
254
- this._sort();
237
+ Manager.prototype.registerMod = function (config_file, cfg, script) {
238
+ var config = cfg || {};
239
+
240
+ // 未提供 cfg 时从文件读取
241
+ if (!config.name && config_file) {
242
+ try {
243
+ var content = require(config_file);
244
+ config = { ...config, ...content };
245
+ } catch {
246
+ // 文件读取失败,使用传入的 cfg
247
+ }
248
+ }
249
+
250
+ // 创建模块实例
251
+ var mod = this._createModInstance(config);
252
+ mod.config_file = config_file;
253
+ mod.mode = config.mode || 3;
254
+
255
+ // 注册脚本方法
256
+ this._regScriptMethods(mod, script);
257
+
258
+ this._mods[config.name] = mod;
259
+ return mod;
255
260
  };
256
261
 
257
- // ==== 模块管理接口 ====
258
262
  /**
259
- * 清除所有模块
263
+ * 根据类型创建模块实例
264
+ * @param {object} config 配置对象
265
+ * @returns {object} 模块实例
266
+ * @private
260
267
  */
261
- Manager.prototype.clearMods = function () {
262
- for (let name in this.mods) {
263
- delete this.mods[name];
268
+ Manager.prototype._createModInstance = function (config) {
269
+ var mod_type = config.type || 'plugin';
270
+ if (mod_type === 'manager') {
271
+ return new Manager(config);
264
272
  }
273
+ return new Drive(config);
265
274
  };
266
275
 
267
276
  /**
268
- * 删除模块
269
- * @param {string} name 模块名
270
- * @returns {object|null} 删除的模块对象
277
+ * 注册脚本方法到模块
278
+ * @param {object} mod 模块实例
279
+ * @param {object|string} script 脚本内容
280
+ * @private
271
281
  */
272
- Manager.prototype.delMod = function (name) {
273
- let mod = this.mods[name];
274
- if (mod) {
275
- delete this.mods[name];
282
+ Manager.prototype._regScriptMethods = function (mod, script) {
283
+ if (!script || typeof script !== 'object') {
284
+ return;
285
+ }
286
+ for (var key in script) {
287
+ var val = script[key];
288
+ if (typeof val === 'function') {
289
+ mod.setMethod(key, val);
290
+ }
291
+ }
292
+ }
293
+
294
+ // 向后兼容:infos 和 mods 通过 getter/setter 暴露
295
+ Object.defineProperty(Manager.prototype, 'infos', {
296
+ get: function () { return this._infos; },
297
+ set: function (val) { this._infos = val; }
298
+ });
299
+ Object.defineProperty(Manager.prototype, 'mods', {
300
+ get: function () { return this._mods; },
301
+ set: function (val) { this._mods = val; }
302
+ });
303
+
304
+ /**
305
+ * 从注册表数据直接创建模块实例(轻量版,0 IO)
306
+ * 已存在的模块跳过(不重复创建)
307
+ * @param {object} data 注册表数据
308
+ * @param {string} data.name 模块名称
309
+ * @param {string} data.config_file 配置文件路径
310
+ * @param {string} [data.type] 模块类型
311
+ * @returns {object|null} 创建的模块实例,已存在返回 null
312
+ */
313
+ Manager.prototype.registerModEx = function (data) {
314
+ if (!data || !data.name) {
315
+ return null;
316
+ }
317
+
318
+ // 去重检查
319
+ if (this.getMod(data.name)) {
320
+ return null;
276
321
  }
322
+
323
+ // 根据 type 选择实例化类
324
+ var mod_type = data.type || 'plugin';
325
+ var mod;
326
+ if (mod_type === 'manager') {
327
+ mod = new Manager(data);
328
+ } else {
329
+ mod = new Drive(data);
330
+ }
331
+
332
+ mod.config_file = data.config_file || '';
333
+ this._mods[data.name] = mod;
277
334
  return mod;
278
335
  };
279
336
 
280
337
  /**
281
- * 设置模块
282
- * @param {string} name 模块名
283
- * @param {object} mod 模块对象
284
- * @returns {object|null} 设置的模块对象
338
+ * 创建文件模块并注册
339
+ * @param {object} cfg 模块配置
340
+ * @param {string} [file] 目标文件路径,不传则按规则生成
341
+ * @returns {object} 模块实例
285
342
  */
286
- Manager.prototype.setMod = function (name, mod) {
287
- this.mods[name] = mod;
288
- return this.getMod(name);
343
+ Manager.prototype.create = function (cfg, file) {
344
+ if (!cfg || !cfg.name) {
345
+ throw new TypeError('模块配置必须包含 name 字段');
346
+ }
347
+
348
+ var new_cfg = { ...cfg };
349
+ var config_file = file || this._resolveConfigFile(new_cfg.name);
350
+ new_cfg.config_file = config_file;
351
+
352
+ // 创建配置文件到磁盘
353
+ Drive.prototype.createConfigFile(config_file, new_cfg);
354
+
355
+ return this.registerMod(config_file, new_cfg);
289
356
  };
290
357
 
291
358
  /**
292
- * 获取模块
293
- * @param {string} name 模块名
294
- * @returns {object|null} 模块对象,若模块则返回模块对象,否则返回null
359
+ * 构建模块信息对象
360
+ * @param {object} mod 模块实例
361
+ * @returns {object} 模块信息
362
+ * @private
295
363
  */
296
- Manager.prototype.getMod = function (name) {
297
- return this.mods[name];
364
+ // eslint-disable-next-line complexity
365
+ Manager.prototype._buildInfo = function (mod) {
366
+ var cfg = mod.config || {};
367
+ return {
368
+ name: cfg.name || '',
369
+ title: cfg.title || '',
370
+ main: cfg.main || '',
371
+ func_name: cfg.func_name || 'main',
372
+ scope: cfg.scope || 0,
373
+ sort: cfg.sort || 100,
374
+ mode: cfg.mode || 3,
375
+ exec_mode: cfg.exec_mode || 1,
376
+ dir: mod.getDir ? mod.getDir() : '',
377
+ config_file: mod.config_file || '',
378
+ script_file: mod._script || '',
379
+ // eslint-disable-next-line mm_eslint/variable-name
380
+ lifecycle_state: mod.lifecycle_state || 'created',
381
+ is_loaded: !!mod.is_loaded,
382
+ enabled: cfg.enabled !== false
383
+ };
298
384
  };
299
385
 
300
386
  /**
301
- * 获取所有模块
302
- * @returns {object[]} 模块对象数组
387
+ * 解析配置文件路径
388
+ * @param {string} name 模块名称
389
+ * @returns {string} 配置文件路径
390
+ * @private
303
391
  */
304
- Manager.prototype.getMods = function () {
305
- return this.mods;
392
+ Manager.prototype._resolveConfigFile = function (name) {
393
+ var base_dir = this.dir || process.cwd();
394
+ return path.join(base_dir, name, name + '.json');
306
395
  };
307
396
 
397
+ // ==================== 模块调用 ====================
398
+
308
399
  /**
309
- * 创建模块
310
- * @param {string} config_file 配置文件路径
311
- * @param {object} config 配置项
312
- * @param {object} script 模块对象
313
- * @returns {object} 模块对象
314
- */
315
- Manager.prototype.newMod = function (config_file, config, script) {
316
- let mod;
317
- switch (this.config.mod_type) {
318
- case 'script':
319
- mod = new Mod(config, this);
400
+ * 调用模块方法
401
+ * @param {string} name 模块名称,传 null 遍历所有已启用模块
402
+ * @param {string} [method] 方法名,默认 'main'
403
+ * @param {*} [params] 参数
404
+ * @returns {Promise<*|Array>} 执行结果
405
+ */
406
+ Manager.prototype.main = async function (name, method, params) {
407
+ var method_name = method || 'main';
408
+
409
+ if (name) {
410
+ // 指定模块
411
+ var mod = this.getMod(name);
412
+ if (!mod) {
413
+ return null;
414
+ }
415
+ return this._runMod(mod, method_name, params);
416
+ }
417
+
418
+ // 遍历所有已启用模块
419
+ var results = [];
420
+ var mods = this._getSortedMods();
421
+ for (var i = 0; i < mods.length; i++) {
422
+ var mod = mods[i];
423
+ var cfg = mod.config || {};
424
+ if (cfg.enabled === false) {
425
+ continue;
426
+ }
427
+ try {
428
+ var result = await this._runMod(mod, method_name, params);
429
+ results.push({ name: mod.config.name, result: result });
430
+ } catch (err) {
431
+ results.push({ name: mod.config.name, error: err });
432
+ }
433
+ }
434
+ return results;
435
+ };
436
+
437
+ /**
438
+ * 运行模块方法(自动推进生命周期)
439
+ * @param {object} mod 模块实例
440
+ * @param {string} method 方法名
441
+ * @param {*} params 参数
442
+ * @returns {Promise<*>} 执行结果
443
+ * @private
444
+ */
445
+ Manager.prototype._runMod = async function (mod, method, params) {
446
+ var state = mod.lifecycle_state;
447
+
448
+ try {
449
+ await this._advanceToStarted(mod, state);
450
+ } catch (err) {
451
+ // 生命周期推进失败,透传错误
452
+ throw err;
453
+ }
454
+
455
+ return mod.call(method, params);
456
+ };
457
+
458
+ /**
459
+ * 推进模块到 started 状态
460
+ * @param {object} mod 模块实例
461
+ * @param {string} state 当前状态
462
+ * @returns {Promise<void>}
463
+ * @private
464
+ */
465
+ Manager.prototype._advanceToStarted = async function (mod, state) {
466
+ var mod_name = mod.config.name || '';
467
+
468
+ switch (state) {
469
+ case 'created':
470
+ await mod.load();
471
+ await mod.init();
472
+ await mod.start();
320
473
  break;
321
- default:
322
- // 创建模块
323
- mod = new this.Drive(config, this);
474
+ case 'loaded':
475
+ await mod.init();
476
+ await mod.start();
477
+ break;
478
+ case 'inited':
479
+ await mod.start();
480
+ break;
481
+ case 'started':
324
482
  break;
483
+ case 'paused':
484
+ throw new Error('模块 [' + mod_name + '] 已暂停,无法执行');
485
+ case 'ended':
486
+ await mod.load();
487
+ await mod.init();
488
+ await mod.start();
489
+ break;
490
+ case 'destroyed':
491
+ throw new Error('模块 [' + mod_name + '] 已销毁,无法执行');
492
+ default:
493
+ throw new Error('模块 [' + mod_name + '] 状态异常');
325
494
  }
326
- mod.mode = this.config.mode;
327
- mod.loadConfig(config_file);
328
- if (script) {
329
- for (let key in script) {
330
- let obj = script[key];
331
- if (typeof obj == 'function') {
332
- mod.setMethod(key, obj);
333
- } else {
334
- mod.setData(key, obj);
335
- }
336
- }
495
+ };
496
+
497
+ /**
498
+ * 获取按 sort 排序的模块列表
499
+ * @returns {Array<object>} 排序后的模块数组
500
+ * @private
501
+ */
502
+ Manager.prototype._getSortedMods = function () {
503
+ var list = [];
504
+ for (var name in this._mods) {
505
+ list.push(this._mods[name]);
337
506
  }
338
- return mod;
507
+ list.sort((a, b) => {
508
+ var sort_a = (a.config && a.config.sort) || 100;
509
+ var sort_b = (b.config && b.config.sort) || 100;
510
+ return sort_a - sort_b;
511
+ });
512
+ return list;
339
513
  };
340
514
 
515
+ // ==================== 模块注销/删除 ====================
516
+
341
517
  /**
342
- * 注册模块
343
- * @param {string} config_file 配置文件路径
344
- * @param {object} config 配置项
345
- * @param {object} script 模块对象
346
- * @returns {object} 模块对象
518
+ * 注销模块
519
+ * end() 卸载,再 delMod 删除内存
520
+ * @param {string} name 模块名称
521
+ * @returns {object|null} 注销的模块实例
347
522
  */
348
- Manager.prototype.registerMod = function (config_file, config = {}, script = {}) {
349
- let mod = this.newMod(config_file, config, script);
350
- this.setMod(mod.config.name, mod);
523
+ Manager.prototype.unreMod = function (name) {
524
+ var mod = this.getMod(name);
525
+ if (!mod) {
526
+ return null;
527
+ }
528
+
529
+ try {
530
+ if (mod.lifecycle_state === 'started' || mod.lifecycle_state === 'paused') {
531
+ mod.end();
532
+ }
533
+ } catch {
534
+ // 忽略
535
+ }
536
+
537
+ this.delMod(name);
538
+ this._removeInfo(name);
351
539
  return mod;
352
540
  };
353
541
 
354
542
  /**
355
- * 加载模块
356
- * @param {string} name 模块名
357
- * @returns {Promise<any>} 加载结果
543
+ * 删除模块(先注销,再删除配置文件+脚本文件)
544
+ * @param {string} name 模块名称
358
545
  */
359
- Manager.prototype.loadMod = async function (name) {
360
- let mod = this.getMod(name);
361
- if (mod) {
362
- return await mod.do('load');
546
+ Manager.prototype.remove = function (name) {
547
+ var mod = this.unreMod(name);
548
+ if (!mod) {
549
+ return;
550
+ }
551
+
552
+ // 删除配置文件
553
+ try {
554
+ if (mod.config_file && require('fs').existsSync(mod.config_file)) {
555
+ require('fs').unlinkSync(mod.config_file);
556
+ }
557
+ } catch {
558
+ // 忽略
559
+ }
560
+
561
+ // 删除脚本文件
562
+ try {
563
+ if (mod._script && require('fs').existsSync(mod._script)) {
564
+ require('fs').unlinkSync(mod._script);
565
+ }
566
+ } catch {
567
+ // 忽略
363
568
  }
364
- return null;
365
569
  };
366
570
 
367
571
  /**
368
- * 卸载模块
369
- * @param {string} name 模块名
370
- * @returns {Promise<any>} 卸载结果
572
+ * 移除模块信息
573
+ * @param {string} name 模块名称
574
+ * @private
371
575
  */
372
- Manager.prototype.unloadMod = async function (name) {
373
- let mod = this.getMod(name);
374
- if (mod) {
375
- return await mod.do('unload');
576
+ Manager.prototype._removeInfo = function (name) {
577
+ for (var i = 0; i < this._infos.length; i++) {
578
+ if (this._infos[i].name === name) {
579
+ this._infos.splice(i, 1);
580
+ return;
581
+ }
376
582
  }
377
- return null;
378
583
  };
379
584
 
585
+ // ==================== 重载模块 ====================
586
+
380
587
  /**
381
588
  * 重载模块
382
- * @param {string} name 模块名
383
- * @returns {Promise<any>} 重载结果
589
+ * 清除缓存 重新执行生命周期到原状态
590
+ * @param {string} name 模块名称
591
+ * @returns {Promise<object>} 重载后的模块状态
384
592
  */
385
- Manager.prototype.reloadMod = async function (name) {
386
- let mod = this.getMod(name);
387
- if (mod) {
388
- return await mod.do('reload');
593
+ Manager.prototype.reload = async function (name) {
594
+ var mod = this.getMod(name);
595
+ if (!mod) {
596
+ throw new Error('模块 [' + name + '] 不存在');
389
597
  }
390
- return null;
598
+
599
+ var current_state = mod.lifecycle_state;
600
+ var priority = statePriority[current_state] !== undefined ? statePriority[current_state] : 0;
601
+
602
+ // 清理:优先级 >= 3 时先 end()
603
+ if (priority >= 3) {
604
+ try {
605
+ await mod.end();
606
+ } catch {
607
+ // 忽略
608
+ }
609
+ }
610
+
611
+ // 清除 require.cache
612
+ this._clearModCache(mod);
613
+
614
+ // 重新执行生命周期到原状态
615
+ try {
616
+ await this._restoreModState(mod, priority);
617
+ } catch (err) {
618
+ throw err;
619
+ }
620
+
621
+ return mod.lifecycle_state;
391
622
  };
392
623
 
393
624
  /**
394
- * 注销模块
395
- * @param {string} name 模块名
396
- * @returns {Promise<any>} 注销结果
625
+ * 清除模块的 require 缓存
626
+ * @param {object} mod 模块实例
627
+ * @private
397
628
  */
398
- Manager.prototype.unregisterMod = function (name) {
399
- // 卸载模块
400
- this.unloadMod(name);
401
- // 删除模块
402
- let mod = this.delMod(name);
403
- return mod;
629
+ Manager.prototype._clearModCache = function (mod) {
630
+ if (!mod._script) {
631
+ return;
632
+ }
633
+ try {
634
+ var resolved = require.resolve(mod._script);
635
+ if (require.cache[resolved]) {
636
+ delete require.cache[resolved];
637
+ }
638
+ } catch {
639
+ // 忽略
640
+ }
404
641
  };
405
642
 
406
643
  /**
407
- * 批量注册模块
408
- * @param {string[]} files 配置文件路径数组
409
- * @param {object} config 配置项
410
- * @param {object} script 模块对象
644
+ * 恢复模块到指定优先级状态
645
+ * @param {object} mod 模块实例
646
+ * @param {number} priority 目标优先级
647
+ * @returns {Promise<void>}
648
+ * @private
411
649
  */
412
- Manager.prototype.registerMods = function (files, config = {}, script = {}) {
413
- for (let file of files) {
414
- this.registerMod(file, config, script);
650
+ Manager.prototype._restoreModState = async function (mod, priority) {
651
+ if (priority >= 1) {
652
+ await mod.load();
653
+ }
654
+ if (priority >= 2) {
655
+ await mod.init();
656
+ }
657
+ if (priority >= 3) {
658
+ await mod.start();
659
+ }
660
+ if (priority >= 4) {
661
+ await mod.pause();
415
662
  }
416
663
  };
417
664
 
665
+ // ==================== 扫描注册 ====================
666
+
418
667
  /**
419
- * 加载所有模块
668
+ * 扫描目录并注册新模块
669
+ * @param {object} scanner DirScanner 实例
670
+ * @param {string[]} dirs 要扫描的目录列表
671
+ * @returns {Promise<number>} 成功注册的模块数量
420
672
  */
421
- Manager.prototype.loadMods = async function () {
422
- var promises = [];
423
- let mods = this.getMods();
424
- for (let name in mods) {
425
- let mod = mods[name];
426
- promises.push(mod.do('load'));
673
+ Manager.prototype.scanAndRegister = async function (scanner, dirs) {
674
+ if (!scanner || typeof scanner.scanAndParse !== 'function') {
675
+ throw new TypeError('scanner 必须包含 scanAndParse 方法');
427
676
  }
428
- await Promise.all(promises);
677
+ if (!dirs || !Array.isArray(dirs)) {
678
+ return 0;
679
+ }
680
+
681
+ var count = 0;
682
+
683
+ for (var i = 0; i < dirs.length; i++) {
684
+ count += this._scanDirModules(scanner, dirs[i]);
685
+ }
686
+
687
+ return count;
429
688
  };
430
689
 
431
690
  /**
432
- * 获取目录名
433
- * @returns {string} 目录名
691
+ * 扫描单个目录并注册模块
692
+ * @param {object} scanner DirScanner 实例
693
+ * @param {string} dir 目录路径
694
+ * @returns {number} 成功注册的模块数量
695
+ * @private
434
696
  */
435
- Manager.prototype.getDirName = function () {
436
- // 前缀,为当前类名
437
- let prefix = this.constructor.name.toLowerCase();
438
- // 后缀,为配置项名称
439
- let name = this.config.name.toLowerCase();
440
- // 目录名,为前缀加下划线加后缀
441
- return prefix + '_' + name;
697
+ Manager.prototype._scanDirModules = function (scanner, dir) {
698
+ var count = 0;
699
+
700
+ try {
701
+ var entries = scanner.scanAndParse(dir, '*.json');
702
+ for (var j = 0; j < entries.length; j++) {
703
+ var entry = entries[j];
704
+ var cfg = entry.config;
705
+ if (cfg && cfg.name && !this.getMod(cfg.name)) {
706
+ this.registerMod(entry.file, cfg);
707
+ count++;
708
+ }
709
+ }
710
+ } catch {
711
+ // 单个目录扫描失败不影响其他目录
712
+ }
713
+
714
+ return count;
442
715
  };
443
716
 
717
+ // ==================== 启动引导 ====================
718
+
444
719
  /**
445
- * 获取所有目录下的文件
446
- * @param {string} dir 目录路径
447
- * @returns {Array} 文件路径数组
448
- */
449
- Manager.prototype._getAllDirFiles = async function (dir) {
450
- var dirs = await $.dir.getAllAsync(dir, this.getDirName());
451
- var filename = this.config.filename;
452
- var results = await Promise.all(dirs.map((d) => $.file.getAllAsync(d, filename)));
453
- var files = [];
454
- for (var i = 0; i < results.length; i++) {
455
- if (results[i].length > 0) {
456
- files.push(...results[i]);
720
+ * 启动引导
721
+ * 外部入口,根据 use_registry 决定走注册表路径还是旧路径
722
+ * @returns {Promise<void>}
723
+ */
724
+ Manager.prototype.boot = async function () {
725
+ if (this.config.use_registry) {
726
+ await this._bootWithRegistry();
727
+ } else {
728
+ await this._bootWithoutRegistry();
729
+ }
730
+ };
731
+
732
+ /**
733
+ * 注册表路径启动引导
734
+ * @returns {Promise<void>}
735
+ * @private
736
+ */
737
+ Manager.prototype._bootWithRegistry = async function () {
738
+ if (!this.registry) {
739
+ this.registry = new Registry(this, {});
740
+ }
741
+ await this.registry.boot();
742
+
743
+ // 注册表为空时,扫描文件系统
744
+ if (this.registry.getEntryCount() === 0 && this.dir) {
745
+ await this._scanAndFillRegistry();
746
+ }
747
+ };
748
+
749
+ /**
750
+ * 旧路径启动引导(直接扫描目录)
751
+ * @returns {Promise<void>}
752
+ * @private
753
+ */
754
+ Manager.prototype._bootWithoutRegistry = async function () {
755
+ if (!this.dir) {
756
+ return;
757
+ }
758
+
759
+ var { DirScanner } = require('./dir_scanner');
760
+ var scanner = new DirScanner();
761
+ var entries = scanner.scanAndParse(this.dir, '*.json');
762
+
763
+ for (var i = 0; i < entries.length; i++) {
764
+ var entry = entries[i];
765
+ if (entry.config && entry.config.name) {
766
+ this.registerMod(entry.file, entry.config);
457
767
  }
458
768
  }
459
- return files;
769
+
770
+ // 启动自启动模块
771
+ await this._startAutoModules();
460
772
  };
461
773
 
462
774
  /**
463
- * 检索目录下所有文件
775
+ * 扫描文件系统并填充注册表
776
+ * @returns {Promise<void>}
464
777
  * @private
465
- * @param {string} dir 目录路径
466
- * @returns {Array} 文件路径数组
467
778
  */
468
- Manager.prototype._findFiles = async function (dir) {
469
- // 检查目录是否存在
470
- if (!dir) {
471
- this.log('error', '目录不能为空');
472
- return [];
779
+ Manager.prototype._scanAndFillRegistry = async function () {
780
+ var { DirScanner } = require('./dir_scanner');
781
+ var scanner = new DirScanner();
782
+ var entries = scanner.scanAndParse(this.dir, '*.json');
783
+ if (entries.length > 0) {
784
+ this.registry.registerAll(entries);
785
+ }
786
+ };
787
+
788
+ /**
789
+ * 启动所有自启动模块
790
+ * @returns {Promise<void>}
791
+ * @private
792
+ */
793
+ Manager.prototype._startAutoModules = async function () {
794
+ var mods = this._getSortedMods();
795
+ for (var j = 0; j < mods.length; j++) {
796
+ var mod = mods[j];
797
+ if (mod.config && mod.config.auto_start) {
798
+ try {
799
+ await mod.load();
800
+ await mod.init();
801
+ await mod.start();
802
+ } catch {
803
+ // 单个模块启动失败不影响其他模块
804
+ }
805
+ }
806
+ }
807
+ };
808
+
809
+ // ==================== 生命周期传播 ====================
810
+
811
+ /**
812
+ * 启动核心
813
+ * 先调用父类逻辑,再传播到子模块
814
+ * @returns {Promise<void>}
815
+ * @private
816
+ * @override
817
+ */
818
+ Manager.prototype._startCore = async function () {
819
+ if (this.lifecycle_state !== 'inited') {
820
+ if (this.lifecycle_state === 'started') {
821
+ return;
822
+ }
823
+ throw new Error('模块未初始化,无法启动');
824
+ }
825
+
826
+ this.emit('start:before');
827
+
828
+ try {
829
+ var start_hook = this.getMethod('_start');
830
+ if (start_hook) {
831
+ var result = start_hook(this);
832
+ if (result instanceof Promise) {
833
+ await result;
834
+ }
835
+ }
836
+
837
+ // 传播到子模块
838
+ await this._propToChildren('start');
839
+
840
+ this.lifecycle_state = 'started';
841
+ this.emit('start:success');
842
+ } catch (err) {
843
+ this.emit('start:error', err);
844
+ throw err;
845
+ }
846
+ };
847
+
848
+ /**
849
+ * 暂停核心
850
+ * 先调用父类逻辑,再传播到子模块
851
+ * @returns {Promise<void>}
852
+ * @private
853
+ * @override
854
+ */
855
+ Manager.prototype._pauseCore = async function () {
856
+ if (this.lifecycle_state !== 'started') {
857
+ throw new Error('模块未启动,无法暂停');
858
+ }
859
+
860
+ this.emit('pause:before');
861
+
862
+ try {
863
+ var pause_hook = this.getMethod('_pause');
864
+ if (pause_hook) {
865
+ pause_hook(this);
866
+ }
867
+
868
+ // 传播到子模块
869
+ this._propToChildren('pause');
870
+
871
+ this.lifecycle_state = 'paused';
872
+ this.emit('pause:success');
873
+ } catch (err) {
874
+ this.emit('pause:error', err);
875
+ throw err;
876
+ }
877
+ };
878
+
879
+ /**
880
+ * 恢复核心
881
+ * 先调用父类逻辑,再传播到子模块
882
+ * @returns {Promise<void>}
883
+ * @private
884
+ * @override
885
+ */
886
+ Manager.prototype._resumeCore = async function () {
887
+ if (this.lifecycle_state !== 'paused') {
888
+ throw new Error('模块未暂停,无法恢复');
473
889
  }
474
- // 检查目录是否存在
475
- if (!dir.hasDir()) {
476
- return [];
890
+
891
+ this.emit('resume:before');
892
+
893
+ try {
894
+ var resume_hook = this.getMethod('_resume');
895
+ if (resume_hook) {
896
+ resume_hook(this);
897
+ }
898
+
899
+ // 传播到子模块
900
+ await this._propToChildren('resume');
901
+
902
+ this.lifecycle_state = 'started';
903
+ this.emit('resume:success');
904
+ } catch (err) {
905
+ this.emit('resume:error', err);
906
+ throw err;
477
907
  }
478
- if (this.config.search_way === 'dir') {
479
- return await this._getAllDirFiles(dir);
908
+ };
909
+
910
+ /**
911
+ * 结束核心
912
+ * 先调用父类逻辑,再传播到子模块
913
+ * @returns {Promise<void>}
914
+ * @private
915
+ * @override
916
+ */
917
+ Manager.prototype._endCore = async function () {
918
+ if (this.lifecycle_state !== 'started' && this.lifecycle_state !== 'paused') {
919
+ throw new Error('模块未启动或暂停,无法结束');
920
+ }
921
+
922
+ this.emit('end:before');
923
+
924
+ try {
925
+ var end_hook = this.getMethod('_end');
926
+ if (end_hook) {
927
+ end_hook(this);
928
+ }
929
+
930
+ // 传播到子模块
931
+ this._propToChildren('end');
932
+
933
+ // 卸载脚本
934
+ this.unloadScript();
935
+ this.lifecycle_state = 'ended';
936
+ this.emit('end:success');
937
+ } catch (err) {
938
+ this.emit('end:error', err);
939
+ throw err;
940
+ }
941
+ };
942
+
943
+ /**
944
+ * 销毁核心
945
+ * 先调用父类逻辑,再传播到子模块
946
+ * @returns {Promise<void>}
947
+ * @private
948
+ * @override
949
+ */
950
+ Manager.prototype._destroyCore = async function () {
951
+ if (this.lifecycle_state !== 'ended') {
952
+ throw new Error('模块未结束,无法销毁');
953
+ }
954
+
955
+ this.emit('destroy:before');
956
+
957
+ try {
958
+ var destroy_hook = this.getMethod('_onDestroy');
959
+ if (destroy_hook) {
960
+ destroy_hook(this);
961
+ }
962
+
963
+ // 传播到子模块
964
+ this._propToChildren('destroy');
965
+
966
+ this._methods = {};
967
+ this.lifecycle_state = 'destroyed';
968
+ this.emit('destroy:success');
969
+ } catch (err) {
970
+ this.emit('destroy:error', err);
971
+ throw err;
972
+ }
973
+ };
974
+
975
+ /**
976
+ * 传播生命周期操作到子模块
977
+ * 最多 3 层深度,异常隔离
978
+ * @param {string} action 操作名称(start/pause/resume/end/destroy)
979
+ * @param {number} [cur_depth] 当前深度
980
+ * @returns {Promise<void>}
981
+ * @private
982
+ */
983
+ Manager.prototype._propToChildren = async function (action, cur_depth) {
984
+ var depth = cur_depth || 0;
985
+
986
+ // 3 层深度限制
987
+ if (depth >= 3) {
988
+ return;
989
+ }
990
+
991
+ var method_map = {
992
+ start: 'start',
993
+ pause: 'pause',
994
+ resume: 'resume',
995
+ end: 'end',
996
+ destroy: 'destroy'
997
+ };
998
+
999
+ var method_name = method_map[action];
1000
+ if (!method_name) {
1001
+ return;
1002
+ }
1003
+
1004
+ for (var name in this._mods) {
1005
+ var mod = this._mods[name];
1006
+ if (typeof mod[method_name] !== 'function') {
1007
+ continue;
1008
+ }
1009
+
1010
+ try {
1011
+ await mod[method_name]();
1012
+ } catch {
1013
+ // 单个模块异常不影响其他模块
1014
+ }
480
1015
  }
481
- // 检索目录下所有文件
482
- return await $.file.getAllAsync(dir, this.config.filename);
483
1016
  };
484
1017
 
1018
+ // ==================== 基础API兼容(旧接口) ====================
1019
+
1020
+ /**
1021
+ * 默认驱动类
1022
+ */
1023
+ Manager.prototype.Drive = Drive;
1024
+
485
1025
  /**
486
- * 获取快照文件路径
487
- * 默认统一放到 /cache/snapshot/ 目录,文件名由类名+配置名组成
488
- * @returns {string} 快照文件完整路径
1026
+ * 创建模块信息对象
1027
+ * 兼容旧接口 newInfo(name, sort, state, file)
1028
+ * @param {string} name 模块名
1029
+ * @param {number} sort 排序值
1030
+ * @param {boolean} state 状态
1031
+ * @param {string} [file] 文件路径
1032
+ * @returns {object} 模块信息对象
489
1033
  */
490
- Manager.prototype.getSnapshotPath = function () {
491
- var snapshot_file = this.config.snapshot_file;
492
- if (!snapshot_file) {
493
- var class_name = this.constructor.name.toLowerCase();
494
- var name = this.config.name || 'default';
495
- snapshot_file = `./cache/snapshot/${class_name}_${name}.json`;
496
- // 确保目录存在
497
- snapshot_file.addDir();
1034
+ Manager.prototype.newInfo = function (name, sort, state, file) {
1035
+ return {
1036
+ name: name,
1037
+ sort: sort,
1038
+ state: state,
1039
+ enabled: true,
1040
+ file: file || '',
1041
+ get available() {
1042
+ return this.state == true && this.enabled;
1043
+ }
1044
+ };
1045
+ };
1046
+
1047
+ /**
1048
+ * 更新模块信息列表
1049
+ * 从 _mods 重新构建 _infos
1050
+ */
1051
+ Manager.prototype.updateInfo = function () {
1052
+ this._infos = [];
1053
+ for (var name in this._mods) {
1054
+ var o = this._mods[name];
1055
+ var info = this.newInfo(name, o.config.sort, o.config.state, o.config_file);
1056
+ this._infos.push(info);
498
1057
  }
499
- return snapshot_file.fullname();
1058
+ this._sort();
1059
+ };
1060
+
1061
+ /**
1062
+ * 内部排序
1063
+ * 按 sort_key 排序 _infos
1064
+ * @private
1065
+ */
1066
+ Manager.prototype._sort = function () {
1067
+ var sort_key = this.config.sort_key || 'sort';
1068
+ this._infos.sort((o1, o2) => {
1069
+ var p1 = o1[sort_key] || 100;
1070
+ var p2 = o2[sort_key] || 100;
1071
+ return p1 - p2;
1072
+ });
1073
+ };
1074
+
1075
+ /**
1076
+ * 排序
1077
+ */
1078
+ Manager.prototype.sort = function () {
1079
+ this._sort();
1080
+ };
1081
+
1082
+ /**
1083
+ * 获取基础目录
1084
+ * @returns {string} 基础目录
1085
+ */
1086
+ Manager.prototype.getBaseDir = function () {
1087
+ return this.config.base_dir || '';
1088
+ };
1089
+
1090
+ /**
1091
+ * 获取模板目录
1092
+ * @returns {string} 模板目录
1093
+ */
1094
+ Manager.prototype.getTplDir = function () {
1095
+ return this.config.tpl_dir || '';
1096
+ };
1097
+
1098
+ /**
1099
+ * 获取目录名
1100
+ * 前缀为当前类名小写,后缀为配置项名称
1101
+ * @returns {string} 目录名
1102
+ */
1103
+ Manager.prototype.getDirName = function () {
1104
+ var prefix = this.constructor.name.toLowerCase();
1105
+ var name = this.config.name.toLowerCase();
1106
+ return prefix + '_' + name;
500
1107
  };
501
1108
 
1109
+ // ==================== 模块生命周期管理 ====================
1110
+
502
1111
  /**
503
- * 保存快照数据
504
- * @param {object} mods_map 目录到模块条目的映射 { dir: [{ file, config }, ...] }
1112
+ * 加载模块
1113
+ * @param {string} name 模块名
1114
+ * @returns {Promise<*>} 加载结果
505
1115
  */
506
- Manager.prototype._saveSnapshot = function (mods_map) {
507
- try {
508
- var snapshot_path = this.getSnapshotPath();
509
- var snapshot = {
510
- mods_map: mods_map,
511
- updated_at: Date.now()
512
- };
513
- snapshot_path.saveJson(snapshot);
514
- } catch (err) {
515
- this.log('error', '保存快照失败', err);
1116
+ Manager.prototype.loadMod = async function (name) {
1117
+ var mod = this.getMod(name);
1118
+ if (mod) {
1119
+ return await mod.load();
516
1120
  }
1121
+ return null;
517
1122
  };
518
1123
 
519
1124
  /**
520
- * 从当前已加载模块刷新快照
521
- * 确保快照与内存中模块状态一致
1125
+ * 卸载模块(旧接口 unloadMod -> end)
1126
+ * @param {string} name 模块名
1127
+ * @returns {Promise<*>} 卸载结果
522
1128
  */
523
- Manager.prototype.refreshSnapshot = function () {
524
- if (!this.config.use_snapshot) {
525
- return;
526
- }
527
- var mods_map = {};
528
- for (var name in this.mods) {
529
- var mod = this.mods[name];
530
- var f = mod.config_file;
531
- if (!f) {
532
- continue;
533
- }
534
- var dir = this._findSnapshotDir(f);
535
- if (!mods_map[dir]) {
536
- mods_map[dir] = [];
1129
+ Manager.prototype.unloadMod = async function (name) {
1130
+ var mod = this.getMod(name);
1131
+ if (mod) {
1132
+ try {
1133
+ if (mod.lifecycle_state === 'started' || mod.lifecycle_state === 'paused') {
1134
+ await mod.end();
1135
+ }
1136
+ } catch {
1137
+ // 忽略
537
1138
  }
538
- mods_map[dir].push({ file: f, config: mod.config });
1139
+ return mod;
539
1140
  }
540
- this._saveSnapshot(mods_map);
1141
+ return null;
541
1142
  };
542
1143
 
543
1144
  /**
544
- * 推断配置文件所属的快照检索目录
545
- * @param {string} file 配置文件路径
546
- * @returns {string} 检索目录
1145
+ * 重载模块(旧接口 reloadMod)
1146
+ * @param {string} name 模块名
1147
+ * @returns {Promise<*>} 重载结果
547
1148
  */
548
- Manager.prototype._findSnapshotDir = function (file) {
549
- var base_dir = this.getBaseDir();
550
- if (base_dir && file.startsWith(base_dir)) {
551
- return base_dir;
552
- }
553
- var dir = this.getDir();
554
- if (dir && file.startsWith(dir)) {
555
- return dir;
1149
+ Manager.prototype.reloadMod = async function (name) {
1150
+ var mod = this.getMod(name);
1151
+ if (mod) {
1152
+ return await mod.reload();
556
1153
  }
557
- return file.dirname();
1154
+ return null;
558
1155
  };
559
1156
 
560
1157
  /**
561
- * 加载快照数据
562
- * @returns {object|null} 快照数据,不存在时返回null
1158
+ * 注销模块
1159
+ * 兼容旧接口 unregisterMod
1160
+ * @param {string} name 模块名称
1161
+ * @returns {object|null} 注销的模块实例
563
1162
  */
564
- Manager.prototype._loadSnapshot = function () {
565
- try {
566
- var snapshot_path = this.getSnapshotPath();
567
- if (!snapshot_path.hasFile()) {
568
- return null;
569
- }
570
- var snapshot = snapshot_path.loadJson();
571
- if (!snapshot || !snapshot.mods_map) {
572
- return null;
573
- }
574
- return snapshot;
575
- } catch (err) {
576
- this.log('error', '加载快照失败', err);
577
- return null;
578
- }
1163
+ Manager.prototype.unregisterMod = function (name) {
1164
+ // 先卸载
1165
+ this.unloadMod(name);
1166
+ // 再删除
1167
+ var mod = this.delMod(name);
1168
+ this._removeInfo(name);
1169
+ return mod;
579
1170
  };
580
1171
 
581
1172
  /**
582
- * 从快照快速加载模块,不进行文件系统扫描和磁盘配置读取
583
- * 使用快照中的config直接创建模块,跳过loadConfig的IO
584
- * @param {object} mods_map 目录到模块条目的映射 { dir: [{ file, config }, ...] }
585
- * @param {boolean} clear 是否清除已有模块
1173
+ * 批量注册模块
1174
+ * @param {string[]} files 配置文件路径数组
1175
+ * @param {object} [cfg] 配置对象
1176
+ * @param {object} [script] 脚本对象
586
1177
  */
587
- Manager.prototype._loadFromSnapshot = function (mods_map, clear = true) {
588
- if (clear) {
589
- this.clearMods();
1178
+ Manager.prototype.registerMods = function (files, cfg, script) {
1179
+ if (!files || !Array.isArray(files)) {
1180
+ return;
590
1181
  }
591
- for (var dir in mods_map) {
592
- var entries = mods_map[dir];
593
- if (!entries || entries.length === 0) {
594
- continue;
595
- }
596
- for (var i = 0; i < entries.length; i++) {
597
- var entry = entries[i];
598
- var f = entry.file;
599
- // 跳过已不存在的文件,等待后台同步处理
600
- if (!f.hasFile || !f.hasFile()) {
601
- continue;
602
- }
603
- this._newModFromSnap(f, entry.config);
604
- }
1182
+ for (var i = 0; i < files.length; i++) {
1183
+ this.registerMod(files[i], cfg, script);
605
1184
  }
606
- this.updateInfo();
607
- this.is_loaded = true;
608
1185
  };
609
1186
 
610
1187
  /**
611
- * 用快照config创建模块,跳过磁盘读取
612
- * @param {string} file 配置文件路径
613
- * @param {object} config 模块配置
614
- * @returns {object} 模块对象
1188
+ * 加载所有模块的脚本
615
1189
  */
616
- Manager.prototype._newModFromSnap = function (file, config) {
617
- var mod;
618
- if (this.config.mod_type === 'script') {
619
- mod = new Mod(config || {}, this);
620
- } else {
621
- mod = new this.Drive(config || {}, this);
622
- }
623
- mod.mode = this.config.mode;
624
- mod.config_file = file;
625
- if (mod._preset) {
626
- mod._preset();
1190
+ Manager.prototype.loadScripts = async function () {
1191
+ var promises = [];
1192
+ for (var name in this._mods) {
1193
+ var mod = this._mods[name];
1194
+ if (typeof mod.loadScript === 'function') {
1195
+ promises.push(mod.loadScript());
1196
+ }
627
1197
  }
628
- return this.setMod(mod.config.name, mod);
1198
+ await Promise.all(promises);
629
1199
  };
630
1200
 
631
1201
  /**
632
- * 后台增量同步:扫描文件系统并与快照进行diff,增减模块
633
- * 使用锁防止多次重入冲突
634
- * @param {object} snapshot 快照数据
1202
+ * 加载所有模块
635
1203
  */
636
- Manager.prototype._syncFromFile = async function (snapshot) {
637
- // 防重入锁,避免多次同步同时执行
638
- if (this._syncing) {
639
- return;
640
- }
641
- this._syncing = true;
642
- try {
643
- var old_dirs = snapshot.mods_map || {};
644
- var old = this._buildSet(old_dirs);
645
-
646
- // 扫描目录,对所有文件重新注册(含快照已有模块,补全热更新监听和脚本加载)
647
- for (var dir in old_dirs) {
648
- var current = await this._findFiles(dir);
649
- for (var fi = 0; fi < current.length; fi++) {
650
- var f = current[fi];
651
- this.registerMod(f);
652
- old[f] = false;
653
- }
654
- }
655
-
656
- // 移除已删除的模块
657
- await this._removeStale(old);
658
- // 加载所有模块脚本(lazy_load时不阻塞但后台仍加载)
659
- if (!this.config.lazy_load) {
660
- await this.loadScripts();
661
- }
662
- this.updateInfo();
663
- await this._saveSnapshotDirs(Object.keys(old_dirs));
664
- // this.log('info', '快照同步完成');
665
- } catch (err) {
666
- this.log('error', '快照增量同步失败', err);
667
- } finally {
668
- this._syncing = false;
1204
+ Manager.prototype.loadMods = async function () {
1205
+ var promises = [];
1206
+ for (var name in this._mods) {
1207
+ var mod = this._mods[name];
1208
+ promises.push(mod.load());
669
1209
  }
1210
+ await Promise.all(promises);
670
1211
  };
671
1212
 
672
1213
  /**
673
1214
  * 构建文件集合
674
- * @param {object} dirs 目录到模块条目的映射 { dir: [{ file, config }, ...] }
1215
+ * @param {object} dirs 目录到模块条目的映射
675
1216
  * @returns {object} 文件集合 { file: true }
1217
+ * @private
676
1218
  */
677
1219
  Manager.prototype._buildSet = function (dirs) {
678
1220
  var set = {};
@@ -686,30 +1228,14 @@ Manager.prototype._buildSet = function (dirs) {
686
1228
  };
687
1229
 
688
1230
  /**
689
- * 移除已删除的模块
690
- * @param {object} old 旧文件集合,标记为false表示仍存在
691
- */
692
- Manager.prototype._removeStale = async function (old) {
693
- for (var file in old) {
694
- if (old[file] === true) {
695
- var mod = this.getMod(file);
696
- if (mod) {
697
- await this.unloadMod(mod.config.name);
698
- this.delMod(mod.config.name);
699
- }
700
- }
701
- }
702
- };
703
-
704
- /**
705
- * 构建文件到config的映射
706
- * @returns {object} 文件到config的映射 { file_path: config }
1231
+ * 构建配置映射
1232
+ * @returns {object} 文件到配置的映射
1233
+ * @private
707
1234
  */
708
1235
  Manager.prototype._buildConfigMap = function () {
709
1236
  var map = {};
710
- var mods = this.getMods();
711
- for (var name in mods) {
712
- var mod = mods[name];
1237
+ for (var name in this._mods) {
1238
+ var mod = this._mods[name];
713
1239
  if (mod.config_file) {
714
1240
  map[mod.config_file] = mod.config;
715
1241
  }
@@ -720,8 +1246,9 @@ Manager.prototype._buildConfigMap = function () {
720
1246
  /**
721
1247
  * 构建模块条目列表
722
1248
  * @param {string[]} files 文件路径数组
723
- * @param {object} config_dict 文件到config的映射
724
- * @returns {object[]} 模块条目数组 [{ file, config }]
1249
+ * @param {object} config_dict 文件到配置的映射
1250
+ * @returns {Array<{file: string, config: object}>} 模块条目数组
1251
+ * @private
725
1252
  */
726
1253
  Manager.prototype._buildEntries = function (files, config_dict) {
727
1254
  var entries = [];
@@ -736,334 +1263,129 @@ Manager.prototype._buildEntries = function (files, config_dict) {
736
1263
  };
737
1264
 
738
1265
  /**
739
- * 保存快照目录(含config)
740
- * @param {string[]} dirs 目录列表
741
- */
742
- Manager.prototype._saveSnapshotDirs = async function (dirs) {
743
- var new_map = {};
744
- var config_dict = this._buildConfigMap();
745
- for (var i = 0; i < dirs.length; i++) {
746
- var dir = dirs[i];
747
- var files = await this._findFiles(dir);
748
- new_map[dir] = this._buildEntries(files, config_dict);
749
- }
750
- this._saveSnapshot(new_map);
751
- };
752
-
753
- /**
754
- * 加载所有模块的脚本
755
- */
756
- Manager.prototype.loadScripts = async function () {
757
- var promises = [];
758
- let mods = this.getMods();
759
- for (let name in mods) {
760
- let mod = mods[name];
761
- promises.push(mod.call('loadScript'));
762
- }
763
- await Promise.all(promises);
764
- };
765
-
766
- /**
767
- * 更新配置前的钩子函数
1266
+ * 更新前的钩子
768
1267
  */
769
1268
  Manager.prototype.updateBefore = function () {
770
- // this.log('info', '更新配置前的钩子函数');
1269
+ // 空实现,子类可重写
771
1270
  };
772
1271
 
773
1272
  /**
774
- * 更新配置前的钩子函数
1273
+ * 更新后的钩子
775
1274
  */
776
1275
  Manager.prototype.updateAfter = function () {
777
- // this.log('info', '更新配置后钩子函数');
1276
+ // 空实现,子类可重写
778
1277
  };
779
1278
 
780
1279
  /**
781
1280
  * 更新配置
782
- * @param {string} dir 检索的路径
783
- * @param {boolean} clear 是否清除缓存
1281
+ * @param {string} dir 检索的目录
1282
+ * @param {boolean} [clear] 是否清除缓存
784
1283
  */
785
- Manager.prototype.update = async function (dir, clear = true) {
786
- if (clear) {
1284
+ Manager.prototype.update = async function (dir, clear) {
1285
+ var do_clear = clear !== false;
1286
+ if (do_clear) {
787
1287
  this.clearMods();
788
1288
  }
789
1289
 
790
- var search_dir = dir || this.config.dir;
791
- // 查找所有json文件
792
- var files = await this._findFiles(search_dir);
793
- // 批量注册模块
794
- this.registerMods(files);
795
- // 更新配置信息
796
- this.updateInfo();
797
- if (!this.config.lazy_load) {
798
- // 批量加载脚本
799
- await this.loadScripts();
800
- }
801
- this.is_loaded = true;
802
- };
803
-
804
- // ==== 高性能调用接口 ====
805
- /**
806
- * 调用模块的方法(高性能版本)
807
- * @param {string} name 模块名
808
- * @param {string} method 方法名
809
- * @param {...any} params 参数集合
810
- * @returns {Promise<any>} 执行结果
811
- */
812
- Manager.prototype.main = async function (name, method, ...params) {
813
- var result = null;
814
- if (name) {
815
- let mod = this.getMod(name);
816
- if (mod && mod.config?.state === 1) {
817
- result = await this._runMod(mod, method, ...params);
818
- }
819
- } else if (name === null) {
820
- result = await this._runAllEnabled(method, ...params);
821
- }
822
- return result;
823
- };
824
-
825
- /**
826
- * 私有方法:执行所有模块方法
827
- * @param {string} method 方法名称
828
- * @param {...any} params 方法参数集合
829
- * @returns {Promise<any>} 执行结果
830
- * @private
831
- */
832
- Manager.prototype._runAllEnabled = async function (method, ...params) {
833
- var result;
834
- for (let i = 0; i < this.infos.length; i++) {
835
- let config = this.infos[i];
836
- let mod = this.getMod(config.name);
837
- if (mod && mod.config?.state === 1) {
838
- var ret = null;
839
- try {
840
- ret = await this._runMod(mod, method, ...params);
841
- } catch (err) {
842
- $.log.error(`执行模块方法失败: `, err);
843
- }
844
- if (ret !== null && ret !== undefined) {
845
- result = ret;
846
- // 如果结果不为空且有结束标志,则停止迭代
847
- if (mod.config?.end) {
848
- break;
849
- }
850
- }
851
- }
852
- }
853
- return result;
854
- };
855
-
856
- /**
857
- * 私有方法:执行模块方法
858
- * @param {object} mod 模块对象
859
- * @param {string} method 方法名称
860
- * @param {...any} params 参数集合
861
- * @returns {Promise<any>} 执行结果
862
- * @private
863
- */
864
- Manager.prototype._runMod = async function (mod, method, ...params) {
865
- if (!mod || !method) return null;
866
-
867
- // 确保模块已加载
868
- if (!mod.isLoaded()) {
869
- try {
870
- await mod.call('loadScript');
871
- } catch (err) {
872
- $.log.error(`加载模块脚本失败: `, err);
873
- }
874
- }
875
-
876
- // 执行方法
877
- let ret = await mod.call(method, ...params);
878
- // 根据模式决定是否重载
879
- if (this.mode >= 4) {
880
- mod.do('reload');
1290
+ var search_dir = dir || this.dir;
1291
+ if (!search_dir) {
1292
+ return;
881
1293
  }
882
1294
 
883
- return ret;
884
- };
885
-
886
- /**
887
- * 初始化管理器
888
- */
889
- Manager.prototype._initManager = function () { };
890
-
891
- /**
892
- * 获取公共模块目录
893
- * @returns {string} 公共模块目录
894
- */
895
- Manager.prototype.getBaseDir = function () {
896
- return this.config.base_dir;
897
- };
898
-
899
- /**
900
- * 获取二次开发模块目录
901
- * @returns {string} 二次开发模块目录
902
- */
903
- Manager.prototype.getDir = function () {
904
- return this.config.dir || this.getDir();
905
- };
906
-
907
- /**
908
- * 加载资源
909
- */
910
- Manager.prototype._loadSources = async function () {
911
- var base_dir = this.getBaseDir();
912
- var dir = this.getDir();
1295
+ // 查找所有 json 文件
1296
+ var { DirScanner } = require('./dir_scanner');
1297
+ var scanner = new DirScanner();
1298
+ var entries = scanner.scanAndParse(search_dir, '*.json');
913
1299
 
914
- // 启用快照加速时,优先从快照加载
915
- if (this.config.use_snapshot) {
916
- var snapshot = this._loadSnapshot();
917
- if (snapshot) {
918
- // 快照存在,用config快速加载模块
919
- this._loadFromSnapshot(snapshot.mods_map);
920
- // this.log('info', '已从快照快速加载模块,正在后台同步文件变更...');
921
- // 同步等待文件同步完成,确保模块已全部注册后再执行初始化,防止竞态条件
922
- await this._syncFromFile(snapshot);
923
- return;
1300
+ for (var i = 0; i < entries.length; i++) {
1301
+ var entry = entries[i];
1302
+ if (entry.config && entry.config.name) {
1303
+ this.registerMod(entry.file, entry.config);
924
1304
  }
925
1305
  }
926
1306
 
927
- // 无快照时走正常流程
928
- if (base_dir) {
929
- await this.call('update', base_dir, true);
930
- await this.call('update', dir, false);
931
- } else {
932
- await this.call('update', dir, true);
933
- }
934
-
935
- // 首次扫描后保存快照(含config)
936
- if (this.config.use_snapshot) {
937
- var dirs = [];
938
- if (base_dir) {
939
- dirs.push(base_dir);
940
- }
941
- if (dir) {
942
- dirs.push(dir);
943
- }
944
- await this._saveSnapshotDirs(dirs);
1307
+ this.updateInfo();
1308
+ if (!this.config.lazy_load) {
1309
+ await this.loadScripts();
945
1310
  }
1311
+ this.is_loaded = true;
946
1312
  };
947
1313
 
948
- /**
949
- * 初始化资源
950
- */
951
- Manager.prototype._initSources = async function () {
952
- // 通过
953
- };
954
-
955
- /**
956
- * 初始化公共属性
957
- */
958
- Manager.prototype._initCore = async function () {
959
- // 初始化管理器
960
- this._initManager();
961
- // 加载资源
962
- await this._loadSources();
963
- // 初始化AI客户端
964
- await this._initSources();
965
- };
1314
+ // ==================== 执行方法 ====================
966
1315
 
967
1316
  /**
968
- * 执行方法 - 内部方法
969
- * @param {string} mod 模块对象
1317
+ * 执行模块方法
1318
+ * @param {object} mod 模块对象
970
1319
  * @param {string} method 方法名称
971
1320
  * @param {...any} params 参数列表
972
- * @returns {any} 返回执行结果
1321
+ * @returns {Promise<*>} 执行结果
1322
+ * @private
973
1323
  */
974
- Manager.prototype._exec = async function (mod, method, ...params) {
1324
+ Manager.prototype._exec = async function (mod, method) {
1325
+ var params = Array.prototype.slice.call(arguments, 2);
975
1326
  if (mod[method]) {
976
- return await mod[method](...params);
1327
+ return await mod[method].apply(mod, params);
977
1328
  } else {
978
- throw new Error(`方法${method}不存在`);
1329
+ throw new Error('方法' + method + '不存在');
979
1330
  }
980
1331
  };
981
1332
 
982
1333
  /**
983
- * 执行方法
1334
+ * 执行模块方法
984
1335
  * @param {string} name 模块名
985
1336
  * @param {string} method 方法名称
986
1337
  * @param {...any} params 参数列表
987
- * @returns {any} 返回执行结果
1338
+ * @returns {Promise<*>} 执行结果
988
1339
  */
989
- Manager.prototype.exec = async function (name, method, ...params) {
990
- let ret;
1340
+ Manager.prototype.exec = async function (name, method) {
1341
+ var params = Array.prototype.slice.call(arguments, 2);
991
1342
  try {
992
- let mod = this.getMod(name);
1343
+ var mod = this.getMod(name);
993
1344
  if (!mod) {
994
- throw new Error(`模块${name}不存在`);
1345
+ throw new Error('模块' + name + '不存在');
995
1346
  }
996
- ret = await this._exec(mod, method, ...params);
1347
+ return await this._exec.apply(this, [mod, method].concat(params));
997
1348
  } catch (error) {
998
- this.log('error', `${method}方法执行失败!`, error);
999
- }
1000
- return ret;
1001
- };
1002
-
1003
- /**
1004
- * 确保所有模块都已加载
1005
- * @returns {Promise} 加载完成
1006
- */
1007
- Manager.prototype._ensureAllLoaded = async function () {
1008
- var infos = this.getInfos();
1009
- var promises = [];
1010
-
1011
- for (var i = 0; i < infos.length; i++) {
1012
- var mod = this.getMod(infos[i].name);
1013
- if (mod && !mod.isLoaded()) {
1014
- ((info) => {
1015
- promises.push(
1016
- mod.call('loadScript').catch((err) => {
1017
- this.log('error', `模块${info.name}加载失败`, err);
1018
- })
1019
- );
1020
- })(infos[i]);
1021
- }
1022
- }
1023
- if (promises.length > 0) {
1024
- await Promise.all(promises);
1349
+ this.log('error', method + '方法执行失败!', error);
1350
+ return null;
1025
1351
  }
1026
1352
  };
1027
1353
 
1028
1354
  /**
1029
1355
  * 异步触发事件,按顺序执行等待完成
1030
1356
  * @param {string} method 方法名称
1031
- * @param {...any} params 事件参数
1032
- * @returns {Promise} 事件触发结果
1357
+ * @param {...any} params 事件参数
1358
+ * @returns {Promise<*>} 事件触发结果
1033
1359
  */
1034
- Manager.prototype.runWait = async function (method, ...params) {
1035
- // 参数校验
1360
+ Manager.prototype.runWait = async function (method) {
1036
1361
  if (typeof method !== 'string') {
1037
1362
  throw new TypeError('方法名称必须是字符串');
1038
1363
  }
1364
+ var params = Array.prototype.slice.call(arguments, 1);
1365
+ var result;
1366
+ var infos = this.getInfos();
1039
1367
 
1040
- let result;
1041
- let infos = this.getInfos();
1042
-
1043
- for (let i = 0; i < infos.length; i++) {
1044
- let info = infos[i];
1045
- let mod = this.getMod(info.name);
1368
+ for (var i = 0; i < infos.length; i++) {
1369
+ var info = infos[i];
1370
+ var mod = this.getMod(info.name);
1046
1371
  if (!mod) {
1047
- continue; // 跳过不存在的模块
1372
+ continue;
1048
1373
  }
1049
- // 确保模块已加载
1050
1374
  if (!mod.isLoaded()) {
1051
1375
  try {
1052
1376
  await mod.call('loadScript');
1053
1377
  } catch (error) {
1054
- this.log('error', `模块${info.name}加载失败`, error);
1055
- continue; // 跳过加载失败的模块
1378
+ this.log('error', '模块' + info.name + '加载失败', error);
1379
+ continue;
1056
1380
  }
1057
1381
  }
1058
-
1059
- // 执行方法
1060
1382
  try {
1061
- let ret = await mod.do(method, ...params);
1383
+ var ret = await mod.call.apply(mod, [method].concat(params));
1062
1384
  if (ret !== null && ret !== undefined) {
1063
1385
  result = ret;
1064
1386
  }
1065
1387
  } catch (error) {
1066
- this.log('error', `模块${info.name}执行${method}方法失败`, error);
1388
+ this.log('error', '模块' + info.name + '执行' + method + '方法失败', error);
1067
1389
  }
1068
1390
  }
1069
1391
 
@@ -1073,28 +1395,23 @@ Manager.prototype.runWait = async function (method, ...params) {
1073
1395
  /**
1074
1396
  * 异步触发事件,并发执行不等待
1075
1397
  * @param {string} method 方法名称
1076
- * @param {...any} params 事件参数
1398
+ * @param {...any} params 事件参数
1077
1399
  * @returns {boolean} 是否成功触发事件
1078
1400
  */
1079
- Manager.prototype.runAsync = async function (method, ...params) {
1080
- // 参数校验
1401
+ Manager.prototype.runAsync = async function (method) {
1081
1402
  if (typeof method !== 'string') {
1082
1403
  throw new TypeError('方法名称必须是字符串');
1083
1404
  }
1405
+ var params = Array.prototype.slice.call(arguments, 1);
1406
+ var infos = this.getInfos();
1084
1407
 
1085
- // 确保所有模块都已加载
1086
- await this._ensureAllLoaded();
1087
-
1088
- let infos = this.getInfos();
1089
-
1090
- // 并发执行所有方法,不等待结果
1091
- for (let i = 0; i < infos.length; i++) {
1092
- let info = infos[i];
1093
- let mod = this.getMod(info.name);
1408
+ for (var i = 0; i < infos.length; i++) {
1409
+ var info = infos[i];
1410
+ var mod = this.getMod(info.name);
1094
1411
  try {
1095
- mod.do(method, ...params);
1412
+ mod.call.apply(mod, [method].concat(params));
1096
1413
  } catch (error) {
1097
- this.log('error', `模块${info.name}执行${method}方法失败`, error);
1414
+ this.log('error', '模块' + info.name + '执行' + method + '方法失败', error);
1098
1415
  }
1099
1416
  }
1100
1417
 
@@ -1104,28 +1421,23 @@ Manager.prototype.runAsync = async function (method, ...params) {
1104
1421
  /**
1105
1422
  * 异步触发事件,并发执行等待完成
1106
1423
  * @param {string} method 方法名称
1107
- * @param {...any} params 事件参数
1108
- * @returns {Promise} 事件触发结果
1424
+ * @param {...any} params 事件参数
1425
+ * @returns {Promise<Array>} 事件触发结果数组
1109
1426
  */
1110
- Manager.prototype.runAll = async function (method, ...params) {
1111
- // 参数校验
1427
+ Manager.prototype.runAll = async function (method) {
1112
1428
  if (typeof method !== 'string') {
1113
1429
  throw new TypeError('方法名称必须是字符串');
1114
1430
  }
1431
+ var params = Array.prototype.slice.call(arguments, 1);
1432
+ var promises = [];
1433
+ var infos = this.getInfos();
1115
1434
 
1116
- // 确保所有模块都已加载
1117
- await this._ensureAllLoaded();
1118
-
1119
- const promises = [];
1120
- let infos = this.getInfos();
1121
-
1122
- // 并发执行所有方法
1123
- for (let i = 0; i < infos.length; i++) {
1124
- let info = infos[i];
1125
- let mod = this.getMod(info.name);
1435
+ for (var i = 0; i < infos.length; i++) {
1436
+ var info = infos[i];
1437
+ var mod = this.getMod(info.name);
1126
1438
  promises.push(
1127
- mod.do(method, ...params).catch((error) => {
1128
- return null; // 失败时返回null
1439
+ mod.call.apply(mod, [method].concat(params)).catch(() => {
1440
+ return null;
1129
1441
  })
1130
1442
  );
1131
1443
  }
@@ -1136,29 +1448,22 @@ Manager.prototype.runAll = async function (method, ...params) {
1136
1448
  /**
1137
1449
  * 异步触发事件,竞争执行
1138
1450
  * @param {string} method 方法名称
1139
- * @param {...any} params 事件参数
1140
- * @returns {Promise} 第一个完成的结果
1451
+ * @param {...any} params 事件参数
1452
+ * @returns {Promise<*>} 第一个完成的结果
1141
1453
  */
1142
- Manager.prototype.runRace = async function (method, ...params) {
1143
- // 参数校验
1454
+ Manager.prototype.runRace = async function (method) {
1144
1455
  if (typeof method !== 'string') {
1145
1456
  throw new TypeError('方法名称必须是字符串');
1146
1457
  }
1458
+ var params = Array.prototype.slice.call(arguments, 1);
1459
+ var promises = [];
1460
+ var infos = this.getInfos();
1147
1461
 
1148
- // 确保所有模块都已加载
1149
- await this._ensureAllLoaded();
1150
-
1151
- const promises = [];
1152
- let infos = this.getInfos();
1153
-
1154
- // 竞争执行所有方法
1155
- for (let i = 0; i < infos.length; i++) {
1156
- let info = infos[i];
1157
- let mod = this.getMod(info.name);
1462
+ for (var i = 0; i < infos.length; i++) {
1463
+ var info = infos[i];
1464
+ var mod = this.getMod(info.name);
1158
1465
  promises.push(
1159
- mod.do(method, ...params).catch((error) => {
1160
- throw error; // 竞争执行中,失败应该抛出错误
1161
- })
1466
+ mod.call.apply(mod, [method].concat(params))
1162
1467
  );
1163
1468
  }
1164
1469
 
@@ -1169,62 +1474,34 @@ Manager.prototype.runRace = async function (method, ...params) {
1169
1474
  * 异步触发事件,瀑布流执行
1170
1475
  * @param {string} method 方法名称
1171
1476
  * @param {*} result 初始值
1172
- * @param {...any} params 事件参数
1173
- * @returns {Promise} 最后一个事件监听者的结果
1477
+ * @param {...any} params 事件参数
1478
+ * @returns {Promise<*>} 最后一个事件监听者的结果
1174
1479
  */
1175
- Manager.prototype.runWaterfall = async function (method, result, ...params) {
1176
- // 参数校验
1480
+ Manager.prototype.runWaterfall = async function (method, result) {
1177
1481
  if (typeof method !== 'string') {
1178
1482
  throw new TypeError('方法名称必须是字符串');
1179
1483
  }
1484
+ var params = Array.prototype.slice.call(arguments, 2);
1485
+ var infos = this.getInfos();
1486
+ var final_result = result;
1180
1487
 
1181
- let infos = this.getInfos();
1182
-
1183
- for (let i = 0; i < infos.length; i++) {
1184
- let info = infos[i];
1185
- let mod = this.getMod(info.name);
1186
-
1187
- // 确保模块已加载
1188
- if (!mod.isLoaded()) {
1189
- try {
1190
- await mod.call('loadScript');
1191
- } catch (error) {
1192
- this.log('error', `模块${info.name}加载失败`, error);
1193
- continue; // 跳过加载失败的模块
1194
- }
1488
+ for (var i = 0; i < infos.length; i++) {
1489
+ var info = infos[i];
1490
+ var mod = this.getMod(info.name);
1491
+ if (!mod) {
1492
+ continue;
1195
1493
  }
1196
-
1197
- // 执行方法,将前一个结果作为参数传递给下一个
1198
1494
  try {
1199
- let ret = await mod.do(method, result, ...params);
1495
+ var ret = await mod.call.apply(mod, [method, final_result].concat(params));
1200
1496
  if (ret !== null && ret !== undefined) {
1201
- result = ret;
1497
+ final_result = ret;
1202
1498
  }
1203
1499
  } catch (error) {
1204
- this.log('error', `模块${info.name}执行${method}方法失败`, error);
1500
+ this.log('error', '模块' + info.name + '执行' + method + '方法失败', error);
1205
1501
  }
1206
1502
  }
1207
1503
 
1208
- return result;
1209
- };
1210
-
1211
- /**
1212
- * 异步触发事件,子模块执行
1213
- * @param {string} name 子模块名称
1214
- * @param {string} method 方法名称
1215
- * @param {...any} params 事件参数
1216
- * @returns {Promise} 事件触发结果
1217
- */
1218
- Manager.prototype.runSubMod = async function (name, method, ...params) {
1219
- // 参数校验
1220
- if (typeof method !== 'string') {
1221
- throw new TypeError('方法名称必须是字符串');
1222
- }
1223
- let mod = this.getMod(name);
1224
- if (!mod) {
1225
- throw new Error(`模块${name}不存在`);
1226
- }
1227
- return await mod.do(method, ...params);
1504
+ return final_result;
1228
1505
  };
1229
1506
 
1230
1507
  /**
@@ -1232,75 +1509,18 @@ Manager.prototype.runSubMod = async function (name, method, ...params) {
1232
1509
  * @param {string} event 事件名
1233
1510
  */
1234
1511
  Manager.prototype.offEvent = function (event) {
1235
- this.getEventer()?.off(event);
1236
- };
1237
-
1238
- /**
1239
- * 创建模块
1240
- * @param {object} config 模块配置
1241
- * @param {string} file 模块文件路径
1242
- * @returns {string} 创建的模块文件路径
1243
- */
1244
- Manager.prototype.createFile = function (config, file) {
1245
- if (!config.name) {
1246
- throw new TypeError('模块名称不能为空');
1247
- }
1248
- if (!config.title) {
1249
- throw new TypeError('模块标题不能为空');
1250
- }
1251
- if (!config.description) {
1252
- throw new TypeError('模块描述不能为空');
1253
- }
1254
- if (this.getMod(config.name)) {
1255
- throw new Error(`创建失败,原因:模块${config.name}已存在`);
1512
+ var eventer = this.getEventer();
1513
+ if (eventer && eventer.off) {
1514
+ eventer.off(event);
1256
1515
  }
1257
- let mod = new this.Drive(config, this);
1258
- let f = file;
1259
- if (!f) {
1260
- f = `${config.name}/${this.config.filename}`;
1261
- if (this.config.search_way === 'dir') {
1262
- let dir_name = this.getDirName();
1263
- f = `${dir_name}/${f}`;
1264
- }
1265
- }
1266
- f = f.fullname(this.config.dir);
1267
- mod.createConfigFile(f, config);
1268
- return f;
1269
- };
1270
-
1271
- /**
1272
- * 创建模块
1273
- * @param {object} config 模块配置
1274
- * @param {string} file 模块文件路径
1275
- * @returns {object} 创建的模块实例
1276
- */
1277
- Manager.prototype.create = function (config, file = '') {
1278
- let f = this.createFile(config, file);
1279
- let mod = this.registerMod(f, config);
1280
- this.refreshSnapshot();
1281
- return mod;
1282
- };
1283
-
1284
- /**
1285
- * 删除模块
1286
- * @param {string} name 模块名称
1287
- * @returns {object} 删除的模块实例
1288
- */
1289
- Manager.prototype.remove = async function (name) {
1290
- let mod = this.getMod(name);
1291
- if (mod) {
1292
- await mod.remove();
1293
- }
1294
- this.unregisterMod(name);
1295
- this.refreshSnapshot();
1296
- return mod;
1297
1516
  };
1298
1517
 
1299
1518
  module.exports = {
1300
1519
  Manager,
1301
1520
  Drive,
1302
- Mod,
1521
+ // eslint-disable-next-line mm_eslint/variable-name
1522
+ Mod: require('./mod').Mod,
1303
1523
  // 向后兼容
1304
1524
  Index: Manager,
1305
1525
  Item: Drive
1306
- };
1526
+ };