@warlock.js/herald 4.0.100

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 (82) hide show
  1. package/README.md +364 -0
  2. package/cjs/communicators/communicator-registry.d.ts +155 -0
  3. package/cjs/communicators/communicator-registry.d.ts.map +1 -0
  4. package/cjs/communicators/communicator-registry.js +206 -0
  5. package/cjs/communicators/communicator-registry.js.map +1 -0
  6. package/cjs/communicators/communicator.d.ts +90 -0
  7. package/cjs/communicators/communicator.d.ts.map +1 -0
  8. package/cjs/communicators/communicator.js +93 -0
  9. package/cjs/communicators/communicator.js.map +1 -0
  10. package/cjs/communicators/index.d.ts +3 -0
  11. package/cjs/communicators/index.d.ts.map +1 -0
  12. package/cjs/contracts/channel.contract.d.ts +175 -0
  13. package/cjs/contracts/channel.contract.d.ts.map +1 -0
  14. package/cjs/contracts/communicator-driver.contract.d.ts +168 -0
  15. package/cjs/contracts/communicator-driver.contract.d.ts.map +1 -0
  16. package/cjs/contracts/index.d.ts +3 -0
  17. package/cjs/contracts/index.d.ts.map +1 -0
  18. package/cjs/drivers/index.d.ts +2 -0
  19. package/cjs/drivers/index.d.ts.map +1 -0
  20. package/cjs/drivers/rabbitmq/index.d.ts +3 -0
  21. package/cjs/drivers/rabbitmq/index.d.ts.map +1 -0
  22. package/cjs/drivers/rabbitmq/rabbitmq-channel.d.ts +70 -0
  23. package/cjs/drivers/rabbitmq/rabbitmq-channel.d.ts.map +1 -0
  24. package/cjs/drivers/rabbitmq/rabbitmq-channel.js +400 -0
  25. package/cjs/drivers/rabbitmq/rabbitmq-channel.js.map +1 -0
  26. package/cjs/drivers/rabbitmq/rabbitmq-driver.d.ts +100 -0
  27. package/cjs/drivers/rabbitmq/rabbitmq-driver.d.ts.map +1 -0
  28. package/cjs/drivers/rabbitmq/rabbitmq-driver.js +299 -0
  29. package/cjs/drivers/rabbitmq/rabbitmq-driver.js.map +1 -0
  30. package/cjs/index.d.ts +45 -0
  31. package/cjs/index.d.ts.map +1 -0
  32. package/cjs/index.js +1 -0
  33. package/cjs/index.js.map +1 -0
  34. package/cjs/types.d.ts +396 -0
  35. package/cjs/types.d.ts.map +1 -0
  36. package/cjs/utils/connect-to-communicator.d.ts +86 -0
  37. package/cjs/utils/connect-to-communicator.d.ts.map +1 -0
  38. package/cjs/utils/connect-to-communicator.js +122 -0
  39. package/cjs/utils/connect-to-communicator.js.map +1 -0
  40. package/cjs/utils/index.d.ts +2 -0
  41. package/cjs/utils/index.d.ts.map +1 -0
  42. package/esm/communicators/communicator-registry.d.ts +155 -0
  43. package/esm/communicators/communicator-registry.d.ts.map +1 -0
  44. package/esm/communicators/communicator-registry.js +206 -0
  45. package/esm/communicators/communicator-registry.js.map +1 -0
  46. package/esm/communicators/communicator.d.ts +90 -0
  47. package/esm/communicators/communicator.d.ts.map +1 -0
  48. package/esm/communicators/communicator.js +93 -0
  49. package/esm/communicators/communicator.js.map +1 -0
  50. package/esm/communicators/index.d.ts +3 -0
  51. package/esm/communicators/index.d.ts.map +1 -0
  52. package/esm/contracts/channel.contract.d.ts +175 -0
  53. package/esm/contracts/channel.contract.d.ts.map +1 -0
  54. package/esm/contracts/communicator-driver.contract.d.ts +168 -0
  55. package/esm/contracts/communicator-driver.contract.d.ts.map +1 -0
  56. package/esm/contracts/index.d.ts +3 -0
  57. package/esm/contracts/index.d.ts.map +1 -0
  58. package/esm/drivers/index.d.ts +2 -0
  59. package/esm/drivers/index.d.ts.map +1 -0
  60. package/esm/drivers/rabbitmq/index.d.ts +3 -0
  61. package/esm/drivers/rabbitmq/index.d.ts.map +1 -0
  62. package/esm/drivers/rabbitmq/rabbitmq-channel.d.ts +70 -0
  63. package/esm/drivers/rabbitmq/rabbitmq-channel.d.ts.map +1 -0
  64. package/esm/drivers/rabbitmq/rabbitmq-channel.js +400 -0
  65. package/esm/drivers/rabbitmq/rabbitmq-channel.js.map +1 -0
  66. package/esm/drivers/rabbitmq/rabbitmq-driver.d.ts +100 -0
  67. package/esm/drivers/rabbitmq/rabbitmq-driver.d.ts.map +1 -0
  68. package/esm/drivers/rabbitmq/rabbitmq-driver.js +299 -0
  69. package/esm/drivers/rabbitmq/rabbitmq-driver.js.map +1 -0
  70. package/esm/index.d.ts +45 -0
  71. package/esm/index.d.ts.map +1 -0
  72. package/esm/index.js +1 -0
  73. package/esm/index.js.map +1 -0
  74. package/esm/types.d.ts +396 -0
  75. package/esm/types.d.ts.map +1 -0
  76. package/esm/utils/connect-to-communicator.d.ts +86 -0
  77. package/esm/utils/connect-to-communicator.d.ts.map +1 -0
  78. package/esm/utils/connect-to-communicator.js +122 -0
  79. package/esm/utils/connect-to-communicator.js.map +1 -0
  80. package/esm/utils/index.d.ts +2 -0
  81. package/esm/utils/index.d.ts.map +1 -0
  82. package/package.json +47 -0
