@loggerjs/node 0.0.2 → 0.3.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 (40) hide show
  1. package/README.md +102 -21
  2. package/dist/bullmq-integration.d.ts +9 -0
  3. package/dist/bullmq-integration.d.ts.map +1 -0
  4. package/dist/file-transport.d.ts +6 -2
  5. package/dist/file-transport.d.ts.map +1 -1
  6. package/dist/hapi-integration.d.ts +44 -0
  7. package/dist/hapi-integration.d.ts.map +1 -0
  8. package/dist/http-transport.d.ts +2 -1
  9. package/dist/http-transport.d.ts.map +1 -1
  10. package/dist/index.cjs +857 -261
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts +8 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +852 -264
  15. package/dist/index.js.map +1 -1
  16. package/dist/internal-types.d.ts +2 -0
  17. package/dist/internal-types.d.ts.map +1 -1
  18. package/dist/koa-integration.d.ts +26 -0
  19. package/dist/koa-integration.d.ts.map +1 -0
  20. package/dist/logger-diagnostics.d.ts +14 -0
  21. package/dist/logger-diagnostics.d.ts.map +1 -0
  22. package/dist/nest-integration.d.ts +5 -0
  23. package/dist/nest-integration.d.ts.map +1 -0
  24. package/dist/node-destination.d.ts +32 -0
  25. package/dist/node-destination.d.ts.map +1 -0
  26. package/dist/payload-transforms.d.ts +12 -0
  27. package/dist/payload-transforms.d.ts.map +1 -0
  28. package/dist/prisma-integration.d.ts +6 -0
  29. package/dist/prisma-integration.d.ts.map +1 -0
  30. package/dist/process-integration.d.ts +4 -0
  31. package/dist/process-integration.d.ts.map +1 -1
  32. package/dist/redis-integration.d.ts +7 -0
  33. package/dist/redis-integration.d.ts.map +1 -0
  34. package/dist/rotating-file-transport.d.ts +2 -0
  35. package/dist/rotating-file-transport.d.ts.map +1 -1
  36. package/dist/stdout-transport.d.ts +2 -0
  37. package/dist/stdout-transport.d.ts.map +1 -1
  38. package/dist/worker-transport.d.ts +16 -2
  39. package/dist/worker-transport.d.ts.map +1 -1
  40. package/package.json +42 -2
package/README.md CHANGED
@@ -1,26 +1,48 @@
1
1
  # @loggerjs/node
2
2
 
