@yumerijs/core 2.0.0 → 2.0.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/context.d.ts CHANGED
@@ -1,8 +1,3 @@
1
- /**
2
- * @time: 2025/10/25 01:30
3
- * @author: FireGuo
4
- * WindyPear-Team All right reserved
5
- **/
6
1
  import { Core } from './core';
7
2
  import { Route } from './route';
8
3
  import { HookHandler } from './hook';
package/dist/context.js CHANGED
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * @time: 2025/10/25 01:30
4
- * @author: FireGuo
5
- * WindyPear-Team All right reserved
6
- **/
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.Context = void 0;
9
4
  const route_1 = require("./route");
package/dist/core.d.ts CHANGED
@@ -13,13 +13,6 @@ interface Plugin {
13
13
  depend: Array<string>;
14
14
  provide: Array<string>;
15
15
  }
16
- interface PluginLoader {
17
- load(pluginName: string): Promise<Plugin>;
18
- unloadPlugin(pluginName: string): Promise<void>;
19
- checkPluginDependencies(pluginPath: string): Promise<boolean>;
20
- installPluginDependencies(pluginName: string): Promise<void>;
21
- logger: Logger;
22
- }
23
16
  interface CoreOptions {
24
17
  port: number;
25
18
  host: string;
@@ -28,22 +21,12 @@ interface CoreOptions {
28
21
  enableWs: boolean;
29
22
  lang: string[];
30
23
  }
31
- /**
32
- * 定义插件状态枚举
33
- */
34
24
  export declare const enum PluginStatus {
35
- ENABLED = "enabled",// 正常启用
36
- DISABLED = "disabled",// 已禁用
25
+ ENABLED = "enabled",
26
+ DISABLED = "disabled",
37
27
  PENDING = "pending"
38
28
  }
39
29
  export declare class Core {
40
- plugins: {
41
- [name: string]: Plugin & {
42
- depend?: string[];
43
- provide?: string[];
44
- };
45
- };
46
- config: any;
47
30
  eventListeners: {
48
31
  [event: string]: ((...args: any[]) => Promise<void>)[];
49
32
  };
@@ -51,163 +34,28 @@ export declare class Core {
51
34
  [name: string]: any;
52
35
  };
53
36
  routes: Record<string, Route>;
54
- pluginLoader: PluginLoader;
55
37
  logger: Logger;
56
38
  globalMiddlewares: Record<string, Middleware>;
57
- pluginStatus: Record<string, PluginStatus>;
58
39
  hooks: Record<string, Hook>;
59
40
  coreConfig: CoreOptions;
60
41
  server: CoreServer;
61
42
  i18n: I18n;
62
- private pluginWatchers;
63
- private pluginModules;
64
- private configPath;
65
- /**
66
- * 插件名 -> Context 映射
67
- */
68
- private pluginContexts;
69
- constructor(pluginLoader?: PluginLoader, coreConfig?: CoreOptions, setCore?: boolean);
70
- /**
71
- * 获取或创建插件对应 Context
72
- */
73
- getContext(pluginName: string): Context;
74
- /**
75
- * 清理指定插件注册的所有内容
76
- */
77
- unregall(pluginName: string): void;
78
- /**
79
- * 加载配置文件
80
- * @param configPath 配置文件的路径
81
- */
82
- loadConfig(configPath: string): Promise<void>;
83
- /**
84
- * 启动应用
85
- * @returns Promise<void>
86
- */
43
+ loader: any;
44
+ constructor(loader?: any, coreConfig?: CoreOptions, setCore?: boolean);
87
45
  runCore(): Promise<void>;
88
- /**
89
- * 监听配置文件变化,实现热重载
90
- * @param configPath 配置文件的路径
91
- */
92
- private watchConfig;
93
- /**
94
- * 获取指定插件的配置
95
- * @param pluginName 插件名称
96
- * @returns 插件的配置对象
97
- */
98
- getPluginConfig(pluginName: string): Promise<Config>;
99
- /**
100
- * 加载所有在配置文件中启用的插件
101
- * @returns Promise<void>
102
- */
103
- loadPlugins(): Promise<void>;
104
- /**
105
- * 加载单个插件
106
- * @param pluginName 要加载的插件名
107
- * @param triggerPendingCheck 是否在加载后触发对其他待定插件的检查,默认为 true
108
- * @returns Promise<boolean> 是否加载成功
109
- */
110
- loadSinglePlugin(pluginName: string, triggerPendingCheck?: boolean, onlypending?: boolean): Promise<boolean>;
111
- /**
112
- * 尝试加载所有处于 PENDING 状态的插件
113
- * @returns Promise<void>
114
- */
115
- private _loadPendingPlugins;
116
- /**
117
- * 卸载插件,并递归卸载依赖于它的其他插件
118
- * @param pluginNameToUnload 要卸载的插件名
119
- */
120
- unloadPlugin(pluginNameToUnload: string, ispending?: boolean): Promise<void>;
121
- /**
122
- * 执行单个插件的卸载逻辑
123
- * @param pluginName 插件名
124
- * @param ispending 是否处于 PENDING 状态
125
- */
126
- private _unloadSinglePlugin;
127
- /**
128
- * 重新加载插件
129
- * @param pluginName 插件名称
130
- */
131
- reloadPlugin(pluginName: string): Promise<void>;
132
- /**
133
- * 监听插件文件变化
134
- * @param pluginName 插件名称
135
- * @param pluginPath 插件路径
136
- */
137
- private watchPlugin;
138
- /**
139
- * 注册组件
140
- * @param name 组件名称
141
- * @param component 组件实例
142
- */
46
+ getShortPluginName(pluginName: string): string;
47
+ plugin(pluginInstance: Plugin, context: Context, config: Config): Promise<void>;
143
48
  registerComponent(name: string, component: any): void;
144
- /**
145
- * 获取组件
146
- * @param name 组件名称
147
- * @returns 组件实例
148
- */
149
49
  getComponent(name: string): any;
150
- /**
151
- * 注销组件
152
- * @param name 组件名称
153
- */
154
50
  unregisterComponent(name: string): void;
155
- /**
156
- * 注册事件监听器
157
- * @param event 事件名称
158
- * @param listener 事件回调函数
159
- */
160
51
  on(event: string, listener: (...args: any[]) => Promise<void>): void;
161
- /**
162
- * 触发事件
163
- * @param event 事件名称
164
- * @param args 传递给事件监听器的参数
165
- */
166
52
  emit(event: string, ...args: any[]): Promise<void>;
167
- /**
168
- * 注册全局中间件
169
- * @param name 中间件名称
170
- * @param middleware 中间件函数
171
- * @returns Core 实例
172
- */
173
53
  use(name: string, middleware: Middleware): Core;
174
- /**
175
- * 注册路由
176
- * @param path 路由路径
177
- * @returns Route 实例
178
- */
179
54
  route(path: string): Route;
180
- /**
181
- * 注册 Hook 钩子
182
- * @param name Hook 点名称
183
- * @param hookname 钩子名称
184
- * @param callback 钩子函数
185
- */
186
55
  hook(name: string, hookname: string, callback: HookHandler): any;
187
- /**
188
- * 消除 Hook 钩子
189
- */
190
56
  unhook(name: string, hookname: string): any;
191
- /**
192
- * 执行 Hook 钩子
193
- * @param name Hook 点名称
194
- * @param args 参数
195
- * @return Promise<any[]>
196
- */
197
57
  hookExecute(name: string, ...args: any[]): Promise<any[]>;
198
- /**
199
- * 执行路由
200
- * @param pathname 需要匹配的URL
201
- * @param session 当前会话
202
- * @param queryParams URL参数
203
- * @returns 是否匹配成功
204
- */
205
58
  executeRoute(pathname: string, session: Session, queryParams: URLSearchParams): Promise<boolean>;
206
- /**
207
- * 获取路由
208
- * @param path 匹配路径
209
- * @return Route 实例或 false
210
- */
211
59
  getRoute(path: string): Route | false;
212
60
  }
213
61
  export {};
package/dist/core.js CHANGED
@@ -1,126 +1,27 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.Core = void 0;
37
- /**
38
- * @time: 2025/08/14 09:48
39
- * @author: FireGuo
40
- * WindyPear-Team All right reserved
41
- **/
42
- const yaml = __importStar(require("js-yaml"));
43
- const fs = __importStar(require("fs"));
44
- const path = __importStar(require("path"));
45
- const config_1 = require("./config");
46
4
  const logger_1 = require("./logger");
47
- const chokidar = __importStar(require("chokidar"));
48
5
  const route_1 = require("./route");
49
- const context_1 = require("./context");
50
6
  const hook_1 = require("./hook");
51
7
  const server_1 = require("./server");
52
- const i18n_1 = require("./i18n");
53
8
  class Core {
54
- plugins = {};
55
- config = null;
56
9
  eventListeners = {};
57
10
  components = {};
58
11
  routes = {};
59
- pluginLoader;
60
12
  logger = new logger_1.Logger('core');
61
13
  globalMiddlewares = {};
62
- pluginStatus = {};
63
14
  hooks = {};
64
15
  coreConfig;
65
16
  server;
66
17
  i18n;
67
- pluginWatchers = {};
68
- pluginModules = {};
69
- configPath = '';
70
- /**
71
- * 插件名 -> Context 映射
72
- */
73
- pluginContexts = {};
74
- constructor(pluginLoader, coreConfig, setCore = true) {
75
- this.coreConfig = coreConfig;
76
- this.pluginLoader = pluginLoader;
18
+ loader;
19
+ constructor(loader, coreConfig, setCore = true) {
20
+ this.coreConfig = coreConfig || {};
21
+ this.loader = loader;
77
22
  if (setCore)
78
23
  logger_1.Logger.setCore(this);
79
24
  }
80
- /**
81
- * 获取或创建插件对应 Context
82
- */
83
- getContext(pluginName) {
84
- if (!this.pluginContexts[pluginName]) {
85
- this.pluginContexts[pluginName] = new context_1.Context(this, pluginName);
86
- }
87
- return this.pluginContexts[pluginName];
88
- }
89
- /**
90
- * 清理指定插件注册的所有内容
91
- */
92
- unregall(pluginName) {
93
- const ctx = this.pluginContexts[pluginName];
94
- if (ctx) {
95
- ctx.dispose();
96
- delete this.pluginContexts[pluginName];
97
- }
98
- }
99
- /**
100
- * 加载配置文件
101
- * @param configPath 配置文件的路径
102
- */
103
- async loadConfig(configPath) {
104
- try {
105
- this.configPath = configPath;
106
- const doc = yaml.load(fs.readFileSync(configPath, 'utf8'));
107
- this.config = doc;
108
- this.logger.info('Config loaded.');
109
- if (process.env.NODE_ENV === 'development') {
110
- this.watchConfig(configPath);
111
- }
112
- this.coreConfig = this.config.core || {};
113
- }
114
- catch (e) {
115
- this.logger.error('Failed to load config:', e);
116
- throw e;
117
- }
118
- this.i18n = new i18n_1.I18n(this.coreConfig.lang || ['zh', 'en']);
119
- }
120
- /**
121
- * 启动应用
122
- * @returns Promise<void>
123
- */
124
25
  async runCore() {
125
26
  this.server = new server_1.Server(this, {
126
27
  port: this.coreConfig.port || 14510,
@@ -132,369 +33,38 @@ class Core {
132
33
  await this.server.start();
133
34
  this.logger.info(`Yumeri server started at ${this.coreConfig.host}:${this.coreConfig.port}`);
134
35
  }
135
- /**
136
- * 监听配置文件变化,实现热重载
137
- * @param configPath 配置文件的路径
138
- */
139
- watchConfig(configPath) {
140
- const watcher = chokidar.watch(configPath, {
141
- persistent: true,
142
- ignoreInitial: true,
143
- awaitWriteFinish: {
144
- stabilityThreshold: 300,
145
- pollInterval: 100,
146
- },
147
- });
148
- watcher.on('change', async () => {
149
- try {
150
- const doc = yaml.load(fs.readFileSync(configPath, 'utf8'));
151
- if (doc && doc.plugins) {
152
- this.config.plugins = doc.plugins;
153
- }
154
- await this.emit('config-changed', this.config);
155
- }
156
- catch (error) {
157
- this.logger.error('Failed to reload config:', error);
158
- }
159
- });
160
- this.logger.info(`Watching for config changes at ${configPath}`);
161
- }
162
- /**
163
- * 获取指定插件的配置
164
- * @param pluginName 插件名称
165
- * @returns 插件的配置对象
166
- */
167
- async getPluginConfig(pluginName) {
168
- const actualPluginName = pluginName.startsWith('~') ? pluginName.substring(1) : pluginName;
169
- if (this.configPath && process.env.NODE_ENV === 'development') {
170
- try {
171
- const doc = yaml.load(fs.readFileSync(this.configPath, 'utf8'));
172
- if (doc && doc.plugins) {
173
- this.config.plugins = doc.plugins;
174
- }
175
- }
176
- catch (error) {
177
- this.logger.warn('Failed to refresh config for hot reload:', error);
178
- }
179
- }
180
- if (!this.config.plugins[actualPluginName]) {
181
- return new config_1.Config(actualPluginName);
182
- }
183
- return new config_1.Config(actualPluginName, this.config.plugins[actualPluginName]);
184
- }
185
- /**
186
- * 加载所有在配置文件中启用的插件
187
- * @returns Promise<void>
188
- */
189
- async loadPlugins() {
190
- if (!this.config || typeof this.config.plugins !== 'object' || this.config.plugins === null) {
191
- this.logger.info('No plugins configuration found. No plugins to load.');
192
- return;
193
- }
194
- const allPluginNames = Object.keys(this.config.plugins);
195
- this.pluginStatus = {}; // 重置状态
196
- // 初始化所有插件的状态
197
- for (const name of allPluginNames) {
198
- if (name.startsWith('~')) {
199
- const actualName = name.substring(1);
200
- this.pluginStatus[actualName] = "disabled" /* PluginStatus.DISABLED */;
201
- }
202
- else {
203
- this.pluginStatus[name] = "pending" /* PluginStatus.PENDING */; // 默认为等待加载
204
- }
205
- }
206
- const enabledPlugins = allPluginNames.filter(name => !name.startsWith('~'));
207
- // 卸载配置中已禁用或移除的已加载插件
208
- const currentlyLoaded = Object.keys(this.plugins);
209
- for (const loadedName of currentlyLoaded) {
210
- if (!enabledPlugins.includes(loadedName)) {
211
- await this.unloadPlugin(loadedName);
212
- }
213
- }
214
- if (enabledPlugins.length === 0) {
215
- this.logger.info('No enabled plugins found in configuration.');
216
- return;
217
- }
218
- // this.logger.info(`Disabled plugins: ${Object.keys(this.pluginStatus).filter(p => this.pluginStatus[p] === PluginStatus.DISABLED).join(', ') || 'None'}`);
219
- // 循环加载,直到没有插件可以被加载
220
- let loadedInLastPass = true;
221
- while (loadedInLastPass) {
222
- loadedInLastPass = false;
223
- for (const pluginName of enabledPlugins) {
224
- // 如果插件已经是启用状态,则跳过
225
- if (this.pluginStatus[pluginName] === "enabled" /* PluginStatus.ENABLED */) {
226
- continue;
227
- }
228
- // 尝试加载单个插件
229
- const success = await this.loadSinglePlugin(pluginName, false); // 内部加载,不触发后续的pending检查
230
- if (success) {
231
- loadedInLastPass = true;
232
- }
233
- }
234
- }
235
- // 加载完所有能加载的插件后,统一检查并加载待定插件
236
- await this._loadPendingPlugins();
237
- const pendingPlugins = Object.keys(this.pluginStatus).filter(p => this.pluginStatus[p] === "pending" /* PluginStatus.PENDING */);
238
- if (pendingPlugins.length > 0) {
239
- // this.logger.warn('Some plugins could not be loaded due to unresolved dependencies:', pendingPlugins);
240
- }
241
- }
242
- /**
243
- * 加载单个插件
244
- * @param pluginName 要加载的插件名
245
- * @param triggerPendingCheck 是否在加载后触发对其他待定插件的检查,默认为 true
246
- * @returns Promise<boolean> 是否加载成功
247
- */
248
- async loadSinglePlugin(pluginName, triggerPendingCheck = true, onlypending = false) {
249
- // 如果插件不存在于状态记录中(例如,动态添加),则设为 PENDING
250
- if (!this.pluginStatus[pluginName]) {
251
- this.pluginStatus[pluginName] = "pending" /* PluginStatus.PENDING */;
252
- }
253
- // 只有 PENDING 状态的插件才能被加载
254
- if (this.pluginStatus[pluginName] !== "pending" /* PluginStatus.PENDING */ && onlypending) {
255
- // this.logger.info(`Plugin "${pluginName}" is not in a pending state (current: ${this.pluginStatus[pluginName]}). Skipping load.`);
256
- return false;
257
- }
258
- try {
259
- const pluginInstance = await this.pluginLoader.load(pluginName);
260
- if (!pluginInstance) {
261
- throw new Error('Plugin loader returned no instance.');
262
- }
263
- const deps = pluginInstance.depend || [];
264
- const unmetDependencies = deps.filter(dep => !this.components[dep]);
265
- if (unmetDependencies.length > 0) {
266
- // this.logger.info(`Plugin "${pluginName}" has unmet dependencies: [${unmetDependencies.join(', ')}]. Will retry later.`);
267
- return false;
268
- }
269
- // 依赖满足,开始加载
270
- this.plugins[pluginName] = pluginInstance;
271
- if (pluginInstance.apply) {
272
- this.pluginLoader.logger.info(`Apply plugin "${pluginName}"`);
273
- await pluginInstance.apply(this.getContext(pluginName), await this.getPluginConfig(pluginName));
274
- }
275
- // 注册提供的组件
276
- // if (pluginInstance.provide) {
277
- // for (const componentName of pluginInstance.provide) {
278
- // if (this.components[componentName]) {
279
- // this.logger.error(`Component "${componentName}" is provided by multiple plugins: "${this.providedComponents[componentName]}" and "${pluginName}".`);
280
- // } else {
281
- // this.components[componentName] = pluginName;
282
- // }
283
- // }
284
- // }
285
- this.pluginStatus[pluginName] = "enabled" /* PluginStatus.ENABLED */; // 更新状态为已启用
286
- // 加载成功后,检查是否可以加载其他等待中的插件
287
- if (triggerPendingCheck) {
288
- await this._loadPendingPlugins();
289
- }
290
- // In development mode, watch the plugin for changes.
291
- if (process.env.NODE_ENV === 'development') {
292
- let pluginPathToWatch = null;
293
- try {
294
- const pkgJsonPath = require.resolve(`${pluginName}/package.json`);
295
- pluginPathToWatch = path.dirname(pkgJsonPath);
296
- }
297
- catch (e) {
298
- const localPluginPath = path.resolve(process.cwd(), pluginName);
299
- const localPluginPathInPlugins = path.resolve(process.cwd(), 'plugins', pluginName);
300
- if (fs.existsSync(localPluginPath)) {
301
- pluginPathToWatch = localPluginPath;
302
- }
303
- else if (fs.existsSync(localPluginPathInPlugins)) {
304
- pluginPathToWatch = localPluginPathInPlugins;
305
- }
306
- }
307
- if (pluginPathToWatch) {
308
- this.watchPlugin(pluginName, pluginPathToWatch);
309
- }
310
- else {
311
- this.logger.warn(`Could not resolve path for plugin ${pluginName} to watch for changes.`);
312
- }
313
- }
314
- return true;
315
- }
316
- catch (err) {
317
- this.pluginLoader.logger.error(`Failed to load plugin "${pluginName}":`, err);
318
- // 加载失败的插件保持 PENDING 状态,以便后续重试
319
- return false;
36
+ getShortPluginName(pluginName) {
37
+ const scopeRegex = /^(@[^/]+\/)/;
38
+ const scopeMatch = pluginName.match(scopeRegex);
39
+ const scope = scopeMatch ? scopeMatch[0] : '';
40
+ const nameWithoutScope = pluginName.replace(scopeRegex, '');
41
+ if (nameWithoutScope.startsWith('yumeri-plugin-')) {
42
+ return `${scope}${nameWithoutScope.substring('yumeri-plugin-'.length)}`;
320
43
  }
44
+ return pluginName;
321
45
  }
322
- /**
323
- * 尝试加载所有处于 PENDING 状态的插件
324
- * @returns Promise<void>
325
- */
326
- async _loadPendingPlugins() {
327
- const pendingPlugins = Object.keys(this.pluginStatus).filter(p => this.pluginStatus[p] === "pending" /* PluginStatus.PENDING */);
328
- if (pendingPlugins.length === 0)
329
- return;
330
- // this.logger.info('Checking pending plugins...');
331
- for (const pluginName of pendingPlugins) {
332
- await this.loadSinglePlugin(pluginName, false); // 递归调用,但不触发顶层的pending检查
46
+ async plugin(pluginInstance, context, config) {
47
+ const shortName = this.getShortPluginName(context.pluginname);
48
+ this.logger.info(`apply plugin ${shortName}`);
49
+ if (pluginInstance.apply) {
50
+ await pluginInstance.apply(context, config);
333
51
  }
334
52
  }
335
- /**
336
- * 卸载插件,并递归卸载依赖于它的其他插件
337
- * @param pluginNameToUnload 要卸载的插件名
338
- */
339
- async unloadPlugin(pluginNameToUnload, ispending = false) {
340
- // 1. 找出所有直接或间接依赖于此插件的插件
341
- const dependents = [];
342
- const pluginsToCheck = [pluginNameToUnload];
343
- while (pluginsToCheck.length > 0) {
344
- const currentPluginName = pluginsToCheck.shift();
345
- const provided = this.plugins[currentPluginName]?.provide || [];
346
- if (provided.length === 0)
347
- continue;
348
- for (const pluginName in this.plugins) {
349
- if (dependents.includes(pluginName) || pluginName === pluginNameToUnload)
350
- continue;
351
- const deps = this.plugins[pluginName].depend || [];
352
- if (provided.some(p => deps.includes(p))) {
353
- if (!dependents.includes(pluginName)) {
354
- dependents.push(pluginName);
355
- pluginsToCheck.push(pluginName); // 递归查找
356
- // this.logger.info(`Plugin "${pluginName}" depends on "${currentPluginName}" and will be unloaded.`);
357
- }
358
- }
359
- }
360
- }
361
- // 2. 卸载所有依赖者
362
- for (const dependentName of dependents) {
363
- await this.unloadPlugin(dependentName, true);
364
- }
365
- // 3. 最后卸载目标插件
366
- await this._unloadSinglePlugin(pluginNameToUnload, ispending);
367
- // 4. 卸载完成后,尝试重新加载处于 PENDING 状态的插件
368
- // await this._loadPendingPlugins();
369
- }
370
- /**
371
- * 执行单个插件的卸载逻辑
372
- * @param pluginName 插件名
373
- * @param ispending 是否处于 PENDING 状态
374
- */
375
- async _unloadSinglePlugin(pluginName, ispending = false) {
376
- if (this.pluginStatus[pluginName] !== "enabled" /* PluginStatus.ENABLED */) {
377
- return; // 只卸载已启用的插件
378
- }
379
- this.logger.info(`Unloading plugin "${pluginName}"...`);
380
- try {
381
- const plugin = this.plugins[pluginName];
382
- if (plugin && plugin.disable)
383
- await plugin.disable(this.getContext(pluginName));
384
- await this.pluginLoader.unloadPlugin(pluginName);
385
- this.unregall(pluginName); // 清理该插件注册的所有内容
386
- delete this.plugins[pluginName];
387
- delete this.pluginModules[pluginName];
388
- this.pluginStatus[pluginName] = ispending ? "pending" /* PluginStatus.PENDING */ : "disabled" /* PluginStatus.DISABLED */;
389
- // Stop watching the plugin directory
390
- if (this.pluginWatchers[pluginName]) {
391
- this.pluginWatchers[pluginName].close();
392
- delete this.pluginWatchers[pluginName];
393
- }
394
- this.emit('plugin-unloaded', pluginName);
395
- }
396
- catch (error) {
397
- this.logger.error(`Failed to unload plugin "${pluginName}":`, error);
398
- }
399
- }
400
- /**
401
- * 重新加载插件
402
- * @param pluginName 插件名称
403
- */
404
- async reloadPlugin(pluginName) {
405
- // this.logger.info(`Reloading plugin "${pluginName}"...`);
406
- // 1. 递归卸载插件及其依赖者
407
- await this.unloadPlugin(pluginName);
408
- // 2. 重新加载该插件
409
- // unloadPlugin 已经将状态设置为 PENDING,所以 loadSinglePlugin 可以直接工作
410
- const success = await this.loadSinglePlugin(pluginName);
411
- if (success) {
412
- // this.logger.info(`Plugin "${pluginName}" reloaded successfully.`);
413
- this.emit('plugin-reloaded', pluginName);
414
- }
415
- else {
416
- this.logger.error(`Failed to reload plugin "${pluginName}". It may have unmet dependencies.`);
417
- }
418
- }
419
- /**
420
- * 监听插件文件变化
421
- * @param pluginName 插件名称
422
- * @param pluginPath 插件路径
423
- */
424
- watchPlugin(pluginName, pluginPath) {
425
- if (this.pluginWatchers[pluginName]) {
426
- return; // Already watching
427
- }
428
- const logger = new logger_1.Logger('hmr');
429
- const watcher = chokidar.watch(pluginPath, {
430
- ignored: /(^|[\/])\../,
431
- persistent: true,
432
- ignoreInitial: true,
433
- awaitWriteFinish: {
434
- stabilityThreshold: 200,
435
- pollInterval: 100,
436
- },
437
- });
438
- watcher.on('change', async (changePath) => {
439
- logger.info(`Plugin file changed: ${changePath}`);
440
- await this.reloadPlugin(pluginName);
441
- });
442
- watcher.on('add', async (changePath) => {
443
- logger.info(`New file added to plugin ${pluginName}: ${changePath}`);
444
- await this.reloadPlugin(pluginName);
445
- });
446
- watcher.on('unlink', async (changePath) => {
447
- logger.info(`File removed from plugin ${pluginName}: ${changePath}`);
448
- await this.reloadPlugin(pluginName);
449
- });
450
- this.pluginWatchers[pluginName] = watcher;
451
- // logger.info(`Watching for changes in plugin: ${pluginName}`);
452
- }
453
- /**
454
- * 注册组件
455
- * @param name 组件名称
456
- * @param component 组件实例
457
- */
458
53
  registerComponent(name, component) {
459
- // if (this.components.hasOwnProperty(name)) {
460
- // this.logger.warn(`Component "${name}" already registered by plugin "${this.providedComponents[name]}".`);
461
- // return;
462
- // }
463
54
  this.components[name] = component;
464
- // this.logger.info(`Component "${name}" registered.`);
465
55
  }
466
- /**
467
- * 获取组件
468
- * @param name 组件名称
469
- * @returns 组件实例
470
- */
471
56
  getComponent(name) {
472
57
  return this.components[name];
473
58
  }
474
- /**
475
- * 注销组件
476
- * @param name 组件名称
477
- */
478
59
  unregisterComponent(name) {
479
60
  delete this.components[name];
480
- // delete this.providedComponents[name];
481
61
  }
482
- /**
483
- * 注册事件监听器
484
- * @param event 事件名称
485
- * @param listener 事件回调函数
486
- */
487
62
  on(event, listener) {
488
63
  if (!this.eventListeners[event]) {
489
64
  this.eventListeners[event] = [];
490
65
  }
491
66
  this.eventListeners[event].push(listener);
492
67
  }
493
- /**
494
- * 触发事件
495
- * @param event 事件名称
496
- * @param args 传递给事件监听器的参数
497
- */
498
68
  async emit(event, ...args) {
499
69
  if (this.eventListeners[event]) {
500
70
  for (const listener of this.eventListeners[event]) {
@@ -507,65 +77,32 @@ class Core {
507
77
  }
508
78
  }
509
79
  }
510
- /**
511
- * 注册全局中间件
512
- * @param name 中间件名称
513
- * @param middleware 中间件函数
514
- * @returns Core 实例
515
- */
516
80
  use(name, middleware) {
517
81
  this.globalMiddlewares[name] = middleware;
518
82
  return this;
519
83
  }
520
- /**
521
- * 注册路由
522
- * @param path 路由路径
523
- * @returns Route 实例
524
- */
525
84
  route(path) {
526
85
  const route = new route_1.Route(path);
527
86
  this.routes[path] = route;
528
87
  return route;
529
88
  }
530
- /**
531
- * 注册 Hook 钩子
532
- * @param name Hook 点名称
533
- * @param hookname 钩子名称
534
- * @param callback 钩子函数
535
- */
536
89
  hook(name, hookname, callback) {
537
90
  if (!this.hooks[name]) {
538
91
  this.hooks[name] = new hook_1.Hook(name);
539
92
  }
540
93
  this.hooks[name].add(hookname, callback);
541
94
  }
542
- /**
543
- * 消除 Hook 钩子
544
- */
545
95
  unhook(name, hookname) {
546
96
  if (this.hooks[name]) {
547
97
  this.hooks[name].remove(hookname);
548
98
  }
549
99
  }
550
- /**
551
- * 执行 Hook 钩子
552
- * @param name Hook 点名称
553
- * @param args 参数
554
- * @return Promise<any[]>
555
- */
556
100
  async hookExecute(name, ...args) {
557
101
  if (this.hooks[name]) {
558
102
  return await this.hooks[name].trigger(...args);
559
103
  }
560
104
  return [];
561
105
  }
562
- /**
563
- * 执行路由
564
- * @param pathname 需要匹配的URL
565
- * @param session 当前会话
566
- * @param queryParams URL参数
567
- * @returns 是否匹配成功
568
- */
569
106
  async executeRoute(pathname, session, queryParams) {
570
107
  for (const routePath in this.routes) {
571
108
  const route = this.routes[routePath];
@@ -585,11 +122,6 @@ class Core {
585
122
  }
586
123
  return false;
587
124
  }
588
- /**
589
- * 获取路由
590
- * @param path 匹配路径
591
- * @return Route 实例或 false
592
- */
593
125
  getRoute(path) {
594
126
  for (const routePath in this.routes) {
595
127
  const route = this.routes[routePath];
package/dist/logger.d.ts CHANGED
@@ -15,7 +15,7 @@ export declare class Logger {
15
15
  }[];
16
16
  static setCore(core: Core): void;
17
17
  constructor(title: string);
18
- private getRandomColor;
18
+ private getColorByName;
19
19
  private getTimestamp;
20
20
  private log;
21
21
  info(...args: any[]): void;
package/dist/logger.js CHANGED
@@ -23,15 +23,22 @@ class Logger {
23
23
  }
24
24
  constructor(title) {
25
25
  this.title = title;
26
- this.titleColor = this.getRandomColor();
26
+ this.titleColor = this.getColorByName(this.title);
27
27
  }
28
- getRandomColor() {
28
+ getColorByName(name) {
29
29
  const availableColors = [
30
30
  picocolors_1.default.red, picocolors_1.default.green, picocolors_1.default.yellow, picocolors_1.default.blue,
31
- picocolors_1.default.magenta, picocolors_1.default.cyan, picocolors_1.default.white, picocolors_1.default.gray
31
+ picocolors_1.default.magenta, picocolors_1.default.cyan, picocolors_1.default.white,
32
+ picocolors_1.default.redBright, picocolors_1.default.greenBright, picocolors_1.default.yellowBright,
33
+ picocolors_1.default.blueBright, picocolors_1.default.magentaBright, picocolors_1.default.cyanBright
32
34
  ];
33
- const randomIndex = Math.floor(Math.random() * availableColors.length);
34
- return availableColors[randomIndex];
35
+ // Simple hash function to ensure consistent color for the same title
36
+ let hash = 0;
37
+ for (let i = 0; i < name.length; i++) {
38
+ hash = name.charCodeAt(i) + ((hash << 5) - hash);
39
+ }
40
+ const index = Math.abs(hash % availableColors.length);
41
+ return availableColors[index];
35
42
  }
36
43
  getTimestamp() {
37
44
  const now = new Date();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yumerijs/core",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Core module for yumeri",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -34,7 +34,6 @@
34
34
  "typescript": "^5.8.2"
35
35
  },
36
36
  "dependencies": {
37
- "@yumerijs/loader": "^2.0.0",
38
37
  "chokidar": "^4.0.3",
39
38
  "formidable": "^3.5.4",
40
39
  "js-yaml": "^4.1.0",