@taicode/common-base 1.6.3 → 1.7.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/output/logger/logger.d.ts +15 -2
- package/output/logger/logger.d.ts.map +1 -1
- package/output/logger/logger.js +67 -5
- package/output/logger/logger.test.js +90 -0
- package/output/logger/transport.d.ts.map +1 -1
- package/output/logger/transport.js +1 -1
- package/package.json +1 -1
- package/output/events/disposer.d.ts +0 -6
- package/output/events/disposer.d.ts.map +0 -1
- package/output/events/disposer.js +0 -19
- package/output/events/disposer.test.d.ts +0 -2
- package/output/events/disposer.test.d.ts.map +0 -1
- package/output/events/disposer.test.js +0 -192
- package/output/events/event-emitter.d.ts +0 -33
- package/output/events/event-emitter.d.ts.map +0 -1
- package/output/events/event-emitter.js +0 -66
- package/output/events/event-emitter.test.d.ts +0 -2
- package/output/events/event-emitter.test.d.ts.map +0 -1
- package/output/events/event-emitter.test.js +0 -213
- package/output/events/index.d.ts +0 -3
- package/output/events/index.d.ts.map +0 -1
- package/output/events/index.js +0 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LogFormatter } from './formatter';
|
|
2
2
|
import { LogTransport } from './transport';
|
|
3
|
-
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
3
|
+
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
|
4
4
|
export interface LoggerOptions {
|
|
5
5
|
level?: LogLevel;
|
|
6
6
|
/** LogTransport 实例数组,用于处理结构化日志数据 */
|
|
@@ -11,7 +11,7 @@ export interface LoggerOptions {
|
|
|
11
11
|
print?: (level: LogLevel, ...args: any[]) => void;
|
|
12
12
|
}
|
|
13
13
|
export declare class Logger {
|
|
14
|
-
private
|
|
14
|
+
private _level;
|
|
15
15
|
private transports;
|
|
16
16
|
private formatter;
|
|
17
17
|
private print?;
|
|
@@ -26,10 +26,13 @@ export declare class Logger {
|
|
|
26
26
|
* 使用传统 print 方式记录日志(向后兼容)
|
|
27
27
|
*/
|
|
28
28
|
private logToPrint;
|
|
29
|
+
trace(...args: any[]): void;
|
|
29
30
|
debug(...args: any[]): void;
|
|
30
31
|
info(...args: any[]): void;
|
|
31
32
|
warn(...args: any[]): void;
|
|
32
33
|
error(...args: any[]): void;
|
|
34
|
+
fatal(...args: any[]): void;
|
|
35
|
+
silent(): void;
|
|
33
36
|
/**
|
|
34
37
|
* 添加 LogTransport
|
|
35
38
|
*/
|
|
@@ -46,6 +49,16 @@ export declare class Logger {
|
|
|
46
49
|
* 关闭所有 LogTransports
|
|
47
50
|
*/
|
|
48
51
|
close(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Pino 兼容的 child 方法
|
|
54
|
+
* 创建一个共享配置的子 logger
|
|
55
|
+
*/
|
|
56
|
+
child(bindings?: Record<string, any>, options?: Partial<LoggerOptions>): Logger;
|
|
57
|
+
/**
|
|
58
|
+
* Pino 兼容的 level getter/setter
|
|
59
|
+
*/
|
|
60
|
+
get level(): LogLevel;
|
|
61
|
+
set level(newLevel: LogLevel);
|
|
49
62
|
}
|
|
50
63
|
export declare const logger: Logger;
|
|
51
64
|
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../source/logger/logger.ts"],"names":[],"mappings":"AACA,OAAO,EAA4C,YAAY,EAAE,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,YAAY,EAAa,MAAM,aAAa,CAAA;AAErD,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../source/logger/logger.ts"],"names":[],"mappings":"AACA,OAAO,EAA4C,YAAY,EAAE,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,YAAY,EAAa,MAAM,aAAa,CAAA;AAErD,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;AAE9E,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,oCAAoC;IACpC,UAAU,CAAC,EAAE,YAAY,EAAE,CAAA;IAC3B,aAAa;IACb,SAAS,CAAC,EAAE,YAAY,CAAA;IACxB,0BAA0B;IAC1B,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;CAClD;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,KAAK,CAAC,CAA2C;gBAE7C,OAAO,GAAE,aAAkB;IAYvC,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,SAAS;IAKjB;;OAEG;IACH,OAAO,CAAC,eAAe;IA2BvB;;OAEG;IACH,OAAO,CAAC,UAAU;IAUlB,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE;IAOpB,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE;IAOpB,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE;IAOnB,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE;IAOnB,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE;IAOpB,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE;IAOpB,MAAM;IAIN;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,YAAY,GAAG,IAAI;IAI3C;;OAEG;IACH,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI;IAI5C;;OAEG;IACH,aAAa,IAAI,SAAS,YAAY,EAAE;IAIxC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B;;;OAGG;IACH,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,MAAM;IAkB/E;;OAEG;IACH,IAAI,KAAK,IAAI,QAAQ,CAEpB;IAED,IAAI,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAE3B;CACF;AAED,eAAO,MAAM,MAAM,QAAe,CAAA"}
|
package/output/logger/logger.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// 基础 Logger 实现,支持简单的日志输出和扩展能力
|
|
2
2
|
import { defaultLogFormatter } from './formatter';
|
|
3
3
|
export class Logger {
|
|
4
|
-
|
|
4
|
+
_level;
|
|
5
5
|
transports;
|
|
6
6
|
formatter;
|
|
7
7
|
print;
|
|
8
8
|
constructor(options = {}) {
|
|
9
|
-
this.
|
|
9
|
+
this._level = options.level || 'info';
|
|
10
10
|
this.formatter = options.formatter || defaultLogFormatter;
|
|
11
11
|
this.transports = options.transports || [];
|
|
12
12
|
this.print = options.print;
|
|
@@ -17,12 +17,32 @@ export class Logger {
|
|
|
17
17
|
}
|
|
18
18
|
defaultPrint(level, ...args) {
|
|
19
19
|
const prefix = `[${level.toUpperCase()}]`;
|
|
20
|
+
// 映射 pino 兼容的 level 到 console 方法
|
|
21
|
+
let consoleMethod;
|
|
22
|
+
switch (level) {
|
|
23
|
+
case 'trace':
|
|
24
|
+
case 'debug':
|
|
25
|
+
consoleMethod = console.debug || console.log;
|
|
26
|
+
break;
|
|
27
|
+
case 'info':
|
|
28
|
+
consoleMethod = console.info || console.log;
|
|
29
|
+
break;
|
|
30
|
+
case 'warn':
|
|
31
|
+
consoleMethod = console.warn || console.log;
|
|
32
|
+
break;
|
|
33
|
+
case 'error':
|
|
34
|
+
case 'fatal':
|
|
35
|
+
consoleMethod = console.error || console.log;
|
|
36
|
+
break;
|
|
37
|
+
default:
|
|
38
|
+
consoleMethod = console.log;
|
|
39
|
+
}
|
|
20
40
|
// eslint-disable-next-line no-console
|
|
21
|
-
(
|
|
41
|
+
consoleMethod(prefix, ...args);
|
|
22
42
|
}
|
|
23
43
|
shouldLog(level) {
|
|
24
|
-
const order = ['debug', 'info', 'warn', 'error'];
|
|
25
|
-
return order.indexOf(level) >= order.indexOf(this.
|
|
44
|
+
const order = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
|
|
45
|
+
return order.indexOf(level) >= order.indexOf(this._level);
|
|
26
46
|
}
|
|
27
47
|
/**
|
|
28
48
|
* 记录日志到所有 transports
|
|
@@ -63,6 +83,12 @@ export class Logger {
|
|
|
63
83
|
const formattedArgs = this.formatter.formatArguments(args);
|
|
64
84
|
this.print(level, ...formattedArgs);
|
|
65
85
|
}
|
|
86
|
+
trace(...args) {
|
|
87
|
+
if (this.shouldLog('trace')) {
|
|
88
|
+
this.logToTransports('trace', args);
|
|
89
|
+
this.logToPrint('trace', args);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
66
92
|
debug(...args) {
|
|
67
93
|
if (this.shouldLog('debug')) {
|
|
68
94
|
this.logToTransports('debug', args);
|
|
@@ -87,6 +113,15 @@ export class Logger {
|
|
|
87
113
|
this.logToPrint('error', args);
|
|
88
114
|
}
|
|
89
115
|
}
|
|
116
|
+
fatal(...args) {
|
|
117
|
+
if (this.shouldLog('fatal')) {
|
|
118
|
+
this.logToTransports('fatal', args);
|
|
119
|
+
this.logToPrint('fatal', args);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
silent() {
|
|
123
|
+
// Pino 兼容的 silent 方法 - 无操作
|
|
124
|
+
}
|
|
90
125
|
/**
|
|
91
126
|
* 添加 LogTransport
|
|
92
127
|
*/
|
|
@@ -114,5 +149,32 @@ export class Logger {
|
|
|
114
149
|
.filter(Boolean);
|
|
115
150
|
await Promise.all(closePromises);
|
|
116
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Pino 兼容的 child 方法
|
|
154
|
+
* 创建一个共享配置的子 logger
|
|
155
|
+
*/
|
|
156
|
+
child(bindings, options) {
|
|
157
|
+
// 合并选项,子 logger 继承父 logger 的配置
|
|
158
|
+
const childOptions = {
|
|
159
|
+
level: options?.level || this._level,
|
|
160
|
+
transports: options?.transports || this.transports,
|
|
161
|
+
formatter: options?.formatter || this.formatter,
|
|
162
|
+
print: options?.print || this.print
|
|
163
|
+
};
|
|
164
|
+
const childLogger = new Logger(childOptions);
|
|
165
|
+
// 如果提供了 bindings,我们可以在这里处理
|
|
166
|
+
// 目前的实现中,bindings 主要用于标识,不直接影响日志输出
|
|
167
|
+
// 在更完整的实现中,这些 bindings 会被添加到每个日志条目中
|
|
168
|
+
return childLogger;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Pino 兼容的 level getter/setter
|
|
172
|
+
*/
|
|
173
|
+
get level() {
|
|
174
|
+
return this._level;
|
|
175
|
+
}
|
|
176
|
+
set level(newLevel) {
|
|
177
|
+
this._level = newLevel;
|
|
178
|
+
}
|
|
117
179
|
}
|
|
118
180
|
export const logger = new Logger();
|
|
@@ -7,24 +7,52 @@ describe('Logger', () => {
|
|
|
7
7
|
it('应该默认记录 info 及以上级别', () => {
|
|
8
8
|
const print = vi.fn();
|
|
9
9
|
const logger = new Logger({ print });
|
|
10
|
+
logger.trace('trace');
|
|
10
11
|
logger.debug('debug');
|
|
11
12
|
logger.info('info');
|
|
12
13
|
logger.warn('warn');
|
|
13
14
|
logger.error('error');
|
|
15
|
+
logger.fatal('fatal');
|
|
16
|
+
expect(print).not.toHaveBeenCalledWith('trace', 'trace');
|
|
14
17
|
expect(print).not.toHaveBeenCalledWith('debug', 'debug');
|
|
15
18
|
expect(print).toHaveBeenCalledWith('info', 'info');
|
|
16
19
|
expect(print).toHaveBeenCalledWith('warn', 'warn');
|
|
17
20
|
expect(print).toHaveBeenCalledWith('error', 'error');
|
|
21
|
+
expect(print).toHaveBeenCalledWith('fatal', 'fatal');
|
|
18
22
|
});
|
|
19
23
|
it('应该遵守日志级别', () => {
|
|
20
24
|
const print = vi.fn();
|
|
21
25
|
const logger = new Logger({ level: 'warn', print });
|
|
26
|
+
logger.trace('trace');
|
|
27
|
+
logger.debug('debug');
|
|
22
28
|
logger.info('info');
|
|
23
29
|
logger.warn('warn');
|
|
24
30
|
logger.error('error');
|
|
31
|
+
logger.fatal('fatal');
|
|
32
|
+
expect(print).not.toHaveBeenCalledWith('trace', 'trace');
|
|
33
|
+
expect(print).not.toHaveBeenCalledWith('debug', 'debug');
|
|
25
34
|
expect(print).not.toHaveBeenCalledWith('info', 'info');
|
|
26
35
|
expect(print).toHaveBeenCalledWith('warn', 'warn');
|
|
27
36
|
expect(print).toHaveBeenCalledWith('error', 'error');
|
|
37
|
+
expect(print).toHaveBeenCalledWith('fatal', 'fatal');
|
|
38
|
+
});
|
|
39
|
+
it('应该支持 trace 级别日志', () => {
|
|
40
|
+
const print = vi.fn();
|
|
41
|
+
const logger = new Logger({ level: 'trace', print });
|
|
42
|
+
logger.trace('trace message');
|
|
43
|
+
expect(print).toHaveBeenCalledWith('trace', 'trace message');
|
|
44
|
+
});
|
|
45
|
+
it('应该支持 fatal 级别日志', () => {
|
|
46
|
+
const print = vi.fn();
|
|
47
|
+
const logger = new Logger({ level: 'fatal', print });
|
|
48
|
+
logger.fatal('fatal error');
|
|
49
|
+
expect(print).toHaveBeenCalledWith('fatal', 'fatal error');
|
|
50
|
+
});
|
|
51
|
+
it('silent 方法应该是无操作函数', () => {
|
|
52
|
+
const print = vi.fn();
|
|
53
|
+
const logger = new Logger({ print });
|
|
54
|
+
logger.silent();
|
|
55
|
+
expect(print).not.toHaveBeenCalled();
|
|
28
56
|
});
|
|
29
57
|
it('应该允许自定义处理器', () => {
|
|
30
58
|
const logs = [];
|
|
@@ -217,3 +245,65 @@ describe('向后兼容性测试', () => {
|
|
|
217
245
|
expect(records[0].args).toEqual(['测试向后兼容性']);
|
|
218
246
|
});
|
|
219
247
|
});
|
|
248
|
+
describe('Pino 兼容性测试', () => {
|
|
249
|
+
it('应该支持 child 方法创建子 logger', () => {
|
|
250
|
+
const print = vi.fn();
|
|
251
|
+
const logger = new Logger({ print });
|
|
252
|
+
const child = logger.child({ requestId: '123' });
|
|
253
|
+
expect(child).toBeInstanceOf(Logger);
|
|
254
|
+
expect(child.level).toBe(logger.level);
|
|
255
|
+
child.info('child log');
|
|
256
|
+
expect(print).toHaveBeenCalledWith('info', 'child log');
|
|
257
|
+
});
|
|
258
|
+
it('child logger 应该继承父 logger 的配置', () => {
|
|
259
|
+
const print = vi.fn();
|
|
260
|
+
const parent = new Logger({ level: 'warn', print });
|
|
261
|
+
const child = parent.child();
|
|
262
|
+
expect(child.level).toBe('warn');
|
|
263
|
+
child.info('info message');
|
|
264
|
+
child.warn('warn message');
|
|
265
|
+
expect(print).not.toHaveBeenCalledWith('info', 'info message');
|
|
266
|
+
expect(print).toHaveBeenCalledWith('warn', 'warn message');
|
|
267
|
+
});
|
|
268
|
+
it('child logger 可以有自己的配置选项', () => {
|
|
269
|
+
const parentPrint = vi.fn();
|
|
270
|
+
const childPrint = vi.fn();
|
|
271
|
+
const parent = new Logger({ level: 'info', print: parentPrint });
|
|
272
|
+
const child = parent.child({}, { level: 'debug', print: childPrint });
|
|
273
|
+
expect(child.level).toBe('debug');
|
|
274
|
+
child.debug('debug message');
|
|
275
|
+
expect(parentPrint).not.toHaveBeenCalled();
|
|
276
|
+
expect(childPrint).toHaveBeenCalledWith('debug', 'debug message');
|
|
277
|
+
});
|
|
278
|
+
it('应该支持 level 属性的 getter 和 setter', () => {
|
|
279
|
+
const logger = new Logger();
|
|
280
|
+
expect(logger.level).toBe('info'); // 默认级别
|
|
281
|
+
logger.level = 'debug';
|
|
282
|
+
expect(logger.level).toBe('debug');
|
|
283
|
+
logger.level = 'error';
|
|
284
|
+
expect(logger.level).toBe('error');
|
|
285
|
+
});
|
|
286
|
+
it('level 变更应该影响日志输出', () => {
|
|
287
|
+
const print = vi.fn();
|
|
288
|
+
const logger = new Logger({ print });
|
|
289
|
+
logger.level = 'error';
|
|
290
|
+
logger.info('info message');
|
|
291
|
+
logger.error('error message');
|
|
292
|
+
expect(print).not.toHaveBeenCalledWith('info', 'info message');
|
|
293
|
+
expect(print).toHaveBeenCalledWith('error', 'error message');
|
|
294
|
+
});
|
|
295
|
+
it('应该包含所有 Pino 兼容的方法', () => {
|
|
296
|
+
const logger = new Logger();
|
|
297
|
+
// 检查所有必需的方法是否存在
|
|
298
|
+
expect(typeof logger.trace).toBe('function');
|
|
299
|
+
expect(typeof logger.debug).toBe('function');
|
|
300
|
+
expect(typeof logger.info).toBe('function');
|
|
301
|
+
expect(typeof logger.warn).toBe('function');
|
|
302
|
+
expect(typeof logger.error).toBe('function');
|
|
303
|
+
expect(typeof logger.fatal).toBe('function');
|
|
304
|
+
expect(typeof logger.silent).toBe('function');
|
|
305
|
+
expect(typeof logger.child).toBe('function');
|
|
306
|
+
// 检查 level 属性
|
|
307
|
+
expect(typeof logger.level).toBe('string');
|
|
308
|
+
});
|
|
309
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../source/logger/transport.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAGxC;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,WAAW;IACX,KAAK,EAAE,QAAQ,CAAA;IACf,UAAU;IACV,SAAS,EAAE,IAAI,CAAA;IACf,aAAa;IACb,IAAI,EAAE,GAAG,EAAE,CAAA;IACX,eAAe;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,sBAAsB;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB,aAAa;IACb,KAAK,CAAC,EAAE,QAAQ,CAAA;IAEhB,WAAW;IACX,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;OAGG;IACH,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE5C;;OAEG;IACH,KAAK,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,gBAAgB;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa;IACb,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,WAAW;IACX,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C,YAAY;IACZ,EAAE,EAAE,MAAM,CAAA;CACX;AAED;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,YAAY;IAClD,SAAgB,IAAI,EAAE,MAAM,CAAY;IACjC,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,OAAO,EAAE,OAAO,CAAQ;IAE/B,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../source/logger/transport.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAGxC;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,WAAW;IACX,KAAK,EAAE,QAAQ,CAAA;IACf,UAAU;IACV,SAAS,EAAE,IAAI,CAAA;IACf,aAAa;IACb,IAAI,EAAE,GAAG,EAAE,CAAA;IACX,eAAe;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,sBAAsB;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAErB,aAAa;IACb,KAAK,CAAC,EAAE,QAAQ,CAAA;IAEhB,WAAW;IACX,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;OAGG;IACH,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE5C;;OAEG;IACH,KAAK,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,gBAAgB;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa;IACb,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,WAAW;IACX,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C,YAAY;IACZ,EAAE,EAAE,MAAM,CAAA;CACX;AAED;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,YAAY;IAClD,SAAgB,IAAI,EAAE,MAAM,CAAY;IACjC,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,OAAO,EAAE,OAAO,CAAQ;IAE/B,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAoE;gBAE1F,OAAO,GAAE,sBAA2B;IAQhD;;OAEG;IACH,OAAO,CAAC,SAAS;IAeV,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAQnC;;OAEG;IACI,UAAU,IAAI,SAAS,cAAc,EAAE;IAO9C;;OAEG;IACI,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,cAAc,EAAE;IAQ3D;;OAEG;IACI,qBAAqB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,GAAG,cAAc,EAAE;IAQtE;;;OAGG;IACI,aAAa,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,GAAG,cAAc,EAAE;IAItF;;;OAGG;IACI,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,EAAE;IAsB5D;;OAEG;IACI,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,EAAE;IAOxD;;OAEG;IACI,QAAQ,IAAI;QACjB,KAAK,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACjC,eAAe,CAAC,EAAE,IAAI,CAAA;QACtB,eAAe,CAAC,EAAE,IAAI,CAAA;KACvB;IA4BD;;OAEG;IACI,KAAK,IAAI,IAAI;IAIpB;;OAEG;IACI,YAAY,IAAI,MAAM;IAI7B;;OAEG;IACI,KAAK,IAAI,IAAI;CAGrB"}
|
|
@@ -9,7 +9,7 @@ export class MemoryTransport {
|
|
|
9
9
|
level;
|
|
10
10
|
enabled = true;
|
|
11
11
|
cache;
|
|
12
|
-
static LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
|
|
12
|
+
static LOG_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
|
|
13
13
|
constructor(options = {}) {
|
|
14
14
|
this.level = options.level;
|
|
15
15
|
this.enabled = options.enabled ?? true;
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"disposer.d.ts","sourceRoot":"","sources":["../../source/events/disposer.ts"],"names":[],"mappings":"AAAA,qBAAa,QAAQ;IACnB,OAAO,CAAC,gBAAgB,CAAyB;IAGjD,WAAW,CAAC,SAAS,EAAE,MAAM,OAAO,GAAG,IAAI;IAK3C,OAAO,IAAI,IAAI;CAUhB"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export class Disposer {
|
|
2
|
-
cleanupFunctions = [];
|
|
3
|
-
// 添加清理函数
|
|
4
|
-
addDisposer(cleanupFn) {
|
|
5
|
-
this.cleanupFunctions.push(cleanupFn);
|
|
6
|
-
}
|
|
7
|
-
// 执行所有清理函数
|
|
8
|
-
dispose() {
|
|
9
|
-
for (const cleanupFn of this.cleanupFunctions) {
|
|
10
|
-
try {
|
|
11
|
-
cleanupFn();
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
console.error('Error during cleanup:', error);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
this.cleanupFunctions = []; // 清空清理函数列表
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"disposer.test.d.ts","sourceRoot":"","sources":["../../source/events/disposer.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { Disposer } from './disposer';
|
|
3
|
-
describe('Disposer', () => {
|
|
4
|
-
let disposer;
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
disposer = new Disposer();
|
|
7
|
-
});
|
|
8
|
-
describe('addDisposer', () => {
|
|
9
|
-
it('应该能够添加清理函数', () => {
|
|
10
|
-
const cleanupFn = vi.fn();
|
|
11
|
-
expect(() => {
|
|
12
|
-
disposer.addDisposer(cleanupFn);
|
|
13
|
-
}).not.toThrow();
|
|
14
|
-
});
|
|
15
|
-
it('应该能够添加多个清理函数', () => {
|
|
16
|
-
const cleanupFn1 = vi.fn();
|
|
17
|
-
const cleanupFn2 = vi.fn();
|
|
18
|
-
const cleanupFn3 = vi.fn();
|
|
19
|
-
disposer.addDisposer(cleanupFn1);
|
|
20
|
-
disposer.addDisposer(cleanupFn2);
|
|
21
|
-
disposer.addDisposer(cleanupFn3);
|
|
22
|
-
disposer.dispose();
|
|
23
|
-
expect(cleanupFn1).toHaveBeenCalledOnce();
|
|
24
|
-
expect(cleanupFn2).toHaveBeenCalledOnce();
|
|
25
|
-
expect(cleanupFn3).toHaveBeenCalledOnce();
|
|
26
|
-
});
|
|
27
|
-
it('应该能够添加返回不同类型的清理函数', () => {
|
|
28
|
-
const voidFn = vi.fn(() => { });
|
|
29
|
-
const stringFn = vi.fn(() => 'cleanup result');
|
|
30
|
-
const numberFn = vi.fn(() => 42);
|
|
31
|
-
const promiseFn = vi.fn(() => Promise.resolve('async cleanup'));
|
|
32
|
-
disposer.addDisposer(voidFn);
|
|
33
|
-
disposer.addDisposer(stringFn);
|
|
34
|
-
disposer.addDisposer(numberFn);
|
|
35
|
-
disposer.addDisposer(promiseFn);
|
|
36
|
-
disposer.dispose();
|
|
37
|
-
expect(voidFn).toHaveBeenCalled();
|
|
38
|
-
expect(stringFn).toHaveBeenCalled();
|
|
39
|
-
expect(numberFn).toHaveBeenCalled();
|
|
40
|
-
expect(promiseFn).toHaveBeenCalled();
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
describe('dispose', () => {
|
|
44
|
-
it('应该执行所有清理函数', () => {
|
|
45
|
-
const cleanupFn1 = vi.fn();
|
|
46
|
-
const cleanupFn2 = vi.fn();
|
|
47
|
-
const cleanupFn3 = vi.fn();
|
|
48
|
-
disposer.addDisposer(cleanupFn1);
|
|
49
|
-
disposer.addDisposer(cleanupFn2);
|
|
50
|
-
disposer.addDisposer(cleanupFn3);
|
|
51
|
-
disposer.dispose();
|
|
52
|
-
expect(cleanupFn1).toHaveBeenCalledOnce();
|
|
53
|
-
expect(cleanupFn2).toHaveBeenCalledOnce();
|
|
54
|
-
expect(cleanupFn3).toHaveBeenCalledOnce();
|
|
55
|
-
});
|
|
56
|
-
it('应该按添加顺序执行清理函数', () => {
|
|
57
|
-
const executionOrder = [];
|
|
58
|
-
disposer.addDisposer(() => executionOrder.push(1));
|
|
59
|
-
disposer.addDisposer(() => executionOrder.push(2));
|
|
60
|
-
disposer.addDisposer(() => executionOrder.push(3));
|
|
61
|
-
disposer.dispose();
|
|
62
|
-
expect(executionOrder).toEqual([1, 2, 3]);
|
|
63
|
-
});
|
|
64
|
-
it('应该处理清理函数中的错误', () => {
|
|
65
|
-
const workingFn1 = vi.fn();
|
|
66
|
-
const errorFn = vi.fn(() => {
|
|
67
|
-
throw new Error('Cleanup error');
|
|
68
|
-
});
|
|
69
|
-
const workingFn2 = vi.fn();
|
|
70
|
-
// 模拟 console.error
|
|
71
|
-
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
72
|
-
disposer.addDisposer(workingFn1);
|
|
73
|
-
disposer.addDisposer(errorFn);
|
|
74
|
-
disposer.addDisposer(workingFn2);
|
|
75
|
-
// dispose 不应该抛出错误
|
|
76
|
-
expect(() => {
|
|
77
|
-
disposer.dispose();
|
|
78
|
-
}).not.toThrow();
|
|
79
|
-
// 所有函数都应该被调用
|
|
80
|
-
expect(workingFn1).toHaveBeenCalled();
|
|
81
|
-
expect(errorFn).toHaveBeenCalled();
|
|
82
|
-
expect(workingFn2).toHaveBeenCalled();
|
|
83
|
-
// 错误应该被记录
|
|
84
|
-
expect(consoleSpy).toHaveBeenCalledWith('Error during cleanup:', expect.any(Error));
|
|
85
|
-
consoleSpy.mockRestore();
|
|
86
|
-
});
|
|
87
|
-
it('应该在执行后清空清理函数列表', () => {
|
|
88
|
-
const cleanupFn = vi.fn();
|
|
89
|
-
disposer.addDisposer(cleanupFn);
|
|
90
|
-
disposer.dispose();
|
|
91
|
-
expect(cleanupFn).toHaveBeenCalledOnce();
|
|
92
|
-
// 再次调用 dispose 不应该执行任何函数
|
|
93
|
-
disposer.dispose();
|
|
94
|
-
expect(cleanupFn).toHaveBeenCalledOnce();
|
|
95
|
-
});
|
|
96
|
-
it('应该能够在清空后重新添加清理函数', () => {
|
|
97
|
-
const firstCleanup = vi.fn();
|
|
98
|
-
const secondCleanup = vi.fn();
|
|
99
|
-
disposer.addDisposer(firstCleanup);
|
|
100
|
-
disposer.dispose();
|
|
101
|
-
disposer.addDisposer(secondCleanup);
|
|
102
|
-
disposer.dispose();
|
|
103
|
-
expect(firstCleanup).toHaveBeenCalledOnce();
|
|
104
|
-
expect(secondCleanup).toHaveBeenCalledOnce();
|
|
105
|
-
});
|
|
106
|
-
it('应该在没有清理函数时安全执行', () => {
|
|
107
|
-
expect(() => {
|
|
108
|
-
disposer.dispose();
|
|
109
|
-
}).not.toThrow();
|
|
110
|
-
});
|
|
111
|
-
it('应该能够多次调用 dispose', () => {
|
|
112
|
-
const cleanupFn = vi.fn();
|
|
113
|
-
disposer.addDisposer(cleanupFn);
|
|
114
|
-
disposer.dispose();
|
|
115
|
-
disposer.dispose();
|
|
116
|
-
disposer.dispose();
|
|
117
|
-
expect(cleanupFn).toHaveBeenCalledOnce();
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
describe('实际使用场景', () => {
|
|
121
|
-
it('应该能够清理事件监听器', () => {
|
|
122
|
-
const removeEventListener = vi.fn();
|
|
123
|
-
const unsubscribe = vi.fn();
|
|
124
|
-
disposer.addDisposer(removeEventListener);
|
|
125
|
-
disposer.addDisposer(unsubscribe);
|
|
126
|
-
disposer.dispose();
|
|
127
|
-
expect(removeEventListener).toHaveBeenCalled();
|
|
128
|
-
expect(unsubscribe).toHaveBeenCalled();
|
|
129
|
-
});
|
|
130
|
-
it('应该能够清理定时器', () => {
|
|
131
|
-
const clearTimeout = vi.fn();
|
|
132
|
-
const clearInterval = vi.fn();
|
|
133
|
-
disposer.addDisposer(() => clearTimeout(123));
|
|
134
|
-
disposer.addDisposer(() => clearInterval(456));
|
|
135
|
-
disposer.dispose();
|
|
136
|
-
expect(clearTimeout).toHaveBeenCalledWith(123);
|
|
137
|
-
expect(clearInterval).toHaveBeenCalledWith(456);
|
|
138
|
-
});
|
|
139
|
-
it('应该能够清理资源连接', () => {
|
|
140
|
-
const closeConnection = vi.fn();
|
|
141
|
-
const releaseResource = vi.fn();
|
|
142
|
-
disposer.addDisposer(() => closeConnection());
|
|
143
|
-
disposer.addDisposer(() => releaseResource());
|
|
144
|
-
disposer.dispose();
|
|
145
|
-
expect(closeConnection).toHaveBeenCalled();
|
|
146
|
-
expect(releaseResource).toHaveBeenCalled();
|
|
147
|
-
});
|
|
148
|
-
it('应该与 EventEmitter 配合使用', () => {
|
|
149
|
-
// 模拟 EventEmitter 的使用
|
|
150
|
-
const mockEventEmitter = {
|
|
151
|
-
on: vi.fn().mockReturnValue(vi.fn()), // 返回 off 函数
|
|
152
|
-
cleanup: vi.fn()
|
|
153
|
-
};
|
|
154
|
-
const offFunction1 = mockEventEmitter.on('event1', vi.fn());
|
|
155
|
-
const offFunction2 = mockEventEmitter.on('event2', vi.fn());
|
|
156
|
-
disposer.addDisposer(offFunction1);
|
|
157
|
-
disposer.addDisposer(offFunction2);
|
|
158
|
-
disposer.addDisposer(() => mockEventEmitter.cleanup());
|
|
159
|
-
disposer.dispose();
|
|
160
|
-
expect(offFunction1).toHaveBeenCalled();
|
|
161
|
-
expect(offFunction2).toHaveBeenCalled();
|
|
162
|
-
expect(mockEventEmitter.cleanup).toHaveBeenCalled();
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
describe('错误处理', () => {
|
|
166
|
-
it('应该捕获并记录同步错误', () => {
|
|
167
|
-
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
168
|
-
const error = new Error('Sync error');
|
|
169
|
-
disposer.addDisposer(() => {
|
|
170
|
-
throw error;
|
|
171
|
-
});
|
|
172
|
-
disposer.dispose();
|
|
173
|
-
expect(consoleSpy).toHaveBeenCalledWith('Error during cleanup:', error);
|
|
174
|
-
consoleSpy.mockRestore();
|
|
175
|
-
});
|
|
176
|
-
it('应该处理不同类型的错误', () => {
|
|
177
|
-
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
178
|
-
disposer.addDisposer(() => {
|
|
179
|
-
throw new TypeError('Type error');
|
|
180
|
-
});
|
|
181
|
-
disposer.addDisposer(() => {
|
|
182
|
-
throw 'String error';
|
|
183
|
-
});
|
|
184
|
-
disposer.addDisposer(() => {
|
|
185
|
-
throw { message: 'Object error' };
|
|
186
|
-
});
|
|
187
|
-
disposer.dispose();
|
|
188
|
-
expect(consoleSpy).toHaveBeenCalledTimes(3);
|
|
189
|
-
consoleSpy.mockRestore();
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
type OffFn = () => void;
|
|
2
|
-
type Listener<T = unknown> = (data: T) => void;
|
|
3
|
-
export declare class EventEmitter<Events extends object = object> {
|
|
4
|
-
private listeners;
|
|
5
|
-
constructor();
|
|
6
|
-
/**
|
|
7
|
-
* 订阅事件
|
|
8
|
-
* @param type 事件类型
|
|
9
|
-
* @param listener 回调函数
|
|
10
|
-
*/
|
|
11
|
-
on<K extends keyof Events>(type: K, listener: Listener<Events[K]>): OffFn;
|
|
12
|
-
/**
|
|
13
|
-
* 取消订阅
|
|
14
|
-
* @param type 事件类型
|
|
15
|
-
* @param listener 要移除的回调函数(可选)
|
|
16
|
-
*/
|
|
17
|
-
off<K extends keyof Events>(type: K, listener?: Listener<Events[K]>): void;
|
|
18
|
-
/**
|
|
19
|
-
* 触发事件
|
|
20
|
-
* @param type 事件类型
|
|
21
|
-
* @param data 要传递的数据
|
|
22
|
-
*/
|
|
23
|
-
emit<K extends keyof Events>(type: K, data: Events[K]): void;
|
|
24
|
-
/**
|
|
25
|
-
* 一次性订阅
|
|
26
|
-
* @param type 事件类型
|
|
27
|
-
* @param listener 回调函数
|
|
28
|
-
*/
|
|
29
|
-
once<K extends keyof Events>(type: K, listener: Listener<Events[K]>): void;
|
|
30
|
-
cleanup(): void;
|
|
31
|
-
}
|
|
32
|
-
export {};
|
|
33
|
-
//# sourceMappingURL=event-emitter.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"event-emitter.d.ts","sourceRoot":"","sources":["../../source/events/event-emitter.ts"],"names":[],"mappings":"AAAA,KAAK,KAAK,GAAG,MAAM,IAAI,CAAA;AACvB,KAAK,QAAQ,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAA;AAE9C,qBAAa,YAAY,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM;IACtD,OAAO,CAAC,SAAS,CAAsD;;IAUvE;;;;OAIG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK;IASzE;;;;OAIG;IACH,GAAG,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAc1E;;;;OAIG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI;IAQ5D;;;;OAIG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAQ1E,OAAO;CAGR"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
export class EventEmitter {
|
|
2
|
-
listeners = {};
|
|
3
|
-
constructor() {
|
|
4
|
-
this.on = this.on.bind(this);
|
|
5
|
-
this.off = this.off.bind(this);
|
|
6
|
-
this.once = this.once.bind(this);
|
|
7
|
-
this.emit = this.emit.bind(this);
|
|
8
|
-
this.cleanup = this.cleanup.bind(this);
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* 订阅事件
|
|
12
|
-
* @param type 事件类型
|
|
13
|
-
* @param listener 回调函数
|
|
14
|
-
*/
|
|
15
|
-
on(type, listener) {
|
|
16
|
-
if (!this.listeners[type]) {
|
|
17
|
-
this.listeners[type] = [];
|
|
18
|
-
}
|
|
19
|
-
this.listeners[type].push(listener);
|
|
20
|
-
return () => this.off(type, listener);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* 取消订阅
|
|
24
|
-
* @param type 事件类型
|
|
25
|
-
* @param listener 要移除的回调函数(可选)
|
|
26
|
-
*/
|
|
27
|
-
off(type, listener) {
|
|
28
|
-
if (!this.listeners[type])
|
|
29
|
-
return;
|
|
30
|
-
if (!listener) {
|
|
31
|
-
// 移除该事件的所有监听器
|
|
32
|
-
delete this.listeners[type];
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
// 移除特定监听器
|
|
36
|
-
this.listeners[type] = this.listeners[type].filter((fn) => fn !== listener);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* 触发事件
|
|
41
|
-
* @param type 事件类型
|
|
42
|
-
* @param data 要传递的数据
|
|
43
|
-
*/
|
|
44
|
-
emit(type, data) {
|
|
45
|
-
const listeners = this.listeners[type];
|
|
46
|
-
// 复制数组避免回调中取消订阅导致的遍历问题
|
|
47
|
-
if (listeners) {
|
|
48
|
-
listeners.slice().forEach((fn) => fn(data));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* 一次性订阅
|
|
53
|
-
* @param type 事件类型
|
|
54
|
-
* @param listener 回调函数
|
|
55
|
-
*/
|
|
56
|
-
once(type, listener) {
|
|
57
|
-
const onceWrapper = (data) => {
|
|
58
|
-
listener(data);
|
|
59
|
-
this.off(type, onceWrapper);
|
|
60
|
-
};
|
|
61
|
-
this.on(type, onceWrapper);
|
|
62
|
-
}
|
|
63
|
-
cleanup() {
|
|
64
|
-
this.listeners = {};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"event-emitter.test.d.ts","sourceRoot":"","sources":["../../source/events/event-emitter.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { EventEmitter } from './event-emitter';
|
|
3
|
-
describe('EventEmitter', () => {
|
|
4
|
-
let emitter;
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
emitter = new EventEmitter();
|
|
7
|
-
});
|
|
8
|
-
describe('on', () => {
|
|
9
|
-
it('应该能够订阅事件', () => {
|
|
10
|
-
const listener = vi.fn();
|
|
11
|
-
emitter.on('test', listener);
|
|
12
|
-
emitter.emit('test', 'hello');
|
|
13
|
-
expect(listener).toHaveBeenCalledOnce();
|
|
14
|
-
expect(listener).toHaveBeenCalledWith('hello');
|
|
15
|
-
});
|
|
16
|
-
it('应该能够订阅多个监听器', () => {
|
|
17
|
-
const listener1 = vi.fn();
|
|
18
|
-
const listener2 = vi.fn();
|
|
19
|
-
emitter.on('test', listener1);
|
|
20
|
-
emitter.on('test', listener2);
|
|
21
|
-
emitter.emit('test', 'hello');
|
|
22
|
-
expect(listener1).toHaveBeenCalledOnce();
|
|
23
|
-
expect(listener2).toHaveBeenCalledOnce();
|
|
24
|
-
expect(listener1).toHaveBeenCalledWith('hello');
|
|
25
|
-
expect(listener2).toHaveBeenCalledWith('hello');
|
|
26
|
-
});
|
|
27
|
-
it('应该返回取消订阅函数', () => {
|
|
28
|
-
const listener = vi.fn();
|
|
29
|
-
const off = emitter.on('test', listener);
|
|
30
|
-
emitter.emit('test', 'hello');
|
|
31
|
-
expect(listener).toHaveBeenCalledOnce();
|
|
32
|
-
off();
|
|
33
|
-
emitter.emit('test', 'world');
|
|
34
|
-
expect(listener).toHaveBeenCalledOnce();
|
|
35
|
-
});
|
|
36
|
-
it('应该支持不同类型的事件数据', () => {
|
|
37
|
-
const stringListener = vi.fn();
|
|
38
|
-
const numberListener = vi.fn();
|
|
39
|
-
const objectListener = vi.fn();
|
|
40
|
-
const voidListener = vi.fn();
|
|
41
|
-
emitter.on('test', stringListener);
|
|
42
|
-
emitter.on('number', numberListener);
|
|
43
|
-
emitter.on('object', objectListener);
|
|
44
|
-
emitter.on('noData', voidListener);
|
|
45
|
-
const testObject = { id: 1, name: 'test' };
|
|
46
|
-
emitter.emit('test', 'hello');
|
|
47
|
-
emitter.emit('number', 42);
|
|
48
|
-
emitter.emit('object', testObject);
|
|
49
|
-
emitter.emit('noData', undefined);
|
|
50
|
-
expect(stringListener).toHaveBeenCalledWith('hello');
|
|
51
|
-
expect(numberListener).toHaveBeenCalledWith(42);
|
|
52
|
-
expect(objectListener).toHaveBeenCalledWith(testObject);
|
|
53
|
-
expect(voidListener).toHaveBeenCalledWith(undefined);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
describe('off', () => {
|
|
57
|
-
it('应该能够取消订阅特定监听器', () => {
|
|
58
|
-
const listener1 = vi.fn();
|
|
59
|
-
const listener2 = vi.fn();
|
|
60
|
-
emitter.on('test', listener1);
|
|
61
|
-
emitter.on('test', listener2);
|
|
62
|
-
emitter.off('test', listener1);
|
|
63
|
-
emitter.emit('test', 'hello');
|
|
64
|
-
expect(listener1).not.toHaveBeenCalled();
|
|
65
|
-
expect(listener2).toHaveBeenCalledOnce();
|
|
66
|
-
});
|
|
67
|
-
it('应该能够取消订阅所有监听器', () => {
|
|
68
|
-
const listener1 = vi.fn();
|
|
69
|
-
const listener2 = vi.fn();
|
|
70
|
-
emitter.on('test', listener1);
|
|
71
|
-
emitter.on('test', listener2);
|
|
72
|
-
emitter.off('test');
|
|
73
|
-
emitter.emit('test', 'hello');
|
|
74
|
-
expect(listener1).not.toHaveBeenCalled();
|
|
75
|
-
expect(listener2).not.toHaveBeenCalled();
|
|
76
|
-
});
|
|
77
|
-
it('应该忽略不存在的事件类型', () => {
|
|
78
|
-
expect(() => {
|
|
79
|
-
emitter.off('test');
|
|
80
|
-
}).not.toThrow();
|
|
81
|
-
});
|
|
82
|
-
it('应该忽略不存在的监听器', () => {
|
|
83
|
-
const listener = vi.fn();
|
|
84
|
-
const anotherListener = vi.fn();
|
|
85
|
-
emitter.on('test', listener);
|
|
86
|
-
expect(() => {
|
|
87
|
-
emitter.off('test', anotherListener);
|
|
88
|
-
}).not.toThrow();
|
|
89
|
-
emitter.emit('test', 'hello');
|
|
90
|
-
expect(listener).toHaveBeenCalledOnce();
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
describe('emit', () => {
|
|
94
|
-
it('应该能够触发事件', () => {
|
|
95
|
-
const listener = vi.fn();
|
|
96
|
-
emitter.on('test', listener);
|
|
97
|
-
emitter.emit('test', 'hello');
|
|
98
|
-
expect(listener).toHaveBeenCalledWith('hello');
|
|
99
|
-
});
|
|
100
|
-
it('应该不抛出错误当没有监听器时', () => {
|
|
101
|
-
expect(() => {
|
|
102
|
-
emitter.emit('test', 'hello');
|
|
103
|
-
}).not.toThrow();
|
|
104
|
-
});
|
|
105
|
-
it('应该按顺序调用监听器', () => {
|
|
106
|
-
const calls = [];
|
|
107
|
-
emitter.on('test', () => calls.push(1));
|
|
108
|
-
emitter.on('test', () => calls.push(2));
|
|
109
|
-
emitter.on('test', () => calls.push(3));
|
|
110
|
-
emitter.emit('test', 'hello');
|
|
111
|
-
expect(calls).toEqual([1, 2, 3]);
|
|
112
|
-
});
|
|
113
|
-
it('应该处理监听器中的错误', () => {
|
|
114
|
-
const workingListener = vi.fn();
|
|
115
|
-
const errorListener = vi.fn(() => {
|
|
116
|
-
throw new Error('Test error');
|
|
117
|
-
});
|
|
118
|
-
const anotherWorkingListener = vi.fn();
|
|
119
|
-
emitter.on('test', workingListener);
|
|
120
|
-
emitter.on('test', errorListener);
|
|
121
|
-
emitter.on('test', anotherWorkingListener);
|
|
122
|
-
expect(() => {
|
|
123
|
-
emitter.emit('test', 'hello');
|
|
124
|
-
}).toThrow('Test error');
|
|
125
|
-
expect(workingListener).toHaveBeenCalled();
|
|
126
|
-
expect(errorListener).toHaveBeenCalled();
|
|
127
|
-
// 由于错误,后续监听器可能不会被调用
|
|
128
|
-
});
|
|
129
|
-
it('应该防止监听器修改监听器列表时的遍历问题', () => {
|
|
130
|
-
const listener1 = vi.fn();
|
|
131
|
-
const listener2 = vi.fn(() => {
|
|
132
|
-
// 在回调中取消订阅
|
|
133
|
-
emitter.off('test', listener3);
|
|
134
|
-
});
|
|
135
|
-
const listener3 = vi.fn();
|
|
136
|
-
emitter.on('test', listener1);
|
|
137
|
-
emitter.on('test', listener2);
|
|
138
|
-
emitter.on('test', listener3);
|
|
139
|
-
emitter.emit('test', 'hello');
|
|
140
|
-
expect(listener1).toHaveBeenCalled();
|
|
141
|
-
expect(listener2).toHaveBeenCalled();
|
|
142
|
-
expect(listener3).toHaveBeenCalled(); // 应该仍然被调用,因为使用了 slice()
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
describe('once', () => {
|
|
146
|
-
it('应该只执行一次监听器', () => {
|
|
147
|
-
const listener = vi.fn();
|
|
148
|
-
emitter.once('test', listener);
|
|
149
|
-
emitter.emit('test', 'first');
|
|
150
|
-
emitter.emit('test', 'second');
|
|
151
|
-
expect(listener).toHaveBeenCalledOnce();
|
|
152
|
-
expect(listener).toHaveBeenCalledWith('first');
|
|
153
|
-
});
|
|
154
|
-
it('应该在执行后自动取消订阅', () => {
|
|
155
|
-
const listener = vi.fn();
|
|
156
|
-
emitter.once('test', listener);
|
|
157
|
-
emitter.emit('test', 'hello');
|
|
158
|
-
// 验证监听器已被移除
|
|
159
|
-
emitter.emit('test', 'world');
|
|
160
|
-
expect(listener).toHaveBeenCalledOnce();
|
|
161
|
-
});
|
|
162
|
-
it('应该与普通监听器一起工作', () => {
|
|
163
|
-
const onceListener = vi.fn();
|
|
164
|
-
const normalListener = vi.fn();
|
|
165
|
-
emitter.once('test', onceListener);
|
|
166
|
-
emitter.on('test', normalListener);
|
|
167
|
-
emitter.emit('test', 'first');
|
|
168
|
-
emitter.emit('test', 'second');
|
|
169
|
-
expect(onceListener).toHaveBeenCalledOnce();
|
|
170
|
-
expect(normalListener).toHaveBeenCalledTimes(2);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
describe('cleanup', () => {
|
|
174
|
-
it('应该清空所有监听器', () => {
|
|
175
|
-
const listener1 = vi.fn();
|
|
176
|
-
const listener2 = vi.fn();
|
|
177
|
-
emitter.on('test', listener1);
|
|
178
|
-
emitter.on('number', listener2);
|
|
179
|
-
emitter.cleanup();
|
|
180
|
-
emitter.emit('test', 'hello');
|
|
181
|
-
emitter.emit('number', 42);
|
|
182
|
-
expect(listener1).not.toHaveBeenCalled();
|
|
183
|
-
expect(listener2).not.toHaveBeenCalled();
|
|
184
|
-
});
|
|
185
|
-
it('清理后应该能够重新添加监听器', () => {
|
|
186
|
-
const listener = vi.fn();
|
|
187
|
-
emitter.on('test', listener);
|
|
188
|
-
emitter.cleanup();
|
|
189
|
-
const newListener = vi.fn();
|
|
190
|
-
emitter.on('test', newListener);
|
|
191
|
-
emitter.emit('test', 'hello');
|
|
192
|
-
expect(listener).not.toHaveBeenCalled();
|
|
193
|
-
expect(newListener).toHaveBeenCalledWith('hello');
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
describe('方法绑定', () => {
|
|
197
|
-
it('方法应该绑定到实例', () => {
|
|
198
|
-
const { on, off, emit, once, cleanup } = emitter;
|
|
199
|
-
const listener = vi.fn();
|
|
200
|
-
on('test', listener);
|
|
201
|
-
emit('test', 'hello');
|
|
202
|
-
expect(listener).toHaveBeenCalledWith('hello');
|
|
203
|
-
off('test', listener);
|
|
204
|
-
emit('test', 'world');
|
|
205
|
-
expect(listener).toHaveBeenCalledOnce();
|
|
206
|
-
// 测试其他方法
|
|
207
|
-
once('test', listener);
|
|
208
|
-
emit('test', 'once');
|
|
209
|
-
expect(listener).toHaveBeenCalledTimes(2);
|
|
210
|
-
cleanup();
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
});
|
package/output/events/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/events/index.ts"],"names":[],"mappings":"AACA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA"}
|
package/output/events/index.js
DELETED