package/README.md ADDED
@@ -0,0 +1,364 @@
1
+ # 📯 Warlock Herald
2
+
3
+ > Let heralds carry your messages across services!
4
+
5
+ A powerful, type-safe message bus library for RabbitMQ, Kafka, and more.
6
+
7
+ ## 📦 Installation
8
+
9
+ ```bash
10
+ npm install @warlock.js/herald
11
+ ```
12
+
13
+ ```bash
14
+ yarn add @warlock.js/herald
15
+ ```
16
+
17
+ ```bash
18
+ pnpm add @warlock.js/herald
19
+ ```
20
+
21
+ ### Driver Dependencies
22
+
23
+ Install the driver for your message broker using the Warlock CLI:
24
+
25
+ ```bash
26
+ # RabbitMQ (recommended)
27
+ npx warlock add herald --driver=rabbitmq
28
+
29
+ # Kafka (coming soon)
30
+ npx warlock add herald --driver=kafka
31
+ ```
32
+
33
+ Or install manually:
34
+
35
+ ```bash
36
+ # RabbitMQ
37
+ npm install amqplib
38
+
39
+ # Kafka (coming soon)
40
+ npm install kafkajs
41
+ ```
42
+
43
+ ## 🚀 Quick Start
44
+
45
+ ```typescript
46
+ import { connectToCommunicator, communicators } from "@warlock.js/herald";
47
+
48
+ // Connect to RabbitMQ
49
+ await connectToCommunicator({
50
+ driver: "rabbitmq",
51
+ host: "localhost",
52
+ port: 5672,
53
+ username: "guest",
54
+ password: "guest",
55
+ });
56
+
57
+ // Publish a message
58
+ await communicators().channel("user.created").publish({
59
+ userId: 1,
60
+ email: "user@example.com",
61
+ });
62
+
63
+ // Subscribe to messages
64
+ communicators()
65
+ .channel<{ userId: number; email: string }>("user.created")
66
+ .subscribe(async (message, ctx) => {
67
+ console.log("User created:", message.payload.userId);
68
+ await ctx.ack();
69
+ });
70
+ ```
71
+
72
+ ## 🎯 Core Concepts
73
+
74
+ ### Communicators
75
+
76
+ A communicator wraps a message broker connection. You can have multiple communicators for different brokers or purposes.
77
+
78
+ ```typescript
79
+ // Single communicator (default)
80
+ await connectToCommunicator({
81
+ driver: "rabbitmq",
82
+ host: "localhost",
83
+ });
84
+
85
+ // Multiple communicators
86
+ await connectToCommunicator({
87
+ driver: "rabbitmq",
88
+ name: "notifications",
89
+ isDefault: true,
90
+ host: process.env.NOTIFICATIONS_HOST,
91
+ });
92
+
93
+ await connectToCommunicator({
94
+ driver: "rabbitmq",
95
+ name: "analytics",
96
+ host: process.env.ANALYTICS_HOST,
97
+ });
98
+
99
+ // Use default
100
+ communicators().channel("notifications").publish({ ... });
101
+
102
+ // Use specific communicator
103
+ communicators("analytics").channel("events").publish({ ... });
104
+ ```
105
+
106
+ ### Channels
107
+
108
+ Channels represent queues (RabbitMQ) or topics (Kafka). They provide a unified pub/sub interface.
109
+
110
+ ```typescript
111
+ // Get a channel
112
+ const userChannel = communicators().channel("user.created");
113
+
114
+ // Publish
115
+ await userChannel.publish({ userId: 1 });
116
+
117
+ // Subscribe
118
+ await userChannel.subscribe(async (message, ctx) => {
119
+ console.log(message.payload);
120
+ await ctx.ack();
121
+ });
122
+ ```
123
+
124
+ ### Typed Channels
125
+
126
+ Full TypeScript support with type inference:
127
+
128
+ ```typescript
129
+ interface UserPayload {
130
+ userId: number;
131
+ email: string;
132
+ }
133
+
134
+ // Typed channel
135
+ const channel = communicators().channel<UserPayload>("user.created");
136
+
137
+ // TypeScript knows the payload type
138
+ await channel.publish({ userId: 1, email: "test@example.com" });
139
+
140
+ channel.subscribe(async (message, ctx) => {
141
+ // message.payload is typed as UserPayload
142
+ console.log(message.payload.userId);
143
+ await ctx.ack();
144
+ });
145
+ ```
146
+
147
+ ### Schema Validation with Seal
148
+
149
+ Use `@warlock.js/seal` for runtime validation:
150
+
151
+ ```typescript
152
+ import { v } from "@warlock.js/seal";
153
+
154
+ const UserSchema = v.object({
155
+ userId: v.int().required(),
156
+ email: v.string().email().required(),
157
+ });
158
+
159
+ // Channel with validation
160
+ const channel = communicators().channel("user.created", {
161
+ schema: UserSchema,
162
+ });
163
+
164
+ // Publishing validates automatically
165
+ await channel.publish({ userId: 1, email: "invalid" }); // Throws validation error
166
+
167
+ // Subscribing receives validated data
168
+ channel.subscribe(async (message, ctx) => {
169
+ // message.payload is guaranteed valid
170
+ await ctx.ack();
171
+ });
172
+ ```
173
+
174
+ ## 📚 API Reference
175
+
176
+ ### connectToCommunicator
177
+
178
+ Connect to a message broker and register the communicator:
179
+
180
+ ```typescript
181
+ const communicator = await connectToCommunicator({
182
+ driver: "rabbitmq",
183
+ name: "default", // Optional, defaults to "default"
184
+ isDefault: true, // Optional, defaults to true
185
+ host: "localhost",
186
+ port: 5672,
187
+ username: "guest",
188
+ password: "guest",
189
+ vhost: "/",
190
+ // Driver-specific options
191
+ heartbeat: 60,
192
+ prefetch: 10,
193
+ reconnect: true,
194
+ reconnectDelay: 5000,
195
+ });
196
+ ```
197
+
198
+ ### communicators(name?)
199
+
200
+ Get a communicator by name or the default one:
201
+
202
+ ```typescript
203
+ // Default communicator
204
+ communicators().channel("events");
205
+
206
+ // Named communicator
207
+ communicators("analytics").channel("events");
208
+ ```
209
+
210
+ ### Channel Methods
211
+
212
+ ```typescript
213
+ const channel = communicators().channel<MyPayload>("channel.name");
214
+
215
+ // Publishing
216
+ await channel.publish(payload, options?);
217
+ await channel.publishBatch([payload1, payload2], options?);
218
+
219
+ // Subscribing
220
+ const subscription = await channel.subscribe(handler, options?);
221
+ await subscription.unsubscribe();
222
+ await subscription.pause();
223
+
224
+ // Request-Response (RPC)
225
+ const response = await channel.request<ResponseType>(payload, { timeout: 30000 });
226
+ await channel.respond(async (message, ctx) => {
227
+ return { result: "processed" };
228
+ });
229
+
230
+ // Queue management
231
+ const stats = await channel.stats();
232
+ await channel.purge();
233
+ await channel.delete();
234
+ ```
235
+
236
+ ### Message Context
237
+
238
+ The context object provides message flow control:
239
+
240
+ ```typescript
241
+ channel.subscribe(async (message, ctx) => {
242
+ // Acknowledge (message processed successfully)
243
+ await ctx.ack();
244
+
245
+ // Negative acknowledge (requeue or dead-letter)
246
+ await ctx.nack(requeue?: boolean);
247
+
248
+ // Reject (don't requeue)
249
+ await ctx.reject();
250
+
251
+ // Reply (for RPC patterns)
252
+ await ctx.reply({ result: "ok" });
253
+
254
+ // Retry with delay
255
+ await ctx.retry(5000);
256
+ });
257
+ ```
258
+
259
+ ### Subscribe Options
260
+
261
+ ```typescript
262
+ await channel.subscribe(handler, {
263
+ group: "my-consumer-group", // Consumer group/tag
264
+ prefetch: 10, // Concurrency
265
+ autoAck: false, // Manual ack (recommended)
266
+ exclusive: false, // Exclusive consumer
267
+ retry: {
268
+ maxRetries: 3,
269
+ delay: 1000, // Or: (attempt) => attempt * 1000
270
+ },
271
+ deadLetter: {
272
+ channel: "channel.failed",
273
+ preserveOriginal: true,
274
+ },
275
+ });
276
+ ```
277
+
278
+ ### Publish Options
279
+
280
+ ```typescript
281
+ await channel.publish(payload, {
282
+ priority: 5, // 0-9 (9 highest)
283
+ ttl: 60000, // Time-to-live in ms
284
+ delay: 5000, // Delayed delivery
285
+ persistent: true, // Survive broker restart
286
+ correlationId: "123", // For tracking
287
+ headers: { key: "value" },
288
+ });
289
+ ```
290
+
291
+ ## 🔌 Drivers
292
+
293
+ ### RabbitMQ
294
+
295
+ ```typescript
296
+ await connectToCommunicator({
297
+ driver: "rabbitmq",
298
+ host: "localhost",
299
+ port: 5672,
300
+ username: "guest",
301
+ password: "guest",
302
+ vhost: "/",
303
+ // Or use URI
304
+ uri: "amqp://guest:guest@localhost:5672/",
305
+ });
306
+ ```
307
+
308
+ ### Kafka (Coming Soon)
309
+
310
+ ```typescript
311
+ await connectToCommunicator({
312
+ driver: "kafka",
313
+ brokers: ["localhost:9092"],
314
+ clientId: "my-app",
315
+ ssl: true,
316
+ sasl: {
317
+ mechanism: "plain",
318
+ username: "user",
319
+ password: "pass",
320
+ },
321
+ });
322
+ ```
323
+
324
+ ## 🏗️ Architecture
325
+
326
+ ```
327
+ ┌─────────────────────────────────────────────────┐
328
+ │ Your Application │
329
+ ├─────────────────────────────────────────────────┤
330
+ │ communicators() → channel() → publish() │
331
+ │ subscribe() │
332
+ ├─────────────────────────────────────────────────┤
333
+ │ Communicator Registry │
334
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
335
+ │ │ default │ │analytics │ │ events │ │
336
+ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
337
+ ├───────┼─────────────┼─────────────┼─────────────┤
338
+ │ │ │ │ │
339
+ │ ┌────▼─────┐ ┌────▼─────┐ ┌────▼─────┐ │
340
+ │ │ RabbitMQ │ │ RabbitMQ │ │ Kafka │ │
341
+ │ │ Driver │ │ Driver │ │ Driver │ │
342
+ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
343
+ ├───────┼─────────────┼─────────────┼─────────────┤
344
+ │ ▼ ▼ ▼ │
345
+ │ RabbitMQ RabbitMQ Kafka │
346
+ │ Broker Broker Cluster │
347
+ └─────────────────────────────────────────────────┘
348
+ ```
349
+
350
+ ## 🤝 Philosophy
351
+
352
+ **Herald** is designed around three principles:
353
+
354
+ 1. **Type Safety First** - Full TypeScript support with inference
355
+ 2. **Simple DX** - Intuitive API that feels natural
356
+ 3. **Driver Agnostic** - Same API for RabbitMQ, Kafka, and more
357
+
358
+ ## 📄 License
359
+
360
+ MIT
361
+
362
+ ---
363
+
364
+ **Let your heralds carry your messages! 📯✨**
@@ -0,0 +1,155 @@
1
+ import type { CommunicatorRegistryEvent, CommunicatorRegistryListener } from "../types";
2
+ import { Communicator, type CommunicatorOptions } from "./communicator";
3
+ /**
4
+ * Error thrown when a communicator is not found
5
+ */
6
+ export declare class MissingCommunicatorError extends Error {
7
+ readonly communicatorName?: string;
8
+ constructor(message: string, communicatorName?: string);
9
+ }
10
+ /**
11
+ * Communicator Registry
12
+ *
13
+ * Maintains registry of named communicators.
14
+ * Similar to DataSourceRegistry in @warlock.js/cascade
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // Register a communicator
19
+ * communicatorRegistry.register({
20
+ * name: "default",
21
+ * driver: rabbitMQDriver,
22
+ * isDefault: true,
23
+ * });
24
+ *
25
+ * // Get the default communicator
26
+ * const comm = communicatorRegistry.get();
27
+ *
28
+ * // Get a specific communicator by name
29
+ * const analytics = communicatorRegistry.get("analytics");
30
+ *
31
+ * // Listen for events
32
+ * communicatorRegistry.on("connected", (comm) => {
33
+ * console.log(`${comm.name} connected`);
34
+ * });
35
+ * ```
36
+ */
37
+ declare class CommunicatorRegistry {
38
+ private readonly sources;
39
+ private defaultSource?;
40
+ private readonly events;
41
+ /**
42
+ * Register a new communicator
43
+ *
44
+ * Sets up event forwarding from the driver to the registry.
45
+ *
46
+ * @param options - Communicator configuration
47
+ * @returns The registered communicator instance
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const communicator = communicatorRegistry.register({
52
+ * name: "primary",
53
+ * driver: myDriver,
54
+ * isDefault: true,
55
+ * });
56
+ * ```
57
+ */
58
+ register(options: CommunicatorOptions): Communicator;
59
+ /**
60
+ * Clear all registered communicators
61
+ */
62
+ clear(): void;
63
+ /**
64
+ * Listen for registry events
65
+ *
66
+ * @param event - Event to listen for
67
+ * @param listener - Callback function
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * communicatorRegistry.on("registered", (comm) => {
72
+ * console.log(`Communicator "${comm.name}" registered`);
73
+ * });
74
+ *
75
+ * communicatorRegistry.on("connected", (comm) => {
76
+ * console.log(`Communicator "${comm.name}" connected`);
77
+ * });
78
+ * ```
79
+ */
80
+ on(event: CommunicatorRegistryEvent, listener: CommunicatorRegistryListener): void;
81
+ /**
82
+ * Listen for a registry event once
83
+ *
84
+ * @param event - Event to listen for
85
+ * @param listener - Callback function
86
+ */
87
+ once(event: CommunicatorRegistryEvent, listener: CommunicatorRegistryListener): void;
88
+ /**
89
+ * Remove an event listener
90
+ *
91
+ * @param event - Event to stop listening for
92
+ * @param listener - Callback to remove
93
+ */
94
+ off(event: CommunicatorRegistryEvent, listener: CommunicatorRegistryListener): void;
95
+ /**
96
+ * Get a communicator by name or the default one
97
+ *
98
+ * @param name - Optional communicator name
99
+ * @returns Communicator instance
100
+ * @throws MissingCommunicatorError if not found
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * // Get default communicator
105
+ * const comm = communicatorRegistry.get();
106
+ *
107
+ * // Get specific communicator
108
+ * const analytics = communicatorRegistry.get("analytics");
109
+ * ```
110
+ */
111
+ get(name?: string): Communicator;
112
+ /**
113
+ * Check if a communicator exists
114
+ *
115
+ * @param name - Communicator name to check
116
+ * @returns True if exists
117
+ */
118
+ has(name: string): boolean;
119
+ /**
120
+ * Check if any communicators are registered
121
+ */
122
+ hasAny(): boolean;
123
+ /**
124
+ * Get all registered communicators
125
+ *
126
+ * @returns Array of all communicators
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * // Disconnect all communicators
131
+ * for (const comm of communicatorRegistry.getAll()) {
132
+ * await comm.disconnect();
133
+ * }
134
+ * ```
135
+ */
136
+ getAll(): Communicator[];
137
+ /**
138
+ * Get all communicator names
139
+ *
140
+ * @returns Array of communicator names
141
+ */
142
+ getNames(): string[];
143
+ /**
144
+ * Get the default communicator (if any)
145
+ *
146
+ * @returns Default communicator or undefined
147
+ */
148
+ getDefault(): Communicator | undefined;
149
+ }
150
+ /**
151
+ * Global communicator registry instance
152
+ */
153
+ export declare const communicatorRegistry: CommunicatorRegistry;
154
+ export {};
155
+ //# sourceMappingURL=communicator-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"communicator-registry.d.ts","sourceRoot":"","sources":["../../src/communicators/communicator-registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAExE;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,SAAgB,gBAAgB,CAAC,EAAE,MAAM,CAAC;gBAEvB,OAAO,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM;CAK9D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,cAAM,oBAAoB;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,aAAa,CAAC,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAE7C;;;;;;;;;;;;;;;;OAgBG;IACI,QAAQ,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY;IA6B3D;;OAEG;IACI,KAAK,IAAI,IAAI;IAKpB;;;;;;;;;;;;;;;;OAgBG;IACI,EAAE,CAAC,KAAK,EAAE,yBAAyB,EAAE,QAAQ,EAAE,4BAA4B,GAAG,IAAI;IAIzF;;;;;OAKG;IACI,IAAI,CAAC,KAAK,EAAE,yBAAyB,EAAE,QAAQ,EAAE,4BAA4B,GAAG,IAAI;IAI3F;;;;;OAKG;IACI,GAAG,CAAC,KAAK,EAAE,yBAAyB,EAAE,QAAQ,EAAE,4BAA4B,GAAG,IAAI;IAI1F;;;;;;;;;;;;;;;OAeG;IACI,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY;IAgBvC;;;;;OAKG;IACI,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIjC;;OAEG;IACI,MAAM,IAAI,OAAO;IAIxB;;;;;;;;;;;;OAYG;IACI,MAAM,IAAI,YAAY,EAAE;IAI/B;;;;OAIG;IACI,QAAQ,IAAI,MAAM,EAAE;IAI3B;;;;OAIG;IACI,UAAU,IAAI,YAAY,GAAG,SAAS;CAG9C;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,sBAA6B,CAAC"}