logixia 1.12.4 → 1.12.6

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 (166) hide show
  1. package/README.md +80 -27
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/async-context-CSHWoHMB.js +88 -0
  4. package/dist/async-context-CSHWoHMB.js.map +1 -0
  5. package/dist/async-context-CwBNMm2E.mjs +68 -0
  6. package/dist/async-context-CwBNMm2E.mjs.map +1 -0
  7. package/dist/azure-monitor.transport-CG7ClkYj.d.ts +62 -0
  8. package/dist/azure-monitor.transport-CG7ClkYj.d.ts.map +1 -0
  9. package/dist/azure-monitor.transport-CHnjVRF-.mjs +152 -0
  10. package/dist/azure-monitor.transport-CHnjVRF-.mjs.map +1 -0
  11. package/dist/azure-monitor.transport-Cm74kXZS.d.mts +62 -0
  12. package/dist/azure-monitor.transport-Cm74kXZS.d.mts.map +1 -0
  13. package/dist/azure-monitor.transport-CrvfHd9C.js +157 -0
  14. package/dist/azure-monitor.transport-CrvfHd9C.js.map +1 -0
  15. package/dist/azure.d.mts +3 -0
  16. package/dist/azure.d.ts +3 -0
  17. package/dist/azure.js +6 -0
  18. package/dist/azure.mjs +6 -0
  19. package/dist/batch-buffer-BY-mRWIK.js +170 -0
  20. package/dist/batch-buffer-BY-mRWIK.js.map +1 -0
  21. package/dist/batch-buffer-PGvSgbmd.mjs +132 -0
  22. package/dist/batch-buffer-PGvSgbmd.mjs.map +1 -0
  23. package/dist/browser.d.mts +427 -0
  24. package/dist/browser.d.mts.map +1 -0
  25. package/dist/browser.d.ts +427 -0
  26. package/dist/browser.d.ts.map +1 -0
  27. package/dist/browser.js +590 -0
  28. package/dist/browser.js.map +1 -0
  29. package/dist/browser.mjs +586 -0
  30. package/dist/browser.mjs.map +1 -0
  31. package/dist/chunk-DLSkYcPk.mjs +7 -0
  32. package/dist/chunk-nOFOJqeH.js +30 -0
  33. package/dist/cloudwatch.d.mts +3 -0
  34. package/dist/cloudwatch.d.ts +3 -0
  35. package/dist/cloudwatch.js +6 -0
  36. package/dist/cloudwatch.mjs +6 -0
  37. package/dist/cloudwatch.transport-CGHY4buE.d.mts +81 -0
  38. package/dist/cloudwatch.transport-CGHY4buE.d.mts.map +1 -0
  39. package/dist/cloudwatch.transport-DK_8cKd3.d.ts +81 -0
  40. package/dist/cloudwatch.transport-DK_8cKd3.d.ts.map +1 -0
  41. package/dist/cloudwatch.transport-pNBNWJLt.js +370 -0
  42. package/dist/cloudwatch.transport-pNBNWJLt.js.map +1 -0
  43. package/dist/cloudwatch.transport-yBwm4OeO.mjs +360 -0
  44. package/dist/cloudwatch.transport-yBwm4OeO.mjs.map +1 -0
  45. package/dist/coerce.utils-BN980Y9V.js +44 -0
  46. package/dist/coerce.utils-BN980Y9V.js.map +1 -0
  47. package/dist/coerce.utils-Dxv4_ikC.mjs +38 -0
  48. package/dist/coerce.utils-Dxv4_ikC.mjs.map +1 -0
  49. package/dist/correlation.d.mts +238 -0
  50. package/dist/correlation.d.mts.map +1 -0
  51. package/dist/correlation.d.ts +238 -0
  52. package/dist/correlation.d.ts.map +1 -0
  53. package/dist/correlation.js +272 -0
  54. package/dist/correlation.js.map +1 -0
  55. package/dist/correlation.mjs +261 -0
  56. package/dist/correlation.mjs.map +1 -0
  57. package/dist/gcp.d.mts +3 -0
  58. package/dist/gcp.d.ts +3 -0
  59. package/dist/gcp.js +6 -0
  60. package/dist/gcp.mjs +6 -0
  61. package/dist/gcp.transport-BJuNRFe_.mjs +192 -0
  62. package/dist/gcp.transport-BJuNRFe_.mjs.map +1 -0
  63. package/dist/gcp.transport-BOhkVKlI.d.ts +71 -0
  64. package/dist/gcp.transport-BOhkVKlI.d.ts.map +1 -0
  65. package/dist/gcp.transport-Bi6CUqZR.d.mts +71 -0
  66. package/dist/gcp.transport-Bi6CUqZR.d.mts.map +1 -0
  67. package/dist/gcp.transport-SHdtc1n0.js +198 -0
  68. package/dist/gcp.transport-SHdtc1n0.js.map +1 -0
  69. package/dist/{index-CwFww7VC.d.ts → index-BPKwwERo.d.mts} +18 -174
  70. package/dist/index-BPKwwERo.d.mts.map +1 -0
  71. package/dist/{index-DnyeaoZU.d.mts → index-C1X-TE5I.d.ts} +18 -174
  72. package/dist/index-C1X-TE5I.d.ts.map +1 -0
  73. package/dist/index.d.mts +41 -11
  74. package/dist/index.d.mts.map +1 -1
  75. package/dist/index.d.ts +40 -10
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +87 -147
  78. package/dist/index.js.map +1 -1
  79. package/dist/index.mjs +62 -132
  80. package/dist/index.mjs.map +1 -1
  81. package/dist/internal-log-BUJYMZbE.js +70 -0
  82. package/dist/internal-log-BUJYMZbE.js.map +1 -0
  83. package/dist/internal-log-Cm8SOWSV.mjs +52 -0
  84. package/dist/internal-log-Cm8SOWSV.mjs.map +1 -0
  85. package/dist/{logitron-logger.module-BIGTm2V6.mjs → logitron-logger.module-Bs3XYZgF.mjs} +266 -166
  86. package/dist/logitron-logger.module-Bs3XYZgF.mjs.map +1 -0
  87. package/dist/{logitron-logger.module-BTxDpK6u.js → logitron-logger.module-BsDZR5BH.js} +297 -203
  88. package/dist/logitron-logger.module-BsDZR5BH.js.map +1 -0
  89. package/dist/{logitron-logger.module-BmT-ewin.d.ts → logitron-logger.module-CxT1F6Cm.d.ts} +23 -10
  90. package/dist/logitron-logger.module-CxT1F6Cm.d.ts.map +1 -0
  91. package/dist/{logitron-logger.module-BCq3sF27.d.mts → logitron-logger.module-zDKVd_o0.d.mts} +23 -10
  92. package/dist/logitron-logger.module-zDKVd_o0.d.mts.map +1 -0
  93. package/dist/middleware.d.mts +5 -2
  94. package/dist/middleware.d.mts.map +1 -1
  95. package/dist/middleware.d.ts +5 -2
  96. package/dist/middleware.d.ts.map +1 -1
  97. package/dist/middleware.js +13 -4
  98. package/dist/middleware.js.map +1 -1
  99. package/dist/middleware.mjs +14 -4
  100. package/dist/middleware.mjs.map +1 -1
  101. package/dist/nest.d.mts +3 -2
  102. package/dist/nest.d.mts.map +1 -1
  103. package/dist/nest.d.ts +3 -2
  104. package/dist/nest.d.ts.map +1 -1
  105. package/dist/nest.js +6 -2
  106. package/dist/nest.mjs +6 -2
  107. package/dist/safe-stringify-DJ66tW5I.mjs +118 -0
  108. package/dist/safe-stringify-DJ66tW5I.mjs.map +1 -0
  109. package/dist/safe-stringify-DWo3aZp_.js +142 -0
  110. package/dist/safe-stringify-DWo3aZp_.js.map +1 -0
  111. package/dist/search-CFQRISUv.mjs +1596 -0
  112. package/dist/search-CFQRISUv.mjs.map +1 -0
  113. package/dist/search-CQOo-Iq6.d.mts +816 -0
  114. package/dist/search-CQOo-Iq6.d.mts.map +1 -0
  115. package/dist/search-DujhR94O.d.ts +816 -0
  116. package/dist/search-DujhR94O.d.ts.map +1 -0
  117. package/dist/search-njTI2hdo.js +1631 -0
  118. package/dist/search-njTI2hdo.js.map +1 -0
  119. package/dist/search.d.mts +4 -1
  120. package/dist/search.d.ts +4 -1
  121. package/dist/search.js +9 -1
  122. package/dist/search.mjs +3 -2
  123. package/dist/testing.d.mts +2 -1
  124. package/dist/testing.d.mts.map +1 -1
  125. package/dist/testing.d.ts +2 -1
  126. package/dist/testing.d.ts.map +1 -1
  127. package/dist/{transport.manager-ciG4r5uz.js → transport.manager-BivyUTtU.js} +497 -437
  128. package/dist/transport.manager-BivyUTtU.js.map +1 -0
  129. package/dist/{transport.manager-DETpXuaY.mjs → transport.manager-D6KL3t9V.mjs} +473 -364
  130. package/dist/transport.manager-D6KL3t9V.mjs.map +1 -0
  131. package/dist/transport.types-B5vcyv2k.d.mts +187 -0
  132. package/dist/transport.types-B5vcyv2k.d.mts.map +1 -0
  133. package/dist/transport.types-BuY84TwO.d.ts +187 -0
  134. package/dist/transport.types-BuY84TwO.d.ts.map +1 -0
  135. package/dist/transports.d.mts +90 -29
  136. package/dist/transports.d.mts.map +1 -1
  137. package/dist/transports.d.ts +90 -29
  138. package/dist/transports.d.ts.map +1 -1
  139. package/dist/transports.js +14 -2
  140. package/dist/transports.mjs +10 -2
  141. package/dist/worker-entry.js +1241 -0
  142. package/dist/worker.d.mts +3 -0
  143. package/dist/worker.d.ts +3 -0
  144. package/dist/worker.js +4 -0
  145. package/dist/worker.mjs +4 -0
  146. package/dist/worker.transport-BP8Q28Z2.d.mts +76 -0
  147. package/dist/worker.transport-BP8Q28Z2.d.mts.map +1 -0
  148. package/dist/worker.transport-BhMyvqQ2.mjs +167 -0
  149. package/dist/worker.transport-BhMyvqQ2.mjs.map +1 -0
  150. package/dist/worker.transport-CfH6z37Z.js +168 -0
  151. package/dist/worker.transport-CfH6z37Z.js.map +1 -0
  152. package/dist/worker.transport-mKXgNK0L.d.ts +76 -0
  153. package/dist/worker.transport-mKXgNK0L.d.ts.map +1 -0
  154. package/package.json +23 -18
  155. package/dist/index-CwFww7VC.d.ts.map +0 -1
  156. package/dist/index-DnyeaoZU.d.mts.map +0 -1
  157. package/dist/logitron-logger.module-BCq3sF27.d.mts.map +0 -1
  158. package/dist/logitron-logger.module-BIGTm2V6.mjs.map +0 -1
  159. package/dist/logitron-logger.module-BTxDpK6u.js.map +0 -1
  160. package/dist/logitron-logger.module-BmT-ewin.d.ts.map +0 -1
  161. package/dist/search-Cg_OasF-.d.ts +0 -1
  162. package/dist/search-DN676Dnz.d.mts +0 -1
  163. package/dist/search-DOvSI-mb.mjs +0 -1
  164. package/dist/search-DoZF3RZj.js +0 -0
  165. package/dist/transport.manager-DETpXuaY.mjs.map +0 -1
  166. package/dist/transport.manager-ciG4r5uz.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","names":["out: unknown","obj: Record<string, unknown>","_otelApi: OtelApi | null | undefined","_bridgeOptions: OtelBridgeOptions | null","warnings: string[]","LEVEL_ORDER: Record<BrowserLogLevel, number>","parsed: URL","res: Response","entry: BrowserLogEntry"],"sources":["../src/utils/safe-stringify.ts","../src/utils/otel.ts","../src/utils/internal-log.ts","../src/utils/typed-logger.ts","../src/browser.ts"],"sourcesContent":["/**\n * logixia — robust JSON serialization for log payloads.\n *\n * Modern Winston/Pino already neutralize circular refs to `[Circular]` (via\n * safe-stable-stringify / fast-safe-stringify), so merely \"surviving cycles\" is\n * not a differentiator. This goes one notch further:\n *\n * - **BigInt** → serialized (as a string by default; JSON.stringify throws on\n * BigInt, which silently breaks logging of e.g. DB bigint ids).\n * - **Deterministic key order** (optional) so identical objects hash/diff equal.\n * - **True decycle** (optional) — replace repeated references with JSONPath\n * `$ref` pointers that round-trip back to the original object graph, instead\n * of the lossy `[Circular]` tag. Useful when you need to reconstruct shared\n * structure downstream.\n *\n * @example\n * ```ts\n * safeStringify({ id: 10n, self: obj }); // → BigInt + cycle safe\n * safeStringify(graph, { decycle: true }); // → round-trippable $ref pointers\n * const back = retrocycle(JSON.parse(json)); // → reconstruct shared refs\n * ```\n */\n\nexport interface SafeStringifyOptions {\n /** Indentation passed to JSON.stringify (number of spaces or a string). */\n indent?: number | string;\n /** Sort object keys for deterministic output. Default: false. */\n deterministic?: boolean;\n /**\n * Use round-trippable `$ref` JSONPath pointers for repeated references instead\n * of the lossy \"[Circular]\" tag. Default: false.\n */\n decycle?: boolean;\n /** How to render BigInt: 'string' (default) or 'number' (may lose precision). */\n bigint?: 'string' | 'number';\n}\n\n/** Build a JSONPath like `$[\"a\"][0][\"b\"]` for a decycle pointer. */\nfunction jsonPath(parts: Array<string | number>): string {\n let path = '$';\n for (const p of parts) {\n path += typeof p === 'number' ? `[${p}]` : `[${JSON.stringify(p)}]`;\n }\n return path;\n}\n\n/**\n * Serialize any value to JSON without throwing on circular references or BigInt.\n * Circular refs become `\"[Circular]\"` (or `{ $ref }` pointers when `decycle`).\n */\nexport function safeStringify(value: unknown, options: SafeStringifyOptions = {}): string {\n const { indent, deterministic = false, decycle = false, bigint = 'string' } = options;\n\n if (decycle) {\n return JSON.stringify(decycleValue(value, bigint), undefined, indent);\n }\n\n const seen = new WeakSet<object>();\n\n const transform = (val: unknown): unknown => {\n if (typeof val === 'bigint') return bigint === 'number' ? Number(val) : val.toString();\n if (typeof val === 'function') return `[Function: ${val.name || 'anonymous'}]`;\n if (typeof val === 'symbol') return val.toString();\n if (val === null || typeof val !== 'object') return val;\n\n if (seen.has(val)) return '[Circular]';\n seen.add(val);\n\n let out: unknown;\n const toJSON = (val as { toJSON?: unknown }).toJSON;\n if (Array.isArray(val)) {\n out = val.map((item) => transform(item));\n } else if (typeof toJSON === 'function') {\n // Dates, URLs, Buffers and classes with their own toJSON print the way\n // JSON.stringify would; a toJSON that returns `this` hits the cycle check.\n out = transform(toJSON.call(val));\n } else {\n const rec = val as Record<string, unknown>;\n const keys = deterministic ? Object.keys(rec).sort() : Object.keys(rec);\n const obj: Record<string, unknown> = {};\n for (const k of keys) {\n if (k === '__proto__' || k === 'constructor' || k === 'prototype') continue;\n obj[k] = transform(rec[k]);\n }\n out = obj;\n }\n // Allow the same object to appear in sibling branches (not a true cycle):\n // remove from `seen` after we finish its subtree.\n seen.delete(val);\n return out;\n };\n\n return JSON.stringify(transform(value), undefined, indent);\n}\n\n/**\n * Replace repeated object references with round-trippable `{ \"$ref\": \"$...\" }`\n * JSONPath pointers (the classic Crockford decycle). Pair with {@link retrocycle}\n * to reconstruct the original shared/circular graph.\n */\nexport function decycleValue(value: unknown, bigint: 'string' | 'number' = 'string'): unknown {\n const paths = new WeakMap<object, string>();\n\n const walk = (val: unknown, path: Array<string | number>): unknown => {\n if (typeof val === 'bigint') return bigint === 'number' ? Number(val) : val.toString();\n if (val === null || typeof val !== 'object') return val;\n if (val instanceof Date) return val.toISOString();\n\n const existing = paths.get(val);\n if (existing !== undefined) return { $ref: existing };\n paths.set(val, jsonPath(path));\n\n if (Array.isArray(val)) {\n return val.map((item, i) => walk(item, [...path, i]));\n }\n const rec = val as Record<string, unknown>;\n const obj: Record<string, unknown> = {};\n for (const k of Object.keys(rec)) {\n if (k === '__proto__' || k === 'constructor' || k === 'prototype') continue;\n obj[k] = walk(rec[k], [...path, k]);\n }\n return obj;\n };\n\n return walk(value, []);\n}\n\n/**\n * Inverse of {@link decycleValue}: resolve `{ \"$ref\": \"$...\" }` pointers back\n * into the live object graph (mutates and returns the parsed input).\n */\nexport function retrocycle<T>(root: T): T {\n const refRe = /^\\$(?:\\[(?:\\d+|\"(?:[^\"\\\\]|\\\\.)*\")\\])*$/;\n\n const resolve = (path: string): unknown => {\n // Parse the JSONPath segments back out.\n const segs: Array<string | number> = [];\n const partRe = /\\[(\\d+|\"(?:[^\"\\\\]|\\\\.)*\")\\]/g;\n let m: RegExpExecArray | null;\n while ((m = partRe.exec(path)) !== null) {\n const raw = m[1]!;\n segs.push(raw.startsWith('\"') ? (JSON.parse(raw) as string) : Number(raw));\n }\n let node: unknown = root;\n for (const s of segs) {\n node = (node as Record<string | number, unknown>)[s];\n }\n return node;\n };\n\n const walk = (val: unknown): void => {\n if (val === null || typeof val !== 'object') return;\n const rec = val as Record<string, unknown>;\n for (const k of Object.keys(rec)) {\n const child = rec[k];\n if (\n child !== null &&\n typeof child === 'object' &&\n typeof (child as { $ref?: unknown }).$ref === 'string' &&\n refRe.test((child as { $ref: string }).$ref)\n ) {\n rec[k] = resolve((child as { $ref: string }).$ref);\n } else {\n walk(child);\n }\n }\n };\n\n walk(root);\n return root;\n}\n\n/**\n * `JSON.stringify` for log output. Takes the plain fast path first and only\n * falls back to {@link safeStringify} when that throws (a cycle, a BigInt, a\n * throwing `toJSON`), so a bad payload costs nothing until it shows up.\n * Never throws: if even the safe walk fails, the value is replaced by a marker.\n */\nexport function stringifyForLog(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n try {\n return safeStringify(value);\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n return JSON.stringify(`[Unserializable: ${reason}]`);\n }\n }\n}\n","/**\n * logixia — OpenTelemetry auto trace-log correlation\n *\n * Zero-config: if `@opentelemetry/api` is installed, logixia automatically\n * reads the active span context and injects `traceId`, `spanId`, and\n * `traceFlags` into every log entry. No manual wiring needed.\n *\n * The integration is completely optional — if `@opentelemetry/api` is absent\n * (or the OTel SDK is not initialised), all helpers return `undefined` silently.\n *\n * Additionally exposes a `createOtelLogExporter()` factory that routes every\n * logixia log entry through the active OTel LoggerProvider (OTLP export).\n *\n * @example Auto bridge (zero config)\n * ```ts\n * import { createLogger } from 'logixia';\n * import { initOtelBridge } from 'logixia';\n *\n * // Call once at app startup, after your OTel SDK is initialised\n * initOtelBridge();\n *\n * const logger = createLogger({ appName: 'api' });\n * // Every logger.info / warn / error call now auto-injects traceId + spanId\n * // from the currently active OTel span — no per-call code needed.\n * ```\n *\n * @example Manual span context injection\n * ```ts\n * import { getActiveOtelContext } from 'logixia';\n *\n * const ctx = getActiveOtelContext();\n * await logger.info('Payment processed', { ...ctx, orderId: 'ord_123' });\n * // → { traceId: 'abc...', spanId: 'def...', traceFlags: 1, orderId: 'ord_123' }\n * ```\n */\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface OtelSpanContext {\n /** W3C 32-hex-char trace ID */\n traceId: string;\n /** W3C 16-hex-char span ID */\n spanId: string;\n /** W3C trace-flags integer (1 = sampled) */\n traceFlags: number;\n /** Whether this context is from a valid, sampled span */\n isSampled: boolean;\n}\n\nexport interface OtelBridgeOptions {\n /**\n * Field name written to log entries for the trace ID.\n * @default 'traceId'\n */\n traceIdField?: string;\n /**\n * Field name written to log entries for the span ID.\n * @default 'spanId'\n */\n spanIdField?: string;\n /**\n * Field name written to log entries for trace flags.\n * @default 'traceFlags'\n */\n traceFlagsField?: string;\n /**\n * Only inject context when the span is sampled (traceFlags bit 1 set).\n * @default false\n */\n sampledOnly?: boolean;\n}\n\n// ── OTel API dynamic import ───────────────────────────────────────────────────\n\ntype OtelApi = {\n context: { active(): unknown };\n trace: {\n getSpanContext(\n ctx: unknown\n ): { traceId: string; spanId: string; traceFlags: number } | undefined;\n isSpanContextValid(sc: { traceId: string; spanId: string; traceFlags: number }): boolean;\n TraceFlags: { SAMPLED: number };\n };\n};\n\nlet _otelApi: OtelApi | null | undefined; // undefined = not yet resolved; null = not available\n\nfunction tryLoadOtelApi(): OtelApi | null {\n if (_otelApi !== undefined) return _otelApi;\n try {\n // Dynamic require so the package stays optional — no hard peer dep\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n _otelApi = require('@opentelemetry/api') as OtelApi;\n return _otelApi;\n } catch {\n _otelApi = null;\n return null;\n }\n}\n\n// ── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Read the currently active OTel span context (if any) and return its fields\n * in a plain object suitable for spreading into a log entry.\n *\n * Returns `undefined` when:\n * - `@opentelemetry/api` is not installed\n * - No active span exists (root context)\n * - The span context is invalid (all-zeros)\n */\nexport function getActiveOtelContext(opts: OtelBridgeOptions = {}): OtelSpanContext | undefined {\n const api = tryLoadOtelApi();\n if (!api) return undefined;\n\n // Wrap the whole OTel interaction: this runs on EVERY log call via the bridge,\n // and a broken/mismatched OTel SDK whose context/trace methods throw would\n // otherwise crash logging itself. The integration is meant to be silent — on\n // any failure we just skip injection.\n try {\n const ctx = api.context.active();\n const sc = api.trace.getSpanContext(ctx);\n if (!sc || !api.trace.isSpanContextValid(sc)) return undefined;\n\n const isSampled =\n (sc.traceFlags & api.trace.TraceFlags.SAMPLED) === api.trace.TraceFlags.SAMPLED;\n\n if (opts.sampledOnly && !isSampled) return undefined;\n\n return {\n traceId: sc.traceId,\n spanId: sc.spanId,\n traceFlags: sc.traceFlags,\n isSampled,\n };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns a metadata object with OTel context fields ready to merge into a log call,\n * using the configured field names.\n *\n * Returns `{}` when no active span exists (safe to spread unconditionally).\n *\n * @example\n * ```ts\n * await logger.info('Payment processed', {\n * ...getOtelMetaFields(),\n * orderId: 'ord_123',\n * });\n * ```\n */\nexport function getOtelMetaFields(opts: OtelBridgeOptions = {}): Record<string, unknown> {\n const { traceIdField = 'traceId', spanIdField = 'spanId', traceFlagsField = 'traceFlags' } = opts;\n\n const ctx = getActiveOtelContext(opts);\n if (!ctx) return {};\n\n return {\n [traceIdField]: ctx.traceId,\n [spanIdField]: ctx.spanId,\n [traceFlagsField]: ctx.traceFlags,\n };\n}\n\n// ── Module-level bridge state ─────────────────────────────────────────────────\n\nlet _bridgeOptions: OtelBridgeOptions | null = null;\n\n/**\n * Initialise the global OTel bridge.\n *\n * Once called, logixia's internal log pipeline checks for an active OTel span\n * on **every** log call and automatically merges the span context into the\n * entry's metadata — no per-call wiring needed.\n *\n * Call once at app startup, **after** the OTel SDK has been initialised:\n * ```ts\n * import { initOtelBridge } from 'logixia';\n * initOtelBridge();\n * ```\n */\nexport function initOtelBridge(opts: OtelBridgeOptions = {}): void {\n _bridgeOptions = opts;\n}\n\n/**\n * @internal\n * True when initOtelBridge() has been called. Lets the logger hot path skip the\n * `_getOtelPayloadIfEnabled()` call (and its `{}` allocation + Object.keys) on\n * every log when the bridge is off — the overwhelmingly common case.\n */\nexport function _isOtelBridgeEnabled(): boolean {\n return _bridgeOptions !== null;\n}\n\n/**\n * @internal\n * Used by the core logger to inject OTel context when the bridge is active.\n * Returns `{}` when the bridge is not initialised or no active span exists.\n */\nexport function _getOtelPayloadIfEnabled(): Record<string, unknown> {\n if (!_bridgeOptions) return {};\n return getOtelMetaFields(_bridgeOptions);\n}\n\n/**\n * Disable the OTel bridge (useful for tests).\n */\nexport function disableOtelBridge(): void {\n _bridgeOptions = null;\n}\n","/**\n * Internal logging helpers for use within the logixia library itself.\n *\n * These functions write directly to stderr/stdout without going through\n * LogixiaLogger — this avoids circular dependencies and ensures internal\n * diagnostics still surface even when the logger is misconfigured.\n *\n * Library code must NEVER call console.log/warn/error directly. Use these\n * helpers instead so that internal output can be silenced in tests by setting\n * the LOGIXIA_SILENT_INTERNAL=1 environment variable.\n */\n\n/**\n * Read the silence flag on each call rather than caching it at import time, so\n * setting LOGIXIA_SILENT_INTERNAL=1 AFTER the module is first imported (e.g. in\n * a test setup file) still takes effect — the documented test-silencing\n * behavior was previously unreliable because the value was frozen at load.\n */\nfunction isSilent(): boolean {\n return process.env.LOGIXIA_SILENT_INTERNAL === '1';\n}\n\n/**\n * Emit an internal debug/info message to stderr.\n * Use for field-enable/disable notifications that a developer might want to see\n * during development but should not appear in production log streams.\n */\nexport function internalLog(message: string): void {\n if (!isSilent()) {\n process.stderr.write(`[logixia] ${message}\\n`);\n }\n}\n\n/**\n * Emit an internal warning to stderr.\n * Use when something is misconfigured but logixia can continue operating.\n */\nexport function internalWarn(message: string): void {\n if (!isSilent()) {\n process.stderr.write(`[logixia:warn] ${message}\\n`);\n }\n}\n\n/**\n * Emit an internal error to stderr.\n * Use when a transport write fails or a serious internal error occurs.\n */\nexport function internalError(message: string, error?: unknown): void {\n if (!isSilent()) {\n let errStr = '';\n if (error instanceof Error) {\n errStr = ` — ${error.message}`;\n } else if (error != null) {\n errStr = ` — ${String(error)}`;\n }\n process.stderr.write(`[logixia:error] ${message}${errStr}\\n`);\n }\n}\n","/**\n * logixia — TypeScript typed log fields\n *\n * Solves the type-safety gap: standard `Record<string, unknown>` metadata gives\n * zero IDE autocomplete and lets typos slip silently into production logs.\n *\n * Two complementary utilities:\n *\n * 1. `createTypedLogger<TFields>()` — wraps any IBaseLogger so that the second\n * argument of every log method is constrained to `TFields` (a typed object).\n * Compile-time safety, zero runtime overhead.\n *\n * 2. `defineLogSchema<TFields>(schema)` — declares expected fields with optional\n * validators. In development (NODE_ENV !== 'production') every log call is\n * validated against the schema and a warning is emitted for missing required\n * fields or type mismatches.\n *\n * @example\n * ```ts\n * import { createTypedLogger, defineLogSchema } from 'logixia';\n *\n * interface OrderFields {\n * orderId: string;\n * userId: string;\n * amount?: number;\n * currency?: string;\n * }\n *\n * const schema = defineLogSchema<OrderFields>({\n * orderId: { type: 'string', required: true },\n * userId: { type: 'string', required: true },\n * amount: { type: 'number' },\n * currency: { type: 'string' },\n * });\n *\n * const orderLogger = createTypedLogger<OrderFields>(baseLogger, schema);\n * await orderLogger.info('Order created', { orderId: 'ord_123', userId: 'usr_456', amount: 99.99 });\n * // ^^^^ fully typed autocomplete ^^^^\n * ```\n */\n\nimport { internalWarn } from './internal-log';\n\n// ── Schema types ─────────────────────────────────────────────────────────────\n\nexport type LogFieldType = 'string' | 'number' | 'boolean' | 'object' | 'array';\n\nexport interface LogFieldDef {\n type: LogFieldType;\n /** Emit a warning when this field is missing from the log call. Default: false */\n required?: boolean;\n /** Custom validator — return a string to emit it as a warning message. */\n validate?: (value: unknown) => string | undefined;\n}\n\nexport type LogSchema<TFields extends Record<string, unknown>> = {\n [K in keyof TFields]: LogFieldDef;\n};\n\nexport interface CompiledSchema<TFields extends Record<string, unknown>> {\n readonly fields: LogSchema<TFields>;\n /**\n * Validate a payload against the schema.\n * Returns an array of warning strings (empty = pass).\n * Only runs in non-production environments.\n */\n validate(payload: Partial<TFields>): string[];\n}\n\n// ── defineLogSchema ──────────────────────────────────────────────────────────\n\n/**\n * Define a typed schema for a category of log entries.\n *\n * Call this once at module initialisation and pass it to `createTypedLogger`.\n * In development, every log call is validated against the schema.\n */\nexport function defineLogSchema<TFields extends Record<string, unknown>>(\n fields: LogSchema<TFields>\n): CompiledSchema<TFields> {\n return {\n fields,\n validate(payload: Partial<TFields>): string[] {\n if (process.env['NODE_ENV'] === 'production') return [];\n\n const warnings: string[] = [];\n\n for (const [key, def] of Object.entries(fields) as [keyof TFields & string, LogFieldDef][]) {\n const value = payload[key];\n\n if (def.required && (value === undefined || value === null)) {\n warnings.push(`Required field \"${key}\" is missing`);\n continue;\n }\n\n if (value !== undefined && value !== null) {\n const actualType = Array.isArray(value) ? 'array' : typeof value;\n if (actualType !== def.type) {\n warnings.push(`Field \"${key}\" expected type \"${def.type}\" but got \"${actualType}\"`);\n }\n\n if (def.validate) {\n const msg = def.validate(value);\n if (msg) warnings.push(`Field \"${key}\": ${msg}`);\n }\n }\n }\n\n return warnings;\n },\n };\n}\n\n// ── TypedLogger ──────────────────────────────────────────────────────────────\n\n/** IBaseLogger-compatible subset used by TypedLogger */\nexport interface LoggerLike {\n error(message: string | Error, data?: Record<string, unknown>): Promise<void>;\n warn(message: string, data?: Record<string, unknown>): Promise<void>;\n info(message: string, data?: Record<string, unknown>): Promise<void>;\n debug(message: string, data?: Record<string, unknown>): Promise<void>;\n verbose?(message: string, data?: Record<string, unknown>): Promise<void>;\n trace?(message: string, data?: Record<string, unknown>): Promise<void>;\n}\n\n/**\n * A logger whose metadata is typed to `TFields`.\n *\n * The `error` overload still accepts a plain `Error` object as the first arg\n * (with optional `TFields` data) so the typed logger remains a drop-in replacement.\n */\nexport interface TypedLogger<TFields extends Record<string, unknown>> {\n error(error: Error, data?: Partial<TFields>): Promise<void>;\n error(message: string, data?: Partial<TFields>): Promise<void>;\n warn(message: string, data?: Partial<TFields>): Promise<void>;\n info(message: string, data?: Partial<TFields>): Promise<void>;\n debug(message: string, data?: Partial<TFields>): Promise<void>;\n verbose(message: string, data?: Partial<TFields>): Promise<void>;\n trace(message: string, data?: Partial<TFields>): Promise<void>;\n /** Access the underlying untyped logger if needed. */\n readonly raw: LoggerLike;\n}\n\n/**\n * Wrap any logixia logger with a type-safe field interface.\n *\n * @param logger Any object that implements `IBaseLogger` (e.g. the result of `createLogger()`)\n * @param schema Optional schema for dev-time validation. Created with `defineLogSchema()`.\n */\nexport function createTypedLogger<TFields extends Record<string, unknown>>(\n logger: LoggerLike,\n schema?: CompiledSchema<TFields>\n): TypedLogger<TFields> {\n function withValidation(\n level: string,\n fn: (msg: string, data?: Record<string, unknown>) => Promise<void>,\n message: string,\n data?: Partial<TFields>\n ): Promise<void> {\n if (schema && data) {\n const warnings = schema.validate(data);\n for (const w of warnings) {\n internalWarn(`[logixia/schema] ${w} — level=${level} message=\"${message}\"`);\n }\n }\n return fn(message, data as Record<string, unknown> | undefined);\n }\n\n return {\n raw: logger,\n error(messageOrError: string | Error, data?: Partial<TFields>): Promise<void> {\n if (schema && data) {\n const warnings = schema.validate(data);\n const display = messageOrError instanceof Error ? messageOrError.message : messageOrError;\n for (const w of warnings) {\n internalWarn(`[logixia/schema] ${w} — level=error message=\"${display}\"`);\n }\n }\n return logger.error(messageOrError, data as Record<string, unknown> | undefined);\n },\n warn: (m, d) => withValidation('warn', logger.warn.bind(logger), m, d),\n info: (m, d) => withValidation('info', logger.info.bind(logger), m, d),\n debug: (m, d) => withValidation('debug', logger.debug.bind(logger), m, d),\n verbose: (m, d) =>\n withValidation('verbose', (logger.verbose ?? logger.debug).bind(logger), m, d),\n trace: (m, d) => withValidation('trace', (logger.trace ?? logger.debug).bind(logger), m, d),\n };\n}\n","/**\n * logixia — Browser / Edge Runtime entry point\n *\n * This module is the browser-safe subset of logixia. It contains **no**\n * references to Node.js built-ins (`node:fs`, `node:http`, `node:worker_threads`,\n * `node:crypto`, `node:async_hooks`) so it can be bundled for:\n *\n * - Browser (Webpack / Vite / esbuild)\n * - Cloudflare Workers / Vercel Edge Functions\n * - Deno (via ESM import)\n * - Bun (core subset, no file/DB transports)\n *\n * Excluded from this entry: FileTransport, DatabaseTransport, WorkerTransport,\n * CloudWatchTransport, GCPTransport, AzureMonitorTransport, and anything that\n * requires `AsyncLocalStorage`.\n *\n * @example Basic browser usage\n * ```ts\n * import { createBrowserLogger, BrowserConsoleTransport } from 'logixia/browser';\n *\n * const logger = createBrowserLogger({ appName: 'my-app' });\n * logger.info('Page loaded', { url: location.href });\n * ```\n *\n * @example Custom transport in browser\n * ```ts\n * import { createBrowserLogger, BrowserConsoleTransport } from 'logixia/browser';\n *\n * const logger = createBrowserLogger({\n * appName: 'my-app',\n * json: false, // pretty-print in devtools\n * level: 'debug',\n * });\n * ```\n */\n\nimport { stringifyForLog } from './utils/safe-stringify';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type BrowserLogLevel = 'verbose' | 'debug' | 'info' | 'warn' | 'error' | 'critical';\n\nexport interface BrowserLogEntry {\n timestamp: string;\n level: BrowserLogLevel;\n appName: string;\n message: string;\n payload?: Record<string, unknown>;\n traceId?: string;\n context?: string;\n}\n\nexport interface BrowserLoggerConfig {\n /** Application name shown in every log entry. @default 'App' */\n appName?: string;\n /** Minimum log level to emit. @default 'info' */\n level?: BrowserLogLevel;\n /**\n * When `true` serializes entries as compact JSON strings — useful for\n * structured logging pipelines (Sentry breadcrumbs, LogRocket, etc.).\n * When `false` (default) uses console group/table for devtools-friendly output.\n */\n json?: boolean;\n /**\n * Additional transports to write to (e.g. a remote analytics endpoint).\n * The built-in `BrowserConsoleTransport` is always added unless `silent: true`.\n */\n transports?: IBrowserTransport[];\n /** Suppress all output (useful for tests). @default false */\n silent?: boolean;\n /** Static context fields merged into every log entry. */\n context?: Record<string, unknown>;\n}\n\nexport interface IBrowserTransport {\n name: string;\n level?: BrowserLogLevel;\n write(entry: BrowserLogEntry): void;\n}\n\n// ── Level ordering ────────────────────────────────────────────────────────────\n\nconst LEVEL_ORDER: Record<BrowserLogLevel, number> = {\n verbose: 0,\n debug: 1,\n info: 2,\n warn: 3,\n error: 4,\n critical: 5,\n};\n\nfunction shouldLog(entryLevel: BrowserLogLevel, minLevel: BrowserLogLevel): boolean {\n return LEVEL_ORDER[entryLevel] >= LEVEL_ORDER[minLevel];\n}\n\n// ── Browser Console Transport ─────────────────────────────────────────────────\n\n/**\n * Default browser transport — writes to the browser console using native\n * level methods so devtools can filter by severity.\n *\n * In pretty mode (default):\n * - Groups each entry under a collapsible header with timestamp + app name\n * - Payload displayed as an expandable object via `console.table` / `console.dir`\n *\n * In JSON mode:\n * - Emits compact JSON strings via `console.log` / `console.error`\n */\nexport class BrowserConsoleTransport implements IBrowserTransport {\n public readonly name = 'browser-console';\n public readonly level?: BrowserLogLevel;\n\n private readonly json: boolean;\n\n constructor(options: { json?: boolean; level?: BrowserLogLevel } = {}) {\n this.json = options.json ?? false;\n if (options.level !== undefined) this.level = options.level;\n }\n\n write(entry: BrowserLogEntry): void {\n if (this.json) {\n this.writeJson(entry);\n } else {\n this.writePretty(entry);\n }\n }\n\n /* eslint-disable no-console */\n private writeJson(entry: BrowserLogEntry): void {\n const line = stringifyForLog(entry);\n switch (entry.level) {\n case 'verbose':\n case 'debug':\n console.debug(line);\n break;\n case 'warn':\n console.warn(line);\n break;\n case 'error':\n case 'critical':\n console.error(line);\n break;\n default:\n console.log(line);\n }\n }\n\n private writePretty(entry: BrowserLogEntry): void {\n const { timestamp, level, appName, message, payload, context } = entry;\n\n const ts = timestamp.slice(11, 23); // HH:MM:SS.mmm\n const contextLabel = context ? ` [${context}]` : '';\n const prefix = `[${appName}]${contextLabel}`;\n const levelLabel = level.toUpperCase().padEnd(8);\n\n const header = `%c${ts} %c${levelLabel} %c${prefix} ${message}`;\n const tsStyle = 'color: #888; font-size: 11px;';\n const levelStyle = this.levelStyle(level);\n const msgStyle = 'color: inherit; font-weight: normal;';\n\n const hasPayload = payload && Object.keys(payload).length > 0;\n\n if (hasPayload) {\n console.groupCollapsed(header, tsStyle, levelStyle, msgStyle);\n console.dir(payload);\n console.groupEnd();\n } else {\n switch (level) {\n case 'verbose':\n case 'debug':\n console.debug(header, tsStyle, levelStyle, msgStyle);\n break;\n case 'warn':\n console.warn(header, tsStyle, levelStyle, msgStyle);\n break;\n case 'error':\n case 'critical':\n console.error(header, tsStyle, levelStyle, msgStyle);\n break;\n default:\n console.log(header, tsStyle, levelStyle, msgStyle);\n }\n }\n }\n /* eslint-enable no-console */\n\n private levelStyle(level: BrowserLogLevel): string {\n const styles: Record<BrowserLogLevel, string> = {\n verbose: 'color: #aaa; font-weight: bold;',\n debug: 'color: #7ec8e3; font-weight: bold;',\n info: 'color: #4caf50; font-weight: bold;',\n warn: 'color: #ff9800; font-weight: bold;',\n error: 'color: #f44336; font-weight: bold;',\n critical: 'color: #fff; background: #f44336; font-weight: bold; padding: 1px 4px;',\n };\n return styles[level] ?? 'color: inherit; font-weight: bold;';\n }\n}\n\n// ── Remote Batch Transport ────────────────────────────────────────────────────\n\nfunction validateRemoteUrl(raw: string): string {\n if (typeof raw !== 'string' || raw.length === 0) {\n throw new TypeError('BrowserRemoteTransport: `url` must be a non-empty string');\n }\n // Allow same-origin relative paths (e.g. \"/api/logs\") — these are safe.\n if (raw.startsWith('/') && !raw.startsWith('//')) return raw;\n let parsed: URL;\n try {\n parsed = new URL(raw);\n } catch {\n throw new TypeError(`BrowserRemoteTransport: invalid url \"${raw}\"`);\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new TypeError(\n `BrowserRemoteTransport: url scheme \"${parsed.protocol}\" is not allowed; use http: or https:`\n );\n }\n return raw;\n}\n\n/**\n * UTF-8 byte length of `s` — the measure browsers use for the keepalive\n * body-size limit. `TextEncoder` is a standard Web API (browsers, workers,\n * Node >= 11); it needs no Node import.\n */\nfunction byteLength(s: string): number {\n return new TextEncoder().encode(s).length;\n}\n\n/**\n * Browser transport that batches log entries and sends them to a remote\n * HTTP endpoint via `fetch` (available in all modern browsers and Edge runtimes).\n *\n * @example\n * ```ts\n * const logger = createBrowserLogger({\n * transports: [\n * new BrowserRemoteTransport({\n * url: '/api/logs',\n * batchSize: 50,\n * flushIntervalMs: 10_000,\n * }),\n * ],\n * });\n * ```\n */\nexport class BrowserRemoteTransport implements IBrowserTransport {\n public readonly name = 'browser-remote';\n public readonly level?: BrowserLogLevel;\n\n private readonly url: string;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n private readonly maxBufferSize: number;\n private readonly headers: Record<string, string>;\n\n private batch: BrowserLogEntry[] = [];\n private timer: ReturnType<typeof setTimeout> | null = null;\n private droppedEntries = 0;\n\n constructor(options: {\n /** Endpoint URL to POST batched log entries to. */\n url: string;\n /** Headers added to each request (e.g. `Authorization`). @default {} */\n headers?: Record<string, string>;\n /** Max entries per batch. @default 100 */\n batchSize?: number;\n /**\n * Auto-flush interval in milliseconds. @default 5000\n * Set to `0` to disable the timer — flushing is then manual (`flush()`)\n * or size-triggered (when the batch reaches `batchSize`).\n */\n flushIntervalMs?: number;\n /**\n * Upper bound on buffered entries. When the buffer exceeds this, the\n * oldest entries are dropped and counted in `dropped`. @default 1000\n */\n maxBufferSize?: number;\n level?: BrowserLogLevel;\n }) {\n this.url = validateRemoteUrl(options.url);\n this.headers = options.headers ?? {};\n this.batchSize = options.batchSize ?? 100;\n this.flushIntervalMs = options.flushIntervalMs ?? 5000;\n this.maxBufferSize = options.maxBufferSize ?? 1000;\n if (options.level !== undefined) this.level = options.level;\n this.scheduleFlush();\n }\n\n /**\n * Number of entries dropped without delivery — either because the buffer\n * exceeded `maxBufferSize` (oldest first) or because a POST failed with a\n * non-retryable HTTP status (a 4xx other than 429).\n */\n get dropped(): number {\n return this.droppedEntries;\n }\n\n write(entry: BrowserLogEntry): void {\n this.batch.push(entry);\n // Enforce the buffer cap so a down endpoint cannot grow the buffer\n // without bound in the user's tab.\n while (this.batch.length > this.maxBufferSize) {\n this.batch.shift();\n this.droppedEntries += 1;\n }\n if (this.batch.length >= this.batchSize) {\n this.flush();\n }\n }\n\n async flush(): Promise<void> {\n // Drain the WHOLE batch (not just one chunk) so a flush during page unload\n // empties everything. splice() detaches synchronously, so entries added\n // concurrently land in a fresh slice and are never sent twice.\n while (this.batch.length > 0) {\n const entries = this.batch.splice(0, this.batchSize);\n const body = stringifyForLog(entries);\n // keepalive only survives bodies under 64 KB; a large batch would\n // otherwise throw on every attempt and be retried forever.\n const keepalive = byteLength(body) < 60_000;\n let res: Response;\n try {\n res = await fetch(this.url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...this.headers },\n body,\n keepalive,\n });\n } catch {\n // Network failure: restore and stop — best effort in browser.\n this.batch.unshift(...entries);\n return;\n }\n if (!res.ok) {\n if (res.status >= 500 || res.status === 429) {\n // Transient server error / rate limit — retry on a later flush.\n this.batch.unshift(...entries);\n } else {\n // Permanent client error — retrying won't help, drop the batch.\n this.droppedEntries += entries.length;\n }\n return;\n }\n }\n }\n\n private scheduleFlush(): void {\n if (this.flushIntervalMs <= 0) return; // timer disabled — manual/size-triggered only\n if (\n typeof globalThis !== 'undefined' &&\n typeof (globalThis as Record<string, unknown>)['setInterval'] === 'function'\n ) {\n this.timer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushIntervalMs);\n }\n }\n\n /**\n * Stop the flush timer and flush remaining entries. Without the final flush,\n * any buffered logs were lost on page unload / teardown — the browser\n * equivalent of the \"last N seconds of logs lost on deploy\" problem.\n */\n destroy(): void {\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.flush().catch(() => {});\n }\n}\n\n// ── Core Browser Logger ───────────────────────────────────────────────────────\n\n/**\n * Lightweight browser-safe logger.\n *\n * Unlike the Node.js `LogixiaLogger`, this implementation has **zero**\n * Node.js dependencies. It can be bundled for any runtime.\n */\nexport class BrowserLogger {\n private readonly config: Required<\n Pick<BrowserLoggerConfig, 'appName' | 'level' | 'silent' | 'json'>\n > & {\n context: Record<string, unknown> | undefined;\n };\n private readonly transports: IBrowserTransport[];\n\n constructor(config: BrowserLoggerConfig = {}) {\n this.config = {\n appName: config.appName ?? 'App',\n level: config.level ?? 'info',\n silent: config.silent ?? false,\n json: config.json ?? false,\n context: config.context,\n };\n\n if (config.silent) {\n this.transports = [];\n } else {\n const builtIn = new BrowserConsoleTransport({ json: this.config.json });\n this.transports = [builtIn, ...(config.transports ?? [])];\n }\n }\n\n // ── Logging methods ───────────────────────────────────────────────────────\n\n verbose(message: string, payload?: Record<string, unknown>): void {\n this.emit('verbose', message, payload);\n }\n\n debug(message: string, payload?: Record<string, unknown>): void {\n this.emit('debug', message, payload);\n }\n\n info(message: string, payload?: Record<string, unknown>): void {\n this.emit('info', message, payload);\n }\n\n log(message: string, payload?: Record<string, unknown>): void {\n this.emit('info', message, payload);\n }\n\n warn(message: string, payload?: Record<string, unknown>): void {\n this.emit('warn', message, payload);\n }\n\n error(message: string, payload?: Record<string, unknown>): void {\n this.emit('error', message, payload);\n }\n\n critical(message: string, payload?: Record<string, unknown>): void {\n this.emit('critical', message, payload);\n }\n\n /**\n * Create a child logger that inherits config and merges extra context fields\n * into every emitted entry.\n *\n * @example\n * ```ts\n * const childLogger = logger.child({ context: 'AuthService', userId: '42' });\n * childLogger.info('Login succeeded'); // → { context: 'AuthService', userId: '42', ... }\n * ```\n */\n child(extraContext: Record<string, unknown>): BrowserLogger {\n // Every key, including `context`, is kept and merged over the parent's\n // context, as the example above shows.\n return new BrowserLogger({\n appName: this.config.appName,\n level: this.config.level,\n silent: this.config.silent,\n json: this.config.json,\n context: { ...this.config.context, ...extraContext },\n // Reuse existing transport list\n transports: this.transports.filter((t) => t.name !== 'browser-console'),\n });\n }\n\n // ── Internal ──────────────────────────────────────────────────────────────\n\n private emit(level: BrowserLogLevel, message: string, payload?: Record<string, unknown>): void {\n if (!shouldLog(level, this.config.level)) return;\n\n const entry: BrowserLogEntry = {\n timestamp: new Date().toISOString(),\n level,\n appName: this.config.appName,\n message,\n ...(payload || this.config.context\n ? { payload: { ...this.config.context, ...payload } }\n : {}),\n };\n\n for (const transport of this.transports) {\n if (transport.level && !shouldLog(level, transport.level)) continue;\n transport.write(entry);\n }\n }\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * Create a browser-safe logixia logger instance.\n *\n * @example\n * ```ts\n * import { createBrowserLogger } from 'logixia/browser';\n *\n * const logger = createBrowserLogger({ appName: 'checkout', level: 'debug' });\n * logger.info('Cart updated', { itemCount: 3, total: 49.99 });\n * ```\n */\nexport function createBrowserLogger(config?: BrowserLoggerConfig): BrowserLogger {\n return new BrowserLogger(config);\n}\n\n// ── Re-exports of browser-safe utilities ─────────────────────────────────────\n\n// OTel utilities are browser-safe (dynamic require with try/catch)\nexport type { OtelBridgeOptions, OtelSpanContext } from './utils/otel';\nexport {\n disableOtelBridge,\n getActiveOtelContext,\n getOtelMetaFields,\n initOtelBridge,\n} from './utils/otel';\n\n// Typed logger utilities are pure TypeScript, no runtime deps\nexport type {\n CompiledSchema,\n LogFieldDef,\n LogFieldType,\n LoggerLike,\n LogSchema,\n TypedLogger,\n} from './utils/typed-logger';\nexport { createTypedLogger, defineLogSchema } from './utils/typed-logger';\n\n/**\n * Default browser logger instance (singleton, console transport, info level).\n *\n * @example\n * ```ts\n * import { browserLogger } from 'logixia/browser';\n * browserLogger.info('Hello from browser');\n * ```\n */\nexport const browserLogger = new BrowserLogger({ appName: 'App' });\n"],"mappings":"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"}