@groundbrick/repository-base 0.0.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/README.md ADDED
@@ -0,0 +1,546 @@
1
+ # @groundbrick/repository-base
2
+
3
+ ๐Ÿ’พ SQL Repository Foundation for PostgreSQL and MySQL
4
+
5
+
6
+ Generic repository base classes with CRUD operations for the microframework. Provides a simple, type-safe data access layer that works consistently across PostgreSQL and MySQL databases.
7
+
8
+ ## ๐Ÿš€ Features
9
+
10
+ - โœ… **Generic CRUD Operations**: Type-safe create, read, update, delete operations
11
+ - ๐Ÿ” **Database Agnostic**: Works with both PostgreSQL and MySQL through unified interface
12
+ - ๐Ÿง  **Simple Query Methods**: Easy-to-use methods for common database operations
13
+ - ๐Ÿงฉ **Pagination Support**: Built-in pagination with metadata
14
+ - ๐Ÿ›ก **Automatic SQL Adaptation**: Handles PostgreSQL `$1` vs MySQL `?` parameter differences
15
+ - ๐Ÿ” **Logging Integration**: Comprehensive logging with performance tracking
16
+ - ๐Ÿงช **Type Safety**: Full TypeScript support with generic entity types
17
+ - ๐Ÿ“ฆ **No ORM Complexity**: Simple, lightweight approach focused on SQL
18
+
19
+ ## ๐Ÿ“ฆ Installation
20
+
21
+ ```bash
22
+ npm install @groundbrick/repository-base
23
+ ```
24
+
25
+ ## โšก Quick Start
26
+
27
+ ### 1. Define Your Entity
28
+
29
+ ```typescript
30
+ import { BaseEntity } from '@groundbrick/repository-base';
31
+
32
+ // Simple entity interface
33
+ interface User extends BaseEntity {
34
+ name: string;
35
+ email: string;
36
+ active: boolean;
37
+ }
38
+ ```
39
+
40
+ ### 2. Create Repository
41
+
42
+ ```typescript
43
+ import { BaseRepository, DatabaseClient } from '@groundbrick/repository-base';
44
+
45
+ class UserRepository extends BaseRepository<User> {
46
+ constructor(db: DatabaseClient) {
47
+ super(db, 'users'); // Pass database client and table name
48
+ }
49
+
50
+ // Add custom methods as needed
51
+ async findByEmail(email: string): Promise<User | null> {
52
+ return this.findOne('email = ?', [email]);
53
+ }
54
+
55
+ async findActiveUsers(): Promise<User[]> {
56
+ return this.findMany('active = ?', [true]);
57
+ }
58
+ }
59
+ ```
60
+
61
+ ### 3. Use Repository
62
+
63
+ ```typescript
64
+ import { DatabaseFactory } from '@groundbrick/db-postgres';
65
+
66
+ // Initialize database
67
+ const db = DatabaseFactory.getInstance({
68
+ host: 'localhost',
69
+ database: 'myapp',
70
+ user: 'user',
71
+ password: 'password'
72
+ });
73
+
74
+ // Create repository
75
+ const userRepository = new UserRepository(db);
76
+
77
+ // Use CRUD operations
78
+ const user = await userRepository.create({
79
+ name: 'John Doe',
80
+ email: 'john@example.com',
81
+ active: true
82
+ });
83
+
84
+ const foundUser = await userRepository.findById(user.id!);
85
+ const allUsers = await userRepository.findAll();
86
+ const activeUsers = await userRepository.findActiveUsers();
87
+ ```
88
+
89
+ ## ๐Ÿ“š API Reference
90
+
91
+ ### BaseRepository<T>
92
+
93
+ Generic base repository class providing CRUD operations for any entity type.
94
+
95
+ #### Constructor
96
+
97
+ ```typescript
98
+ constructor(
99
+ db: DatabaseClient,
100
+ tableName: string,
101
+ logger?: Logger
102
+ )
103
+ ```
104
+
105
+ #### Basic CRUD Methods
106
+
107
+ ##### `findById(id: number): Promise<T | null>`
108
+ Find a single record by ID.
109
+
110
+ ```typescript
111
+ const user = await userRepository.findById(123);
112
+ ```
113
+
114
+ ##### `findOne(condition: string, params?: any[]): Promise<T | null>`
115
+ Find a single record by condition.
116
+
117
+ ```typescript
118
+ const user = await userRepository.findOne('email = ?', ['john@example.com']);
119
+ ```
120
+
121
+ ##### `findMany(condition?: string, params?: any[]): Promise<T[]>`
122
+ Find multiple records by condition.
123
+
124
+ ```typescript
125
+ const activeUsers = await userRepository.findMany('active = ?', [true]);
126
+ const allUsers = await userRepository.findMany(); // No condition = all records
127
+ ```
128
+
129
+ ##### `findAll(options?: QueryOptions): Promise<T[]>`
130
+ Find all records with optional sorting and pagination.
131
+
132
+ ```typescript
133
+ // Simple find all
134
+ const users = await userRepository.findAll();
135
+
136
+ // With pagination
137
+ const users = await userRepository.findAll({
138
+ pagination: { limit: 10, offset: 20 }
139
+ });
140
+
141
+ // With sorting
142
+ const users = await userRepository.findAll({
143
+ sort: { column: 'name', direction: 'ASC' }
144
+ });
145
+
146
+ // Multiple sorts
147
+ const users = await userRepository.findAll({
148
+ sort: [
149
+ { column: 'active', direction: 'DESC' },
150
+ { column: 'name', direction: 'ASC' }
151
+ ]
152
+ });
153
+ ```
154
+
155
+ ##### `create(data: CreateEntityData<T>): Promise<T>`
156
+ Create a new record. Automatically excludes `id`, `created_at`, and `updated_at`.
157
+
158
+ ```typescript
159
+ const user = await userRepository.create({
160
+ name: 'John Doe',
161
+ email: 'john@example.com',
162
+ active: true
163
+ });
164
+ ```
165
+
166
+ ##### `update(id: number, data: UpdateEntityData<T>): Promise<T | null>`
167
+ Update a record by ID. Automatically excludes `id` and `created_at`.
168
+
169
+ ```typescript
170
+ const updated = await userRepository.update(123, {
171
+ name: 'Jane Doe',
172
+ active: false
173
+ });
174
+ ```
175
+
176
+ ##### `delete(id: number): Promise<boolean>`
177
+ Delete a record by ID. Returns `true` if record was deleted, `false` if not found.
178
+
179
+ ```typescript
180
+ const deleted = await userRepository.delete(123);
181
+ ```
182
+
183
+ #### Utility Methods
184
+
185
+ ##### `count(condition?: string, params?: any[]): Promise<number>`
186
+ Count records with optional condition.
187
+
188
+ ```typescript
189
+ const totalUsers = await userRepository.count();
190
+ const activeCount = await userRepository.count('active = ?', [true]);
191
+ ```
192
+
193
+ ##### `exists(id: number): Promise<boolean>`
194
+ Check if a record exists by ID.
195
+
196
+ ```typescript
197
+ const userExists = await userRepository.exists(123);
198
+ ```
199
+
200
+ ##### `findPaginated(options): Promise<PaginatedResult<T>>`
201
+ Find records with pagination and metadata.
202
+
203
+ ```typescript
204
+ const result = await userRepository.findPaginated({
205
+ condition: 'active = ?',
206
+ params: [true],
207
+ pagination: { limit: 10, offset: 0 }
208
+ });
209
+
210
+ console.log(result.data); // Array of records
211
+ console.log(result.total); // Total count
212
+ console.log(result.hasNext); // Boolean
213
+ console.log(result.hasPrevious); // Boolean
214
+ ```
215
+
216
+ ##### `rawQuery<R>(sql: string, params?: any[]): Promise<QueryResult<R>>`
217
+ Execute raw SQL query (use with caution).
218
+
219
+ ```typescript
220
+ const result = await userRepository.rawQuery<User>(
221
+ 'SELECT * FROM users WHERE created_at > ?',
222
+ [new Date('2024-01-01')]
223
+ );
224
+ ```
225
+
226
+ ## ๐Ÿงพ Types
227
+
228
+ ### Entity Types
229
+
230
+ ```typescript
231
+ // Base entity with common fields
232
+ interface BaseEntity {
233
+ id?: number;
234
+ created_at?: Date;
235
+ updated_at?: Date;
236
+ }
237
+
238
+ // Simple entity with just ID
239
+ interface SimpleEntity {
240
+ id?: number;
241
+ }
242
+
243
+ // Helper types for CRUD operations
244
+ type CreateEntityData<T> = Omit<T, 'id' | 'created_at' | 'updated_at'>;
245
+ type UpdateEntityData<T> = Partial<Omit<T, 'id' | 'created_at'>>;
246
+ ```
247
+
248
+ ### Query Types
249
+
250
+ ```typescript
251
+ interface QueryOptions {
252
+ pagination?: {
253
+ limit?: number;
254
+ offset?: number;
255
+ };
256
+ sort?: {
257
+ column: string;
258
+ direction?: 'ASC' | 'DESC';
259
+ } | Array<{
260
+ column: string;
261
+ direction?: 'ASC' | 'DESC';
262
+ }>;
263
+ }
264
+
265
+ interface PaginatedResult<T> {
266
+ data: T[];
267
+ total: number;
268
+ limit: number;
269
+ offset: number;
270
+ hasNext: boolean;
271
+ hasPrevious: boolean;
272
+ }
273
+ ```
274
+
275
+ ## ๐Ÿ›  Database Compatibility
276
+
277
+ This package automatically handles differences between PostgreSQL and MySQL:
278
+
279
+ | Feature | PostgreSQL | MySQL | Repository Behavior |
280
+ |---------|------------|-------|-------------------|
281
+ | Parameters | `$1, $2, $3` | `?, ?, ?` | Automatically converted |
282
+ | RETURNING | Supported | Not supported | Uses RETURNING when available, falls back to SELECT |
283
+ | INSERT ID | RETURNING * | LAST_INSERT_ID() | Handles both approaches |
284
+ | Timestamps | NOW() | NOW() | Consistent across both |
285
+
286
+ ## ๐Ÿง  Custom Repository Examples
287
+
288
+ ### Basic Custom Repository
289
+
290
+ ```typescript
291
+ class ProductRepository extends BaseRepository<Product> {
292
+ constructor(db: DatabaseClient) {
293
+ super(db, 'products');
294
+ }
295
+
296
+ async findByCategory(categoryId: number): Promise<Product[]> {
297
+ return this.findMany('category_id = ?', [categoryId]);
298
+ }
299
+
300
+ async findFeatured(): Promise<Product[]> {
301
+ return this.findMany('featured = ?', [true]);
302
+ }
303
+
304
+ async searchByName(name: string): Promise<Product[]> {
305
+ return this.findMany('name ILIKE ?', [`%${name}%`]);
306
+ }
307
+ }
308
+ ```
309
+
310
+ ### Repository with Complex Queries
311
+
312
+ ```typescript
313
+ class OrderRepository extends BaseRepository<Order> {
314
+ constructor(db: DatabaseClient) {
315
+ super(db, 'orders');
316
+ }
317
+
318
+ async findByDateRange(startDate: Date, endDate: Date): Promise<Order[]> {
319
+ return this.findMany(
320
+ 'created_at >= ? AND created_at <= ?',
321
+ [startDate, endDate]
322
+ );
323
+ }
324
+
325
+ async getMonthlyStats(year: number, month: number) {
326
+ const sql = `
327
+ SELECT
328
+ COUNT(*) as total_orders,
329
+ SUM(total_amount) as total_revenue,
330
+ AVG(total_amount) as avg_order_value
331
+ FROM orders
332
+ WHERE EXTRACT(YEAR FROM created_at) = ?
333
+ AND EXTRACT(MONTH FROM created_at) = ?
334
+ `;
335
+
336
+ const result = await this.rawQuery<{
337
+ total_orders: number;
338
+ total_revenue: number;
339
+ avg_order_value: number;
340
+ }>(sql, [year, month]);
341
+
342
+ return result.rows[0];
343
+ }
344
+ }
345
+ ```
346
+
347
+ ### Repository with Soft Deletes
348
+
349
+ ```typescript
350
+ interface SoftDeletableEntity extends BaseEntity {
351
+ deleted_at?: Date;
352
+ }
353
+
354
+ class UserRepository extends BaseRepository<User & SoftDeletableEntity> {
355
+ constructor(db: DatabaseClient) {
356
+ super(db, 'users');
357
+ }
358
+
359
+ // Override findMany to exclude soft-deleted records by default
360
+ async findMany(condition?: string, params: any[] = []): Promise<User[]> {
361
+ const softDeleteCondition = 'deleted_at IS NULL';
362
+
363
+ if (condition) {
364
+ condition = `${condition} AND ${softDeleteCondition}`;
365
+ } else {
366
+ condition = softDeleteCondition;
367
+ }
368
+
369
+ return super.findMany(condition, params);
370
+ }
371
+
372
+ // Soft delete method
373
+ async softDelete(id: number): Promise<boolean> {
374
+ const updated = await this.update(id, {
375
+ deleted_at: new Date()
376
+ } as Partial<User>);
377
+ return updated !== null;
378
+ }
379
+
380
+ // Find including soft-deleted records
381
+ async findAllIncludingDeleted(): Promise<User[]> {
382
+ return super.findMany(); // Bypass the override
383
+ }
384
+ }
385
+ ```
386
+
387
+ ## ๐Ÿงฉ Integration with Service Layer
388
+
389
+ ```typescript
390
+ import { UserRepository } from './repositories/UserRepository';
391
+ import { createLogger } from '@groundbrick/logger';
392
+
393
+ class UserService {
394
+ private logger = createLogger().child('user-service');
395
+
396
+ constructor(private userRepository: UserRepository) {}
397
+
398
+ async createUser(userData: CreateUserRequest): Promise<User> {
399
+ // Validation happens here in service layer
400
+ if (!userData.email || !userData.name) {
401
+ throw new Error('Email and name are required');
402
+ }
403
+
404
+ // Check if user already exists
405
+ const existing = await this.userRepository.findByEmail(userData.email);
406
+ if (existing) {
407
+ throw new Error('User with this email already exists');
408
+ }
409
+
410
+ // Create user (repository handles the database operation)
411
+ const user = await this.userRepository.create({
412
+ name: userData.name,
413
+ email: userData.email,
414
+ active: true
415
+ });
416
+
417
+ this.logger.info('User created successfully', { userId: user.id });
418
+ return user;
419
+ }
420
+
421
+ async getUserById(id: number): Promise<User> {
422
+ const user = await this.userRepository.findById(id);
423
+ if (!user) {
424
+ throw new Error('User not found');
425
+ }
426
+ return user;
427
+ }
428
+ }
429
+ ```
430
+
431
+ ## ๐Ÿ’ก Best Practices
432
+
433
+ ### 1. Keep Repositories Simple
434
+ Repositories should focus on data access, not business logic:
435
+
436
+ ```typescript
437
+ // โœ… Good - Simple data access
438
+ async findActiveUsers(): Promise<User[]> {
439
+ return this.findMany('active = ?', [true]);
440
+ }
441
+
442
+ // โŒ Bad - Business logic in repository
443
+ async createUserWithValidation(userData: any): Promise<User> {
444
+ // Validation should be in service layer
445
+ if (!userData.email) throw new Error('Email required');
446
+ return this.create(userData);
447
+ }
448
+ ```
449
+
450
+ ### 2. Use Type Safety
451
+ Always define proper entity interfaces:
452
+
453
+ ```typescript
454
+ // โœ… Good - Properly typed
455
+ interface User extends BaseEntity {
456
+ name: string;
457
+ email: string;
458
+ active: boolean;
459
+ }
460
+
461
+ // โŒ Bad - Losing type safety
462
+ interface User {
463
+ [key: string]: any;
464
+ }
465
+ ```
466
+
467
+ ### 3. Custom Methods for Domain Logic
468
+ Add repository methods that make sense for your domain:
469
+
470
+ ```typescript
471
+ class UserRepository extends BaseRepository<User> {
472
+ // Domain-specific finder methods
473
+ async findByEmail(email: string): Promise<User | null> {
474
+ return this.findOne('email = ?', [email]);
475
+ }
476
+
477
+ async findActiveUsersInDepartment(departmentId: number): Promise<User[]> {
478
+ return this.findMany(
479
+ 'active = ? AND department_id = ?',
480
+ [true, departmentId]
481
+ );
482
+ }
483
+ }
484
+ ```
485
+
486
+ ### 4. Error Handling
487
+ Let errors bubble up to the service layer:
488
+
489
+ ```typescript
490
+ class UserService {
491
+ async getUser(id: number): Promise<User> {
492
+ try {
493
+ const user = await this.userRepository.findById(id);
494
+ if (!user) {
495
+ throw new Error('User not found');
496
+ }
497
+ return user;
498
+ } catch (error) {
499
+ this.logger.error('Failed to get user', error, { userId: id });
500
+ throw error; // Re-throw for caller to handle
501
+ }
502
+ }
503
+ }
504
+ ```
505
+
506
+ ## โš™๏ธ Performance Tips
507
+
508
+ ### 1. Use Appropriate Queries
509
+ ```typescript
510
+ // โœ… Good - Specific condition
511
+ const activeUsers = await userRepository.findMany('active = ?', [true]);
512
+
513
+ // โŒ Bad - Loading all then filtering
514
+ const allUsers = await userRepository.findAll();
515
+ const activeUsers = allUsers.filter(u => u.active);
516
+ ```
517
+
518
+ ### 2. Pagination for Large Datasets
519
+ ```typescript
520
+ // โœ… Good - Paginated
521
+ const users = await userRepository.findPaginated({
522
+ pagination: { limit: 20, offset: 0 }
523
+ });
524
+
525
+ // โŒ Bad - Loading everything
526
+ const allUsers = await userRepository.findAll();
527
+ ```
528
+
529
+ ### 3. Count Before Fetching
530
+ ```typescript
531
+ // โœ… Good - Check count first
532
+ const userCount = await userRepository.count('active = ?', [true]);
533
+ if (userCount > 1000) {
534
+ // Handle large dataset differently
535
+ }
536
+ ```
537
+
538
+ ## ๐Ÿงฌ Dependencies
539
+
540
+ This package depends on:
541
+ - `@groundbrick/db-core` - Database interfaces and types
542
+ - `@groundbrick/logger` - Logging functionality
543
+
544
+ ## ๐Ÿ“œ License
545
+
546
+ MIT
@@ -0,0 +1,70 @@
1
+ import { DatabaseClient, DatabaseTransaction, QueryResult } from '@groundbrick/db-core';
2
+ import { Logger } from '@groundbrick/logger';
3
+ import { RepositoryEntity, CreateEntityData, UpdateEntityData, QueryOptions, FindManyOptions, PaginatedResult, PaginationOptions } from '../types';
4
+ import { SqlAdapter } from './SqlAdapter.js';
5
+ /**
6
+ * Generic base repository providing CRUD operations for any entity type
7
+ */
8
+ export declare abstract class BaseRepository<T extends RepositoryEntity> {
9
+ protected readonly db: DatabaseClient;
10
+ protected readonly tableName: string;
11
+ protected readonly logger: Logger;
12
+ protected readonly sqlAdapter: SqlAdapter;
13
+ protected readonly dbType: 'postgresql' | 'mysql';
14
+ constructor(db: DatabaseClient, tableName: string, logger?: Logger);
15
+ /**
16
+ * Find a record by ID
17
+ */
18
+ findById(id: number | string): Promise<T | null>;
19
+ /**
20
+ * Find one record by condition
21
+ */
22
+ findOne(condition: string, params?: any[]): Promise<T | null>;
23
+ /**
24
+ * Find multiple records by condition
25
+ */
26
+ findMany(condition?: string, params?: any[]): Promise<T[]>;
27
+ /**
28
+ * Find all records with optional query options
29
+ */
30
+ findAll(options?: QueryOptions): Promise<T[]>;
31
+ /**
32
+ * Create a new record
33
+ */
34
+ create(data: CreateEntityData<T>): Promise<T>;
35
+ /**
36
+ * Update a record by ID
37
+ */
38
+ update(id: number | string, data: UpdateEntityData<T>): Promise<T | null>;
39
+ /**
40
+ * Delete a record by ID
41
+ */
42
+ delete(id: number | string): Promise<boolean>;
43
+ /**
44
+ * Count records with optional condition
45
+ */
46
+ count(condition?: string, params?: any[]): Promise<number>;
47
+ /**
48
+ * Check if a record exists by ID
49
+ */
50
+ exists(id: number | string): Promise<boolean>;
51
+ /**
52
+ * Find records with pagination
53
+ */
54
+ findPaginated(options: FindManyOptions & {
55
+ pagination: PaginationOptions;
56
+ }): Promise<PaginatedResult<T>>;
57
+ /**
58
+ * Execute a raw SQL query (use with caution)
59
+ */
60
+ rawQuery<R = any>(sql: string, params?: any[]): Promise<QueryResult<R>>;
61
+ /**
62
+ * Helper method for transaction-aware create operations
63
+ */
64
+ protected createWithTransaction(tx: DatabaseTransaction, data: Partial<T>): Promise<T>;
65
+ /**
66
+ * Helper method for transaction-aware update operations
67
+ */
68
+ protected updateWithTransaction(tx: DatabaseTransaction, id: number, data: Partial<T>): Promise<T | null>;
69
+ }
70
+ //# sourceMappingURL=BaseRepository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseRepository.d.ts","sourceRoot":"","sources":["../../src/core/BaseRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAE,MAAM,EAAgB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACH,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,eAAe,EACf,iBAAiB,EACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C;;GAEG;AACH,8BAAsB,cAAc,CAAC,CAAC,SAAS,gBAAgB;IAMvD,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,cAAc;IACrC,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM;IANxC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAClC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAC1C,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC;gBAG3B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACpC,MAAM,CAAC,EAAE,MAAM;IAmBnB;;OAEG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IA4BtD;;OAEG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,EAAO,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAiCvE;;OAEG;IACG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,EAAO,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAoCpE;;OAEG;IACG,OAAO,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IA2CvD;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAqDnD;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IA+D/E;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkCnD;;OAEG;IACG,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,EAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAiCpE;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUnD;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG;QAAE,UAAU,EAAE,iBAAiB,CAAA;KAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAwC9G;;OAEG;IACG,QAAQ,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,EAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAgCjF;;OAEG;cACa,qBAAqB,CAAC,EAAE,EAAE,mBAAmB,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAsB5F;;OAEG;cACa,qBAAqB,CAAC,EAAE,EAAE,mBAAmB,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;CA8BlH"}