@taicode/common-base 1.7.4 → 1.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.
- package/output/error/error.d.ts +104 -67
- package/output/error/error.d.ts.map +1 -1
- package/output/error/error.js +85 -59
- package/output/error/error.test.d.ts +0 -17
- package/output/error/error.test.d.ts.map +1 -1
- package/output/error/error.test.js +172 -99
- package/output/error/index.d.ts +2 -1
- package/output/error/index.d.ts.map +1 -1
- package/output/error/index.js +1 -1
- package/output/events/disposer.d.ts +6 -0
- package/output/events/disposer.d.ts.map +1 -0
- package/output/events/disposer.js +19 -0
- package/output/events/disposer.test.d.ts +2 -0
- package/output/events/disposer.test.d.ts.map +1 -0
- package/output/events/disposer.test.js +192 -0
- package/output/events/event-emitter.d.ts +33 -0
- package/output/events/event-emitter.d.ts.map +1 -0
- package/output/events/event-emitter.js +66 -0
- package/output/events/event-emitter.test.d.ts +2 -0
- package/output/events/event-emitter.test.d.ts.map +1 -0
- package/output/events/event-emitter.test.js +213 -0
- package/output/events/index.d.ts +3 -0
- package/output/events/index.d.ts.map +1 -0
- package/output/events/index.js +3 -0
- package/output/logger/formatter.d.ts +0 -1
- package/output/logger/formatter.d.ts.map +1 -1
- package/output/logger/formatter.js +1 -17
- package/output/logger/formatter.test.d.ts +0 -24
- package/output/logger/formatter.test.d.ts.map +1 -1
- package/output/logger/formatter.test.js +11 -30
- package/output/logger/logger.test.d.ts +0 -19
- package/output/logger/logger.test.d.ts.map +1 -1
- package/output/logger/logger.test.js +6 -16
- package/package.json +3 -2
|
@@ -0,0 +1,33 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-emitter.test.d.ts","sourceRoot":"","sources":["../../source/events/event-emitter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,213 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/events/index.ts"],"names":[],"mappings":"AACA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA"}
|
|
@@ -52,7 +52,6 @@ export declare class DefaultLogFormatter implements LogFormatter {
|
|
|
52
52
|
private formatError;
|
|
53
53
|
private formatUserError;
|
|
54
54
|
private formatSystemError;
|
|
55
|
-
private formatUnknownError;
|
|
56
55
|
private formatGenericError;
|
|
57
56
|
private formatUnknownValue;
|
|
58
57
|
private formatContext;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../source/logger/formatter.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,eAAe;IACf,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAA;IAClC,aAAa;IACb,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAA;IAC7B,aAAa;IACb,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAA;IAC7B,aAAa;IACb,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,MAAM,CAAA;IAC9B,aAAa;IACb,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,MAAM,CAAA;IACnC,eAAe;IACf,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,CAAA;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,cAAc;IACd,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAAA;IACxB,cAAc;IACd,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CAAA;CACvC;AAED,MAAM,WAAW,qBAAqB;IACpC,eAAe;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gBAAgB;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,sBAAsB;IACtB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,YAAY;IACtD,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,MAAM,CAA0C;gBAE5C,MAAM,GAAE,eAAoB,EAAE,YAAY,GAAE,qBAA0B;IAgBlF;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM;IAwBxB;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE;IAItC;;OAEG;IACH,OAAO,CAAC,WAAW;
|
|
1
|
+
{"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../source/logger/formatter.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,eAAe;IACf,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAA;IAClC,aAAa;IACb,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAA;IAC7B,aAAa;IACb,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAA;IAC7B,aAAa;IACb,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,MAAM,CAAA;IAC9B,aAAa;IACb,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,MAAM,CAAA;IACnC,eAAe;IACf,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,CAAA;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,cAAc;IACd,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAAA;IACxB,cAAc;IACd,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CAAA;CACvC;AAED,MAAM,WAAW,qBAAqB;IACpC,eAAe;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,gBAAgB;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,sBAAsB;IACtB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,YAAY;IACtD,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,MAAM,CAA0C;gBAE5C,MAAM,GAAE,eAAoB,EAAE,YAAY,GAAE,qBAA0B;IAgBlF;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM;IAwBxB;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE;IAItC;;OAEG;IACH,OAAO,CAAC,WAAW;IAsBnB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,kBAAkB;IAuB1B,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,QAAQ;IAehB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,WAAW;CAWpB;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB,qBAA4B,CAAA"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 格式化器配置模块
|
|
3
3
|
* 提供各种类型数据的格式化功能
|
|
4
4
|
*/
|
|
5
|
-
import { UserError, SystemError
|
|
5
|
+
import { UserError, SystemError } from '../error';
|
|
6
6
|
/**
|
|
7
7
|
* 日志格式化器
|
|
8
8
|
* 负责根据配置格式化各种类型的参数
|
|
@@ -66,9 +66,6 @@ export class DefaultLogFormatter {
|
|
|
66
66
|
if (SystemError.is(error)) {
|
|
67
67
|
return this.formatSystemError(error, opts);
|
|
68
68
|
}
|
|
69
|
-
if (UnknownError.is(error)) {
|
|
70
|
-
return this.formatUnknownError(error, opts);
|
|
71
|
-
}
|
|
72
69
|
if (error instanceof Error) {
|
|
73
70
|
return this.formatGenericError(error, opts);
|
|
74
71
|
}
|
|
@@ -100,19 +97,6 @@ export class DefaultLogFormatter {
|
|
|
100
97
|
}
|
|
101
98
|
return lines.join('\n ');
|
|
102
99
|
}
|
|
103
|
-
formatUnknownError(error, opts) {
|
|
104
|
-
const lines = [
|
|
105
|
-
this.colorize('❓ Unknown Error', 'magenta', opts.useColors),
|
|
106
|
-
`Message: ${error.message}`,
|
|
107
|
-
];
|
|
108
|
-
if (opts.showContext && error.context) {
|
|
109
|
-
lines.push(`Context: ${this.formatContext(error.context)}`);
|
|
110
|
-
}
|
|
111
|
-
if (opts.showStack && error.stack) {
|
|
112
|
-
lines.push(`Stack: ${error.stack}`);
|
|
113
|
-
}
|
|
114
|
-
return lines.join('\n ');
|
|
115
|
-
}
|
|
116
100
|
formatGenericError(error, opts) {
|
|
117
101
|
const lines = [
|
|
118
102
|
this.colorize(`💥 ${error.name}`, 'red', opts.useColors),
|
|
@@ -1,26 +1,2 @@
|
|
|
1
|
-
declare module '../error' {
|
|
2
|
-
interface UserErrorTypes {
|
|
3
|
-
'validation-failed': {
|
|
4
|
-
field?: string;
|
|
5
|
-
value?: unknown;
|
|
6
|
-
rule?: string;
|
|
7
|
-
};
|
|
8
|
-
'test-error': {
|
|
9
|
-
field?: string;
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
interface SystemErrorTypes {
|
|
13
|
-
'database-error': {
|
|
14
|
-
query?: string;
|
|
15
|
-
table?: string;
|
|
16
|
-
operation?: string;
|
|
17
|
-
};
|
|
18
|
-
'network-error': {
|
|
19
|
-
url?: string;
|
|
20
|
-
method?: string;
|
|
21
|
-
status?: number;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
1
|
export {};
|
|
26
2
|
//# sourceMappingURL=formatter.test.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter.test.d.ts","sourceRoot":"","sources":["../../source/logger/formatter.test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"formatter.test.d.ts","sourceRoot":"","sources":["../../source/logger/formatter.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
2
|
import { DefaultLogFormatter } from './formatter';
|
|
3
|
-
import { UserError, SystemError
|
|
3
|
+
import { UserError, SystemError } from '../error';
|
|
4
4
|
describe('DefaultLogFormatter', () => {
|
|
5
5
|
let formatter;
|
|
6
6
|
beforeEach(() => {
|
|
@@ -119,11 +119,11 @@ describe('DefaultLogFormatter', () => {
|
|
|
119
119
|
describe('错误格式化', () => {
|
|
120
120
|
describe('UserError 格式化', () => {
|
|
121
121
|
it('应该格式化带有完整信息的 UserError', () => {
|
|
122
|
-
const error = new UserError('validation-failed',
|
|
122
|
+
const error = new UserError('validation-failed', {
|
|
123
123
|
field: 'email',
|
|
124
124
|
value: 'invalid-email',
|
|
125
125
|
rule: 'email-format'
|
|
126
|
-
});
|
|
126
|
+
}, '验证失败');
|
|
127
127
|
const result = formatter.format(error);
|
|
128
128
|
expect(result).toContain('🚫 User Error [validation-failed]');
|
|
129
129
|
expect(result).toContain('Message: 验证失败');
|
|
@@ -132,7 +132,7 @@ describe('DefaultLogFormatter', () => {
|
|
|
132
132
|
});
|
|
133
133
|
it('应该在禁用颜色时不包含 ANSI 代码', () => {
|
|
134
134
|
const formatterWithoutColors = new DefaultLogFormatter({}, { useColors: false });
|
|
135
|
-
const error = new UserError('test-error', '测试错误');
|
|
135
|
+
const error = new UserError('test-error', undefined, '测试错误');
|
|
136
136
|
const result = formatterWithoutColors.format(error);
|
|
137
137
|
expect(result).not.toContain('\x1b[');
|
|
138
138
|
expect(result).toContain('🚫 User Error [test-error]');
|
|
@@ -140,14 +140,14 @@ describe('DefaultLogFormatter', () => {
|
|
|
140
140
|
});
|
|
141
141
|
it('应该在禁用堆栈时不显示堆栈信息', () => {
|
|
142
142
|
const formatterWithoutStack = new DefaultLogFormatter({}, { showStack: false });
|
|
143
|
-
const error = new UserError('test-error', '测试错误');
|
|
143
|
+
const error = new UserError('test-error', undefined, '测试错误');
|
|
144
144
|
const result = formatterWithoutStack.format(error);
|
|
145
145
|
expect(result).not.toContain('Stack:');
|
|
146
146
|
expect(result).toContain('Message: 测试错误');
|
|
147
147
|
});
|
|
148
148
|
it('应该在禁用上下文时不显示上下文信息', () => {
|
|
149
149
|
const formatterWithoutContext = new DefaultLogFormatter({}, { showContext: false });
|
|
150
|
-
const error = new UserError('test-error',
|
|
150
|
+
const error = new UserError('test-error', { field: 'test' }, '测试错误');
|
|
151
151
|
const result = formatterWithoutContext.format(error);
|
|
152
152
|
expect(result).not.toContain('Context:');
|
|
153
153
|
expect(result).toContain('Message: 测试错误');
|
|
@@ -155,11 +155,11 @@ describe('DefaultLogFormatter', () => {
|
|
|
155
155
|
});
|
|
156
156
|
describe('SystemError 格式化', () => {
|
|
157
157
|
it('应该格式化带有完整信息的 SystemError', () => {
|
|
158
|
-
const error = new SystemError('database-error',
|
|
158
|
+
const error = new SystemError('database-error', {
|
|
159
159
|
table: 'users',
|
|
160
160
|
query: 'SELECT * FROM users',
|
|
161
161
|
operation: 'select'
|
|
162
|
-
});
|
|
162
|
+
}, '数据库连接失败');
|
|
163
163
|
const result = formatter.format(error);
|
|
164
164
|
expect(result).toContain('⚠️ System Error [database-error]');
|
|
165
165
|
expect(result).toContain('Message: 数据库连接失败');
|
|
@@ -168,31 +168,12 @@ describe('DefaultLogFormatter', () => {
|
|
|
168
168
|
});
|
|
169
169
|
it('应该在禁用颜色时不包含 ANSI 代码', () => {
|
|
170
170
|
const formatterWithoutColors = new DefaultLogFormatter({}, { useColors: false });
|
|
171
|
-
const error = new SystemError('network-error', '网络错误');
|
|
171
|
+
const error = new SystemError('network-error', undefined, '网络错误');
|
|
172
172
|
const result = formatterWithoutColors.format(error);
|
|
173
173
|
expect(result).not.toContain('\x1b[');
|
|
174
174
|
expect(result).toContain('⚠️ System Error [network-error]');
|
|
175
175
|
});
|
|
176
176
|
});
|
|
177
|
-
describe('UnknownError 格式化', () => {
|
|
178
|
-
it('应该格式化带有完整信息的 UnknownError', () => {
|
|
179
|
-
const error = new UnknownError('未知错误', {
|
|
180
|
-
cause: 'something went wrong'
|
|
181
|
-
});
|
|
182
|
-
const result = formatter.format(error);
|
|
183
|
-
expect(result).toContain('❓ Unknown Error');
|
|
184
|
-
expect(result).toContain('Message: 未知错误');
|
|
185
|
-
expect(result).toContain('Context:');
|
|
186
|
-
expect(result).toContain('Stack:');
|
|
187
|
-
});
|
|
188
|
-
it('应该在禁用颜色时不包含 ANSI 代码', () => {
|
|
189
|
-
const formatterWithoutColors = new DefaultLogFormatter({}, { useColors: false });
|
|
190
|
-
const error = new UnknownError('测试未知错误');
|
|
191
|
-
const result = formatterWithoutColors.format(error);
|
|
192
|
-
expect(result).not.toContain('\x1b[');
|
|
193
|
-
expect(result).toContain('❓ Unknown Error');
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
177
|
describe('通用 Error 格式化', () => {
|
|
197
178
|
it('应该格式化普通 Error', () => {
|
|
198
179
|
const error = new Error('普通错误');
|
|
@@ -291,7 +272,7 @@ describe('DefaultLogFormatter', () => {
|
|
|
291
272
|
showContext: false,
|
|
292
273
|
useColors: false
|
|
293
274
|
});
|
|
294
|
-
const error = new UserError('test-error',
|
|
275
|
+
const error = new UserError('test-error', { field: 'test' }, '测试错误');
|
|
295
276
|
const result = minimalistFormatter.format(error);
|
|
296
277
|
expect(result).not.toContain('Stack:');
|
|
297
278
|
expect(result).not.toContain('Context:');
|
|
@@ -305,7 +286,7 @@ describe('DefaultLogFormatter', () => {
|
|
|
305
286
|
showContext: false,
|
|
306
287
|
useColors: false
|
|
307
288
|
});
|
|
308
|
-
const error = new SystemError('database-error',
|
|
289
|
+
const error = new SystemError('database-error', { table: 'users' }, '数据库错误');
|
|
309
290
|
const result = stackOnlyFormatter.format(error);
|
|
310
291
|
expect(result).toContain('Stack:');
|
|
311
292
|
expect(result).not.toContain('Context:');
|
|
@@ -1,21 +1,2 @@
|
|
|
1
|
-
declare module '../error' {
|
|
2
|
-
interface UserErrorTypes {
|
|
3
|
-
'validation-failed': {
|
|
4
|
-
field?: string;
|
|
5
|
-
value?: unknown;
|
|
6
|
-
rule?: string;
|
|
7
|
-
};
|
|
8
|
-
'test-error': {
|
|
9
|
-
field?: string;
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
interface SystemErrorTypes {
|
|
13
|
-
'database-error': {
|
|
14
|
-
query?: string;
|
|
15
|
-
table?: string;
|
|
16
|
-
operation?: string;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
1
|
export {};
|
|
21
2
|
//# sourceMappingURL=logger.test.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.test.d.ts","sourceRoot":"","sources":["../../source/logger/logger.test.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logger.test.d.ts","sourceRoot":"","sources":["../../source/logger/logger.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from 'vitest';
|
|
2
2
|
import { Logger } from './logger';
|
|
3
3
|
import { DefaultLogFormatter } from './formatter';
|
|
4
|
-
import { UserError, SystemError
|
|
4
|
+
import { UserError, SystemError } from '../error';
|
|
5
5
|
import { MemoryTransport } from './transport'; // 测试向后兼容性
|
|
6
6
|
describe('Logger', () => {
|
|
7
7
|
it('应该默认记录 info 及以上级别', () => {
|
|
@@ -66,11 +66,11 @@ describe('Logger 错误处理', () => {
|
|
|
66
66
|
it('应该正确格式化 UserError', () => {
|
|
67
67
|
const mockPrint = vi.fn();
|
|
68
68
|
const logger = new Logger({ print: mockPrint });
|
|
69
|
-
const userError = new UserError('validation-failed',
|
|
69
|
+
const userError = new UserError('validation-failed', {
|
|
70
70
|
field: 'email',
|
|
71
71
|
value: 'invalid-email',
|
|
72
72
|
rule: 'email-format'
|
|
73
|
-
});
|
|
73
|
+
}, '邮箱格式无效');
|
|
74
74
|
logger.error('测试用户错误:', userError);
|
|
75
75
|
expect(mockPrint).toHaveBeenCalledWith('error', '测试用户错误:', expect.stringContaining('🚫 User Error [validation-failed]'));
|
|
76
76
|
expect(mockPrint).toHaveBeenCalledWith('error', '测试用户错误:', expect.stringContaining('邮箱格式无效'));
|
|
@@ -78,25 +78,15 @@ describe('Logger 错误处理', () => {
|
|
|
78
78
|
it('应该正确格式化 SystemError', () => {
|
|
79
79
|
const mockPrint = vi.fn();
|
|
80
80
|
const logger = new Logger({ print: mockPrint });
|
|
81
|
-
const systemError = new SystemError('database-error',
|
|
81
|
+
const systemError = new SystemError('database-error', {
|
|
82
82
|
table: 'users',
|
|
83
83
|
query: 'SELECT * FROM users',
|
|
84
84
|
operation: 'select'
|
|
85
|
-
});
|
|
85
|
+
}, '数据库连接失败');
|
|
86
86
|
logger.error('测试系统错误:', systemError);
|
|
87
87
|
expect(mockPrint).toHaveBeenCalledWith('error', '测试系统错误:', expect.stringContaining('⚠️ System Error [database-error]'));
|
|
88
88
|
expect(mockPrint).toHaveBeenCalledWith('error', '测试系统错误:', expect.stringContaining('数据库连接失败'));
|
|
89
89
|
});
|
|
90
|
-
it('应该正确格式化 UnknownError', () => {
|
|
91
|
-
const mockPrint = vi.fn();
|
|
92
|
-
const logger = new Logger({ print: mockPrint });
|
|
93
|
-
const unknownError = new UnknownError('未知错误', {
|
|
94
|
-
cause: 'something went wrong'
|
|
95
|
-
});
|
|
96
|
-
logger.error('测试未知错误:', unknownError);
|
|
97
|
-
expect(mockPrint).toHaveBeenCalledWith('error', '测试未知错误:', expect.stringContaining('❓ Unknown Error'));
|
|
98
|
-
expect(mockPrint).toHaveBeenCalledWith('error', '测试未知错误:', expect.stringContaining('未知错误'));
|
|
99
|
-
});
|
|
100
90
|
it('应该正确格式化普通 Error', () => {
|
|
101
91
|
const mockPrint = vi.fn();
|
|
102
92
|
const logger = new Logger({ print: mockPrint });
|
|
@@ -132,7 +122,7 @@ describe('Logger 错误处理', () => {
|
|
|
132
122
|
print: mockPrint,
|
|
133
123
|
formatter: customFormatter
|
|
134
124
|
});
|
|
135
|
-
const userError = new UserError('test-error',
|
|
125
|
+
const userError = new UserError('test-error', { field: 'test' }, '测试错误');
|
|
136
126
|
logger.error(userError);
|
|
137
127
|
const formattedError = mockPrint.mock.calls[0][1];
|
|
138
128
|
expect(formattedError).not.toContain('Stack:');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taicode/common-base",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.5",
|
|
4
4
|
"author": "Alain",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"description": "",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"output"
|
|
29
29
|
],
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"vitest": "^3.2.4"
|
|
31
|
+
"vitest": "^3.2.4",
|
|
32
|
+
"mobx": "^6.0.0"
|
|
32
33
|
}
|
|
33
34
|
}
|