3
- Node transports, process integrations, diagnostics-channel capture, and AsyncLocalStorage context.
3
+ > Node.js transports, AsyncLocalStorage context, and 16 automatic integrations — re-exports all of `@loggerjs/core`.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@loggerjs/node.svg)](https://www.npmjs.com/package/@loggerjs/node)
6
+ [![license](https://img.shields.io/npm/l/@loggerjs/node)](../../LICENSE)
7
+ [![Node](https://img.shields.io/badge/Node-%E2%89%A522.13-339933?logo=node.js&logoColor=white)](package.json)
8
+
9
+ The Node platform package for [LoggerJS](../../README.md). It re-exports the entire `@loggerjs/core` API and adds stdout/stderr/file/HTTP/syslog/worker transports, an AsyncLocalStorage context bridge, and integrations that turn process crashes, HTTP frameworks, clients, and queues into structured logs — all opt-in.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @loggerjs/node @loggerjs/processors
15
+ ```
16
+
17
+ `@loggerjs/processors` is optional but recommended for redaction, sampling, and enrichment.
18
+
19
+ ## Usage
4
20
 
5
21
  ```ts
6
22
  import {
7
23
  captureProcessIntegration,
8
24
  createLogger,
9
- expressIntegration,
10
- fastifyIntegration,
11
25
  installAsyncLocalStorageContext,
26
+ nodeCompressionPayloadTransform,
12
27
  nodeFetchIntegration,
13
28
  nodeHttpClientIntegration,
14
- nodeSyslogTransport,
15
- rotatingFileTransport,
29
+ nodeHttpTransport,
16
30
  stdoutTransport,
17
31
  } from "@loggerjs/node";
18
32
 
19
- installAsyncLocalStorageContext();
33
+ installAsyncLocalStorageContext(); // once at startup — context follows async execution
20
34
 
21
35
  const logger = createLogger({
22
- name: "api",
23
- transports: [stdoutTransport()],
36
+ category: ["api"],
37
+ level: "info",
38
+ tags: { service: "checkout", env: process.env.NODE_ENV ?? "dev" },
39
+ transports: [
40
+ stdoutTransport(), // one NDJSON line per log
41
+ nodeHttpTransport({
42
+ url: "https://collector.example/logs",
43
+ transformPayload: nodeCompressionPayloadTransform({ format: "brotli" }),
44
+ }),
45
+ ],
24
46
  integrations: [captureProcessIntegration(), nodeHttpClientIntegration(), nodeFetchIntegration()],
25
47
  });
26
48
 
@@ -28,26 +50,85 @@ logger.info("server started", { port: 3000 });
28
50
  await logger.flush();
29
51
  ```
30
52
 
31
- ```ts
32
- const infraLogger = createLogger({
33
- name: "infra",
34
- transports: [nodeSyslogTransport({ host: "127.0.0.1", port: 514, facility: 16 })],
35
- });
36
- ```
53
+ ### HTTP frameworks
37
54
 
38
55
  ```ts
39
- const auditLogger = createLogger({
40
- name: "audit",
41
- transports: [rotatingFileTransport({ path: "audit.log", maxBytes: 10 * 1024 * 1024 })],
42
- });
56
+ app.use(expressIntegration(logger, { captureAll: true })); // Express middleware
57
+ fastify.register(fastifyIntegration(logger, { captureAll: true })); // Fastify plugin
43
58
  ```
44
59
 
60
+ ### Other destinations
61
+
45
62
  ```ts
46
- app.use(expressIntegration(logger, { captureAll: true }));
63
+ import { nodeSyslogTransport, rotatingFileTransport } from "@loggerjs/node";
64
+
65
+ nodeSyslogTransport({ host: "127.0.0.1", port: 514, facility: 16 });
66
+ rotatingFileTransport({ path: "audit.log", maxBytes: 10 * 1024 * 1024 });
47
67
  ```
48
68
 
69
+ ## Transports
70
+
71
+ | Transport | Delivers to |
72
+ | --- | --- |
73
+ | `stdoutTransport` / `stderrTransport` | process streams, one NDJSON line per log, drain-aware flush, optional `minLength` buffering, clean `EPIPE` shutdown |
74
+ | `fileTransport` / `rotatingFileTransport` | files, with `mkdir`/`append` controls, optional `sync: true`, crash-path `flushSync`, and size-based rotation |
75
+ | `nodeHttpTransport` | a collector over `fetch`, wrapped in `batchTransport` (batching + retry) |
76
+ | `nodeSyslogTransport` | RFC 5424 syslog over UDP/TCP |
77
+ | `workerTransport` | a worker thread (encodes batches with a codec, optional buffer transfer, ready/ack lifecycle, fallback on worker failure) |
78
+
79
+ File and process-stream transports share the same internal destination logic for
80
+ write callbacks, drain waiting, `minLength` buffering, close, and sync crash
81
+ flush. Use `await flush()` for normal shutdown. Use `flushSync()` only on fatal
82
+ paths, or configure `fileTransport({ sync: true })` when every write must reach
83
+ the filesystem before the log call returns.
84
+
85
+ `workerTransport()` is fire-and-forget unless you opt into lifecycle checks.
86
+ Use `readyTimeoutMs` for workers that send `{ type: "loggerjs:ready" }`, and
87
+ `ackTimeoutMs` for workers that acknowledge `{ type: "loggerjs:batch", id }`
88
+ with `{ type: "loggerjs:batch:ack", id }`. Pending batches fall back or are
89
+ counted as drops when readiness, posting, ack, or worker exit fails. Set
90
+ `autoEnd: false` for shared workers you close elsewhere. Explicit
91
+ `transport.ready()` / `logger.ready()` waits for the ready handshake when
92
+ `readyTimeoutMs` is configured.
93
+
94
+ Call `installLoggerDiagnosticsChannel()` to publish LoggerJS internal
95
+ diagnostics to Node `diagnostics_channel` channels such as `loggerjs.dispatch`,
96
+ `loggerjs.transport`, `loggerjs.flush`, `loggerjs.encode`, and
97
+ `loggerjs.worker`.
98
+
99
+ ## Integrations (16)
100
+
101
+ | Group | Integrations |
102
+ | --- | --- |
103
+ | **Process & runtime** | `captureProcessIntegration`, `captureCliIntegration`, `diagnosticsChannelIntegration`, `serverlessIntegration` |
104
+ | **HTTP frameworks** | `expressIntegration`, `fastifyIntegration`, `koaIntegration`, `hapiIntegration`, `nestMiddlewareIntegration` |
105
+ | **Clients** | `nodeFetchIntegration`, `nodeHttpClientIntegration`, `redisIntegration`, `prismaIntegration`, `databaseIntegration` |
106
+ | **Queues** | `queueIntegration`, `bullMqIntegration` |
107
+
108
+ `captureProcessIntegration()` turns uncaught exceptions (fatal), unhandled rejections, warnings, and exit into log events; with `exitOnUncaught` it captures the fatal record, calls `flushSync()`, waits for bounded async `flush()`, then exits. Every integration is tagged `source: "integration:<name>"` and guarded against capture loops.
109
+
110
+ ### Context manager
111
+
49
112
  ```ts
50
- fastify.register(fastifyIntegration(logger, { captureAll: true }));
113
+ import { installAsyncLocalStorageContext, withContext } from "@loggerjs/node";
114
+
115
+ installAsyncLocalStorageContext(); // once at startup
116
+ await withContext({ requestId: "req_123" }, async () => {
117
+ logger.info("request started"); // { requestId } follows across awaits
118
+ });
51
119
  ```
52
120
 
53
- Subpaths expose `transport-http`, `transport-file`, `transport-rotating-file`, `transport-stdout`, `transport-syslog`, `transport-worker`, `integration-process`, `integration-cli`, `integration-queue`, `integration-serverless`, `integration-database`, `integration-express`, `integration-fastify`, `integration-fetch`, `integration-http-client`, `integration-diagnostics`, and `context`.
121
+ ## Subpath exports
122
+
123
+ Transports — `transport-stdout` · `transport-file` · `transport-rotating-file` · `transport-http` · `transport-syslog` · `transport-worker` · `payload-transforms` · `logger-diagnostics` · `context`
124
+
125
+ Integrations — `integration-process` · `integration-cli` · `integration-diagnostics` · `integration-serverless` · `integration-express` · `integration-fastify` · `integration-koa` · `integration-nest` · `integration-hapi` · `integration-fetch` · `integration-http-client` · `integration-redis` · `integration-prisma` · `integration-database` · `integration-queue` · `integration-bullmq`
126
+
127
+ ## Documentation
128
+
129
+ - [Transports](../../docs/TRANSPORTS.md) · [Integrations](../../docs/INTEGRATIONS.md) · [Operations](../../docs/OPERATIONS.md)
130
+ - [Getting Started](../../docs/GETTING-STARTED.md) · [LoggerJS root README](../../README.md)
131
+
132
+ ## License
133
+
134
+ [MIT](../../LICENSE) © JS Kits
@@ -0,0 +1,9 @@
1
+ import { type QueueClientLike, type QueueIntegrationOptions } from "./queue-integration.js";
2
+ export interface BullMqIntegrationOptions extends Omit<QueueIntegrationOptions, "client" | "system" | "methods" | "getQueueName"> {
3
+ client: QueueClientLike & {
4
+ name?: string;
5
+ };
6
+ methods?: readonly string[];
7
+ }
8
+ export declare function bullMqIntegration(options: BullMqIntegrationOptions): import("@loggerjs/core").Integration;
9
+ //# sourceMappingURL=bullmq-integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bullmq-integration.d.ts","sourceRoot":"","sources":["../src/bullmq-integration.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC7B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,wBAAyB,SAAQ,IAAI,CACpD,uBAAuB,EACvB,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,cAAc,CACjD;IACC,MAAM,EAAE,eAAe,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AASD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,wCASlE"}
@@ -1,14 +1,18 @@
1
- import { type WriteStream } from "fs";
2
1
  import { type Codec, type LoggerLevel, type Transport } from "@loggerjs/core";
2
+ import type { WriteStream } from "fs";
3
3
  export interface FileTransportOptions {
4
4
  path: string;
5
5
  name?: string;
6
6
  codec?: Codec<string | Uint8Array>;
7
7
  minLevel?: LoggerLevel;
8
8
  flags?: string;
9
+ append?: boolean;
10
+ mkdir?: boolean;
11
+ sync?: boolean;
12
+ minLength?: number;
9
13
  }
10
14
  export interface FileTransport extends Transport {
11
- stream: WriteStream;
15
+ stream?: WriteStream;
12
16
  flushSync: () => void;
13
17
  }
14
18
  export declare function fileTransport(options: FileTransportOptions): FileTransport;
@@ -1 +1 @@
1
- {"version":3,"file":"file-transport.d.ts","sourceRoot":"","sources":["../src/file-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,WAAW,EAAE,MAAM,IAAI,CAAC;AACzF,OAAO,EAGL,KAAK,KAAK,EAEV,KAAK,WAAW,EAChB,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CA8D1E"}
1
+ {"version":3,"file":"file-transport.d.ts","sourceRoot":"","sources":["../src/file-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,KAAK,EAEV,KAAK,WAAW,EAChB,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AAGtC,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAwB1E"}
@@ -0,0 +1,44 @@
1
+ import { type LoggerLevel, type LoggerLike } from "@loggerjs/core";
2
+ export interface HapiRequestLike {
3
+ method?: string;
4
+ path?: string;
5
+ url?: {
6
+ pathname?: string;
7
+ };
8
+ info?: {
9
+ id?: string;
10
+ received?: number;
11
+ remoteAddress?: string;
12
+ };
13
+ response?: {
14
+ statusCode?: number;
15
+ } | {
16
+ isBoom?: boolean;
17
+ output?: {
18
+ statusCode?: number;
19
+ };
20
+ };
21
+ app?: Record<string, unknown>;
22
+ }
23
+ export interface HapiToolkitLike {
24
+ continue: unknown;
25
+ }
26
+ export interface HapiServerLike {
27
+ ext: (event: "onRequest", handler: (request: HapiRequestLike, h: HapiToolkitLike) => unknown) => unknown;
28
+ events?: {
29
+ on?: (event: "response", handler: (request: HapiRequestLike) => void) => unknown;
30
+ };
31
+ }
32
+ export interface HapiIntegrationOptions {
33
+ minStatus?: number;
34
+ captureAll?: boolean;
35
+ bindContext?: boolean;
36
+ getRequestId?: (request: HapiRequestLike) => string | undefined;
37
+ context?: (request: HapiRequestLike) => Record<string, unknown> | undefined;
38
+ level?: (status: number, request: HapiRequestLike) => LoggerLevel;
39
+ }
40
+ export declare function hapiIntegration(logger: LoggerLike, options?: HapiIntegrationOptions): {
41
+ name: string;
42
+ register(server: HapiServerLike): void;
43
+ };
44
+ //# sourceMappingURL=hapi-integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hapi-integration.d.ts","sourceRoot":"","sources":["../src/hapi-integration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,WAAW,EAAE,KAAK,UAAU,EAAgB,MAAM,gBAAgB,CAAC;AAE9F,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5B,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,QAAQ,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE;YAAE,UAAU,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IAC5F,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,CACH,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,CAAC,EAAE,eAAe,KAAK,OAAO,KAC/D,OAAO,CAAC;IACb,MAAM,CAAC,EAAE;QACP,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,KAAK,OAAO,CAAC;KAClF,CAAC;CACH;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,GAAG,SAAS,CAAC;IAChE,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC5E,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,KAAK,WAAW,CAAC;CACnE;AAkBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,sBAA2B;;qBAQnE,cAAc;EAsClC"}
@@ -1,4 +1,4 @@
1
- import { type BatchTransportOptions, type Codec, type LoggerLevel, type Transport } from "@loggerjs/core";
1
+ import { type BatchTransportOptions, type Codec, type LoggerLevel, type PayloadTransform, type Transport } from "@loggerjs/core";
2
2
  export interface NodeHttpTransportOptions extends BatchTransportOptions {
3
3
  url: string;
4
4
  name?: string;
@@ -7,6 +7,7 @@ export interface NodeHttpTransportOptions extends BatchTransportOptions {
7
7
  codec?: Codec<string | Uint8Array>;
8
8
  minLevel?: LoggerLevel;
9
9
  fetchFn?: typeof fetch;
10
+ transformPayload?: PayloadTransform | readonly PayloadTransform[];
10
11
  }
11
12
  export declare function nodeHttpTransport(options: NodeHttpTransportOptions): Transport;
12
13
  //# sourceMappingURL=http-transport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-transport.d.ts","sourceRoot":"","sources":["../src/http-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,KAAK,EAEV,KAAK,WAAW,EAChB,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,wBAAyB,SAAQ,qBAAqB;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAOD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,SAAS,CAsC9E"}
1
+ {"version":3,"file":"http-transport.d.ts","sourceRoot":"","sources":["../src/http-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,KAAK,EAGV,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,wBAAyB,SAAQ,qBAAqB;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,SAAS,gBAAgB,EAAE,CAAC;CACnE;AAOD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,SAAS,CAiD9E"}