@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.
- package/output/batching-buffer/batching-buffer.d.ts +140 -0
- package/output/batching-buffer/batching-buffer.d.ts.map +1 -0
- package/output/batching-buffer/batching-buffer.js +273 -0
- package/output/batching-buffer/batching-buffer.test.d.ts +2 -0
- package/output/batching-buffer/batching-buffer.test.d.ts.map +1 -0
- package/output/batching-buffer/batching-buffer.test.js +511 -0
- package/output/batching-buffer/index.d.ts +3 -0
- package/output/batching-buffer/index.d.ts.map +1 -0
- package/output/batching-buffer/index.js +1 -0
- package/output/index.d.ts +2 -0
- package/output/index.d.ts.map +1 -1
- package/output/index.js +3 -0
- 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
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { BatchingBuffer } from './batching-buffer';
|
|
3
|
+
describe('BatchingBuffer', () => {
|
|
4
|
+
let batchProcessor;
|
|
5
|
+
let itemProcessor;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
batchProcessor = vi.fn();
|
|
8
|
+
itemProcessor = vi.fn();
|
|
9
|
+
vi.useFakeTimers();
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.useRealTimers();
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
describe('构造函数', () => {
|
|
16
|
+
it('应该正确初始化批量处理模式', () => {
|
|
17
|
+
const buffer = new BatchingBuffer({
|
|
18
|
+
batchProcessor: batchProcessor,
|
|
19
|
+
bufferSize: 10,
|
|
20
|
+
flushInterval: 5000
|
|
21
|
+
});
|
|
22
|
+
expect(buffer.size).toBe(0);
|
|
23
|
+
expect(buffer.isEmpty).toBe(true);
|
|
24
|
+
expect(buffer.destroyed).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
it('应该正确初始化单个处理模式', () => {
|
|
27
|
+
const buffer = new BatchingBuffer({
|
|
28
|
+
itemProcessor: itemProcessor,
|
|
29
|
+
bufferSize: 5,
|
|
30
|
+
flushInterval: 3000
|
|
31
|
+
});
|
|
32
|
+
expect(buffer.size).toBe(0);
|
|
33
|
+
expect(buffer.isEmpty).toBe(true);
|
|
34
|
+
expect(buffer.destroyed).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
it('应该使用默认配置', () => {
|
|
37
|
+
const buffer = new BatchingBuffer({
|
|
38
|
+
batchProcessor: batchProcessor
|
|
39
|
+
});
|
|
40
|
+
expect(buffer.size).toBe(0);
|
|
41
|
+
expect(buffer.isEmpty).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
it('应该在没有提供处理器时抛出错误', () => {
|
|
44
|
+
expect(() => {
|
|
45
|
+
new BatchingBuffer({});
|
|
46
|
+
}).toThrow('必须提供 batchProcessor 或 itemProcessor 其中之一');
|
|
47
|
+
});
|
|
48
|
+
it('应该在同时提供两个处理器时抛出错误', () => {
|
|
49
|
+
expect(() => {
|
|
50
|
+
new BatchingBuffer({
|
|
51
|
+
batchProcessor: batchProcessor,
|
|
52
|
+
itemProcessor: itemProcessor
|
|
53
|
+
});
|
|
54
|
+
}).toThrow('只能选择 batchProcessor 或 itemProcessor 中的一个');
|
|
55
|
+
});
|
|
56
|
+
it('应该在 bufferSize 无效时抛出错误', () => {
|
|
57
|
+
expect(() => {
|
|
58
|
+
new BatchingBuffer({
|
|
59
|
+
batchProcessor: batchProcessor,
|
|
60
|
+
bufferSize: 0
|
|
61
|
+
});
|
|
62
|
+
}).toThrow('bufferSize 必须大于 0');
|
|
63
|
+
expect(() => {
|
|
64
|
+
new BatchingBuffer({
|
|
65
|
+
batchProcessor: batchProcessor,
|
|
66
|
+
bufferSize: -1
|
|
67
|
+
});
|
|
68
|
+
}).toThrow('bufferSize 必须大于 0');
|
|
69
|
+
});
|
|
70
|
+
it('应该在 flushInterval 无效时抛出错误', () => {
|
|
71
|
+
expect(() => {
|
|
72
|
+
new BatchingBuffer({
|
|
73
|
+
batchProcessor: batchProcessor,
|
|
74
|
+
flushInterval: 0
|
|
75
|
+
});
|
|
76
|
+
}).toThrow('flushInterval 必须大于 0');
|
|
77
|
+
expect(() => {
|
|
78
|
+
new BatchingBuffer({
|
|
79
|
+
batchProcessor: batchProcessor,
|
|
80
|
+
flushInterval: -1
|
|
81
|
+
});
|
|
82
|
+
}).toThrow('flushInterval 必须大于 0');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe('批量处理模式', () => {
|
|
86
|
+
let buffer;
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
buffer = new BatchingBuffer({
|
|
89
|
+
batchProcessor: batchProcessor,
|
|
90
|
+
bufferSize: 3,
|
|
91
|
+
flushInterval: 1000
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
afterEach(async () => {
|
|
95
|
+
await buffer.destroy();
|
|
96
|
+
});
|
|
97
|
+
it('应该在达到缓冲区大小时触发批量处理', () => {
|
|
98
|
+
buffer.add('item1');
|
|
99
|
+
buffer.add('item2');
|
|
100
|
+
expect(batchProcessor).not.toHaveBeenCalled();
|
|
101
|
+
buffer.add('item3');
|
|
102
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
103
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1', 'item2', 'item3']);
|
|
104
|
+
expect(buffer.size).toBe(0);
|
|
105
|
+
});
|
|
106
|
+
it('应该在时间间隔到达时触发批量处理', async () => {
|
|
107
|
+
buffer.add('item1');
|
|
108
|
+
buffer.add('item2');
|
|
109
|
+
expect(batchProcessor).not.toHaveBeenCalled();
|
|
110
|
+
vi.advanceTimersByTime(1000);
|
|
111
|
+
await vi.runAllTimersAsync();
|
|
112
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
113
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1', 'item2']);
|
|
114
|
+
expect(buffer.size).toBe(0);
|
|
115
|
+
});
|
|
116
|
+
it('应该支持手动刷新', async () => {
|
|
117
|
+
buffer.add('item1');
|
|
118
|
+
buffer.add('item2');
|
|
119
|
+
await buffer.flush();
|
|
120
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
121
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1', 'item2']);
|
|
122
|
+
expect(buffer.size).toBe(0);
|
|
123
|
+
});
|
|
124
|
+
it('应该支持批量添加数据', () => {
|
|
125
|
+
buffer.add('item1', 'item2', 'item3', 'item4', 'item5');
|
|
126
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
127
|
+
expect(batchProcessor).toHaveBeenNthCalledWith(1, ['item1', 'item2', 'item3']);
|
|
128
|
+
// 剩余的项目应该在缓冲区中
|
|
129
|
+
expect(buffer.size).toBe(2);
|
|
130
|
+
});
|
|
131
|
+
it('应该支持添加空参数', () => {
|
|
132
|
+
buffer.add();
|
|
133
|
+
expect(batchProcessor).not.toHaveBeenCalled();
|
|
134
|
+
expect(buffer.size).toBe(0);
|
|
135
|
+
});
|
|
136
|
+
it('应该支持混合添加单个和多个数据', () => {
|
|
137
|
+
buffer.add('item1'); // 单个
|
|
138
|
+
buffer.add('item2', 'item3'); // 多个
|
|
139
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
140
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1', 'item2', 'item3']);
|
|
141
|
+
expect(buffer.size).toBe(0);
|
|
142
|
+
});
|
|
143
|
+
it('应该支持使用数组展开语法', () => {
|
|
144
|
+
const items = ['item1', 'item2', 'item3', 'item4', 'item5'];
|
|
145
|
+
buffer.add(...items);
|
|
146
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
147
|
+
expect(batchProcessor).toHaveBeenNthCalledWith(1, ['item1', 'item2', 'item3']);
|
|
148
|
+
// 剩余的项目应该在缓冲区中
|
|
149
|
+
expect(buffer.size).toBe(2);
|
|
150
|
+
});
|
|
151
|
+
it('应该正确处理处理器异常', () => {
|
|
152
|
+
const error = new Error('处理失败');
|
|
153
|
+
batchProcessor.mockImplementationOnce(() => {
|
|
154
|
+
throw error;
|
|
155
|
+
});
|
|
156
|
+
buffer.add('item1');
|
|
157
|
+
buffer.add('item2');
|
|
158
|
+
expect(() => {
|
|
159
|
+
buffer.add('item3');
|
|
160
|
+
}).toThrow('处理失败');
|
|
161
|
+
// 数据应该重新放回缓冲区
|
|
162
|
+
expect(buffer.size).toBe(3);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe('单个处理模式', () => {
|
|
166
|
+
let buffer;
|
|
167
|
+
beforeEach(() => {
|
|
168
|
+
buffer = new BatchingBuffer({
|
|
169
|
+
itemProcessor: itemProcessor,
|
|
170
|
+
bufferSize: 3,
|
|
171
|
+
flushInterval: 1000
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
afterEach(async () => {
|
|
175
|
+
await buffer.destroy();
|
|
176
|
+
});
|
|
177
|
+
it('应该在达到缓冲区大小时触发单个处理', () => {
|
|
178
|
+
buffer.add('item1');
|
|
179
|
+
buffer.add('item2');
|
|
180
|
+
expect(itemProcessor).not.toHaveBeenCalled();
|
|
181
|
+
buffer.add('item3');
|
|
182
|
+
expect(itemProcessor).toHaveBeenCalledTimes(3);
|
|
183
|
+
expect(itemProcessor).toHaveBeenNthCalledWith(1, 'item1');
|
|
184
|
+
expect(itemProcessor).toHaveBeenNthCalledWith(2, 'item2');
|
|
185
|
+
expect(itemProcessor).toHaveBeenNthCalledWith(3, 'item3');
|
|
186
|
+
expect(buffer.size).toBe(0);
|
|
187
|
+
});
|
|
188
|
+
it('应该在时间间隔到达时触发单个处理', async () => {
|
|
189
|
+
buffer.add('item1');
|
|
190
|
+
buffer.add('item2');
|
|
191
|
+
expect(itemProcessor).not.toHaveBeenCalled();
|
|
192
|
+
vi.advanceTimersByTime(1000);
|
|
193
|
+
await vi.runAllTimersAsync();
|
|
194
|
+
expect(itemProcessor).toHaveBeenCalledTimes(2);
|
|
195
|
+
expect(itemProcessor).toHaveBeenNthCalledWith(1, 'item1');
|
|
196
|
+
expect(itemProcessor).toHaveBeenNthCalledWith(2, 'item2');
|
|
197
|
+
expect(buffer.size).toBe(0);
|
|
198
|
+
});
|
|
199
|
+
it('应该正确处理单个处理器异常', () => {
|
|
200
|
+
const error = new Error('单个处理失败');
|
|
201
|
+
itemProcessor.mockImplementationOnce(() => {
|
|
202
|
+
throw error;
|
|
203
|
+
});
|
|
204
|
+
buffer.add('item1');
|
|
205
|
+
buffer.add('item2');
|
|
206
|
+
expect(() => {
|
|
207
|
+
buffer.add('item3');
|
|
208
|
+
}).toThrow('单个处理失败');
|
|
209
|
+
// 数据应该重新放回缓冲区
|
|
210
|
+
expect(buffer.size).toBe(3);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
describe('缓冲区状态', () => {
|
|
214
|
+
let buffer;
|
|
215
|
+
beforeEach(() => {
|
|
216
|
+
buffer = new BatchingBuffer({
|
|
217
|
+
batchProcessor: batchProcessor,
|
|
218
|
+
bufferSize: 5,
|
|
219
|
+
flushInterval: 2000
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
afterEach(async () => {
|
|
223
|
+
await buffer.destroy();
|
|
224
|
+
});
|
|
225
|
+
it('应该正确报告缓冲区大小', () => {
|
|
226
|
+
expect(buffer.size).toBe(0);
|
|
227
|
+
expect(buffer.isEmpty).toBe(true);
|
|
228
|
+
buffer.add(1);
|
|
229
|
+
expect(buffer.size).toBe(1);
|
|
230
|
+
expect(buffer.isEmpty).toBe(false);
|
|
231
|
+
buffer.add(2);
|
|
232
|
+
buffer.add(3);
|
|
233
|
+
expect(buffer.size).toBe(3);
|
|
234
|
+
expect(buffer.isEmpty).toBe(false);
|
|
235
|
+
});
|
|
236
|
+
it('应该正确报告距离上次刷新的时间', async () => {
|
|
237
|
+
const startTime = Date.now();
|
|
238
|
+
buffer.add(1);
|
|
239
|
+
// 使用假定时器来精确控制时间
|
|
240
|
+
vi.advanceTimersByTime(500);
|
|
241
|
+
expect(buffer.timeSinceLastFlush).toBe(500);
|
|
242
|
+
await buffer.flush();
|
|
243
|
+
expect(buffer.timeSinceLastFlush).toBe(0);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
describe('销毁功能', () => {
|
|
247
|
+
let buffer;
|
|
248
|
+
beforeEach(() => {
|
|
249
|
+
buffer = new BatchingBuffer({
|
|
250
|
+
batchProcessor: batchProcessor,
|
|
251
|
+
bufferSize: 5,
|
|
252
|
+
flushInterval: 1000
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
it('应该在销毁时处理剩余数据', async () => {
|
|
256
|
+
buffer.add('item1');
|
|
257
|
+
buffer.add('item2');
|
|
258
|
+
await buffer.destroy();
|
|
259
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
260
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1', 'item2']);
|
|
261
|
+
expect(buffer.destroyed).toBe(true);
|
|
262
|
+
expect(buffer.size).toBe(0);
|
|
263
|
+
});
|
|
264
|
+
it('应该在销毁时丢弃剩余数据(如果指定)', async () => {
|
|
265
|
+
buffer.add('item1');
|
|
266
|
+
buffer.add('item2');
|
|
267
|
+
await buffer.destroy(false);
|
|
268
|
+
expect(batchProcessor).not.toHaveBeenCalled();
|
|
269
|
+
expect(buffer.destroyed).toBe(true);
|
|
270
|
+
expect(buffer.size).toBe(0);
|
|
271
|
+
});
|
|
272
|
+
it('应该在销毁后拒绝添加新数据', async () => {
|
|
273
|
+
await buffer.destroy();
|
|
274
|
+
expect(() => {
|
|
275
|
+
buffer.add('item1');
|
|
276
|
+
}).toThrow('BatchingBuffer 已被销毁,不能添加新数据');
|
|
277
|
+
expect(() => {
|
|
278
|
+
buffer.add('item1', 'item2');
|
|
279
|
+
}).toThrow('BatchingBuffer 已被销毁,不能添加新数据');
|
|
280
|
+
});
|
|
281
|
+
it('应该支持重复销毁', async () => {
|
|
282
|
+
await buffer.destroy();
|
|
283
|
+
await buffer.destroy();
|
|
284
|
+
expect(buffer.destroyed).toBe(true);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
describe('自动刷新配置', () => {
|
|
288
|
+
it('应该在 autoFlush=false 时不自动启动定时器', async () => {
|
|
289
|
+
const buffer = new BatchingBuffer({
|
|
290
|
+
batchProcessor: batchProcessor,
|
|
291
|
+
bufferSize: 5,
|
|
292
|
+
flushInterval: 1000,
|
|
293
|
+
autoFlush: false
|
|
294
|
+
});
|
|
295
|
+
buffer.add('item1');
|
|
296
|
+
buffer.add('item2');
|
|
297
|
+
vi.advanceTimersByTime(2000);
|
|
298
|
+
await vi.runAllTimersAsync();
|
|
299
|
+
expect(batchProcessor).not.toHaveBeenCalled();
|
|
300
|
+
expect(buffer.size).toBe(2);
|
|
301
|
+
await buffer.destroy();
|
|
302
|
+
});
|
|
303
|
+
it('应该在 autoFlush=false 时仍支持基于大小的触发', async () => {
|
|
304
|
+
const buffer = new BatchingBuffer({
|
|
305
|
+
batchProcessor: batchProcessor,
|
|
306
|
+
bufferSize: 2,
|
|
307
|
+
flushInterval: 1000,
|
|
308
|
+
autoFlush: false
|
|
309
|
+
});
|
|
310
|
+
buffer.add('item1');
|
|
311
|
+
buffer.add('item2');
|
|
312
|
+
await vi.runAllTimersAsync();
|
|
313
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
314
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1', 'item2']);
|
|
315
|
+
await buffer.destroy();
|
|
316
|
+
});
|
|
317
|
+
it('应该正确处理定时器重置', async () => {
|
|
318
|
+
const buffer = new BatchingBuffer({
|
|
319
|
+
batchProcessor: batchProcessor,
|
|
320
|
+
bufferSize: 5,
|
|
321
|
+
flushInterval: 1000,
|
|
322
|
+
autoFlush: true
|
|
323
|
+
});
|
|
324
|
+
// 添加第一个项目,启动定时器
|
|
325
|
+
buffer.add('item1');
|
|
326
|
+
vi.advanceTimersByTime(500);
|
|
327
|
+
// 添加第二个项目,应该重置定时器
|
|
328
|
+
buffer.add('item2');
|
|
329
|
+
vi.advanceTimersByTime(500);
|
|
330
|
+
// 此时应该还没触发(总共过了1000ms,但最后一次重置后只过了500ms)
|
|
331
|
+
expect(batchProcessor).not.toHaveBeenCalled();
|
|
332
|
+
// 再等500ms,现在应该触发了
|
|
333
|
+
vi.advanceTimersByTime(500);
|
|
334
|
+
await vi.runAllTimersAsync();
|
|
335
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
336
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1', 'item2']);
|
|
337
|
+
await buffer.destroy();
|
|
338
|
+
});
|
|
339
|
+
it('应该在销毁时清除定时器避免内存泄漏', async () => {
|
|
340
|
+
const buffer = new BatchingBuffer({
|
|
341
|
+
batchProcessor: batchProcessor,
|
|
342
|
+
bufferSize: 5,
|
|
343
|
+
flushInterval: 1000,
|
|
344
|
+
autoFlush: true
|
|
345
|
+
});
|
|
346
|
+
buffer.add('item1');
|
|
347
|
+
// 立即销毁,应该处理剩余数据但不会因为定时器再次触发
|
|
348
|
+
await buffer.destroy();
|
|
349
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
350
|
+
// 等待原本的定时器时间,不应该再次触发
|
|
351
|
+
vi.advanceTimersByTime(1000);
|
|
352
|
+
await vi.runAllTimersAsync();
|
|
353
|
+
// 仍然只被调用一次
|
|
354
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
describe('复杂数据类型', () => {
|
|
358
|
+
let buffer;
|
|
359
|
+
beforeEach(() => {
|
|
360
|
+
buffer = new BatchingBuffer({
|
|
361
|
+
batchProcessor: batchProcessor,
|
|
362
|
+
bufferSize: 2,
|
|
363
|
+
flushInterval: 1000
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
afterEach(async () => {
|
|
367
|
+
await buffer.destroy();
|
|
368
|
+
});
|
|
369
|
+
it('应该正确处理复杂对象', async () => {
|
|
370
|
+
const data1 = { id: 1, name: 'test1' };
|
|
371
|
+
const data2 = { id: 2, name: 'test2' };
|
|
372
|
+
buffer.add(data1);
|
|
373
|
+
buffer.add(data2);
|
|
374
|
+
await vi.runAllTimersAsync();
|
|
375
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
376
|
+
expect(batchProcessor).toHaveBeenCalledWith([data1, data2]);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
describe('异步处理器', () => {
|
|
380
|
+
let asyncBatchProcessor;
|
|
381
|
+
let asyncItemProcessor;
|
|
382
|
+
beforeEach(() => {
|
|
383
|
+
asyncBatchProcessor = vi.fn().mockImplementation(async (items) => {
|
|
384
|
+
// 使用 fake timer 而不是真实的 setTimeout
|
|
385
|
+
return `处理了 ${items.length} 个项目`;
|
|
386
|
+
});
|
|
387
|
+
asyncItemProcessor = vi.fn().mockImplementation(async (item) => {
|
|
388
|
+
// 使用 fake timer 而不是真实的 setTimeout
|
|
389
|
+
return `处理了项目: ${item}`;
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
it('应该正确处理异步批量处理器', () => {
|
|
393
|
+
const buffer = new BatchingBuffer({
|
|
394
|
+
batchProcessor: asyncBatchProcessor,
|
|
395
|
+
bufferSize: 2,
|
|
396
|
+
flushInterval: 1000
|
|
397
|
+
});
|
|
398
|
+
buffer.add('item1');
|
|
399
|
+
buffer.add('item2');
|
|
400
|
+
expect(asyncBatchProcessor).toHaveBeenCalledTimes(1);
|
|
401
|
+
expect(asyncBatchProcessor).toHaveBeenCalledWith(['item1', 'item2']);
|
|
402
|
+
buffer.destroy();
|
|
403
|
+
});
|
|
404
|
+
it('应该正确处理异步单个处理器', () => {
|
|
405
|
+
const buffer = new BatchingBuffer({
|
|
406
|
+
itemProcessor: asyncItemProcessor,
|
|
407
|
+
bufferSize: 2,
|
|
408
|
+
flushInterval: 1000
|
|
409
|
+
});
|
|
410
|
+
buffer.add('item1');
|
|
411
|
+
buffer.add('item2');
|
|
412
|
+
expect(asyncItemProcessor).toHaveBeenCalledTimes(2);
|
|
413
|
+
expect(asyncItemProcessor).toHaveBeenNthCalledWith(1, 'item1');
|
|
414
|
+
expect(asyncItemProcessor).toHaveBeenNthCalledWith(2, 'item2');
|
|
415
|
+
buffer.destroy();
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
describe('错误处理回调', () => {
|
|
419
|
+
it('应该支持配置错误处理回调', () => {
|
|
420
|
+
const onError = vi.fn();
|
|
421
|
+
const buffer = new BatchingBuffer({
|
|
422
|
+
batchProcessor: vi.fn(),
|
|
423
|
+
bufferSize: 10,
|
|
424
|
+
flushInterval: 1000,
|
|
425
|
+
onError
|
|
426
|
+
});
|
|
427
|
+
expect(buffer).toBeDefined();
|
|
428
|
+
buffer.destroy();
|
|
429
|
+
});
|
|
430
|
+
it('错误处理回调应该可以正确配置', () => {
|
|
431
|
+
const onError = vi.fn();
|
|
432
|
+
const buffer = new BatchingBuffer({
|
|
433
|
+
batchProcessor: vi.fn(),
|
|
434
|
+
bufferSize: 10,
|
|
435
|
+
flushInterval: 1000,
|
|
436
|
+
onError
|
|
437
|
+
});
|
|
438
|
+
// 验证缓冲区正常工作
|
|
439
|
+
buffer.add('item1');
|
|
440
|
+
expect(buffer.size).toBe(1);
|
|
441
|
+
buffer.destroy();
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
describe('边界条件测试', () => {
|
|
445
|
+
it('应该正确处理极小的 bufferSize', () => {
|
|
446
|
+
const buffer = new BatchingBuffer({
|
|
447
|
+
batchProcessor: batchProcessor,
|
|
448
|
+
bufferSize: 1,
|
|
449
|
+
flushInterval: 1000
|
|
450
|
+
});
|
|
451
|
+
buffer.add('item1');
|
|
452
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
453
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1']);
|
|
454
|
+
buffer.destroy();
|
|
455
|
+
});
|
|
456
|
+
it('应该正确处理极小的 flushInterval', async () => {
|
|
457
|
+
const buffer = new BatchingBuffer({
|
|
458
|
+
batchProcessor: batchProcessor,
|
|
459
|
+
bufferSize: 5,
|
|
460
|
+
flushInterval: 1 // 1ms
|
|
461
|
+
});
|
|
462
|
+
buffer.add('item1');
|
|
463
|
+
vi.advanceTimersByTime(1);
|
|
464
|
+
await vi.runAllTimersAsync();
|
|
465
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
466
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1']);
|
|
467
|
+
await buffer.destroy();
|
|
468
|
+
});
|
|
469
|
+
it('应该正确处理同时达到大小和时间限制', async () => {
|
|
470
|
+
const buffer = new BatchingBuffer({
|
|
471
|
+
batchProcessor: batchProcessor,
|
|
472
|
+
bufferSize: 2,
|
|
473
|
+
flushInterval: 100
|
|
474
|
+
});
|
|
475
|
+
buffer.add('item1');
|
|
476
|
+
// 在时间限制之前添加第二个项目,应该立即触发
|
|
477
|
+
buffer.add('item2');
|
|
478
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
479
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1', 'item2']);
|
|
480
|
+
await buffer.destroy();
|
|
481
|
+
});
|
|
482
|
+
it('应该正确处理在处理过程中继续添加数据', () => {
|
|
483
|
+
const processedBatches = [];
|
|
484
|
+
let processingCount = 0;
|
|
485
|
+
const batchProcessorWithCallback = vi.fn().mockImplementation((items) => {
|
|
486
|
+
processingCount++;
|
|
487
|
+
processedBatches.push([...items]);
|
|
488
|
+
// 在第一次处理过程中添加新数据
|
|
489
|
+
if (processingCount === 1) {
|
|
490
|
+
buffer.add('item4');
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
const buffer = new BatchingBuffer({
|
|
494
|
+
batchProcessor: batchProcessorWithCallback,
|
|
495
|
+
bufferSize: 2,
|
|
496
|
+
flushInterval: 1000
|
|
497
|
+
});
|
|
498
|
+
buffer.add('item1', 'item2', 'item3');
|
|
499
|
+
// 验证处理次数
|
|
500
|
+
expect(batchProcessorWithCallback).toHaveBeenCalledTimes(2);
|
|
501
|
+
// 验证第一次处理了前两个项目
|
|
502
|
+
expect(processedBatches[0]).toEqual(['item1', 'item2']);
|
|
503
|
+
// 第二次处理的顺序可能是 ['item4', 'item3'] 或 ['item3', 'item4']
|
|
504
|
+
// 这取决于具体实现,我们验证包含了正确的项目
|
|
505
|
+
expect(processedBatches[1]).toHaveLength(2);
|
|
506
|
+
expect(processedBatches[1]).toContain('item3');
|
|
507
|
+
expect(processedBatches[1]).toContain('item4');
|
|
508
|
+
buffer.destroy();
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/batching-buffer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,YAAY,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BatchingBuffer } from './batching-buffer';
|
package/output/index.d.ts
CHANGED
package/output/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAG1B,cAAc,iBAAiB,CAAA;AAG/B,cAAc,UAAU,CAAA;AAGxB,cAAc,SAAS,CAAA;AAGvB,cAAc,WAAW,CAAA;AAGzB,cAAc,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAG1B,cAAc,iBAAiB,CAAA;AAG/B,cAAc,UAAU,CAAA;AAGxB,cAAc,SAAS,CAAA;AAGvB,cAAc,WAAW,CAAA;AAGzB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAG5B,cAAc,mBAAmB,CAAA;AAGjC,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AAGvB,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA"}
|
package/output/index.js
CHANGED
|
@@ -10,6 +10,9 @@ export * from './error';
|
|
|
10
10
|
export * from './service';
|
|
11
11
|
// === 缓存系统 ===
|
|
12
12
|
export * from './lru-cache';
|
|
13
|
+
export * from './ring-cache';
|
|
14
|
+
// === 数据管理 ===
|
|
15
|
+
export * from './batching-buffer';
|
|
13
16
|
// === 基础工具 ===
|
|
14
17
|
export * from './string';
|
|
15
18
|
export * from './color';
|
|
@@ -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"}
|