@mks2508/better-logger 0.18.2-alpha.2 → 0.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/Logger.d.ts +113 -21
  2. package/dist/Logger.d.ts.map +1 -1
  3. package/dist/ScopedLogger.d.ts +38 -1
  4. package/dist/ScopedLogger.d.ts.map +1 -1
  5. package/dist/chunks/LogContext-BO-f0KXu.js +142 -0
  6. package/dist/chunks/LogContext-BO-f0KXu.js.map +1 -0
  7. package/dist/chunks/{LogContext-BaMXleWj.cjs → LogContext-CIKxWPrK.cjs} +54 -12
  8. package/dist/chunks/LogContext-CIKxWPrK.cjs.map +1 -0
  9. package/dist/chunks/{transports-yK6CL0Ml.cjs → transports-BZ-Mc2IT.cjs} +196 -24
  10. package/dist/chunks/transports-BZ-Mc2IT.cjs.map +1 -0
  11. package/dist/chunks/{transports-BGfwwakw.js → transports-DvaLAeGJ.js} +191 -25
  12. package/dist/chunks/transports-DvaLAeGJ.js.map +1 -0
  13. package/dist/context/LogContext.d.ts +2 -4
  14. package/dist/context/LogContext.d.ts.map +1 -1
  15. package/dist/context.cjs +1 -1
  16. package/dist/context.js +1 -1
  17. package/dist/index.cjs +345 -34
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.ts +5 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +343 -35
  22. package/dist/index.js.map +1 -1
  23. package/dist/transports/OtlpTraceTransport.d.ts +118 -0
  24. package/dist/transports/OtlpTraceTransport.d.ts.map +1 -0
  25. package/dist/transports/OtlpTransport.d.ts +27 -1
  26. package/dist/transports/OtlpTransport.d.ts.map +1 -1
  27. package/dist/transports/SpanRuntime.d.ts +76 -0
  28. package/dist/transports/SpanRuntime.d.ts.map +1 -0
  29. package/dist/transports/TransportManager.d.ts +18 -11
  30. package/dist/transports/TransportManager.d.ts.map +1 -1
  31. package/dist/transports/index.d.ts +1 -0
  32. package/dist/transports/index.d.ts.map +1 -1
  33. package/dist/transports-module.d.ts +3 -1
  34. package/dist/transports-module.d.ts.map +1 -1
  35. package/dist/transports.cjs +2 -1
  36. package/dist/transports.js +2 -2
  37. package/dist/types/index.d.ts +1 -1
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/types/transports.d.ts +121 -2
  40. package/dist/types/transports.d.ts.map +1 -1
  41. package/dist/utils/asyncLocalStorage.d.ts +33 -0
  42. package/dist/utils/asyncLocalStorage.d.ts.map +1 -0
  43. package/package.json +1 -1
  44. package/dist/chunks/LogContext-BaMXleWj.cjs.map +0 -1
  45. package/dist/chunks/LogContext-DjlITOzZ.js +0 -100
  46. package/dist/chunks/LogContext-DjlITOzZ.js.map +0 -1
  47. package/dist/chunks/transports-BGfwwakw.js.map +0 -1
  48. package/dist/chunks/transports-yK6CL0Ml.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogContext-CIKxWPrK.cjs","names":[],"sources":["../../src/utils/asyncLocalStorage.ts","../../src/context/LogContext.ts"],"sourcesContent":["/**\n * @fileoverview Resolución compartida de `AsyncLocalStorage` — utilidad\n * interna, NO exportada por el barrel público (`src/utils/index.ts`).\n *\n * Único punto de verdad para resolver `node:async_hooks` por capas. Lo\n * consumen tanto `transports/SpanRuntime.ts` (ALS singleton de spans) como\n * `context/LogContext.ts` (ALS singleton de MDC). Antes había dos copias de\n * este conocimiento: una acertaba (SpanRuntime, la que sigue este archivo) y\n * otra usaba `typeof AsyncLocalStorage !== 'undefined'` — un feature-detect\n * roto, porque en Node y Bun el constructor NO es global, así que esa rama\n * quedaba siempre en `undefined` y el MDC de logs era no-op silencioso fuera\n * del browser.\n */\n\n/**\n * Forma mínima de `AsyncLocalStorage<T>` que necesita este paquete.\n */\nexport interface AsyncLocalStorageLike<T> {\n run<R>(store: T, fn: () => R): R;\n getStore(): T | undefined;\n}\n\n/**\n * Resuelve `AsyncLocalStorage` por capas:\n * 1. global `AsyncLocalStorage` (runtimes/polyfills que lo exponen),\n * 2. `process.getBuiltinModule('async_hooks')` (Node >= 22.3, Bun),\n * 3. `require('node:async_hooks')` (CJS interop),\n * 4. `undefined` → browser estricto: sin correlación (degradación\n * documentada; el caller decide qué hacer sin ALS).\n *\n * Nota: NO basta con `typeof AsyncLocalStorage !== 'undefined'` — en Node y\n * Bun el constructor NO es global y hay que ir a `node:async_hooks`.\n */\nexport function resolveAsyncLocalStorage<T>(): AsyncLocalStorageLike<T> | undefined {\n const globalCtor = (globalThis as { AsyncLocalStorage?: new () => AsyncLocalStorageLike<T> }).AsyncLocalStorage;\n if (typeof globalCtor === 'function') {\n return new globalCtor();\n }\n\n const proc = globalThis as {\n process?: {\n getBuiltinModule?: (id: string) => { AsyncLocalStorage?: new () => AsyncLocalStorageLike<T> } | undefined;\n };\n };\n try {\n const asyncHooks = proc.process?.getBuiltinModule?.('async_hooks');\n if (asyncHooks?.AsyncLocalStorage) {\n return new asyncHooks.AsyncLocalStorage();\n }\n } catch {\n // getBuiltinModule no disponible — siguiente capa\n }\n\n try {\n if (typeof require === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const asyncHooks = require('node:async_hooks') as { AsyncLocalStorage?: new () => AsyncLocalStorageLike<T> };\n if (asyncHooks?.AsyncLocalStorage) {\n return new asyncHooks.AsyncLocalStorage();\n }\n }\n } catch {\n // ESM puro sin require ni builtin accessor — browser\n }\n\n return undefined;\n}\n","/**\n * @fileoverview Bridge de LogContext — gestión de MDC (Mapped Diagnostic Context).\n *\n * Encapsula el contexto estructurado por logger, la creación de child loggers y\n * el merge de resource OTel en cada record emitido.\n *\n * Modelo de API:\n * - `withContext(bindings, fn?)` — si se pasa `fn`, lo ejecuta dentro de un\n * scope de AsyncLocalStorage mergeando `bindings`. Sin `fn`: no-op (shim de\n * backwards compat para la vieja forma de setter).\n * - `withContextAsync(bindings, fn)` — variante async callback.\n * - `child(bindings)` — inmutable (patrón canónico de MDC).\n * - Feature-detect de AsyncLocalStorage; en browser sin ALS es no-op.\n */\n\nimport type { ILogResourceRef } from '../types/index.js';\nimport type { LoggerConfig } from '../types/index.js';\nimport { resolveAsyncLocalStorage, type AsyncLocalStorageLike } from '../utils/asyncLocalStorage.js';\n\n/**\n * Snapshot del contexto bound. Lo retorna {@link LogContext.getContext}.\n *\n * Es `Readonly` para marcar contractually que el objeto devuelto es una shallow\n * copy: mutarlo no afecta a los records que emitan futuras llamadas de log.\n */\nexport type ContextSnapshot = Readonly<Record<string, unknown>>;\n\n/**\n * Tipo de la factory function para crear instancias child de Logger.\n *\n * Se inyecta en LogContext para que `child()` pueda instanciar nuevos loggers\n * sin introducir un import circular entre `Logger.ts` y `LogContext.ts`.\n * Retorna `unknown` — la clase Logger concreta la maneja el caller, y la\n * instancia devuelta tiene su campo `context` escrito por LogContext tras la\n * creación.\n */\nexport type ChildLoggerFactory = (config: Partial<LoggerConfig>) => unknown;\n\n/**\n * Shape mínima de una instancia de Logger que LogContext necesita ver.\n *\n * Evita dependencias circulares entre LogContext y Logger. El campo\n * `_parentContextRecord` lo setea el Logger padre después de que\n * `LogContext.child()` retorna, estableciendo la cadena de contextos.\n */\nexport interface ChildLoggerShape {\n _parentContextRecord?: Record<string, unknown>;\n /** Campo legacy — ya no es la fuente canónica del contexto. */\n context?: Record<string, unknown>;\n}\n\n/**\n * Options que se pasan a {@link createLogContext}.\n */\nexport interface ILogContextOptions {\n /** Pares key-value iniciales del contexto. */\n initialContext?: Record<string, unknown>;\n /** Factory para crear instancias child de logger. */\n childLoggerFactory: ChildLoggerFactory;\n /** Resource OTel inicial a mergear en cada record emitido. */\n initialResource?: Partial<ILogResourceRef>;\n /**\n * Retorna el record de contexto mergeado del logger padre en el momento\n * de creación del child. Lo usa `_getContextRecord()` para construir la\n * cadena de contextos.\n * @internal\n */\n getParentContextRecord?: () => Record<string, unknown>;\n /**\n * Instancia de AsyncLocalStorage a usar para el scoping de `withContext`.\n * @internal\n */\n alsInstance?: ALS;\n}\n\n/**\n * Contrato que retorna {@link createLogContext}.\n *\n * Fachada de MDC (Mapped Diagnostic Context) por logger. Combina tres fuentes\n * de contexto:\n * - **base inmutable** vía `child()` (snapshot capturado al crear el child),\n * - **scope transitorio** vía `withContext()` / `withContextAsync()` sobre\n * AsyncLocalStorage,\n * - **resource OTel** mergeado en cada record.\n *\n * En entornos browser sin `AsyncLocalStorage`, las variantes `withContext*`\n * degradan a no-op: ejecutan `fn` sin scoping (o lo skipan si no hay `fn`).\n * `child()` sigue operativo en browser porque no depende de ALS.\n */\nexport interface LogContext {\n /**\n * Snapshot actual del contexto bound.\n *\n * @returns Copia inmutable (shallow) del contexto; mutarla no afecta a los\n * records que emitan futuras llamadas de log.\n *\n * @example\n * const ctx = logContext.getContext();\n * console.log(ctx.requestId); // 'abc-123'\n */\n getContext(): ContextSnapshot;\n\n /**\n * Ejecuta `fn` dentro de un scope de AsyncLocalStorage donde `bindings`\n * se mergean al contexto para todas las llamadas de log dentro de `fn`.\n *\n * Si no se pasa `fn` (la vieja forma de setter), es no-op por backwards\n * compatibility. Para binding persistente prefiere `child()`; para\n * callbacks async usa `withContextAsync()`.\n *\n * **Browser fallback**: sin ALS, ejecuta `fn` directamente sin scoping\n * (los bindings NO se mergean). Si tampoco hay `fn`, retorna `undefined`.\n *\n * @param bindings - Pares key-value a attachar durante la ejecución de `fn`\n * @param fn - Función sincrónica opcional a ejecutar bajo el scope ALS\n * @returns El valor de retorno de `fn`, o `undefined` si no se pasa `fn`\n *\n * @example\n * logContext.withContext({ requestId: 'abc-123' }, () => {\n * logger.info('procesando'); // el record lleva requestId=abc-123\n * });\n * // fuera de fn: requestId ya no está presente en próximos logs\n *\n * @see {@link LogContext.withContextAsync} para callbacks async\n * @see {@link LogContext.child} para binding persistente inmutable (sin ALS)\n */\n withContext<R>(bindings: Record<string, unknown>, fn?: () => R): R | undefined;\n\n /**\n * Variante async de {@link withContext}. Ejecuta `fn` dentro de un scope\n * de AsyncLocalStorage para que los bindings queden disponibles a todas las\n * llamadas de log async dentro de `fn` (incluso tras `await`).\n *\n * **Browser fallback**: sin ALS, ejecuta `fn` directamente sin scoping.\n *\n * @param bindings - Pares key-value a attachar durante la ejecución de `fn`\n * @param fn - Función async a ejecutar bajo el scope ALS\n * @returns El Promise retornado por `fn`\n *\n * @example\n * await logContext.withContextAsync({ traceId }, async () => {\n * const user = await fetchUser();\n * logger.info('user cargado', { id: user.id });\n * // el record lleva el traceId aunque el log ocurra tras un await\n * });\n */\n withContextAsync<R>(bindings: Record<string, unknown>, fn: () => Promise<R>): Promise<R>;\n\n /**\n * Droppea todas las keys del contexto bound. Tras esta llamada, los\n * records emitidos ya no llevan `attributes` hasta que\n * {@link withContext} o {@link child} restablezcan uno.\n *\n * @returns La misma instancia de LogContext, ahora sin contexto\n *\n * @example\n * logContext.clearContext();\n * logger.info('limpio'); // sin attributes\n */\n clearContext(): this;\n\n /**\n * Actualiza el resource OTel por defecto (service.name, version,\n * deployment.environment, ...).\n *\n * Se persiste en el campo `resource` de cada record emitido, salvo que el\n * propio record lo overridee.\n *\n * @param resource - Resource OTel parcial a mergear con el actual\n * @returns La misma instancia de LogContext, para encadenar calls\n *\n * @example\n * logContext.setResource({ serviceName: 'api-gateway', environment: 'prod' });\n */\n setResource(resource: Partial<ILogResourceRef>): this;\n\n /**\n * Devuelve una copia inmutable de este logger con el contexto extra bound.\n *\n * Las llamadas futuras sobre el child emiten con el contexto mergeado, sin\n * mutar al padre — patrón canónico de MDC.\n *\n * A diferencia de {@link withContext}, **no involucra AsyncLocalStorage**:\n * el binding es persistente y queda capturado en el snapshot del child al\n * crearse. Por eso `child()` es operativo también en browser sin ALS.\n *\n * Los bindings transitorios de ALS activos en el momento de `child()` NO\n * se bakean en el child — solo se captura el contexto base. ALS se aplica\n * fresco en cada dispatch vía `_getContextRecord()`.\n *\n * @param extra - Pares key-value a attachar (requestId, userId, ...)\n * @returns Un nuevo Logger con el contexto mergeado\n *\n * @example\n * const requestLog = logContext.child({ requestId: 'abc-123' });\n * requestLog.info('inicio'); // siempre lleva requestId=abc-123\n * requestLog.info('fin');\n * // el logger padre no se ve afectado por estos bindings\n *\n * @see {@link LogContext.withContext} para scoping transitorio (ALS)\n */\n child(extra: Record<string, unknown>): ChildLoggerShape;\n\n /**\n * Record de contexto interno. Expuesto para el ensamblado de TransportRecord\n * (base + overlay ALS si hay store activo).\n * @internal\n */\n _getContextRecord(): Record<string, unknown>;\n /**\n * Retorna el contexto base SIN el overlay de ALS.\n *\n * Lo usa `Logger.child()` para capturar el snapshot del contexto padre al\n * crear un child logger, garantizando que el binding ALS transitorio no\n * se bakeé en el child.\n * @internal\n */\n _getBaseContextRecord(): Record<string, unknown>;\n /**\n * Record de resource interno. Expuesto para el ensamblado de TransportRecord.\n * @internal\n */\n _getResource(): Partial<ILogResourceRef> | undefined;\n /**\n * Retorna el store actual de AsyncLocalStorage, si ALS está activo en el\n * call stack corriente.\n * @internal\n */\n _getAlsStore(): Record<string, unknown> | undefined;\n}\n\n// AsyncLocalStorage type (Node 14+, undefined in browser)\ntype ALS = AsyncLocalStorageLike<Record<string, unknown>>;\n\n// ALS singleton del módulo. Resolución por capas (global →\n// `process.getBuiltinModule` → `require`, undefined en browser) delegada al\n// helper compartido `resolveAsyncLocalStorage` — el mismo que usa el ALS de\n// spans en `transports/SpanRuntime.ts`. Antes esto era\n// `typeof AsyncLocalStorage !== 'undefined'`, un feature-detect roto: ese\n// constructor no es global en node/bun, así que siempre resolvía `undefined`\n// y `withContext`/`withContextAsync` eran no-op silenciosos fuera del browser.\n//\n// Se resuelve lazy en el primer `withContext(fn)`: cargar `node:async_hooks`\n// cuesta ~2,2 MB de RSS en bun, y `_getContextRecord` consulta el store en\n// cada dispatch de log — resolverlo al importar se lo cobraría a todo\n// proceso que loguea, use MDC o no. Sin `run()` previo `getStore()` devuelve\n// `undefined`, así que el guard por flag es observable-identico.\nlet alsInstance: ALS | undefined;\nlet alsActivated = false;\n\n/**\n * Factory que crea una instancia de {@link LogContext}.\n *\n * @param options - Configuración (ver {@link ILogContextOptions})\n * @returns Una instancia de LogContext lista para usar\n *\n * @example\n * const logContext = createLogContext({\n * childLoggerFactory: (cfg) => new Logger(cfg),\n * initialContext: { service: 'orders-api' },\n * initialResource: { serviceName: 'orders-api', environment: 'prod' }\n * });\n *\n * // Child inmutable con contexto persistente\n * const requestLog = logContext.child({ requestId: 'abc-123' });\n *\n * // Scope transitorio vía ALS (Node; en browser sin ALS es no-op)\n * logContext.withContext({ traceId: 't-9' }, () => {\n * requestLog.info('procesando orden');\n * });\n */\nexport function createLogContext(options: ILogContextOptions): LogContext {\n let context: Record<string, unknown> = { ...options.initialContext };\n let resource: Partial<ILogResourceRef> | undefined = options.initialResource\n ? { ...options.initialResource }\n : undefined;\n\n // Provided instance wins; otherwise the module singleton resolves on\n // first real use (see the note on `alsInstance` above). The module\n // instance stays shared across LogContexts: nested `withContext` de\n // loggers distintos se ven entre sí vía el mismo store.\n const activateAls = (): ALS | undefined => {\n if (!alsActivated) {\n alsActivated = true;\n alsInstance = resolveAsyncLocalStorage<Record<string, unknown>>();\n }\n return options.alsInstance ?? alsInstance;\n };\n\n return {\n getContext(): ContextSnapshot {\n return { ...context };\n },\n\n withContext<R>(bindings: Record<string, unknown>, fn?: () => R): R | undefined {\n // No fn: backwards-compat no-op setter shim — never activates ALS\n if (!fn) return undefined;\n const store = activateAls();\n // No AsyncLocalStorage (browser fallback): run fn as-is\n if (!store) return fn();\n // Run fn within AsyncLocalStorage scope, merged with the active\n // outer scope (if any) so nested withContext calls accumulate\n // bindings instead of replacing them.\n const merged = { ...context, ...store.getStore(), ...bindings };\n return store.run(merged, fn);\n },\n\n async withContextAsync<R>(bindings: Record<string, unknown>, fn: () => Promise<R>): Promise<R> {\n const store = activateAls();\n if (!store) return fn();\n const merged = { ...context, ...store.getStore(), ...bindings };\n return store.run(merged, fn);\n },\n\n clearContext(): typeof this {\n context = {};\n return this;\n },\n\n setResource(res: Partial<ILogResourceRef>): typeof this {\n resource = { ...resource, ...res };\n return this;\n },\n\n child(_extra: Record<string, unknown>): ChildLoggerShape {\n // Creates the child logger via factory. Captures the current\n // _getBaseContextRecord() snapshot (parent context WITHOUT ALS) at\n // child-creation time. ALS is transient and should not be baked into\n // the child's _parentContextRecord — it is applied fresh at dispatch time.\n // Note: _extra (bindings) are stored by Logger.child() as _bindings.\n const snapshot = this._getBaseContextRecord();\n const childLogger = options.childLoggerFactory({}) as ChildLoggerShape;\n // Store on childLogger for Logger.child() to pick up\n (childLogger as unknown as Record<string, unknown>)['__parentSnapshot'] = snapshot;\n return childLogger;\n },\n\n _getContextRecord(): Record<string, unknown> {\n // Returns the full merged context for dispatch purposes.\n // Base (parent snapshot + own context) plus ALS overlay if active.\n // An injected instance is read as-is (external runs count); the\n // module singleton only after activation — pre-activation its\n // store would be empty anyway.\n const base = this._getBaseContextRecord();\n const store = options.alsInstance ?? (alsActivated ? alsInstance : undefined);\n const alsContext = store?.getStore();\n if (alsContext && Object.keys(alsContext).length > 0) {\n return { ...base, ...alsContext };\n }\n return base;\n },\n\n _getBaseContextRecord(): Record<string, unknown> {\n // Returns parent context chain + own context (NO ALS overlay).\n // ALS is applied by _getContextRecord() as a live overlay.\n let base: Record<string, unknown> = {};\n const parentRecord = options.getParentContextRecord?.() ?? null;\n if (parentRecord && Object.keys(parentRecord).length > 0) {\n base = parentRecord;\n } else if (Object.keys(context).length > 0) {\n base = context;\n }\n if (Object.keys(context).length > 0) {\n base = { ...base, ...context };\n }\n return base;\n },\n\n _getResource(): Partial<ILogResourceRef> | undefined {\n return resource;\n },\n\n _getAlsStore(): Record<string, unknown> | undefined {\n return (options.alsInstance ?? (alsActivated ? alsInstance : undefined))?.getStore();\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;AAiCA,SAAgB,2BAAoE;CAChF,MAAM,aAAc,WAA0E;CAC9F,IAAI,OAAO,eAAe,YACtB,OAAO,IAAI,WAAW;CAG1B,MAAM,OAAO;CAKb,IAAI;EACA,MAAM,aAAa,KAAK,SAAS,mBAAmB,aAAa;EACjE,IAAI,YAAY,mBACZ,OAAO,IAAI,WAAW,kBAAkB;CAEhD,QAAQ,CAER;CAEA,IAAI;EACA,IAAI,OAAO,YAAY,YAAY;GAE/B,MAAM,aAAa,QAAQ,kBAAkB;GAC7C,IAAI,YAAY,mBACZ,OAAO,IAAI,WAAW,kBAAkB;EAEhD;CACJ,QAAQ,CAER;AAGJ;;;ACqLA,IAAI;AACJ,IAAI,eAAe;;;;;;;;;;;;;;;;;;;;;;AAuBnB,SAAgB,iBAAiB,SAAyC;CACtE,IAAI,UAAmC,EAAE,GAAG,QAAQ,eAAe;CACnE,IAAI,WAAiD,QAAQ,kBACvD,EAAE,GAAG,QAAQ,gBAAgB,IAC7B,KAAA;CAMN,MAAM,oBAAqC;EACvC,IAAI,CAAC,cAAc;GACf,eAAe;GACf,cAAc,yBAAkD;EACpE;EACA,OAAO,QAAQ,eAAe;CAClC;CAEA,OAAO;EACH,aAA8B;GAC1B,OAAO,EAAE,GAAG,QAAQ;EACxB;EAEA,YAAe,UAAmC,IAA6B;GAE3E,IAAI,CAAC,IAAI,OAAO,KAAA;GAChB,MAAM,QAAQ,YAAY;GAE1B,IAAI,CAAC,OAAO,OAAO,GAAG;GAItB,MAAM,SAAS;IAAE,GAAG;IAAS,GAAG,MAAM,SAAS;IAAG,GAAG;GAAS;GAC9D,OAAO,MAAM,IAAI,QAAQ,EAAE;EAC/B;EAEA,MAAM,iBAAoB,UAAmC,IAAkC;GAC3F,MAAM,QAAQ,YAAY;GAC1B,IAAI,CAAC,OAAO,OAAO,GAAG;GACtB,MAAM,SAAS;IAAE,GAAG;IAAS,GAAG,MAAM,SAAS;IAAG,GAAG;GAAS;GAC9D,OAAO,MAAM,IAAI,QAAQ,EAAE;EAC/B;EAEA,eAA4B;GACxB,UAAU,CAAC;GACX,OAAO;EACX;EAEA,YAAY,KAA4C;GACpD,WAAW;IAAE,GAAG;IAAU,GAAG;GAAI;GACjC,OAAO;EACX;EAEA,MAAM,QAAmD;GAMrD,MAAM,WAAW,KAAK,sBAAsB;GAC5C,MAAM,cAAc,QAAQ,mBAAmB,CAAC,CAAC;GAEjD,YAAoD,sBAAsB;GAC1E,OAAO;EACX;EAEA,oBAA6C;GAMzC,MAAM,OAAO,KAAK,sBAAsB;GAExC,MAAM,cADQ,QAAQ,gBAAgB,eAAe,cAAc,KAAA,GAAA,EACzC,SAAS;GACnC,IAAI,cAAc,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,GAC/C,OAAO;IAAE,GAAG;IAAM,GAAG;GAAW;GAEpC,OAAO;EACX;EAEA,wBAAiD;GAG7C,IAAI,OAAgC,CAAC;GACrC,MAAM,eAAe,QAAQ,yBAAyB,KAAK;GAC3D,IAAI,gBAAgB,OAAO,KAAK,YAAY,CAAC,CAAC,SAAS,GACnD,OAAO;QACJ,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,GACrC,OAAO;GAEX,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,GAC9B,OAAO;IAAE,GAAG;IAAM,GAAG;GAAQ;GAEjC,OAAO;EACX;EAEA,eAAqD;GACjD,OAAO;EACX;EAEA,eAAoD;GAChD,QAAQ,QAAQ,gBAAgB,eAAe,cAAc,KAAA,GAAA,EAAa,SAAS;EACvF;CACJ;AACJ"}
@@ -809,6 +809,11 @@ var OtlpTransport = class extends HttpTransport {
809
809
  return base;
810
810
  }
811
811
  };
812
+ /**
813
+ * Quita la barra final de un endpoint si la lleva.
814
+ *
815
+ * @internal Compartido con {@link OtlpTraceTransport}; no es API pública.
816
+ */
812
817
  function stripTrailingSlash(value) {
813
818
  return value.endsWith("/") ? value.slice(0, -1) : value;
814
819
  }
@@ -818,7 +823,8 @@ function stripTrailingSlash(value) {
818
823
  * browser) o si la variable no está seteada. Nunca throwea, nunca loguea
819
824
  * el valor.
820
825
  *
821
- * @internal Helper del constructor de {@link OtlpTransport}; no es API pública.
826
+ * @internal Helper del constructor de {@link OtlpTransport} y
827
+ * {@link OtlpTraceTransport}; no es API pública.
822
828
  * @param envVarName - Nombre de la env var a leer.
823
829
  * @returns Valor de la key, o `undefined` si no está disponible.
824
830
  */
@@ -862,6 +868,13 @@ function collectAttributes(record) {
862
868
  }
863
869
  return out;
864
870
  }
