@mks2508/better-logger 0.18.2-alpha.1 → 0.18.3
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 +38 -336
- package/dist/Logger.d.ts +356 -157
- package/dist/Logger.d.ts.map +1 -1
- package/dist/ScopedLogger.d.ts +549 -9
- package/dist/ScopedLogger.d.ts.map +1 -1
- package/dist/chunks/HookBridge-C-AvXmPD.cjs +504 -0
- package/dist/chunks/HookBridge-C-AvXmPD.cjs.map +1 -0
- package/dist/chunks/HookBridge-CI2PH79S.js +487 -0
- package/dist/chunks/HookBridge-CI2PH79S.js.map +1 -0
- package/dist/chunks/{LogContext-L7HzynFw.cjs → LogContext-BaMXleWj.cjs} +19 -4
- package/dist/chunks/LogContext-BaMXleWj.cjs.map +1 -0
- package/dist/chunks/LogContext-DZasm_P5.cjs +141 -0
- package/dist/chunks/LogContext-DZasm_P5.cjs.map +1 -0
- package/dist/chunks/{LogContext-Cwn-1Zzb.js → LogContext-DjlITOzZ.js} +19 -4
- package/dist/chunks/LogContext-DjlITOzZ.js.map +1 -0
- package/dist/chunks/LogContext-Dyzs61XG.js +136 -0
- package/dist/chunks/LogContext-Dyzs61XG.js.map +1 -0
- package/dist/chunks/SerializerBridge-Ba43Mk7j.js +393 -0
- package/dist/chunks/SerializerBridge-Ba43Mk7j.js.map +1 -0
- package/dist/chunks/SerializerBridge-C4a9Z37F.cjs +410 -0
- package/dist/chunks/SerializerBridge-C4a9Z37F.cjs.map +1 -0
- package/dist/chunks/{StyleManager-CIpd6wbO.cjs → StyleManager-DQ6UNRB-.cjs} +37 -11
- package/dist/chunks/StyleManager-DQ6UNRB-.cjs.map +1 -0
- package/dist/chunks/StyleManager-DjwAYbxE.js +113 -0
- package/dist/chunks/StyleManager-DjwAYbxE.js.map +1 -0
- package/dist/chunks/{core-PoT7RrTK.js → core-Blfi2klP.js} +1 -4
- package/dist/chunks/core-Blfi2klP.js.map +1 -0
- package/dist/chunks/{core-Dzz7agGa.cjs → core-CqS_UBzJ.cjs} +1 -4
- package/dist/chunks/core-CqS_UBzJ.cjs.map +1 -0
- package/dist/chunks/{environment-detector-CI3TrWK_.js → environment-detector-7NvnYUfr.js} +11 -11
- package/dist/chunks/environment-detector-7NvnYUfr.js.map +1 -0
- package/dist/chunks/{environment-detector-Cnn6wr6O.cjs → environment-detector-D-tHkKWA.cjs} +11 -11
- package/dist/chunks/environment-detector-D-tHkKWA.cjs.map +1 -0
- package/dist/chunks/server-fallback-CaCPjWby.cjs +119 -0
- package/dist/chunks/server-fallback-CaCPjWby.cjs.map +1 -0
- package/dist/chunks/server-fallback-jj0T6XaK.js +114 -0
- package/dist/chunks/server-fallback-jj0T6XaK.js.map +1 -0
- package/dist/chunks/{spinner-DNvxbM9a.cjs → spinner-BHyYEXsM.cjs} +319 -37
- package/dist/chunks/spinner-BHyYEXsM.cjs.map +1 -0
- package/dist/chunks/{spinner-D3FsF78o.js → spinner-BtkwpzYv.js} +319 -37
- package/dist/chunks/spinner-BtkwpzYv.js.map +1 -0
- package/dist/chunks/styling-CRw3KQW4.js +1592 -0
- package/dist/chunks/styling-CRw3KQW4.js.map +1 -0
- package/dist/chunks/styling-Cel2wPRy.cjs +1645 -0
- package/dist/chunks/styling-Cel2wPRy.cjs.map +1 -0
- package/dist/chunks/transports-BGfwwakw.js +1237 -0
- package/dist/chunks/transports-BGfwwakw.js.map +1 -0
- package/dist/chunks/transports-BZ-Mc2IT.cjs +1450 -0
- package/dist/chunks/transports-BZ-Mc2IT.cjs.map +1 -0
- package/dist/chunks/transports-DvaLAeGJ.js +1403 -0
- package/dist/chunks/transports-DvaLAeGJ.js.map +1 -0
- package/dist/chunks/transports-yK6CL0Ml.cjs +1278 -0
- package/dist/chunks/transports-yK6CL0Ml.cjs.map +1 -0
- package/dist/chunks/{utils-BqlFYocD.cjs → utils-W_cxqriN.cjs} +44 -37
- package/dist/chunks/utils-W_cxqriN.cjs.map +1 -0
- package/dist/chunks/{utils-VETbVpkR.js → utils-tKfBAWUM.js} +44 -37
- package/dist/chunks/utils-tKfBAWUM.js.map +1 -0
- package/dist/cli/CommandProcessor.d.ts +166 -19
- package/dist/cli/CommandProcessor.d.ts.map +1 -1
- package/dist/cli/commands/ConfigCommand.d.ts +36 -1
- package/dist/cli/commands/ConfigCommand.d.ts.map +1 -1
- package/dist/cli/commands/ExportCommand.d.ts +49 -3
- package/dist/cli/commands/ExportCommand.d.ts.map +1 -1
- package/dist/cli/commands/ThemeCommand.d.ts +62 -3
- package/dist/cli/commands/ThemeCommand.d.ts.map +1 -1
- package/dist/cli/help.d.ts +30 -2
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/index.d.ts +29 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli.cjs +2 -2
- package/dist/cli.js +2 -2
- package/dist/context/LogContext.d.ts +167 -70
- package/dist/context/LogContext.d.ts.map +1 -1
- package/dist/context.cjs +1 -1
- package/dist/context.js +1 -1
- package/dist/core.cjs +52 -29
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.ts +50 -27
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +52 -29
- package/dist/core.js.map +1 -1
- package/dist/hooks/HookBridge.d.ts +15 -9
- package/dist/hooks/HookBridge.d.ts.map +1 -1
- package/dist/hooks/HookManager.d.ts +295 -2
- package/dist/hooks/HookManager.d.ts.map +1 -1
- package/dist/hooks.cjs +1 -1
- package/dist/hooks.js +1 -1
- package/dist/index.cjs +1415 -214
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1413 -215
- package/dist/index.js.map +1 -1
- package/dist/playground/TerminalBridge.d.ts +23 -16
- package/dist/playground/TerminalBridge.d.ts.map +1 -1
- package/dist/playground/box.d.ts +36 -5
- package/dist/playground/box.d.ts.map +1 -1
- package/dist/playground/cli-table.d.ts +41 -5
- package/dist/playground/cli-table.d.ts.map +1 -1
- package/dist/playground/divider.d.ts +22 -3
- package/dist/playground/divider.d.ts.map +1 -1
- package/dist/playground/header.d.ts +21 -4
- package/dist/playground/header.d.ts.map +1 -1
- package/dist/playground/server-fallback.d.ts +96 -9
- package/dist/playground/server-fallback.d.ts.map +1 -1
- package/dist/playground/spinner.d.ts +159 -10
- package/dist/playground/spinner.d.ts.map +1 -1
- package/dist/playground/step.d.ts +25 -6
- package/dist/playground/step.d.ts.map +1 -1
- package/dist/playground.cjs +1 -1
- package/dist/playground.js +1 -1
- package/dist/serializers/SerializerBridge.d.ts +13 -6
- package/dist/serializers/SerializerBridge.d.ts.map +1 -1
- package/dist/serializers/SerializerRegistry.d.ts +236 -0
- package/dist/serializers/SerializerRegistry.d.ts.map +1 -1
- package/dist/serializers.cjs +1 -1
- package/dist/serializers.js +1 -1
- package/dist/styles/StyleManager.d.ts +138 -31
- package/dist/styles/StyleManager.d.ts.map +1 -1
- package/dist/styles.cjs +2 -2
- package/dist/styles.js +2 -2
- package/dist/styling/SmartPresets.d.ts +100 -6
- package/dist/styling/SmartPresets.d.ts.map +1 -1
- package/dist/styling/StyleBuilder.d.ts +453 -32
- package/dist/styling/StyleBuilder.d.ts.map +1 -1
- package/dist/styling/banners.d.ts +83 -7
- package/dist/styling/banners.d.ts.map +1 -1
- package/dist/styling/themes.d.ts +41 -2
- package/dist/styling/themes.d.ts.map +1 -1
- package/dist/transports/ConsoleTransport.d.ts +95 -0
- package/dist/transports/ConsoleTransport.d.ts.map +1 -1
- package/dist/transports/FileTransport.d.ts +99 -18
- package/dist/transports/FileTransport.d.ts.map +1 -1
- package/dist/transports/HttpTransport.d.ts +135 -22
- package/dist/transports/HttpTransport.d.ts.map +1 -1
- package/dist/transports/OtlpTraceTransport.d.ts +118 -0
- package/dist/transports/OtlpTraceTransport.d.ts.map +1 -0
- package/dist/transports/OtlpTransport.d.ts +80 -37
- package/dist/transports/OtlpTransport.d.ts.map +1 -1
- package/dist/transports/SpanRuntime.d.ts +73 -0
- package/dist/transports/SpanRuntime.d.ts.map +1 -0
- package/dist/transports/TransportBridge.d.ts +19 -13
- package/dist/transports/TransportBridge.d.ts.map +1 -1
- package/dist/transports/TransportManager.d.ts +232 -10
- package/dist/transports/TransportManager.d.ts.map +1 -1
- package/dist/transports/index.d.ts +1 -0
- package/dist/transports/index.d.ts.map +1 -1
- package/dist/transports-module.d.ts +3 -1
- package/dist/transports-module.d.ts.map +1 -1
- package/dist/transports.cjs +2 -1
- package/dist/transports.js +2 -2
- package/dist/types/core.d.ts +155 -59
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/hooks.d.ts +92 -14
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/serializers.d.ts +72 -0
- package/dist/types/serializers.d.ts.map +1 -1
- package/dist/types/transports.d.ts +163 -39
- package/dist/types/transports.d.ts.map +1 -1
- package/dist/utils/ansi-colors.d.ts +15 -15
- package/dist/utils/asyncLocalStorage.d.ts +33 -0
- package/dist/utils/asyncLocalStorage.d.ts.map +1 -0
- package/dist/utils/environment-detector.d.ts +11 -11
- package/dist/utils/formatting.d.ts +8 -8
- package/dist/utils/output.d.ts +9 -9
- package/dist/utils/output.d.ts.map +1 -1
- package/dist/utils/stackTrace.d.ts +2 -2
- package/package.json +24 -25
- package/dist/chunks/HookBridge-CiRfR67f.cjs +0 -190
- package/dist/chunks/HookBridge-CiRfR67f.cjs.map +0 -1
- package/dist/chunks/HookBridge-SgMmbXZB.js +0 -173
- package/dist/chunks/HookBridge-SgMmbXZB.js.map +0 -1
- package/dist/chunks/LogContext-Cwn-1Zzb.js.map +0 -1
- package/dist/chunks/LogContext-L7HzynFw.cjs.map +0 -1
- package/dist/chunks/SerializerBridge-BaOQOb8q.cjs +0 -166
- package/dist/chunks/SerializerBridge-BaOQOb8q.cjs.map +0 -1
- package/dist/chunks/SerializerBridge-BkpQu5c9.js +0 -149
- package/dist/chunks/SerializerBridge-BkpQu5c9.js.map +0 -1
- package/dist/chunks/StyleManager-CIpd6wbO.cjs.map +0 -1
- package/dist/chunks/StyleManager-LCvVlVdx.js +0 -87
- package/dist/chunks/StyleManager-LCvVlVdx.js.map +0 -1
- package/dist/chunks/core-Dzz7agGa.cjs.map +0 -1
- package/dist/chunks/core-PoT7RrTK.js.map +0 -1
- package/dist/chunks/environment-detector-CI3TrWK_.js.map +0 -1
- package/dist/chunks/environment-detector-Cnn6wr6O.cjs.map +0 -1
- package/dist/chunks/server-fallback-BUKjdLS7.cjs +0 -42
- package/dist/chunks/server-fallback-BUKjdLS7.cjs.map +0 -1
- package/dist/chunks/server-fallback-BsuWH1Dk.js +0 -37
- package/dist/chunks/server-fallback-BsuWH1Dk.js.map +0 -1
- package/dist/chunks/spinner-D3FsF78o.js.map +0 -1
- package/dist/chunks/spinner-DNvxbM9a.cjs.map +0 -1
- package/dist/chunks/styling-84N8T97t.js +0 -941
- package/dist/chunks/styling-84N8T97t.js.map +0 -1
- package/dist/chunks/styling-Cg5saQ46.cjs +0 -994
- package/dist/chunks/styling-Cg5saQ46.cjs.map +0 -1
- package/dist/chunks/transports-BiDk345e.js +0 -715
- package/dist/chunks/transports-BiDk345e.js.map +0 -1
- package/dist/chunks/transports-COPWmlF1.cjs +0 -756
- package/dist/chunks/transports-COPWmlF1.cjs.map +0 -1
- package/dist/chunks/utils-BqlFYocD.cjs.map +0 -1
- package/dist/chunks/utils-VETbVpkR.js.map +0 -1
- package/dist/example.d.ts +0 -18
- package/dist/example.d.ts.map +0 -1
- package/dist/main.d.ts +0 -2
- package/dist/main.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transports-yK6CL0Ml.cjs","names":["proc","LOG_LEVELS"],"sources":["../../src/types/transports.ts","../../src/transports/ConsoleTransport.ts","../../src/transports/FileTransport.ts","../../src/transports/HttpTransport.ts","../../src/transports/OtlpTransport.ts","../../src/transports/TransportManager.ts"],"sourcesContent":["import type { LogLevel, LogTag, StackInfo } from './core.js';\n\n/**\n * Mapea un `LogLevel` a la severidad numérica de OpenTelemetry (1-24) usada\n * por SigNoz / cualquier backend OTLP/HTTP. Valores por banda conformes a la\n * spec: TRACE=1-4, DEBUG=5-8, INFO=9-12, WARN=13-16, ERROR=17-20, FATAL=21-24.\n * Se usa el valor canónico del medio de cada banda.\n *\n * @see https://opentelemetry.io/docs/specs/otel/logs/data-model/#severity-fields\n */\nexport const LOG_LEVEL_TO_SEVERITY_NUMBER: Record<LogLevel, number> = {\n trace: 1, // TRACE\n debug: 5, // DEBUG\n info: 9, // INFO\n warn: 13, // WARN\n error: 17, // ERROR\n critical: 21 // FATAL\n} as const;\n\n/**\n * Mapea un `LogLevel` a su nombre de severidad OpenTelemetry (mayúsculas, spec OTel).\n */\nexport const LOG_LEVEL_TO_SEVERITY_TEXT: Record<LogLevel, string> = {\n trace: 'TRACE',\n debug: 'DEBUG',\n info: 'INFO',\n warn: 'WARN',\n error: 'ERROR',\n critical: 'FATAL'\n} as const;\n\n/**\n * Atributos de recurso OTel adjuntados una vez por batch (service.name, version,\n * env, ...). Los valores son string-coerced por la capa de transport.\n */\nexport interface ILogResource {\n 'service.name': string;\n 'service.version'?: string;\n 'deployment.environment'?: string;\n [key: string]: string | undefined;\n}\n\n/**\n * Valor admitido en un {@link ILogAttributes}: unión recursiva de primitivos\n * serializables (string, number, boolean, null), arrays y structs anidados.\n * Compatible con el modelo de attribute value de OpenTelemetry.\n */\nexport type LogAttributeValue = string | number | boolean | null | LogAttributeValue[] | { [k: string]: LogAttributeValue };\n\n/**\n * Bag estructurado de atributos (compatible con OpenTelemetry). Cada clave\n * mapea a un valor tipado; la capa de transport decide cómo serializarlo.\n */\nexport interface ILogAttributes {\n [key: string]: LogAttributeValue;\n}\n\n/**\n * Payload enviado a cada transport en cada llamada a `log()`. Los campos son\n * deliberadamente explícitos (sin escape hatch `[key: string]: any`) para que\n * implementaciones como `OtlpTransport` puedan mapear 1:1 a `logRecords` OTLP.\n */\nexport interface TransportRecord {\n /** Nivel canónico (trace/debug/info/warn/error/critical). */\n level: LogLevel;\n /** Severidad numérica, copiada de `LOG_LEVELS[level]`. */\n levelValue: number;\n /** Severidad numérica OTel (1-24). La asigna automáticamente el Logger. */\n severityNumber: number;\n /** Nombre de severidad OTel (TRACE/DEBUG/INFO/WARN/ERROR/FATAL). */\n severityText: string;\n /** Epoch milliseconds en el momento del log. */\n time: number;\n /** Texto final del mensaje, ya procesado por los hooks. */\n msg: string;\n /** Scope lógico opcional (p.ej. \"Auth\", \"API:Users\"). */\n prefix?: string;\n /** Ubicación del caller, solo cuando `enableStackTrace` es true. */\n location?: {\n file: string;\n line: number;\n column: number;\n function?: string;\n };\n /** Trace id hex de 32 chars (OTel), cuando hay correlación con un span activo. */\n traceId?: string;\n /** Span id hex de 16 chars (OTel), cuando hay correlación con un span activo. */\n spanId?: string;\n /** Atributos estructurados (requestId, userId, tags custom). */\n attributes?: ILogAttributes;\n /** Recurso a nivel de log (sobreescribe el resource del transport). */\n resource?: Partial<ILogResource>;\n /** Tag especial \"success\" — lo setea `Logger.success()`. */\n tag?: LogTag;\n}\n\n/**\n * Opciones aceptadas por cualquier transport al registrarse.\n */\nexport interface TransportOptions {\n level?: LogLevel;\n /** Transforma el record antes de serializarlo. Devolver null descarta el record. */\n transform?: (record: TransportRecord) => TransportRecord | null;\n /** Flush cuando el buffer alcanza esta cantidad de records. */\n batchSize?: number;\n /** Flush periódico a este intervalo (ms). */\n flushInterval?: number;\n /** Tope duro de records en buffer. Los más antiguos se descartan al desbordar. */\n maxBufferSize?: number;\n}\n\n/**\n * Registro de un transport. `target` puede ser:\n * - Una instancia de `ITransport` (legacy / inline).\n * - Un string registrado en el registry built-in de `TransportManager`\n * (`'console' | 'file' | 'http' | 'otlp'`).\n */\nexport interface TransportTarget {\n target: string | ITransport;\n options?: TransportOptions;\n level?: LogLevel;\n}\n\n/**\n * Contrato mínimo que todo transport debe satisfacer.\n */\nexport interface ITransport {\n readonly name: string;\n write(record: TransportRecord): void | Promise<void>;\n flush?(): void | Promise<void>;\n close?(): void | Promise<void>;\n isReady?(): boolean;\n}\n\n/**\n * Contrato de transport con buffer. Transports concretos como `HttpTransport`,\n * `FileTransport` y `OtlpTransport` extienden esta interfaz.\n */\nexport interface IBufferedTransport extends ITransport {\n readonly bufferSize: number;\n readonly maxBufferSize: number;\n flush(): Promise<void>;\n}\n\n/**\n * Contrato del registry / dispatch de transports.\n */\nexport interface ITransportManager {\n add(target: TransportTarget): string;\n remove(id: string): boolean;\n write(record: TransportRecord): Promise<void>;\n flush(): Promise<void>;\n close(): Promise<void>;\n}\n","import type { TransportRecord, TransportOptions, ITransport, LogLevel } from '../types/index.js';\n\n/**\n * Transport por defecto que escribe cada {@link TransportRecord} al `console`\n * global del runtime (navegador o Node.js).\n *\n * Es el transport que el Logger registra automáticamente cuando no se configura\n * ninguno explícito, garantizando que los registros siempre lleguen a un\n * destino visible sin configuración adicional.\n *\n * **Mapeo level → console method**: traduce cada nivel de log al método más\n * cercano de la API `console`, de forma que el filtrado nativo del DevTools /\n * `NODE_DEBUG` siga funcionando:\n *\n * | LogLevel | console method |\n * |--------------|----------------|\n * | `debug` | `console.log` |\n * | `info` | `console.info` |\n * | `warn` | `console.warn` |\n * | `error` | `console.error`|\n * | `critical` | `console.error`|\n *\n * **Formato de output**: cada línea se compone como\n * `[LEVEL] [prefix] message (file:line)`, donde `prefix` y la localización\n * se omiten si el registro no las trae.\n *\n * @example\n * // Uso directo como ITransport\n * import { ConsoleTransport } from '@mks2508/better-logger/transports';\n * const transport = new ConsoleTransport();\n * transport.write({\n * level: 'info',\n * msg: 'Arrancando worker',\n * prefix: 'worker',\n * // ... resto del TransportRecord\n * });\n * // → console.info(\"[INFO] [worker] Arrancando worker (worker.ts:12)\")\n *\n * @example\n * // Registro a través del Logger (típico — el logger lo añade por defecto)\n * logger.addTransport({ target: new ConsoleTransport() });\n *\n * @see {@link ITransport}\n * @see {@link TransportRecord}\n */\nexport class ConsoleTransport implements ITransport {\n /** Identificador del transport usado por el Logger para deduplicar y exponer metadatos. */\n readonly name = 'console';\n\n /**\n * Crea una instancia de {@link ConsoleTransport}.\n *\n * El parámetro `options` se acepta para cumplir con la firma canónica de\n * {@link TransportOptions} (filtros de nivel, formateadores, etc.), aunque\n * la implementación actual escribe el registro tal cual llega sin\n * transformaciones adicionales.\n *\n * @param {TransportOptions} [options] - Configuración opcional del transport\n * (nivel mínimo, formatter, etc.).\n *\n * @example\n * const transport = new ConsoleTransport({ level: 'warn' });\n */\n constructor(private options?: TransportOptions) {}\n\n /**\n * Escribe un {@link TransportRecord} al `console` global.\n *\n * Selecciona el método de console según el nivel del registro, compone el\n * prefijo `[LEVEL] [prefix]` y, si la localización está disponible, añade\n * el sufijo `(file:line)`. No lanza ni retorna errores: si `console[method]`\n * fallara (raro), la excepción propagaría al caller.\n *\n * @param {TransportRecord} record - Registro normalizado producido por el Logger.\n * @returns {void}\n *\n * @example\n * transport.write({\n * level: 'error',\n * msg: 'DB connection lost',\n * prefix: 'db',\n * location: { file: 'pool.ts', line: 87, function: 'acquire' },\n * // ... resto del TransportRecord\n * });\n * // → console.error(\"[ERROR] [db] DB connection lost (pool.ts:87)\")\n */\n write(record: TransportRecord): void {\n const method = this.getConsoleMethod(record.level);\n const prefix = record.prefix ? `[${record.prefix}] ` : '';\n const location = record.location\n ? ` (${record.location.file}:${record.location.line})`\n : '';\n\n console[method](`[${record.level.toUpperCase()}]${prefix} ${record.msg}${location}`);\n }\n\n /**\n * Resuelve el método de `console` apropiado para un {@link LogLevel}.\n *\n * Tabla de mapeo:\n * - `debug` → `log` (sin ruido en DevTools por defecto)\n * - `info` → `info`\n * - `warn` → `warn`\n * - `error` → `error`\n * - `critical` → `error` (no existe `console.critical`)\n * - cualquier otro → `log` (fallback seguro)\n *\n * @internal Método privado; no forma parte de la API pública del transport.\n *\n * @param {LogLevel} level - Nivel del registro a traducir.\n * @returns {'log' | 'info' | 'warn' | 'error'} Nombre del método de `console`.\n */\n private getConsoleMethod(level: LogLevel): 'log' | 'info' | 'warn' | 'error' {\n switch (level) {\n case 'debug': return 'log';\n case 'info': return 'info';\n case 'warn': return 'warn';\n case 'error':\n case 'critical': return 'error';\n default: return 'log';\n }\n }\n}\n","import type {\n TransportRecord,\n TransportOptions,\n ITransport,\n IBufferedTransport,\n HookLogEntry,\n HookEvent\n} from '../types/index.js';\n\nconst MAX_BUFFER_DEFAULT = 10_000;\nconst BATCH_SIZE_DEFAULT = 100;\nconst FS_PROMISES_LOAD_TIMEOUT_MS = 50;\nconst LOCAL_STORAGE_KEY_PREFIX = 'better-logger:';\n\n/**\n * Opciones para {@link FileTransport}. El campo `destination` se interpreta\n * como ruta de fichero en Node y como clave de `localStorage` en el browser\n * (componente saneado a `[a-zA-Z0-9_-]`).\n *\n * Hereda los defaults de {@link TransportOptions}: `batchSize=100`,\n * `maxBufferSize=10000`.\n *\n * @example\n * const transport = new FileTransport({\n * destination: 'logs/app.log',\n * batchSize: 200,\n * flushInterval: 2000,\n * maxBufferSize: 5000,\n * onError: (entry) => captureFailure(entry)\n * });\n */\nexport interface FileTransportOptions extends TransportOptions {\n /**\n * Destino de escritura. En Node es una ruta de fichero (resuelta relativa\n * a `process.cwd()` y saneada contra path traversal). En el browser es una\n * clave de `localStorage` (saneada a alfanumérico + `-_`).\n * @default 'app.log' (Node) | 'better-logger:default' (browser)\n */\n destination?: string;\n /**\n * Hook opcional que se dispara cuando el transport no puede escribir\n * (error de FS, quota agotada, tab del browser en modo privado, ...).\n * Recibe un {@link HookLogEntry} sintético.\n */\n onError?: (entry: HookLogEntry) => void | Promise<void>;\n}\n\n/**\n * Transport que escribe registros a fichero (Node) o `localStorage` (browser).\n *\n * En Node, hace `appendFile` asíncrono vía `fs.promises` cargado con dynamic\n * import (no bloquea el event loop, no envía código Node-only al bundle del\n * browser). En el browser, acumula en `localStorage` con prefijo `better-logger:`\n * y degrada a no-op silencioso si el storage no está disponible (modo privado,\n * sandbox de iframes, quota agotada).\n *\n * El buffer es bounded: al llegar a `maxBufferSize` suelta el registro más\n * viejo (drop-oldest) e invoca `onError` con el payload descartado, de modo\n * que un pico de tráfico sostenido no agota memoria.\n *\n * El `destination` se sanea antes de usarse:\n * - Node: se rechazan rutas con segmentos `..`, `~` o absolutas (path traversal).\n * - Browser: se colapsa a `[a-zA-Z0-9_-]` recortado a 64 caracteres.\n *\n * @implements {IBufferedTransport}\n *\n * @example\n * // Node: append a fichero con flush cada segundo\n * logger.addTransport({\n * target: new FileTransport({\n * destination: 'logs/app.log',\n * batchSize: 100,\n * flushInterval: 1000,\n * onError: (entry) => captureFailure(entry)\n * })\n * });\n *\n * @example\n * // Browser: persiste en localStorage bajo 'better-logger:audit'\n * logger.addTransport({\n * target: new FileTransport({ destination: 'audit' })\n * });\n *\n * @see {@link FileTransportOptions}\n * @see {@link IBufferedTransport}\n */\nexport class FileTransport implements IBufferedTransport {\n /** Identificador del transport dentro del pipeline (`'file'`). */\n readonly name = 'file';\n\n private buffer: string[] = [];\n private flushTimer?: ReturnType<typeof setInterval>;\n private options: FileTransportOptions;\n private closed = false;\n\n /**\n * Construye el transport. Si se pasa `flushInterval`, arranca un timer\n * periódico que vacía el buffer al vencimiento; si no, el flush se\n * dispara solo cuando el buffer alcanza `batchSize`.\n *\n * @param {FileTransportOptions} [options] - Configuración. Defaults: `batchSize=100`, `maxBufferSize=10000`.\n *\n * @example\n * const t = new FileTransport({ destination: 'app.log', flushInterval: 1000 });\n */\n constructor(options?: FileTransportOptions) {\n this.options = {\n batchSize: BATCH_SIZE_DEFAULT,\n maxBufferSize: MAX_BUFFER_DEFAULT,\n ...(options ?? {})\n };\n\n if (this.options.flushInterval) {\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.options.flushInterval);\n }\n }\n\n /** Registros pendientes en el buffer (aún sin flush). */\n get bufferSize(): number {\n return this.buffer.length;\n }\n\n /** Capacidad máxima del buffer; al superarla se aplica drop-oldest. */\n get maxBufferSize(): number {\n return this.options.maxBufferSize ?? MAX_BUFFER_DEFAULT;\n }\n\n /**\n * Indica si el transport acepta escrituras. Devuelve `false` después de\n * {@link FileTransport.close} — cualquier `write` posterior se descarta.\n *\n * @returns {boolean} `true` mientras el transport no esté cerrado.\n */\n isReady(): boolean {\n return !this.closed;\n }\n\n /**\n * Encola un registro serializado (JSON + `\\n`). Si el buffer está a tope,\n * suelta el registro más viejo (drop-oldest) y emite un evento `onError`\n * con el payload descartado para que la pérdida sea observable. Si al\n * encolar se alcanza `batchSize`, dispara un flush asíncrono.\n *\n * No-op silencioso si el transport está cerrado.\n *\n * @param {TransportRecord} record - Registro a escribir.\n */\n write(record: TransportRecord): void {\n if (this.closed) return;\n\n // Drop-oldest: sin esta evacuation, un pico de tráfico sostenido\n // haría crecer el buffer sin límite hasta agotar memoria.\n if (this.buffer.length >= this.maxBufferSize) {\n const dropped = this.buffer.shift();\n if (dropped && this.options.onError) {\n const entry: HookLogEntry = {\n level: 'warn',\n message: 'FileTransport buffer overflow: oldest record dropped',\n args: [],\n timestamp: new Date().toISOString(),\n hookEvent: 'onError' as HookEvent,\n error: new Error('FileTransport buffer overflow'),\n extra: { droppedRecord: dropped }\n };\n void this.options.onError(entry);\n }\n }\n\n this.buffer.push(JSON.stringify(record) + '\\n');\n\n const batchSize = this.options.batchSize ?? BATCH_SIZE_DEFAULT;\n if (this.buffer.length >= batchSize) {\n void this.flush();\n }\n }\n\n /**\n * Vuelca el buffer al destino. En Node concatena el contenido y hace\n * un único `appendFile`; en browser hace un único `setItem` sobre\n * `localStorage`. El buffer se vacía antes del I/O para que los registros\n * entrantes no esperen al disco. Los errores de escritura se reportan\n * vía `onError` (nunca lanzan al caller).\n *\n * @returns {Promise<void>} Resuelve cuando el I/O terminó o falló.\n */\n async flush(): Promise<void> {\n if (this.closed || this.buffer.length === 0) return;\n\n const payload = this.buffer.join('');\n this.buffer = [];\n\n if (isNodeLike()) {\n await this.flushNode(payload);\n } else {\n await this.flushBrowser(payload);\n }\n }\n\n private async flushNode(payload: string): Promise<void> {\n const destination = this.resolveNodeDestination();\n try {\n // Dynamic import para no mandar código Node-only al bundle del\n // browser y para que los consumidores ESM no lidien con un\n // `require` undefined.\n const fsPromises = await loadNodeFsPromises();\n await fsPromises.appendFile(destination, payload, 'utf8');\n } catch (error) {\n this.emitError('FileTransport failed to write to disk', error);\n }\n }\n\n private async flushBrowser(payload: string): Promise<void> {\n if (typeof localStorage === 'undefined') {\n this.emitError('FileTransport: localStorage is not available in this environment', null);\n return;\n }\n try {\n const key = LOCAL_STORAGE_KEY_PREFIX + this.resolveBrowserKey();\n const existing = localStorage.getItem(key) ?? '';\n localStorage.setItem(key, existing + payload);\n } catch (error) {\n this.emitError('FileTransport: localStorage write failed (quota? private mode?)', error);\n }\n }\n\n /**\n * Cierra el transport: detiene el timer de flush y dispara un flush\n * final para no perder registros pendientes. Tras cerrar, `write` y\n * `flush` se vuelven no-op.\n *\n * @returns {Promise<void>} Resuelve cuando el flush final termina.\n */\n async close(): Promise<void> {\n this.closed = true;\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n await this.flush();\n }\n\n private resolveNodeDestination(): string {\n const requested = this.options.destination ?? 'app.log';\n const sanitised = sanitiseNodePath(requested);\n if (sanitised === null) {\n this.emitError(`FileTransport: refusing destination with traversal segment: ${requested}`, null);\n return 'app.log';\n }\n return sanitised;\n }\n\n private resolveBrowserKey(): string {\n const requested = this.options.destination ?? 'default';\n return sanitiseBrowserKey(requested);\n }\n\n private emitError(message: string, cause: unknown): void {\n if (!this.options.onError) return;\n const entry: HookLogEntry = {\n level: 'error',\n message,\n args: [],\n timestamp: new Date().toISOString(),\n hookEvent: 'onError' as HookEvent,\n error: cause instanceof Error ? cause : new Error(String(cause))\n };\n void this.options.onError(entry);\n }\n}\n\n// ===== Helpers de entorno =====\n\n/**\n * Detecta si el runtime es Node comprobando `process.versions.node`.\n *\n * @internal Dispatch Node/browser dentro del transport.\n * @returns {boolean} `true` si corre sobre Node.\n */\nfunction isNodeLike(): boolean {\n return typeof process !== 'undefined'\n && process.versions != null\n && process.versions.node != null;\n}\n\n// ===== Saneadores =====\n\n/**\n * Rechaza rutas que escapan del working directory. Permite rutas relativas\n * bajo `cwd/`. Las rutas absolutas se rechazan a propósito — si un caller\n * necesita una ubicación absoluta, debe usar un escape hatch documentado\n * (no expuesto aquí).\n *\n * @internal\n * @param {string} input - Ruta cruda pedida por el caller.\n * @returns {string | null} Ruta saneada, o `null` si se rechaza por traversal.\n */\nfunction sanitiseNodePath(input: string): string | null {\n if (!input) return null;\n const normalised = input.replace(/\\\\/g, '/').trim();\n if (normalised.length === 0) return null;\n if (normalised.startsWith('/') || /^[a-zA-Z]:\\//.test(normalised)) return null;\n const segments = normalised.split('/').filter(s => s.length > 0 && s !== '.');\n if (segments.some(s => s === '..' || s === '~')) return null;\n return segments.join('/');\n}\n\n/**\n * Convierte cualquier string en una clave válida para `localStorage`:\n * colapsa todo carácter fuera de `[a-zA-Z0-9_-]` a `_` y recorta a 64\n * caracteres. Devuelve `'default'` si el resultado es vacío.\n *\n * @internal\n * @param {string} input - Clave cruda pedida por el caller.\n * @returns {string} Clave saneada lista para `localStorage`.\n */\nfunction sanitiseBrowserKey(input: string): string {\n return input.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 64) || 'default';\n}\n\n// ===== Loader de fs de Node =====\n\n/**\n * Dynamic import cacheado de `node:fs/promises`. La caché es intencionadamente\n * module-scoped para que los flushes posteriores no paguen el coste del import.\n * Además corre el import contra un timeout corto (`FS_PROMISES_LOAD_TIMEOUT_MS`)\n * para que un entorno Node roto (bindings nativos corruptos) no bloquee el\n * transport indefinidamente.\n *\n * @internal\n * @returns {Promise<typeof import('node:fs/promises')>} Módulo `fs/promises` resuelto.\n * @throws {Error} Si el import excede el timeout o el módulo no está disponible.\n */\nlet _fsPromisesPromise: Promise<typeof import('node:fs/promises')> | null = null;\n\nasync function loadNodeFsPromises(): Promise<typeof import('node:fs/promises')> {\n if (_fsPromisesPromise) return _fsPromisesPromise;\n\n // Pone el dynamic import a competir contra un timeout corto para que\n // un entorno Node colgado (bindings nativos corruptos, etc.) no\n // bloquee el transport indefinidamente.\n const importPromise = import('node:fs/promises');\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => reject(new Error(`fs/promises import timed out after ${FS_PROMISES_LOAD_TIMEOUT_MS}ms`)), FS_PROMISES_LOAD_TIMEOUT_MS);\n });\n _fsPromisesPromise = Promise.race([importPromise, timeoutPromise]).catch(err => {\n _fsPromisesPromise = null;\n throw err;\n }) as Promise<typeof import('node:fs/promises')>;\n return _fsPromisesPromise;\n}","import type {\n TransportRecord,\n TransportOptions,\n ITransport,\n IBufferedTransport,\n HookEvent,\n HookLogEntry\n} from '../types/index.js';\n\n/**\n * Configuración para {@link HttpTransport}. Extiende {@link TransportOptions}\n * con parámetros HTTP: URL destino, headers custom, buffer acotado,\n * retry con backoff para fallos transitorios, timeout de fetch por intento, y\n * un hook `onError` que reporta drops (overflow / 4xx / retry exhausto)\n * sin escalar excepciones al caller.\n *\n * @example\n * // Mínimo: solo URL\n * new HttpTransport({ url: 'https://logs.example.com/ingest' });\n *\n * @example\n * // Con retry/backoff ajustado y hook de errores\n * new HttpTransport({\n * url: 'https://logs.example.com/ingest',\n * headers: { Authorization: 'Bearer <REDACTED>' },\n * maxRetries: 5,\n * initialBackoffMs: 500,\n * maxBackoffMs: 30_000,\n * fetchTimeoutMs: 5_000,\n * onError: (entry) => metrics.increment('log_drop', { reason: entry.message })\n * });\n */\nexport interface HttpTransportOptions extends TransportOptions {\n /** URL destino. Se usa POST con body JSON. */\n url?: string;\n /** Headers extra de la request (ej. `Authorization: Bearer ...`). */\n headers?: Record<string, string>;\n /** Tope máximo de records en buffer. En overflow se dropea el más viejo. */\n maxBufferSize?: number;\n /** Reintentos consecutivos por batch antes de dropear los records. */\n maxRetries?: number;\n /** Backoff inicial en ms. Duplica por intento hasta `maxBackoffMs`. */\n initialBackoffMs?: number;\n /** Techo de backoff en ms. */\n maxBackoffMs?: number;\n /** Timeout del fetch en ms (por intento). Default 10_000. */\n fetchTimeoutMs?: number;\n /**\n * Hook opcional que se dispara cuando el buffer hace overflow o un batch\n * se dropea tras `maxRetries`. Recibe una entrada {@link HookLogEntry}\n * sintética.\n */\n onError?: (entry: HookLogEntry) => void | Promise<void>;\n}\n\nconst DEFAULT_MAX_BUFFER = 10_000;\nconst DEFAULT_BATCH_SIZE = 50;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_INITIAL_BACKOFF = 250;\nconst DEFAULT_MAX_BACKOFF = 5_000;\nconst DEFAULT_FETCH_TIMEOUT = 10_000;\n\n/**\n * Transport basado en HTTP. Bufferea records, los batcha por tamaño o\n * intervalo, POSTea el batch como JSON, y reporta fallos vía retry, buffer\n * acotado y un hook `onError` — nunca un `.catch(() => {})` silencioso.\n *\n * Lifecycle de cada batch (driveado internamente por `sendWithRetry`):\n * 1. `fetch(url, { method: 'POST', body, signal })` con un `AbortController`\n * que aborta tras `fetchTimeoutMs`.\n * 2. Si `response.ok` → batch considerado entregado.\n * 3. Si `4xx` → dropeado sin reintento: el cliente nunca se recupera de un\n * error de URL/auth/payload mal formado. Se dispara `onError`.\n * 4. Si `5xx` o `fetch` lanza (red caída / abort por timeout) → reintento con\n * backoff exponencial: arranca en `initialBackoffMs`, duplica por intento,\n * techo `maxBackoffMs`, hasta `maxRetries` intentos. Tras el agotamiento\n * el batch se re-bufferiza (o se trimea contra `maxBufferSize`) y se\n * dispara `onError` con `droppedCount`.\n *\n * El body por defecto es el envelope JSON `{ logs: TransportRecord[] }`.\n * Para cambiar el wire format, sobrescribe los hooks `protected`\n * {@link HttpTransport.serializeBody} y {@link HttpTransport.buildHeaders}\n * (referencia: {@link OtlpTransport}).\n *\n * Extender esta clase es la vía recomendada para shippear un transport\n * nuevo orientado a HTTP.\n *\n * @example\n * // Registro en un logger\n * import logger from '@mks2508/better-logger';\n * import { HttpTransport } from '@mks2508/better-logger/transports';\n *\n * logger.addTransport({\n * target: new HttpTransport({\n * url: 'https://logs.example.com/ingest',\n * flushInterval: 5_000,\n * batchSize: 100,\n * onError: (entry) => console.error('[log-drop]', entry.message)\n * })\n * });\n *\n * @see {@link HttpTransportOptions}\n * @see {@link OtlpTransport}\n */\nexport class HttpTransport implements IBufferedTransport {\n /** Identificador del transport. Los loggers lo usan para lookup, dedup y logs de diagnóstico. */\n readonly name: string = 'http';\n\n private buffer: TransportRecord[] = [];\n private flushTimer?: ReturnType<typeof setInterval>;\n private closed = false;\n\n /** Bag de options — `protected` para que subclasses (ej. {@link OtlpTransport}) puedan leerlo o extenderlo. */\n protected options: HttpTransportOptions;\n\n /**\n * Crea una instancia de {@link HttpTransport}.\n *\n * Los campos omitidos en `options` se rellenan con defaults sensatos\n * (`batchSize=50`, `maxBufferSize=10_000`, `maxRetries=3`,\n * `initialBackoffMs=250`, `maxBackoffMs=5_000`, `fetchTimeoutMs=10_000`).\n * Si se pasa `flushInterval`, arranca un `setInterval` que flushea cada\n * N ms; si se omite, el flush solo dispara por llenado de `batchSize`.\n *\n * @param {HttpTransportOptions} [options] - Configuración opcional. Si se omite por completo, el transport queda inactivo hasta que se setee `options.url` por otra vía (subclasses).\n *\n * @example\n * const t = new HttpTransport({\n * url: 'https://logs.example.com/ingest',\n * flushInterval: 5_000\n * });\n */\n constructor(options?: HttpTransportOptions) {\n this.options = {\n batchSize: DEFAULT_BATCH_SIZE,\n maxBufferSize: DEFAULT_MAX_BUFFER,\n maxRetries: DEFAULT_MAX_RETRIES,\n initialBackoffMs: DEFAULT_INITIAL_BACKOFF,\n maxBackoffMs: DEFAULT_MAX_BACKOFF,\n fetchTimeoutMs: DEFAULT_FETCH_TIMEOUT,\n ...(options ?? {})\n };\n\n if (this.options.flushInterval) {\n this.flushTimer = setInterval(() => {\n void this.flush();\n }, this.options.flushInterval);\n }\n }\n\n /** Records actualmente encolados esperando el próximo flush. */\n get bufferSize(): number {\n return this.buffer.length;\n }\n\n /** Capacidad máxima del buffer. Al superarla, el registro más viejo se dropea y se notifica vía `onError`. */\n get maxBufferSize(): number {\n return this.options.maxBufferSize ?? DEFAULT_MAX_BUFFER;\n }\n\n /**\n * Indica si el transport está listo para aceptar y entregar records.\n * Devuelve `false` tras {@link close} o si no se configuró `url`.\n *\n * @returns {boolean} `true` si el transport puede enviar.\n */\n isReady(): boolean {\n return !this.closed && Boolean(this.options.url);\n }\n\n /**\n * Encola un record en el buffer. Si el buffer está lleno, aplica la\n * política de overflow (dropea el más viejo + dispara `onError`). Si tras\n * el push se alcanza `batchSize`, dispara un flush asíncrono (sin await).\n *\n * No-op si el transport ya fue cerrado ({@link close}).\n *\n * @param {TransportRecord} record - Registro a encolar.\n */\n write(record: TransportRecord): void {\n if (this.closed) return;\n\n if (this.buffer.length >= this.maxBufferSize) {\n this.applyOverflowPolicy();\n }\n\n this.buffer.push(record);\n\n const batchSize = this.options.batchSize ?? DEFAULT_BATCH_SIZE;\n if (this.buffer.length >= batchSize) {\n void this.flush();\n }\n }\n\n /**\n * Serializa un batch al body de la request. Las subclasses sobrescriben\n * para cambiar la codificación (ej. {@link OtlpTransport} produce OTLP/HTTP\n * JSON en vez del envelope default `{ logs: [...] }`).\n *\n */\n protected serializeBody(records: TransportRecord[]): string {\n return JSON.stringify({ logs: records });\n }\n\n /**\n * Construye los headers de la request. Las subclasses pueden prependear\n * headers transport-specific (ej. `signoz-ingestion-key`).\n *\n */\n protected buildHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n ...(this.options.headers ?? {})\n };\n }\n\n private applyOverflowPolicy(): void {\n const dropped = this.buffer.shift();\n if (dropped && this.options.onError) {\n const entry: HookLogEntry = {\n level: 'warn',\n message: 'HttpTransport buffer overflow: oldest record dropped',\n args: [dropped],\n timestamp: new Date().toISOString(),\n hookEvent: 'onError' as HookEvent,\n error: new Error('HttpTransport buffer overflow'),\n extra: { droppedRecord: dropped }\n };\n void this.options.onError(entry);\n }\n }\n\n /**\n * Flushea el buffer actual: toma un snapshot de los records pendientes,\n * los envía con retry/backoff vía `sendWithRetry`, y ante fallo los\n * re-bufferiza preservando el orden. Si la re-bufferización excede\n * `maxBufferSize`, trimea los más viejos y dispara `onError` con\n * `droppedCount`.\n *\n * No-op si el transport está cerrado, el buffer está vacío o no hay\n * `url` configurada.\n *\n * @returns {Promise<void>} Resuelve cuando el intento de entrega del batch actual terminó (success, drop definitivo o no-op).\n *\n * @see {@link HttpTransportOptions.onError}\n */\n async flush(): Promise<void> {\n if (this.closed || this.buffer.length === 0 || !this.options.url) return;\n\n const records = [...this.buffer];\n this.buffer = [];\n\n const success = await this.sendWithRetry(records);\n if (!success) {\n // Re-bufferiza al frente para preservar orden; si volviera a hacer\n // overflow, applyOverflowPolicy trimea el más viejo y lo reporta.\n const combined = records.concat(this.buffer);\n if (combined.length > this.maxBufferSize) {\n const trimmed = combined.slice(combined.length - this.maxBufferSize);\n const droppedCount = combined.length - trimmed.length;\n if (droppedCount > 0 && this.options.onError) {\n const entry: HookLogEntry = {\n level: 'error',\n message: `HttpTransport dropped ${droppedCount} records after retry exhaustion`,\n args: [],\n timestamp: new Date().toISOString(),\n hookEvent: 'onError' as HookEvent,\n error: new Error('HttpTransport retry exhaustion'),\n extra: { droppedCount }\n };\n void this.options.onError(entry);\n }\n this.buffer = trimmed;\n } else {\n this.buffer = combined;\n }\n }\n }\n\n private async sendWithRetry(records: TransportRecord[]): Promise<boolean> {\n const url = this.options.url;\n if (!url) return false;\n\n const maxRetries = this.options.maxRetries ?? DEFAULT_MAX_RETRIES;\n const initialBackoff = this.options.initialBackoffMs ?? DEFAULT_INITIAL_BACKOFF;\n const maxBackoff = this.options.maxBackoffMs ?? DEFAULT_MAX_BACKOFF;\n const fetchTimeout = this.options.fetchTimeoutMs ?? DEFAULT_FETCH_TIMEOUT;\n const body = this.serializeBody(records);\n const headers = this.buildHeaders();\n\n let attempt = 0;\n let backoff = initialBackoff;\n\n while (attempt <= maxRetries) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), fetchTimeout);\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body,\n signal: controller.signal\n });\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return true;\n }\n\n // Non-2xx es fallo lógico — NO se reintenta en 4xx (errores\n // de cliente no son transitorios). 5xx sí se reintenta.\n if (response.status >= 400 && response.status < 500) {\n if (this.options.onError) {\n const entry: HookLogEntry = {\n level: 'error',\n message: `HttpTransport ${response.status} ${response.statusText} (not retried)`,\n args: [],\n timestamp: new Date().toISOString(),\n hookEvent: 'onError' as HookEvent,\n error: new Error(`HTTP ${response.status}`),\n extra: { responseStatus: response.status }\n };\n void this.options.onError(entry);\n }\n return false;\n }\n\n // 5xx → retry con backoff\n if (attempt === maxRetries) break;\n await sleep(backoff);\n backoff = Math.min(backoff * 2, maxBackoff);\n } catch (error) {\n if (attempt === maxRetries) {\n if (this.options.onError) {\n const entry: HookLogEntry = {\n level: 'error',\n message: `HttpTransport fetch failed after ${maxRetries + 1} attempts`,\n args: [],\n timestamp: new Date().toISOString(),\n hookEvent: 'onError' as HookEvent,\n error: error instanceof Error ? error : new Error(String(error))\n };\n void this.options.onError(entry);\n }\n return false;\n }\n await sleep(backoff);\n backoff = Math.min(backoff * 2, maxBackoff);\n }\n attempt++;\n }\n return false;\n }\n\n /**\n * Cierra el transport: marca el flag `closed`, detiene el timer de\n * `flushInterval` si estaba corriendo, y ejecuta un flush final para\n * entregar lo pendiente.\n *\n * Tras `close()`, todo {@link write} posterior es no-op y\n * {@link isReady} devuelve `false`.\n *\n * @returns {Promise<void>} Resuelve cuando el flush final termina.\n *\n * @see {@link flush}\n */\n async close(): Promise<void> {\n this.closed = true;\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n await this.flush();\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}","import type {\n TransportRecord,\n HookLogEntry,\n HookEvent,\n ILogResource\n} from '../types/index.js';\nimport { LOG_LEVEL_TO_SEVERITY_NUMBER } from '../types/index.js';\nimport { HttpTransport, type HttpTransportOptions } from './HttpTransport.js';\n\n/**\n * Opciones de configuración para {@link OtlpTransport}.\n */\nexport interface OtlpTransportOptions {\n /**\n * URL base del collector OTLP/HTTP, p.ej. `https://collector.example.com:4318`.\n * El transport hace POST a `<endpoint>/v1/logs`.\n */\n endpoint: string;\n /** Atributo de resource `service.name`. Requerido por la spec OTel. */\n serviceName: string;\n /** Atributo de resource `service.version`. Recomendado. */\n serviceVersion?: string;\n /** Atributo de resource `deployment.environment` (production/staging/dev). */\n environment?: string;\n /** Atributos de resource adicionales que se merguean en el bloque `resource`. */\n resourceAttributes?: Record<string, string>;\n /**\n * Nombre de la env var cuyo valor se envía como header\n * `signoz-ingestion-key`. El transport lee `process.env[ingestKeyEnvVar]`\n * al construirse — nunca desde un string literal. Si la env var no está\n * seteada, el header se omite silenciosamente (el collector OTLP usa el\n * path default sin auth).\n */\n ingestKeyEnvVar?: string;\n /** Headers extra que se merguean (Authorization, X-* custom). */\n headers?: Record<string, string>;\n /** Tamaño del batch (records por POST). Default 50. */\n batchSize?: number;\n /** Intervalo de flush en ms. */\n flushInterval?: number;\n /** Tope duro de records bufferizados. Default 10_000. */\n maxBufferSize?: number;\n /** Reintentos máximos por batch. Default 3. */\n maxRetries?: number;\n /** Backoff inicial en ms. Default 250. */\n initialBackoffMs?: number;\n /** Techo de backoff en ms. Default 5_000. */\n maxBackoffMs?: number;\n /** Timeout por fetch en ms. Default 10_000. */\n fetchTimeoutMs?: number;\n /** Hook que se dispara ante falla del transport (drop, agotamiento de reintentos, ...). */\n onError?: (entry: HookLogEntry) => void | Promise<void>;\n}\n\n/**\n * Shape del body JSON de un request OTLP/HTTP. Coincide con el JSON mapping\n * del proto `LogsData` para `/v1/logs`. Ver\n * https://opentelemetry.io/docs/specs/otlp/#json-protobuf-encoding\n */\ninterface OtlpLogsPayload {\n resourceLogs: Array<{\n resource: {\n attributes: Array<{ key: string; value: { stringValue: string } }>;\n };\n scopeLogs: Array<{\n scope: {\n name: string;\n version: string;\n };\n logRecords: Array<OtlpLogRecord>;\n }>;\n }>;\n}\n\n/**\n * Registro individual de log dentro del payload OTLP. Mappea mensaje,\n * severidad, timestamp (en nanosegundos), atributos y contexto de trace al\n * formato `LogRecord` definido por la spec OTel Logs.\n * @see https://opentelemetry.io/docs/specs/otel/logs/data-model/\n */\ninterface OtlpLogRecord {\n timeUnixNano: string;\n observedTimeUnixNano: string;\n severityNumber: number;\n severityText: string;\n body: { stringValue: string };\n attributes?: Array<{ key: string; value: OtlpAttributeValue }>;\n traceId?: string;\n spanId?: string;\n}\n\n/**\n * Unión discriminada que representa un `AnyValue` de OTel para atributos de\n * log. Soporta string, int, double, bool y arrays anidados (homogéneos o\n * mixtos).\n * @see https://opentelemetry.io/docs/specs/otel/common/\n */\ntype OtlpAttributeValue =\n | { stringValue: string }\n | { intValue: number }\n | { doubleValue: number }\n | { boolValue: boolean }\n | { arrayValue: { values: OtlpAttributeValue[] } };\n\n/**\n * Transport OTLP/HTTP para SigNoz (o cualquier backend compatible con OTLP).\n *\n * Extiende {@link HttpTransport} — hereda retry, buffer acotado, status check,\n * close asincrónico y el hook on-error. Overridea únicamente la shape del\n * payload y los headers del request.\n *\n * @example\n * ```ts\n * logger.addTransport({\n * target: new OtlpTransport({\n * endpoint: 'https://otelcollector.example.com:4318',\n * serviceName: 'my-app',\n * serviceVersion: '1.2.3',\n * environment: 'production',\n * ingestKeyEnvVar: 'SIGNOZ_KEY'\n * })\n * });\n * ```\n */\nexport class OtlpTransport extends HttpTransport {\n override readonly name = 'otlp';\n\n private readonly resource: ILogResource;\n /** Resuelto al construir. Nunca se loguea, nunca se escribe a source. */\n private readonly ingestKeyValue: string | undefined;\n\n constructor(options: OtlpTransportOptions) {\n if (!options.endpoint) {\n throw new Error('OtlpTransport: `endpoint` is required');\n }\n if (!options.serviceName) {\n throw new Error('OtlpTransport: `serviceName` is required');\n }\n\n const ingestKey = readIngestKey(options.ingestKeyEnvVar);\n\n const httpOptions: HttpTransportOptions = {\n url: `${stripTrailingSlash(options.endpoint)}/v1/logs`,\n headers: {\n ...(ingestKey ? { 'signoz-ingestion-key': ingestKey } : {}),\n ...(options.headers ?? {})\n },\n batchSize: options.batchSize,\n flushInterval: options.flushInterval,\n maxBufferSize: options.maxBufferSize,\n maxRetries: options.maxRetries,\n initialBackoffMs: options.initialBackoffMs,\n maxBackoffMs: options.maxBackoffMs,\n fetchTimeoutMs: options.fetchTimeoutMs,\n onError: options.onError\n };\n\n super(httpOptions);\n\n this.resource = {\n 'service.name': options.serviceName,\n ...(options.serviceVersion ? { 'service.version': options.serviceVersion } : {}),\n ...(options.environment ? { 'deployment.environment': options.environment } : {}),\n ...(options.resourceAttributes ?? {})\n };\n this.ingestKeyValue = ingestKey;\n }\n\n /**\n * Construye el payload JSON OTLP/HTTP a partir de los records bufferizados.\n * Un bloque `resourceLogs` por batch (sigue la guía de batching del\n * collector OTel). Expuesto para tests y para subclasses custom de transport.\n *\n * @param records - Records de log a serializar dentro del payload.\n * @returns Objeto `LogsData` listo para `JSON.stringify`.\n * @see {@link OtlpLogsPayload}\n */\n buildPayload(records: TransportRecord[]): OtlpLogsPayload {\n const resourceAttrs = Object.entries(this.resource)\n .filter((entry): entry is [string, string] => typeof entry[1] === 'string')\n .map(([key, value]) => ({ key, value: { stringValue: value } }));\n\n return {\n resourceLogs: [\n {\n resource: { attributes: resourceAttrs },\n scopeLogs: [\n {\n scope: {\n name: 'better-logger',\n version: '5.1.0'\n },\n logRecords: records.map(r => this.toLogRecord(r))\n }\n ]\n }\n ]\n };\n }\n\n /**\n * Serializa un batch al body JSON del request OTLP/HTTP. Override de\n * {@link HttpTransport.serializeBody}.\n *\n * @param records - Records del batch a serializar.\n * @returns String JSON listo para usar como `body` del fetch POST.\n */\n protected override serializeBody(records: TransportRecord[]): string {\n return JSON.stringify(this.buildPayload(records));\n }\n\n /**\n * Construye los headers del request. Agrega `signoz-ingestion-key` con el\n * valor resuelto al construir (nunca se loguea, nunca se escribe a source).\n *\n * @returns Record de headers a merguear en el fetch.\n */\n protected override buildHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n ...(this.ingestKeyValue ? { 'signoz-ingestion-key': this.ingestKeyValue } : {}),\n ...(this.options.headers ?? {})\n };\n }\n\n private toLogRecord(record: TransportRecord): OtlpLogRecord {\n const timeUnixNano = String(BigInt(record.time) * 1_000_000n);\n const observedTimeUnixNano = timeUnixNano;\n const severityNumber = LOG_LEVEL_TO_SEVERITY_NUMBER[record.level];\n\n const base: OtlpLogRecord = {\n timeUnixNano,\n observedTimeUnixNano,\n severityNumber,\n severityText: record.severityText,\n body: { stringValue: record.msg }\n };\n\n const attributes = collectAttributes(record);\n if (attributes.length > 0) {\n base.attributes = attributes;\n }\n\n if (record.traceId) base.traceId = record.traceId;\n if (record.spanId) base.spanId = record.spanId;\n\n return base;\n }\n}\n\n// ===== Internal helpers =====\n\nfunction stripTrailingSlash(value: string): string {\n return value.endsWith('/') ? value.slice(0, -1) : value;\n}\n\n/**\n * Lee la ingest API key desde `process.env[name]`. Devuelve `undefined`\n * silenciosamente si `process` no está disponible (bundles estrictos de\n * browser) o si la variable no está seteada. Nunca throwea, nunca loguea\n * el valor.\n *\n * @internal Helper del constructor de {@link OtlpTransport}; no es API pública.\n * @param envVarName - Nombre de la env var a leer.\n * @returns Valor de la key, o `undefined` si no está disponible.\n */\nfunction readIngestKey(envVarName: string | undefined): string | undefined {\n if (!envVarName) return undefined;\n if (typeof process === 'undefined') return undefined;\n const proc = process as { env?: Record<string, string | undefined> };\n const value = proc.env?.[envVarName];\n if (!value) return undefined;\n return value;\n}\n\nfunction collectAttributes(record: TransportRecord): Array<{ key: string; value: OtlpAttributeValue }> {\n const out: Array<{ key: string; value: OtlpAttributeValue }> = [];\n\n if (record.prefix) {\n out.push({ key: 'logger.prefix', value: { stringValue: record.prefix } });\n }\n if (record.tag) {\n out.push({ key: 'logger.tag', value: { stringValue: record.tag } });\n }\n if (record.location) {\n out.push({ key: 'code.filepath', value: { stringValue: record.location.file } });\n out.push({ key: 'code.lineno', value: { intValue: record.location.line } });\n if (record.location.function) {\n out.push({ key: 'code.function', value: { stringValue: record.location.function } });\n }\n }\n\n if (record.attributes) {\n for (const [key, value] of Object.entries(record.attributes)) {\n const mapped = toOtlpAttribute(value);\n if (mapped) out.push({ key, value: mapped });\n }\n }\n\n return out;\n}\n\nfunction toOtlpAttribute(value: unknown): OtlpAttributeValue | null {\n if (value === null || value === undefined) return null;\n if (typeof value === 'string') return { stringValue: value };\n if (typeof value === 'number') {\n if (Number.isInteger(value)) return { intValue: value };\n return { doubleValue: value };\n }\n if (typeof value === 'boolean') return { boolValue: value };\n if (Array.isArray(value)) {\n return {\n arrayValue: {\n values: value\n .map(v => toOtlpAttribute(v))\n .filter((v): v is OtlpAttributeValue => v !== null)\n }\n };\n }\n return { stringValue: JSON.stringify(value) };\n}","import type {\n TransportRecord,\n TransportTarget,\n TransportOptions,\n ITransport,\n ITransportManager,\n LogLevel\n} from '../types/index.js';\nimport { LOG_LEVELS } from '../types/index.js';\nimport { ConsoleTransport } from './ConsoleTransport.js';\nimport { FileTransport } from './FileTransport.js';\nimport { HttpTransport } from './HttpTransport.js';\nimport { OtlpTransport } from './OtlpTransport.js';\n\n/**\n * Genera un id opaco y estable en el tiempo para cada transport añadido al\n * manager. Combina timestamp (base36) con un sufijo aleatorio para evitar\n * colisiones entre adds casi simultáneos.\n *\n * @internal No es API pública — el formato del id es inestable y los callers\n * deben tratarlo como opaco (solo compararlo y pasárselo a `remove`).\n */\nfunction generateTransportId(): string {\n return `transport-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * Firma del constructor de los transports registrados en el registry\n * built-in. Acepta el tipo propio de options del transport; el pass-through\n * se enforcea en registro vía TypeScript pero se borra en runtime.\n */\nexport type TransportConstructor = new (options?: TransportOptions) => ITransport;\n\n/**\n * Registry estático de los transports built-in, con el que se inicializa el\n * registry vivo de cada instancia de {@link TransportManager}. Los customs\n * se añaden al registry por-instancia vía {@link TransportManager.register}\n * (no a este Map módulo-nivel).\n *\n * Los cuatro built-ins registrados automáticamente:\n * - `'console'` → {@link ConsoleTransport}\n * - `'file'` → {@link FileTransport}\n * - `'http'` → {@link HttpTransport}\n * - `'otlp'` → {@link OtlpTransport}\n *\n * @internal No exportado — los callers externos usan\n * {@link TransportManager.register} / {@link TransportManager.listRegistered}.\n */\nconst BUILTIN_REGISTRY: Map<string, TransportConstructor> = new Map([\n ['console', ConsoleTransport as unknown as TransportConstructor],\n ['file', FileTransport as unknown as TransportConstructor],\n ['http', HttpTransport as unknown as TransportConstructor],\n ['otlp', OtlpTransport as unknown as TransportConstructor]\n]);\n\n/**\n * Entrada interna del set de transports activos. Guarda la instancia, las\n * options (con `transform`) y el nivel pre-resuelto a su valor numérico\n * (`levelValue`) para que el filtro en `write()` sea una comparación directa.\n *\n * @internal\n */\ninterface TransportEntry {\n id: string;\n transport: ITransport;\n options: TransportOptions;\n level: LogLevel;\n levelValue: number;\n}\n\n/**\n * Registry y dispatcher de transports. Mantiene el set activo de destinos\n * de log (console, file, http, otlp, o transports custom registrados vía\n * {@link TransportManager.register}) y les dispatcha cada\n * {@link TransportRecord} producido por el Logger.\n *\n * Cada transport added se identifica por un `id` opaco (generado por la\n * propia instancia) que devuelven {@link add} y {@link list}, y que acepta\n * {@link remove}. El registry arranca con los cuatro built-ins\n * (`console` / `file` / `http` / `otlp`) ya cargados; {@link register}\n * añade customs sin poder sobreescribir los built-ins (lanza).\n *\n * El dispatch ({@link write}) aplica el filtro de `level` por transport y\n * el `transform` opcional de {@link TransportOptions}, y captura toda\n * excepción / rechazo de cada transport para que un transport roto nunca\n * rompa el log call del caller. El propio Logger habla con el manager a\n * través del facade {@link TransportBridge} (ver\n * `Logger.addTransport` / `Logger.getTransportManager`).\n *\n * Implementa {@link ITransportManager}.\n *\n * @example\n * // Manager con nivel default y transports añadidos por nombre\n * const tm = new TransportManager('info');\n * tm.add({ target: 'console' });\n * tm.add({ target: 'file', options: { path: './app.log' } });\n *\n * @example\n * // Registrar un transport custom y usarlo por nombre\n * tm.register('datadog', DatadogTransport);\n * tm.add({ target: 'datadog', options: { apiKey: env('DD_KEY') } });\n *\n * @example\n * // Pasar una instancia de ITransport directamente (sin pasar por registry)\n * tm.add({ target: new MyTransport(), level: 'warn' });\n *\n * @example\n * // Dispatch + ciclo de vida\n * await tm.write(record);\n * await tm.flush(); // flusha todos los buffered\n * await tm.close(); // flush + close + clear\n *\n * @see {@link ITransportManager}\n * @see {@link TransportTarget}\n * @see {@link TransportBridge}\n */\nexport class TransportManager implements ITransportManager {\n private transports: Map<string, TransportEntry> = new Map();\n private defaultLevel: LogLevel = 'info';\n private readonly registry: Map<string, TransportConstructor> = new Map(BUILTIN_REGISTRY);\n\n /**\n * Crea un nuevo manager.\n *\n * @param {LogLevel} [defaultLevel='info'] - Nivel mínimo por defecto\n * para transports añadidos sin `level` explícito en su\n * {@link TransportTarget}. Records con `levelValue` inferior al del\n * transport se descartan en {@link write}.\n *\n * @example\n * const tm = new TransportManager('debug'); // todo pasa salvo filter propio\n */\n constructor(defaultLevel?: LogLevel) {\n if (defaultLevel) {\n this.defaultLevel = defaultLevel;\n }\n }\n\n /**\n * Registra un constructor de transport bajo un nombre string. Tras el\n * registro, `add({ target: name, options })` instancia ese transport con\n * las options pasadas.\n *\n * No se puede sobreescribir un built-in (`console` / `file` / `http` /\n * `otlp`): lanza para evitar silenciar un transport crítico por un\n * accidente de naming. El registro es por-instancia (no comparte entre\n * managers).\n *\n * @param {string} name - Nombre bajo el que registrar (usado luego como\n * `target` en {@link TransportTarget}).\n * @param {TransportConstructor} ctor - Constructor que acepta\n * {@link TransportOptions} y devuelve un {@link ITransport}.\n * @throws {Error} Si `name` colisiona con un built-in del registry.\n *\n * @example\n * tm.register('datadog', DatadogTransport);\n * tm.add({ target: 'datadog', options: { apiKey: env('DD_KEY') } });\n *\n * @see {@link listRegistered}\n */\n register(name: string, ctor: TransportConstructor): void {\n if (BUILTIN_REGISTRY.has(name)) {\n throw new Error(`TransportManager.register: '${name}' is a built-in and cannot be overridden`);\n }\n this.registry.set(name, ctor);\n }\n\n /**\n * Lista los nombres de transports registrados en esta instancia\n * (built-ins + customs añadidos vía {@link register}).\n *\n * @returns {string[]} Nombres registrados. Incluye siempre los cuatro\n * built-ins.\n *\n * @example\n * tm.register('loki', LokiTransport);\n * tm.listRegistered(); // ['console', 'file', 'http', 'otlp', 'loki']\n */\n listRegistered(): string[] {\n return [...this.registry.keys()];\n }\n\n /**\n * Añade un transport al set activo y devuelve su id opaco.\n *\n * Acepta dos formas de `target`:\n * - **string**: se resuelve contra el registry (built-ins + customs\n * vía {@link register}). Lanza si el nombre no existe, listando los\n * registrados para facilitar el debug.\n * - **ITransport instancia**: se registra tal cual, sin pasar por el\n * registry. Útil para transports one-off o cuya configuración no\n * encaja en un constructor reutilizable.\n *\n * El `level` (explícito en el target o `defaultLevel` del manager) fija\n * el filtro por transport — los records con `levelValue` inferior se\n * descartan en {@link write}. El `transform` opcional de\n * {@link TransportOptions} se aplica por transport antes de delegar al\n * `write` concreto.\n *\n * @param {TransportTarget} target - Especificación del transport a añadir.\n * @returns {string} Id opaco del transport añadido. Úsalo con\n * {@link remove}; aparece en {@link list}.\n * @throws {Error} Si `target.target` es un string no presente en el\n * registry.\n *\n * @example\n * // Por nombre (built-in o custom registrado)\n * const id = tm.add({ target: 'console' });\n *\n * @example\n * // Instancia directa con nivel y transform propios\n * const id = tm.add({\n * target: new MyTransport(),\n * level: 'warn',\n * options: { transform: r => r.level === 'debug' ? null : r }\n * });\n *\n * @see {@link TransportTarget}\n */\n add(target: TransportTarget): string {\n const id = generateTransportId();\n const level = target.level || this.defaultLevel;\n\n let transport: ITransport;\n\n if (typeof target.target === 'string') {\n const ctor = this.registry.get(target.target);\n if (!ctor) {\n throw new Error(\n `TransportManager.add: unknown transport name '${target.target}'. ` +\n `Registered: ${this.listRegistered().join(', ')}`\n );\n }\n transport = new ctor(target.options ?? {});\n } else {\n transport = target.target;\n }\n\n const entry: TransportEntry = {\n id,\n transport,\n options: target.options || {},\n level,\n levelValue: LOG_LEVELS[level]\n };\n\n this.transports.set(id, entry);\n return id;\n }\n\n /**\n * Elimina un transport del set por su id. Si el transport expone\n * `close()`, lo invoca para liberar recursos (timers, sockets, file\n * handles). Si `close()` devuelve una Promise, su eventual rechazo se\n * ignora de forma best-effort — los errores de close durante la\n * remoción no se propagan al caller.\n *\n * @param {string} id - Id devuelto por {@link add}.\n * @returns {boolean} `true` si había un transport con ese id (y se\n * eliminó), `false` si el id no existía.\n *\n * @example\n * const id = tm.add({ target: 'file', options: { path: './app.log' } });\n * tm.remove(id); // true — FileTransport.close() se invoca\n */\n remove(id: string): boolean {\n const entry = this.transports.get(id);\n if (entry) {\n const closeResult = entry.transport.close?.();\n if (closeResult instanceof Promise) {\n void closeResult.catch(() => {\n // los fallos de close son best-effort durante remove\n });\n }\n return this.transports.delete(id);\n }\n return false;\n }\n\n /**\n * Dispatcha un {@link TransportRecord} a todos los transports activos\n * que pasen el filtro de nivel.\n *\n * Por cada transport, en orden:\n * 1. **Filtro de nivel**: si `record.levelValue < entry.levelValue`,\n * se skipa (ese transport no recibe este record).\n * 2. **Transform**: si `entry.options.transform` está seteado, se\n * aplica al record. Si devuelve `null`, el record se droppea para\n * este transport (no para los demás).\n * 3. **Write**: se llama a `transport.write(transformedRecord)`. Si\n * devuelve una Promise, se añade al batch await. Toda excepción\n * sincrónica o rechazo de Promise se captura y se loguea por\n * consola — el log call original del caller nunca rompe por un\n * transport roto.\n *\n * El método es async: retorna después de que todos los transports hayan\n * resuelto (o fallado) su write. Para fire-and-forget desde el Logger,\n * el caller puede ignorar la Promise.\n *\n * @param {TransportRecord} record - Record a dispatchar.\n * @returns {Promise<void>} Resuelve cuando todos los writes terminaron\n * (exitosos o fallidos). Nunca rechaza.\n *\n * @example\n * await tm.write({\n * level: 'info', levelValue: 1, severityNumber: 9, severityText: 'INFO',\n * time: Date.now(), msg: 'boot ok'\n * });\n *\n * @see {@link TransportRecord}\n */\n async write(record: TransportRecord): Promise<void> {\n const promises: Promise<unknown>[] = [];\n\n for (const entry of this.transports.values()) {\n if (record.levelValue < entry.levelValue) {\n continue;\n }\n\n let transformedRecord: TransportRecord = record;\n if (entry.options.transform) {\n const result = entry.options.transform(record);\n if (result === null) continue;\n transformedRecord = result;\n }\n\n try {\n const result = entry.transport.write(transformedRecord);\n if (result instanceof Promise) {\n promises.push(\n result.catch(err => {\n // Surfear los fallos de write por consola (sin drop silencioso)\n // sin throwear — el path principal del logger se queda sync.\n // eslint-disable-next-line no-console\n console.error('TransportManager.write: transport failed:', err);\n })\n );\n }\n } catch (error) {\n // Los errores sincrónicos de write se surfean pero nunca rompen el log call.\n // eslint-disable-next-line no-console\n console.error('TransportManager.write: transport threw synchronously:', error);\n }\n }\n\n await Promise.all(promises);\n }\n\n /**\n * Flushea todos los transports que expongan `flush()` (buffered transports\n * como {@link HttpTransport}, {@link FileTransport}, {@link OtlpTransport}).\n * Los transports sin `flush()` (p.ej. {@link ConsoleTransport}) se\n * skipan sin error.\n *\n * Errores de flush (sync throw o Promise rejection) se capturan y\n * loguean por consola — nunca propagan al caller. Útil para forzar el\n * envío del batch pendiente antes de un graceful shutdown.\n *\n * @returns {Promise<void>} Resuelve cuando todos los flushes terminaron.\n *\n * @example\n * await tm.flush();\n */\n async flush(): Promise<void> {\n const promises: Promise<unknown>[] = [];\n\n for (const entry of this.transports.values()) {\n if (entry.transport.flush) {\n try {\n const result = entry.transport.flush();\n if (result instanceof Promise) {\n promises.push(result.catch(err => {\n // eslint-disable-next-line no-console\n console.error('TransportManager.flush: transport failed:', err);\n }));\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('TransportManager.flush: transport threw synchronously:', error);\n }\n }\n }\n\n await Promise.all(promises);\n }\n\n /**\n * Shutdown ordenado: flushea todos los transports buffered, luego\n * invoca `close()` en cada uno, y finalmente limpia el set interno.\n * Tras esto la instancia queda sin transports — reusarla requiere\n * `add()` de nuevo.\n *\n * Los errores de close (sync throw o Promise rejection) se capturan y\n * loguean — nunca propagan al caller.\n *\n * @returns {Promise<void>} Resuelve cuando flush + close de todos los\n * transports terminaron.\n *\n * @example\n * // Shutdown limpio del proceso\n * process.on('SIGTERM', async () => {\n * await logger.getTransportManager()?.close();\n * process.exit(0);\n * });\n */\n async close(): Promise<void> {\n await this.flush();\n\n const promises: Promise<unknown>[] = [];\n\n for (const entry of this.transports.values()) {\n if (entry.transport.close) {\n try {\n const result = entry.transport.close();\n if (result instanceof Promise) {\n promises.push(result.catch(err => {\n // eslint-disable-next-line no-console\n console.error('TransportManager.close: transport failed:', err);\n }));\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('TransportManager.close: transport threw synchronously:', error);\n }\n }\n }\n\n await Promise.all(promises);\n this.transports.clear();\n }\n\n /**\n * Número de transports actualmente en el set activo.\n *\n * @returns {number}\n *\n * @example\n * if (tm.count === 0) console.warn('no transports configured');\n */\n get count(): number {\n return this.transports.size;\n }\n\n /**\n * Lista los ids opacos de los transports actualmente activos.\n *\n * @returns {string[]} Array de ids (mismo formato opaco que devolvió\n * {@link add}).\n *\n * @example\n * for (const id of tm.list()) {\n * console.log('removing transport', id);\n * tm.remove(id);\n * }\n */\n list(): string[] {\n return Array.from(this.transports.keys());\n }\n}"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const require_styling = require("./styling-
|
|
2
|
-
const require_environment_detector = require("./environment-detector-
|
|
1
|
+
const require_styling = require("./styling-Cel2wPRy.cjs");
|
|
2
|
+
const require_environment_detector = require("./environment-detector-D-tHkKWA.cjs");
|
|
3
3
|
//#region src/utils/stackTrace.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Comprueba si un nombre de archivo corresponde a un archivo minificado/bundle
|
|
6
6
|
*/
|
|
7
7
|
function isMinifiedFile(filename) {
|
|
8
8
|
return [
|
|
@@ -18,13 +18,13 @@ function isMinifiedFile(filename) {
|
|
|
18
18
|
].some((pattern) => pattern.test(filename));
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Extrae un nombre de archivo limpio a partir de la ruta completa
|
|
22
22
|
*/
|
|
23
23
|
function extractCleanFilename(fullPath) {
|
|
24
24
|
return (fullPath.split(/[/\\]/).pop() || fullPath).replace(/\.min\.js$/, ".js").replace(/\.bundle\.js$/, ".js").replace(/\.chunk\.js$/, ".js").replace(/-[\w\d]{8,}\.js$/, ".js").replace(/Logger-[A-Za-z0-9]+\.js$/, "logger.ts");
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* Parsea el stack trace actual para extraer la información del caller
|
|
28
28
|
*/
|
|
29
29
|
function parseStackTrace() {
|
|
30
30
|
try {
|
|
@@ -221,7 +221,7 @@ const CLI_LEVEL_MAP = {
|
|
|
221
221
|
//#endregion
|
|
222
222
|
//#region src/utils/ansi-colors.ts
|
|
223
223
|
/**
|
|
224
|
-
*
|
|
224
|
+
* Códigos de color ANSI para estilos en terminal
|
|
225
225
|
*/
|
|
226
226
|
const ANSI_CODES = {
|
|
227
227
|
black: 30,
|
|
@@ -275,7 +275,7 @@ const ANSI_CODES = {
|
|
|
275
275
|
resetStrikethrough: 29
|
|
276
276
|
};
|
|
277
277
|
/**
|
|
278
|
-
* ANSI
|
|
278
|
+
* Presets de colores ANSI para los distintos niveles de log
|
|
279
279
|
*/
|
|
280
280
|
const ANSI_LEVEL_STYLES = {
|
|
281
281
|
trace: {
|
|
@@ -319,60 +319,60 @@ const ANSI_LEVEL_STYLES = {
|
|
|
319
319
|
}
|
|
320
320
|
};
|
|
321
321
|
/**
|
|
322
|
-
*
|
|
322
|
+
* Crea una secuencia de escape ANSI
|
|
323
323
|
*/
|
|
324
324
|
function ansi(code) {
|
|
325
325
|
return `\x1b[${(Array.isArray(code) ? code : [code]).join(";")}m`;
|
|
326
326
|
}
|
|
327
327
|
/**
|
|
328
|
-
*
|
|
328
|
+
* Aplica estilos ANSI al texto
|
|
329
329
|
*/
|
|
330
330
|
function ansiStyle(text, codes) {
|
|
331
331
|
if (!require_environment_detector.supportsANSI()) return text;
|
|
332
332
|
return `${ansi(codes)}${text}${ansi(ANSI_CODES.reset)}`;
|
|
333
333
|
}
|
|
334
334
|
/**
|
|
335
|
-
*
|
|
335
|
+
* Aplica color al texto
|
|
336
336
|
*/
|
|
337
337
|
function ansiColor(text, color) {
|
|
338
338
|
return ansiStyle(text, [color]);
|
|
339
339
|
}
|
|
340
340
|
/**
|
|
341
|
-
*
|
|
341
|
+
* Aplica color de fondo al texto
|
|
342
342
|
*/
|
|
343
343
|
function ansiBackground(text, bg, color) {
|
|
344
344
|
if (color) return ansiStyle(text, [color, bg]);
|
|
345
345
|
return ansiStyle(text, [bg]);
|
|
346
346
|
}
|
|
347
347
|
/**
|
|
348
|
-
*
|
|
348
|
+
* Pone el texto en bold
|
|
349
349
|
*/
|
|
350
350
|
function ansiBold(text) {
|
|
351
351
|
return ansiStyle(text, [ANSI_CODES.bold]);
|
|
352
352
|
}
|
|
353
353
|
/**
|
|
354
|
-
*
|
|
354
|
+
* Aplica dim al texto
|
|
355
355
|
*/
|
|
356
356
|
function ansiDim(text) {
|
|
357
357
|
return ansiStyle(text, [ANSI_CODES.dim]);
|
|
358
358
|
}
|
|
359
359
|
/**
|
|
360
|
-
*
|
|
360
|
+
* Subraya el texto
|
|
361
361
|
*/
|
|
362
362
|
function ansiUnderline(text) {
|
|
363
363
|
return ansiStyle(text, [ANSI_CODES.underline]);
|
|
364
364
|
}
|
|
365
365
|
/**
|
|
366
|
-
*
|
|
366
|
+
* Formatters especializados para distintos contextos
|
|
367
367
|
*/
|
|
368
368
|
/**
|
|
369
|
-
*
|
|
369
|
+
* Formatea el timestamp para terminal
|
|
370
370
|
*/
|
|
371
371
|
function formatTimestampANSI(timestamp) {
|
|
372
372
|
return ansiColor(timestamp, ANSI_CODES.brightBlack);
|
|
373
373
|
}
|
|
374
374
|
/**
|
|
375
|
-
*
|
|
375
|
+
* Formatea el nivel de log para terminal
|
|
376
376
|
*/
|
|
377
377
|
function formatLogLevelANSI(level, useEmojis = true) {
|
|
378
378
|
const style = ANSI_LEVEL_STYLES[level];
|
|
@@ -384,7 +384,7 @@ function formatLogLevelANSI(level, useEmojis = true) {
|
|
|
384
384
|
return ansiStyle(`${emoji} ${label}`, codes);
|
|
385
385
|
}
|
|
386
386
|
/**
|
|
387
|
-
*
|
|
387
|
+
* Formatea el mensaje de success para terminal
|
|
388
388
|
*/
|
|
389
389
|
function formatSuccessANSI(message, useEmojis = true) {
|
|
390
390
|
const style = ANSI_LEVEL_STYLES.success;
|
|
@@ -393,14 +393,14 @@ function formatSuccessANSI(message, useEmojis = true) {
|
|
|
393
393
|
return `${ansiStyle(`${emoji} SUCCESS:`, [style.color, ...style.styles])} ${message}`;
|
|
394
394
|
}
|
|
395
395
|
/**
|
|
396
|
-
*
|
|
396
|
+
* Formatea el prefix para terminal
|
|
397
397
|
*/
|
|
398
398
|
function formatPrefixANSI(prefix) {
|
|
399
399
|
if (!require_environment_detector.supportsANSI()) return `[${prefix}]`;
|
|
400
400
|
return ansiStyle(`[${prefix}]`, [ANSI_CODES.cyan, ANSI_CODES.bold]);
|
|
401
401
|
}
|
|
402
402
|
/**
|
|
403
|
-
*
|
|
403
|
+
* Formatea la ubicación del archivo para terminal
|
|
404
404
|
*/
|
|
405
405
|
function formatLocationANSI(location) {
|
|
406
406
|
if (!require_environment_detector.supportsANSI()) return `(${location})`;
|
|
@@ -409,7 +409,7 @@ function formatLocationANSI(location) {
|
|
|
409
409
|
//#endregion
|
|
410
410
|
//#region src/utils/output.ts
|
|
411
411
|
/**
|
|
412
|
-
*
|
|
412
|
+
* Detecta la preferencia de theme actual de DevTools
|
|
413
413
|
*/
|
|
414
414
|
function detectDevToolsTheme() {
|
|
415
415
|
try {
|
|
@@ -421,13 +421,13 @@ function detectDevToolsTheme() {
|
|
|
421
421
|
}
|
|
422
422
|
}
|
|
423
423
|
/**
|
|
424
|
-
*
|
|
424
|
+
* Obtiene el color adaptativo según el theme actual de DevTools
|
|
425
425
|
*/
|
|
426
426
|
function getAdaptiveColor(colors, theme) {
|
|
427
427
|
return colors[theme ?? detectDevToolsTheme()];
|
|
428
428
|
}
|
|
429
429
|
/**
|
|
430
|
-
*
|
|
430
|
+
* Registra un listener de cambios de theme para actualizaciones dinámicas
|
|
431
431
|
*/
|
|
432
432
|
function setupThemeChangeListener(callback) {
|
|
433
433
|
try {
|
|
@@ -450,11 +450,18 @@ function setupThemeChangeListener(callback) {
|
|
|
450
450
|
}
|
|
451
451
|
}
|
|
452
452
|
/**
|
|
453
|
-
*
|
|
454
|
-
*
|
|
453
|
+
* Crea output estilizado para consola con múltiples formatters `%c`
|
|
454
|
+
* Se adapta automáticamente al entorno (browser o terminal)
|
|
455
455
|
*/
|
|
456
456
|
function createStyledOutput(level, levelStyles, prefix, message, stackInfo, autoDetectTheme = true, presetStyles, presetName) {
|
|
457
|
-
const levelConfig = levelStyles[level]
|
|
457
|
+
const levelConfig = levelStyles[level] ?? {
|
|
458
|
+
emoji: "📝",
|
|
459
|
+
label: level.toUpperCase(),
|
|
460
|
+
background: "#6c757d",
|
|
461
|
+
color: "#ffffff",
|
|
462
|
+
border: "1px solid #6c757d",
|
|
463
|
+
shadow: "none"
|
|
464
|
+
};
|
|
458
465
|
const timestamp = formatTimestamp();
|
|
459
466
|
if (require_environment_detector.getEnvironment() !== "browser" && require_environment_detector.supportsANSI()) return createTerminalOutput(level, message, timestamp, prefix, stackInfo);
|
|
460
467
|
const currentTheme = autoDetectTheme ? detectDevToolsTheme() : "light";
|
|
@@ -478,8 +485,8 @@ function createStyledOutput(level, levelStyles, prefix, message, stackInfo, auto
|
|
|
478
485
|
return [format, ...styles];
|
|
479
486
|
}
|
|
480
487
|
/**
|
|
481
|
-
*
|
|
482
|
-
* runtime
|
|
488
|
+
* Construye una línea apta para terminal usando los helpers ANSI. Se usa
|
|
489
|
+
* cuando el runtime no es browser pero soporta códigos ANSI (TTY, terminal moderna).
|
|
483
490
|
*
|
|
484
491
|
* Layout: `[HH:MM:SS.mmm] [LEVEL] [prefix] message (file:line)`
|
|
485
492
|
*/
|
|
@@ -497,8 +504,8 @@ function createTerminalOutput(level, message, timestamp, prefix, stackInfo) {
|
|
|
497
504
|
//#endregion
|
|
498
505
|
//#region src/utils/formatting.ts
|
|
499
506
|
/**
|
|
500
|
-
*
|
|
501
|
-
*
|
|
507
|
+
* Crea output en texto plano para cualquier entorno
|
|
508
|
+
* Sin colores ni estilos — máxima compatibilidad
|
|
502
509
|
*/
|
|
503
510
|
function createPlainOutput(level, message, prefix, stackInfo) {
|
|
504
511
|
let output = `[${formatTimestamp().slice(11, 23)}] [${level.toUpperCase()}]`;
|
|
@@ -508,8 +515,8 @@ function createPlainOutput(level, message, prefix, stackInfo) {
|
|
|
508
515
|
return output;
|
|
509
516
|
}
|
|
510
517
|
/**
|
|
511
|
-
*
|
|
512
|
-
*
|
|
518
|
+
* Obtiene el método de console adecuado para el nivel
|
|
519
|
+
* Usa distintos métodos de console para mejor semántica
|
|
513
520
|
*/
|
|
514
521
|
function getConsoleMethod(level) {
|
|
515
522
|
switch (level) {
|
|
@@ -522,7 +529,7 @@ function getConsoleMethod(level) {
|
|
|
522
529
|
}
|
|
523
530
|
}
|
|
524
531
|
/**
|
|
525
|
-
*
|
|
532
|
+
* Crea un objeto simple de log entry para los handlers
|
|
526
533
|
*/
|
|
527
534
|
function createLogEntry(level, message, args, prefix, stackInfo) {
|
|
528
535
|
return {
|
|
@@ -541,7 +548,7 @@ function createLogEntry(level, message, args, prefix, stackInfo) {
|
|
|
541
548
|
};
|
|
542
549
|
}
|
|
543
550
|
/**
|
|
544
|
-
*
|
|
551
|
+
* Formatea datos para visualización en tabla como texto plano
|
|
545
552
|
*/
|
|
546
553
|
function formatTablePlain(data, columns) {
|
|
547
554
|
try {
|
|
@@ -554,11 +561,11 @@ function formatTablePlain(data, columns) {
|
|
|
554
561
|
if (typeof data === "object" && data !== null) return Object.entries(data).map(([key, value]) => `${key}: ${value}`).join("\n");
|
|
555
562
|
return String(data);
|
|
556
563
|
} catch (error) {
|
|
557
|
-
return `[Error
|
|
564
|
+
return `[Error al formatear la tabla: ${error}]`;
|
|
558
565
|
}
|
|
559
566
|
}
|
|
560
567
|
/**
|
|
561
|
-
*
|
|
568
|
+
* Serialización segura de objetos para logging
|
|
562
569
|
*/
|
|
563
570
|
function safeSerialize(obj, maxDepth = 3, currentDepth = 0) {
|
|
564
571
|
if (currentDepth >= maxDepth) return "[Max Depth Reached]";
|
|
@@ -695,4 +702,4 @@ Object.defineProperty(exports, "setupThemeChangeListener", {
|
|
|
695
702
|
}
|
|
696
703
|
});
|
|
697
704
|
|
|
698
|
-
//# sourceMappingURL=utils-
|
|
705
|
+
//# sourceMappingURL=utils-W_cxqriN.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-W_cxqriN.cjs","names":["supportsANSI","getEnvironment","supportsANSI","StyleBuilder"],"sources":["../../src/utils/stackTrace.ts","../../src/utils/timestamps.ts","../../src/constants.ts","../../src/utils/ansi-colors.ts","../../src/utils/output.ts","../../src/utils/formatting.ts"],"sourcesContent":["/**\n * @fileoverview Utilidades de parseo de stack traces para Advanced Logger\n */\n\nimport type { StackInfo } from '../types/index.js';\n\n/**\n * Comprueba si un nombre de archivo corresponde a un archivo minificado/bundle\n */\nfunction isMinifiedFile(filename: string): boolean {\n // Salta si el nombre contiene patrones de minificado\n const minifiedPatterns = [\n /\\.min\\.js$/,\n /\\.bundle\\.js$/,\n /\\.chunk\\.js$/,\n /-[\\w\\d]{8,}\\.js$/, // Nombres basados en hash\n /Logger-[A-Za-z0-9]+\\.js$/, // Bundles de Logger\n /node_modules/,\n /dist\\//,\n /build\\//,\n /\\.mjs$/\n ];\n\n return minifiedPatterns.some(pattern => pattern.test(filename));\n}\n\n/**\n * Extrae un nombre de archivo limpio a partir de la ruta completa\n */\nfunction extractCleanFilename(fullPath: string): string {\n // Extrae solo el nombre de archivo de la ruta completa\n const filename = fullPath.split(/[/\\\\]/).pop() || fullPath;\n\n // Elimina sufijos comunes de build/minificado\n return filename\n .replace(/\\.min\\.js$/, '.js')\n .replace(/\\.bundle\\.js$/, '.js')\n .replace(/\\.chunk\\.js$/, '.js')\n .replace(/-[\\w\\d]{8,}\\.js$/, '.js')\n .replace(/Logger-[A-Za-z0-9]+\\.js$/, 'logger.ts');\n}\n\n/**\n * Parsea el stack trace actual para extraer la información del caller\n */\nexport function parseStackTrace(): StackInfo | null {\n try {\n const stack = new Error().stack;\n if (!stack) {\n return null;\n }\n\n const lines = stack.split('\\n').filter(line => line.trim());\n\n // Busca el primer caller que no provenga de métodos de Logger\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n\n if (!line) {\n continue;\n }\n\n // Salta si la línea contiene métodos de Logger o parseStackTrace\n if (line.includes('parseStackTrace') ||\n line.includes('Logger.') ||\n line.includes('.log(') ||\n line.includes('createStyledOutput')) {\n continue;\n }\n\n // Parsea distintos formatos de stack trace\n let match;\n\n // Formato Chrome: \"at functionName (file:line:column)\" o \"at file:line:column\"\n const chromeMatch = line.match(/at\\s+(?:(.+?)\\s+\\()?(.+?):(\\d+):(\\d+)\\)?$/);\n if (chromeMatch) {\n const filename = chromeMatch[2];\n // Salta archivos minificados/bundle\n if (filename && isMinifiedFile(filename)) {\n continue;\n }\n match = chromeMatch;\n } else {\n // Formato Firefox: \"functionName@file:line:column\"\n const firefoxMatch = line.match(/(.+?)@(.+?):(\\d+):(\\d+)$/);\n if (firefoxMatch) {\n const filename = firefoxMatch[2];\n // Salta archivos minificados/bundle\n if (filename && isMinifiedFile(filename)) {\n continue;\n }\n match = firefoxMatch;\n } else {\n // Formato Safari/otros\n const safariMatch = line.match(/(\\S+)?@(.+?):(\\d+):(\\d+)$/);\n if (safariMatch) {\n const filename = safariMatch[2];\n // Salta archivos minificados/bundle\n if (filename && isMinifiedFile(filename)) {\n continue;\n }\n match = safariMatch;\n }\n }\n }\n\n if (!match) {\n continue;\n }\n\n const [, functionName, file, lineStr, columnStr] = match;\n\n if (!file) {\n continue;\n }\n\n // Usa la extracción limpia para evitar archivos minificados\n const cleanFileName = extractCleanFilename(file).split('?')[0];\n if (!cleanFileName) {\n continue;\n }\n\n // Parsea los números de línea y columna\n const lineNum = lineStr ? parseInt(lineStr, 10) : 0;\n const columnNum = columnStr ? parseInt(columnStr, 10) : 0;\n\n // Limpia el nombre de la función\n const cleanFunction = functionName && functionName.trim()\n ? functionName.trim()\n : undefined;\n \n return {\n file: cleanFileName ? cleanFileName : 'unknown',\n line: lineNum,\n column: columnNum,\n function: cleanFunction\n };\n }\n \n return null;\n } catch {\n return null;\n }\n}","/**\n * @fileoverview Timestamp utilities for Advanced Logger\n */\n\n/**\n * Formats the timestamp using modern Date API\n */\nexport function formatTimestamp(): string {\n try {\n // Use modern Temporal API if available, fallback to Date\n const now = new Date();\n return now.toISOString();\n } catch {\n return new Date().toISOString();\n }\n}","/**\n * @fileoverview Global constants for Advanced Logger\n */\n\nimport type { LogLevel, AdaptiveColors, CLILogLevel, Verbosity } from './types/index.js';\nimport type { LevelStyleConfig } from './utils/index.js';\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CONFIG = {\n verbosity: 'info' as const,\n enableColors: true,\n enableTimestamps: true,\n enableStackTrace: true,\n theme: 'default' as const,\n bannerType: 'simple' as const,\n bufferSize: 1000,\n autoDetectTheme: true,\n outputFormat: 'auto' as const,\n} as const;\n\n/**\n * Basic level styles for core module (minimal CSS styling)\n */\nexport const LEVEL_STYLES: Record<LogLevel | 'success', LevelStyleConfig> = {\n trace: {\n emoji: '🔬',\n label: 'TRACE',\n background: 'linear-gradient(90deg, #495057, #343a40)',\n color: '#dee2e6',\n border: '1px solid #495057',\n shadow: '0 1px 2px rgba(73, 80, 87, 0.3)'\n },\n debug: {\n emoji: '🔍',\n label: 'DEBUG',\n background: 'linear-gradient(90deg, #6c757d, #495057)',\n color: '#ffffff',\n border: '1px solid #6c757d',\n shadow: '0 2px 4px rgba(108, 117, 125, 0.3)'\n },\n info: {\n emoji: 'ℹ️',\n label: 'INFO',\n background: 'linear-gradient(90deg, #007bff, #0056b3)',\n color: '#ffffff',\n border: '1px solid #007bff',\n shadow: '0 2px 4px rgba(0, 123, 255, 0.3)'\n },\n warn: {\n emoji: '⚠️',\n label: 'WARN',\n background: 'linear-gradient(90deg, #ffc107, #e0a800)',\n color: '#000000',\n border: '1px solid #ffc107',\n shadow: '0 2px 4px rgba(255, 193, 7, 0.3)'\n },\n error: {\n emoji: '❌',\n label: 'ERROR',\n background: 'linear-gradient(90deg, #dc3545, #c82333)',\n color: '#ffffff',\n border: '1px solid #dc3545',\n shadow: '0 2px 4px rgba(220, 53, 69, 0.3)'\n },\n critical: {\n emoji: '🚨',\n label: 'CRITICAL',\n background: 'linear-gradient(90deg, #8B0000, #FF0000)',\n color: '#ffffff',\n border: '2px solid #FF0000',\n shadow: '0 4px 8px rgba(255, 0, 0, 0.4)'\n },\n success: {\n emoji: '✅',\n label: 'SUCCESS',\n background: 'linear-gradient(90deg, #28a745, #1e7e34)',\n color: '#ffffff',\n border: '1px solid #28a745',\n shadow: '0 2px 4px rgba(40, 167, 69, 0.3)'\n }\n} as const;\n\n/**\n * Time parsing constants for relative time filters\n */\nexport const TIME_UNITS = {\n ms: 1,\n s: 1000,\n m: 60 * 1000,\n h: 60 * 60 * 1000,\n d: 24 * 60 * 60 * 1000,\n} as const;\n\n/**\n * Export format definitions with extensions\n */\nexport const EXPORT_FORMATS = {\n json: { extension: '.json', mimeType: 'application/json' },\n csv: { extension: '.csv', mimeType: 'text/csv' },\n markdown: { extension: '.md', mimeType: 'text/markdown' },\n plain: { extension: '.txt', mimeType: 'text/plain' },\n html: { extension: '.html', mimeType: 'text/html' },\n} as const;\n\n/**\n * Adaptive color configurations for DevTools theme compatibility\n */\nexport const ADAPTIVE_COLORS = {\n timestamp: {\n light: '#666666',\n dark: '#a0a0a0',\n },\n messageText: {\n light: '#2d3748',\n dark: '#f7fafc',\n },\n prefix: {\n light: '#2d3748',\n dark: '#e2e8f0',\n },\n prefixBackground: {\n light: '#2d3748',\n dark: '#4a5568',\n },\n location: {\n light: '#718096',\n dark: '#a0aec0',\n },\n} as const satisfies Record<string, AdaptiveColors>;\n\n/**\n * CLI log level to internal verbosity mapping\n */\nexport const CLI_LEVEL_MAP: Record<CLILogLevel, { verbosity: Verbosity; showPrimitives: boolean }> = {\n silent: { verbosity: 'silent', showPrimitives: false },\n quiet: { verbosity: 'error', showPrimitives: false },\n normal: { verbosity: 'info', showPrimitives: true },\n verbose: { verbosity: 'debug', showPrimitives: true },\n debug: { verbosity: 'debug', showPrimitives: true },\n} as const;","/**\n * @fileoverview Utilidades de colores ANSI para entornos de terminal\n */\n\nimport type { LogLevel } from '../types/index.js';\nimport { supportsANSI } from './environment-detector.js';\n\n/**\n * Códigos de color ANSI para estilos en terminal\n */\nexport const ANSI_CODES = {\n // Colors\n black: 30,\n red: 31,\n green: 32,\n yellow: 33,\n blue: 34,\n magenta: 35,\n cyan: 36,\n white: 37,\n\n // Bright colors\n brightBlack: 90,\n brightRed: 91,\n brightGreen: 92,\n brightYellow: 93,\n brightBlue: 94,\n brightMagenta: 95,\n brightCyan: 96,\n brightWhite: 97,\n\n // Background colors\n bgBlack: 40,\n bgRed: 41,\n bgGreen: 42,\n bgYellow: 43,\n bgBlue: 44,\n bgMagenta: 45,\n bgCyan: 46,\n bgWhite: 47,\n\n // Background bright colors\n bgBrightBlack: 100,\n bgBrightRed: 101,\n bgBrightGreen: 102,\n bgBrightYellow: 103,\n bgBrightBlue: 104,\n bgBrightMagenta: 105,\n bgBrightCyan: 106,\n bgBrightWhite: 107,\n\n // Styles\n reset: 0,\n bold: 1,\n dim: 2,\n italic: 3,\n underline: 4,\n blink: 5,\n inverse: 7,\n hidden: 8,\n strikethrough: 9,\n\n // Reset specific\n resetBold: 22,\n resetDim: 22,\n resetItalic: 23,\n resetUnderline: 24,\n resetBlink: 25,\n resetInverse: 27,\n resetHidden: 28,\n resetStrikethrough: 29\n} as const;\n\n/**\n * Presets de colores ANSI para los distintos niveles de log\n */\nexport const ANSI_LEVEL_STYLES: Record<LogLevel | 'success', {\n color: number;\n background?: number;\n styles: number[];\n emoji: string;\n}> = {\n trace: {\n color: ANSI_CODES.brightBlack,\n styles: [ANSI_CODES.dim],\n emoji: '🔬'\n },\n debug: {\n color: ANSI_CODES.brightBlack,\n styles: [ANSI_CODES.dim],\n emoji: '🔍'\n },\n info: {\n color: ANSI_CODES.blue,\n styles: [ANSI_CODES.bold],\n emoji: 'ℹ️'\n },\n warn: {\n color: ANSI_CODES.yellow,\n background: ANSI_CODES.bgYellow,\n styles: [ANSI_CODES.bold, ANSI_CODES.black],\n emoji: '⚠️'\n },\n error: {\n color: ANSI_CODES.red,\n background: ANSI_CODES.bgRed,\n styles: [ANSI_CODES.bold],\n emoji: '❌'\n },\n critical: {\n color: ANSI_CODES.white,\n background: ANSI_CODES.bgRed,\n styles: [ANSI_CODES.bold, ANSI_CODES.blink],\n emoji: '🚨'\n },\n success: {\n color: ANSI_CODES.green,\n background: ANSI_CODES.bgGreen,\n styles: [ANSI_CODES.bold],\n emoji: '✅'\n }\n};\n\n/**\n * Crea una secuencia de escape ANSI\n */\nfunction ansi(code: number | number[]): string {\n const codes = Array.isArray(code) ? code : [code];\n return `\\x1b[${codes.join(';')}m`;\n}\n\n/**\n * Aplica estilos ANSI al texto\n */\nexport function ansiStyle(text: string, codes: number[]): string {\n if (!supportsANSI()) return text;\n return `${ansi(codes)}${text}${ansi(ANSI_CODES.reset)}`;\n}\n\n/**\n * Aplica color al texto\n */\nexport function ansiColor(text: string, color: number): string {\n return ansiStyle(text, [color]);\n}\n\n/**\n * Aplica color de fondo al texto\n */\nexport function ansiBackground(text: string, bg: number, color?: number): string {\n if (color) {\n return ansiStyle(text, [color, bg]);\n }\n return ansiStyle(text, [bg]);\n}\n\n/**\n * Pone el texto en bold\n */\nexport function ansiBold(text: string): string {\n return ansiStyle(text, [ANSI_CODES.bold]);\n}\n\n/**\n * Aplica dim al texto\n */\nexport function ansiDim(text: string): string {\n return ansiStyle(text, [ANSI_CODES.dim]);\n}\n\n/**\n * Subraya el texto\n */\nexport function ansiUnderline(text: string): string {\n return ansiStyle(text, [ANSI_CODES.underline]);\n}\n\n/**\n * Formatters especializados para distintos contextos\n */\n\n/**\n * Formatea el timestamp para terminal\n */\nexport function formatTimestampANSI(timestamp: string): string {\n return ansiColor(timestamp, ANSI_CODES.brightBlack);\n}\n\n/**\n * Formatea el nivel de log para terminal\n */\nexport function formatLogLevelANSI(level: LogLevel, useEmojis: boolean = true): string {\n const style = ANSI_LEVEL_STYLES[level];\n const emoji = useEmojis ? style.emoji : '';\n const label = level.toUpperCase().padEnd(8);\n\n if (!supportsANSI()) {\n return `${emoji} ${label}`;\n }\n\n let codes = [style.color, ...style.styles];\n if (style.background) {\n codes.push(style.background);\n }\n\n return ansiStyle(`${emoji} ${label}`, codes);\n}\n\n/**\n * Formatea el mensaje de success para terminal\n */\nexport function formatSuccessANSI(message: string, useEmojis: boolean = true): string {\n const style = ANSI_LEVEL_STYLES.success;\n const emoji = useEmojis ? style.emoji : '';\n\n if (!supportsANSI()) {\n return `${emoji} SUCCESS: ${message}`;\n }\n\n const prefix = ansiStyle(`${emoji} SUCCESS:`, [style.color, ...style.styles]);\n return `${prefix} ${message}`;\n}\n\n/**\n * Formatea el prefix para terminal\n */\nexport function formatPrefixANSI(prefix: string): string {\n if (!supportsANSI()) {\n return `[${prefix}]`;\n }\n\n return ansiStyle(`[${prefix}]`, [ANSI_CODES.cyan, ANSI_CODES.bold]);\n}\n\n/**\n * Formatea la ubicación del archivo para terminal\n */\nexport function formatLocationANSI(location: string): string {\n if (!supportsANSI()) {\n return `(${location})`;\n }\n\n return ansiColor(`(${location})`, ANSI_CODES.brightBlack);\n}","/**\n * @fileoverview Utilidades para crear output estilizado para Advanced Logger\n */\n\nimport type { LogLevel, StackInfo, DevToolsTheme, AdaptiveColors } from '../types/index.js';\nimport { formatTimestamp } from './timestamps.js';\nimport { StyleBuilder } from '../styling/index.js';\nimport { ADAPTIVE_COLORS } from '../constants.js';\nimport { getEnvironment, supportsANSI } from './environment-detector.js';\nimport { formatLogLevelANSI, formatTimestampANSI, formatPrefixANSI, formatLocationANSI } from './ansi-colors.js';\nimport type { LogStyles } from '../types/index.js';\n\n/**\n * Configuración de estilo para cada nivel de log\n */\nexport interface LevelStyleConfig {\n emoji: string;\n label: string;\n background: string;\n color: string;\n border: string;\n shadow: string;\n}\n\n/**\n * Detecta la preferencia de theme actual de DevTools\n */\nexport function detectDevToolsTheme(): DevToolsTheme {\n try {\n if (typeof window === 'undefined' || !window.matchMedia) {\n return 'light'; // Fallback seguro para SSR o browsers antiguos\n }\n\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n return mediaQuery.matches ? 'dark' : 'light';\n } catch (error) {\n console.warn('Failed to detect DevTools theme:', error);\n return 'light'; // Fallback al theme light\n }\n}\n\n/**\n * Obtiene el color adaptativo según el theme actual de DevTools\n */\nexport function getAdaptiveColor(colors: AdaptiveColors, theme?: DevToolsTheme): string {\n const currentTheme = theme ?? detectDevToolsTheme();\n return colors[currentTheme];\n}\n\n/**\n * Registra un listener de cambios de theme para actualizaciones dinámicas\n */\nexport function setupThemeChangeListener(callback: (theme: DevToolsTheme) => void): (() => void) | null {\n try {\n if (typeof window === 'undefined' || !window.matchMedia) {\n return null;\n }\n\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n const handler = (e: MediaQueryListEvent) => {\n callback(e.matches ? 'dark' : 'light');\n };\n\n // Usa addEventListener si está disponible, fallback a addListener\n if (mediaQuery.addEventListener) {\n mediaQuery.addEventListener('change', handler);\n return () => mediaQuery.removeEventListener('change', handler);\n } else if (mediaQuery.addListener) {\n mediaQuery.addListener(handler);\n return () => mediaQuery.removeListener?.(handler);\n }\n\n return null;\n } catch (error) {\n console.warn('Failed to set up theme change listener:', error);\n return null;\n }\n}\n\n\n/**\n * Crea output estilizado para consola con múltiples formatters `%c`\n * Se adapta automáticamente al entorno (browser o terminal)\n */\nexport function createStyledOutput(\n level: LogLevel,\n levelStyles: Record<LogLevel | 'success', LevelStyleConfig>,\n prefix: string | undefined,\n message: string,\n stackInfo: StackInfo | null,\n autoDetectTheme: boolean = true,\n presetStyles?: LogStyles,\n presetName?: string\n): [string, ...string[]] {\n const levelConfig = levelStyles[level] ?? {\n emoji: '📝',\n label: level.toUpperCase(),\n background: '#6c757d',\n color: '#ffffff',\n border: '1px solid #6c757d',\n shadow: 'none'\n };\n const timestamp = formatTimestamp();\n const environment = getEnvironment();\n\n // Comprueba si se debe usar rendering en terminal\n if (environment !== 'browser' && supportsANSI()) {\n return createTerminalOutput(level, message, timestamp, prefix, stackInfo);\n }\n\n // Rendering en browser (lógica existente)\n const currentTheme = autoDetectTheme ? detectDevToolsTheme() : 'light';\n\n // Estilos base con colores adaptativos\n const timestampStyle = new StyleBuilder()\n .color(getAdaptiveColor(ADAPTIVE_COLORS.timestamp, currentTheme))\n .size('11px')\n .font('Monaco, Consolas, monospace')\n .build();\n\n const levelStyle = new StyleBuilder()\n .bg(levelConfig.background)\n .color(levelConfig.color)\n .border(levelConfig.border)\n .shadow(levelConfig.shadow)\n .padding('2px 8px')\n .rounded('4px')\n .bold()\n .font('Monaco, Consolas, monospace')\n .size('12px')\n .build();\n\n const prefixStyle = new StyleBuilder()\n .bg(getAdaptiveColor(ADAPTIVE_COLORS.prefixBackground, currentTheme))\n .color(getAdaptiveColor(ADAPTIVE_COLORS.prefix, currentTheme))\n .padding('2px 6px')\n .rounded('3px')\n .bold()\n .font('Monaco, Consolas, monospace')\n .size('11px')\n .build();\n\n const messageStyle = new StyleBuilder()\n .color(getAdaptiveColor(ADAPTIVE_COLORS.messageText, currentTheme))\n .font('system-ui, -apple-system, sans-serif')\n .size('14px')\n .build();\n\n const locationStyle = new StyleBuilder()\n .color(getAdaptiveColor(ADAPTIVE_COLORS.location, currentTheme))\n .size('11px')\n .font('Monaco, Consolas, monospace')\n .build();\n\n // Construye el format string y los estilos\n let format = `%c${timestamp.slice(11, 23)} %c${levelConfig.emoji} ${levelConfig.label}`;\n const styles = [timestampStyle, levelStyle];\n\n if (prefix) {\n format += ` %c${prefix}`;\n styles.push(prefixStyle);\n }\n\n format += ` %c${message}`;\n styles.push(messageStyle);\n\n if (stackInfo) {\n format += ` %c(${stackInfo.file}:${stackInfo.line}:${stackInfo.column})`;\n styles.push(locationStyle);\n }\n\n return [format, ...styles];\n}\n\n/**\n * Construye una línea apta para terminal usando los helpers ANSI. Se usa\n * cuando el runtime no es browser pero soporta códigos ANSI (TTY, terminal moderna).\n *\n * Layout: `[HH:MM:SS.mmm] [LEVEL] [prefix] message (file:line)`\n */\nexport function createTerminalOutput(\n level: LogLevel,\n message: string,\n timestamp?: string,\n prefix?: string,\n stackInfo?: StackInfo | null\n): [string, ...string[]] {\n const ts = timestamp?.slice(11, 23) ?? '';\n const location = stackInfo ? `${stackInfo.file}:${stackInfo.line}:${stackInfo.column}` : undefined;\n\n const parts: string[] = [];\n parts.push(formatTimestampANSI(`[${ts}]`));\n parts.push(formatLogLevelANSI(level));\n if (prefix) parts.push(formatPrefixANSI(prefix));\n parts.push(message);\n if (location) parts.push(formatLocationANSI(`(${location})`));\n\n return [parts.join(' ') + '\\n'];\n}","/**\n * @fileoverview Utilidades de formatting para Universal Logger\n */\n\nimport type { LogLevel, StackInfo } from '../types/index.js';\nimport { formatTimestamp } from './timestamps.js';\n\n// Re-export del tipo por conveniencia\nexport type { OutputFormat } from '../types/index.js';\n\n/**\n * Crea output en texto plano para cualquier entorno\n * Sin colores ni estilos — máxima compatibilidad\n */\nexport function createPlainOutput(\n level: LogLevel,\n message: string,\n prefix?: string,\n stackInfo?: StackInfo | null\n): string {\n const timestamp = formatTimestamp();\n const timeStr = timestamp.slice(11, 23); // HH:MM:SS.mmm\n\n let output = `[${timeStr}] [${level.toUpperCase()}]`;\n\n if (prefix) {\n output += ` [${prefix}]`;\n }\n\n output += ` ${message}`;\n\n if (stackInfo) {\n output += ` (${stackInfo.file}:${stackInfo.line}:${stackInfo.column})`;\n }\n\n return output;\n}\n\n/**\n * Obtiene el método de console adecuado para el nivel\n * Usa distintos métodos de console para mejor semántica\n */\nexport function getConsoleMethod(level: LogLevel): 'log' | 'info' | 'warn' | 'error' {\n switch (level) {\n case 'debug':\n return 'log';\n case 'info':\n return 'info';\n case 'warn':\n return 'warn';\n case 'error':\n case 'critical':\n return 'error';\n default:\n return 'log';\n }\n}\n\n/**\n * Crea un objeto simple de log entry para los handlers\n */\nexport function createLogEntry(\n level: LogLevel,\n message: string,\n args: any[],\n prefix?: string,\n stackInfo?: StackInfo | null\n) {\n return {\n timestamp: formatTimestamp(),\n level: level.toUpperCase(),\n prefix,\n message,\n args: args.slice(1), // Excluye el primer argumento (el mensaje)\n location: stackInfo ? {\n file: stackInfo.file,\n line: stackInfo.line,\n column: stackInfo.column,\n function: stackInfo.function\n } : undefined,\n raw: args\n };\n}\n\n/**\n * Formatea datos para visualización en tabla como texto plano\n */\nexport function formatTablePlain(data: any, columns?: string[]): string {\n try {\n if (Array.isArray(data)) {\n if (data.length === 0) return '[Empty Array]';\n\n // Formateo simple de array\n if (typeof data[0] !== 'object') {\n return data.map((item, i) => `${i}: ${item}`).join('\\n');\n }\n\n // Formateo de array de objetos\n const keys = columns || Object.keys(data[0] || {});\n const header = keys.join('\\t');\n const rows = data.map(item =>\n keys.map(key => String(item[key] || '')).join('\\t')\n );\n\n return [header, ...rows].join('\\n');\n }\n\n if (typeof data === 'object' && data !== null) {\n // Formateo simple de objeto\n return Object.entries(data)\n .map(([key, value]) => `${key}: ${value}`)\n .join('\\n');\n }\n\n return String(data);\n } catch (error) {\n return `[Error al formatear la tabla: ${error}]`;\n }\n}\n\n/**\n * Serialización segura de objetos para logging\n */\nexport function safeSerialize(obj: any, maxDepth: number = 3, currentDepth: number = 0): any {\n if (currentDepth >= maxDepth) {\n return '[Max Depth Reached]';\n }\n\n if (obj === null) return null;\n if (obj === undefined) return '[undefined]';\n if (typeof obj === 'function') return '[Function]';\n if (obj instanceof Error) return `[Error: ${obj.message}]`;\n if (obj instanceof Date) return obj.toISOString();\n if (typeof obj !== 'object') return obj;\n\n if (Array.isArray(obj)) {\n return obj.map(item => safeSerialize(item, maxDepth, currentDepth + 1));\n }\n\n const result: any = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = safeSerialize(value, maxDepth, currentDepth + 1);\n }\n return result;\n}"],"mappings":"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"}
|