@ixo/matrix 1.1.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.
Files changed (67) hide show
  1. package/README.md +396 -0
  2. package/dist/src/checkpointer/checkpointer.d.ts +56 -0
  3. package/dist/src/checkpointer/checkpointer.d.ts.map +1 -0
  4. package/dist/src/checkpointer/checkpointer.js +669 -0
  5. package/dist/src/checkpointer/checkpointer.js.map +1 -0
  6. package/dist/src/checkpointer/index.d.ts +3 -0
  7. package/dist/src/checkpointer/index.d.ts.map +1 -0
  8. package/dist/src/checkpointer/index.js +3 -0
  9. package/dist/src/checkpointer/index.js.map +1 -0
  10. package/dist/src/checkpointer/types.d.ts +66 -0
  11. package/dist/src/checkpointer/types.d.ts.map +1 -0
  12. package/dist/src/checkpointer/types.js +2 -0
  13. package/dist/src/checkpointer/types.js.map +1 -0
  14. package/dist/src/checkpointer/utils.d.ts +2 -0
  15. package/dist/src/checkpointer/utils.d.ts.map +1 -0
  16. package/dist/src/checkpointer/utils.js +15 -0
  17. package/dist/src/checkpointer/utils.js.map +1 -0
  18. package/dist/src/index.d.ts +5 -0
  19. package/dist/src/index.d.ts.map +1 -0
  20. package/dist/src/index.js +5 -0
  21. package/dist/src/index.js.map +1 -0
  22. package/dist/src/matrix-manager.d.ts +61 -0
  23. package/dist/src/matrix-manager.d.ts.map +1 -0
  24. package/dist/src/matrix-manager.js +337 -0
  25. package/dist/src/matrix-manager.js.map +1 -0
  26. package/dist/src/matrix-state-manager/index.d.ts +2 -0
  27. package/dist/src/matrix-state-manager/index.d.ts.map +1 -0
  28. package/dist/src/matrix-state-manager/index.js +2 -0
  29. package/dist/src/matrix-state-manager/index.js.map +1 -0
  30. package/dist/src/matrix-state-manager/matrix-state-manager.d.ts +22 -0
  31. package/dist/src/matrix-state-manager/matrix-state-manager.d.ts.map +1 -0
  32. package/dist/src/matrix-state-manager/matrix-state-manager.js +172 -0
  33. package/dist/src/matrix-state-manager/matrix-state-manager.js.map +1 -0
  34. package/dist/src/types/matrix.d.ts +27 -0
  35. package/dist/src/types/matrix.d.ts.map +1 -0
  36. package/dist/src/types/matrix.js +2 -0
  37. package/dist/src/types/matrix.js.map +1 -0
  38. package/dist/src/types.d.ts +64 -0
  39. package/dist/src/types.d.ts.map +1 -0
  40. package/dist/src/types.js +55 -0
  41. package/dist/src/types.js.map +1 -0
  42. package/dist/src/utils/cache.d.ts +13 -0
  43. package/dist/src/utils/cache.d.ts.map +1 -0
  44. package/dist/src/utils/cache.js +44 -0
  45. package/dist/src/utils/cache.js.map +1 -0
  46. package/dist/src/utils/create-simple-matrix-client.d.ts +45 -0
  47. package/dist/src/utils/create-simple-matrix-client.d.ts.map +1 -0
  48. package/dist/src/utils/create-simple-matrix-client.js +269 -0
  49. package/dist/src/utils/create-simple-matrix-client.js.map +1 -0
  50. package/dist/src/utils/format-msg.d.ts +13 -0
  51. package/dist/src/utils/format-msg.d.ts.map +1 -0
  52. package/dist/src/utils/format-msg.js +14 -0
  53. package/dist/src/utils/format-msg.js.map +1 -0
  54. package/dist/src/utils/login.d.ts +3 -0
  55. package/dist/src/utils/login.d.ts.map +1 -0
  56. package/dist/src/utils/login.js +10 -0
  57. package/dist/src/utils/login.js.map +1 -0
  58. package/dist/src/utils/mx.d.ts +7 -0
  59. package/dist/src/utils/mx.d.ts.map +1 -0
  60. package/dist/src/utils/mx.js +58 -0
  61. package/dist/src/utils/mx.js.map +1 -0
  62. package/dist/src/utils/secretStorageKeys.d.ts +10 -0
  63. package/dist/src/utils/secretStorageKeys.d.ts.map +1 -0
  64. package/dist/src/utils/secretStorageKeys.js +33 -0
  65. package/dist/src/utils/secretStorageKeys.js.map +1 -0
  66. package/dist/tsconfig.tsbuildinfo +1 -0
  67. package/package.json +60 -0
