@yumerijs/core 1.3.3 → 2.0.0

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,5 +1,5 @@
1
1
  /**
2
- * @time: 2025/08/14 19:10
2
+ * @time: 2025/10/25 01:30
3
3
  * @author: FireGuo
4
4
  * WindyPear-Team All right reserved
5
5
  **/
@@ -7,6 +7,13 @@ import { Core } from './core';
7
7
  import { Route } from './route';
8
8
  import { HookHandler } from './hook';
9
9
  import { Middleware } from './middleware';
10
+ import { Config } from './config';
11
+ interface Plugin {
12
+ apply: (ctx: Context, config: Config) => Promise<void>;
13
+ disable: (ctx: Context) => Promise<void>;
14
+ depend: Array<string>;
15
+ provide: Array<string>;
16
+ }
10
17
  /**
11
18
  * 插件上下文对象
12
19
  * 每个插件一个 Context,用于管理插件注册的命令、路由、事件、组件和中间件
@@ -18,9 +25,10 @@ export declare class Context {
18
25
  private components;
19
26
  private middlewares;
20
27
  private hooks;
21
- /**
22
- * 插件名称
23
- */
28
+ private childContexts;
29
+ private childPlugins;
30
+ private i18ns;
31
+ /** 插件名称 */
24
32
  pluginname: string;
25
33
  /**
26
34
  * 创建 Context 实例
@@ -57,25 +65,19 @@ export declare class Context {
57
65
  * 执行 Hook 钩子
58
66
  * @param name Hook 点名称
59
67
  * @param args Hook 参数
60
- * @returns Promise<any[]>
61
68
  */
62
69
  executeHook(name: string, ...args: any[]): Promise<any[]>;
63
- /**
64
- * 获取 Core 实例
65
- * @returns Core
66
- */
70
+ /** 获取 Core 实例 */
67
71
  getCore(): Core;
68
72
  /**
69
73
  * 触发事件
70
74
  * @param event 事件名称
71
75
  * @param args 事件参数
72
- * @returns Promise<void>
73
76
  */
74
77
  emit(event: string, ...args: any[]): Promise<void>;
75
78
  /**
76
79
  * 获取组件实例
77
80
  * @param name 组件名称
78
- * @returns 组件实例
79
81
  */
80
82
  getComponent(name: string): any;
81
83
  /**
@@ -84,9 +86,26 @@ export declare class Context {
84
86
  * @param component 组件实例
85
87
  */
86
88
  registerComponent(name: string, component: any): void;
89
+ /**
90
+ * 注册子 Context
91
+ * @param name 子 Context 名称
92
+ */
93
+ fork(name?: string): Context;
94
+ /**
95
+ * 注册子插件
96
+ * @param plugin 插件实例
97
+ * @param config 插件配置
98
+ */
99
+ apply(plugin: Plugin, config: Config): Promise<void>;
100
+ /**
101
+ * 注册 i18n
102
+ * @param content 内容(可以是嵌套对象或单个key)
103
+ * @param locale 可选的语言映射
104
+ */
105
+ i18n(content: string | Record<string, any>, locale?: Record<string, string>): void;
87
106
  /**
88
107
  * 卸载插件时清理注册的所有资源
89
- * 包括组件、路由、中间件和事件监听器
90
108
  */
91
- dispose(): void;
109
+ dispose(): Promise<void>;
92
110
  }
111
+ export {};
package/dist/context.js CHANGED
@@ -1,4 +1,9 @@
1
1
  "use strict";
2
+ /**
3
+ * @time: 2025/10/25 01:30
4
+ * @author: FireGuo
5
+ * WindyPear-Team All right reserved
6
+ **/
2
7
  Object.defineProperty(exports, "__esModule", { value: true });
3
8
  exports.Context = void 0;
4
9
  const route_1 = require("./route");
