@be-link/http 1.0.1-beta.0

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/README.md ADDED
@@ -0,0 +1,425 @@
1
+ # @be-link/http
2
+
3
+ 共比邻 HTTP 请求库,提供简洁的 HTTP 请求封装,支持时间同步和 Token 加密。
4
+
5
+ ## 特性
6
+
7
+ - 单例模式,全局统一配置
8
+ - 服务器时间同步(解决客户端时间不准问题)
9
+ - Token AES 加密(防重放攻击)
10
+ - 请求/响应拦截器
11
+ - TypeScript 类型支持
12
+ - 所有参数由应用方传递,灵活配置
13
+ - 自定义 Header 名称(Token、UserId)
14
+ - 并发请求时自动合并时间同步(防止重复请求)
15
+ - 使用 `performance.now()` 防止客户端系统时间篡改
16
+
17
+ ## 安装
18
+
19
+ ```bash
20
+ pnpm add @be-link/http axios crypto-js
21
+ ```
22
+
23
+ ### 依赖说明
24
+
25
+ | 依赖 | 是否必须 | 说明 |
26
+ | ----------- | -------- | ----------------- |
27
+ | `axios` | ✅ 必须 | HTTP 客户端 |
28
+ | `crypto-js` | ✅ 必须 | Token 加密(AES) |
29
+
30
+ ## 快速开始
31
+
32
+ ### 基本用法
33
+
34
+ ```typescript
35
+ import { beLinkHttp } from '@be-link/http';
36
+
37
+ // 1. 初始化(应用启动时调用一次)
38
+ beLinkHttp.init({
39
+ baseURL: 'https://api.example.com',
40
+ timeout: 30000,
41
+ });
42
+
43
+ // 2. 发起请求
44
+ const users = await beLinkHttp.get('/api/users');
45
+ const result = await beLinkHttp.post('/api/users', { name: '张三' });
46
+ ```
47
+
48
+ ### 完整配置
49
+
50
+ ```typescript
51
+ import { beLinkHttp } from '@be-link/http';
52
+
53
+ beLinkHttp.init({
54
+ // 基础配置
55
+ baseURL: 'https://api.example.com',
56
+ timeout: 30000,
57
+ headers: {
58
+ 'X-Custom-Header': 'value',
59
+ },
60
+
61
+ // 时间同步配置
62
+ timeSync: {
63
+ enabled: true,
64
+ syncUrl: 'https://api.example.com/api/time',
65
+ syncGapTime: 50000, // 同步间隔阈值(毫秒)
66
+ storagePrefix: 'myapp', // 存储 key 前缀
67
+ },
68
+
69
+ // 加密配置
70
+ encryption: {
71
+ enabled: true,
72
+ key: 'your-aes-key-16ch', // 16 位 AES 密钥
73
+ iv: 'your-aes-iv-16ch', // 16 位 AES IV
74
+ },
75
+
76
+ // Token 获取函数
77
+ getToken: () => localStorage.getItem('token'),
78
+
79
+ // 用户 ID 获取函数
80
+ getUserId: () => localStorage.getItem('userId'),
81
+
82
+ // 自定义 Header 名称(可选)
83
+ tokenHeaderName: 'X-BeLink-Token', // 默认值
84
+ userIdHeaderName: 'X-BeLink-UserId', // 默认值
85
+
86
+ // 自定义请求拦截器
87
+ onRequest: (config) => {
88
+ console.log('请求发出:', config.url);
89
+ return config;
90
+ },
91
+
92
+ // 自定义响应拦截器
93
+ onResponse: (response) => {
94
+ console.log('响应收到:', response);
95
+ return response;
96
+ },
97
+
98
+ // 自定义错误处理
99
+ onError: (error) => {
100
+ if (error.response?.status === 401) {
101
+ window.location.href = '/login';
102
+ }
103
+ throw error;
104
+ },
105
+ });
106
+ ```
107
+
108
+ ## API 文档
109
+
110
+ ### beLinkHttp
111
+
112
+ 单例请求客户端,提供以下方法:
113
+
114
+ #### init(options)
115
+
116
+ 初始化请求客户端,必须在使用其他方法之前调用。
117
+
118
+ ```typescript
119
+ beLinkHttp.init({
120
+ baseURL: 'https://api.example.com',
121
+ // ... 其他配置
122
+ });
123
+ ```
124
+
125
+ #### HTTP 方法
126
+
127
+ ```typescript
128
+ // GET 请求
129
+ beLinkHttp.get<T>(url: string, config?: AxiosRequestConfig): Promise<T>
130
+
131
+ // POST 请求
132
+ beLinkHttp.post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
133
+
134
+ // PUT 请求
135
+ beLinkHttp.put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
136
+
137
+ // PATCH 请求
138
+ beLinkHttp.patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>
139
+
140
+ // DELETE 请求
141
+ beLinkHttp.delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>
142
+
143
+ // 通用请求
144
+ beLinkHttp.request<T>(config: AxiosRequestConfig): Promise<T>
145
+ ```
146
+
147
+ #### 其他方法
148
+
149
+ ```typescript
150
+ // 获取时间同步服务实例
151
+ beLinkHttp.getTimeSyncService(): TimeSyncService | null
152
+
153
+ // 获取加密服务实例
154
+ beLinkHttp.getEncryptionService(): EncryptionService | null
155
+
156
+ // 获取 Axios 实例
157
+ beLinkHttp.getAxiosInstance(): AxiosInstance | null
158
+
159
+ // 重置客户端(清除配置,需重新 init)
160
+ beLinkHttp.reset(): void
161
+ ```
162
+
163
+ ### 配置选项 RequestOptions
164
+
165
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
166
+ | ---------------- | ----------------------------------- | :--: | --------------- | ------------------ |
167
+ | baseURL | `string` | ✅ | - | API 基础地址 |
168
+ | timeout | `number` | ❌ | 30000 | 请求超时时间(ms) |
169
+ | headers | `Record<string, string>` | ❌ | {} | 默认请求头 |
170
+ | timeSync | `TimeSyncConfig` | ❌ | - | 时间同步配置 |
171
+ | encryption | `EncryptionConfig` | ❌ | - | 加密配置 |
172
+ | getToken | `() => string \| null \| undefined` | ❌ | - | Token 获取函数 |
173
+ | getUserId | `() => string \| null \| undefined` | ❌ | - | 用户 ID 获取函数 |
174
+ | tokenHeaderName | `string` | ❌ | X-BeLink-Token | Token Header 名称 |
175
+ | userIdHeaderName | `string` | ❌ | X-BeLink-UserId | UserId Header 名称 |
176
+ | onRequest | `(config) => config` | ❌ | - | 自定义请求拦截器 |
177
+ | onResponse | `(response) => response` | ❌ | - | 自定义响应拦截器 |
178
+ | onError | `(error) => any` | ❌ | - | 自定义错误处理 |
179
+
180
+ ### TimeSyncConfig
181
+
182
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
183
+ | ------------- | --------- | :--: | ------ | --------------------- |
184
+ | enabled | `boolean` | ❌ | true | 是否启用时间同步 |
185
+ | syncUrl | `string` | ✅ | - | 时间同步接口 URL |
186
+ | syncGapTime | `number` | ❌ | 50000 | 同步间隔阈值(毫秒) |
187
+ | storagePrefix | `string` | ❌ | - | localStorage key 前缀 |
188
+
189
+ ### EncryptionConfig
190
+
191
+ | 参数 | 类型 | 必填 | 默认值 | 说明 |
192
+ | ------- | --------- | :--: | ------ | -------------- |
193
+ | enabled | `boolean` | ❌ | true | 是否启用加密 |
194
+ | key | `string` | ✅ | - | 16 位 AES 密钥 |
195
+ | iv | `string` | ✅ | - | 16 位 AES IV |
196
+
197
+ ## 使用示例
198
+
199
+ ### 基础请求
200
+
201
+ ```typescript
202
+ import { beLinkHttp } from '@be-link/http';
203
+
204
+ // 初始化
205
+ beLinkHttp.init({
206
+ baseURL: 'https://api.example.com',
207
+ });
208
+
209
+ // GET 请求
210
+ const users = await beLinkHttp.get('/api/users');
211
+
212
+ // 带参数的 GET 请求
213
+ const user = await beLinkHttp.get('/api/users/1', {
214
+ params: { include: 'profile' },
215
+ });
216
+
217
+ // POST 请求
218
+ const newUser = await beLinkHttp.post('/api/users', {
219
+ name: '张三',
220
+ age: 25,
221
+ });
222
+
223
+ // PUT 请求
224
+ const updated = await beLinkHttp.put('/api/users/1', {
225
+ name: '李四',
226
+ });
227
+
228
+ // DELETE 请求
229
+ await beLinkHttp.delete('/api/users/1');
230
+ ```
231
+
232
+ ### 带类型的请求
233
+
234
+ ```typescript
235
+ interface User {
236
+ id: number;
237
+ name: string;
238
+ age: number;
239
+ }
240
+
241
+ interface ApiResult<T> {
242
+ success: boolean;
243
+ data: T;
244
+ message?: string;
245
+ }
246
+
247
+ // 指定响应类型
248
+ const result = await beLinkHttp.get<ApiResult<User[]>>('/api/users');
249
+ console.log(result.data); // User[]
250
+
251
+ const user = await beLinkHttp.post<ApiResult<User>>('/api/users', {
252
+ name: '张三',
253
+ age: 25,
254
+ });
255
+ console.log(user.data); // User
256
+ ```
257
+
258
+ ### 错误处理
259
+
260
+ ```typescript
261
+ beLinkHttp.init({
262
+ baseURL: 'https://api.example.com',
263
+ onError: (error) => {
264
+ // 处理 401 未授权
265
+ if (error.response?.status === 401) {
266
+ localStorage.removeItem('token');
267
+ window.location.href = '/login';
268
+ return;
269
+ }
270
+
271
+ // 处理业务错误
272
+ if (error.data?.message) {
273
+ alert(error.data.message);
274
+ }
275
+
276
+ throw error;
277
+ },
278
+ });
279
+ ```
280
+
281
+ ### 多实例场景
282
+
283
+ 如果需要创建多个独立的请求实例,可以使用 `BeLinkHttp` 类:
284
+
285
+ ```typescript
286
+ import { BeLinkHttp } from '@be-link/http';
287
+
288
+ // 创建实例 A
289
+ const serviceA = new BeLinkHttp();
290
+ serviceA.init({
291
+ baseURL: 'https://api-a.example.com',
292
+ });
293
+
294
+ // 创建实例 B
295
+ const serviceB = new BeLinkHttp();
296
+ serviceB.init({
297
+ baseURL: 'https://api-b.example.com',
298
+ });
299
+
300
+ // 分别使用
301
+ const dataA = await serviceA.get('/api/data');
302
+ const dataB = await serviceB.get('/api/data');
303
+ ```
304
+
305
+ ## 工作流程
306
+
307
+ ### Token 加密流程
308
+
309
+ ```
310
+ 请求发起时:
311
+ ┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
312
+ │ getToken() │────>│ EncryptionService│────>│ 请求头 │
313
+ │ 获取 Token │ │ AES(token|+|time)│ │ Authorization │
314
+ └─────────────┘ └──────────────────┘ └─────────────────┘
315
+
316
+
317
+ TimeSyncService
318
+ (服务器时间校正)
319
+ ```
320
+
321
+ ### 时间同步流程
322
+
323
+ ```
324
+ 1. 请求发起前检查本地时间是否需要同步
325
+ 2. 如果超过同步间隔阈值(默认 50 秒),发起同步请求
326
+ 3. 将服务器时间和客户端时间存储到 localStorage
327
+ 4. 后续加密时使用校正后的时间戳
328
+ ```
329
+
330
+ #### 并发控制
331
+
332
+ 当多个请求同时发起时,时间同步使用 Promise 锁机制,确保只发起一次同步请求:
333
+
334
+ ```
335
+ 请求1 ──┬── ensureSync() ──> 发起同步 ──> 等待完成 ──> 继续请求
336
+ 请求2 ──┤ ↓
337
+ 请求3 ──┼── ensureSync() ──> 检测到锁 ──> 等待同一个 Promise ──> 继续请求
338
+ 请求4 ──┤
339
+ 请求5 ──┘
340
+ ```
341
+
342
+ #### 防止系统时间篡改
343
+
344
+ 使用 `performance.now()` 替代 `Date.now()` 计算时间流逝:
345
+
346
+ - `performance.now()` 返回页面加载后的高精度时间,不受系统时间修改影响
347
+ - 即使用户修改系统时间,Token 加密的时间戳仍然准确
348
+
349
+ ## 类型导出
350
+
351
+ ```typescript
352
+ // 单例实例
353
+ import { beLinkHttp } from '@be-link/http';
354
+
355
+ // 类(用于多实例)
356
+ import { BeLinkHttp } from '@be-link/http';
357
+
358
+ // 服务类
359
+ import { TimeSyncService, EncryptionService } from '@be-link/http';
360
+
361
+ // 配置类型
362
+ import type { RequestOptions, TimeSyncConfig, EncryptionConfig, ApiResponse } from '@be-link/http';
363
+ ```
364
+
365
+ ## 完整示例
366
+
367
+ ```typescript
368
+ import { beLinkHttp } from '@be-link/http';
369
+
370
+ // 应用入口初始化
371
+ beLinkHttp.init({
372
+ baseURL: import.meta.env.VITE_API_URL,
373
+ timeout: 30000,
374
+
375
+ // 时间同步
376
+ timeSync: {
377
+ syncUrl: `${import.meta.env.VITE_API_URL}/api/time`,
378
+ },
379
+
380
+ // Token 加密
381
+ encryption: {
382
+ key: import.meta.env.VITE_ENCRYPTION_KEY,
383
+ iv: import.meta.env.VITE_ENCRYPTION_IV,
384
+ },
385
+
386
+ // Token 获取
387
+ getToken: () => localStorage.getItem('token'),
388
+
389
+ // 用户 ID
390
+ getUserId: () => {
391
+ const user = JSON.parse(localStorage.getItem('user') || '{}');
392
+ return user.id;
393
+ },
394
+
395
+ // 错误处理
396
+ onError: (error) => {
397
+ if (error.response?.status === 401) {
398
+ localStorage.removeItem('token');
399
+ window.location.href = '/login';
400
+ }
401
+ throw error;
402
+ },
403
+ });
404
+
405
+ // API 调用
406
+ async function getUserInfo(userId: string) {
407
+ return beLinkHttp.get(`/api/users/${userId}`);
408
+ }
409
+
410
+ async function createUser(data: { name: string; age: number }) {
411
+ return beLinkHttp.post('/api/users', data);
412
+ }
413
+
414
+ async function updateUser(userId: string, data: { name?: string; age?: number }) {
415
+ return beLinkHttp.put(`/api/users/${userId}`, data);
416
+ }
417
+
418
+ async function deleteUser(userId: string) {
419
+ return beLinkHttp.delete(`/api/users/${userId}`);
420
+ }
421
+ ```
422
+
423
+ ## License
424
+
425
+ ISC
@@ -0,0 +1,206 @@
1
+ /**
2
+ * BeLinkHttp 请求客户端
3
+ *
4
+ * 单例模式的 HTTP 请求客户端
5
+ * 支持时间同步和 Token 加密
6
+ *
7
+ * @module BeLinkHttp
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { beLinkHttp } from '@be-link/http';
12
+ *
13
+ * // 初始化
14
+ * beLinkHttp.init({
15
+ * baseURL: 'https://api.example.com',
16
+ * timeSync: { syncUrl: 'https://api.example.com/api/time' },
17
+ * encryption: { key: '...', iv: '...' },
18
+ * getToken: () => localStorage.getItem('token'),
19
+ * });
20
+ *
21
+ * // 发起请求
22
+ * const data = await beLinkHttp.get('/api/users');
23
+ * ```
24
+ */
25
+ import { AxiosInstance, AxiosRequestConfig } from 'axios';
26
+ import type { RequestOptions } from './types';
27
+ import { TimeSyncService, EncryptionService } from './services';
28
+ /**
29
+ * BeLinkHttp 单例类
30
+ *
31
+ * 提供统一的 HTTP 请求接口,支持:
32
+ * - 时间同步
33
+ * - Token 加密
34
+ * - 请求/响应拦截
35
+ */
36
+ declare class BeLinkHttp {
37
+ /** Axios 实例 */
38
+ private instance;
39
+ /** 时间同步服务 */
40
+ private timeSyncService;
41
+ /** 加密服务 */
42
+ private encryptionService;
43
+ /** 是否已初始化 */
44
+ private initialized;
45
+ /**
46
+ * 初始化请求客户端
47
+ *
48
+ * 必须在使用其他方法之前调用此方法进行初始化
49
+ *
50
+ * @param options - 请求配置选项
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * beLinkHttp.init({
55
+ * baseURL: 'https://api.example.com',
56
+ * timeout: 30000,
57
+ * timeSync: {
58
+ * syncUrl: 'https://api.example.com/api/time',
59
+ * },
60
+ * encryption: {
61
+ * key: 'your-aes-key-16ch',
62
+ * iv: 'your-aes-iv-16ch',
63
+ * },
64
+ * getToken: () => localStorage.getItem('token'),
65
+ * getUserId: () => localStorage.getItem('userId'),
66
+ * });
67
+ * ```
68
+ */
69
+ init(options: RequestOptions): void;
70
+ /**
71
+ * 检查是否已初始化
72
+ */
73
+ private checkInitialized;
74
+ /**
75
+ * GET 请求
76
+ *
77
+ * @param url - 请求 URL
78
+ * @param config - 可选的 Axios 请求配置
79
+ * @returns Promise 响应数据
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * const users = await beLinkHttp.get('/api/users');
84
+ * const user = await beLinkHttp.get('/api/users/1', { params: { include: 'profile' } });
85
+ * ```
86
+ */
87
+ get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
88
+ /**
89
+ * POST 请求
90
+ *
91
+ * @param url - 请求 URL
92
+ * @param data - 请求体数据
93
+ * @param config - 可选的 Axios 请求配置
94
+ * @returns Promise 响应数据
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * const result = await beLinkHttp.post('/api/users', { name: '张三', age: 25 });
99
+ * ```
100
+ */
101
+ post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
102
+ /**
103
+ * PUT 请求
104
+ *
105
+ * @param url - 请求 URL
106
+ * @param data - 请求体数据
107
+ * @param config - 可选的 Axios 请求配置
108
+ * @returns Promise 响应数据
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * const result = await beLinkHttp.put('/api/users/1', { name: '李四' });
113
+ * ```
114
+ */
115
+ put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
116
+ /**
117
+ * PATCH 请求
118
+ *
119
+ * @param url - 请求 URL
120
+ * @param data - 请求体数据
121
+ * @param config - 可选的 Axios 请求配置
122
+ * @returns Promise 响应数据
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * const result = await beLinkHttp.patch('/api/users/1', { age: 26 });
127
+ * ```
128
+ */
129
+ patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
130
+ /**
131
+ * DELETE 请求
132
+ *
133
+ * @param url - 请求 URL
134
+ * @param config - 可选的 Axios 请求配置
135
+ * @returns Promise 响应数据
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * await beLinkHttp.delete('/api/users/1');
140
+ * ```
141
+ */
142
+ delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
143
+ /**
144
+ * 通用请求方法
145
+ *
146
+ * @param config - Axios 请求配置
147
+ * @returns Promise 响应数据
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * const result = await beLinkHttp.request({
152
+ * method: 'post',
153
+ * url: '/api/users',
154
+ * data: { name: '张三' },
155
+ * headers: { 'X-Custom': 'value' },
156
+ * });
157
+ * ```
158
+ */
159
+ request<T = any>(config: AxiosRequestConfig): Promise<T>;
160
+ /**
161
+ * 获取时间同步服务实例
162
+ *
163
+ * @returns 时间同步服务实例,未初始化时返回 null
164
+ */
165
+ getTimeSyncService(): TimeSyncService | null;
166
+ /**
167
+ * 获取加密服务实例
168
+ *
169
+ * @returns 加密服务实例,未初始化时返回 null
170
+ */
171
+ getEncryptionService(): EncryptionService | null;
172
+ /**
173
+ * 获取 Axios 实例
174
+ *
175
+ * @returns Axios 实例,未初始化时返回 null
176
+ */
177
+ getAxiosInstance(): AxiosInstance | null;
178
+ /**
179
+ * 重置客户端
180
+ *
181
+ * 清除所有配置和实例,需要重新调用 init() 初始化
182
+ */
183
+ reset(): void;
184
+ }
185
+ /**
186
+ * BeLinkHttp 单例实例
187
+ *
188
+ * @example
189
+ * ```ts
190
+ * import { beLinkHttp } from '@be-link/http';
191
+ *
192
+ * // 初始化
193
+ * beLinkHttp.init({
194
+ * baseURL: 'https://api.example.com',
195
+ * });
196
+ *
197
+ * // 使用
198
+ * const data = await beLinkHttp.get('/api/users');
199
+ * ```
200
+ */
201
+ export declare const beLinkHttp: BeLinkHttp;
202
+ /**
203
+ * 导出类,用于创建多个实例(如需要)
204
+ */
205
+ export { BeLinkHttp };
206
+ //# sourceMappingURL=BeLinkHttp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BeLinkHttp.d.ts","sourceRoot":"","sources":["../src/BeLinkHttp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAc,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAEjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGhE;;;;;;;GAOG;AACH,cAAM,UAAU;IACd,eAAe;IACf,OAAO,CAAC,QAAQ,CAA8B;IAE9C,aAAa;IACb,OAAO,CAAC,eAAe,CAAgC;IAEvD,WAAW;IACX,OAAO,CAAC,iBAAiB,CAAkC;IAE3D,aAAa;IACb,OAAO,CAAC,WAAW,CAAS;IAE5B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACI,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAmC1C;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;;;;;;;;;;;OAYG;IACU,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAK/E;;;;;;;;;;;;OAYG;IACU,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAK5F;;;;;;;;;;;;OAYG;IACU,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAK3F;;;;;;;;;;;;OAYG;IACU,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAK7F;;;;;;;;;;;OAWG;IACU,MAAM,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAKlF;;;;;;;;;;;;;;;OAeG;IACU,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC;IAKrE;;;;OAIG;IACI,kBAAkB,IAAI,eAAe,GAAG,IAAI;IAInD;;;;OAIG;IACI,oBAAoB,IAAI,iBAAiB,GAAG,IAAI;IAIvD;;;;OAIG;IACI,gBAAgB,IAAI,aAAa,GAAG,IAAI;IAI/C;;;;OAIG;IACI,KAAK,IAAI,IAAI;CAMrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,UAAU,YAAmB,CAAC;AAE3C;;GAEG;AACH,OAAO,EAAE,UAAU,EAAE,CAAC"}