chanjs 2.7.3 → 2.7.5

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 (93) hide show
  1. package/USAGE.md +533 -0
  2. package/config/index.js +37 -6
  3. package/core/App.js +166 -0
  4. package/core/Container.js +77 -0
  5. package/core/Controller.js +29 -0
  6. package/core/Database.js +93 -0
  7. package/core/Repository.js +327 -0
  8. package/core/Service.js +11 -0
  9. package/core/bootstrap/error-handler.js +104 -0
  10. package/core/bootstrap/hook-runner.js +64 -0
  11. package/core/bootstrap/middleware.js +35 -0
  12. package/core/bootstrap/router-loader.js +53 -0
  13. package/core/errors.js +224 -0
  14. package/core/loader.js +89 -0
  15. package/core/registry.js +17 -0
  16. package/doc/Cache.md +279 -106
  17. package/doc/Common.md +590 -134
  18. package/doc/Controller.md +166 -95
  19. package/doc/Help.md +299 -698
  20. package/doc/QuickStart.md +116 -0
  21. package/doc/Repository.md +560 -0
  22. package/doc/Service.md +201 -527
  23. package/index.js +75 -37
  24. package/middleware/body.js +17 -0
  25. package/middleware/cookie.js +7 -15
  26. package/middleware/cors.js +9 -27
  27. package/middleware/favicon.js +7 -17
  28. package/middleware/header.js +15 -16
  29. package/middleware/index.js +11 -11
  30. package/middleware/log.js +26 -56
  31. package/middleware/static.js +15 -28
  32. package/middleware/template.js +75 -115
  33. package/middleware/validate.js +79 -0
  34. package/middleware/waf.js +174 -197
  35. package/package.json +11 -3
  36. package/response/code.js +73 -0
  37. package/response/index.js +9 -6
  38. package/response/response.js +82 -236
  39. package/security/checker.js +26 -74
  40. package/security/index.js +4 -9
  41. package/security/jwt.js +69 -142
  42. package/security/keywords.js +32 -136
  43. package/security/rate-limit.js +38 -80
  44. package/security/sign.js +83 -176
  45. package/security/xss-filter.js +21 -53
  46. package/storage/cache.js +57 -196
  47. package/storage/index.js +3 -6
  48. package/storage/redis.js +123 -181
  49. package/storage/store.js +163 -188
  50. package/utils/data-parse.js +42 -186
  51. package/utils/file.js +73 -244
  52. package/utils/filter.js +22 -25
  53. package/utils/html.js +49 -33
  54. package/utils/index.js +21 -7
  55. package/utils/ip.js +31 -71
  56. package/utils/logger.js +117 -0
  57. package/utils/pages.js +55 -0
  58. package/utils/paths.js +18 -0
  59. package/utils/request.js +94 -136
  60. package/utils/signal.js +87 -0
  61. package/utils/time.js +33 -75
  62. package/utils/tree.js +112 -104
  63. package/App.js +0 -533
  64. package/base/Aop.js +0 -195
  65. package/base/Container.js +0 -161
  66. package/base/Controller.js +0 -65
  67. package/base/Database.js +0 -133
  68. package/base/Event.js +0 -61
  69. package/base/Repository.js +0 -644
  70. package/common/api.js +0 -35
  71. package/common/code.js +0 -52
  72. package/common/email.js +0 -191
  73. package/common/index.js +0 -5
  74. package/common/pages.js +0 -120
  75. package/common/utils.js +0 -73
  76. package/config/code.js +0 -166
  77. package/config/paths.js +0 -60
  78. package/doc/Aop.md +0 -269
  79. package/doc/Email.md +0 -114
  80. package/doc/Event.md +0 -232
  81. package/global/env.js +0 -11
  82. package/global/import.js +0 -39
  83. package/global/index.js +0 -8
  84. package/helper/index.js +0 -79
  85. package/loader/index.js +0 -6
  86. package/loader/loader.js +0 -138
  87. package/middleware/compress.js +0 -185
  88. package/middleware/setBody.js +0 -32
  89. package/realtime/index.js +0 -7
  90. package/realtime/sse.js +0 -424
  91. package/realtime/websocket.js +0 -540
  92. package/schedule/index.js +0 -6
  93. package/schedule/schedule.js +0 -491