@@ -13,9 +18,10 @@ class Context {
13
18
  components = [];
14
19
  middlewares = [];
15
20
  hooks = {};
16
- /**
17
- * 插件名称
18
- */
21
+ childContexts = [];
22
+ childPlugins = new Map();
23
+ i18ns = [];
24
+ /** 插件名称 */
19
25
  pluginname;
20
26
  /**
21
27
  * 创建 Context 实例
@@ -25,6 +31,7 @@ class Context {
25
31
  constructor(core, pluginname) {
26
32
  this.core = core;
27
33
  this.pluginname = pluginname;
34
+ this.childPlugins = new Map();
28
35
  }
29
36
  /**
30
37
  * 注册路由
@@ -45,9 +52,9 @@ class Context {
45
52
  * @param listener 事件监听器
46
53
  */
47
54
  on(name, listener) {
48
- // 记录插件自己的事件监听器
55
+ if (!listener)
56
+ return;
49
57
  this.eventlisteners.push({ name, listener });
50
- // 注册到 core 的全局事件监听器中
51
58
  this.core.on(name, listener);
52
59
  }
53
60
  /**
@@ -56,6 +63,8 @@ class Context {
56
63
  * @param callback 中间件回调函数
57
64
  */
58
65
  use(name, callback) {
66
+ if (!callback)
67
+ return;
59
68
  this.middlewares.push(name);
60
69
  this.core.use(name, callback);
61
70
  }
@@ -66,22 +75,22 @@ class Context {
66
75
  * @param callback 钩子回调函数
67
76
  */
68
77
  hook(name, hookname, callback) {
78
+ if (!callback || !hookname)
79
+ return;
69
80
  this.core.hook(name, hookname, callback);
81
+ if (!this.hooks[name])
82
+ this.hooks[name] = [];
70
83
  this.hooks[name].push(hookname);
71
84
  }
72
85
  /**
73
86
  * 执行 Hook 钩子
74
87
  * @param name Hook 点名称
75
88
  * @param args Hook 参数
76
- * @returns Promise<any[]>
77
89
  */
78
90
  async executeHook(name, ...args) {
79
91
  return await this.core.hookExecute(name, ...args);
80
92
  }
81
- /**
82
- * 获取 Core 实例
83
- * @returns Core
84
- */
93
+ /** 获取 Core 实例 */
85
94
  getCore() {
86
95
  return this.core;
87
96
  }
@@ -89,7 +98,6 @@ class Context {
89
98
  * 触发事件
90
99
  * @param event 事件名称
91
100
  * @param args 事件参数
92
- * @returns Promise<void>
93
101
  */
94
102
  async emit(event, ...args) {
95
103
  return await this.core.emit(event, ...args);
@@ -97,7 +105,6 @@ class Context {
97
105
  /**
98
106
  * 获取组件实例
99
107
  * @param name 组件名称
100
- * @returns 组件实例
101
108
  */
102
109
  getComponent(name) {
103
110
  return this.core.getComponent(name);
@@ -108,6 +115,8 @@ class Context {
108
115
  * @param component 组件实例
109
116
  */
110
117
  registerComponent(name, component) {
118
+ if (!component)
119
+ return;
111
120
  if (this.core.components[name]) {
112
121
  this.core.logger.warn(`Plugin "${this.pluginname}" attempt to register component "${name}", but it has already been registered.`);
113
122
  return;
@@ -115,41 +124,107 @@ class Context {
115
124
  this.core.components[name] = component;
116
125
  this.components.push(name);
117
126
  }
127
+ /**
128
+ * 注册子 Context
129
+ * @param name 子 Context 名称
130
+ */
131
+ fork(name = this.pluginname) {
132
+ const ctx = new Context(this.core, name);
133
+ this.childContexts.push(ctx);
134
+ return ctx;
135
+ }
136
+ /**
137
+ * 注册子插件
138
+ * @param plugin 插件实例
139
+ * @param config 插件配置
140
+ */
141
+ async apply(plugin, config) {
142
+ if (!plugin || !plugin.apply)
143
+ return;
144
+ const ctx = this.fork();
145
+ await plugin.apply(ctx, config);
146
+ this.childPlugins.set(ctx, plugin);
147
+ }
148
+ /**
149
+ * 注册 i18n
150
+ * @param content 内容(可以是嵌套对象或单个key)
151
+ * @param locale 可选的语言映射
152
+ */
153
+ i18n(content, locale) {
154
+ if (!this.i18ns)
155
+ this.i18ns = [];
156
+ const isLangObject = (obj) => typeof obj === 'object' && Object.values(obj).every(v => typeof v === 'string');
157
+ const flatten = (obj, prefix = '') => {
158
+ const result = {};
159
+ for (const [key, value] of Object.entries(obj)) {
160
+ const fullKey = prefix ? `${prefix}.${key}` : key;
161
+ if (isLangObject(value)) {
162
+ result[fullKey] = value;
163
+ }
164
+ else if (typeof value === 'object') {
165
+ Object.assign(result, flatten(value, fullKey));
166
+ }
167
+ }
168
+ return result;
169
+ };
170
+ if (typeof content === 'string' && locale) {
171
+ this.core.i18n.register(content, locale);
172
+ this.i18ns.push(content);
173
+ }
174
+ else if (typeof content === 'object') {
175
+ const flat = flatten(content);
176
+ this.core.i18n.register(flat);
177
+ this.i18ns.push(...Object.keys(flat));
178
+ }
179
+ }
118
180
  /**
119
181
  * 卸载插件时清理注册的所有资源
120
- * 包括组件、路由、中间件和事件监听器
121
182
  */
122
- dispose() {
183
+ async dispose() {
123
184
  // 删除组件
124
- this.components.forEach((name) => {
125
- delete this.core.components[name];
126
- });
185
+ this.components.forEach((name) => delete this.core.components[name]);
127
186
  // 删除路由
128
- this.routes.forEach((route) => {
129
- delete this.core.routes[route];
130
- });
187
+ this.routes.forEach((route) => delete this.core.routes[route]);
131
188
  // 删除中间件
132
- this.middlewares.forEach((middleware) => {
133
- delete this.core.globalMiddlewares[middleware];
134
- });
189
+ this.middlewares.forEach((middleware) => delete this.core.globalMiddlewares[middleware]);
135
190
  // 删除事件监听器
136
191
  this.eventlisteners.forEach(({ name, listener }) => {
137
- if (this.core.eventListeners?.[name]) {
138
- this.core.eventListeners[name] =
139
- this.core.eventListeners[name].filter((l) => l !== listener);
192
+ const listeners = this.core.eventListeners?.[name];
193
+ if (listeners) {
194
+ this.core.eventListeners[name] = listeners.filter(l => l !== listener);
140
195
  }
141
196
  });
142
197
  // 删除钩子
143
198
  for (const hook in this.hooks) {
144
199
  this.hooks[hook].forEach((hookname) => {
145
- this.core.unhook(hook, hookname);
200
+ if (hookname)
201
+ this.core.unhook(hook, hookname);
146
202
  });
147
203
  }
148
- // 清空 Context 内部记录,避免内存泄漏
204
+ // 卸载子插件(异步即可,不必按顺序)
205
+ this.childPlugins.forEach(async (plugin, ctx) => {
206
+ if (plugin?.disable)
207
+ await plugin.disable(ctx);
208
+ });
209
+ // 删除子上下文
210
+ this.childContexts.forEach((ctx) => {
211
+ if (ctx?.dispose)
212
+ ctx.dispose();
213
+ });
214
+ // 删除i18n
215
+ if (this.i18ns?.length) {
216
+ for (const key of this.i18ns) {
217
+ this.core.i18n.delete(key);
218
+ }
219
+ this.i18ns.length = 0;
220
+ }
221
+ // 清空内部记录
149
222
  this.components = [];
150
223
  this.routes = [];
151
224
  this.middlewares = [];
152
225
  this.eventlisteners = [];
226
+ this.hooks = {};
227
+ this.childContexts = [];
153
228
  }
154
229
  }
155
230
  exports.Context = Context;
package/dist/core.d.ts CHANGED
@@ -6,6 +6,7 @@ import { Route } from './route';
6
6
  import { Context } from './context';
7
7
  import { HookHandler, Hook } from './hook';
8
8
  import { Server as CoreServer } from './server';
9
+ import { I18n } from './i18n';
9
10
  interface Plugin {
10
11
  apply: (ctx: Context, config: Config) => Promise<void>;
11
12
  disable: (ctx: Context) => Promise<void>;
@@ -25,6 +26,7 @@ interface CoreOptions {
25
26
  staticDir: string;
26
27
  enableCors: boolean;
27
28
  enableWs: boolean;
29
+ lang: string[];
28
30
  }
29
31
  /**
30
32
  * 定义插件状态枚举
@@ -56,6 +58,7 @@ export declare class Core {
56
58
  hooks: Record<string, Hook>;
57
59
  coreConfig: CoreOptions;
58
60
  server: CoreServer;
61
+ i18n: I18n;
59
62
  private pluginWatchers;
60
63
  private pluginModules;
61
64
  private configPath;
@@ -63,7 +66,7 @@ export declare class Core {
63
66
  * 插件名 -> Context 映射
64
67
  */
65
68
  private pluginContexts;
66
- constructor(pluginLoader?: PluginLoader, coreConfig?: CoreOptions);
69
+ constructor(pluginLoader?: PluginLoader, coreConfig?: CoreOptions, setCore?: boolean);
67
70
  /**
68
71
  * 获取或创建插件对应 Context
69
72
  */
package/dist/core.js CHANGED
@@ -49,6 +49,7 @@ const route_1 = require("./route");
49
49
  const context_1 = require("./context");
50
50
  const hook_1 = require("./hook");
51
51
  const server_1 = require("./server");
52
+ const i18n_1 = require("./i18n");
52
53
  class Core {
53
54
  plugins = {};
54
55
  config = null;
@@ -62,6 +63,7 @@ class Core {
62
63
  hooks = {};
63
64
  coreConfig;
64
65
  server;
66
+ i18n;
65
67
  pluginWatchers = {};
66
68
  pluginModules = {};
67
69
  configPath = '';
@@ -69,9 +71,11 @@ class Core {
69
71
  * 插件名 -> Context 映射
70
72
  */
71
73
  pluginContexts = {};
72
- constructor(pluginLoader, coreConfig) {
74
+ constructor(pluginLoader, coreConfig, setCore = true) {
73
75
  this.coreConfig = coreConfig;
74
76
  this.pluginLoader = pluginLoader;
77
+ if (setCore)
78
+ logger_1.Logger.setCore(this);
75
79
  }
76
80
  /**
77
81
  * 获取或创建插件对应 Context
@@ -111,6 +115,7 @@ class Core {
111
115
  this.logger.error('Failed to load config:', e);
112
116
  throw e;
113
117
  }
118
+ this.i18n = new i18n_1.I18n(this.coreConfig.lang || ['zh', 'en']);
114
119
  }
115
120
  /**
116
121
  * 启动应用
package/dist/hook.js CHANGED
@@ -41,9 +41,9 @@ class Hook {
41
41
  * @returns 执行结果
42
42
  */
43
43
  async trigger(...args) {
44
- let result;
44
+ let result = [];
45
45
  for (const handler of Object.values(this.handlers)) {
46
- result.push(handler(...args));
46
+ result.push(await handler(...args));
47
47
  }
48
48
  return result;
49
49
  }
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @time: 2025/10/28 22:13
3
+ * @author: FireGuo
4
+ * WindyPear-Team All right reserved
5
+ **/
6
+ type I18nData = Record<string, Record<string, string>>;
7
+ export declare class I18n {
8
+ private data;
9
+ private fallback;
10
+ constructor(fallback?: string[]);
11
+ register(key: string | Record<string, any>, lang?: Record<string, string>): void;
12
+ setFallback(fallback: string[]): void;
13
+ private flattenAndRegister;
14
+ isRegistered(key: string): boolean;
15
+ delete(key: string): void;
16
+ /**
17
+ * 获取指定key的翻译
18
+ * @param key 文本点
19
+ * @param langs 用户的语言优先级数组
20
+ */
21
+ get(key: string, langs?: string[]): string;
22
+ /**
23
+ * 替换模板字符串中的文本点
24
+ * e.g. "Hello {{app.title}}" -> "Hello 世界"
25
+ */
26
+ replaceAll(input: string, langs?: string[]): string;
27
+ all(): I18nData;
28
+ }
29
+ export {};
package/dist/i18n.js ADDED
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * @time: 2025/10/28 22:13
4
+ * @author: FireGuo
5
+ * WindyPear-Team All right reserved
6
+ **/
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.I18n = void 0;
9
+ class I18n {
10
+ data = {};
11
+ fallback;
12
+ constructor(fallback = ['en']) {
13
+ this.fallback = fallback;
14
+ }
15
+ register(key, lang) {
16
+ if (typeof key === 'string' && lang) {
17
+ if (!this.data[key])
18
+ this.data[key] = {};
19
+ Object.assign(this.data[key], lang);
20
+ }
21
+ else if (typeof key === 'object') {
22
+ this.flattenAndRegister(key);
23
+ }
24
+ }
25
+ setFallback(fallback) {
26
+ this.fallback = fallback;
27
+ }
28
+ flattenAndRegister(obj, prefix = '') {
29
+ for (const [k, v] of Object.entries(obj)) {
30
+ const fullKey = prefix ? `${prefix}.${k}` : k;
31
+ if (typeof v === 'object' && !('zh' in v || 'en' in v)) {
32
+ this.flattenAndRegister(v, fullKey);
33
+ }
34
+ else if (typeof v === 'object') {
35
+ if (!this.data[fullKey])
36
+ this.data[fullKey] = {};
37
+ Object.assign(this.data[fullKey], v);
38
+ }
39
+ }
40
+ }
41
+ isRegistered(key) {
42
+ return !!this.data[key];
43
+ }
44
+ delete(key) {
45
+ delete this.data[key];
46
+ }
47
+ /**
48
+ * 获取指定key的翻译
49
+ * @param key 文本点
50
+ * @param langs 用户的语言优先级数组
51
+ */
52
+ get(key, langs) {
53
+ const entry = this.data[key];
54
+ if (!entry)
55
+ return key;
56
+ if (langs && langs.length) {
57
+ for (const l of langs) {
58
+ if (entry[l])
59
+ return entry[l];
60
+ }
61
+ }
62
+ for (const fb of this.fallback) {
63
+ if (entry[fb])
64
+ return entry[fb];
65
+ }
66
+ return key;
67
+ }
68
+ /**
69
+ * 替换模板字符串中的文本点
70
+ * e.g. "Hello {{app.title}}" -> "Hello 世界"
71
+ */
72
+ replaceAll(input, langs) {
73
+ return input.replace(/\{\{(.*?)\}\}/g, (_, key) => this.get(key.trim(), langs));
74
+ }
75
+ all() {
76
+ return this.data;
77
+ }
78
+ }
79
+ exports.I18n = I18n;
package/dist/index.d.ts CHANGED
@@ -11,3 +11,4 @@ export * from './logger';
11
11
  export * from './context';
12
12
  export * from './hook';
13
13
  export * from './middleware';
14
+ export * from './i18n';
package/dist/index.js CHANGED
@@ -27,3 +27,4 @@ __exportStar(require("./logger"), exports);
27
27
  __exportStar(require("./context"), exports);
28
28
  __exportStar(require("./hook"), exports);
29
29
  __exportStar(require("./middleware"), exports);
30
+ __exportStar(require("./i18n"), exports);
package/dist/logger.d.ts CHANGED
@@ -8,7 +8,11 @@ export declare class Logger {
8
8
  private title;
9
9
  private titleColor;
10
10
  static coreInstance: Core | null;
11
- static logs: string[];
11
+ static logs: {
12
+ level: string;
13
+ message: string;
14
+ timestamp: string;
15
+ }[];
12
16
  static setCore(core: Core): void;
13
17
  constructor(title: string);
14
18
  private getRandomColor;
package/dist/logger.js CHANGED
@@ -17,8 +17,7 @@ class Logger {
17
17
  static logs = [];
18
18
  static setCore(core) {
19
19
  if (Logger.coreInstance !== null) {
20
- const logger = new Logger('Logger');
21
- logger.warn("Logger.coreInstance is already set. Overwriting would cause confusion.");
20
+ return;
22
21
  }
23
22
  Logger.coreInstance = core;
24
23
  }
@@ -43,7 +42,8 @@ class Logger {
43
42
  const levelColor = level === 'E' ? picocolors_1.default.red : level === 'W' ? picocolors_1.default.yellow : picocolors_1.default.cyan;
44
43
  const timestamp = this.getTimestamp();
45
44
  console.log(`${picocolors_1.default.gray(timestamp)} [${levelColor(level)}] ${this.titleColor(this.title)}`, ...args);
46
- Logger.coreInstance?.emit('log', { level, message: args.join(' ') });
45
+ Logger.coreInstance?.emit('log', { level, message: args.join(' '), timestamp });
46
+ Logger.logs.push({ level, message: args.join(' '), timestamp });
47
47
  }
48
48
  info(...args) { this.log('I', ...args); }
49
49
  warn(...args) { this.log('W', ...args); }
package/dist/route.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  **/
6
6
  import { Session } from './session';
7
7
  import { Middleware } from './middleware';
8
+ import { WebSocketServer } from 'ws';
8
9
  export type RouteHandler = (session: Session, queryParams: URLSearchParams, ...pathParams: string[]) => Promise<void> | void;
9
10
  /**
10
11
  * Route class using segment-based matching (no fragile capture-group-index mapping).
@@ -21,6 +22,7 @@ export declare class Route {
21
22
  private handler;
22
23
  middlewares: Middleware[];
23
24
  allowedMethods: string[];
25
+ ws: WebSocketServer;
24
26
  /**
25
27
  * 创建路由
26
28
  * @param path 路由路径
@@ -60,4 +62,9 @@ export declare class Route {
60
62
  * @returns this
61
63
  */
62
64
  methods(...methods: string[]): Route;
65
+ /**
66
+ * 注册Ws处理器
67
+ * @param handler 处理器
68
+ */
69
+ wsOn(event: string, handler: (...args: any[]) => void): this;
63
70
  }