871
+ /**
872
+ * Convierte un valor JS a un `AnyValue` de OTel. Compartido con
873
+ * {@link OtlpTraceTransport} para serializar attributes de span con el mismo
874
+ * mapping.
875
+ *
876
+ * @internal Exportado para reuse entre transports OTLP; no es API pública.
877
+ */
865
878
  function toOtlpAttribute(value) {
866
879
  if (value === null || value === void 0) return null;
867
880
  if (typeof value === "string") return { stringValue: value };
@@ -874,6 +887,139 @@ function toOtlpAttribute(value) {
874
887
  return { stringValue: JSON.stringify(value) };
875
888
  }
876
889
  //#endregion
890
+ //#region src/transports/OtlpTraceTransport.ts
891
+ /**
892
+ * Transport OTLP/HTTP para traces (spans) — POST a `<endpoint>/v1/traces`.
893
+ *
894
+ * Espeja {@link OtlpTransport} (que va a `/v1/logs`): hereda de
895
+ * {@link HttpTransport} el batch, retry con backoff, buffer acotado, close
896
+ * asincrónico y el hook on-error. Solo cambia la shape del payload y acepta
897
+ * exclusivamente records de kind `'span'` (`accepts = ['span']`): el
898
+ * {@link TransportManager} garantiza que jamás recibe un log record.
899
+ *
900
+ * @example
901
+ * ```ts
902
+ * logger.addTransport({
903
+ * target: new OtlpTraceTransport({
904
+ * endpoint: 'https://otelcollector.example.com:4318',
905
+ * serviceName: 'my-app',
906
+ * scopeVersion: '1.2.3'
907
+ * })
908
+ * });
909
+ *
910
+ * await logger.span('db.query', { table: 'users' }, async () => {
911
+ * await db.select();
912
+ * });
913
+ * ```
914
+ */
915
+ var OtlpTraceTransport = class extends HttpTransport {
916
+ name = "otlp-trace";
917
+ /** Este transport SOLO acepta spans — jamás recibe log records. */
918
+ accepts = ["span"];
919
+ resource;
920
+ scopeVersion;
921
+ /** Resuelto al construir. Nunca se loguea, nunca se escribe a source. */
922
+ ingestKeyValue;
923
+ constructor(options) {
924
+ if (!options.endpoint) throw new Error("OtlpTraceTransport: `endpoint` is required");
925
+ if (!options.serviceName) throw new Error("OtlpTraceTransport: `serviceName` is required");
926
+ const ingestKey = readIngestKey(options.ingestKeyEnvVar);
927
+ const httpOptions = {
928
+ url: `${stripTrailingSlash(options.endpoint)}/v1/traces`,
929
+ headers: {
930
+ ...ingestKey ? { "signoz-ingestion-key": ingestKey } : {},
931
+ ...options.headers ?? {}
932
+ },
933
+ batchSize: options.batchSize,
934
+ flushInterval: options.flushInterval,
935
+ maxBufferSize: options.maxBufferSize,
936
+ maxRetries: options.maxRetries,
937
+ initialBackoffMs: options.initialBackoffMs,
938
+ maxBackoffMs: options.maxBackoffMs,
939
+ fetchTimeoutMs: options.fetchTimeoutMs,
940
+ onError: options.onError
941
+ };
942
+ super(httpOptions);
943
+ this.resource = {
944
+ "service.name": options.serviceName,
945
+ ...options.serviceVersion ? { "service.version": options.serviceVersion } : {},
946
+ ...options.environment ? { "deployment.environment": options.environment } : {},
947
+ ...options.resourceAttributes ?? {}
948
+ };
949
+ this.scopeVersion = options.scopeVersion;
950
+ this.ingestKeyValue = ingestKey;
951
+ }
952
+ /**
953
+ * Construye el payload JSON OTLP/HTTP a partir de los spans bufferizados.
954
+ * Un bloque `resourceSpans` por batch. Expuesto para tests y subclasses.
955
+ *
956
+ * @param spans - Spans a serializar dentro del payload.
957
+ * @returns Objeto `TracesData` listo para `JSON.stringify`.
958
+ */
959
+ buildPayload(spans) {
960
+ return { resourceSpans: [{
961
+ resource: { attributes: Object.entries(this.resource).filter((entry) => typeof entry[1] === "string").map(([key, value]) => ({
962
+ key,
963
+ value: { stringValue: value }
964
+ })) },
965
+ scopeSpans: [{
966
+ scope: {
967
+ name: "better-logger",
968
+ ...this.scopeVersion ? { version: this.scopeVersion } : {}
969
+ },
970
+ spans: spans.map((s) => this.toOtlpSpan(s))
971
+ }]
972
+ }] };
973
+ }
974
+ /**
975
+ * Serializa un batch al body JSON del request OTLP/HTTP. Override de
976
+ * {@link HttpTransport.serializeBody}.
977
+ */
978
+ serializeBody(records) {
979
+ return JSON.stringify(this.buildPayload(records));
980
+ }
981
+ /**
982
+ * Construye los headers del request. Agrega `signoz-ingestion-key` con el
983
+ * valor resuelto al construir.
984
+ */
985
+ buildHeaders() {
986
+ return {
987
+ "Content-Type": "application/json",
988
+ ...this.ingestKeyValue ? { "signoz-ingestion-key": this.ingestKeyValue } : {},
989
+ ...this.options.headers ?? {}
990
+ };
991
+ }
992
+ toOtlpSpan(span) {
993
+ const attributes = [{
994
+ key: "logger.scope",
995
+ value: { stringValue: span.scope }
996
+ }];
997
+ for (const [key, value] of Object.entries(span.attributes)) {
998
+ const mapped = toOtlpAttribute(value);
999
+ if (mapped) attributes.push({
1000
+ key,
1001
+ value: mapped
1002
+ });
1003
+ }
1004
+ if (span.incomplete) attributes.push({
1005
+ key: "span.incomplete",
1006
+ value: { boolValue: true }
1007
+ });
1008
+ const out = {
1009
+ traceId: span.traceId,
1010
+ spanId: span.spanId,
1011
+ ...span.parentSpanId ? { parentSpanId: span.parentSpanId } : {},
1012
+ name: span.name,
1013
+ kind: span.spanKind,
1014
+ startTimeUnixNano: span.startTimeUnixNano,
1015
+ endTimeUnixNano: span.endTimeUnixNano,
1016
+ attributes
1017
+ };
1018
+ if (span.status) out.status = span.status;
1019
+ return out;
1020
+ }
1021
+ };
1022
+ //#endregion
877
1023
  //#region src/transports/TransportManager.ts
878
1024
  /**
879
1025
  * Genera un id opaco y estable en el tiempo para cada transport añadido al
@@ -892,11 +1038,12 @@ function generateTransportId() {
892
1038
  * se añaden al registry por-instancia vía {@link TransportManager.register}
893
1039
  * (no a este Map módulo-nivel).
894
1040
  *
895
- * Los cuatro built-ins registrados automáticamente:
896
- * - `'console'` → {@link ConsoleTransport}
897
- * - `'file'` → {@link FileTransport}
898
- * - `'http'` → {@link HttpTransport}
899
- * - `'otlp'` → {@link OtlpTransport}
1041
+ * Los cinco built-ins registrados automáticamente:
1042
+ * - `'console'` → {@link ConsoleTransport} (logs)
1043
+ * - `'file'` → {@link FileTransport} (logs)
1044
+ * - `'http'` → {@link HttpTransport} (logs)
1045
+ * - `'otlp'` → {@link OtlpTransport} (logs → `/v1/logs`)
1046
+ * - `'otlp-trace'` → {@link OtlpTraceTransport} (spans → `/v1/traces`)
900
1047
  *
901
1048
  * @internal No exportado — los callers externos usan
902
1049
  * {@link TransportManager.register} / {@link TransportManager.listRegistered}.
@@ -905,8 +1052,15 @@ const BUILTIN_REGISTRY = /* @__PURE__ */ new Map([
905
1052
  ["console", ConsoleTransport],
906
1053
  ["file", FileTransport],
907
1054
  ["http", HttpTransport],
908
- ["otlp", OtlpTransport]
1055
+ ["otlp", OtlpTransport],
1056
+ ["otlp-trace", OtlpTraceTransport]
909
1057
  ]);
1058
+ /** `accepts` efectivo cuando el transport no lo declara: log-only. */
1059
+ const DEFAULT_ACCEPTS = ["log"];
1060
+ /** Estrecha un record del pipeline a su kind: todo lo que no es span es log. */
1061
+ function recordKind(record) {
1062
+ return record.kind === "span" ? "span" : "log";
1063
+ }
910
1064
  /**
911
1065
  * Registry y dispatcher de transports. Mantiene el set activo de destinos
912
1066
  * de log (console, file, http, otlp, o transports custom registrados vía
@@ -1092,16 +1246,22 @@ var TransportManager = class {
1092
1246
  return false;
1093
1247
  }
1094
1248
  /**
1095
- * Dispatcha un {@link TransportRecord} a todos los transports activos
1096
- * que pasen el filtro de nivel.
1249
+ * Dispatcha un {@link TransportRecord} o {@link SpanRecord} a todos los
1250
+ * transports activos que acepten su kind.
1097
1251
  *
1098
1252
  * Por cada transport, en orden:
1099
- * 1. **Filtro de nivel**: si `record.levelValue < entry.levelValue`,
1100
- * se skipa (ese transport no recibe este record).
1101
- * 2. **Transform**: si `entry.options.transform` está seteado, se
1102
- * aplica al record. Si devuelve `null`, el record se droppea para
1103
- * este transport (no para los demás).
1104
- * 3. **Write**: se llama a `transport.write(transformedRecord)`. Si
1253
+ * 1. **Routing por kind**: el kind del record (`'log'` o `'span'`)
1254
+ * debe estar en el `accepts` del transport (default `['log']` cuando
1255
+ * no lo declara). Un `SpanRecord` JAMÁS llega a un transport
1256
+ * log-only, y viceversa esto evita que un `OtlpTransport` serialice
1257
+ * spans como `logRecords` hacia `/v1/logs`.
1258
+ * 2. **Filtro de nivel** (solo logs): si `record.levelValue <
1259
+ * entry.levelValue`, se skipa. Los spans no tienen nivel.
1260
+ * 3. **Transform** (solo logs): si `entry.options.transform` está
1261
+ * seteado, se aplica al record. Si devuelve `null`, el record se
1262
+ * droppea para este transport. Los spans NO pasan por transform —
1263
+ * su contrato asume shape de log (muta `record.message`).
1264
+ * 4. **Write**: se llama a `transport.write(record)`. Si
1105
1265
  * devuelve una Promise, se añade al batch await. Toda excepción
1106
1266
  * sincrónica o rechazo de Promise se captura y se loguea por
1107
1267
  * consola — el log call original del caller nunca rompe por un
@@ -1111,7 +1271,7 @@ var TransportManager = class {
1111
1271
  * resuelto (o fallado) su write. Para fire-and-forget desde el Logger,
1112
1272
  * el caller puede ignorar la Promise.
1113
1273
  *
1114
- * @param {TransportRecord} record - Record a dispatchar.
1274
+ * @param {TransportDispatchRecord} record - Record (log o span) a dispatchar.
1115
1275
  * @returns {Promise<void>} Resuelve cuando todos los writes terminaron
1116
1276
  * (exitosos o fallidos). Nunca rechaza.
1117
1277
  *
@@ -1122,19 +1282,25 @@ var TransportManager = class {
1122
1282
  * });
1123
1283
  *
1124
1284
  * @see {@link TransportRecord}
1285
+ * @see {@link SpanRecord}
1125
1286
  */
1126
1287
  async write(record) {
1127
1288
  const promises = [];
1289
+ const kind = recordKind(record);
1128
1290
  for (const entry of this.transports.values()) {
1129
- if (record.levelValue < entry.levelValue) continue;
1130
- let transformedRecord = record;
1131
- if (entry.options.transform) {
1132
- const result = entry.options.transform(record);
1133
- if (result === null) continue;
1134
- transformedRecord = result;
1291
+ if (!(entry.transport.accepts ?? DEFAULT_ACCEPTS).includes(kind)) continue;
1292
+ let dispatchRecord = record;
1293
+ if (kind === "log") {
1294
+ const logRecord = record;
1295
+ if (logRecord.levelValue < entry.levelValue) continue;
1296
+ if (entry.options.transform) {
1297
+ const result = entry.options.transform(logRecord);
1298
+ if (result === null) continue;
1299
+ dispatchRecord = result;
1300
+ }
1135
1301
  }
1136
1302
  try {
1137
- const result = entry.transport.write(transformedRecord);
1303
+ const result = entry.transport.write(dispatchRecord);
1138
1304
  if (result instanceof Promise) promises.push(result.catch((err) => {
1139
1305
  console.error("TransportManager.write: transport failed:", err);
1140
1306
  }));
@@ -1262,6 +1428,12 @@ Object.defineProperty(exports, "LOG_LEVEL_TO_SEVERITY_TEXT", {
1262
1428
  return LOG_LEVEL_TO_SEVERITY_TEXT;
1263
1429
  }
1264
1430
  });
1431
+ Object.defineProperty(exports, "OtlpTraceTransport", {
1432
+ enumerable: true,
1433
+ get: function() {
1434
+ return OtlpTraceTransport;
1435
+ }
1436
+ });
1265
1437
  Object.defineProperty(exports, "OtlpTransport", {
1266
1438
  enumerable: true,
1267
1439
  get: function() {
@@ -1275,4 +1447,4 @@ Object.defineProperty(exports, "TransportManager", {
1275
1447
  }
1276
1448
  });
1277
1449
 
1278
- //# sourceMappingURL=transports-yK6CL0Ml.cjs.map
1450
+ //# sourceMappingURL=transports-BZ-Mc2IT.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transports-BZ-Mc2IT.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/OtlpTraceTransport.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 * Kind de record que circula por el pipeline de transports. Los transports\n * declaran cuáles aceptan vía {@link ITransport.accepts}; el\n * {@link ITransportManager} enruta cada record solo a los transports cuyo\n * `accepts` lo incluye.\n */\nexport type TransportRecordKind = 'log' | 'span';\n\n/**\n * Valor admitido en los attributes de un {@link SpanRecord}. Deliberadamente\n * más estrecho que {@link LogAttributeValue}: el JSON mapping de OTLP traces\n * solo acepta primitivos en `span.attributes` sin schema extra.\n */\nexport type SpanAttributeValue = string | number | boolean;\n\n/**\n * Bag de attributes de un span (compatible con el modelo de attributes de\n * OpenTelemetry para traces).\n */\nexport interface SpanAttributes {\n [key: string]: SpanAttributeValue;\n}\n\n/**\n * Status de finalización de un span, conforme al proto OTel `Status`.\n * `code: 2` = `STATUS_CODE_ERROR`.\n *\n * @see https://opentelemetry.io/docs/specs/otel/trace/api/#set-status\n */\nexport interface SpanStatus {\n code: number;\n message?: string;\n}\n\n/**\n * Record de span (trace) que emite el trio API `event`/`span`/`startSpan`.\n * A diferencia de {@link TransportRecord}, NO atraviesa el hook path de logs:\n * el {@link ITransportManager} lo despacha solo a transports cuyo\n * {@link ITransport.accepts} incluye `'span'`.\n *\n * `traceId` son 16 bytes en hex (32 chars) y `spanId` 8 bytes en hex\n * (16 chars), conforme a la spec W3C Trace Context que exige OTLP/HTTP.\n */\nexport interface SpanRecord {\n kind: 'span';\n /** Trace id hex de 32 chars. Se hereda del span activo si existe. */\n traceId: string;\n /** Span id hex de 16 chars. */\n spanId: string;\n /** Span id del padre, tomado del span activo en el ALS al crearse. */\n parentSpanId?: string;\n /** Nombre de la operación (`db.query`, `http.request`, ...). */\n name: string;\n /** Span kind OTel numérico. 1 = SPAN_KIND_INTERNAL. */\n spanKind: number;\n /** Instante de inicio en nanosegundos (string decimal). */\n startTimeUnixNano: string;\n /** Instante de fin en nanosegundos (string decimal). `'0'` hasta end(). */\n endTimeUnixNano: string;\n /** Attributes del span. Mutables vía {@link Span.set} hasta el end(). */\n attributes: SpanAttributes;\n /** Status de finalización. Presente si el span terminó con `fail()`. */\n status?: SpanStatus;\n /** `true` cuando el span se cerró por flush sin `end()` explícito. */\n incomplete?: boolean;\n /** Scope del logger que emitió el span (nombre del scoped logger). */\n scope: string;\n}\n\n/**\n * Handle de un span externally-ended. Lo devuelven `startSpan()` y el\n * callback de `span(fn)`.\n */\nexport interface Span {\n /**\n * Añade o actualiza un attribute del span.\n *\n * @param key - Nombre del attribute.\n * @param value - Valor primitivo (string | number | boolean).\n * @returns `this`, para encadenar.\n */\n set(key: string, value: SpanAttributeValue): this;\n /**\n * Finaliza el span: fija `endTimeUnixNano` y encola el\n * {@link SpanRecord} para export. Llamadas posteriores son no-op.\n *\n * @param attributes - Attributes finales a mergear antes del export.\n */\n end(attributes?: SpanAttributes): void;\n /**\n * Marca el span como fallido (`status.code = 2` con el mensaje del\n * error) y luego lo finaliza como {@link end}.\n *\n * @param err - Error (o valor) que causó el fallo.\n */\n fail(err: unknown): void;\n /** Trace id hex de 32 chars del span. */\n readonly traceId: string;\n /** Span id hex de 16 chars del span. */\n readonly spanId: string;\n}\n\n/**\n * Unión de todo record que puede atravesar el {@link ITransportManager}:\n * un log ({@link TransportRecord}) o un span ({@link SpanRecord}).\n * El routing por kind lo hace el manager vía {@link ITransport.accepts}.\n */\nexport type TransportDispatchRecord = TransportRecord | SpanRecord;\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 *\n * Un transport declara qué kinds de record acepta vía {@link accepts}:\n * ausente o `['log']` → log-only (default, comportamiento de todos los\n * transports históricos); `['span']` → solo spans (p.ej. `OtlpTraceTransport`);\n * `['log', 'span']` → ambos. El {@link ITransportManager} garantiza que un\n * transport jamás recibe un record de un kind que no declaró — un\n * `SpanRecord` nunca llega a un transport de logs.\n */\nexport interface ITransport {\n readonly name: string;\n /**\n * Kinds de record que este transport acepta. **Default `['log']` cuando\n * está ausente** — los transports existentes siguen siendo log-only sin\n * cambios.\n */\n readonly accepts?: readonly TransportRecordKind[];\n write(record: TransportDispatchRecord): 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 /**\n * Dispatcha un record (log o span) a los transports que acepten su kind\n * y (para logs) pasen el filtro de nivel del transport.\n */\n write(record: TransportDispatchRecord): 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 */\nexport type 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\n/**\n * Quita la barra final de un endpoint si la lleva.\n *\n * @internal Compartido con {@link OtlpTraceTransport}; no es API pública.\n */\nexport function 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} y\n * {@link OtlpTraceTransport}; 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 */\nexport function 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\n/**\n * Convierte un valor JS a un `AnyValue` de OTel. Compartido con\n * {@link OtlpTraceTransport} para serializar attributes de span con el mismo\n * mapping.\n *\n * @internal Exportado para reuse entre transports OTLP; no es API pública.\n */\nexport function 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 { SpanRecord, TransportRecord, ILogResource } from '../types/index.js';\nimport { HttpTransport, type HttpTransportOptions } from './HttpTransport.js';\nimport {\n toOtlpAttribute,\n readIngestKey,\n stripTrailingSlash,\n type OtlpAttributeValue,\n type OtlpTransportOptions\n} from './OtlpTransport.js';\n\n/**\n * Opciones de configuración para {@link OtlpTraceTransport}. Hereda todas las\n * opciones de {@link OtlpTransportOptions} (endpoint, serviceName,\n * ingestKeyEnvVar, batching, retry, ...) porque el transport de traces espeja\n * al de logs.\n */\nexport interface OtlpTraceTransportOptions extends OtlpTransportOptions {\n /**\n * Versión del scope que emite los spans (`scopeSpans[].scope.version`).\n * Opcional: si se omite, el bloque `scope` se emite sin `version` en vez\n * de inventar una.\n */\n scopeVersion?: string;\n}\n\n/**\n * Shape del body JSON de un request OTLP/HTTP para traces. Coincide con el\n * JSON mapping del proto `TracesData` para `/v1/traces`. Ver\n * https://opentelemetry.io/docs/specs/otlp/#json-protobuf-encoding\n */\ninterface OtlpTracesPayload {\n resourceSpans: Array<{\n resource: {\n attributes: Array<{ key: string; value: { stringValue: string } }>;\n };\n scopeSpans: Array<{\n scope: {\n name: string;\n version?: string;\n };\n spans: Array<OtlpSpan>;\n }>;\n }>;\n}\n\n/**\n * Span individual dentro del payload OTLP. Mappea 1:1 los campos del\n * `SpanRecord` al proto `Span` de la spec OTel Trace.\n * @see https://opentelemetry.io/docs/specs/otel/trace/api/#span\n */\ninterface OtlpSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n kind: number;\n startTimeUnixNano: string;\n endTimeUnixNano: string;\n attributes?: Array<{ key: string; value: OtlpAttributeValue }>;\n status?: { code: number; message?: string };\n}\n\n/**\n * Transport OTLP/HTTP para traces (spans) — POST a `<endpoint>/v1/traces`.\n *\n * Espeja {@link OtlpTransport} (que va a `/v1/logs`): hereda de\n * {@link HttpTransport} el batch, retry con backoff, buffer acotado, close\n * asincrónico y el hook on-error. Solo cambia la shape del payload y acepta\n * exclusivamente records de kind `'span'` (`accepts = ['span']`): el\n * {@link TransportManager} garantiza que jamás recibe un log record.\n *\n * @example\n * ```ts\n * logger.addTransport({\n * target: new OtlpTraceTransport({\n * endpoint: 'https://otelcollector.example.com:4318',\n * serviceName: 'my-app',\n * scopeVersion: '1.2.3'\n * })\n * });\n *\n * await logger.span('db.query', { table: 'users' }, async () => {\n * await db.select();\n * });\n * ```\n */\nexport class OtlpTraceTransport extends HttpTransport {\n override readonly name = 'otlp-trace';\n\n /** Este transport SOLO acepta spans — jamás recibe log records. */\n readonly accepts = ['span'] as const;\n\n private readonly resource: ILogResource;\n private readonly scopeVersion: string | undefined;\n /** Resuelto al construir. Nunca se loguea, nunca se escribe a source. */\n private readonly ingestKeyValue: string | undefined;\n\n constructor(options: OtlpTraceTransportOptions) {\n if (!options.endpoint) {\n throw new Error('OtlpTraceTransport: `endpoint` is required');\n }\n if (!options.serviceName) {\n throw new Error('OtlpTraceTransport: `serviceName` is required');\n }\n\n const ingestKey = readIngestKey(options.ingestKeyEnvVar);\n\n const httpOptions: HttpTransportOptions = {\n url: `${stripTrailingSlash(options.endpoint)}/v1/traces`,\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.scopeVersion = options.scopeVersion;\n this.ingestKeyValue = ingestKey;\n }\n\n /**\n * Construye el payload JSON OTLP/HTTP a partir de los spans bufferizados.\n * Un bloque `resourceSpans` por batch. Expuesto para tests y subclasses.\n *\n * @param spans - Spans a serializar dentro del payload.\n * @returns Objeto `TracesData` listo para `JSON.stringify`.\n */\n buildPayload(spans: SpanRecord[]): OtlpTracesPayload {\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 resourceSpans: [\n {\n resource: { attributes: resourceAttrs },\n scopeSpans: [\n {\n scope: {\n name: 'better-logger',\n ...(this.scopeVersion ? { version: this.scopeVersion } : {})\n },\n spans: spans.map(s => this.toOtlpSpan(s))\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 protected override serializeBody(records: TransportRecord[]): string {\n // accepts=['span'] hace que el manager solo despache SpanRecords aquí;\n // el cast refleja ese routing by-kind.\n return JSON.stringify(this.buildPayload(records as unknown as SpanRecord[]));\n }\n\n /**\n * Construye los headers del request. Agrega `signoz-ingestion-key` con el\n * valor resuelto al construir.\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 toOtlpSpan(span: SpanRecord): OtlpSpan {\n const attributes: NonNullable<OtlpSpan['attributes']> = [\n { key: 'logger.scope', value: { stringValue: span.scope } }\n ];\n\n for (const [key, value] of Object.entries(span.attributes)) {\n const mapped = toOtlpAttribute(value);\n if (mapped) attributes.push({ key, value: mapped });\n }\n\n if (span.incomplete) {\n attributes.push({ key: 'span.incomplete', value: { boolValue: true } });\n }\n\n const out: OtlpSpan = {\n traceId: span.traceId,\n spanId: span.spanId,\n ...(span.parentSpanId ? { parentSpanId: span.parentSpanId } : {}),\n name: span.name,\n kind: span.spanKind,\n startTimeUnixNano: span.startTimeUnixNano,\n endTimeUnixNano: span.endTimeUnixNano,\n attributes\n };\n\n if (span.status) {\n out.status = span.status;\n }\n\n return out;\n }\n}\n\n// ===== Internal helpers =====\n","import type {\n TransportRecord,\n TransportTarget,\n TransportOptions,\n ITransport,\n ITransportManager,\n LogLevel,\n SpanRecord,\n TransportDispatchRecord\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';\nimport { OtlpTraceTransport } from './OtlpTraceTransport.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 cinco built-ins registrados automáticamente:\n * - `'console'` → {@link ConsoleTransport} (logs)\n * - `'file'` → {@link FileTransport} (logs)\n * - `'http'` → {@link HttpTransport} (logs)\n * - `'otlp'` → {@link OtlpTransport} (logs → `/v1/logs`)\n * - `'otlp-trace'` → {@link OtlpTraceTransport} (spans → `/v1/traces`)\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 ['otlp-trace', OtlpTraceTransport as unknown as TransportConstructor]\n]);\n\n/** `accepts` efectivo cuando el transport no lo declara: log-only. */\nconst DEFAULT_ACCEPTS: readonly ('log' | 'span')[] = ['log'];\n\n/** Estrecha un record del pipeline a su kind: todo lo que no es span es log. */\nfunction recordKind(record: TransportDispatchRecord): 'log' | 'span' {\n return (record as SpanRecord).kind === 'span' ? 'span' : 'log';\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} o {@link SpanRecord} a todos los\n * transports activos que acepten su kind.\n *\n * Por cada transport, en orden:\n * 1. **Routing por kind**: el kind del record (`'log'` o `'span'`)\n * debe estar en el `accepts` del transport (default `['log']` cuando\n * no lo declara). Un `SpanRecord` JAMÁS llega a un transport\n * log-only, y viceversa — esto evita que un `OtlpTransport` serialice\n * spans como `logRecords` hacia `/v1/logs`.\n * 2. **Filtro de nivel** (solo logs): si `record.levelValue <\n * entry.levelValue`, se skipa. Los spans no tienen nivel.\n * 3. **Transform** (solo logs): si `entry.options.transform` está\n * seteado, se aplica al record. Si devuelve `null`, el record se\n * droppea para este transport. Los spans NO pasan por transform —\n * su contrato asume shape de log (muta `record.message`).\n * 4. **Write**: se llama a `transport.write(record)`. 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 {TransportDispatchRecord} record - Record (log o span) 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 * @see {@link SpanRecord}\n */\n async write(record: TransportDispatchRecord): Promise<void> {\n const promises: Promise<unknown>[] = [];\n const kind = recordKind(record);\n\n for (const entry of this.transports.values()) {\n const accepts = entry.transport.accepts ?? DEFAULT_ACCEPTS;\n if (!accepts.includes(kind)) {\n continue;\n }\n\n let dispatchRecord: TransportDispatchRecord = record;\n\n if (kind === 'log') {\n const logRecord = record as TransportRecord;\n if (logRecord.levelValue < entry.levelValue) {\n continue;\n }\n if (entry.options.transform) {\n const result = entry.options.transform(logRecord);\n if (result === null) continue;\n dispatchRecord = result;\n }\n }\n\n try {\n const result = entry.transport.write(dispatchRecord);\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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"}