@taicode/common-server 1.0.12 → 1.0.13
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/index.d.ts +1 -0
- package/output/index.d.ts.map +1 -1
- package/output/index.js +1 -0
- package/output/redis-queue/index.d.ts +1 -0
- package/output/redis-queue/index.d.ts.map +1 -1
- package/output/redis-queue/redis-batch-consumer.d.ts +4 -9
- package/output/redis-queue/redis-batch-consumer.d.ts.map +1 -1
- package/output/redis-queue/redis-batch-consumer.js +3 -55
- package/output/redis-queue/redis-batch-consumer.test.d.ts +5 -0
- package/output/redis-queue/redis-batch-consumer.test.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-common.d.ts +15 -3
- package/output/redis-queue/redis-queue-common.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-common.js +80 -0
- package/output/redis-queue/redis-queue-common.test.d.ts +17 -0
- package/output/redis-queue/redis-queue-common.test.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-common.test.js +11 -11
- package/output/redis-queue/redis-queue-consumer.d.ts +6 -27
- package/output/redis-queue/redis-queue-consumer.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-consumer.js +23 -187
- package/output/redis-queue/redis-queue-consumer.test.d.ts +5 -0
- package/output/redis-queue/redis-queue-consumer.test.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-provider.d.ts +14 -15
- package/output/redis-queue/redis-queue-provider.d.ts.map +1 -1
- package/output/redis-queue/redis-queue-provider.js +8 -9
- package/output/redis-queue/redis-queue-provider.test.d.ts +5 -0
- package/output/redis-queue/redis-queue-provider.test.d.ts.map +1 -1
- package/output/redis-queue/types.d.ts +47 -9
- package/output/redis-queue/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/output/logger/logger.d.ts +0 -33
- package/output/logger/logger.d.ts.map +0 -1
- package/output/logger/logger.js +0 -65
- package/output/logger/logger.test.d.ts +0 -2
- package/output/logger/logger.test.d.ts.map +0 -1
- package/output/logger/logger.test.js +0 -87
- package/output/redis-queue/batch-consumer.d.ts +0 -107
- package/output/redis-queue/batch-consumer.d.ts.map +0 -1
- package/output/redis-queue/batch-consumer.js +0 -492
- package/output/redis-queue/batch-consumer.test.d.ts +0 -2
- package/output/redis-queue/batch-consumer.test.d.ts.map +0 -1
- package/output/redis-queue/batch-consumer.test.js +0 -216
- package/output/redis-queue/batch-redis-queue.d.ts +0 -136
- package/output/redis-queue/batch-redis-queue.d.ts.map +0 -1
- package/output/redis-queue/batch-redis-queue.js +0 -583
- package/output/redis-queue/batch-redis-queue.test.d.ts +0 -2
- package/output/redis-queue/batch-redis-queue.test.d.ts.map +0 -1
- package/output/redis-queue/batch-redis-queue.test.js +0 -243
- package/output/redis-queue/redis-queue.d.ts +0 -129
- package/output/redis-queue/redis-queue.d.ts.map +0 -1
- package/output/redis-queue/redis-queue.js +0 -557
- package/output/redis-queue/redis-queue.test.d.ts +0 -2
- package/output/redis-queue/redis-queue.test.d.ts.map +0 -1
- package/output/redis-queue/redis-queue.test.js +0 -234
- package/output/redis-queue/registry.d.ts +0 -57
- package/output/redis-queue/registry.d.ts.map +0 -1
- package/output/redis-queue/registry.js +0 -30
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
-
import { catchIt } from '@taicode/common-base';
|
|
3
|
-
import { RedisQueueProvider } from './redis-queue-provider';
|
|
4
|
-
import { RedisBatchConsumer } from './batch-consumer';
|
|
5
|
-
const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379';
|
|
6
|
-
describe('RedisBatchConsumer', () => {
|
|
7
|
-
const providers = [];
|
|
8
|
-
const consumers = [];
|
|
9
|
-
afterEach(async () => {
|
|
10
|
-
for (const consumer of consumers) {
|
|
11
|
-
consumer.disconnect();
|
|
12
|
-
}
|
|
13
|
-
for (const provider of providers) {
|
|
14
|
-
try {
|
|
15
|
-
await provider.clear();
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
18
|
-
// 忽略清理错误
|
|
19
|
-
}
|
|
20
|
-
provider.disconnect();
|
|
21
|
-
}
|
|
22
|
-
providers.length = 0;
|
|
23
|
-
consumers.length = 0;
|
|
24
|
-
});
|
|
25
|
-
const createQueue = (handler, options) => {
|
|
26
|
-
const uniqueKey = `test:batch:${Date.now()}:${Math.random()}`;
|
|
27
|
-
const provider = new RedisQueueProvider({
|
|
28
|
-
redisUrl: REDIS_URL,
|
|
29
|
-
queueKey: uniqueKey,
|
|
30
|
-
});
|
|
31
|
-
const consumer = new RedisBatchConsumer({
|
|
32
|
-
redisUrl: REDIS_URL,
|
|
33
|
-
queueKey: uniqueKey,
|
|
34
|
-
consumerInterval: 100,
|
|
35
|
-
maxRetries: 2,
|
|
36
|
-
batchSize: 10,
|
|
37
|
-
...options,
|
|
38
|
-
handler,
|
|
39
|
-
});
|
|
40
|
-
providers.push(provider);
|
|
41
|
-
consumers.push(consumer);
|
|
42
|
-
return { provider, consumer };
|
|
43
|
-
};
|
|
44
|
-
describe('连接管理', () => {
|
|
45
|
-
it('应该成功连接到 Redis', async () => {
|
|
46
|
-
const { consumer } = createQueue(async () => catchIt(() => { }));
|
|
47
|
-
await consumer.connect();
|
|
48
|
-
const health = await consumer.health();
|
|
49
|
-
expect(health).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
it('连接后应该自动启动消费者', async () => {
|
|
52
|
-
let processed = false;
|
|
53
|
-
const { provider, consumer } = createQueue(async () => {
|
|
54
|
-
processed = true;
|
|
55
|
-
return catchIt(() => { });
|
|
56
|
-
});
|
|
57
|
-
await provider.connect();
|
|
58
|
-
await consumer.connect();
|
|
59
|
-
await provider.enqueue({ test: true });
|
|
60
|
-
await new Promise(resolve => setTimeout(resolve, 300));
|
|
61
|
-
expect(processed).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
describe('批量处理', () => {
|
|
65
|
-
it('应该批量处理任务', async () => {
|
|
66
|
-
let batchSize = 0;
|
|
67
|
-
const { provider, consumer } = createQueue(async (dataList) => {
|
|
68
|
-
batchSize = dataList.length;
|
|
69
|
-
return catchIt(() => { });
|
|
70
|
-
}, { batchSize: 5 });
|
|
71
|
-
await provider.connect();
|
|
72
|
-
await consumer.connect();
|
|
73
|
-
await provider.enqueue([
|
|
74
|
-
{ value: 1 }, { value: 2 }, { value: 3 },
|
|
75
|
-
{ value: 4 }, { value: 5 },
|
|
76
|
-
]);
|
|
77
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
78
|
-
expect(batchSize).toBe(5);
|
|
79
|
-
const stats = await consumer.statistics();
|
|
80
|
-
expect(stats.completed).toBe(5);
|
|
81
|
-
});
|
|
82
|
-
it('应该能处理大批量任务', async () => {
|
|
83
|
-
let totalProcessed = 0;
|
|
84
|
-
const { provider, consumer } = createQueue(async (dataList) => {
|
|
85
|
-
totalProcessed += dataList.length;
|
|
86
|
-
return catchIt(() => { });
|
|
87
|
-
}, { batchSize: 50 });
|
|
88
|
-
await provider.connect();
|
|
89
|
-
await consumer.connect();
|
|
90
|
-
const tasks = Array.from({ length: 200 }, (_, i) => ({ value: i }));
|
|
91
|
-
await provider.enqueue(tasks);
|
|
92
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
93
|
-
expect(totalProcessed).toBe(200);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
describe('重试机制', () => {
|
|
97
|
-
it('失败的批次应该重试', async () => {
|
|
98
|
-
let attemptCount = 0;
|
|
99
|
-
const { provider, consumer } = createQueue(async () => {
|
|
100
|
-
attemptCount++;
|
|
101
|
-
throw new Error('Batch failed');
|
|
102
|
-
}, { batchSize: 2, maxRetries: 1 });
|
|
103
|
-
await provider.connect();
|
|
104
|
-
await consumer.connect();
|
|
105
|
-
await provider.enqueue([{ value: 1 }, { value: 2 }]);
|
|
106
|
-
await new Promise(resolve => setTimeout(resolve, 2500));
|
|
107
|
-
const stats = await consumer.statistics();
|
|
108
|
-
expect(stats.failed).toBe(2);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
describe('幂等性', () => {
|
|
112
|
-
it('应该支持在 data 中指定 id 来实现幂等性', async () => {
|
|
113
|
-
let processCount = 0;
|
|
114
|
-
const { provider, consumer } = createQueue(async (dataList) => {
|
|
115
|
-
processCount += dataList.length;
|
|
116
|
-
return catchIt(() => { });
|
|
117
|
-
}, { batchSize: 10 });
|
|
118
|
-
await provider.connect();
|
|
119
|
-
await consumer.connect();
|
|
120
|
-
await provider.enqueue({ id: 'unique-1', value: 1 });
|
|
121
|
-
await provider.enqueue({ id: 'unique-1', value: 2 });
|
|
122
|
-
await provider.enqueue({ id: 'unique-2', value: 3 });
|
|
123
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
124
|
-
expect(processCount).toBe(2); // 只处理 unique-1 和 unique-2
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
describe('延迟处理', () => {
|
|
128
|
-
it('应该支持延迟处理任务', async () => {
|
|
129
|
-
const startTime = Date.now();
|
|
130
|
-
let processTime = 0;
|
|
131
|
-
const { provider, consumer } = createQueue(async () => {
|
|
132
|
-
processTime = Date.now() - startTime;
|
|
133
|
-
return catchIt(() => { });
|
|
134
|
-
});
|
|
135
|
-
await provider.connect();
|
|
136
|
-
await consumer.connect();
|
|
137
|
-
await provider.enqueue({ test: true });
|
|
138
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
139
|
-
expect(processTime).toBeGreaterThan(0);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
describe('并发安全', () => {
|
|
143
|
-
it('多个消费者实例应该能协同工作', async () => {
|
|
144
|
-
let consumer1Count = 0;
|
|
145
|
-
let consumer2Count = 0;
|
|
146
|
-
const uniqueKey = `test:batch:concurrent:${Date.now()}:${Math.random()}`;
|
|
147
|
-
const provider = new RedisQueueProvider({
|
|
148
|
-
redisUrl: REDIS_URL,
|
|
149
|
-
queueKey: uniqueKey,
|
|
150
|
-
});
|
|
151
|
-
const consumer1 = new RedisBatchConsumer({
|
|
152
|
-
redisUrl: REDIS_URL,
|
|
153
|
-
queueKey: uniqueKey,
|
|
154
|
-
batchSize: 5,
|
|
155
|
-
handler: async (dataList) => {
|
|
156
|
-
consumer1Count += dataList.length;
|
|
157
|
-
return catchIt(() => { });
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
const consumer2 = new RedisBatchConsumer({
|
|
161
|
-
redisUrl: REDIS_URL,
|
|
162
|
-
queueKey: uniqueKey,
|
|
163
|
-
batchSize: 5,
|
|
164
|
-
handler: async (dataList) => {
|
|
165
|
-
consumer2Count += dataList.length;
|
|
166
|
-
return catchIt(() => { });
|
|
167
|
-
},
|
|
168
|
-
});
|
|
169
|
-
providers.push(provider);
|
|
170
|
-
consumers.push(consumer1, consumer2);
|
|
171
|
-
await provider.connect();
|
|
172
|
-
await consumer1.connect();
|
|
173
|
-
await consumer2.connect();
|
|
174
|
-
const tasks = Array.from({ length: 20 }, (_, i) => ({ value: i }));
|
|
175
|
-
await provider.enqueue(tasks);
|
|
176
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
177
|
-
// 两个消费者协同处理,总数应该是 20
|
|
178
|
-
// 但由于并发,可能有一些任务被重复处理或未处理,放宽条件
|
|
179
|
-
expect(consumer1Count + consumer2Count).toBeGreaterThanOrEqual(10);
|
|
180
|
-
expect(consumer1Count + consumer2Count).toBeLessThanOrEqual(20);
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
describe('队列统计', () => {
|
|
184
|
-
it('应该正确返回队列统计信息', async () => {
|
|
185
|
-
const { provider, consumer } = createQueue(async () => {
|
|
186
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
187
|
-
return catchIt(() => { });
|
|
188
|
-
});
|
|
189
|
-
await provider.connect();
|
|
190
|
-
await consumer.connect();
|
|
191
|
-
await provider.enqueue(Array.from({ length: 10 }, (_, i) => ({ value: i })));
|
|
192
|
-
await new Promise(resolve => setTimeout(resolve, 50));
|
|
193
|
-
const stats1 = await consumer.statistics();
|
|
194
|
-
expect(stats1.pending).toBeGreaterThan(0);
|
|
195
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
196
|
-
const stats2 = await consumer.statistics();
|
|
197
|
-
expect(stats2.completed).toBe(10);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
describe('数据边界', () => {
|
|
201
|
-
it('应该能处理较大的数据', async () => {
|
|
202
|
-
let receivedData = [];
|
|
203
|
-
const { provider, consumer } = createQueue(async (dataList) => {
|
|
204
|
-
receivedData = dataList;
|
|
205
|
-
return catchIt(() => { });
|
|
206
|
-
}, { batchSize: 5 });
|
|
207
|
-
await provider.connect();
|
|
208
|
-
await consumer.connect();
|
|
209
|
-
const largeString = 'x'.repeat(10 * 1024); // 10KB
|
|
210
|
-
await provider.enqueue(Array.from({ length: 5 }, () => ({ data: largeString })));
|
|
211
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
212
|
-
expect(receivedData).toHaveLength(5);
|
|
213
|
-
expect(receivedData[0].data).toBe(largeString);
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
});
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import type { BatchTaskQueueConfig, TaskData, Task, QueueStats } from './types';
|
|
2
|
-
/**
|
|
3
|
-
* 批量任务队列类(泛型)
|
|
4
|
-
*
|
|
5
|
-
* 提供基于 Redis 的批量任务队列功能,支持:
|
|
6
|
-
* - 批量处理任务(每次处理多条)
|
|
7
|
-
* - 任务入队和持久化
|
|
8
|
-
* - 自动重试机制
|
|
9
|
-
* - 任务状态追踪
|
|
10
|
-
* - 分布式消费
|
|
11
|
-
*
|
|
12
|
-
* @template T 任务数据类型
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```ts
|
|
16
|
-
* interface EmailTask {
|
|
17
|
-
* to: string
|
|
18
|
-
* }
|
|
19
|
-
*
|
|
20
|
-
* const queue = new BatchRedisQueue<EmailTask>({
|
|
21
|
-
* redisUrl: 'redis://localhost:6379',
|
|
22
|
-
* queueKey: 'email-batch-queue',
|
|
23
|
-
* batchSize: 50,
|
|
24
|
-
* handler: async (dataList) => {
|
|
25
|
-
* await sendEmailsBatch(dataList.map(d => d.to))
|
|
26
|
-
* return catchIt(() => {})
|
|
27
|
-
* }
|
|
28
|
-
* })
|
|
29
|
-
*
|
|
30
|
-
* // 连接 Redis(自动启动消费者)
|
|
31
|
-
* await queue.connect()
|
|
32
|
-
*
|
|
33
|
-
* // 入队任务(自动开始处理)
|
|
34
|
-
* await queue.enqueue([
|
|
35
|
-
* { to: 'user1@example.com' },
|
|
36
|
-
* { to: 'user2@example.com' },
|
|
37
|
-
* ])
|
|
38
|
-
*
|
|
39
|
-
* // 获取队列统计信息(O(1) 时间复杂度)
|
|
40
|
-
* const stats = await queue.statistics()
|
|
41
|
-
* console.log(stats) // { pending: 100, processing: 50, completed: 200, failed: 5 }
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
export declare class BatchRedisQueue<T extends TaskData = TaskData> {
|
|
45
|
-
private consumerRunning;
|
|
46
|
-
private redis;
|
|
47
|
-
private consumerInterval;
|
|
48
|
-
private recoveryInterval;
|
|
49
|
-
private processingTasks;
|
|
50
|
-
private readonly config;
|
|
51
|
-
private readonly handler;
|
|
52
|
-
private readonly failedQueue;
|
|
53
|
-
private readonly pendingQueue;
|
|
54
|
-
private readonly processingQueue;
|
|
55
|
-
private readonly completedQueue;
|
|
56
|
-
constructor(config: BatchTaskQueueConfig<string, T>);
|
|
57
|
-
/**
|
|
58
|
-
* 连接 Redis 并自动启动消费者
|
|
59
|
-
*/
|
|
60
|
-
connect(): Promise<void>;
|
|
61
|
-
/**
|
|
62
|
-
* 断开 Redis 连接并停止消费者
|
|
63
|
-
*/
|
|
64
|
-
disconnect(): void;
|
|
65
|
-
/**
|
|
66
|
-
* 将任务推入队列(支持单个或批量)
|
|
67
|
-
* @param data 任务数据(单个或数组)。可以在 data 中包含 `id` 字段来实现幂等性
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* // 普通任务,自动生成 ID
|
|
71
|
-
* await queue.enqueue({ to: 'user@example.com' })
|
|
72
|
-
*
|
|
73
|
-
* // 幂等任务,手动指定 ID,重复提交会被忽略
|
|
74
|
-
* await queue.enqueue({ id: 'email-123', to: 'user@example.com' })
|
|
75
|
-
* await queue.enqueue({ id: 'email-123', to: 'user@example.com' }) // 会被跳过
|
|
76
|
-
*/
|
|
77
|
-
enqueue(data: T[]): Promise<string[]>;
|
|
78
|
-
enqueue(data: T): Promise<string>;
|
|
79
|
-
/**
|
|
80
|
-
* 获取任务详情
|
|
81
|
-
*/
|
|
82
|
-
getTask(taskId: string): Promise<Task<T> | null>;
|
|
83
|
-
/**
|
|
84
|
-
* 更新任务状态并移动到对应队列(原子操作)
|
|
85
|
-
*/
|
|
86
|
-
private applyStatus;
|
|
87
|
-
/**
|
|
88
|
-
* 批量更新任务状态
|
|
89
|
-
*/
|
|
90
|
-
private applyStatusBatch;
|
|
91
|
-
/**
|
|
92
|
-
* 根据状态获取对应的队列键
|
|
93
|
-
*/
|
|
94
|
-
private getQueueByStatus;
|
|
95
|
-
/**
|
|
96
|
-
* 恢复超时的任务
|
|
97
|
-
* 检查 processing 队列中的任务,将超时的任务重试或标记为失败
|
|
98
|
-
*/
|
|
99
|
-
private recoverStalledTasks;
|
|
100
|
-
/**
|
|
101
|
-
* 批量处理任务
|
|
102
|
-
*/
|
|
103
|
-
private processBatch;
|
|
104
|
-
/**
|
|
105
|
-
* 启动恢复机制(内部方法,自动调用)
|
|
106
|
-
*/
|
|
107
|
-
private startRecovery;
|
|
108
|
-
/**
|
|
109
|
-
* 停止恢复机制(内部方法,自动调用)
|
|
110
|
-
*/
|
|
111
|
-
private stopRecovery;
|
|
112
|
-
/**
|
|
113
|
-
* 启动消费者(内部方法,自动调用)
|
|
114
|
-
*/
|
|
115
|
-
private startConsumer;
|
|
116
|
-
/**
|
|
117
|
-
* 停止消费者(内部方法,自动调用)
|
|
118
|
-
*/
|
|
119
|
-
private stopConsumer;
|
|
120
|
-
/**
|
|
121
|
-
* 获取队列统计信息
|
|
122
|
-
* 返回队列中各种状态任务的详细数量
|
|
123
|
-
*
|
|
124
|
-
* 使用分离队列设计,O(1) 时间复杂度
|
|
125
|
-
*/
|
|
126
|
-
statistics(): Promise<QueueStats>;
|
|
127
|
-
/**
|
|
128
|
-
* 清空所有队列
|
|
129
|
-
*/
|
|
130
|
-
clear(): Promise<void>;
|
|
131
|
-
/**
|
|
132
|
-
* 健康检查
|
|
133
|
-
*/
|
|
134
|
-
health(): Promise<boolean>;
|
|
135
|
-
}
|
|
136
|
-
//# sourceMappingURL=batch-redis-queue.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"batch-redis-queue.d.ts","sourceRoot":"","sources":["../../source/redis-queue/batch-redis-queue.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAU,QAAQ,EAAE,IAAI,EAAoB,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,qBAAa,eAAe,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ;IACxD,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,eAAe,CAAI;IAE3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAQtB;IACD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAG7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;gBAE3B,MAAM,EAAE,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC;IAkDnD;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAY9B;;OAEG;IACH,UAAU,IAAI,IAAI;IAUlB;;;;;;;;;;;OAWG;IACG,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IACrC,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAkEvC;;OAEG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAWtD;;OAEG;YACW,WAAW;IA8BzB;;OAEG;YACW,gBAAgB;IAI9B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;;OAGG;YACW,mBAAmB;IA6EjC;;OAEG;YACW,YAAY;IA8F1B;;OAEG;IACH,OAAO,CAAC,aAAa;IAoBrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAQpB;;OAEG;IACH,OAAO,CAAC,aAAa;IA2ErB;;OAEG;IACH,OAAO,CAAC,YAAY;IASpB;;;;;OAKG;IACG,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IA0BvC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB5B;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;CAMjC"}
|