@gyramais/log-transport 0.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.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # @gyramais/log-transport
2
+
3
+ Como o log **chega ao destino**: monta os transports (Console, File, Loki) a
4
+ partir de configuração explícita, e registra exceção fatal com flush antes do
5
+ exit.
6
+
7
+ ```ts
8
+ import {
9
+ buildNestLogger,
10
+ redirectConsoleToNestLogger,
11
+ registerFatalHandlers,
12
+ } from '@gyramais/log-transport';
13
+
14
+ const { logger, lokiTransport } = buildNestLogger({
15
+ service: 'gyra-core', // obrigatório
16
+ level: 'debug',
17
+ pretty: false,
18
+ loki: { host: 'http://loki:3100', basicAuth: '...' },
19
+ });
20
+
21
+ registerFatalHandlers(logger, lokiTransport);
22
+ ```
23
+
24
+ ## Serviço com mais de um logger
25
+
26
+ Se o serviço monta **vários loggers no mesmo processo**, crie o transport uma vez e
27
+ compartilhe. O worker do gyra-core monta quatro — um para a app e três para
28
+ conexões RMQ (`src/main.worker.ts`):
29
+
30
+ ```ts
31
+ import { createLokiTransport, buildNestLogger } from '@gyramais/log-transport';
32
+
33
+ // UM transport para o processo inteiro
34
+ const lokiTransport = createLokiTransport({
35
+ service: 'gyra-core',
36
+ host: process.env.LOKI_URL,
37
+ basicAuth: process.env.LOKI_BASIC_AUTH,
38
+ });
39
+
40
+ const { logger } = buildNestLogger({ service: 'gyra-core', lokiTransport });
41
+
42
+ // e o mesmo transport nos outros loggers
43
+ const rmqLogger = buildNestLogger({ service: 'gyra-core', lokiTransport }).logger;
44
+ ```
45
+
46
+ Não é conveniência, é correção: com um transport por logger há **um batcher por
47
+ logger**, e o `registerFatalHandlers` drena apenas o que recebeu — as linhas
48
+ bufferizadas nos outros morrem com o processo, que é o sintoma que a entrega
49
+ confiável existe para impedir.
50
+
51
+ Passar `loki` e `lokiTransport` juntos lança erro: um pede transport novo, o outro
52
+ pede reuso, e escolher em silêncio deixaria o consumidor com dois batchers sem
53
+ saber.
54
+
55
+ **`registerFatalHandlers` é chamado UMA vez por processo**, com o transport
56
+ compartilhado — não um por logger. Handler de `uncaughtException` é recurso único
57
+ do processo: registros duplicados produziriam linhas `[FATAL]` repetidas e flushes
58
+ concorrentes no mesmo transport. O pacote ignora a chamada extra e emite
59
+ `[FATAL-HANDLERS-DUP]` no stdout, mas o certo é chamar uma vez. Ele devolve uma
60
+ função de `unregister`, útil em teste.
61
+
62
+ ## O pacote não lê `process.env`
63
+
64
+ Toda configuração entra por parâmetro. Três razões, em ordem de peso:
65
+
66
+ 1. **Correção.** Os serviços não leem os mesmos envs: `gyra-export` lê `APP_NAME`,
67
+ e `gyra-file` e `gyra-websocket-server` não leem `ENVIRONMENT` nem `LOG_LEVEL`.
68
+ Um helper de env compartilhado mudaria o comportamento desses em silêncio.
69
+ 2. **Testabilidade.** Ler env obriga o teste a mutar estado global do processo, e
70
+ a suíte passa a depender da ordem de execução.
71
+ 3. **Contrato visível.** A assinatura diz o que o pacote precisa.
72
+
73
+ Quem lê env é o serviço, no entrypoint. Há um teste que compila o pacote com
74
+ `removeComments` e falha se `process.env` aparecer no código emitido.
75
+
76
+ ## Detalhes que não são arbitrários
77
+
78
+ - **`service` é obrigatório no tipo.** O `gyra-nest-boilerplate` rotula hoje
79
+ `service: 'gyra-credit-policy'` por cópia de arquivo, e todo serviço novo nasce
80
+ logando sob o rótulo errado. Agora isso é erro de compilação.
81
+ - **A presença de `loki` é o gate de envio**, no lugar de um booleano em string.
82
+ - **`app` tem default `saas`**: as consultas e dashboards em produção usam
83
+ `{app="saas", service="gyra-x"}`.
84
+ - **`registerFatalHandlers` é opt-in.** Instalar handler de `uncaughtException` é
85
+ efeito colateral no ciclo de vida do processo — importar um pacote não deve
86
+ mudar como o processo morre.
87
+ - **`winston-loki: ^6.1.6`** é dependência daqui. São dois bugs corrigidos lá:
88
+ metadado estruturado não-string fazia o Loki responder 400 e descartar o lote
89
+ inteiro, e a resposta de erro era tratada como sucesso, tornando a perda 100%
90
+ silenciosa. O consumidor recebe a correção transitivamente.
@@ -0,0 +1,5 @@
1
+ export interface ConsoleRedirectOptions {
2
+ pretty?: boolean;
3
+ }
4
+ export declare const redirectConsoleToNestLogger: (options?: ConsoleRedirectOptions) => void;
5
+ //# sourceMappingURL=console-redirect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-redirect.d.ts","sourceRoot":"","sources":["../src/console-redirect.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,sBAAsB;IAOrC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,2BAA2B,GACtC,UAAS,sBAA2B,KACnC,IAwBF,CAAC"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.redirectConsoleToNestLogger = void 0;
5
+ /*
6
+ Redireciona `console.*` para o Logger do Nest, para que biblioteca de terceiro (e
7
+ código legado) que escreve em `console` também chegue aos transports.
8
+
9
+ Saiu do `buildNestLogger` de propósito: trocar os métodos de `console` é efeito
10
+ colateral global do processo, e importar um pacote não deve mudar o que
11
+ `console.log` faz no serviço inteiro. Quem quer, chama.
12
+ */
13
+ const common_1 = require("@nestjs/common");
14
+ const redirectConsoleToNestLogger = (options = {}) => {
15
+ console.log = (message, ...optionalParams) => {
16
+ if (options.pretty) {
17
+ common_1.Logger.log(message, ...optionalParams);
18
+ }
19
+ else {
20
+ common_1.Logger.debug(message, ...optionalParams);
21
+ }
22
+ };
23
+ console.error = (message, data, ...optionalParams) => {
24
+ common_1.Logger.error(message, { data }, ...optionalParams);
25
+ };
26
+ console.warn = (message, ...optionalParams) => {
27
+ common_1.Logger.warn(message, ...optionalParams);
28
+ };
29
+ console.debug = (message, ...optionalParams) => {
30
+ common_1.Logger.debug(message, ...optionalParams);
31
+ };
32
+ console.info = (message, ...optionalParams) => {
33
+ common_1.Logger.verbose(message, ...optionalParams);
34
+ };
35
+ };
36
+ exports.redirectConsoleToNestLogger = redirectConsoleToNestLogger;
37
+ //# sourceMappingURL=console-redirect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-redirect.js","sourceRoot":"","sources":["../src/console-redirect.ts"],"names":[],"mappings":";AAAA,uDAAuD;;;AAEvD;;;;;;;EAOE;AAEF,2CAAwC;AAYjC,MAAM,2BAA2B,GAAG,CACzC,UAAkC,EAAE,EAC9B,EAAE;IACR,OAAO,CAAC,GAAG,GAAG,CAAC,OAAa,EAAE,GAAG,cAAqB,EAAE,EAAE;QACxD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,eAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,eAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CAAC,KAAK,GAAG,CAAC,OAAa,EAAE,IAAU,EAAE,GAAG,cAAqB,EAAE,EAAE;QACtE,eAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,cAAc,CAAC,CAAC;IACrD,CAAC,CAAC;IAEF,OAAO,CAAC,IAAI,GAAG,CAAC,OAAa,EAAE,GAAG,cAAqB,EAAE,EAAE;QACzD,eAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;IAC1C,CAAC,CAAC;IAEF,OAAO,CAAC,KAAK,GAAG,CAAC,OAAa,EAAE,GAAG,cAAqB,EAAE,EAAE;QAC1D,eAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;IAC3C,CAAC,CAAC;IAEF,OAAO,CAAC,IAAI,GAAG,CAAC,OAAa,EAAE,GAAG,cAAqB,EAAE,EAAE;QACzD,eAAM,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC;IAC7C,CAAC,CAAC;AACJ,CAAC,CAAC;AA1BW,QAAA,2BAA2B,+BA0BtC"}
@@ -0,0 +1,21 @@
1
+ import { LoggerService } from '@nestjs/common';
2
+ import LokiTransport = require('winston-loki');
3
+ interface FatalHandlersOptions {
4
+ flushTimeoutMs?: number;
5
+ exit?: (code: number) => void;
6
+ }
7
+ /**
8
+ * Entrega ao Loki o que está bufferizado e só então devolve o controle,
9
+ * desistindo depois de `timeoutMs`. Nunca lança: no caminho de saída do
10
+ * processo, uma falha de flush não pode virar um segundo erro.
11
+ *
12
+ * O `flush()` do winston-loki apenas *aguarda* o laço periódico de envio, que
13
+ * pode estar a um intervalo inteiro de distância; quem está encerrando precisa
14
+ * *forçar* o envio agora. `close()` faz exatamente isso — interrompe a espera do
15
+ * laço e manda o batch pendente — e o `flush()` seguinte resolve quando esse
16
+ * envio termina.
17
+ */
18
+ declare const flushLokiTransport: (transport: LokiTransport | undefined, timeoutMs?: number) => Promise<void>;
19
+ declare const registerFatalHandlers: (logger: LoggerService, transport: LokiTransport | undefined, options?: FatalHandlersOptions) => (() => void);
20
+ export { registerFatalHandlers, flushLokiTransport };
21
+ //# sourceMappingURL=fatal-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fatal-handlers.d.ts","sourceRoot":"","sources":["../src/fatal-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,aAAa,GAAG,QAAQ,cAAc,CAAC,CAAC;AAU/C,UAAU,oBAAoB;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAwBD;;;;;;;;;;GAUG;AACH,QAAA,MAAM,kBAAkB,GACtB,WAAW,aAAa,GAAG,SAAS,EACpC,kBAA4B,KAC3B,OAAO,CAAC,IAAI,CA6Bd,CAAC;AAmDF,QAAA,MAAM,qBAAqB,GACzB,QAAQ,aAAa,EACrB,WAAW,aAAa,GAAG,SAAS,EACpC,UAAS,oBAAyB,KACjC,CAAC,MAAM,IAAI,CA6Fb,CAAC;AAEF,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.flushLokiTransport = exports.registerFatalHandlers = void 0;
4
+ /*
5
+ O flush no caminho do crash é limitado no tempo de propósito: um Loki
6
+ indisponível não pode impedir o pod de morrer e reiniciar.
7
+ */
8
+ const FLUSH_TIMEOUT_MS = 2000;
9
+ /*
10
+ Winston entrega a linha ao transport por stream, num tick posterior ao
11
+ `logger.error()`. Sem esperar por essa entrega, o batch ainda está vazio quando
12
+ o flush é pedido — e aí o flush resolve na hora e o processo sai levando a linha
13
+ embora. O transport emite `logged` quando recebe.
14
+ */
15
+ const LOGGED_HANDOFF_TIMEOUT_MS = 100;
16
+ const waitForHandoff = (transport, timeoutMs) => new Promise((resolve) => {
17
+ const done = () => {
18
+ clearTimeout(timer);
19
+ transport.off?.('logged', done);
20
+ resolve();
21
+ };
22
+ const timer = setTimeout(done, timeoutMs);
23
+ transport.once?.('logged', done);
24
+ });
25
+ /**
26
+ * Entrega ao Loki o que está bufferizado e só então devolve o controle,
27
+ * desistindo depois de `timeoutMs`. Nunca lança: no caminho de saída do
28
+ * processo, uma falha de flush não pode virar um segundo erro.
29
+ *
30
+ * O `flush()` do winston-loki apenas *aguarda* o laço periódico de envio, que
31
+ * pode estar a um intervalo inteiro de distância; quem está encerrando precisa
32
+ * *forçar* o envio agora. `close()` faz exatamente isso — interrompe a espera do
33
+ * laço e manda o batch pendente — e o `flush()` seguinte resolve quando esse
34
+ * envio termina.
35
+ */
36
+ const flushLokiTransport = async (transport, timeoutMs = FLUSH_TIMEOUT_MS) => {
37
+ if (!transport?.flush)
38
+ return;
39
+ let timer;
40
+ const drain = async () => {
41
+ await waitForHandoff(transport, Math.min(LOGGED_HANDOFF_TIMEOUT_MS, timeoutMs));
42
+ transport.close?.();
43
+ await transport.flush();
44
+ };
45
+ try {
46
+ await Promise.race([
47
+ drain(),
48
+ new Promise((resolve) => {
49
+ timer = setTimeout(resolve, timeoutMs);
50
+ }),
51
+ ]);
52
+ }
53
+ catch {
54
+ /*
55
+ Silencioso por escolha: quem chama já está encerrando o processo e a linha
56
+ de erro do flush não teria para onde ir.
57
+ */
58
+ }
59
+ finally {
60
+ if (timer)
61
+ clearTimeout(timer);
62
+ }
63
+ };
64
+ exports.flushLokiTransport = flushLokiTransport;
65
+ const describeReason = (reason) => {
66
+ if (reason instanceof Error) {
67
+ return {
68
+ message: `${reason.name}: ${reason.message}`,
69
+ stack: reason.stack ?? '',
70
+ };
71
+ }
72
+ return { message: String(reason), stack: '' };
73
+ };
74
+ /**
75
+ * Registra os handlers de exceção fatal do processo.
76
+ *
77
+ * Sem eles, o handler padrão do Node imprime a stack direto no stderr — sem
78
+ * passar por nenhum transport do winston — e o crash fica invisível na
79
+ * observabilidade, encontrável apenas por `kubectl logs --previous`.
80
+ *
81
+ * O processo continua morrendo: depois de uma exceção não capturada o estado é
82
+ * indefinido, e reiniciar é o comportamento correto. O que muda é o crash passar
83
+ * a ficar registrado.
84
+ */
85
+ /*
86
+ Handler de exceção fatal é recurso ÚNICO do processo, e o guard de reentrância
87
+ vive no closure de cada chamada — então dois registros são dois guards, e um
88
+ único erro fatal dispara os dois. Medido: quatro registros produziram quatro
89
+ linhas `[FATAL]`, quatro flushes concorrentes no mesmo transport e quatro
90
+ chamadas de exit.
91
+
92
+ O risco cresceu quando o pacote passou a suportar vários loggers com um
93
+ transport compartilhado (ver `createLokiTransport`): fica natural registrar um
94
+ handler por logger.
95
+
96
+ Este é o único estado global do pacote, e a distinção importa: ele descreve o
97
+ PROCESSO — se os handlers já estão instalados nele —, não configuração. Config
98
+ em variável global é o que `options.ts` recusa; isto é outra coisa.
99
+
100
+ `Symbol.for` e não um `let` de módulo: sobrevive a duas instâncias do pacote na
101
+ árvore de dependências, que é exatamente o caso em que o registro duplicado
102
+ aconteceria sem ninguém perceber.
103
+ */
104
+ const REGISTERED = Symbol.for('@gyramais/log-transport.fatalHandlersRegistered');
105
+ const registerFatalHandlers = (logger, transport, options = {}) => {
106
+ const target = globalThis;
107
+ if (target[REGISTERED]) {
108
+ /*
109
+ Avisa em vez de ignorar em silêncio: registro duplicado é erro de
110
+ integração do consumidor, e descobrir isso durante um crash em produção é
111
+ o pior momento possível.
112
+ */
113
+ process.stdout.write(`${JSON.stringify({
114
+ level: 'warn',
115
+ message: '[FATAL-HANDLERS-DUP] registerFatalHandlers chamado mais de uma vez ' +
116
+ 'neste processo; a chamada extra foi ignorada. Chame uma vez, com o ' +
117
+ 'transport compartilhado.',
118
+ timestamp: new Date().toISOString(),
119
+ })}\n`);
120
+ return () => undefined;
121
+ }
122
+ target[REGISTERED] = true;
123
+ const flushTimeoutMs = options.flushTimeoutMs ?? FLUSH_TIMEOUT_MS;
124
+ const exit = options.exit ?? ((code) => process.exit(code));
125
+ /*
126
+ Um segundo erro fatal enquanto o primeiro ainda está sendo flushado não pode
127
+ reiniciar o ciclo — senão o processo nunca chega ao exit.
128
+ */
129
+ let handling = false;
130
+ const handleFatal = async (origin, reason) => {
131
+ if (handling)
132
+ return;
133
+ handling = true;
134
+ /*
135
+ O exit vai no finally porque o guard de reentrância já está levantado: se
136
+ registrar ou flushar falhasse aqui, uma segunda passagem sairia na primeira
137
+ linha e o processo ficaria pendurado — travado num estado indefinido em vez
138
+ de reiniciar.
139
+ */
140
+ try {
141
+ const { message, stack } = describeReason(reason);
142
+ logger.error(`[FATAL] ${origin}: ${message}`, {
143
+ origin,
144
+ /*
145
+ String, não array: o Loki exige valores string na structured metadata e
146
+ rejeita a requisição inteira quando recebe outra coisa.
147
+ */
148
+ stack,
149
+ });
150
+ await flushLokiTransport(transport, flushTimeoutMs);
151
+ }
152
+ finally {
153
+ exit(1);
154
+ }
155
+ };
156
+ /*
157
+ O catch fecha o laço: sem ele, uma falha dentro do handler de
158
+ unhandledRejection viraria uma nova unhandledRejection.
159
+ */
160
+ const onFatal = (origin) => (reason) => {
161
+ handleFatal(origin, reason).catch(() => {
162
+ /*
163
+ Vazio de propósito: o processo já está encerrando, e uma linha de erro
164
+ daqui não teria para onde ir.
165
+ */
166
+ });
167
+ };
168
+ const onUncaught = onFatal('uncaughtException');
169
+ const onRejection = onFatal('unhandledRejection');
170
+ process.on('uncaughtException', onUncaught);
171
+ process.on('unhandledRejection', onRejection);
172
+ /*
173
+ Desfaz o registro e libera o guard. Em produção o processo não desregistra —
174
+ ele morre. Isto existe para o teste poder registrar de novo na próxima
175
+ execução, e é o que torna o guard acima verificável em vez de um efeito
176
+ global impossível de exercitar.
177
+ */
178
+ return () => {
179
+ process.off('uncaughtException', onUncaught);
180
+ process.off('unhandledRejection', onRejection);
181
+ delete target[REGISTERED];
182
+ };
183
+ };
184
+ exports.registerFatalHandlers = registerFatalHandlers;
185
+ //# sourceMappingURL=fatal-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fatal-handlers.js","sourceRoot":"","sources":["../src/fatal-handlers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { buildNestLogger, createLokiTransport } from './logger';
2
+ export type { BuiltLogger, CreateLokiTransportOptions } from './logger';
3
+ export { redirectConsoleToNestLogger } from './console-redirect';
4
+ export type { ConsoleRedirectOptions } from './console-redirect';
5
+ export { registerFatalHandlers, flushLokiTransport } from './fatal-handlers';
6
+ export { DEFAULT_APP_LABEL, DEFAULT_LEVEL, DEFAULT_LOKI_INTERVAL_SECONDS, DEFAULT_LOKI_TIMEOUT_MS, } from './options';
7
+ export type { LoggerOptions, LokiOptions, FileOptions, LokiTransportLike, } from './options';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAChE,YAAY,EAAE,WAAW,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAExE,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,aAAa,EACb,WAAW,EACX,WAAW,EACX,iBAAiB,GAClB,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ /*
3
+ Entrega de log dos serviços gyra-*: monta os transports (Console, File, Loki) a
4
+ partir de configuração explícita, e registra exceção fatal com flush antes do
5
+ exit.
6
+
7
+ Nada aqui lê variável de ambiente — ver `options.ts` para o porquê. Quem lê é o
8
+ serviço, no entrypoint dele.
9
+
10
+ O exemplo de uso completo está no README do pacote. Ele NÃO fica aqui de
11
+ propósito: o exemplo mostra o consumidor lendo `process.env`, e como o build
12
+ publica os comentários (`removeComments: false`), essas linhas apareceriam no
13
+ `dist/` e fariam um grep de auditoria acusar o pacote de ler ambiente.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.DEFAULT_LOKI_TIMEOUT_MS = exports.DEFAULT_LOKI_INTERVAL_SECONDS = exports.DEFAULT_LEVEL = exports.DEFAULT_APP_LABEL = exports.flushLokiTransport = exports.registerFatalHandlers = exports.redirectConsoleToNestLogger = exports.createLokiTransport = exports.buildNestLogger = void 0;
17
+ var logger_1 = require("./logger");
18
+ Object.defineProperty(exports, "buildNestLogger", { enumerable: true, get: function () { return logger_1.buildNestLogger; } });
19
+ Object.defineProperty(exports, "createLokiTransport", { enumerable: true, get: function () { return logger_1.createLokiTransport; } });
20
+ var console_redirect_1 = require("./console-redirect");
21
+ Object.defineProperty(exports, "redirectConsoleToNestLogger", { enumerable: true, get: function () { return console_redirect_1.redirectConsoleToNestLogger; } });
22
+ var fatal_handlers_1 = require("./fatal-handlers");
23
+ Object.defineProperty(exports, "registerFatalHandlers", { enumerable: true, get: function () { return fatal_handlers_1.registerFatalHandlers; } });
24
+ Object.defineProperty(exports, "flushLokiTransport", { enumerable: true, get: function () { return fatal_handlers_1.flushLokiTransport; } });
25
+ var options_1 = require("./options");
26
+ Object.defineProperty(exports, "DEFAULT_APP_LABEL", { enumerable: true, get: function () { return options_1.DEFAULT_APP_LABEL; } });
27
+ Object.defineProperty(exports, "DEFAULT_LEVEL", { enumerable: true, get: function () { return options_1.DEFAULT_LEVEL; } });
28
+ Object.defineProperty(exports, "DEFAULT_LOKI_INTERVAL_SECONDS", { enumerable: true, get: function () { return options_1.DEFAULT_LOKI_INTERVAL_SECONDS; } });
29
+ Object.defineProperty(exports, "DEFAULT_LOKI_TIMEOUT_MS", { enumerable: true, get: function () { return options_1.DEFAULT_LOKI_TIMEOUT_MS; } });
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;EAYE;;;AAEF,mCAAgE;AAAvD,yGAAA,eAAe,OAAA;AAAE,6GAAA,mBAAmB,OAAA;AAG7C,uDAAiE;AAAxD,+HAAA,2BAA2B,OAAA;AAGpC,mDAA6E;AAApE,uHAAA,qBAAqB,OAAA;AAAE,oHAAA,kBAAkB,OAAA;AAElD,qCAKmB;AAJjB,4GAAA,iBAAiB,OAAA;AACjB,wGAAA,aAAa,OAAA;AACb,wHAAA,6BAA6B,OAAA;AAC7B,kHAAA,uBAAuB,OAAA"}
@@ -0,0 +1,36 @@
1
+ import LokiTransport = require('winston-loki');
2
+ import type { LoggerService } from '@nestjs/common';
3
+ import { LoggerOptions, LokiOptions } from './options';
4
+ export interface BuiltLogger {
5
+ logger: LoggerService;
6
+ lokiTransport?: LokiTransport;
7
+ }
8
+ export interface CreateLokiTransportOptions extends LokiOptions {
9
+ service: string;
10
+ app?: string;
11
+ instance?: string;
12
+ pretty?: boolean;
13
+ }
14
+ /**
15
+ * Constrói o transport do Loki isoladamente, para o consumidor COMPARTILHAR um
16
+ * único batcher entre vários loggers do mesmo processo.
17
+ *
18
+ * Use isto quando o serviço monta mais de um logger — o worker do gyra-core monta
19
+ * quatro. Com um transport por logger, cada um tem seu batcher, e o flush de
20
+ * exceção fatal cobre só um deles.
21
+ *
22
+ * const lokiTransport = createLokiTransport({ service: 'gyra-core', host });
23
+ * const { logger } = buildNestLogger({ service: 'gyra-core', lokiTransport });
24
+ * // ...e o mesmo `lokiTransport` nos outros loggers do processo
25
+ */
26
+ export declare const createLokiTransport: (options: CreateLokiTransportOptions) => LokiTransport;
27
+ /**
28
+ * Monta o logger do Nest com os transports pedidos e devolve, junto, o transport
29
+ * do Loki — necessário para `registerFatalHandlers` dar flush antes do exit.
30
+ *
31
+ * Não instala nada no processo: nem handler de exceção, nem redirecionamento de
32
+ * `console.*`. Esses são opt-in explícito, porque mexem no ciclo de vida do
33
+ * processo.
34
+ */
35
+ export declare const buildNestLogger: (options: LoggerOptions) => BuiltLogger;
36
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AA4BA,OAAO,aAAa,GAAG,QAAQ,cAAc,CAAC,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD,OAAO,EAKL,aAAa,EACb,WAAW,EACZ,MAAM,WAAW,CAAC;AAQnB,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,aAAa,CAAC;IAOtB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAoED,MAAM,WAAW,0BAA2B,SAAQ,WAAW;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB,GAC9B,SAAS,0BAA0B,KAClC,aAeF,CAAC;AAkCF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,aAAa,KAAG,WAoDxD,CAAC"}
package/dist/logger.js ADDED
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.buildNestLogger = exports.createLokiTransport = void 0;
5
+ /*
6
+ PROVENIÊNCIA: derivado de gyra-core/src/common/logger/LoggerFactory.ts na branch
7
+ GYR-1555 (tag `backup/GYR-1555-pre-close`), onde o defeito de entrega ao Loki foi
8
+ diagnosticado e provado contra um Loki 3.0 real: antes, 21 linhas enviadas e 0
9
+ chegando, sem nenhum erro reportado; depois, 21 de 21.
10
+
11
+ Diferenças em relação à fonte, todas deliberadas:
12
+
13
+ - Nenhuma leitura de `process.env` — ver `options.ts`.
14
+ - Sem singleton de módulo. A fonte guardava o transport numa variável de módulo
15
+ para o handler de exceção fatal alcançá-lo; aqui ele volta no retorno, porque
16
+ estado global no pacote é o mesmo problema de ler env com outra roupa.
17
+
18
+ O singleton da fonte, porém, resolvia um problema real que o retorno sozinho
19
+ não resolve: serviço que monta VÁRIOS loggers no mesmo processo — o worker do
20
+ gyra-core monta quatro — teria um batcher por logger, e o flush de exceção
21
+ fatal drenaria só um deles. Para isso existe `createLokiTransport`: o
22
+ consumidor cria o transport uma vez e passa o mesmo em `lokiTransport`. O
23
+ compartilhamento fica explícito no call site, em vez de escondido no módulo.
24
+ - O redirecionamento de `console.*` saiu para `console-redirect.ts`, como opt-in
25
+ explícito: é efeito colateral de processo, e importar um pacote não deve mudar
26
+ o que `console.log` faz.
27
+ */
28
+ const winston_1 = require("winston");
29
+ const nest_winston_1 = require("nest-winston");
30
+ const LokiTransport = require("winston-loki");
31
+ const os_1 = require("os");
32
+ const options_1 = require("./options");
33
+ const formatMeta = (meta) => {
34
+ const splat = meta[Symbol.for('splat')];
35
+ if (splat?.[0].stack) {
36
+ return ' ' + JSON.stringify(splat?.[0].stack?.[0], null, 2);
37
+ }
38
+ if (splat && splat.length) {
39
+ const obj = splat?.[0]?.context ?? splat;
40
+ if (typeof obj === 'string') {
41
+ return ' ' + obj;
42
+ }
43
+ if (Array.isArray(obj) &&
44
+ obj.length === 1 &&
45
+ typeof obj[0] === 'object' &&
46
+ Object.keys(obj[0]).length === 1 &&
47
+ obj[0].context === undefined) {
48
+ return '';
49
+ }
50
+ return ' ' + JSON.stringify(obj, null, 2);
51
+ }
52
+ return '';
53
+ };
54
+ const buildPrettyFormat = () => {
55
+ const localFormat = winston_1.format.printf(({ message, level, ..._meta }) => {
56
+ const color = {
57
+ white: '\x1B[38;2;173;190;203m',
58
+ blue: '\x1B[34m',
59
+ red: '\x1B[31m',
60
+ yellow: '\x1B[33m',
61
+ cyan: '\x1B[36m',
62
+ magenta: '\x1B[35m',
63
+ reset: '\x1B[0m',
64
+ };
65
+ const levelColor = {
66
+ info: color.white,
67
+ error: color.red,
68
+ warn: color.yellow,
69
+ debug: color.cyan,
70
+ verbose: color.magenta,
71
+ };
72
+ const currentColor = levelColor[level];
73
+ const meta = formatMeta(_meta);
74
+ /*
75
+ `verbose` é o maior level, com 7 letras; +1 para sempre sobrar espaço.
76
+ */
77
+ let finalMessage = `${level.padEnd(8)} ${message}${meta ?? ''}`;
78
+ finalMessage = finalMessage.replaceAll('\n', `\n${currentColor ?? 'red'}`);
79
+ return `${currentColor}` + finalMessage + `${color.reset}`;
80
+ });
81
+ return winston_1.format.combine(winston_1.format.splat(), localFormat);
82
+ };
83
+ /**
84
+ * Constrói o transport do Loki isoladamente, para o consumidor COMPARTILHAR um
85
+ * único batcher entre vários loggers do mesmo processo.
86
+ *
87
+ * Use isto quando o serviço monta mais de um logger — o worker do gyra-core monta
88
+ * quatro. Com um transport por logger, cada um tem seu batcher, e o flush de
89
+ * exceção fatal cobre só um deles.
90
+ *
91
+ * const lokiTransport = createLokiTransport({ service: 'gyra-core', host });
92
+ * const { logger } = buildNestLogger({ service: 'gyra-core', lokiTransport });
93
+ * // ...e o mesmo `lokiTransport` nos outros loggers do processo
94
+ */
95
+ const createLokiTransport = (options) => {
96
+ const { service, app, instance, pretty, ...loki } = options;
97
+ return buildLokiTransport(loki, {
98
+ service,
99
+ app: app ?? options_1.DEFAULT_APP_LABEL,
100
+ instance: instance ?? (0, os_1.hostname)(),
101
+ ...loki.labels,
102
+ }, pretty
103
+ ? buildPrettyFormat()
104
+ : winston_1.format.combine(winston_1.format.timestamp(), winston_1.format.json()));
105
+ };
106
+ exports.createLokiTransport = createLokiTransport;
107
+ const buildLokiTransport = (loki, labels, lineFormat) => new LokiTransport({
108
+ host: loki.host,
109
+ basicAuth: loki.basicAuth,
110
+ json: true,
111
+ labels,
112
+ interval: loki.interval ?? options_1.DEFAULT_LOKI_INTERVAL_SECONDS,
113
+ batching: true,
114
+ timeout: loki.timeout ?? options_1.DEFAULT_LOKI_TIMEOUT_MS,
115
+ format: lineFormat,
116
+ /*
117
+ A falha de entrega tem que ser audível: enquanto era silenciosa, o serviço
118
+ perdeu ~99,5% dos seus logs sem ninguém notar. Escreve direto no console
119
+ porque passar pelo logger completo realimentaria o Loki num laço de erro.
120
+ */
121
+ onConnectionError: (err) => process.stdout.write(`${JSON.stringify({
122
+ level: 'error',
123
+ message: `[LOKI-SHIP-FAIL] status=${err?.statusCode ?? 'none'} ${err?.message ?? err}`,
124
+ timestamp: new Date().toISOString(),
125
+ })}\n`),
126
+ });
127
+ /**
128
+ * Monta o logger do Nest com os transports pedidos e devolve, junto, o transport
129
+ * do Loki — necessário para `registerFatalHandlers` dar flush antes do exit.
130
+ *
131
+ * Não instala nada no processo: nem handler de exceção, nem redirecionamento de
132
+ * `console.*`. Esses são opt-in explícito, porque mexem no ciclo de vida do
133
+ * processo.
134
+ */
135
+ const buildNestLogger = (options) => {
136
+ const lineFormat = options.pretty
137
+ ? buildPrettyFormat()
138
+ : winston_1.format.combine(winston_1.format.timestamp(), winston_1.format.json());
139
+ /*
140
+ Passar os dois é ambíguo: um pede para construir um transport novo, o outro
141
+ para reusar um existente. Falhar alto é melhor que escolher em silêncio e
142
+ deixar o consumidor com dois batchers sem saber.
143
+ */
144
+ if (options.loki && options.lokiTransport) {
145
+ throw new Error('[log-transport] `loki` e `lokiTransport` sao mutuamente exclusivos: ' +
146
+ 'use `loki` para construir um transport novo, ou `lokiTransport` para ' +
147
+ 'reusar um ja construido por createLokiTransport().');
148
+ }
149
+ const transportsList = [
150
+ new winston_1.transports.Console({ format: lineFormat }),
151
+ ];
152
+ let lokiTransport;
153
+ if (options.lokiTransport) {
154
+ lokiTransport = options.lokiTransport;
155
+ transportsList.push(lokiTransport);
156
+ }
157
+ else if (options.loki) {
158
+ lokiTransport = buildLokiTransport(options.loki, {
159
+ service: options.service,
160
+ app: options.app ?? options_1.DEFAULT_APP_LABEL,
161
+ instance: options.instance ?? (0, os_1.hostname)(),
162
+ ...options.loki.labels,
163
+ }, lineFormat);
164
+ transportsList.push(lokiTransport);
165
+ }
166
+ else if (options.file) {
167
+ transportsList.push(new winston_1.transports.File({ filename: options.file.filename }));
168
+ }
169
+ const logger = nest_winston_1.WinstonModule.createLogger({
170
+ level: options.level ?? options_1.DEFAULT_LEVEL,
171
+ transports: transportsList,
172
+ });
173
+ return { logger, lokiTransport };
174
+ };
175
+ exports.buildNestLogger = buildNestLogger;
176
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":";AAAA,uDAAuD;;;AAEvD;;;;;;;;;;;;;;;;;;;;;;EAsBE;AAEF,qCAAsD;AACtD,+CAA6C;AAC7C,8CAA+C;AAE/C,2BAA8B;AAE9B,uCAOmB;AAmBnB,MAAM,UAAU,GAAG,CAAC,IAAkC,EAAU,EAAE;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAExC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,KAAK,CAAC;QAEzC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,GAAG,GAAG,GAAG,CAAC;QACnB,CAAC;QAED,IACE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;YAClB,GAAG,CAAC,MAAM,KAAK,CAAC;YAChB,OAAO,GAAG,CAAC,CAAC,CAAC,KAAK,QAAQ;YAC1B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC;YAChC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,EAC5B,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,GAAmB,EAAE;IAC7C,MAAM,WAAW,GAAG,gBAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE;QACjE,MAAM,KAAK,GAAG;YACZ,KAAK,EAAE,wBAAwB;YAC/B,IAAI,EAAE,UAAU;YAChB,GAAG,EAAE,UAAU;YACf,MAAM,EAAE,UAAU;YAClB,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,UAAU;YACnB,KAAK,EAAE,SAAS;SACjB,CAAC;QAEF,MAAM,UAAU,GAA2B;YACzC,IAAI,EAAE,KAAK,CAAC,KAAK;YACjB,KAAK,EAAE,KAAK,CAAC,GAAG;YAChB,IAAI,EAAE,KAAK,CAAC,MAAM;YAClB,KAAK,EAAE,KAAK,CAAC,IAAI;YACjB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC;QAEF,MAAM,YAAY,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAEvC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAE/B;;UAEE;QACF,IAAI,YAAY,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,OAAO,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;QACjE,YAAY,GAAG,YAAY,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,YAAY,IAAI,KAAK,EAAE,CAAC,CAAC;QAE3E,OAAO,GAAG,YAAY,EAAE,GAAG,YAAY,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,OAAO,gBAAM,CAAC,OAAO,CAAC,gBAAM,CAAC,KAAK,EAAE,EAAE,WAAW,CAAC,CAAC;AACrD,CAAC,CAAC;AASF;;;;;;;;;;;GAWG;AACI,MAAM,mBAAmB,GAAG,CACjC,OAAmC,EACpB,EAAE;IACjB,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAE5D,OAAO,kBAAkB,CACvB,IAAI,EACJ;QACE,OAAO;QACP,GAAG,EAAE,GAAG,IAAI,2BAAiB;QAC7B,QAAQ,EAAE,QAAQ,IAAI,IAAA,aAAQ,GAAE;QAChC,GAAG,IAAI,CAAC,MAAM;KACf,EACD,MAAM;QACJ,CAAC,CAAC,iBAAiB,EAAE;QACrB,CAAC,CAAC,gBAAM,CAAC,OAAO,CAAC,gBAAM,CAAC,SAAS,EAAE,EAAE,gBAAM,CAAC,IAAI,EAAE,CAAC,CACtD,CAAC;AACJ,CAAC,CAAC;AAjBW,QAAA,mBAAmB,uBAiB9B;AAEF,MAAM,kBAAkB,GAAG,CACzB,IAAiB,EACjB,MAA8B,EAC9B,UAA0B,EACX,EAAE,CACjB,IAAI,aAAa,CAAC;IAChB,IAAI,EAAE,IAAI,CAAC,IAAI;IACf,SAAS,EAAE,IAAI,CAAC,SAAS;IACzB,IAAI,EAAE,IAAI;IACV,MAAM;IACN,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,uCAA6B;IACxD,QAAQ,EAAE,IAAI;IACd,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,iCAAuB;IAChD,MAAM,EAAE,UAAU;IAElB;;;;MAIE;IACF,iBAAiB,EAAE,CAAC,GAAkB,EAAE,EAAE,CACxC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,IAAI,CAAC,SAAS,CAAC;QAChB,KAAK,EAAE,OAAO;QACd,OAAO,EAAE,2BAA2B,GAAG,EAAE,UAAU,IAAI,MAAM,IAC3D,GAAG,EAAE,OAAO,IAAI,GAClB,EAAE;QACF,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC,IAAI,CACP;CACJ,CAAC,CAAC;AAEL;;;;;;;GAOG;AACI,MAAM,eAAe,GAAG,CAAC,OAAsB,EAAe,EAAE;IACrE,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM;QAC/B,CAAC,CAAC,iBAAiB,EAAE;QACrB,CAAC,CAAC,gBAAM,CAAC,OAAO,CAAC,gBAAM,CAAC,SAAS,EAAE,EAAE,gBAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAEtD;;;;MAIE;IACF,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,sEAAsE;YACpE,uEAAuE;YACvE,oDAAoD,CACvD,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAU;QAC5B,IAAI,oBAAU,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;KAC/C,CAAC;IAEF,IAAI,aAAwC,CAAC;IAE7C,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC1B,aAAa,GAAG,OAAO,CAAC,aAA8B,CAAC;QACvD,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACrC,CAAC;SAAM,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACxB,aAAa,GAAG,kBAAkB,CAChC,OAAO,CAAC,IAAI,EACZ;YACE,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,2BAAiB;YACrC,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,IAAA,aAAQ,GAAE;YACxC,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM;SACvB,EACD,UAAU,CACX,CAAC;QAEF,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACrC,CAAC;SAAM,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACxB,cAAc,CAAC,IAAI,CACjB,IAAI,oBAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CACzD,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,4BAAa,CAAC,YAAY,CAAC;QACxC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,uBAAa;QACrC,UAAU,EAAE,cAAc;KAC3B,CAAC,CAAC;IAEH,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AACnC,CAAC,CAAC;AApDW,QAAA,eAAe,mBAoD1B"}