package/README.md ADDED
@@ -0,0 +1,396 @@
1
+ # @ixo/matrix
2
+
3
+ ## Overview
4
+
5
+ The `@ixo/matrix` package is a robust wrapper around the Matrix.org client SDK, specifically designed for the ixo-oracles ecosystem. It provides a secure, type-safe interface for managing Matrix communications with end-to-end encryption.
6
+
7
+ ### Key Features
8
+
9
+ - 🔐 End-to-end encrypted room management
10
+ - 💬 Secure messaging with threading support
11
+ - 🔄 Type-safe state management
12
+ - 🔑 Cross-signing and cryptographic key management
13
+ - 🏗️ Oracle-specific room naming and access control
14
+ - 📦 Local storage for crypto and general data
15
+ - ⚡ Singleton pattern for efficient resource management
16
+
17
+ ## Table of Contents
18
+
19
+ 1. [Getting Started](#getting-started)
20
+ - [Installation](#installation)
21
+ - [Configuration](#configuration)
22
+ - [Quick Start](#quick-start)
23
+ 2. [Core Concepts](#core-concepts)
24
+ - [Room Management](#room-management)
25
+ - [Messaging](#messaging)
26
+ - [State Management](#state-management)
27
+ - [Client Operations and User Tokens](#client-operations-and-user-tokens)
28
+ 3. [API Reference](#api-reference)
29
+ 4. [Development](#development)
30
+
31
+ ## Getting Started
32
+
33
+ ### Installation
34
+
35
+ ```bash
36
+ # Install using pnpm (recommended)
37
+ pnpm install @ixo/matrix
38
+
39
+ # Or using npm
40
+ npm install @ixo/matrix
41
+
42
+ # Or using yarn
43
+ yarn add @ixo/matrix
44
+ ```
45
+
46
+ ### Configuration
47
+
48
+ The package requires several environment variables for proper operation:
49
+
50
+ #### Matrix Server
51
+
52
+ ```bash
53
+ MATRIX_BASE_URL=https://your-matrix-server.com # Your Matrix homeserver URL
54
+ ```
55
+
56
+ #### Oracle Admin Credentials
57
+
58
+ ```bash
59
+ MATRIX_ORACLE_ADMIN_ACCESS_TOKEN=your_token # Admin access token
60
+ MATRIX_ORACLE_ADMIN_USER_ID=@admin:your.server # Admin user ID
61
+ MATRIX_ORACLE_ADMIN_PASSWORD=your_password # Password for cross-signing
62
+ ```
63
+
64
+ #### Security
65
+
66
+ ```bash
67
+ MATRIX_RECOVERY_PHRASE=your_recovery_phrase # For secret storage & key backup
68
+ ```
69
+
70
+ #### Storage Paths (Optional)
71
+
72
+ All paths are created in the `matrix-local-storage` folder:
73
+
74
+ ```bash
75
+ MATRIX_CRYPTO_STORE_PATH=./matrix-crypto-store # Crypto storage
76
+ MATRIX_STORE_PATH=./matrix-store # General storage
77
+ MATRIX_SECRET_STORAGE_KEYS_PATH=./matrix-secret-storage # Secret keys
78
+ ```
79
+
80
+ ### Environment Variables Explained
81
+
82
+ #### Cross-Signing and Security Variables
83
+
84
+ The package uses Matrix's cross-signing feature for enhanced security. This requires specific environment variables:
85
+
86
+ - `MATRIX_ORACLE_ADMIN_PASSWORD`: Required for authenticating cross-signing key uploads. When setting up cross-signing, the package needs to upload device signing keys to the server. This operation requires authentication using the admin password.
87
+
88
+ - `MATRIX_RECOVERY_PHRASE`: Used for:
89
+ - Creating and managing secret storage
90
+ - Generating recovery keys
91
+ - Restoring key backups
92
+ - Accessing encrypted data across devices
93
+
94
+ #### Prerequisites
95
+
96
+ - Access to a Matrix homeserver
97
+ - fs write permissions
98
+
99
+ ### Quick Start
100
+
101
+ ```typescript
102
+ import { MatrixManager } from '@ixo/matrix';
103
+
104
+ async function main() {
105
+ // 1. Get the singleton instance
106
+ const manager = MatrixManager.getInstance();
107
+
108
+ // 2. Initialize the manager
109
+ await manager.init();
110
+
111
+ // 3. Create and join a room
112
+ const roomId = await manager.createRoomAndJoin({
113
+ did: 'did:ixo:123',
114
+ oracleName: 'myOracle',
115
+ userAccessToken: 'user_access_token',
116
+ });
117
+
118
+ // 4. Send a message
119
+ await manager.sendMessage({
120
+ roomId,
121
+ message: 'Hello World',
122
+ });
123
+ }
124
+ ```
125
+
126
+ ## Core Concepts
127
+
128
+ ### Room Management
129
+
130
+ Rooms in the ixo-matrix ecosystem are secure, encrypted spaces for communication. Each room has:
131
+
132
+ - Unique DID-based identification
133
+ - Oracle-specific naming convention
134
+ - Controlled access permissions
135
+ - End-to-end encryption
136
+
137
+ #### Room Creation Process
138
+
139
+ 1. **Room Naming**
140
+
141
+ ```typescript
142
+ // Rooms are named using a deterministic hash of DID and oracle name
143
+ const roomName = MatrixManager.generateRoomNameFromDidAndOracle(
144
+ 'did:ixo:123',
145
+ 'myOracle',
146
+ ); // Results in format: 'ixo-{md5hash}'
147
+
148
+ // Room aliases are generated by replacing spaces with underscores
149
+ const roomAlias = MatrixManager.generateRoomAliasFromName(roomName);
150
+ ```
151
+
152
+ 1. **Room Configuration**
153
+ Each room is created with specific security settings:
154
+
155
+ - Private visibility (not publicly listed)
156
+ - End-to-end encryption enabled (using m.megolm.v1.aes-sha2)
157
+ - Guest access forbidden
158
+ - Shared history visibility
159
+ - Admin power levels for specific operations:
160
+ - Kick: 9999
161
+ - Ban: 9999
162
+ - Invite: 9999
163
+ - Redact: 9999
164
+
165
+ 1. **Creating and Joining**
166
+
167
+ ```typescript
168
+ // Create a room with all security features enabled
169
+ const roomId = await manager.createRoomAndJoin({
170
+ did: 'did:ixo:123',
171
+ oracleName: 'myOracle',
172
+ userAccessToken: 'user_token',
173
+ });
174
+ ```
175
+
176
+ 1. **Access Control**
177
+
178
+ ```typescript
179
+ // Check if a user has access to a room
180
+ const hasAccess = await manager.checkIsUserInRoom({
181
+ roomId,
182
+ userAccessToken: 'user_token',
183
+ });
184
+
185
+ // Get room by ID
186
+ const room = manager.getRoom(roomId);
187
+ ```
188
+
189
+ 1. **Room Lifecycle**
190
+
191
+ - Rooms are created with encryption enabled by default
192
+ - Admin user is automatically given highest power level
193
+ - Initial state includes encryption, guest access, and history visibility settings
194
+ - Room can be found using DID and oracle name:
195
+
196
+ ```typescript
197
+ // Get room ID from DID and oracle name
198
+ const existingRoomId = await manager.getRoomId({
199
+ did: 'did:ixo:123',
200
+ oracleName: 'myOracle',
201
+ });
202
+ ```
203
+
204
+ ### Client Operations and User Tokens
205
+
206
+ The package uses two types of tokens for different operations:
207
+
208
+ #### 1. Admin Token (`MATRIX_ORACLE_ADMIN_ACCESS_TOKEN`)
209
+
210
+ Used for:
211
+
212
+ - Sending messages in rooms
213
+ - Managing room state
214
+ - Setting up encryption
215
+ - Cross-signing operations
216
+ - Getting room information
217
+ - Managing room power levels
218
+ - Creating rooms
219
+ - Inviting users to rooms
220
+
221
+ #### 2. User Token (Provided per operation)
222
+
223
+ Used only for:
224
+
225
+ - joining rooms
226
+ - Verifying room membership
227
+
228
+ Example usage:
229
+
230
+ ```typescript
231
+ // Operations using Admin Token (automatically handled)
232
+ await manager.sendMessage({
233
+ roomId,
234
+ message: 'Hello World',
235
+ });
236
+
237
+ // Operations requiring User Token
238
+ // the user token is used only for joining the room -- the rest of the operations are handled by the admin token
239
+ await manager.createRoomAndJoin({
240
+ did: 'did:ixo:123',
241
+ oracleName: 'myOracle',
242
+ userAccessToken: 'user_token', // User token required
243
+ });
244
+
245
+ // Check if the user is in the room using the user token
246
+ await manager.checkIsUserInRoom({
247
+ roomId,
248
+ userAccessToken: 'user_token', // User token required
249
+ });
250
+ ```
251
+
252
+ Note: User tokens are temporary and automatically cleaned up after use. All other operations use the admin token internally.
253
+
254
+ ### Messaging
255
+
256
+ The package supports various message types and threading:
257
+ All the messages will be sent from the Oracle Admin client.
258
+
259
+ ```typescript
260
+ // Regular message
261
+ await manager.sendMessage({
262
+ roomId,
263
+ message: 'Hello World',
264
+ });
265
+
266
+ // Threaded reply
267
+ await manager.sendMessage({
268
+ roomId,
269
+ message: 'Reply',
270
+ threadId: 'original_message_id',
271
+ });
272
+
273
+ // Oracle admin message
274
+ await manager.sendMessage({
275
+ roomId,
276
+ message: 'Admin notification',
277
+ isOracleAdmin: true,
278
+ });
279
+ ```
280
+
281
+ ### State Management
282
+
283
+ Type-safe state management with validation:
284
+
285
+ ```typescript
286
+ interface ProjectState {
287
+ status: string;
288
+ lastUpdate: number;
289
+ }
290
+
291
+ // Set state
292
+ await manager.stateManager.setState<ProjectState>({
293
+ roomId,
294
+ stateKey: 'oracle_project',
295
+ data: {
296
+ status: 'active',
297
+ lastUpdate: Date.now(),
298
+ },
299
+ });
300
+
301
+ // Get state
302
+ const state = await manager.stateManager.getState<ProjectState>(
303
+ roomId,
304
+ 'oracle_project',
305
+ );
306
+
307
+ // Update state
308
+ await manager.stateManager.updateState<ProjectState>({
309
+ roomId,
310
+ stateKey: 'oracle_project',
311
+ data: {
312
+ status: 'completed',
313
+ },
314
+ });
315
+ ```
316
+
317
+ ### LangChain Graph Checkpointing
318
+
319
+ The package provides a Matrix-based checkpointer implementation for LangChain graphs, allowing you to persist graph state in Matrix rooms:
320
+
321
+ ```typescript
322
+ import { MatrixCheckpointSaver } from '@ixo/matrix';
323
+ import { StateGraph } from '@langchain/langgraph';
324
+
325
+ // Create your graph
326
+ const workflow = new StateGraph(graphState)
327
+ .addNode('myNode', (state) => {
328
+ // Your node logic
329
+ })
330
+ .addEdge(START, 'myNode')
331
+ .addEdge('myNode', END);
332
+
333
+ // Compile the graph with Matrix checkpointing
334
+ const graph = workflow.compile({
335
+ checkpointer: new MatrixCheckpointSaver('your-graph-name'),
336
+ });
337
+ ```
338
+
339
+ ## API Reference
340
+
341
+ ### MatrixManager
342
+
343
+ The main interface for Matrix operations:
344
+
345
+ - `getInstance()`: Get singleton instance
346
+ - `init()`: Initialize the manager
347
+ - `createRoomAndJoin()`: Create and join a room
348
+ - `sendMessage()`: Send a message
349
+ - `stop()`: Cleanup resources
350
+
351
+ ### MatrixStateManager
352
+
353
+ Handles state management:
354
+
355
+ - `setState<T>()`: Set typed state
356
+ - `getState<T>()`: Get typed state
357
+ - `updateState<T>()`: Update existing state
358
+ - `listStateEvents<T>()`: List all state events
359
+
360
+ ## Development
361
+
362
+ ### Testing
363
+
364
+ ```bash
365
+ # Run unit tests
366
+ pnpm test
367
+
368
+ # Run integration tests
369
+ pnpm test:e2e
370
+
371
+ # Run with coverage
372
+ pnpm test:coverage
373
+ ```
374
+
375
+ ### Error Handling
376
+
377
+ ```typescript
378
+ try {
379
+ await manager.sendMessage({
380
+ roomId,
381
+ message: 'Hello',
382
+ });
383
+ } catch (error) {
384
+ if (error instanceof MatrixError) {
385
+ // Handle Matrix-specific errors
386
+ console.error('Matrix error:', error.errcode);
387
+ } else {
388
+ // Handle other errors
389
+ console.error('General error:', error);
390
+ }
391
+ }
392
+ ```
393
+
394
+ ## License
395
+
396
+ Internal package - All rights reserved.
@@ -0,0 +1,56 @@
1
+ import 'dotenv/config';
2
+ import type { RunnableConfig } from '@langchain/core/runnables';
3
+ import { BaseCheckpointSaver, type Checkpoint, type CheckpointListOptions, type CheckpointMetadata, type CheckpointTuple, type PendingWrite, type SerializerProtocol } from '@langchain/langgraph-checkpoint';
4
+ import type { IGraphStateWithRequiredFields } from './types.js';
5
+ interface CacheMetrics {
6
+ hits: number;
7
+ misses: number;
8
+ indexRebuilds: number;
9
+ filteredEvents: number;
10
+ duplicateEvents: number;
11
+ }
12
+ export declare class MatrixCheckpointSaver<_GraphState extends IGraphStateWithRequiredFields = IGraphStateWithRequiredFields> extends BaseCheckpointSaver {
13
+ private stateManager;
14
+ private checkpointCache;
15
+ private writesCache;
16
+ private latestCache;
17
+ private indexCache;
18
+ private metrics;
19
+ private readonly cacheTTL;
20
+ private readonly cacheMax;
21
+ private readonly maxCheckpointSizeBytes;
22
+ constructor(serde?: SerializerProtocol);
23
+ private sanitizeOracleDid;
24
+ private getCheckpointKey;
25
+ private getWritesKey;
26
+ private getLatestCheckpointKey;
27
+ private getThreadMapKey;
28
+ private getCacheWritesKey;
29
+ private getCacheLatestKey;
30
+ private getCacheCheckpointKey;
31
+ private getThreadMap;
32
+ private updateThreadMap;
33
+ private storeCheckpoint;
34
+ private storeWrites;
35
+ private getStoredCheckpoint;
36
+ private isValidCheckpoint;
37
+ private getStoredWrites;
38
+ private migratePendingSends;
39
+ getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;
40
+ list(config: RunnableConfig, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple>;
41
+ put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata): Promise<RunnableConfig>;
42
+ putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;
43
+ deleteThread(threadId: string): Promise<void>;
44
+ deleteThreadWithContext(roomId: string, oracleDid: string, threadId: string, checkpointNs?: string): Promise<void>;
45
+ rebuildThreadMap(roomId: string, oracleDid: string): Promise<void>;
46
+ end(): Promise<void>;
47
+ getCacheMetrics(): CacheMetrics;
48
+ getFilteringMetrics(): {
49
+ filteredEvents: number;
50
+ duplicateEvents: number;
51
+ indexRebuilds: number;
52
+ avgEventsPerRebuild: number;
53
+ };
54
+ }
55
+ export {};
56
+ //# sourceMappingURL=checkpointer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpointer.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/checkpointer.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EACL,mBAAmB,EACnB,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EAEvB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EAKxB,MAAM,iCAAiC,CAAC;AAKzC,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AA6FhE,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,qBAAqB,CAChC,WAAW,SACT,6BAA6B,GAAG,6BAA6B,CAC/D,SAAQ,mBAAmB;IAC3B,OAAO,CAAC,YAAY,CAAsB;IAG1C,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,WAAW,CAGhB;IACH,OAAO,CAAC,UAAU,CAAgB;IAGlC,OAAO,CAAC,OAAO,CAMb;IAGF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;gBAEpC,KAAK,CAAC,EAAE,kBAAkB;IAyBtC,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,qBAAqB;YAWf,YAAY;YAqCZ,eAAe;YAwBf,eAAe;YAsEf,WAAW;YAwEX,mBAAmB;IA6EjC,OAAO,CAAC,iBAAiB;YAcX,eAAe;YAqEf,mBAAmB;IA2C3B,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAyHrE,IAAI,CACT,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAAC,eAAe,CAAC;IAyJ5B,GAAG,CACP,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAAC,cAAc,CAAC;IAqDpB,SAAS,CACb,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IA2DH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB7C,uBAAuB,CAClC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,YAAY,GAAE,MAAW,GACxB,OAAO,CAAC,IAAI,CAAC;IAwGH,gBAAgB,CAC3B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IA6DH,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAM1B,eAAe,IAAI,YAAY;IAK/B,mBAAmB,IAAI;QAC5B,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,MAAM,CAAC;KAC7B;CAaF"}