package/base/Aop.js DELETED
@@ -1,195 +0,0 @@
1
- /**
2
- * @description: 轻量级切面类 用于注册和执行切面函数
3
- * @class Aop
4
- * @property {Map} aspects - 存储注册的切面函数
5
- * @property {Set} types - 切面类型集合(内置关键字)
6
- */
7
- export class Aop {
8
- // 定义内置的切面类型关键字(避免和切面名称冲突)
9
- static TYPES = new Set(['type', 'enabled']);
10
- // 支持的切面类型(仅保留常用的3种)
11
- static SUPPORTED_TYPES = new Set(['before', 'after', 'error']);
12
- // 幂等标记 Symbol,避免 wrap 多次嵌套包装
13
- static WRAP_FLAG = Symbol('aop.wrapped');
14
-
15
- constructor() {
16
- this.aspects = new Map();
17
- }
18
-
19
- /**
20
- * 注册切面
21
- * @param {string} name - 切面名称
22
- * @param {Function} fn - 切面函数
23
- * @returns {Aop} - 当前实例(链式调用)
24
- * @throws {TypeError} - 入参类型错误时抛出
25
- */
26
- set(name, fn) {
27
- if (typeof name !== 'string' || !name.trim()) {
28
- throw new TypeError(`切面名称 ${name} 必须是非空字符串`);
29
- }
30
- if (typeof fn !== 'function') {
31
- throw new TypeError(`切面 ${name} 必须是函数,当前类型:${typeof fn}`);
32
- }
33
- this.aspects.set(name.trim(), fn);
34
- return this;
35
- }
36
-
37
- /**
38
- * 获取已注册的切面函数
39
- * @param {string} name - 切面名称
40
- * @returns {Function|null} - 切面函数(不存在则返回null)
41
- */
42
- get(name) {
43
- return this.aspects.get(name?.trim()) || null;
44
- }
45
-
46
- /**
47
- * 移除指定切面
48
- * @param {string} name - 切面名称
49
- * @returns {Aop} - 当前实例
50
- */
51
- remove(name) {
52
- this.aspects.delete(name?.trim());
53
- return this;
54
- }
55
-
56
- /**
57
- * 清空所有切面
58
- * @returns {Aop} - 当前实例
59
- */
60
- clear() {
61
- this.aspects.clear();
62
- return this;
63
- }
64
-
65
- /**
66
- * 绑定切面到实例方法
67
- * @param {object} instance - 目标实例(如 new Controller())
68
- * @param {object} config - 切面配置 { methodName: [{ type: 'before', enabled: true, log: true }] }
69
- * @returns {object} - 绑定后的实例对象
70
- * @throws {TypeError} - 入参类型错误时抛出
71
- * @description
72
- * 幂等性:用 Symbol 标记已包装的方法,避免多次调用 wrap 导致嵌套
73
- * 同一方法第二次 wrap 会跳过,防止切面函数被重复执行
74
- */
75
- wrap(instance, config) {
76
- if (typeof instance !== 'object' || instance === null) {
77
- throw new TypeError('instance 必须是非空对象');
78
- }
79
- if (typeof config !== 'object' || config === null) {
80
- throw new TypeError('config 必须是非空对象');
81
- }
82
-
83
- Object.entries(config).forEach(([methodName, rules]) => {
84
- const originalMethod = instance[methodName];
85
- if (typeof originalMethod !== 'function') {
86
- console.warn(`[AOP警告] 实例不存在方法 ${methodName},跳过切面绑定`);
87
- return;
88
- }
89
-
90
- // 幂等检查:已包装过的方法直接跳过,避免多次 wrap 嵌套
91
- if (originalMethod[Aop.WRAP_FLAG]) {
92
- console.warn(`[AOP警告] 方法 ${methodName} 已包装过切面,跳过重复绑定`);
93
- return;
94
- }
95
-
96
- // 统一规则为数组 + 过滤禁用规则 + 过滤不支持的类型
97
- const ruleList = Array.isArray(rules) ? rules : [rules];
98
- const validRules = ruleList.filter(rule => {
99
- return rule?.enabled !== false && Aop.SUPPORTED_TYPES.has(rule?.type);
100
- });
101
-
102
- // 无有效规则时直接返回原方法
103
- if (validRules.length === 0) return;
104
-
105
- // 包装原方法(仅保留 before/after/error 逻辑)
106
- const wrapped = async (...args) => {
107
- const ctx = instance;
108
- let result;
109
-
110
- try {
111
- // 执行前置切面
112
- await this._executeAspectByType(ctx, validRules, 'before', args, originalMethod);
113
-
114
- // 执行原方法
115
- result = await Reflect.apply(originalMethod, ctx, args);
116
-
117
- // 执行后置切面(仅无异常时执行)
118
- await this._executeAspectByType(ctx, validRules, 'after', args, originalMethod, result);
119
- } catch (error) {
120
- // 执行异常切面
121
- await this._executeAspectByType(ctx, validRules, 'error', args, originalMethod, null, error);
122
- throw error; // 重新抛出异常,不阻断流程
123
- }
124
-
125
- return result;
126
- };
127
-
128
- // 打幂等标记,防止重复 wrap
129
- wrapped[Aop.WRAP_FLAG] = true;
130
- // 保留原方法名,便于调试
131
- Object.defineProperty(wrapped, 'name', { value: originalMethod.name, configurable: true });
132
- instance[methodName] = wrapped;
133
- });
134
-
135
- return instance;
136
- }
137
-
138
- /**
139
- * 按类型批量执行切面(私有方法)
140
- * @private
141
- * @param {object} ctx - 上下文
142
- * @param {Array} validRules - 有效规则列表
143
- * @param {string} type - 切面类型(before/after/error)
144
- * @param {Array} args - 方法参数
145
- * @param {Function} originalMethod - 原方法
146
- * @param {*} result - 方法执行结果
147
- * @param {Error} error - 方法执行错误
148
- */
149
- async _executeAspectByType(ctx, validRules, type, args, originalMethod, result = null, error = null) {
150
- const rulesOfType = validRules.filter(rule => rule.type === type);
151
- for (const rule of rulesOfType) {
152
- await this._executeSingleRule(ctx, rule, args, originalMethod, result, error);
153
- }
154
- }
155
-
156
- /**
157
- * 执行单个切面规则(私有方法)
158
- * @private
159
- * @param {object} ctx - 上下文
160
- * @param {object} rule - 单个切面规则
161
- * @param {Array} args - 方法参数
162
- * @param {Function} originalMethod - 原方法
163
- * @param {*} result - 方法执行结果
164
- * @param {Error} error - 方法执行错误
165
- */
166
- async _executeSingleRule(ctx, rule, args, originalMethod, result = null, error = null) {
167
- for (const [key, value] of Object.entries(rule)) {
168
- // 跳过内置关键字 + 空值
169
- if (Aop.TYPES.has(key) || !value) continue;
170
-
171
- const aspectFn = this.get(key);
172
- if (!aspectFn) {
173
- console.warn(`[AOP警告] 未注册切面 ${key},跳过执行`);
174
- continue;
175
- }
176
-
177
- // 构造切面入参(浅拷贝避免原数据被修改)
178
- const aspectParams = {
179
- ctx,
180
- methodName: originalMethod.name || 'anonymous',
181
- args: [...args],
182
- originalMethod,
183
- result,
184
- error,
185
- params: typeof value === 'object' && !Array.isArray(value) ? { ...value } : {}
186
- };
187
-
188
- await aspectFn(aspectParams);
189
- }
190
- }
191
- }
192
-
193
- // 全局单例
194
- export const aop = new Aop();
195
- export default Aop;
package/base/Container.js DELETED
@@ -1,161 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { Paths } from '../config/paths.js';
4
- import Chan from '../App.js';
5
-
6
- /**
7
- * 容器基类(依赖注入容器)
8
- *
9
- * ============================================================
10
- * 设计说明
11
- * ============================================================
12
- * 不再使用 global.Chan 全局污染。
13
- * 通过 import Chan 获取应用类引用,所有资源(config/db/paths 等)
14
- * 都通过 Chan 类的静态属性动态访问,避免构造时快照导致的不同步问题。
15
- *
16
- * 子类(Controller/Repository)可通过以下方式获取应用资源:
17
- * - this.app → Chan 类本身(静态属性容器)
18
- * - this.app.config → 应用配置
19
- * - this.app.db → 默认数据库连接
20
- * - this.app.dbManager → 数据库连接管理器
21
- * - this.app.paths → 路径配置(兼容 Chan.paths)
22
- *
23
- * ============================================================
24
- * DI 方法
25
- * ============================================================
26
- * - get(moduleName, fileName) → 加载业务模块组件(service/controller)
27
- * - getResource(name) → 获取应用级资源(config/db/dbManager/paths)
28
- */
29
- export class Container {
30
- constructor(type = 'service') {
31
- this.map = new Map();
32
- // 组件类型,例如:'service','controller'
33
- this.type = type;
34
- // modules 目录路径
35
- this.baseDir = Paths.modulesPath;
36
- // 应用类引用(DI 核心:通过它访问 config/db 等静态资源)
37
- this.app = Chan;
38
- }
39
-
40
- /**
41
- * 获取应用级资源(显式 DI)
42
- * @param {string} name - 资源名:'config' | 'db' | 'dbManager' | 'paths'
43
- * @returns {*} 对应的资源
44
- * @example
45
- * const config = this.getResource('config');
46
- * const db = this.getResource('db');
47
- */
48
- getResource(name) {
49
- return Chan[name];
50
- }
51
-
52
- /**
53
- * config 快捷访问(getter 动态读取,避免快照不同步)
54
- */
55
- get config() {
56
- return Chan.config;
57
- }
58
-
59
- /**
60
- * db 快捷访问(getter 动态读取)
61
- */
62
- get db() {
63
- return Chan.db;
64
- }
65
-
66
- /**
67
- * paths 快捷访问(getter 动态读取)
68
- */
69
- get paths() {
70
- return Chan.paths || Paths;
71
- }
72
-
73
- /**
74
- * 获取模块下的组件实例
75
- * @param {string} moduleName - 模块名 ,例如:'web','api'
76
- * @param {string} fileName - 文件名
77
- * @returns {Promise<any>} - 组件实例
78
- */
79
- async get(moduleName, fileName) {
80
- const key = `${moduleName}.${fileName}`;
81
- // 如果组件实例已存在,直接返回
82
- if (this.map.has(key)) {
83
- return this.map.get(key);
84
- }
85
-
86
- try {
87
- // 没有,加载组件实例
88
- await this.loadFile(moduleName, fileName);
89
- const obj = this.map.get(key);
90
- if (!obj) {
91
- console.log(`文件 ${fileName}.js 加载后组件实例为空`);
92
- }
93
- return obj;
94
- } catch (err) {
95
- console.error(`获取模块 ${moduleName} 下文件 ${fileName}.js 对应的组件失败:${err.message}`);
96
- return null;
97
- }
98
- }
99
-
100
- /**
101
- * 设置模块下的组件实例
102
- * @param {string} moduleName - 模块名 ,例如:'web','api'
103
- * @param {string} fileName - 文件名
104
- * @param {any} obj - 组件实例
105
- * @returns {Container} - 当前实例
106
- */
107
- set(moduleName, fileName, obj) {
108
- const key = `${moduleName}.${fileName}`;
109
- this.map.set(key, obj);
110
- return this;
111
- }
112
-
113
- /**
114
- * 加载模块下的组件实例
115
- * @param {string} moduleName - 模块名 ,例如:'web','api'
116
- * @param {string} fileName - 文件名
117
- * @returns {Promise<any>} - 组件实例
118
- * @description
119
- * 安全改进:
120
- * - 名称白名单校验(仅允许字母开头、字母数字下划线/横线),拒绝路径注入
121
- * - 解析后路径必须仍在 baseDir 内,防止路径越界
122
- * - 用 fs.promises.access 替代 existsSync,避免同步 IO 阻塞事件循环
123
- */
124
- async loadFile(moduleName, fileName) {
125
- // 模块/文件名白名单:字母开头,允许字母数字下划线横线
126
- const NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
127
- if (!NAME_REGEX.test(moduleName) || !NAME_REGEX.test(fileName)) {
128
- throw new Error(`非法模块/文件名: ${moduleName}/${fileName}`);
129
- }
130
-
131
- const filePath = path.resolve(
132
- this.baseDir,
133
- moduleName,
134
- this.type,
135
- `${fileName}.js`
136
- );
137
-
138
- // 路径越界检查:解析后必须仍在 baseDir 内
139
- const rel = path.relative(this.baseDir, filePath);
140
- if (rel.startsWith('..') || path.isAbsolute(rel)) {
141
- throw new Error(`路径越界: ${filePath}`);
142
- }
143
-
144
- // 异步检查文件存在性,避免同步 IO
145
- try {
146
- await fs.promises.access(filePath, fs.constants.R_OK);
147
- } catch (e) {
148
- console.log(`模块 ${moduleName} 下 ${this.type} 目录中未找到文件:${fileName}.js`);
149
- return null;
150
- }
151
-
152
- const module = await import(`file://${filePath}`);
153
- const obj = module.default;
154
-
155
- const key = `${moduleName}.${fileName}`;
156
- this.map.set(key, obj);
157
- return obj;
158
- }
159
- }
160
-
161
- export default Container;
@@ -1,65 +0,0 @@
1
- import { success, fail } from "../response/response.js";
2
- import Container from "./Container.js";
3
-
4
- /**
5
- * 控制器基类
6
- * 提供统一的响应格式和常用方法
7
- *
8
- * ============================================================
9
- * 响应格式(三层 code 体系)
10
- * ============================================================
11
- * 所有响应都遵循:{ success, code, msg, data }
12
- * - code: 0 成功 / 1xxx 业务 / 5xxx 系统 / 6xxx 数据库
13
- * - HTTP Status 由网关/Express 控制,与业务 code 解耦
14
- *
15
- * 使用示例:
16
- * this.success(data) // → code: 0
17
- * this.success({ data, msg: '创建成功' }) // → code: 0
18
- * this.fail({ msg: '栏目下存在文章' }) // → code: 1008 业务失败
19
- * this.fail({ msg: '参数缺失', code: 1007 }) // → code: 1007
20
- * this.fail('密码错误') // 字符串简写,→ code: 1008
21
- */
22
- export default class Controller extends Container {
23
- constructor() {
24
- // 调用父类构造函数,指定组件类型为 'controller'
25
- super('controller');
26
- }
27
-
28
- /**
29
- * 返回成功响应
30
- * @param {Object} options - 响应选项
31
- * @param {*} options.data - 响应数据
32
- * @param {string} [options.msg="操作成功"] - 响应消息
33
- * @returns {Object} 标准成功响应,code = 0
34
- */
35
- success({ data, msg = "操作成功" } = {}) {
36
- return success({ data, msg });
37
- }
38
-
39
- /**
40
- * 返回失败响应
41
- * @param {Object|string} options - 响应选项,或失败消息字符串(简写)
42
- * @param {string} [options.msg="操作失败"] - 失败消息
43
- * @param {*} [options.data={}] - 响应数据
44
- * @param {number} [options.code=1008] - 业务 code,默认 1008(业务处理失败)
45
- * @returns {Object} 标准失败响应
46
- * @description
47
- * 兼容两种调用方式:
48
- * - 对象:this.fail({ msg: '...', code: 1007 })
49
- * - 字符串简写:this.fail('密码错误') // 等价于 this.fail({ msg: '密码错误' })
50
- *
51
- * 默认 code = 1008(业务处理失败)
52
- * 业务方可指定 code:
53
- * - 1001 认证失败 / 1002 token 过期 / 1003 权限不足
54
- * - 1004 资源不存在 / 1005 资源已存在
55
- * - 1006 参数无效 / 1007 参数缺失
56
- * - 1008 业务处理失败(通用兜底)
57
- */
58
- fail(options = {}) {
59
- // 字符串简写:this.fail('错误消息') → this.fail({ msg: '错误消息' })
60
- if (typeof options === 'string') {
61
- return fail({ msg: options });
62
- }
63
- return fail(options);
64
- }
65
- }
package/base/Database.js DELETED
@@ -1,133 +0,0 @@
1
- import knex from "knex";
2
-
3
- /**
4
- * 数据库管理器类
5
- * 用于管理多个数据库连接
6
- */
7
- class DatabaseManager {
8
- /**
9
- * 构造函数
10
- * 初始化数据库连接存储和默认连接名称
11
- */
12
- constructor() {
13
- this._connections = new Map();
14
- this._defaultName = "default";
15
- }
16
-
17
- /**
18
- * 添加数据库连接
19
- * @param {string} name - 连接名称
20
- * @param {Object} config - Knex配置对象
21
- * @param {Object} options - 选项
22
- * @param {boolean} options.isDefault - 是否设为默认连接
23
- * @returns {Object} Knex连接实例
24
- */
25
- add(name, config, options = {}) {
26
- const { isDefault = false } = options;
27
-
28
- const connection = knex(config);
29
- this._connections.set(name, connection);
30
-
31
- if (isDefault || this._connections.size === 1) {
32
- this._defaultName = name;
33
- }
34
-
35
- return connection;
36
- }
37
-
38
- /**
39
- * 获取数据库连接
40
- * @param {string} name - 连接名称,默认为默认连接
41
- * @returns {Object} Knex连接实例
42
- * @throws {Error} 连接不存在时抛出异常
43
- */
44
- get(name = this._defaultName) {
45
- const connection = this._connections.get(name);
46
- if (!connection) {
47
- throw new Error(`Database connection "${name}" not found`);
48
- }
49
- return connection;
50
- }
51
-
52
- /**
53
- * 获取默认数据库连接
54
- * @returns {Object} Knex连接实例
55
- */
56
- getDefault() {
57
- return this.get(this._defaultName);
58
- }
59
-
60
- /**
61
- * 检查连接是否存在
62
- * @param {string} name - 连接名称
63
- * @returns {boolean} 是否存在
64
- */
65
- has(name) {
66
- return this._connections.has(name);
67
- }
68
-
69
- /**
70
- * 移除数据库连接
71
- * @param {string} name - 连接名称
72
- */
73
- remove(name) {
74
- const connection = this._connections.get(name);
75
- if (connection) {
76
- connection.destroy();
77
- this._connections.delete(name);
78
- }
79
- }
80
-
81
- /**
82
- * 关闭所有数据库连接(优雅停机使用)
83
- * 单个连接关闭失败不影响其他连接
84
- * @returns {Promise<Array<{name:string, success:boolean, error?:string}>>} 每个连接的关闭结果
85
- */
86
- async closeAll() {
87
- const results = [];
88
- for (const [name, conn] of this._connections) {
89
- try {
90
- await conn.destroy();
91
- results.push({ name, success: true });
92
- } catch (err) {
93
- results.push({ name, success: false, error: err.message });
94
- console.error(`[Database] 关闭连接 ${name} 失败:`, err.message);
95
- }
96
- }
97
- this._connections.clear();
98
- console.log(`[Database] 已关闭所有连接(共 ${results.length} 个)`);
99
- return results;
100
- }
101
-
102
- /**
103
- * 心跳检查(单个连接)
104
- * @param {string} [name] - 连接名称,默认检查默认连接
105
- * @returns {Promise<boolean>} 是否健康
106
- */
107
- async ping(name = this._defaultName) {
108
- const conn = this._connections.get(name);
109
- if (!conn) return false;
110
- try {
111
- // 用最简 SELECT 1 触发一次实际查询,验证连接可用
112
- await conn.raw('SELECT 1');
113
- return true;
114
- } catch (err) {
115
- console.error(`[Database] ping ${name} 失败:`, err.message);
116
- return false;
117
- }
118
- }
119
-
120
- /**
121
- * 获取所有连接状态(健康检查 / 监控使用)
122
- * @returns {Array<{name:string, isDefault:boolean}>} 连接列表
123
- */
124
- getStatus() {
125
- return Array.from(this._connections.keys()).map(name => ({
126
- name,
127
- isDefault: name === this._defaultName,
128
- }));
129
- }
130
-
131
- }
132
-
133
- export default DatabaseManager;
package/base/Event.js DELETED
@@ -1,61 +0,0 @@
1
- import { EventEmitter } from 'events';
2
-
3
- /**
4
- * 默认最大监听器数量上限,防止业务侧泄漏过多监听器导致内存警告
5
- */
6
- const DEFAULT_MAX_LISTENERS = 50;
7
-
8
- export class Event extends EventEmitter {
9
- constructor() {
10
- super();
11
- // 设置最大监听器数量,避免业务侧泄漏导致 process 警告
12
- this.setMaxListeners(DEFAULT_MAX_LISTENERS);
13
- // 兜底 error 监听器:未注册 error 监听时 EventEmitter 默认会抛出并崩溃进程
14
- // 这里注册一个仅打印日志的 error 处理器,确保 unhandled error 不会让进程退出
15
- this.on('error', (err) => {
16
- console.error('[Event] 未处理的 error 事件:', err?.message || err);
17
- });
18
- }
19
-
20
- /**
21
- * 注册事件监听器
22
- * @param {string} eventName - 事件名
23
- * @param {*} listener - 监听器函数
24
- * @returns {Event} - 当前实例
25
- */
26
- on(eventName, listener) {
27
- return super.on(eventName, listener);
28
- }
29
-
30
- /**
31
- * 触发事件
32
- * @param {string} eventName - 事件名
33
- * @param {*} data - 事件数据
34
- * @returns {Event} - 当前实例
35
- */
36
- emit(eventName, data) {
37
- return super.emit(eventName, data);
38
- }
39
-
40
- /**
41
- * 注销事件监听器
42
- * @param {string} eventName - 事件名
43
- * @param {*} listener - 监听器函数
44
- * @returns {Event} - 当前实例
45
- */
46
- off(eventName, listener) {
47
- return super.off(eventName, listener);
48
- }
49
-
50
- /**
51
- * 注销所有事件监听器
52
- * @param {string} eventName - 事件名
53
- * @returns {Event} - 当前实例
54
- */
55
- removeAllListeners(eventName) {
56
- return super.removeAllListeners(eventName);
57
- }
58
- }
59
-
60
- export const event = new Event();
61
- export default Event;