@taicode/common-base 1.6.3 → 1.7.1

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 (34) hide show
  1. package/output/batching-buffer/batching-buffer.d.ts +140 -0
  2. package/output/batching-buffer/batching-buffer.d.ts.map +1 -0
  3. package/output/batching-buffer/batching-buffer.js +273 -0
  4. package/output/batching-buffer/batching-buffer.test.d.ts +2 -0
  5. package/output/batching-buffer/batching-buffer.test.d.ts.map +1 -0
  6. package/output/batching-buffer/batching-buffer.test.js +511 -0
  7. package/output/batching-buffer/index.d.ts +3 -0
  8. package/output/batching-buffer/index.d.ts.map +1 -0
  9. package/output/batching-buffer/index.js +1 -0
  10. package/output/index.d.ts +2 -0
  11. package/output/index.d.ts.map +1 -1
  12. package/output/index.js +3 -0
  13. package/output/logger/logger.d.ts +15 -2
  14. package/output/logger/logger.d.ts.map +1 -1
  15. package/output/logger/logger.js +67 -5
  16. package/output/logger/logger.test.js +90 -0
  17. package/output/logger/transport.d.ts.map +1 -1
  18. package/output/logger/transport.js +1 -1
  19. package/package.json +1 -1
  20. package/output/events/disposer.d.ts +0 -6
  21. package/output/events/disposer.d.ts.map +0 -1
  22. package/output/events/disposer.js +0 -19
  23. package/output/events/disposer.test.d.ts +0 -2
  24. package/output/events/disposer.test.d.ts.map +0 -1
  25. package/output/events/disposer.test.js +0 -192
  26. package/output/events/event-emitter.d.ts +0 -33
  27. package/output/events/event-emitter.d.ts.map +0 -1
  28. package/output/events/event-emitter.js +0 -66
  29. package/output/events/event-emitter.test.d.ts +0 -2
  30. package/output/events/event-emitter.test.d.ts.map +0 -1
  31. package/output/events/event-emitter.test.js +0 -213
  32. package/output/events/index.d.ts +0 -3
  33. package/output/events/index.d.ts.map +0 -1
  34. package/output/events/index.js +0 -3
@@ -1,12 +1,12 @@
1
1
  // 基础 Logger 实现,支持简单的日志输出和扩展能力
2
2
  import { defaultLogFormatter } from './formatter';
3
3
  export class Logger {
4
- level;
4
+ _level;
5
5
  transports;
6
6
  formatter;
7
7
  print;
8
8
  constructor(options = {}) {
9
- this.level = options.level || 'info';
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
- (console[level] || console.log)(prefix, ...args);
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.level);
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,CAAkD;gBAExE,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"}
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@taicode/common-base",
3
- "version": "1.6.3",
3
+ "version": "1.7.1",
4
4
  "author": "Alain",
5
5
  "license": "ISC",
6
6
  "description": "",
@@ -1,6 +0,0 @@
1
- export declare class Disposer {
2
- private cleanupFunctions;
3
- addDisposer(cleanupFn: () => unknown): void;
4
- dispose(): void;
5
- }
6
- //# sourceMappingURL=disposer.d.ts.map
@@ -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,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=disposer.test.d.ts.map
@@ -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,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=event-emitter.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"event-emitter.test.d.ts","sourceRoot":"","sources":["../../source/events/event-emitter.test.ts"],"names":[],"mappings":""}