@taicode/common-base 1.7.0 → 1.7.2
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 +133 -0
- package/output/batching-buffer/batching-buffer.d.ts.map +1 -0
- package/output/batching-buffer/batching-buffer.js +264 -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 +358 -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 +3 -0
- package/output/index.d.ts.map +1 -1
- package/output/index.js +4 -0
- package/output/logger/logger.d.ts +3 -3
- package/output/logger/logger.d.ts.map +1 -1
- package/output/logger/logger.js +2 -3
- package/output/logger/logger.test.js +1 -1
- package/output/ttl-cache/index.d.ts +2 -0
- package/output/ttl-cache/index.d.ts.map +1 -0
- package/output/ttl-cache/index.js +1 -0
- package/output/ttl-cache/ttl-cache.d.ts +148 -0
- package/output/ttl-cache/ttl-cache.d.ts.map +1 -0
- package/output/ttl-cache/ttl-cache.js +313 -0
- package/output/ttl-cache/ttl-cache.test.d.ts +2 -0
- package/output/ttl-cache/ttl-cache.test.d.ts.map +1 -0
- package/output/ttl-cache/ttl-cache.test.js +222 -0
- package/package.json +1 -1
|
@@ -0,0 +1,358 @@
|
|
|
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.addBatch(['item1', 'item2', 'item3', 'item4', 'item5']);
|
|
126
|
+
expect(batchProcessor).toHaveBeenCalledTimes(2);
|
|
127
|
+
expect(batchProcessor).toHaveBeenNthCalledWith(1, ['item1', 'item2', 'item3']);
|
|
128
|
+
expect(batchProcessor).toHaveBeenNthCalledWith(2, ['item4', 'item5']);
|
|
129
|
+
expect(buffer.size).toBe(0);
|
|
130
|
+
});
|
|
131
|
+
it('应该正确处理处理器异常', () => {
|
|
132
|
+
const error = new Error('处理失败');
|
|
133
|
+
batchProcessor.mockImplementationOnce(() => {
|
|
134
|
+
throw error;
|
|
135
|
+
});
|
|
136
|
+
buffer.add('item1');
|
|
137
|
+
buffer.add('item2');
|
|
138
|
+
expect(() => {
|
|
139
|
+
buffer.add('item3');
|
|
140
|
+
}).toThrow('处理失败');
|
|
141
|
+
// 数据应该重新放回缓冲区
|
|
142
|
+
expect(buffer.size).toBe(3);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
describe('单个处理模式', () => {
|
|
146
|
+
let buffer;
|
|
147
|
+
beforeEach(() => {
|
|
148
|
+
buffer = new BatchingBuffer({
|
|
149
|
+
itemProcessor: itemProcessor,
|
|
150
|
+
bufferSize: 3,
|
|
151
|
+
flushInterval: 1000
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
afterEach(async () => {
|
|
155
|
+
await buffer.destroy();
|
|
156
|
+
});
|
|
157
|
+
it('应该在达到缓冲区大小时触发单个处理', () => {
|
|
158
|
+
buffer.add('item1');
|
|
159
|
+
buffer.add('item2');
|
|
160
|
+
expect(itemProcessor).not.toHaveBeenCalled();
|
|
161
|
+
buffer.add('item3');
|
|
162
|
+
expect(itemProcessor).toHaveBeenCalledTimes(3);
|
|
163
|
+
expect(itemProcessor).toHaveBeenNthCalledWith(1, 'item1');
|
|
164
|
+
expect(itemProcessor).toHaveBeenNthCalledWith(2, 'item2');
|
|
165
|
+
expect(itemProcessor).toHaveBeenNthCalledWith(3, 'item3');
|
|
166
|
+
expect(buffer.size).toBe(0);
|
|
167
|
+
});
|
|
168
|
+
it('应该在时间间隔到达时触发单个处理', async () => {
|
|
169
|
+
buffer.add('item1');
|
|
170
|
+
buffer.add('item2');
|
|
171
|
+
expect(itemProcessor).not.toHaveBeenCalled();
|
|
172
|
+
vi.advanceTimersByTime(1000);
|
|
173
|
+
await vi.runAllTimersAsync();
|
|
174
|
+
expect(itemProcessor).toHaveBeenCalledTimes(2);
|
|
175
|
+
expect(itemProcessor).toHaveBeenNthCalledWith(1, 'item1');
|
|
176
|
+
expect(itemProcessor).toHaveBeenNthCalledWith(2, 'item2');
|
|
177
|
+
expect(buffer.size).toBe(0);
|
|
178
|
+
});
|
|
179
|
+
it('应该正确处理单个处理器异常', () => {
|
|
180
|
+
const error = new Error('单个处理失败');
|
|
181
|
+
itemProcessor.mockImplementationOnce(() => {
|
|
182
|
+
throw error;
|
|
183
|
+
});
|
|
184
|
+
buffer.add('item1');
|
|
185
|
+
buffer.add('item2');
|
|
186
|
+
expect(() => {
|
|
187
|
+
buffer.add('item3');
|
|
188
|
+
}).toThrow('单个处理失败');
|
|
189
|
+
// 数据应该重新放回缓冲区
|
|
190
|
+
expect(buffer.size).toBe(3);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
describe('缓冲区状态', () => {
|
|
194
|
+
let buffer;
|
|
195
|
+
beforeEach(() => {
|
|
196
|
+
buffer = new BatchingBuffer({
|
|
197
|
+
batchProcessor: batchProcessor,
|
|
198
|
+
bufferSize: 5,
|
|
199
|
+
flushInterval: 2000
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
afterEach(async () => {
|
|
203
|
+
await buffer.destroy();
|
|
204
|
+
});
|
|
205
|
+
it('应该正确报告缓冲区大小', () => {
|
|
206
|
+
expect(buffer.size).toBe(0);
|
|
207
|
+
expect(buffer.isEmpty).toBe(true);
|
|
208
|
+
buffer.add(1);
|
|
209
|
+
expect(buffer.size).toBe(1);
|
|
210
|
+
expect(buffer.isEmpty).toBe(false);
|
|
211
|
+
buffer.add(2);
|
|
212
|
+
buffer.add(3);
|
|
213
|
+
expect(buffer.size).toBe(3);
|
|
214
|
+
expect(buffer.isEmpty).toBe(false);
|
|
215
|
+
});
|
|
216
|
+
it('应该正确报告距离上次刷新的时间', async () => {
|
|
217
|
+
const startTime = Date.now();
|
|
218
|
+
buffer.add(1);
|
|
219
|
+
vi.advanceTimersByTime(500);
|
|
220
|
+
expect(buffer.timeSinceLastFlush).toBeGreaterThanOrEqual(500);
|
|
221
|
+
await buffer.flush();
|
|
222
|
+
expect(buffer.timeSinceLastFlush).toBeLessThan(100);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
describe('销毁功能', () => {
|
|
226
|
+
let buffer;
|
|
227
|
+
beforeEach(() => {
|
|
228
|
+
buffer = new BatchingBuffer({
|
|
229
|
+
batchProcessor: batchProcessor,
|
|
230
|
+
bufferSize: 5,
|
|
231
|
+
flushInterval: 1000
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
it('应该在销毁时处理剩余数据', async () => {
|
|
235
|
+
buffer.add('item1');
|
|
236
|
+
buffer.add('item2');
|
|
237
|
+
await buffer.destroy();
|
|
238
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
239
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1', 'item2']);
|
|
240
|
+
expect(buffer.destroyed).toBe(true);
|
|
241
|
+
expect(buffer.size).toBe(0);
|
|
242
|
+
});
|
|
243
|
+
it('应该在销毁时丢弃剩余数据(如果指定)', async () => {
|
|
244
|
+
buffer.add('item1');
|
|
245
|
+
buffer.add('item2');
|
|
246
|
+
await buffer.destroy(false);
|
|
247
|
+
expect(batchProcessor).not.toHaveBeenCalled();
|
|
248
|
+
expect(buffer.destroyed).toBe(true);
|
|
249
|
+
expect(buffer.size).toBe(0);
|
|
250
|
+
});
|
|
251
|
+
it('应该在销毁后拒绝添加新数据', async () => {
|
|
252
|
+
await buffer.destroy();
|
|
253
|
+
expect(() => {
|
|
254
|
+
buffer.add('item1');
|
|
255
|
+
}).toThrow('BatchingBuffer 已被销毁,不能添加新数据');
|
|
256
|
+
expect(() => {
|
|
257
|
+
buffer.addBatch(['item1', 'item2']);
|
|
258
|
+
}).toThrow('BatchingBuffer 已被销毁,不能添加新数据');
|
|
259
|
+
});
|
|
260
|
+
it('应该支持重复销毁', async () => {
|
|
261
|
+
await buffer.destroy();
|
|
262
|
+
await buffer.destroy();
|
|
263
|
+
expect(buffer.destroyed).toBe(true);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
describe('自动刷新配置', () => {
|
|
267
|
+
it('应该在 autoFlush=false 时不自动启动定时器', async () => {
|
|
268
|
+
const buffer = new BatchingBuffer({
|
|
269
|
+
batchProcessor: batchProcessor,
|
|
270
|
+
bufferSize: 5,
|
|
271
|
+
flushInterval: 1000,
|
|
272
|
+
autoFlush: false
|
|
273
|
+
});
|
|
274
|
+
buffer.add('item1');
|
|
275
|
+
buffer.add('item2');
|
|
276
|
+
vi.advanceTimersByTime(2000);
|
|
277
|
+
await vi.runAllTimersAsync();
|
|
278
|
+
expect(batchProcessor).not.toHaveBeenCalled();
|
|
279
|
+
expect(buffer.size).toBe(2);
|
|
280
|
+
await buffer.destroy();
|
|
281
|
+
});
|
|
282
|
+
it('应该在 autoFlush=false 时仍支持基于大小的触发', async () => {
|
|
283
|
+
const buffer = new BatchingBuffer({
|
|
284
|
+
batchProcessor: batchProcessor,
|
|
285
|
+
bufferSize: 2,
|
|
286
|
+
flushInterval: 1000,
|
|
287
|
+
autoFlush: false
|
|
288
|
+
});
|
|
289
|
+
buffer.add('item1');
|
|
290
|
+
buffer.add('item2');
|
|
291
|
+
await vi.runAllTimersAsync();
|
|
292
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
293
|
+
expect(batchProcessor).toHaveBeenCalledWith(['item1', 'item2']);
|
|
294
|
+
await buffer.destroy();
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
describe('复杂数据类型', () => {
|
|
298
|
+
let buffer;
|
|
299
|
+
beforeEach(() => {
|
|
300
|
+
buffer = new BatchingBuffer({
|
|
301
|
+
batchProcessor: batchProcessor,
|
|
302
|
+
bufferSize: 2,
|
|
303
|
+
flushInterval: 1000
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
afterEach(async () => {
|
|
307
|
+
await buffer.destroy();
|
|
308
|
+
});
|
|
309
|
+
it('应该正确处理复杂对象', async () => {
|
|
310
|
+
const data1 = { id: 1, name: 'test1' };
|
|
311
|
+
const data2 = { id: 2, name: 'test2' };
|
|
312
|
+
buffer.add(data1);
|
|
313
|
+
buffer.add(data2);
|
|
314
|
+
await vi.runAllTimersAsync();
|
|
315
|
+
expect(batchProcessor).toHaveBeenCalledTimes(1);
|
|
316
|
+
expect(batchProcessor).toHaveBeenCalledWith([data1, data2]);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
describe('异步处理器', () => {
|
|
320
|
+
let asyncBatchProcessor;
|
|
321
|
+
let asyncItemProcessor;
|
|
322
|
+
beforeEach(() => {
|
|
323
|
+
asyncBatchProcessor = vi.fn().mockImplementation(async (items) => {
|
|
324
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
325
|
+
return `处理了 ${items.length} 个项目`;
|
|
326
|
+
});
|
|
327
|
+
asyncItemProcessor = vi.fn().mockImplementation(async (item) => {
|
|
328
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
329
|
+
return `处理了项目: ${item}`;
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
it('应该正确处理异步批量处理器', () => {
|
|
333
|
+
const buffer = new BatchingBuffer({
|
|
334
|
+
batchProcessor: asyncBatchProcessor,
|
|
335
|
+
bufferSize: 2,
|
|
336
|
+
flushInterval: 1000
|
|
337
|
+
});
|
|
338
|
+
buffer.add('item1');
|
|
339
|
+
buffer.add('item2');
|
|
340
|
+
expect(asyncBatchProcessor).toHaveBeenCalledTimes(1);
|
|
341
|
+
expect(asyncBatchProcessor).toHaveBeenCalledWith(['item1', 'item2']);
|
|
342
|
+
buffer.destroy();
|
|
343
|
+
});
|
|
344
|
+
it('应该正确处理异步单个处理器', () => {
|
|
345
|
+
const buffer = new BatchingBuffer({
|
|
346
|
+
itemProcessor: asyncItemProcessor,
|
|
347
|
+
bufferSize: 2,
|
|
348
|
+
flushInterval: 1000
|
|
349
|
+
});
|
|
350
|
+
buffer.add('item1');
|
|
351
|
+
buffer.add('item2');
|
|
352
|
+
expect(asyncItemProcessor).toHaveBeenCalledTimes(2);
|
|
353
|
+
expect(asyncItemProcessor).toHaveBeenNthCalledWith(1, 'item1');
|
|
354
|
+
expect(asyncItemProcessor).toHaveBeenNthCalledWith(2, 'item2');
|
|
355
|
+
buffer.destroy();
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
});
|
|
@@ -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
|
@@ -3,7 +3,10 @@ export * from './event-emitter';
|
|
|
3
3
|
export * from './logger';
|
|
4
4
|
export * from './error';
|
|
5
5
|
export * from './service';
|
|
6
|
+
export * from './ttl-cache';
|
|
6
7
|
export * from './lru-cache';
|
|
8
|
+
export * from './ring-cache';
|
|
9
|
+
export * from './batching-buffer';
|
|
7
10
|
export * from './string';
|
|
8
11
|
export * from './color';
|
|
9
12
|
export * from './object';
|
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,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
|
@@ -9,7 +9,11 @@ export * from './error';
|
|
|
9
9
|
// === 服务管理 ===
|
|
10
10
|
export * from './service';
|
|
11
11
|
// === 缓存系统 ===
|
|
12
|
+
export * from './ttl-cache';
|
|
12
13
|
export * from './lru-cache';
|
|
14
|
+
export * from './ring-cache';
|
|
15
|
+
// === 数据管理 ===
|
|
16
|
+
export * from './batching-buffer';
|
|
13
17
|
// === 基础工具 ===
|
|
14
18
|
export * from './string';
|
|
15
19
|
export * from './color';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { LogFormatter } from './formatter';
|
|
2
1
|
import { LogTransport } from './transport';
|
|
2
|
+
import { LogFormatter } from './formatter';
|
|
3
3
|
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
|
4
4
|
export interface LoggerOptions {
|
|
5
5
|
level?: LogLevel;
|
|
@@ -12,8 +12,8 @@ export interface LoggerOptions {
|
|
|
12
12
|
}
|
|
13
13
|
export declare class Logger {
|
|
14
14
|
private _level;
|
|
15
|
-
private transports;
|
|
16
15
|
private formatter;
|
|
16
|
+
private transports;
|
|
17
17
|
private print?;
|
|
18
18
|
constructor(options?: LoggerOptions);
|
|
19
19
|
private defaultPrint;
|
|
@@ -48,7 +48,7 @@ export declare class Logger {
|
|
|
48
48
|
/**
|
|
49
49
|
* 关闭所有 LogTransports
|
|
50
50
|
*/
|
|
51
|
-
|
|
51
|
+
closeTransports(): Promise<void>;
|
|
52
52
|
/**
|
|
53
53
|
* Pino 兼容的 child 方法
|
|
54
54
|
* 创建一个共享配置的子 logger
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../source/logger/logger.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../source/logger/logger.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAa,MAAM,aAAa,CAAA;AACrD,OAAO,EAAuB,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/D,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,SAAS,CAAc;IAC/B,OAAO,CAAC,UAAU,CAAgB;IAClC,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,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtC;;;OAGG;IACH,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,MAAM;IAkB/E;;OAEG;IACH,IAAI,KAAK,IAAI,QAAQ,CAEpB;IAED,IAAI,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAE3B;CACF;AAED,eAAO,MAAM,MAAM,QAAe,CAAA"}
|
package/output/logger/logger.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
// 基础 Logger 实现,支持简单的日志输出和扩展能力
|
|
2
1
|
import { defaultLogFormatter } from './formatter';
|
|
3
2
|
export class Logger {
|
|
4
3
|
_level;
|
|
5
|
-
transports;
|
|
6
4
|
formatter;
|
|
5
|
+
transports;
|
|
7
6
|
print;
|
|
8
7
|
constructor(options = {}) {
|
|
9
8
|
this._level = options.level || 'info';
|
|
@@ -143,7 +142,7 @@ export class Logger {
|
|
|
143
142
|
/**
|
|
144
143
|
* 关闭所有 LogTransports
|
|
145
144
|
*/
|
|
146
|
-
async
|
|
145
|
+
async closeTransports() {
|
|
147
146
|
const closePromises = this.transports
|
|
148
147
|
.map(transport => transport.close?.())
|
|
149
148
|
.filter(Boolean);
|
|
@@ -229,7 +229,7 @@ describe('Logger Transport 集成', () => {
|
|
|
229
229
|
close: mockClose2
|
|
230
230
|
};
|
|
231
231
|
const logger = new Logger({ transports: [transport1, transport2] });
|
|
232
|
-
await logger.
|
|
232
|
+
await logger.closeTransports();
|
|
233
233
|
expect(mockClose1).toHaveBeenCalled();
|
|
234
234
|
expect(mockClose2).toHaveBeenCalled();
|
|
235
235
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/ttl-cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TtlCache } from './ttl-cache';
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTL 缓存配置选项
|
|
3
|
+
*/
|
|
4
|
+
export interface TtlCacheOptions {
|
|
5
|
+
/** 默认 TTL 时间(毫秒),默认为 5 分钟 */
|
|
6
|
+
defaultTtl?: number;
|
|
7
|
+
/** 清理间隔(毫秒),默认为 1 分钟 */
|
|
8
|
+
cleanupInterval?: number;
|
|
9
|
+
/** 最大缓存条目数量,默认无限制 */
|
|
10
|
+
maxSize?: number;
|
|
11
|
+
/** 是否启用自动清理,默认为 true */
|
|
12
|
+
enableCleanup?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* TTL 缓存统计信息
|
|
16
|
+
*/
|
|
17
|
+
export interface TtlCacheStats {
|
|
18
|
+
/** 当前条目数量 */
|
|
19
|
+
size: number;
|
|
20
|
+
/** 最大条目数量限制 */
|
|
21
|
+
maxSize?: number;
|
|
22
|
+
/** 命中次数 */
|
|
23
|
+
hits: number;
|
|
24
|
+
/** 未命中次数 */
|
|
25
|
+
misses: number;
|
|
26
|
+
/** 过期次数 */
|
|
27
|
+
expiredCount: number;
|
|
28
|
+
/** 命中率 */
|
|
29
|
+
hitRate: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* TTL(Time To Live)缓存实现
|
|
33
|
+
* 支持过期时间、自动清理、统计信息等功能
|
|
34
|
+
*
|
|
35
|
+
* 特点:
|
|
36
|
+
* - 支持为每个条目设置独立的 TTL
|
|
37
|
+
* - 自动定期清理过期条目
|
|
38
|
+
* - 支持最大容量限制
|
|
39
|
+
* - 提供详细的统计信息
|
|
40
|
+
* - 优雅的资源清理机制
|
|
41
|
+
*/
|
|
42
|
+
export declare class TtlCache<T> {
|
|
43
|
+
private cache;
|
|
44
|
+
private cleanupTimer?;
|
|
45
|
+
private readonly options;
|
|
46
|
+
private stats;
|
|
47
|
+
constructor(options?: TtlCacheOptions);
|
|
48
|
+
/**
|
|
49
|
+
* 设置缓存项
|
|
50
|
+
* @param key 缓存键
|
|
51
|
+
* @param value 缓存值
|
|
52
|
+
* @param ttl 过期时间(毫秒),默认使用构造函数中的值
|
|
53
|
+
*/
|
|
54
|
+
set(key: string, value: T, ttl?: number): void;
|
|
55
|
+
/**
|
|
56
|
+
* 获取缓存项
|
|
57
|
+
* @param key 缓存键
|
|
58
|
+
* @returns 缓存值或 undefined
|
|
59
|
+
*/
|
|
60
|
+
get(key: string): T | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* 检查缓存项是否存在且未过期
|
|
63
|
+
* @param key 缓存键
|
|
64
|
+
*/
|
|
65
|
+
has(key: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* 删除缓存项
|
|
68
|
+
* @param key 缓存键
|
|
69
|
+
* @returns 是否成功删除
|
|
70
|
+
*/
|
|
71
|
+
delete(key: string): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* 清空所有缓存
|
|
74
|
+
*/
|
|
75
|
+
clear(): void;
|
|
76
|
+
/**
|
|
77
|
+
* 获取当前缓存大小
|
|
78
|
+
*/
|
|
79
|
+
get size(): number;
|
|
80
|
+
/**
|
|
81
|
+
* 获取缓存容量
|
|
82
|
+
*/
|
|
83
|
+
get capacity(): number;
|
|
84
|
+
/**
|
|
85
|
+
* 获取所有未过期的键
|
|
86
|
+
*/
|
|
87
|
+
keys(): string[];
|
|
88
|
+
/**
|
|
89
|
+
* 获取所有未过期的值
|
|
90
|
+
*/
|
|
91
|
+
values(): T[];
|
|
92
|
+
/**
|
|
93
|
+
* 获取所有未过期的键值对
|
|
94
|
+
*/
|
|
95
|
+
entries(): Array<[string, T]>;
|
|
96
|
+
/**
|
|
97
|
+
* 获取或设置缓存(如果不存在)
|
|
98
|
+
* @param key 缓存键
|
|
99
|
+
* @param factory 工厂函数,用于生成缓存值
|
|
100
|
+
* @param ttl 过期时间(毫秒)
|
|
101
|
+
*/
|
|
102
|
+
getOrSet(key: string, factory: () => Promise<T>, ttl?: number): Promise<T>;
|
|
103
|
+
getOrSet(key: string, factory: () => T, ttl?: number): T;
|
|
104
|
+
/**
|
|
105
|
+
* 获取缓存统计信息
|
|
106
|
+
*/
|
|
107
|
+
getStats(): TtlCacheStats;
|
|
108
|
+
/**
|
|
109
|
+
* 重置统计信息
|
|
110
|
+
*/
|
|
111
|
+
resetStats(): void;
|
|
112
|
+
/**
|
|
113
|
+
* 手动清理过期缓存
|
|
114
|
+
* @returns 清理的条目数量
|
|
115
|
+
*/
|
|
116
|
+
cleanup(): number;
|
|
117
|
+
/**
|
|
118
|
+
* 获取指定键的剩余 TTL 时间
|
|
119
|
+
* @param key 缓存键
|
|
120
|
+
* @returns 剩余时间(毫秒),如果不存在或已过期返回 -1
|
|
121
|
+
*/
|
|
122
|
+
getTtl(key: string): number;
|
|
123
|
+
/**
|
|
124
|
+
* 销毁缓存实例,清理所有资源
|
|
125
|
+
*/
|
|
126
|
+
destroy(): void;
|
|
127
|
+
/**
|
|
128
|
+
* 启动定期清理过期缓存
|
|
129
|
+
*/
|
|
130
|
+
private startCleanup;
|
|
131
|
+
/**
|
|
132
|
+
* 停止定期清理
|
|
133
|
+
*/
|
|
134
|
+
private stopCleanup;
|
|
135
|
+
/**
|
|
136
|
+
* 驱逐最旧的缓存条目
|
|
137
|
+
*/
|
|
138
|
+
private evictOldest;
|
|
139
|
+
/**
|
|
140
|
+
* 更新统计信息
|
|
141
|
+
*/
|
|
142
|
+
private updateStats;
|
|
143
|
+
/**
|
|
144
|
+
* 更新命中率
|
|
145
|
+
*/
|
|
146
|
+
private updateHitRate;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=ttl-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ttl-cache.d.ts","sourceRoot":"","sources":["../../source/ttl-cache/ttl-cache.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wBAAwB;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wBAAwB;IACxB,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,aAAa;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,eAAe;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,WAAW;IACX,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU;IACV,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,QAAQ,CAAC,CAAC;IACrB,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,YAAY,CAAC,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,KAAK,CAAe;gBAEhB,OAAO,GAAE,eAAoB;IAiCzC;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IA0B9C;;;;OAIG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAuB/B;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAiBzB;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAQ5B;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED;;OAEG;IACH,IAAI,IAAI,MAAM,EAAE;IAahB;;OAEG;IACH,MAAM,IAAI,CAAC,EAAE;IAab;;OAEG;IACH,OAAO,IAAI,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAa7B;;;;;OAKG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAChF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC;IAoBxD;;OAEG;IACH,QAAQ,IAAI,aAAa;IAKzB;;OAEG;IACH,UAAU,IAAI,IAAI;IAWlB;;;OAGG;IACH,OAAO,IAAI,MAAM;IAmBjB;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAc3B;;OAEG;IACH,OAAO,IAAI,IAAI;IAKf;;OAEG;IACH,OAAO,CAAC,YAAY;IAUpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAOnB;;OAEG;IACH,OAAO,CAAC,WAAW;IAgBnB;;OAEG;IACH,OAAO,CAAC,WAAW;IAKnB;;OAEG;IACH,OAAO,CAAC,aAAa;CAItB"}
|