@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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { UserError, SystemError
|
|
2
|
+
import { UserError, SystemError } from './error';
|
|
3
3
|
describe('UserError', () => {
|
|
4
4
|
describe('constructor', () => {
|
|
5
5
|
it('应该正确创建UserError实例', () => {
|
|
6
6
|
const type = 'VALIDATION_ERROR';
|
|
7
7
|
const message = '输入验证失败';
|
|
8
|
-
const error = new UserError(type, message);
|
|
8
|
+
const error = new UserError(type, undefined, message);
|
|
9
9
|
expect(error).toBeInstanceOf(Error);
|
|
10
10
|
expect(error).toBeInstanceOf(UserError);
|
|
11
11
|
expect(error.name).toBe('UserError');
|
|
@@ -32,7 +32,7 @@ describe('UserError', () => {
|
|
|
32
32
|
});
|
|
33
33
|
it('应该在提供空字符串message时保持空字符串', () => {
|
|
34
34
|
const type = 'VALIDATION_ERROR';
|
|
35
|
-
const error = new UserError(type, '');
|
|
35
|
+
const error = new UserError(type, {}, '');
|
|
36
36
|
expect(error).toBeInstanceOf(Error);
|
|
37
37
|
expect(error).toBeInstanceOf(UserError);
|
|
38
38
|
expect(error.name).toBe('UserError');
|
|
@@ -43,34 +43,32 @@ describe('UserError', () => {
|
|
|
43
43
|
const type = 'VALIDATION_ERROR';
|
|
44
44
|
const message = '输入验证失败';
|
|
45
45
|
const cause = new Error('原始错误');
|
|
46
|
-
const error = new UserError(type,
|
|
46
|
+
const error = new UserError(type, { cause }, message);
|
|
47
47
|
expect(error.cause).toBe(cause);
|
|
48
48
|
});
|
|
49
49
|
it('应该支持ErrorOptions参数且message为可选', () => {
|
|
50
50
|
const type = 'VALIDATION_ERROR';
|
|
51
51
|
const cause = new Error('原始错误');
|
|
52
|
-
const error = new UserError(type,
|
|
52
|
+
const error = new UserError(type, { cause });
|
|
53
53
|
expect(error.cause).toBe(cause);
|
|
54
54
|
expect(error.message).toBe('');
|
|
55
55
|
});
|
|
56
56
|
it('应该正确设置错误堆栈信息', () => {
|
|
57
|
-
const error = new UserError('TEST_ERROR', '测试错误');
|
|
57
|
+
const error = new UserError('TEST_ERROR', undefined, '测试错误');
|
|
58
58
|
expect(error.stack).toBeDefined();
|
|
59
59
|
expect(error.stack).toContain('UserError');
|
|
60
60
|
});
|
|
61
61
|
});
|
|
62
62
|
describe('is', () => {
|
|
63
63
|
it('应该正确识别UserError实例', () => {
|
|
64
|
-
const error = new UserError('TEST_ERROR', '测试错误');
|
|
64
|
+
const error = new UserError('TEST_ERROR', undefined, '测试错误');
|
|
65
65
|
expect(UserError.is(error)).toBe(true);
|
|
66
66
|
});
|
|
67
67
|
it('应该正确识别非UserError实例', () => {
|
|
68
68
|
const regularError = new Error('普通错误');
|
|
69
|
-
const systemError = new SystemError('SYSTEM_ERROR', '系统错误');
|
|
70
|
-
const unknownError = new UnknownError('未知错误');
|
|
69
|
+
const systemError = new SystemError('SYSTEM_ERROR', undefined, '系统错误');
|
|
71
70
|
expect(UserError.is(regularError)).toBe(false);
|
|
72
71
|
expect(UserError.is(systemError)).toBe(false);
|
|
73
|
-
expect(UserError.is(unknownError)).toBe(false);
|
|
74
72
|
expect(UserError.is(null)).toBe(false);
|
|
75
73
|
expect(UserError.is(undefined)).toBe(false);
|
|
76
74
|
expect(UserError.is('字符串')).toBe(false);
|
|
@@ -78,7 +76,7 @@ describe('UserError', () => {
|
|
|
78
76
|
expect(UserError.is({})).toBe(false);
|
|
79
77
|
});
|
|
80
78
|
it('应该支持类型守卫功能', () => {
|
|
81
|
-
const error = new UserError('TEST_ERROR', '测试错误');
|
|
79
|
+
const error = new UserError('TEST_ERROR', undefined, '测试错误');
|
|
82
80
|
if (UserError.is(error)) {
|
|
83
81
|
// 在类型守卫内部,error的类型应该被正确推断
|
|
84
82
|
expect(error.type).toBe('TEST_ERROR');
|
|
@@ -89,13 +87,13 @@ describe('UserError', () => {
|
|
|
89
87
|
});
|
|
90
88
|
describe('继承行为', () => {
|
|
91
89
|
it('应该正确继承Error的所有属性和方法', () => {
|
|
92
|
-
const error = new UserError('TEST_ERROR', '测试错误');
|
|
90
|
+
const error = new UserError('TEST_ERROR', undefined, '测试错误');
|
|
93
91
|
expect(error.toString()).toContain('测试错误');
|
|
94
92
|
expect(error.toString()).toContain('UserError');
|
|
95
93
|
expect(error instanceof Error).toBe(true);
|
|
96
94
|
});
|
|
97
95
|
it('应该支持Error的标准用法', () => {
|
|
98
|
-
const error = new UserError('TEST_ERROR', '测试错误');
|
|
96
|
+
const error = new UserError('TEST_ERROR', undefined, '测试错误');
|
|
99
97
|
try {
|
|
100
98
|
throw error;
|
|
101
99
|
}
|
|
@@ -111,7 +109,7 @@ describe('SystemError', () => {
|
|
|
111
109
|
it('应该正确创建SystemError实例', () => {
|
|
112
110
|
const type = 'DATABASE_ERROR';
|
|
113
111
|
const message = '数据库连接失败';
|
|
114
|
-
const error = new SystemError(type, message);
|
|
112
|
+
const error = new SystemError(type, undefined, message);
|
|
115
113
|
expect(error).toBeInstanceOf(Error);
|
|
116
114
|
expect(error).toBeInstanceOf(SystemError);
|
|
117
115
|
expect(error.name).toBe('SystemError');
|
|
@@ -138,7 +136,7 @@ describe('SystemError', () => {
|
|
|
138
136
|
});
|
|
139
137
|
it('应该在提供空字符串message时保持空字符串', () => {
|
|
140
138
|
const type = 'DATABASE_ERROR';
|
|
141
|
-
const error = new SystemError(type, '');
|
|
139
|
+
const error = new SystemError(type, {}, '');
|
|
142
140
|
expect(error).toBeInstanceOf(Error);
|
|
143
141
|
expect(error).toBeInstanceOf(SystemError);
|
|
144
142
|
expect(error.name).toBe('SystemError');
|
|
@@ -149,34 +147,32 @@ describe('SystemError', () => {
|
|
|
149
147
|
const type = 'NETWORK_ERROR';
|
|
150
148
|
const message = '网络请求失败';
|
|
151
149
|
const cause = new Error('连接超时');
|
|
152
|
-
const error = new SystemError(type,
|
|
150
|
+
const error = new SystemError(type, { cause }, message);
|
|
153
151
|
expect(error.cause).toBe(cause);
|
|
154
152
|
});
|
|
155
153
|
it('应该支持ErrorOptions参数且message为可选', () => {
|
|
156
154
|
const type = 'NETWORK_ERROR';
|
|
157
155
|
const cause = new Error('连接超时');
|
|
158
|
-
const error = new SystemError(type,
|
|
156
|
+
const error = new SystemError(type, { cause });
|
|
159
157
|
expect(error.cause).toBe(cause);
|
|
160
158
|
expect(error.message).toBe('');
|
|
161
159
|
});
|
|
162
160
|
it('应该正确设置错误堆栈信息', () => {
|
|
163
|
-
const error = new SystemError('FILE_ERROR', '文件读取失败');
|
|
161
|
+
const error = new SystemError('FILE_ERROR', undefined, '文件读取失败');
|
|
164
162
|
expect(error.stack).toBeDefined();
|
|
165
163
|
expect(error.stack).toContain('SystemError');
|
|
166
164
|
});
|
|
167
165
|
});
|
|
168
166
|
describe('is', () => {
|
|
169
167
|
it('应该正确识别SystemError实例', () => {
|
|
170
|
-
const error = new SystemError('TEST_ERROR', '测试错误');
|
|
168
|
+
const error = new SystemError('TEST_ERROR', undefined, '测试错误');
|
|
171
169
|
expect(SystemError.is(error)).toBe(true);
|
|
172
170
|
});
|
|
173
171
|
it('应该正确识别非SystemError实例', () => {
|
|
174
172
|
const regularError = new Error('普通错误');
|
|
175
|
-
const userError = new UserError('USER_ERROR', '用户错误');
|
|
176
|
-
const unknownError = new UnknownError('未知错误');
|
|
173
|
+
const userError = new UserError('USER_ERROR', undefined, '用户错误');
|
|
177
174
|
expect(SystemError.is(regularError)).toBe(false);
|
|
178
175
|
expect(SystemError.is(userError)).toBe(false);
|
|
179
|
-
expect(SystemError.is(unknownError)).toBe(false);
|
|
180
176
|
expect(SystemError.is(null)).toBe(false);
|
|
181
177
|
expect(SystemError.is(undefined)).toBe(false);
|
|
182
178
|
expect(SystemError.is('字符串')).toBe(false);
|
|
@@ -184,7 +180,7 @@ describe('SystemError', () => {
|
|
|
184
180
|
expect(SystemError.is({})).toBe(false);
|
|
185
181
|
});
|
|
186
182
|
it('应该支持类型守卫功能', () => {
|
|
187
|
-
const error = new SystemError('DATABASE_ERROR', '数据库错误');
|
|
183
|
+
const error = new SystemError('DATABASE_ERROR', undefined, '数据库错误');
|
|
188
184
|
if (SystemError.is(error)) {
|
|
189
185
|
// 在类型守卫内部,error的类型应该被正确推断
|
|
190
186
|
expect(error.type).toBe('DATABASE_ERROR');
|
|
@@ -195,13 +191,13 @@ describe('SystemError', () => {
|
|
|
195
191
|
});
|
|
196
192
|
describe('继承行为', () => {
|
|
197
193
|
it('应该正确继承Error的所有属性和方法', () => {
|
|
198
|
-
const error = new SystemError('TEST_ERROR', '测试错误');
|
|
194
|
+
const error = new SystemError('TEST_ERROR', undefined, '测试错误');
|
|
199
195
|
expect(error.toString()).toContain('测试错误');
|
|
200
196
|
expect(error.toString()).toContain('SystemError');
|
|
201
197
|
expect(error instanceof Error).toBe(true);
|
|
202
198
|
});
|
|
203
199
|
it('应该支持Error的标准用法', () => {
|
|
204
|
-
const error = new SystemError('TEST_ERROR', '测试错误');
|
|
200
|
+
const error = new SystemError('TEST_ERROR', undefined, '测试错误');
|
|
205
201
|
try {
|
|
206
202
|
throw error;
|
|
207
203
|
}
|
|
@@ -212,99 +208,176 @@ describe('SystemError', () => {
|
|
|
212
208
|
});
|
|
213
209
|
});
|
|
214
210
|
});
|
|
215
|
-
describe('UnknownError', () => {
|
|
216
|
-
describe('constructor', () => {
|
|
217
|
-
it('应该正确创建UnknownError实例', () => {
|
|
218
|
-
const message = '发生了未知错误';
|
|
219
|
-
const error = new UnknownError(message);
|
|
220
|
-
expect(error).toBeInstanceOf(Error);
|
|
221
|
-
expect(error).toBeInstanceOf(UnknownError);
|
|
222
|
-
expect(error.name).toBe('UnknownError');
|
|
223
|
-
expect(error.message).toBe(message);
|
|
224
|
-
});
|
|
225
|
-
it('应该支持ErrorOptions参数', () => {
|
|
226
|
-
const message = '未知错误';
|
|
227
|
-
const cause = new Error('原始未知错误');
|
|
228
|
-
const error = new UnknownError(message, { cause });
|
|
229
|
-
expect(error.cause).toBe(cause);
|
|
230
|
-
});
|
|
231
|
-
it('应该正确设置错误堆栈信息', () => {
|
|
232
|
-
const error = new UnknownError('测试未知错误');
|
|
233
|
-
expect(error.stack).toBeDefined();
|
|
234
|
-
expect(error.stack).toContain('UnknownError');
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
describe('is', () => {
|
|
238
|
-
it('应该正确识别UnknownError实例', () => {
|
|
239
|
-
const error = new UnknownError('测试错误');
|
|
240
|
-
expect(UnknownError.is(error)).toBe(true);
|
|
241
|
-
});
|
|
242
|
-
it('应该正确识别非UnknownError实例', () => {
|
|
243
|
-
const regularError = new Error('普通错误');
|
|
244
|
-
const userError = new UserError('USER_ERROR', '用户错误');
|
|
245
|
-
const systemError = new SystemError('SYSTEM_ERROR', '系统错误');
|
|
246
|
-
expect(UnknownError.is(regularError)).toBe(false);
|
|
247
|
-
expect(UnknownError.is(userError)).toBe(false);
|
|
248
|
-
expect(UnknownError.is(systemError)).toBe(false);
|
|
249
|
-
expect(UnknownError.is(null)).toBe(false);
|
|
250
|
-
expect(UnknownError.is(undefined)).toBe(false);
|
|
251
|
-
expect(UnknownError.is('字符串')).toBe(false);
|
|
252
|
-
expect(UnknownError.is(123)).toBe(false);
|
|
253
|
-
expect(UnknownError.is({})).toBe(false);
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
describe('继承行为', () => {
|
|
257
|
-
it('应该正确继承Error的所有属性和方法', () => {
|
|
258
|
-
const error = new UnknownError('测试错误');
|
|
259
|
-
expect(error.toString()).toContain('测试错误');
|
|
260
|
-
expect(error instanceof Error).toBe(true);
|
|
261
|
-
});
|
|
262
|
-
it('应该支持Error的标准用法', () => {
|
|
263
|
-
const error = new UnknownError('测试错误');
|
|
264
|
-
try {
|
|
265
|
-
throw error;
|
|
266
|
-
}
|
|
267
|
-
catch (caught) {
|
|
268
|
-
expect(caught).toBe(error);
|
|
269
|
-
expect(caught).toBeInstanceOf(UnknownError);
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
211
|
describe('错误类型区分', () => {
|
|
275
|
-
it('
|
|
276
|
-
const userError = new UserError('USER_ERROR', '用户错误');
|
|
277
|
-
const systemError = new SystemError('SYSTEM_ERROR', '系统错误');
|
|
278
|
-
const unknownError = new UnknownError('未知错误');
|
|
212
|
+
it('两种错误类型应该是不同的类', () => {
|
|
213
|
+
const userError = new UserError('USER_ERROR', undefined, '用户错误');
|
|
214
|
+
const systemError = new SystemError('SYSTEM_ERROR', undefined, '系统错误');
|
|
279
215
|
// 每种错误只能被自己的类型检查函数识别
|
|
280
216
|
expect(UserError.is(userError)).toBe(true);
|
|
281
217
|
expect(SystemError.is(userError)).toBe(false);
|
|
282
|
-
expect(UnknownError.is(userError)).toBe(false);
|
|
283
218
|
expect(UserError.is(systemError)).toBe(false);
|
|
284
219
|
expect(SystemError.is(systemError)).toBe(true);
|
|
285
|
-
expect(UnknownError.is(systemError)).toBe(false);
|
|
286
|
-
expect(UserError.is(unknownError)).toBe(false);
|
|
287
|
-
expect(SystemError.is(unknownError)).toBe(false);
|
|
288
|
-
expect(UnknownError.is(unknownError)).toBe(true);
|
|
289
220
|
});
|
|
290
221
|
it('所有错误类型都应该是Error的实例', () => {
|
|
291
|
-
const userError = new UserError('USER_ERROR', '用户错误');
|
|
292
|
-
const systemError = new SystemError('SYSTEM_ERROR', '系统错误');
|
|
293
|
-
const unknownError = new UnknownError('未知错误');
|
|
222
|
+
const userError = new UserError('USER_ERROR', undefined, '用户错误');
|
|
223
|
+
const systemError = new SystemError('SYSTEM_ERROR', undefined, '系统错误');
|
|
294
224
|
expect(userError instanceof Error).toBe(true);
|
|
295
225
|
expect(systemError instanceof Error).toBe(true);
|
|
296
|
-
expect(unknownError instanceof Error).toBe(true);
|
|
297
226
|
});
|
|
298
227
|
});
|
|
299
228
|
describe('泛型类型支持', () => {
|
|
300
229
|
it('UserError应该支持字符串字面量类型', () => {
|
|
301
|
-
const error = new UserError('REQUIRED_FIELD', '字段必填');
|
|
230
|
+
const error = new UserError('REQUIRED_FIELD', {}, '字段必填');
|
|
302
231
|
expect(error.type).toBe('REQUIRED_FIELD');
|
|
303
232
|
expect(UserError.is(error)).toBe(true);
|
|
304
233
|
});
|
|
305
234
|
it('SystemError应该支持字符串字面量类型', () => {
|
|
306
|
-
const error = new SystemError('DATABASE_ERROR', '数据库连接失败');
|
|
235
|
+
const error = new SystemError('DATABASE_ERROR', {}, '数据库连接失败');
|
|
307
236
|
expect(error.type).toBe('DATABASE_ERROR');
|
|
308
237
|
expect(SystemError.is(error)).toBe(true);
|
|
309
238
|
});
|
|
310
239
|
});
|
|
240
|
+
describe('define 工厂方法', () => {
|
|
241
|
+
describe('UserError.define', () => {
|
|
242
|
+
it('应该定义一个绑定了特定错误类型的 UserError 类', () => {
|
|
243
|
+
const MyUserError = UserError.define({
|
|
244
|
+
'validation-failed': {},
|
|
245
|
+
'permission-denied': {},
|
|
246
|
+
'not-found': {}
|
|
247
|
+
});
|
|
248
|
+
const error = new MyUserError('validation-failed', {
|
|
249
|
+
field: 'email',
|
|
250
|
+
value: 'invalid@'
|
|
251
|
+
}, '验证失败');
|
|
252
|
+
expect(error).toBeInstanceOf(Error);
|
|
253
|
+
expect(error.name).toBe('UserError');
|
|
254
|
+
expect(error.type).toBe('validation-failed');
|
|
255
|
+
expect(error.message).toBe('验证失败');
|
|
256
|
+
expect(error.context).toEqual({ field: 'email', value: 'invalid@' });
|
|
257
|
+
});
|
|
258
|
+
it('应该支持 is 类型守卫', () => {
|
|
259
|
+
const MyUserError = UserError.define({
|
|
260
|
+
'test-error': {}
|
|
261
|
+
});
|
|
262
|
+
const error = new MyUserError('test-error', { code: 123 }, '测试错误');
|
|
263
|
+
expect(MyUserError.is(error)).toBe(true);
|
|
264
|
+
expect(MyUserError.is(new Error('普通错误'))).toBe(false);
|
|
265
|
+
expect(MyUserError.is(null)).toBe(false);
|
|
266
|
+
});
|
|
267
|
+
it('应该支持多次调用 define 定义不同的错误类', () => {
|
|
268
|
+
const ValidationError = UserError.define({
|
|
269
|
+
'required': {},
|
|
270
|
+
'invalid': {}
|
|
271
|
+
});
|
|
272
|
+
const AuthError = UserError.define({
|
|
273
|
+
'unauthorized': {},
|
|
274
|
+
'forbidden': {}
|
|
275
|
+
});
|
|
276
|
+
const validationError = new ValidationError('required', { field: 'username' }, '必填字段');
|
|
277
|
+
const authError = new AuthError('unauthorized', { reason: 'token expired' }, '未授权');
|
|
278
|
+
expect(validationError.type).toBe('required');
|
|
279
|
+
expect(authError.type).toBe('unauthorized');
|
|
280
|
+
expect(ValidationError.is(validationError)).toBe(true);
|
|
281
|
+
expect(ValidationError.is(authError)).toBe(false); // 不同的类不会互相识别
|
|
282
|
+
expect(AuthError.is(authError)).toBe(true);
|
|
283
|
+
expect(AuthError.is(validationError)).toBe(false); // 不同的类不会互相识别
|
|
284
|
+
});
|
|
285
|
+
it('应该支持不传 context 参数', () => {
|
|
286
|
+
const MyUserError = UserError.define({
|
|
287
|
+
'simple-error': {}
|
|
288
|
+
});
|
|
289
|
+
const error = new MyUserError('simple-error', undefined, '简单错误');
|
|
290
|
+
expect(error.type).toBe('simple-error');
|
|
291
|
+
expect(error.message).toBe('简单错误');
|
|
292
|
+
expect(error.context).toBeUndefined();
|
|
293
|
+
});
|
|
294
|
+
it('应该支持不传 message 参数', () => {
|
|
295
|
+
const MyUserError = UserError.define({
|
|
296
|
+
'no-message': {}
|
|
297
|
+
});
|
|
298
|
+
const error = new MyUserError('no-message');
|
|
299
|
+
expect(error.type).toBe('no-message');
|
|
300
|
+
expect(error.message).toBe('');
|
|
301
|
+
});
|
|
302
|
+
it('应该暴露所有定义的错误类型', () => {
|
|
303
|
+
const MyUserError = UserError.define({
|
|
304
|
+
'validation-failed': {},
|
|
305
|
+
'permission-denied': {},
|
|
306
|
+
'not-found': {}
|
|
307
|
+
});
|
|
308
|
+
expect(MyUserError.types).toEqual(['validation-failed', 'permission-denied', 'not-found']);
|
|
309
|
+
expect(MyUserError.types).toHaveLength(3);
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
describe('SystemError.define', () => {
|
|
313
|
+
it('应该定义一个绑定了特定错误类型的 SystemError 类', () => {
|
|
314
|
+
const MySystemError = SystemError.define({
|
|
315
|
+
'database-error': {},
|
|
316
|
+
'network-error': {},
|
|
317
|
+
'service-unavailable': {}
|
|
318
|
+
});
|
|
319
|
+
const error = new MySystemError('database-error', {
|
|
320
|
+
query: 'SELECT * FROM users',
|
|
321
|
+
table: 'users'
|
|
322
|
+
}, '数据库错误');
|
|
323
|
+
expect(error).toBeInstanceOf(Error);
|
|
324
|
+
expect(error.name).toBe('SystemError');
|
|
325
|
+
expect(error.type).toBe('database-error');
|
|
326
|
+
expect(error.message).toBe('数据库错误');
|
|
327
|
+
expect(error.context).toEqual({ query: 'SELECT * FROM users', table: 'users' });
|
|
328
|
+
});
|
|
329
|
+
it('应该支持 is 类型守卫', () => {
|
|
330
|
+
const MySystemError = SystemError.define({
|
|
331
|
+
'test-error': {}
|
|
332
|
+
});
|
|
333
|
+
const error = new MySystemError('test-error', { code: 500 }, '测试错误');
|
|
334
|
+
expect(MySystemError.is(error)).toBe(true);
|
|
335
|
+
expect(MySystemError.is(new Error('普通错误'))).toBe(false);
|
|
336
|
+
expect(MySystemError.is(null)).toBe(false);
|
|
337
|
+
});
|
|
338
|
+
it('应该支持多次调用 define 定义不同的错误类', () => {
|
|
339
|
+
const DatabaseError = SystemError.define({
|
|
340
|
+
'connection-failed': {},
|
|
341
|
+
'query-timeout': {}
|
|
342
|
+
});
|
|
343
|
+
const NetworkError = SystemError.define({
|
|
344
|
+
'request-failed': {},
|
|
345
|
+
'timeout': {}
|
|
346
|
+
});
|
|
347
|
+
const dbError = new DatabaseError('connection-failed', { host: 'localhost', port: 5432 }, '连接失败');
|
|
348
|
+
const netError = new NetworkError('request-failed', { url: '/api/data', method: 'GET' }, '请求失败');
|
|
349
|
+
expect(dbError.type).toBe('connection-failed');
|
|
350
|
+
expect(netError.type).toBe('request-failed');
|
|
351
|
+
expect(DatabaseError.is(dbError)).toBe(true);
|
|
352
|
+
expect(DatabaseError.is(netError)).toBe(false); // 不同的类不会互相识别
|
|
353
|
+
expect(NetworkError.is(netError)).toBe(true);
|
|
354
|
+
expect(NetworkError.is(dbError)).toBe(false); // 不同的类不会互相识别
|
|
355
|
+
});
|
|
356
|
+
it('应该支持不传 context 参数', () => {
|
|
357
|
+
const MySystemError = SystemError.define({
|
|
358
|
+
'simple-error': {}
|
|
359
|
+
});
|
|
360
|
+
const error = new MySystemError('simple-error', undefined, '简单错误');
|
|
361
|
+
expect(error.type).toBe('simple-error');
|
|
362
|
+
expect(error.message).toBe('简单错误');
|
|
363
|
+
expect(error.context).toBeUndefined();
|
|
364
|
+
});
|
|
365
|
+
it('应该支持不传 message 参数', () => {
|
|
366
|
+
const MySystemError = SystemError.define({
|
|
367
|
+
'no-message': {}
|
|
368
|
+
});
|
|
369
|
+
const error = new MySystemError('no-message');
|
|
370
|
+
expect(error.type).toBe('no-message');
|
|
371
|
+
expect(error.message).toBe('');
|
|
372
|
+
});
|
|
373
|
+
it('应该暴露所有定义的错误类型', () => {
|
|
374
|
+
const MySystemError = SystemError.define({
|
|
375
|
+
'database-error': {},
|
|
376
|
+
'network-error': {},
|
|
377
|
+
'service-unavailable': {}
|
|
378
|
+
});
|
|
379
|
+
expect(MySystemError.types).toEqual(['database-error', 'network-error', 'service-unavailable']);
|
|
380
|
+
expect(MySystemError.types).toHaveLength(3);
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
});
|
package/output/error/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { UserError, SystemError
|
|
1
|
+
export { UserError, SystemError } from './error';
|
|
2
|
+
export type { ErrorContext } from './error';
|
|
2
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/error/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/error/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,WAAW,EACZ,MAAM,SAAS,CAAA;AAEhB,YAAY,EACV,YAAY,EACb,MAAM,SAAS,CAAA"}
|
package/output/error/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { UserError, SystemError
|
|
1
|
+
export { UserError, SystemError } from './error';
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disposer.test.d.ts","sourceRoot":"","sources":["../../source/events/disposer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,192 @@
|
|
|
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
|
+
});
|