@qianxude/logger 0.1.4

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,352 @@
1
+ # @qianxude/logger
2
+
3
+ Production-ready pino-based logger implementations for the qianxude stack.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @qianxude/logger
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ This package provides:
14
+
15
+ 1. **Factory functions** to create raw pino loggers configured for common use cases
16
+ 2. **PinoLogger class** that wraps pino and implements the stack-standard `Logger` interface from `@qianxude/shared`
17
+
18
+ ## Quick Start
19
+
20
+ ### Pretty Console Logger (Development)
21
+
22
+ ```typescript
23
+ import { createPrettyPino, PinoLogger } from '@qianxude/logger';
24
+
25
+ // Create a raw pino logger with pretty output
26
+ const rawPino = createPrettyPino({
27
+ level: 'debug',
28
+ colorize: true,
29
+ });
30
+
31
+ // Wrap it as a stack-standard Logger
32
+ const logger = new PinoLogger(rawPino, { service: 'my-app' });
33
+
34
+ logger.info('Application started');
35
+ logger.debug({ userId: 123 }, 'User logged in');
36
+ logger.error({ err: new Error('Oops') }, 'Something went wrong');
37
+ ```
38
+
39
+ ### Duo-Target Logger (Production)
40
+
41
+ Outputs to both console (pretty) and file (with rotation):
42
+
43
+ ```typescript
44
+ import { createDuoTargetPino, PinoLogger } from '@qianxude/logger';
45
+
46
+ const rawPino = createDuoTargetPino({
47
+ level: 'info',
48
+ file: './logs/app.log',
49
+ frequency: 'daily', // Rotate daily
50
+ limit: { count: 7 }, // Keep 7 days of logs
51
+ });
52
+
53
+ const logger = new PinoLogger(rawPino);
54
+ logger.info('Application started');
55
+ ```
56
+
57
+ ### File-Only Logger
58
+
59
+ ```typescript
60
+ import { createRollFilePino, PinoLogger } from '@qianxude/logger';
61
+
62
+ const rawPino = createRollFilePino({
63
+ level: 'info',
64
+ file: './logs/app.log',
65
+ size: '10m', // Rotate at 10MB
66
+ limit: { count: 5 }, // Keep 5 rotated files
67
+ });
68
+
69
+ const logger = new PinoLogger(rawPino);
70
+ ```
71
+
72
+ ## Logger Interface
73
+
74
+ `PinoLogger` implements the `Logger` interface from `@qianxude/shared`:
75
+
76
+ ```typescript
77
+ interface Logger {
78
+ // Log methods
79
+ log(level: LogLevel, message: string, ctx?: Context): void;
80
+ fatal(message: string, ctx?: Context): void;
81
+ error(message: string, ctx?: Context): void;
82
+ warn(message: string, ctx?: Context): void;
83
+ info(message: string, ctx?: Context): void;
84
+ debug(message: string, ctx?: Context): void;
85
+ trace(message: string, ctx?: Context): void;
86
+
87
+ // Context management
88
+ get ctx(): Context;
89
+ attach(key: string, value: any): void;
90
+ attach(ctx: Context): void;
91
+ detach(ctx: Context): void;
92
+ spawn(ctx?: Context): this;
93
+ }
94
+ ```
95
+
96
+ ### Context Management
97
+
98
+ ```typescript
99
+ // Attach context
100
+ logger.attach('userId', 123);
101
+ logger.attach({ requestId: 'abc-123', method: 'GET' });
102
+
103
+ // Create child logger with extended context
104
+ const childLogger = logger.spawn({ operation: 'query' });
105
+ childLogger.info('Executing query'); // Includes userId, requestId, method, operation
106
+
107
+ // Detach context
108
+ logger.detach({ userId: undefined });
109
+ ```
110
+
111
+ ### Log Levels
112
+
113
+ - `trace` (10) - Ultra-verbose tracing
114
+ - `debug` (20) - Debugging information
115
+ - `info` (30) - General information (default)
116
+ - `warn` (40) - Warnings
117
+ - `error` (50) - Errors
118
+ - `fatal` (60) - Critical errors
119
+
120
+ ## Factory Functions
121
+
122
+ ### createPrettyPino(options)
123
+
124
+ Creates a pino logger with pretty console output.
125
+
126
+ ```typescript
127
+ interface PrettyPinoOptions {
128
+ level?: LogLevel; // Default: 'info'
129
+ colorize?: boolean; // Default: true
130
+ translateTime?: string; // Default: 'SYS:standard'
131
+ prettyOptions?: pino.PrettyOptions;
132
+ }
133
+ ```
134
+
135
+ ### createDuoTargetPino(options)
136
+
137
+ Creates a pino logger that outputs to both console (pretty) and file (with rotation).
138
+
139
+ ```typescript
140
+ interface DuoTargetPinoOptions {
141
+ level?: LogLevel; // Default: 'info'
142
+ file: string; // Required: log file path
143
+ frequency?: 'daily' | 'hourly' | number; // Time-based rotation
144
+ size?: string | number; // Size-based rotation (e.g., '10m')
145
+ mkdir?: boolean; // Create parent dirs (default: true)
146
+ symlink?: boolean; // Create symlink to current log (default: true)
147
+ limit?: { count: number }; // Number of rotated files to keep
148
+ colorize?: boolean; // Console colorize (default: true)
149
+ translateTime?: string; // Default: 'SYS:standard'
150
+ }
151
+ ```
152
+
153
+ ### createRollFilePino(options)
154
+
155
+ Creates a pino logger that outputs only to file (with rotation).
156
+
157
+ ```typescript
158
+ interface RotationOptions {
159
+ level?: LogLevel;
160
+ file: string; // Required
161
+ frequency?: 'daily' | 'hourly' | number;
162
+ size?: string | number;
163
+ mkdir?: boolean;
164
+ symlink?: boolean;
165
+ limit?: { count: number };
166
+ }
167
+ ```
168
+
169
+ ### createPino(options)
170
+
171
+ Creates a basic pino logger with custom options.
172
+
173
+ ```typescript
174
+ const logger = createPino({
175
+ level: 'debug',
176
+ base: { service: 'my-service' },
177
+ });
178
+ ```
179
+
180
+ ## Environment Variables
181
+
182
+ The logger package supports configuration via environment variables. Use the parsing functions to convert environment variables into logger options.
183
+
184
+ ### Required vs Optional
185
+
186
+ | Variable | Required For | Optional For | Default |
187
+ |----------|--------------|--------------|---------|
188
+ | `LOG_FILE` | `parsePinoRollOptionsEnv`, `parseDuoTargetPinoOptionsEnv` | `parsePrettyPinoOptionsEnv` | - |
189
+ | `LOG_LEVEL` | - | All functions | `'info'` |
190
+ | `LOG_COLORIZE` | - | All functions | `true` |
191
+ | `LOG_TRANSLATE_TIME` | - | All functions | `'SYS:standard'` |
192
+ | `LOG_FILE_SIZE` | - | File logging | - |
193
+ | `LOG_FILE_FREQUENCY` | - | File logging | - |
194
+ | `LOG_FILE_EXTENSION` | - | File logging | - |
195
+ | `LOG_FILE_SYMLINK` | - | File logging | `true` |
196
+ | `LOG_FILE_LIMIT_COUNT` | - | File logging | - |
197
+ | `LOG_FILE_DATE_FORMAT` | - | File logging | - |
198
+ | `LOG_FILE_MKDIR` | - | File logging | `true` |
199
+
200
+ **Important**: `LOG_FILE` is the only required environment variable, and it's only required for file-based logging functions (`parsePinoRollOptionsEnv` and `parseDuoTargetPinoOptionsEnv`).
201
+
202
+ ### Available Environment Variables
203
+
204
+ #### Common/Pretty Options (All Optional)
205
+
206
+ | Variable | Description | Example Values |
207
+ |----------|-------------|----------------|
208
+ | `LOG_LEVEL` | Minimum log level | `fatal`, `error`, `warn`, `info`, `debug`, `trace` |
209
+ | `LOG_COLORIZE` | Enable colorized output | `true`, `false` |
210
+ | `LOG_TRANSLATE_TIME` | Time format string | `SYS:standard`, `SYS:iso` |
211
+
212
+ #### File Roll Options
213
+
214
+ | Variable | Description | Required | Example Values |
215
+ |----------|-------------|----------|----------------|
216
+ | `LOG_FILE` | Log file path | **Yes** (for file logging) | `./logs/app.log` |
217
+ | `LOG_FILE_SIZE` | Max file size | No | `10m`, `100k`, `1g`, or number (MB) |
218
+ | `LOG_FILE_FREQUENCY` | Rotation frequency | No | `daily`, `hourly`, or number (ms) |
219
+ | `LOG_FILE_EXTENSION` | File extension to append | No | `.log` |
220
+ | `LOG_FILE_SYMLINK` | Create symlink to current log | No | `true`, `false` |
221
+ | `LOG_FILE_LIMIT_COUNT` | Number of rotated files to keep | No | `7` |
222
+ | `LOG_FILE_DATE_FORMAT` | Date format for filename | No | `yyyy-MM-dd` |
223
+ | `LOG_FILE_MKDIR` | Create parent directories | No | `true`, `false` |
224
+
225
+ ### Usage Examples
226
+
227
+ #### Parse Pretty Logger Options
228
+
229
+ ```typescript
230
+ import { parsePrettyPinoOptionsEnv, createPrettyPino } from '@qianxude/logger';
231
+
232
+ // Parse from process.env
233
+ const options = parsePrettyPinoOptionsEnv();
234
+
235
+ // Or pass a custom env object
236
+ const options = parsePrettyPinoOptionsEnv({
237
+ LOG_LEVEL: 'debug',
238
+ LOG_COLORIZE: 'true',
239
+ });
240
+
241
+ const logger = createPrettyPino(options);
242
+ ```
243
+
244
+ #### Parse File Roll Options
245
+
246
+ ```typescript
247
+ import { parsePinoRollOptionsEnv, createRollFilePino } from '@qianxude/logger';
248
+
249
+ const options = parsePinoRollOptionsEnv({
250
+ LOG_FILE: './logs/app.log',
251
+ LOG_FILE_FREQUENCY: 'daily',
252
+ LOG_FILE_LIMIT_COUNT: '7',
253
+ });
254
+
255
+ if (options.file) {
256
+ const logger = createRollFilePino({ level: 'info', ...options });
257
+ }
258
+ ```
259
+
260
+ #### Parse Duo-Target Options
261
+
262
+ ```typescript
263
+ import { parseDuoTargetPinoOptionsEnv, createDuoTargetPino } from '@qianxude/logger';
264
+
265
+ const options = parseDuoTargetPinoOptionsEnv(process.env);
266
+
267
+ if (options.file) {
268
+ const logger = createDuoTargetPino(options);
269
+ }
270
+ ```
271
+
272
+ ### Environment Variable Constants
273
+
274
+ Use the exported `LOG_ENV_VARS` constant for type-safe env var names:
275
+
276
+ ```typescript
277
+ import { LOG_ENV_VARS } from '@qianxude/logger';
278
+
279
+ console.log(LOG_ENV_VARS.LOG_LEVEL); // 'LOG_LEVEL'
280
+ console.log(LOG_ENV_VARS.LOG_FILE); // 'LOG_FILE'
281
+ ```
282
+
283
+ ### Error Handling
284
+
285
+ The parsing functions throw descriptive errors for invalid values:
286
+
287
+ ```typescript
288
+ import { parsePrettyPinoOptionsEnv } from '@qianxude/logger';
289
+
290
+ try {
291
+ parsePrettyPinoOptionsEnv({ LOG_LEVEL: 'verbose' });
292
+ } catch (err) {
293
+ // Error: Invalid LOG_LEVEL value 'verbose'. Must be one of: fatal, error, warn, info, debug, trace.
294
+ }
295
+ ```
296
+
297
+ ### Graceful Error Handling with `noThrow`
298
+
299
+ When you want to handle missing required env vars gracefully without exceptions, use the `noThrow` option:
300
+
301
+ ```typescript
302
+ import { parsePinoRollOptionsEnv, createRollFilePino } from '@qianxude/logger';
303
+
304
+ const options = parsePinoRollOptionsEnv(process.env, { noThrow: true });
305
+
306
+ if (options._invalid) {
307
+ console.warn('File logging disabled:', options._invalidVars?.join(', '));
308
+ // Fall back to console-only logging
309
+ } else {
310
+ const logger = createRollFilePino({ level: 'info', ...options });
311
+ }
312
+ ```
313
+
314
+ This is useful when file logging is optional and you want to check at runtime whether it's available.
315
+
316
+ ```typescript
317
+ import { parseDuoTargetPinoOptionsEnv, createDuoTargetPino, createPrettyPino } from '@qianxude/logger';
318
+
319
+ const options = parseDuoTargetPinoOptionsEnv(process.env, { noThrow: true });
320
+
321
+ // Use duo-target if LOG_FILE is set, otherwise fall back to pretty console
322
+ const logger = options._invalid
323
+ ? createPrettyPino({ level: 'info' })
324
+ : createDuoTargetPino(options);
325
+ ```
326
+
327
+ ## Integration with @qianxude/shared
328
+
329
+ Use with the shared logger:
330
+
331
+ ```typescript
332
+ import { logger } from '@qianxude/shared';
333
+ import { createDuoTargetPino, PinoLogger } from '@qianxude/logger';
334
+
335
+ // At app startup, configure the shared logger
336
+ const pinoLogger = new PinoLogger(createDuoTargetPino({
337
+ level: 'info',
338
+ file: './logs/app.log',
339
+ frequency: 'daily',
340
+ }));
341
+
342
+ // Use configureLogger to set the delegate
343
+ import { configureLogger } from '@qianxude/shared';
344
+ configureLogger(logger, pinoLogger);
345
+
346
+ // Now all packages using the shared logger will use pino
347
+ logger.info('Application initialized');
348
+ ```
349
+
350
+ ## License
351
+
352
+ MIT
package/dist/env.d.ts ADDED
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Environment variable parsing for logger options.
3
+ *
4
+ * This module provides conventions and parsing functions for configuring
5
+ * logger options via environment variables.
6
+ */
7
+ import type { PrettyPinoOptions, DuoTargetPinoOptions } from './pino-factory.js';
8
+ import type { PinoRollOptions } from './types/index.js';
9
+ /**
10
+ * Options for environment variable parsing functions.
11
+ */
12
+ export interface ParseEnvOptions {
13
+ /**
14
+ * If true, don't throw errors for missing required env vars.
15
+ * Instead, return partial options with `_invalid` metadata.
16
+ * @default false
17
+ */
18
+ noThrow?: boolean;
19
+ }
20
+ /**
21
+ * Metadata properties added to parsed options when `noThrow=true`
22
+ * and required env vars are missing or invalid.
23
+ */
24
+ export interface ParseEnvMeta {
25
+ /**
26
+ * True if any required env var is missing or invalid.
27
+ */
28
+ _invalid?: boolean;
29
+ /**
30
+ * List of required env var names that are missing or invalid.
31
+ */
32
+ _invalidVars?: string[];
33
+ }
34
+ /**
35
+ * Result type for `parsePinoRollOptionsEnv` with `noThrow=true`.
36
+ */
37
+ export type ParsedPinoRollOptions = Partial<PinoRollOptions> & ParseEnvMeta;
38
+ /**
39
+ * Result type for `parseDuoTargetPinoOptionsEnv` with `noThrow=true`.
40
+ */
41
+ export type ParsedDuoTargetPinoOptions = Partial<DuoTargetPinoOptions> & ParseEnvMeta;
42
+ /**
43
+ * Environment variable names for logger configuration.
44
+ */
45
+ export declare const LOG_ENV_VARS: {
46
+ readonly LOG_LEVEL: "LOG_LEVEL";
47
+ readonly LOG_COLORIZE: "LOG_COLORIZE";
48
+ readonly LOG_TRANSLATE_TIME: "LOG_TRANSLATE_TIME";
49
+ readonly LOG_FILE: "LOG_FILE";
50
+ readonly LOG_FILE_SIZE: "LOG_FILE_SIZE";
51
+ readonly LOG_FILE_FREQUENCY: "LOG_FILE_FREQUENCY";
52
+ readonly LOG_FILE_EXTENSION: "LOG_FILE_EXTENSION";
53
+ readonly LOG_FILE_SYMLINK: "LOG_FILE_SYMLINK";
54
+ readonly LOG_FILE_LIMIT_COUNT: "LOG_FILE_LIMIT_COUNT";
55
+ readonly LOG_FILE_DATE_FORMAT: "LOG_FILE_DATE_FORMAT";
56
+ readonly LOG_FILE_MKDIR: "LOG_FILE_MKDIR";
57
+ };
58
+ /**
59
+ * Parse PrettyPinoOptions from environment variables.
60
+ *
61
+ * All environment variables are optional.
62
+ *
63
+ * ## Optional Environment Variables
64
+ *
65
+ * - `LOG_LEVEL`: Log level ('fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'), default: 'info'
66
+ * - `LOG_COLORIZE`: Enable colorized output ('true' | 'false'), default: true
67
+ * - `LOG_TRANSLATE_TIME`: Time format string, default: 'SYS:standard'
68
+ *
69
+ * @param env - Environment variables object (defaults to process.env)
70
+ * @returns Partial PrettyPinoOptions parsed from environment
71
+ * @throws Error if any environment variable value is invalid
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * const options = parsePrettyPinoOptionsEnv(process.env);
76
+ * const logger = createPrettyPino(options);
77
+ * ```
78
+ */
79
+ export declare function parsePrettyPinoOptionsEnv(env?: NodeJS.ProcessEnv): Partial<PrettyPinoOptions>;
80
+ /**
81
+ * Parse PinoRollOptions from environment variables.
82
+ *
83
+ * ## Required Environment Variables
84
+ *
85
+ * - `LOG_FILE`: Log file path. **Required** for file-based logging.
86
+ *
87
+ * ## Optional Environment Variables
88
+ *
89
+ * - `LOG_FILE_SIZE`: Max file size ('10m', '100k', '1g', or number in MB)
90
+ * - `LOG_FILE_FREQUENCY`: Rotation frequency ('daily', 'hourly', or number in ms)
91
+ * - `LOG_FILE_EXTENSION`: File extension to append
92
+ * - `LOG_FILE_SYMLINK`: Create symlink to current log ('true' | 'false'), default: true
93
+ * - `LOG_FILE_LIMIT_COUNT`: Number of rotated files to keep
94
+ * - `LOG_FILE_DATE_FORMAT`: Date format for filename (date-fns format)
95
+ * - `LOG_FILE_MKDIR`: Create parent directories ('true' | 'false'), default: true
96
+ *
97
+ * @param env - Environment variables object (defaults to process.env)
98
+ * @returns Partial PinoRollOptions parsed from environment
99
+ * @throws Error if LOG_FILE is missing/empty (when noThrow=false) or any env var value is invalid
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * // Default: throws if LOG_FILE is missing
104
+ * const options = parsePinoRollOptionsEnv(process.env);
105
+ * if (options.file) {
106
+ * const logger = createRollFilePino({ level: 'info', ...options });
107
+ * }
108
+ * ```
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * // With noThrow: returns partial options with metadata instead of throwing
113
+ * const options = parsePinoRollOptionsEnv(process.env, { noThrow: true });
114
+ * if (options._invalid) {
115
+ * console.log('Missing required env vars:', options._invalidVars);
116
+ * } else {
117
+ * const logger = createRollFilePino({ level: 'info', ...options });
118
+ * }
119
+ * ```
120
+ */
121
+ export declare function parsePinoRollOptionsEnv(env?: NodeJS.ProcessEnv): Partial<PinoRollOptions>;
122
+ export declare function parsePinoRollOptionsEnv(env: NodeJS.ProcessEnv | undefined, options: ParseEnvOptions): ParsedPinoRollOptions;
123
+ /**
124
+ * Parse DuoTargetPinoOptions from environment variables.
125
+ *
126
+ * Combines both pretty options and roll options from environment.
127
+ *
128
+ * ## Required Environment Variables
129
+ *
130
+ * - `LOG_FILE`: Log file path. **Required** for duo-target logging (file output).
131
+ *
132
+ * ## Optional Environment Variables
133
+ *
134
+ * ### Common/Pretty Options
135
+ * - `LOG_LEVEL`: Log level ('fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'), default: 'info'
136
+ * - `LOG_COLORIZE`: Enable colorized output ('true' | 'false'), default: true
137
+ * - `LOG_TRANSLATE_TIME`: Time format string, default: 'SYS:standard'
138
+ *
139
+ * ### File Roll Options
140
+ * - `LOG_FILE_SIZE`: Max file size ('10m', '100k', '1g', or number in MB)
141
+ * - `LOG_FILE_FREQUENCY`: Rotation frequency ('daily', 'hourly', or number in ms)
142
+ * - `LOG_FILE_EXTENSION`: File extension to append
143
+ * - `LOG_FILE_SYMLINK`: Create symlink to current log ('true' | 'false'), default: true
144
+ * - `LOG_FILE_LIMIT_COUNT`: Number of rotated files to keep
145
+ * - `LOG_FILE_DATE_FORMAT`: Date format for filename (date-fns format)
146
+ * - `LOG_FILE_MKDIR`: Create parent directories ('true' | 'false'), default: true
147
+ *
148
+ * @param env - Environment variables object (defaults to process.env)
149
+ * @returns Partial DuoTargetPinoOptions parsed from environment
150
+ * @throws Error if LOG_FILE is missing/empty (when noThrow=false) or any env var value is invalid
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * // Default: throws if LOG_FILE is missing
155
+ * const options = parseDuoTargetPinoOptionsEnv(process.env);
156
+ * if (options.file) {
157
+ * const logger = createDuoTargetPino(options);
158
+ * }
159
+ * ```
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * // With noThrow: returns partial options with metadata instead of throwing
164
+ * const options = parseDuoTargetPinoOptionsEnv(process.env, { noThrow: true });
165
+ * if (options._invalid) {
166
+ * console.log('Missing required env vars:', options._invalidVars);
167
+ * } else {
168
+ * const logger = createDuoTargetPino(options);
169
+ * }
170
+ * ```
171
+ */
172
+ export declare function parseDuoTargetPinoOptionsEnv(env?: NodeJS.ProcessEnv): Partial<DuoTargetPinoOptions>;
173
+ export declare function parseDuoTargetPinoOptionsEnv(env: NodeJS.ProcessEnv | undefined, options: ParseEnvOptions): ParsedDuoTargetPinoOptions;
174
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG,YAAY,CAAC;AAE5E;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC,GAAG,YAAY,CAAC;AAEtF;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;CAef,CAAC;AAuHX;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,iBAAiB,CAAC,CAkB5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;AAC3F,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,EAClC,OAAO,EAAE,eAAe,GACvB,qBAAqB,CAAC;AAiEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AACrG,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,EAClC,OAAO,EAAE,eAAe,GACvB,0BAA0B,CAAC"}