logixia 2.2.0 → 2.3.1

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 (150) hide show
  1. package/dist/{async-context-CoPhbRG0.js → async-context-BqjwNzgS.js} +2 -2
  2. package/dist/{async-context-CoPhbRG0.js.map → async-context-BqjwNzgS.js.map} +1 -1
  3. package/dist/{async-context-1YMPGgJZ.mjs → async-context-DJdxbbCU.mjs} +2 -2
  4. package/dist/{async-context-1YMPGgJZ.mjs.map → async-context-DJdxbbCU.mjs.map} +1 -1
  5. package/dist/{azure-monitor.transport-C5VjvREZ.d.mts → azure-monitor.transport-BZDaQjy8.d.mts} +2 -2
  6. package/dist/{azure-monitor.transport-C5VjvREZ.d.mts.map → azure-monitor.transport-BZDaQjy8.d.mts.map} +1 -1
  7. package/dist/{azure-monitor.transport-B_dqSV_o.mjs → azure-monitor.transport-DNccn1vp.mjs} +3 -3
  8. package/dist/{azure-monitor.transport-B_dqSV_o.mjs.map → azure-monitor.transport-DNccn1vp.mjs.map} +1 -1
  9. package/dist/{azure-monitor.transport-Bl3xVvpN.js → azure-monitor.transport-DTIgEZwg.js} +3 -3
  10. package/dist/{azure-monitor.transport-Bl3xVvpN.js.map → azure-monitor.transport-DTIgEZwg.js.map} +1 -1
  11. package/dist/{azure-monitor.transport-CyHDjHS-.d.ts → azure-monitor.transport-MDigkKsS.d.ts} +2 -2
  12. package/dist/{azure-monitor.transport-CyHDjHS-.d.ts.map → azure-monitor.transport-MDigkKsS.d.ts.map} +1 -1
  13. package/dist/azure.d.mts +1 -1
  14. package/dist/azure.d.ts +1 -1
  15. package/dist/azure.js +1 -1
  16. package/dist/azure.mjs +1 -1
  17. package/dist/{batch-buffer-C-ETEX3Y.js → batch-buffer-C6EmQcD5.js} +2 -2
  18. package/dist/{batch-buffer-C-ETEX3Y.js.map → batch-buffer-C6EmQcD5.js.map} +1 -1
  19. package/dist/{batch-buffer-BnmVCCwI.mjs → batch-buffer-CcubZ2cW.mjs} +2 -2
  20. package/dist/{batch-buffer-BnmVCCwI.mjs.map → batch-buffer-CcubZ2cW.mjs.map} +1 -1
  21. package/dist/browser.js +3 -2
  22. package/dist/browser.js.map +1 -1
  23. package/dist/browser.mjs +3 -2
  24. package/dist/browser.mjs.map +1 -1
  25. package/dist/{builder-CbttbixM.mjs → builder-CcRxxVwU.mjs} +132 -76
  26. package/dist/builder-CcRxxVwU.mjs.map +1 -0
  27. package/dist/{builder-Ck1tsC5d.js → builder-Dm8M1WXD.js} +132 -76
  28. package/dist/builder-Dm8M1WXD.js.map +1 -0
  29. package/dist/cloudwatch.d.mts +1 -1
  30. package/dist/cloudwatch.d.ts +1 -1
  31. package/dist/cloudwatch.js +1 -1
  32. package/dist/cloudwatch.mjs +1 -1
  33. package/dist/{cloudwatch.transport-_hS7Mcc5.d.mts → cloudwatch.transport-CqXAGHPH.d.mts} +2 -2
  34. package/dist/{cloudwatch.transport-_hS7Mcc5.d.mts.map → cloudwatch.transport-CqXAGHPH.d.mts.map} +1 -1
  35. package/dist/{cloudwatch.transport-DO9e__Z-.js → cloudwatch.transport-DCTWV9p6.js} +3 -3
  36. package/dist/{cloudwatch.transport-DO9e__Z-.js.map → cloudwatch.transport-DCTWV9p6.js.map} +1 -1
  37. package/dist/{cloudwatch.transport-DdSjUWvu.d.ts → cloudwatch.transport-pY96iAfO.d.ts} +2 -2
  38. package/dist/{cloudwatch.transport-DdSjUWvu.d.ts.map → cloudwatch.transport-pY96iAfO.d.ts.map} +1 -1
  39. package/dist/{cloudwatch.transport-Brh5fmRN.mjs → cloudwatch.transport-wWrht2Gv.mjs} +3 -3
  40. package/dist/{cloudwatch.transport-Brh5fmRN.mjs.map → cloudwatch.transport-wWrht2Gv.mjs.map} +1 -1
  41. package/dist/correlation.js +2 -2
  42. package/dist/correlation.mjs +2 -2
  43. package/dist/fastify.d.mts +1 -1
  44. package/dist/fastify.d.ts +1 -1
  45. package/dist/fastify.js +1 -1
  46. package/dist/fastify.mjs +1 -1
  47. package/dist/gcp.d.mts +1 -1
  48. package/dist/gcp.d.ts +1 -1
  49. package/dist/gcp.js +1 -1
  50. package/dist/gcp.mjs +1 -1
  51. package/dist/{gcp.transport-C8VJiEM7.d.mts → gcp.transport-BFlvl8uy.d.mts} +2 -2
  52. package/dist/{gcp.transport-C8VJiEM7.d.mts.map → gcp.transport-BFlvl8uy.d.mts.map} +1 -1
  53. package/dist/{gcp.transport-Ct0BtKFo.d.ts → gcp.transport-Bo60F-_3.d.ts} +2 -2
  54. package/dist/{gcp.transport-Ct0BtKFo.d.ts.map → gcp.transport-Bo60F-_3.d.ts.map} +1 -1
  55. package/dist/{gcp.transport-BY1Z3cS0.mjs → gcp.transport-BqzWORwO.mjs} +3 -3
  56. package/dist/{gcp.transport-BY1Z3cS0.mjs.map → gcp.transport-BqzWORwO.mjs.map} +1 -1
  57. package/dist/{gcp.transport-V9R4dwdT.js → gcp.transport-BvKR7MIz.js} +3 -3
  58. package/dist/{gcp.transport-V9R4dwdT.js.map → gcp.transport-BvKR7MIz.js.map} +1 -1
  59. package/dist/hono.js +1 -1
  60. package/dist/hono.js.map +1 -1
  61. package/dist/hono.mjs +1 -1
  62. package/dist/hono.mjs.map +1 -1
  63. package/dist/{index-B3ajtXJd.d.mts → index-0X_XpQ_f.d.mts} +2 -2
  64. package/dist/{index-B3ajtXJd.d.mts.map → index-0X_XpQ_f.d.mts.map} +1 -1
  65. package/dist/{index-oqD3WNxf.d.ts → index-BnbqeNl6.d.ts} +2 -2
  66. package/dist/{index-oqD3WNxf.d.ts.map → index-BnbqeNl6.d.ts.map} +1 -1
  67. package/dist/index.d.mts +5 -5
  68. package/dist/index.d.ts +5 -5
  69. package/dist/index.js +5 -5
  70. package/dist/index.js.map +1 -1
  71. package/dist/index.mjs +5 -5
  72. package/dist/index.mjs.map +1 -1
  73. package/dist/{logitron-logger-fMOkXr8H.d.mts → logixia-logger-DK6oHsmN.d.mts} +12 -4
  74. package/dist/logixia-logger-DK6oHsmN.d.mts.map +1 -0
  75. package/dist/{logitron-logger-Csn3e6Qu.d.ts → logixia-logger-DSh5ZUEz.d.ts} +12 -4
  76. package/dist/logixia-logger-DSh5ZUEz.d.ts.map +1 -0
  77. package/dist/middleware.d.mts +1 -1
  78. package/dist/middleware.d.ts +1 -1
  79. package/dist/middleware.js +1 -1
  80. package/dist/middleware.mjs +1 -1
  81. package/dist/nest.d.mts +2 -2
  82. package/dist/nest.d.mts.map +1 -1
  83. package/dist/nest.d.ts +2 -2
  84. package/dist/nest.d.ts.map +1 -1
  85. package/dist/nest.js +5 -5
  86. package/dist/nest.js.map +1 -1
  87. package/dist/nest.mjs +5 -5
  88. package/dist/nest.mjs.map +1 -1
  89. package/dist/{otel-BrgvxLDW.js → otel-BAzbTiJw.js} +5 -7
  90. package/dist/otel-BAzbTiJw.js.map +1 -0
  91. package/dist/{otel-C5jBV_l_.mjs → otel-DWVTrsgM.mjs} +5 -7
  92. package/dist/otel-DWVTrsgM.mjs.map +1 -0
  93. package/dist/{request-trace-txUhyyre.mjs → request-trace-B-S5-LVk.mjs} +2 -2
  94. package/dist/request-trace-B-S5-LVk.mjs.map +1 -0
  95. package/dist/{request-trace-6NBSw5lt.js → request-trace-BqU7ARjg.js} +2 -2
  96. package/dist/request-trace-BqU7ARjg.js.map +1 -0
  97. package/dist/{search-BlKBLm5D.d.ts → search-B9fKizkh.d.ts} +2 -2
  98. package/dist/{search-BlKBLm5D.d.ts.map → search-B9fKizkh.d.ts.map} +1 -1
  99. package/dist/{search-B-hJe2N-.d.mts → search-CdTwNSQm.d.mts} +2 -2
  100. package/dist/{search-B-hJe2N-.d.mts.map → search-CdTwNSQm.d.mts.map} +1 -1
  101. package/dist/search.d.mts +1 -1
  102. package/dist/search.d.ts +1 -1
  103. package/dist/{secret-config-Cng7XPLx.mjs → secret-config-BFKJrSYF.mjs} +13 -4
  104. package/dist/secret-config-BFKJrSYF.mjs.map +1 -0
  105. package/dist/{secret-config-OqmIPmpV.js → secret-config-DB84lWy4.js} +18 -3
  106. package/dist/secret-config-DB84lWy4.js.map +1 -0
  107. package/dist/{slack.transport-Ci0swStG.js → slack.transport-BA97gQ8V.js} +4 -4
  108. package/dist/{slack.transport-Ci0swStG.js.map → slack.transport-BA97gQ8V.js.map} +1 -1
  109. package/dist/{slack.transport-C6dzPJHX.mjs → slack.transport-CrRlsrs5.mjs} +4 -4
  110. package/dist/{slack.transport-C6dzPJHX.mjs.map → slack.transport-CrRlsrs5.mjs.map} +1 -1
  111. package/dist/{slack.transport-5OPL7H3u.d.mts → slack.transport-DAGuMvm7.d.mts} +2 -2
  112. package/dist/{slack.transport-5OPL7H3u.d.mts.map → slack.transport-DAGuMvm7.d.mts.map} +1 -1
  113. package/dist/{slack.transport-BFpBD_Ri.d.ts → slack.transport-LvYsOoFZ.d.ts} +2 -2
  114. package/dist/{slack.transport-BFpBD_Ri.d.ts.map → slack.transport-LvYsOoFZ.d.ts.map} +1 -1
  115. package/dist/testing.d.mts +1 -1
  116. package/dist/testing.d.ts +1 -1
  117. package/dist/{transport.types-B1ejJJP6.d.mts → transport.types-MjMDjOQ7.d.mts} +6 -1
  118. package/dist/{transport.types-B1ejJJP6.d.mts.map → transport.types-MjMDjOQ7.d.mts.map} +1 -1
  119. package/dist/{transport.types-B1ejJJP6.d.ts → transport.types-MjMDjOQ7.d.ts} +6 -1
  120. package/dist/{transport.types-B1ejJJP6.d.ts.map → transport.types-MjMDjOQ7.d.ts.map} +1 -1
  121. package/dist/transports.d.mts +8 -10
  122. package/dist/transports.d.mts.map +1 -1
  123. package/dist/transports.d.ts +8 -10
  124. package/dist/transports.d.ts.map +1 -1
  125. package/dist/transports.js +6 -6
  126. package/dist/transports.mjs +6 -6
  127. package/dist/worker-entry.js +3 -2
  128. package/dist/worker.d.mts +1 -1
  129. package/dist/worker.d.ts +1 -1
  130. package/dist/worker.js +1 -1
  131. package/dist/worker.mjs +1 -1
  132. package/dist/{worker.transport-CxFD8mvS.mjs → worker.transport-BN0BmxOr.mjs} +2 -2
  133. package/dist/{worker.transport-CxFD8mvS.mjs.map → worker.transport-BN0BmxOr.mjs.map} +1 -1
  134. package/dist/{worker.transport-B7olL1Uh.d.ts → worker.transport-ByBBDx7U.d.ts} +2 -2
  135. package/dist/{worker.transport-B7olL1Uh.d.ts.map → worker.transport-ByBBDx7U.d.ts.map} +1 -1
  136. package/dist/{worker.transport-CZOutlxC.js → worker.transport-CaVYRowu.js} +2 -2
  137. package/dist/{worker.transport-CZOutlxC.js.map → worker.transport-CaVYRowu.js.map} +1 -1
  138. package/dist/{worker.transport-DnU0_9Bd.d.mts → worker.transport-DWyyRVJW.d.mts} +2 -2
  139. package/dist/{worker.transport-DnU0_9Bd.d.mts.map → worker.transport-DWyyRVJW.d.mts.map} +1 -1
  140. package/package.json +1 -1
  141. package/dist/builder-CbttbixM.mjs.map +0 -1
  142. package/dist/builder-Ck1tsC5d.js.map +0 -1
  143. package/dist/logitron-logger-Csn3e6Qu.d.ts.map +0 -1
  144. package/dist/logitron-logger-fMOkXr8H.d.mts.map +0 -1
  145. package/dist/otel-BrgvxLDW.js.map +0 -1
  146. package/dist/otel-C5jBV_l_.mjs.map +0 -1
  147. package/dist/request-trace-6NBSw5lt.js.map +0 -1
  148. package/dist/request-trace-txUhyyre.mjs.map +0 -1
  149. package/dist/secret-config-Cng7XPLx.mjs.map +0 -1
  150. package/dist/secret-config-OqmIPmpV.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  const require_own_property = require("./own-property-BWkibepN.js");
2
- const require_request_trace = require("./request-trace-6NBSw5lt.js");
2
+ const require_request_trace = require("./request-trace-BqU7ARjg.js");
3
3
  // src/context/async-context.ts
4
4
  /**
5
5
  * LogixiaContext — Zero-boilerplate AsyncLocalStorage context propagation.
@@ -145,4 +145,4 @@ Object.defineProperty(exports, "createFastifyContextHook", {
145
145
  }
146
146
  });
147
147
 
148
- //# sourceMappingURL=async-context-CoPhbRG0.js.map
148
+ //# sourceMappingURL=async-context-BqjwNzgS.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"async-context-CoPhbRG0.js","names":["AsyncLocalStorage","TraceContext","resolveRequestTraceId","resolveTraceHeaders"],"sources":["../src/context/async-context.ts"],"sourcesContent":["/**\n * LogixiaContext — Zero-boilerplate AsyncLocalStorage context propagation.\n *\n * Lets every `logger.info()` call inside a context automatically pick up stored\n * fields (requestId, userId, tenantId, …) without passing them through every function.\n *\n * Works across `Promise.all`, `setTimeout`, and event emitters — anything that\n * inherits the async execution context.\n *\n * @example\n * ```ts\n * import { LogixiaContext } from 'logixia';\n *\n * // Wrap a request handler:\n * app.use((req, res, next) => {\n * LogixiaContext.run({ requestId: req.id, userId: req.user?.id }, next);\n * });\n *\n * // Anywhere deeper in the call tree:\n * async function processOrder(orderId: string) {\n * const ctx = LogixiaContext.get(); // { requestId, userId, orderId }\n * logger.info('Processing order', { orderId, ...ctx });\n * }\n * ```\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport { assignOwn } from '../utils/own-property';\nimport {\n type ContextRequestLike,\n resolveRequestTraceId,\n resolveTraceHeaders,\n type TraceHeaderOption,\n} from '../utils/request-trace';\nimport { TraceContext } from '../utils/trace.utils';\n\nexport interface LogContext {\n traceId?: string;\n spanId?: string;\n userId?: string;\n tenantId?: string;\n sessionId?: string;\n [key: string]: unknown;\n}\n\n// Module-level singleton so all loggers share the same ALS store.\nconst _storage = new AsyncLocalStorage<LogContext>();\n\nexport const LogixiaContext = {\n /**\n * Run `callback` inside an async context that carries `store` fields.\n * All loggers queried within `callback` (and its async descendants) will\n * automatically see these fields.\n *\n * @example\n * ```ts\n * LogixiaContext.run({ requestId: 'abc', userId: '42' }, async () => {\n * await processOrder(); // sees requestId + userId in every log\n * });\n * ```\n */\n run<T>(store: LogContext, callback: () => T): T {\n const parent = _storage.getStore() ?? {};\n const scoped = (): T => _storage.run({ ...parent, ...store }, callback);\n const traceId = store.traceId;\n if (typeof traceId !== 'string' || traceId === '') return scoped();\n // An explicit traceId also becomes the TraceContext ID, so getCurrentTraceId(),\n // the logger, outbound propagation and the OTel bridge all see the same value.\n const trace = TraceContext.instance;\n return trace.storage.run({ ...trace.storage.getStore(), [trace.contextKey]: traceId }, scoped);\n },\n\n /**\n * Return the context fields active in the current async scope.\n * Returns `undefined` when called outside any `LogixiaContext.run()`.\n */\n get(): LogContext | undefined {\n return _storage.getStore();\n },\n\n /**\n * Merge `fields` into the **existing** context for the current scope.\n * If called outside a `run()` context this is a no-op (logs a dev warning).\n */\n set(fields: Partial<LogContext>): void {\n const store = _storage.getStore();\n if (!store) {\n // Silently ignore — avoids crashing in code that may call setContext\n // before a request context is established (e.g. background jobs).\n return;\n }\n for (const key of Object.keys(fields)) {\n assignOwn(store, key, fields[key]);\n }\n },\n\n /**\n * Retrieve the underlying `AsyncLocalStorage` instance.\n * Useful for advanced use-cases like custom middleware.\n */\n getStorage(): AsyncLocalStorage<LogContext> {\n return _storage;\n },\n};\n\nexport type { ContextRequestLike };\n\ninterface ContextHelperOptions<Req extends ContextRequestLike> {\n /**\n * Extract additional fields from the request. A `traceId` returned here is ignored:\n * the resolved request trace ID always wins.\n */\n enrich?: (request: Req & Record<string, unknown>) => Partial<LogContext>;\n /**\n * Header name, or list of names tried in order, to read the trace ID from.\n * Default: `DEFAULT_TRACE_HEADERS` (traceparent is parsed to its 32-hex trace id).\n */\n traceIdHeader?: TraceHeaderOption;\n /**\n * Echo the resolved trace ID on the response under this header name.\n * Default: `false` (no response header is set).\n */\n responseHeader?: string | false;\n}\n\nfunction buildScope<Req extends ContextRequestLike>(\n request: Req,\n options: ContextHelperOptions<Req>,\n headers: readonly string[]\n): LogContext {\n const extra = options.enrich ? options.enrich(request as Req & Record<string, unknown>) : {};\n return { ...extra, traceId: resolveRequestTraceId(request, headers) };\n}\n\n/**\n * Create an Express/Connect-compatible middleware that wraps each request in\n * a `LogixiaContext.run()` scope populated with common request fields.\n *\n * Trace ID: the first present header of `traceIdHeader` (default\n * `DEFAULT_TRACE_HEADERS`, `traceparent` parsed), else `request.id`, else a\n * generated UUID. It is available via `getCurrentTraceId()` and is the logger's\n * top-level `traceId` inside the request. No response header is set unless\n * `responseHeader` is given.\n *\n * @example\n * ```ts\n * import { createExpressContextMiddleware } from 'logixia';\n * app.use(createExpressContextMiddleware({ responseHeader: 'x-trace-id' }));\n * ```\n */\nexport function createExpressContextMiddleware<Req extends ContextRequestLike = ContextRequestLike>(\n options: ContextHelperOptions<Req> = {}\n) {\n const headers = resolveTraceHeaders(options.traceIdHeader);\n\n return function logixiaContextMiddleware(\n req: Req,\n res: { setHeader?: (name: string, value: string) => unknown },\n next: () => void\n ): void {\n const scope = buildScope(req, options, headers);\n if (options.responseHeader) res.setHeader?.(options.responseHeader, scope.traceId as string);\n LogixiaContext.run(scope, next);\n };\n}\n\n/**\n * Create a Fastify lifecycle hook that wraps each request in a context scope.\n *\n * Register with `fastify.addHook('onRequest', createFastifyContextHook())`.\n * Resolves the trace ID exactly like {@link createExpressContextMiddleware} and\n * shares it with `createFastifyPlugin()` from `logixia/middleware`. Fastify's\n * default counter `request.id` (`req-1`, ...) is never used as a trace ID.\n */\nexport function createFastifyContextHook<Req extends ContextRequestLike = ContextRequestLike>(\n options: ContextHelperOptions<Req> = {}\n) {\n const headers = resolveTraceHeaders(options.traceIdHeader);\n\n return function logixiaFastifyHook(\n request: Req,\n reply: { header?: (name: string, value: string) => unknown },\n done: () => void\n ): void {\n const scope = buildScope(request, options, headers);\n if (options.responseHeader) reply.header?.(options.responseHeader, scope.traceId as string);\n LogixiaContext.run(scope, done);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,MAAM,WAAW,K,0BAAIA,EAAAA,CAAAA,kBAA8B;AAEnD,MAAa,iBAAiB;;;;;;;;;;;;;CAa5B,IAAO,OAAmB,UAAsB;EAC9C,MAAM,SAAS,SAAS,SAAS,KAAK,CAAC;EACvC,MAAM,eAAkB,SAAS,IAAI;GAAE,GAAG;GAAQ,GAAG;EAAM,GAAG,QAAQ;EACtE,MAAM,UAAU,MAAM;EACtB,IAAI,OAAO,YAAY,YAAY,YAAY,IAAI,OAAO,OAAO;EAGjE,MAAM,QAAQC,sBAAAA,aAAa;EAC3B,OAAO,MAAM,QAAQ,IAAI;GAAE,GAAG,MAAM,QAAQ,SAAS;IAAI,MAAM,aAAa;EAAQ,GAAG,MAAM;CAC/F;;;;;CAMA,MAA8B;EAC5B,OAAO,SAAS,SAAS;CAC3B;;;;;CAMA,IAAI,QAAmC;EACrC,MAAM,QAAQ,SAAS,SAAS;EAChC,IAAI,CAAC,OAGH;EAEF,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAClC,qBAAA,UAAU,OAAO,KAAK,OAAO,IAAI;CAErC;;;;;CAMA,aAA4C;EAC1C,OAAO;CACT;AACF;AAsBA,SAAS,WACP,SACA,SACA,SACY;CAEZ,OAAO;EAAE,GADK,QAAQ,SAAS,QAAQ,OAAO,OAAwC,IAAI,CAAC;EACxE,SAASC,sBAAAA,sBAAsB,SAAS,OAAO;CAAE;AACtE;;;;;;;;;;;;;;;;;AAkBA,SAAgB,+BACd,UAAqC,CAAC,GACtC;CACA,MAAM,UAAUC,sBAAAA,oBAAoB,QAAQ,aAAa;CAEzD,OAAO,SAAS,yBACd,KACA,KACA,MACM;;EACN,MAAM,QAAQ,WAAW,KAAK,SAAS,OAAO;EAC9C,IAAI,QAAQ,gBAAgB,CAAA,iBAAA,IAAI,eAAA,QAAA,mBAAA,KAAA,KAAA,eAAA,KAAA,KAAY,QAAQ,gBAAgB,MAAM,OAAiB;EAC3F,eAAe,IAAI,OAAO,IAAI;CAChC;AACF;;;;;;;;;AAUA,SAAgB,yBACd,UAAqC,CAAC,GACtC;CACA,MAAM,UAAUA,sBAAAA,oBAAoB,QAAQ,aAAa;CAEzD,OAAO,SAAS,mBACd,SACA,OACA,MACM;;EACN,MAAM,QAAQ,WAAW,SAAS,SAAS,OAAO;EAClD,IAAI,QAAQ,gBAAgB,CAAA,gBAAA,MAAM,YAAA,QAAA,kBAAA,KAAA,KAAA,cAAA,KAAA,OAAS,QAAQ,gBAAgB,MAAM,OAAiB;EAC1F,eAAe,IAAI,OAAO,IAAI;CAChC;AACF"}
1
+ {"version":3,"file":"async-context-BqjwNzgS.js","names":["AsyncLocalStorage","TraceContext","resolveRequestTraceId","resolveTraceHeaders"],"sources":["../src/context/async-context.ts"],"sourcesContent":["/**\n * LogixiaContext — Zero-boilerplate AsyncLocalStorage context propagation.\n *\n * Lets every `logger.info()` call inside a context automatically pick up stored\n * fields (requestId, userId, tenantId, …) without passing them through every function.\n *\n * Works across `Promise.all`, `setTimeout`, and event emitters — anything that\n * inherits the async execution context.\n *\n * @example\n * ```ts\n * import { LogixiaContext } from 'logixia';\n *\n * // Wrap a request handler:\n * app.use((req, res, next) => {\n * LogixiaContext.run({ requestId: req.id, userId: req.user?.id }, next);\n * });\n *\n * // Anywhere deeper in the call tree:\n * async function processOrder(orderId: string) {\n * const ctx = LogixiaContext.get(); // { requestId, userId, orderId }\n * logger.info('Processing order', { orderId, ...ctx });\n * }\n * ```\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport { assignOwn } from '../utils/own-property';\nimport {\n type ContextRequestLike,\n resolveRequestTraceId,\n resolveTraceHeaders,\n type TraceHeaderOption,\n} from '../utils/request-trace';\nimport { TraceContext } from '../utils/trace.utils';\n\nexport interface LogContext {\n traceId?: string;\n spanId?: string;\n userId?: string;\n tenantId?: string;\n sessionId?: string;\n [key: string]: unknown;\n}\n\n// Module-level singleton so all loggers share the same ALS store.\nconst _storage = new AsyncLocalStorage<LogContext>();\n\nexport const LogixiaContext = {\n /**\n * Run `callback` inside an async context that carries `store` fields.\n * All loggers queried within `callback` (and its async descendants) will\n * automatically see these fields.\n *\n * @example\n * ```ts\n * LogixiaContext.run({ requestId: 'abc', userId: '42' }, async () => {\n * await processOrder(); // sees requestId + userId in every log\n * });\n * ```\n */\n run<T>(store: LogContext, callback: () => T): T {\n const parent = _storage.getStore() ?? {};\n const scoped = (): T => _storage.run({ ...parent, ...store }, callback);\n const traceId = store.traceId;\n if (typeof traceId !== 'string' || traceId === '') return scoped();\n // An explicit traceId also becomes the TraceContext ID, so getCurrentTraceId(),\n // the logger, outbound propagation and the OTel bridge all see the same value.\n const trace = TraceContext.instance;\n return trace.storage.run({ ...trace.storage.getStore(), [trace.contextKey]: traceId }, scoped);\n },\n\n /**\n * Return the context fields active in the current async scope.\n * Returns `undefined` when called outside any `LogixiaContext.run()`.\n */\n get(): LogContext | undefined {\n return _storage.getStore();\n },\n\n /**\n * Merge `fields` into the **existing** context for the current scope.\n * If called outside a `run()` context this is a no-op (logs a dev warning).\n */\n set(fields: Partial<LogContext>): void {\n const store = _storage.getStore();\n if (!store) {\n // Silently ignore — avoids crashing in code that may call setContext\n // before a request context is established (e.g. background jobs).\n return;\n }\n for (const key of Object.keys(fields)) {\n assignOwn(store, key, fields[key]);\n }\n },\n\n /**\n * Retrieve the underlying `AsyncLocalStorage` instance.\n * Useful for advanced use-cases like custom middleware.\n */\n getStorage(): AsyncLocalStorage<LogContext> {\n return _storage;\n },\n};\n\nexport type { ContextRequestLike };\n\ninterface ContextHelperOptions<Req extends ContextRequestLike> {\n /**\n * Extract additional fields from the request. A `traceId` returned here is ignored:\n * the resolved request trace ID always wins.\n */\n enrich?: (request: Req & Record<string, unknown>) => Partial<LogContext>;\n /**\n * Header name, or list of names tried in order, to read the trace ID from.\n * Default: `DEFAULT_TRACE_HEADERS` (traceparent is parsed to its 32-hex trace id).\n */\n traceIdHeader?: TraceHeaderOption;\n /**\n * Echo the resolved trace ID on the response under this header name.\n * Default: `false` (no response header is set).\n */\n responseHeader?: string | false;\n}\n\nfunction buildScope<Req extends ContextRequestLike>(\n request: Req,\n options: ContextHelperOptions<Req>,\n headers: readonly string[]\n): LogContext {\n const extra = options.enrich ? options.enrich(request as Req & Record<string, unknown>) : {};\n return { ...extra, traceId: resolveRequestTraceId(request, headers) };\n}\n\n/**\n * Create an Express/Connect-compatible middleware that wraps each request in\n * a `LogixiaContext.run()` scope populated with common request fields.\n *\n * Trace ID: the first present header of `traceIdHeader` (default\n * `DEFAULT_TRACE_HEADERS`, `traceparent` parsed), else `request.id`, else a\n * generated UUID. It is available via `getCurrentTraceId()` and is the logger's\n * top-level `traceId` inside the request. No response header is set unless\n * `responseHeader` is given.\n *\n * @example\n * ```ts\n * import { createExpressContextMiddleware } from 'logixia';\n * app.use(createExpressContextMiddleware({ responseHeader: 'x-trace-id' }));\n * ```\n */\nexport function createExpressContextMiddleware<Req extends ContextRequestLike = ContextRequestLike>(\n options: ContextHelperOptions<Req> = {}\n) {\n const headers = resolveTraceHeaders(options.traceIdHeader);\n\n return function logixiaContextMiddleware(\n req: Req,\n res: { setHeader?: (name: string, value: string) => unknown },\n next: () => void\n ): void {\n const scope = buildScope(req, options, headers);\n if (options.responseHeader) res.setHeader?.(options.responseHeader, scope.traceId as string);\n LogixiaContext.run(scope, next);\n };\n}\n\n/**\n * Create a Fastify lifecycle hook that wraps each request in a context scope.\n *\n * Register with `fastify.addHook('onRequest', createFastifyContextHook())`.\n * Resolves the trace ID exactly like {@link createExpressContextMiddleware} and\n * shares it with `createFastifyPlugin()` from `logixia/middleware`. Fastify's\n * default counter `request.id` (`req-1`, ...) is never used as a trace ID.\n */\nexport function createFastifyContextHook<Req extends ContextRequestLike = ContextRequestLike>(\n options: ContextHelperOptions<Req> = {}\n) {\n const headers = resolveTraceHeaders(options.traceIdHeader);\n\n return function logixiaFastifyHook(\n request: Req,\n reply: { header?: (name: string, value: string) => unknown },\n done: () => void\n ): void {\n const scope = buildScope(request, options, headers);\n if (options.responseHeader) reply.header?.(options.responseHeader, scope.traceId as string);\n LogixiaContext.run(scope, done);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,MAAM,WAAW,K,0BAAIA,EAAAA,CAAAA,kBAA8B;AAEnD,MAAa,iBAAiB;;;;;;;;;;;;;CAa5B,IAAO,OAAmB,UAAsB;EAC9C,MAAM,SAAS,SAAS,SAAS,KAAK,CAAC;EACvC,MAAM,eAAkB,SAAS,IAAI;GAAE,GAAG;GAAQ,GAAG;EAAM,GAAG,QAAQ;EACtE,MAAM,UAAU,MAAM;EACtB,IAAI,OAAO,YAAY,YAAY,YAAY,IAAI,OAAO,OAAO;EAGjE,MAAM,QAAQC,sBAAAA,aAAa;EAC3B,OAAO,MAAM,QAAQ,IAAI;GAAE,GAAG,MAAM,QAAQ,SAAS;IAAI,MAAM,aAAa;EAAQ,GAAG,MAAM;CAC/F;;;;;CAMA,MAA8B;EAC5B,OAAO,SAAS,SAAS;CAC3B;;;;;CAMA,IAAI,QAAmC;EACrC,MAAM,QAAQ,SAAS,SAAS;EAChC,IAAI,CAAC,OAGH;EAEF,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAClC,qBAAA,UAAU,OAAO,KAAK,OAAO,IAAI;CAErC;;;;;CAMA,aAA4C;EAC1C,OAAO;CACT;AACF;AAsBA,SAAS,WACP,SACA,SACA,SACY;CAEZ,OAAO;EAAE,GADK,QAAQ,SAAS,QAAQ,OAAO,OAAwC,IAAI,CAAC;EACxE,SAASC,sBAAAA,sBAAsB,SAAS,OAAO;CAAE;AACtE;;;;;;;;;;;;;;;;;AAkBA,SAAgB,+BACd,UAAqC,CAAC,GACtC;CACA,MAAM,UAAUC,sBAAAA,oBAAoB,QAAQ,aAAa;CAEzD,OAAO,SAAS,yBACd,KACA,KACA,MACM;;EACN,MAAM,QAAQ,WAAW,KAAK,SAAS,OAAO;EAC9C,IAAI,QAAQ,gBAAgB,CAAA,iBAAA,IAAI,eAAA,QAAA,mBAAA,KAAA,KAAA,eAAA,KAAA,KAAY,QAAQ,gBAAgB,MAAM,OAAiB;EAC3F,eAAe,IAAI,OAAO,IAAI;CAChC;AACF;;;;;;;;;AAUA,SAAgB,yBACd,UAAqC,CAAC,GACtC;CACA,MAAM,UAAUA,sBAAAA,oBAAoB,QAAQ,aAAa;CAEzD,OAAO,SAAS,mBACd,SACA,OACA,MACM;;EACN,MAAM,QAAQ,WAAW,SAAS,SAAS,OAAO;EAClD,IAAI,QAAQ,gBAAgB,CAAA,gBAAA,MAAM,YAAA,QAAA,kBAAA,KAAA,KAAA,cAAA,KAAA,OAAS,QAAQ,gBAAgB,MAAM,OAAiB;EAC1F,eAAe,IAAI,OAAO,IAAI;CAChC;AACF"}
@@ -1,5 +1,5 @@
1
1
  import { t as assignOwn } from "./own-property-CjFw9qwZ.mjs";
2
- import { n as resolveTraceHeaders, s as TraceContext, t as resolveRequestTraceId } from "./request-trace-txUhyyre.mjs";
2
+ import { n as resolveTraceHeaders, s as TraceContext, t as resolveRequestTraceId } from "./request-trace-B-S5-LVk.mjs";
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
4
  // src/context/async-context.ts
5
5
  /**
@@ -129,4 +129,4 @@ function createFastifyContextHook(options = {}) {
129
129
  }
130
130
  export { createExpressContextMiddleware as n, createFastifyContextHook as r, LogixiaContext as t };
131
131
 
132
- //# sourceMappingURL=async-context-1YMPGgJZ.mjs.map
132
+ //# sourceMappingURL=async-context-DJdxbbCU.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"async-context-1YMPGgJZ.mjs","names":[],"sources":["../src/context/async-context.ts"],"sourcesContent":["/**\n * LogixiaContext — Zero-boilerplate AsyncLocalStorage context propagation.\n *\n * Lets every `logger.info()` call inside a context automatically pick up stored\n * fields (requestId, userId, tenantId, …) without passing them through every function.\n *\n * Works across `Promise.all`, `setTimeout`, and event emitters — anything that\n * inherits the async execution context.\n *\n * @example\n * ```ts\n * import { LogixiaContext } from 'logixia';\n *\n * // Wrap a request handler:\n * app.use((req, res, next) => {\n * LogixiaContext.run({ requestId: req.id, userId: req.user?.id }, next);\n * });\n *\n * // Anywhere deeper in the call tree:\n * async function processOrder(orderId: string) {\n * const ctx = LogixiaContext.get(); // { requestId, userId, orderId }\n * logger.info('Processing order', { orderId, ...ctx });\n * }\n * ```\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport { assignOwn } from '../utils/own-property';\nimport {\n type ContextRequestLike,\n resolveRequestTraceId,\n resolveTraceHeaders,\n type TraceHeaderOption,\n} from '../utils/request-trace';\nimport { TraceContext } from '../utils/trace.utils';\n\nexport interface LogContext {\n traceId?: string;\n spanId?: string;\n userId?: string;\n tenantId?: string;\n sessionId?: string;\n [key: string]: unknown;\n}\n\n// Module-level singleton so all loggers share the same ALS store.\nconst _storage = new AsyncLocalStorage<LogContext>();\n\nexport const LogixiaContext = {\n /**\n * Run `callback` inside an async context that carries `store` fields.\n * All loggers queried within `callback` (and its async descendants) will\n * automatically see these fields.\n *\n * @example\n * ```ts\n * LogixiaContext.run({ requestId: 'abc', userId: '42' }, async () => {\n * await processOrder(); // sees requestId + userId in every log\n * });\n * ```\n */\n run<T>(store: LogContext, callback: () => T): T {\n const parent = _storage.getStore() ?? {};\n const scoped = (): T => _storage.run({ ...parent, ...store }, callback);\n const traceId = store.traceId;\n if (typeof traceId !== 'string' || traceId === '') return scoped();\n // An explicit traceId also becomes the TraceContext ID, so getCurrentTraceId(),\n // the logger, outbound propagation and the OTel bridge all see the same value.\n const trace = TraceContext.instance;\n return trace.storage.run({ ...trace.storage.getStore(), [trace.contextKey]: traceId }, scoped);\n },\n\n /**\n * Return the context fields active in the current async scope.\n * Returns `undefined` when called outside any `LogixiaContext.run()`.\n */\n get(): LogContext | undefined {\n return _storage.getStore();\n },\n\n /**\n * Merge `fields` into the **existing** context for the current scope.\n * If called outside a `run()` context this is a no-op (logs a dev warning).\n */\n set(fields: Partial<LogContext>): void {\n const store = _storage.getStore();\n if (!store) {\n // Silently ignore — avoids crashing in code that may call setContext\n // before a request context is established (e.g. background jobs).\n return;\n }\n for (const key of Object.keys(fields)) {\n assignOwn(store, key, fields[key]);\n }\n },\n\n /**\n * Retrieve the underlying `AsyncLocalStorage` instance.\n * Useful for advanced use-cases like custom middleware.\n */\n getStorage(): AsyncLocalStorage<LogContext> {\n return _storage;\n },\n};\n\nexport type { ContextRequestLike };\n\ninterface ContextHelperOptions<Req extends ContextRequestLike> {\n /**\n * Extract additional fields from the request. A `traceId` returned here is ignored:\n * the resolved request trace ID always wins.\n */\n enrich?: (request: Req & Record<string, unknown>) => Partial<LogContext>;\n /**\n * Header name, or list of names tried in order, to read the trace ID from.\n * Default: `DEFAULT_TRACE_HEADERS` (traceparent is parsed to its 32-hex trace id).\n */\n traceIdHeader?: TraceHeaderOption;\n /**\n * Echo the resolved trace ID on the response under this header name.\n * Default: `false` (no response header is set).\n */\n responseHeader?: string | false;\n}\n\nfunction buildScope<Req extends ContextRequestLike>(\n request: Req,\n options: ContextHelperOptions<Req>,\n headers: readonly string[]\n): LogContext {\n const extra = options.enrich ? options.enrich(request as Req & Record<string, unknown>) : {};\n return { ...extra, traceId: resolveRequestTraceId(request, headers) };\n}\n\n/**\n * Create an Express/Connect-compatible middleware that wraps each request in\n * a `LogixiaContext.run()` scope populated with common request fields.\n *\n * Trace ID: the first present header of `traceIdHeader` (default\n * `DEFAULT_TRACE_HEADERS`, `traceparent` parsed), else `request.id`, else a\n * generated UUID. It is available via `getCurrentTraceId()` and is the logger's\n * top-level `traceId` inside the request. No response header is set unless\n * `responseHeader` is given.\n *\n * @example\n * ```ts\n * import { createExpressContextMiddleware } from 'logixia';\n * app.use(createExpressContextMiddleware({ responseHeader: 'x-trace-id' }));\n * ```\n */\nexport function createExpressContextMiddleware<Req extends ContextRequestLike = ContextRequestLike>(\n options: ContextHelperOptions<Req> = {}\n) {\n const headers = resolveTraceHeaders(options.traceIdHeader);\n\n return function logixiaContextMiddleware(\n req: Req,\n res: { setHeader?: (name: string, value: string) => unknown },\n next: () => void\n ): void {\n const scope = buildScope(req, options, headers);\n if (options.responseHeader) res.setHeader?.(options.responseHeader, scope.traceId as string);\n LogixiaContext.run(scope, next);\n };\n}\n\n/**\n * Create a Fastify lifecycle hook that wraps each request in a context scope.\n *\n * Register with `fastify.addHook('onRequest', createFastifyContextHook())`.\n * Resolves the trace ID exactly like {@link createExpressContextMiddleware} and\n * shares it with `createFastifyPlugin()` from `logixia/middleware`. Fastify's\n * default counter `request.id` (`req-1`, ...) is never used as a trace ID.\n */\nexport function createFastifyContextHook<Req extends ContextRequestLike = ContextRequestLike>(\n options: ContextHelperOptions<Req> = {}\n) {\n const headers = resolveTraceHeaders(options.traceIdHeader);\n\n return function logixiaFastifyHook(\n request: Req,\n reply: { header?: (name: string, value: string) => unknown },\n done: () => void\n ): void {\n const scope = buildScope(request, options, headers);\n if (options.responseHeader) reply.header?.(options.responseHeader, scope.traceId as string);\n LogixiaContext.run(scope, done);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,MAAM,WAAW,IAAI,kBAA8B;AAEnD,MAAa,iBAAiB;;;;;;;;;;;;;CAa5B,IAAO,OAAmB,UAAsB;EAC9C,MAAM,SAAS,SAAS,SAAS,KAAK,CAAC;EACvC,MAAM,eAAkB,SAAS,IAAI;GAAE,GAAG;GAAQ,GAAG;EAAM,GAAG,QAAQ;EACtE,MAAM,UAAU,MAAM;EACtB,IAAI,OAAO,YAAY,YAAY,YAAY,IAAI,OAAO,OAAO;EAGjE,MAAM,QAAQ,aAAa;EAC3B,OAAO,MAAM,QAAQ,IAAI;GAAE,GAAG,MAAM,QAAQ,SAAS;IAAI,MAAM,aAAa;EAAQ,GAAG,MAAM;CAC/F;;;;;CAMA,MAA8B;EAC5B,OAAO,SAAS,SAAS;CAC3B;;;;;CAMA,IAAI,QAAmC;EACrC,MAAM,QAAQ,SAAS,SAAS;EAChC,IAAI,CAAC,OAGH;EAEF,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAClC,UAAU,OAAO,KAAK,OAAO,IAAI;CAErC;;;;;CAMA,aAA4C;EAC1C,OAAO;CACT;AACF;AAsBA,SAAS,WACP,SACA,SACA,SACY;CAEZ,OAAO;EAAE,GADK,QAAQ,SAAS,QAAQ,OAAO,OAAwC,IAAI,CAAC;EACxE,SAAS,sBAAsB,SAAS,OAAO;CAAE;AACtE;;;;;;;;;;;;;;;;;AAkBA,SAAgB,+BACd,UAAqC,CAAC,GACtC;CACA,MAAM,UAAU,oBAAoB,QAAQ,aAAa;CAEzD,OAAO,SAAS,yBACd,KACA,KACA,MACM;;EACN,MAAM,QAAQ,WAAW,KAAK,SAAS,OAAO;EAC9C,IAAI,QAAQ,gBAAgB,CAAA,iBAAA,IAAI,eAAA,QAAA,mBAAA,KAAA,KAAA,eAAA,KAAA,KAAY,QAAQ,gBAAgB,MAAM,OAAiB;EAC3F,eAAe,IAAI,OAAO,IAAI;CAChC;AACF;;;;;;;;;AAUA,SAAgB,yBACd,UAAqC,CAAC,GACtC;CACA,MAAM,UAAU,oBAAoB,QAAQ,aAAa;CAEzD,OAAO,SAAS,mBACd,SACA,OACA,MACM;;EACN,MAAM,QAAQ,WAAW,SAAS,SAAS,OAAO;EAClD,IAAI,QAAQ,gBAAgB,CAAA,gBAAA,MAAM,YAAA,QAAA,kBAAA,KAAA,KAAA,cAAA,KAAA,OAAS,QAAQ,gBAAgB,MAAM,OAAiB;EAC1F,eAAe,IAAI,OAAO,IAAI;CAChC;AACF"}
1
+ {"version":3,"file":"async-context-DJdxbbCU.mjs","names":[],"sources":["../src/context/async-context.ts"],"sourcesContent":["/**\n * LogixiaContext — Zero-boilerplate AsyncLocalStorage context propagation.\n *\n * Lets every `logger.info()` call inside a context automatically pick up stored\n * fields (requestId, userId, tenantId, …) without passing them through every function.\n *\n * Works across `Promise.all`, `setTimeout`, and event emitters — anything that\n * inherits the async execution context.\n *\n * @example\n * ```ts\n * import { LogixiaContext } from 'logixia';\n *\n * // Wrap a request handler:\n * app.use((req, res, next) => {\n * LogixiaContext.run({ requestId: req.id, userId: req.user?.id }, next);\n * });\n *\n * // Anywhere deeper in the call tree:\n * async function processOrder(orderId: string) {\n * const ctx = LogixiaContext.get(); // { requestId, userId, orderId }\n * logger.info('Processing order', { orderId, ...ctx });\n * }\n * ```\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport { assignOwn } from '../utils/own-property';\nimport {\n type ContextRequestLike,\n resolveRequestTraceId,\n resolveTraceHeaders,\n type TraceHeaderOption,\n} from '../utils/request-trace';\nimport { TraceContext } from '../utils/trace.utils';\n\nexport interface LogContext {\n traceId?: string;\n spanId?: string;\n userId?: string;\n tenantId?: string;\n sessionId?: string;\n [key: string]: unknown;\n}\n\n// Module-level singleton so all loggers share the same ALS store.\nconst _storage = new AsyncLocalStorage<LogContext>();\n\nexport const LogixiaContext = {\n /**\n * Run `callback` inside an async context that carries `store` fields.\n * All loggers queried within `callback` (and its async descendants) will\n * automatically see these fields.\n *\n * @example\n * ```ts\n * LogixiaContext.run({ requestId: 'abc', userId: '42' }, async () => {\n * await processOrder(); // sees requestId + userId in every log\n * });\n * ```\n */\n run<T>(store: LogContext, callback: () => T): T {\n const parent = _storage.getStore() ?? {};\n const scoped = (): T => _storage.run({ ...parent, ...store }, callback);\n const traceId = store.traceId;\n if (typeof traceId !== 'string' || traceId === '') return scoped();\n // An explicit traceId also becomes the TraceContext ID, so getCurrentTraceId(),\n // the logger, outbound propagation and the OTel bridge all see the same value.\n const trace = TraceContext.instance;\n return trace.storage.run({ ...trace.storage.getStore(), [trace.contextKey]: traceId }, scoped);\n },\n\n /**\n * Return the context fields active in the current async scope.\n * Returns `undefined` when called outside any `LogixiaContext.run()`.\n */\n get(): LogContext | undefined {\n return _storage.getStore();\n },\n\n /**\n * Merge `fields` into the **existing** context for the current scope.\n * If called outside a `run()` context this is a no-op (logs a dev warning).\n */\n set(fields: Partial<LogContext>): void {\n const store = _storage.getStore();\n if (!store) {\n // Silently ignore — avoids crashing in code that may call setContext\n // before a request context is established (e.g. background jobs).\n return;\n }\n for (const key of Object.keys(fields)) {\n assignOwn(store, key, fields[key]);\n }\n },\n\n /**\n * Retrieve the underlying `AsyncLocalStorage` instance.\n * Useful for advanced use-cases like custom middleware.\n */\n getStorage(): AsyncLocalStorage<LogContext> {\n return _storage;\n },\n};\n\nexport type { ContextRequestLike };\n\ninterface ContextHelperOptions<Req extends ContextRequestLike> {\n /**\n * Extract additional fields from the request. A `traceId` returned here is ignored:\n * the resolved request trace ID always wins.\n */\n enrich?: (request: Req & Record<string, unknown>) => Partial<LogContext>;\n /**\n * Header name, or list of names tried in order, to read the trace ID from.\n * Default: `DEFAULT_TRACE_HEADERS` (traceparent is parsed to its 32-hex trace id).\n */\n traceIdHeader?: TraceHeaderOption;\n /**\n * Echo the resolved trace ID on the response under this header name.\n * Default: `false` (no response header is set).\n */\n responseHeader?: string | false;\n}\n\nfunction buildScope<Req extends ContextRequestLike>(\n request: Req,\n options: ContextHelperOptions<Req>,\n headers: readonly string[]\n): LogContext {\n const extra = options.enrich ? options.enrich(request as Req & Record<string, unknown>) : {};\n return { ...extra, traceId: resolveRequestTraceId(request, headers) };\n}\n\n/**\n * Create an Express/Connect-compatible middleware that wraps each request in\n * a `LogixiaContext.run()` scope populated with common request fields.\n *\n * Trace ID: the first present header of `traceIdHeader` (default\n * `DEFAULT_TRACE_HEADERS`, `traceparent` parsed), else `request.id`, else a\n * generated UUID. It is available via `getCurrentTraceId()` and is the logger's\n * top-level `traceId` inside the request. No response header is set unless\n * `responseHeader` is given.\n *\n * @example\n * ```ts\n * import { createExpressContextMiddleware } from 'logixia';\n * app.use(createExpressContextMiddleware({ responseHeader: 'x-trace-id' }));\n * ```\n */\nexport function createExpressContextMiddleware<Req extends ContextRequestLike = ContextRequestLike>(\n options: ContextHelperOptions<Req> = {}\n) {\n const headers = resolveTraceHeaders(options.traceIdHeader);\n\n return function logixiaContextMiddleware(\n req: Req,\n res: { setHeader?: (name: string, value: string) => unknown },\n next: () => void\n ): void {\n const scope = buildScope(req, options, headers);\n if (options.responseHeader) res.setHeader?.(options.responseHeader, scope.traceId as string);\n LogixiaContext.run(scope, next);\n };\n}\n\n/**\n * Create a Fastify lifecycle hook that wraps each request in a context scope.\n *\n * Register with `fastify.addHook('onRequest', createFastifyContextHook())`.\n * Resolves the trace ID exactly like {@link createExpressContextMiddleware} and\n * shares it with `createFastifyPlugin()` from `logixia/middleware`. Fastify's\n * default counter `request.id` (`req-1`, ...) is never used as a trace ID.\n */\nexport function createFastifyContextHook<Req extends ContextRequestLike = ContextRequestLike>(\n options: ContextHelperOptions<Req> = {}\n) {\n const headers = resolveTraceHeaders(options.traceIdHeader);\n\n return function logixiaFastifyHook(\n request: Req,\n reply: { header?: (name: string, value: string) => unknown },\n done: () => void\n ): void {\n const scope = buildScope(request, options, headers);\n if (options.responseHeader) reply.header?.(options.responseHeader, scope.traceId as string);\n LogixiaContext.run(scope, done);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,MAAM,WAAW,IAAI,kBAA8B;AAEnD,MAAa,iBAAiB;;;;;;;;;;;;;CAa5B,IAAO,OAAmB,UAAsB;EAC9C,MAAM,SAAS,SAAS,SAAS,KAAK,CAAC;EACvC,MAAM,eAAkB,SAAS,IAAI;GAAE,GAAG;GAAQ,GAAG;EAAM,GAAG,QAAQ;EACtE,MAAM,UAAU,MAAM;EACtB,IAAI,OAAO,YAAY,YAAY,YAAY,IAAI,OAAO,OAAO;EAGjE,MAAM,QAAQ,aAAa;EAC3B,OAAO,MAAM,QAAQ,IAAI;GAAE,GAAG,MAAM,QAAQ,SAAS;IAAI,MAAM,aAAa;EAAQ,GAAG,MAAM;CAC/F;;;;;CAMA,MAA8B;EAC5B,OAAO,SAAS,SAAS;CAC3B;;;;;CAMA,IAAI,QAAmC;EACrC,MAAM,QAAQ,SAAS,SAAS;EAChC,IAAI,CAAC,OAGH;EAEF,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAClC,UAAU,OAAO,KAAK,OAAO,IAAI;CAErC;;;;;CAMA,aAA4C;EAC1C,OAAO;CACT;AACF;AAsBA,SAAS,WACP,SACA,SACA,SACY;CAEZ,OAAO;EAAE,GADK,QAAQ,SAAS,QAAQ,OAAO,OAAwC,IAAI,CAAC;EACxE,SAAS,sBAAsB,SAAS,OAAO;CAAE;AACtE;;;;;;;;;;;;;;;;;AAkBA,SAAgB,+BACd,UAAqC,CAAC,GACtC;CACA,MAAM,UAAU,oBAAoB,QAAQ,aAAa;CAEzD,OAAO,SAAS,yBACd,KACA,KACA,MACM;;EACN,MAAM,QAAQ,WAAW,KAAK,SAAS,OAAO;EAC9C,IAAI,QAAQ,gBAAgB,CAAA,iBAAA,IAAI,eAAA,QAAA,mBAAA,KAAA,KAAA,eAAA,KAAA,KAAY,QAAQ,gBAAgB,MAAM,OAAiB;EAC3F,eAAe,IAAI,OAAO,IAAI;CAChC;AACF;;;;;;;;;AAUA,SAAgB,yBACd,UAAqC,CAAC,GACtC;CACA,MAAM,UAAU,oBAAoB,QAAQ,aAAa;CAEzD,OAAO,SAAS,mBACd,SACA,OACA,MACM;;EACN,MAAM,QAAQ,WAAW,SAAS,SAAS,OAAO;EAClD,IAAI,QAAQ,gBAAgB,CAAA,gBAAA,MAAM,YAAA,QAAA,kBAAA,KAAA,KAAA,cAAA,KAAA,OAAS,QAAQ,gBAAgB,MAAM,OAAiB;EAC1F,eAAe,IAAI,OAAO,IAAI;CAChC;AACF"}
@@ -1,4 +1,4 @@
1
- import { _ as TransportLogEntry, l as IAsyncTransport } from "./transport.types-B1ejJJP6.mjs";
1
+ import { _ as TransportLogEntry, l as IAsyncTransport } from "./transport.types-MjMDjOQ7.mjs";
2
2
  interface AzureMonitorTransportConfig {
3
3
  /** DCE (Data Collection Endpoint) URL, e.g. `https://<name>.ingest.monitor.azure.com`. */
4
4
  endpoint: string;
@@ -56,4 +56,4 @@ declare class AzureMonitorTransport implements IAsyncTransport {
56
56
  private sendEntries;
57
57
  }
58
58
  export { AzureMonitorTransportConfig as n, AzureMonitorTransport as t };
59
- //# sourceMappingURL=azure-monitor.transport-C5VjvREZ.d.mts.map
59
+ //# sourceMappingURL=azure-monitor.transport-BZDaQjy8.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"azure-monitor.transport-C5VjvREZ.d.mts","names":[],"sources":["../src/transports/azure-monitor.transport.ts"],"mappings":";UAsDiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;;;;;cAmBW,iCAAiC;WAC5B;WACA;WACA;WACA;mBAEC;mBACA;UACT;UACA;UACA;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;EAEL,YAAA,QAAQ;EA0BpB,MAAM,OAAO;EAKb,SAAS;;;;;;EASH,SAAS;UAQP;UAYA;UAIM;UAgDA"}
1
+ {"version":3,"file":"azure-monitor.transport-BZDaQjy8.d.mts","names":[],"sources":["../src/transports/azure-monitor.transport.ts"],"mappings":";UAsDiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;;;;;cAmBW,iCAAiC;WAC5B;WACA;WACA;WACA;mBAEC;mBACA;UACT;UACA;UACA;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;EAEL,YAAA,QAAQ;EA0BpB,MAAM,OAAO;EAKb,SAAS;;;;;;EASH,SAAS;UAQP;UAYA;UAIM;UAgDA"}
@@ -1,7 +1,7 @@
1
- import { i as scrubSecrets, n as protectSecrets, o as urlSecrets, r as scrubError } from "./secret-config-Cng7XPLx.mjs";
1
+ import { a as scrubSecrets, i as scrubError, n as protectSecrets, s as urlSecrets } from "./secret-config-BFKJrSYF.mjs";
2
2
  import { r as internalWarn, t as internalError } from "./internal-log-Cy5nF1ZL.mjs";
3
3
  import { i as stringifyForLog } from "./safe-stringify-B5p5ktFe.mjs";
4
- import { a as httpRequest, t as BatchBuffer } from "./batch-buffer-BnmVCCwI.mjs";
4
+ import { a as httpRequest, t as BatchBuffer } from "./batch-buffer-CcubZ2cW.mjs";
5
5
  // src/transports/azure-monitor.transport.ts
6
6
  /**
7
7
  * Azure AD tenant IDs are either a GUID or a verified domain (e.g. `contoso.onmicrosoft.com`).
@@ -172,4 +172,4 @@ var AzureMonitorTransport = class {
172
172
  };
173
173
  export { AzureMonitorTransport as t };
174
174
 
175
- //# sourceMappingURL=azure-monitor.transport-B_dqSV_o.mjs.map
175
+ //# sourceMappingURL=azure-monitor.transport-DNccn1vp.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"azure-monitor.transport-B_dqSV_o.mjs","names":[],"sources":["../src/transports/azure-monitor.transport.ts"],"sourcesContent":["/**\n * logixia — Azure Monitor / Application Insights transport\n *\n * Sends log entries to Azure Monitor via the Data Collection Rules (DCR) API\n * using the Logs Ingestion API (the modern replacement for the legacy HTTP\n * Data Collector API).\n *\n * Maps logixia levels to Application Insights severity levels:\n * verbose → 0, debug → 1, info → 1, warn → 2, error → 3, critical → 4\n *\n * @example\n * ```ts\n * import { createLogger } from 'logixia';\n * import { AzureMonitorTransport } from 'logixia/azure';\n *\n * const logger = createLogger({\n * transports: {\n * custom: [\n * new AzureMonitorTransport({\n * // Logs Ingestion API endpoint (from your DCR)\n * endpoint: 'https://<dce>.ingest.monitor.azure.com',\n * ruleId: 'dcr-xxxxxxxxxxxxxxxx',\n * streamName: 'Custom-LogixiaLogs',\n * // Client credentials are required; managed identity is not supported.\n * tenantId: process.env.AZURE_TENANT_ID,\n * clientId: process.env.AZURE_CLIENT_ID,\n * clientSecret: process.env.AZURE_CLIENT_SECRET,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n httpRequest,\n} from './batch-buffer';\nimport { protectSecrets, scrubError, scrubSecrets, urlSecrets } from './secret-config';\n\n/**\n * Azure AD tenant IDs are either a GUID or a verified domain (e.g. `contoso.onmicrosoft.com`).\n * This pattern is deliberately permissive on structure but strict on character\n * set — it rejects the breakout characters (`/`, `@`, `:`, `?`, `#`, whitespace)\n * that would let a hostile config smuggle a different host, path, or query into\n * the OAuth token URL.\n */\nconst AZURE_TENANT_RE = /^[a-z0-9][a-z0-9.-]{1,253}$/i;\n\nexport interface AzureMonitorTransportConfig {\n /** DCE (Data Collection Endpoint) URL, e.g. `https://<name>.ingest.monitor.azure.com`. */\n endpoint: string;\n /** Data Collection Rule immutable ID (from Azure portal). */\n ruleId: string;\n /** Stream name defined in the DCR, e.g. `'Custom-LogixiaLogs_CL'`. */\n streamName: string;\n /** Azure AD tenant ID for OAuth2 client-credentials flow. */\n tenantId?: string;\n /** Azure AD app (service principal) client ID. */\n clientId?: string;\n /** Azure AD app client secret. */\n clientSecret?: string;\n /** Number of entries to batch. @default 200 */\n batchSize?: number;\n /** Flush interval in ms. @default 5000 */\n flushIntervalMs?: number;\n /** Abort a request that has not finished after this many ms. @default 10000 */\n requestTimeoutMs?: number;\n /** Max buffered entries; the oldest are dropped past this. @default 10000 */\n maxBufferSize?: number;\n level?: string;\n}\n\nconst SEVERITY_MAP: Record<string, number> = {\n verbose: 0,\n trace: 0,\n debug: 1,\n info: 1,\n log: 1,\n warn: 2,\n warning: 2,\n error: 3,\n critical: 4,\n fatal: 4,\n};\n\n/**\n * Azure Monitor Logs Ingestion transport.\n */\nexport class AzureMonitorTransport implements IAsyncTransport {\n public readonly name = 'azure-monitor';\n public readonly batchSize: number;\n public readonly flushInterval: number;\n public readonly level: string | undefined;\n\n private readonly buffer: BatchBuffer<TransportLogEntry>;\n private readonly requestTimeoutMs: number;\n private flushTimer: NodeJS.Timeout | null = null;\n private accessToken: string | null = null;\n private tokenExpiry = 0;\n\n private readonly endpoint: string;\n private readonly ruleId: string;\n private readonly streamName: string;\n private readonly tenantId: string | undefined;\n private readonly clientId: string | undefined;\n private readonly clientSecret: string | undefined;\n\n constructor(config: AzureMonitorTransportConfig) {\n protectSecrets(this, ['clientSecret', 'accessToken']);\n this.endpoint = config.endpoint.replace(/\\/$/, '');\n this.ruleId = config.ruleId;\n this.streamName = config.streamName;\n this.tenantId = config.tenantId ?? process.env['AZURE_TENANT_ID'];\n this.clientId = config.clientId ?? process.env['AZURE_CLIENT_ID'];\n this.clientSecret = config.clientSecret ?? process.env['AZURE_CLIENT_SECRET'];\n this.batchSize = config.batchSize ?? 200;\n this.flushInterval = config.flushIntervalMs ?? 5000;\n this.level = config.level;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.buffer = new BatchBuffer<TransportLogEntry>({\n name: 'AzureMonitorTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (entries) => this.sendEntries(entries),\n secrets: () => this.secretValues(),\n });\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushInterval);\n if (this.flushTimer.unref) this.flushTimer.unref();\n }\n\n write(entry: TransportLogEntry): void {\n this.buffer.push(entry);\n this.buffer.flushIfFull();\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n /**\n * Flush remaining entries and stop the interval timer on shutdown. Without\n * this the manager's close() skipped the transport (close was undefined) and\n * buffered entries were lost on deploy. Waits for any in-flight send first.\n */\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.buffer.close();\n }\n\n private toAzureRecord(entry: TransportLogEntry): Record<string, unknown> {\n return {\n TimeGenerated: entry.timestamp ?? new Date().toISOString(),\n SeverityLevel: SEVERITY_MAP[entry.level.toLowerCase()] ?? 1,\n Message: entry.message,\n AppName: entry.appName,\n Context: entry.context ?? '',\n TraceId: entry.traceId ?? '',\n Properties: stringifyForLog(entry.data ?? {}),\n };\n }\n\n private secretValues(token?: string): (string | undefined)[] {\n return [this.clientSecret, this.accessToken ?? undefined, token, ...urlSecrets(this.endpoint)];\n }\n\n private async getAccessToken(): Promise<string | null> {\n if (this.accessToken && Date.now() < this.tokenExpiry) return this.accessToken;\n\n if (!this.tenantId || !this.clientId || !this.clientSecret) {\n internalWarn(\n 'AzureMonitorTransport: missing credentials — set tenantId, clientId, clientSecret'\n );\n return null;\n }\n\n const body = new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n scope: 'https://monitor.azure.com/.default',\n }).toString();\n\n const tenantId = this.tenantId;\n if (!AZURE_TENANT_RE.test(tenantId)) {\n internalWarn(\n `AzureMonitorTransport: refusing to request token — tenantId \"${tenantId}\" is not a valid GUID or domain`\n );\n return null;\n }\n\n const reqUrl = new URL(\n `https://login.microsoftonline.com/${encodeURIComponent(tenantId)}/oauth2/v2.0/token`\n );\n try {\n const res = await httpRequest({\n url: reqUrl,\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body,\n timeoutMs: this.requestTimeoutMs,\n label: 'Azure token request',\n secrets: () => this.secretValues(),\n });\n const parsed = JSON.parse(res.body) as { access_token?: string; expires_in?: number };\n if (!parsed.access_token) return null;\n this.accessToken = parsed.access_token;\n this.tokenExpiry = Date.now() + (parsed.expires_in ?? 3600) * 1000 - 60_000;\n return this.accessToken;\n } catch (err) {\n internalError('AzureMonitorTransport token request failed', err);\n return null;\n }\n }\n\n private async sendEntries(entries: TransportLogEntry[]): Promise<void> {\n const token = await this.getAccessToken();\n if (!token) throw new Error('AzureMonitorTransport: no access token available');\n\n const records = entries.map((e) => this.toAzureRecord(e));\n const body = stringifyForLog(records);\n let reqUrl: URL;\n try {\n // Build from the user-configured DCE endpoint; encodeURIComponent-ing the\n // ruleId/streamName stops a hostile config from smuggling extra path\n // segments or query params into the request.\n reqUrl = new URL(\n `dataCollectionRules/${encodeURIComponent(this.ruleId)}/streams/${encodeURIComponent(this.streamName)}?api-version=2023-01-01`,\n this.endpoint.endsWith('/') ? this.endpoint : `${this.endpoint}/`\n );\n } catch (error) {\n internalError(\n `AzureMonitorTransport: invalid endpoint \"${scrubSecrets(this.endpoint, urlSecrets(this.endpoint))}\"`,\n scrubError(error, urlSecrets(this.endpoint))\n );\n return;\n }\n if (reqUrl.protocol !== 'https:') {\n internalError(`AzureMonitorTransport: endpoint must use https (got ${reqUrl.protocol})`);\n return;\n }\n\n await httpRequest({\n url: reqUrl,\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body,\n timeoutMs: this.requestTimeoutMs,\n label: 'Azure Monitor API',\n secrets: () => this.secretValues(token),\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"azure-monitor.transport-DNccn1vp.mjs","names":[],"sources":["../src/transports/azure-monitor.transport.ts"],"sourcesContent":["/**\n * logixia — Azure Monitor / Application Insights transport\n *\n * Sends log entries to Azure Monitor via the Data Collection Rules (DCR) API\n * using the Logs Ingestion API (the modern replacement for the legacy HTTP\n * Data Collector API).\n *\n * Maps logixia levels to Application Insights severity levels:\n * verbose → 0, debug → 1, info → 1, warn → 2, error → 3, critical → 4\n *\n * @example\n * ```ts\n * import { createLogger } from 'logixia';\n * import { AzureMonitorTransport } from 'logixia/azure';\n *\n * const logger = createLogger({\n * transports: {\n * custom: [\n * new AzureMonitorTransport({\n * // Logs Ingestion API endpoint (from your DCR)\n * endpoint: 'https://<dce>.ingest.monitor.azure.com',\n * ruleId: 'dcr-xxxxxxxxxxxxxxxx',\n * streamName: 'Custom-LogixiaLogs',\n * // Client credentials are required; managed identity is not supported.\n * tenantId: process.env.AZURE_TENANT_ID,\n * clientId: process.env.AZURE_CLIENT_ID,\n * clientSecret: process.env.AZURE_CLIENT_SECRET,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n httpRequest,\n} from './batch-buffer';\nimport { protectSecrets, scrubError, scrubSecrets, urlSecrets } from './secret-config';\n\n/**\n * Azure AD tenant IDs are either a GUID or a verified domain (e.g. `contoso.onmicrosoft.com`).\n * This pattern is deliberately permissive on structure but strict on character\n * set — it rejects the breakout characters (`/`, `@`, `:`, `?`, `#`, whitespace)\n * that would let a hostile config smuggle a different host, path, or query into\n * the OAuth token URL.\n */\nconst AZURE_TENANT_RE = /^[a-z0-9][a-z0-9.-]{1,253}$/i;\n\nexport interface AzureMonitorTransportConfig {\n /** DCE (Data Collection Endpoint) URL, e.g. `https://<name>.ingest.monitor.azure.com`. */\n endpoint: string;\n /** Data Collection Rule immutable ID (from Azure portal). */\n ruleId: string;\n /** Stream name defined in the DCR, e.g. `'Custom-LogixiaLogs_CL'`. */\n streamName: string;\n /** Azure AD tenant ID for OAuth2 client-credentials flow. */\n tenantId?: string;\n /** Azure AD app (service principal) client ID. */\n clientId?: string;\n /** Azure AD app client secret. */\n clientSecret?: string;\n /** Number of entries to batch. @default 200 */\n batchSize?: number;\n /** Flush interval in ms. @default 5000 */\n flushIntervalMs?: number;\n /** Abort a request that has not finished after this many ms. @default 10000 */\n requestTimeoutMs?: number;\n /** Max buffered entries; the oldest are dropped past this. @default 10000 */\n maxBufferSize?: number;\n level?: string;\n}\n\nconst SEVERITY_MAP: Record<string, number> = {\n verbose: 0,\n trace: 0,\n debug: 1,\n info: 1,\n log: 1,\n warn: 2,\n warning: 2,\n error: 3,\n critical: 4,\n fatal: 4,\n};\n\n/**\n * Azure Monitor Logs Ingestion transport.\n */\nexport class AzureMonitorTransport implements IAsyncTransport {\n public readonly name = 'azure-monitor';\n public readonly batchSize: number;\n public readonly flushInterval: number;\n public readonly level: string | undefined;\n\n private readonly buffer: BatchBuffer<TransportLogEntry>;\n private readonly requestTimeoutMs: number;\n private flushTimer: NodeJS.Timeout | null = null;\n private accessToken: string | null = null;\n private tokenExpiry = 0;\n\n private readonly endpoint: string;\n private readonly ruleId: string;\n private readonly streamName: string;\n private readonly tenantId: string | undefined;\n private readonly clientId: string | undefined;\n private readonly clientSecret: string | undefined;\n\n constructor(config: AzureMonitorTransportConfig) {\n protectSecrets(this, ['clientSecret', 'accessToken']);\n this.endpoint = config.endpoint.replace(/\\/$/, '');\n this.ruleId = config.ruleId;\n this.streamName = config.streamName;\n this.tenantId = config.tenantId ?? process.env['AZURE_TENANT_ID'];\n this.clientId = config.clientId ?? process.env['AZURE_CLIENT_ID'];\n this.clientSecret = config.clientSecret ?? process.env['AZURE_CLIENT_SECRET'];\n this.batchSize = config.batchSize ?? 200;\n this.flushInterval = config.flushIntervalMs ?? 5000;\n this.level = config.level;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.buffer = new BatchBuffer<TransportLogEntry>({\n name: 'AzureMonitorTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (entries) => this.sendEntries(entries),\n secrets: () => this.secretValues(),\n });\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushInterval);\n if (this.flushTimer.unref) this.flushTimer.unref();\n }\n\n write(entry: TransportLogEntry): void {\n this.buffer.push(entry);\n this.buffer.flushIfFull();\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n /**\n * Flush remaining entries and stop the interval timer on shutdown. Without\n * this the manager's close() skipped the transport (close was undefined) and\n * buffered entries were lost on deploy. Waits for any in-flight send first.\n */\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.buffer.close();\n }\n\n private toAzureRecord(entry: TransportLogEntry): Record<string, unknown> {\n return {\n TimeGenerated: entry.timestamp ?? new Date().toISOString(),\n SeverityLevel: SEVERITY_MAP[entry.level.toLowerCase()] ?? 1,\n Message: entry.message,\n AppName: entry.appName,\n Context: entry.context ?? '',\n TraceId: entry.traceId ?? '',\n Properties: stringifyForLog(entry.data ?? {}),\n };\n }\n\n private secretValues(token?: string): (string | undefined)[] {\n return [this.clientSecret, this.accessToken ?? undefined, token, ...urlSecrets(this.endpoint)];\n }\n\n private async getAccessToken(): Promise<string | null> {\n if (this.accessToken && Date.now() < this.tokenExpiry) return this.accessToken;\n\n if (!this.tenantId || !this.clientId || !this.clientSecret) {\n internalWarn(\n 'AzureMonitorTransport: missing credentials — set tenantId, clientId, clientSecret'\n );\n return null;\n }\n\n const body = new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n scope: 'https://monitor.azure.com/.default',\n }).toString();\n\n const tenantId = this.tenantId;\n if (!AZURE_TENANT_RE.test(tenantId)) {\n internalWarn(\n `AzureMonitorTransport: refusing to request token — tenantId \"${tenantId}\" is not a valid GUID or domain`\n );\n return null;\n }\n\n const reqUrl = new URL(\n `https://login.microsoftonline.com/${encodeURIComponent(tenantId)}/oauth2/v2.0/token`\n );\n try {\n const res = await httpRequest({\n url: reqUrl,\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body,\n timeoutMs: this.requestTimeoutMs,\n label: 'Azure token request',\n secrets: () => this.secretValues(),\n });\n const parsed = JSON.parse(res.body) as { access_token?: string; expires_in?: number };\n if (!parsed.access_token) return null;\n this.accessToken = parsed.access_token;\n this.tokenExpiry = Date.now() + (parsed.expires_in ?? 3600) * 1000 - 60_000;\n return this.accessToken;\n } catch (err) {\n internalError('AzureMonitorTransport token request failed', err);\n return null;\n }\n }\n\n private async sendEntries(entries: TransportLogEntry[]): Promise<void> {\n const token = await this.getAccessToken();\n if (!token) throw new Error('AzureMonitorTransport: no access token available');\n\n const records = entries.map((e) => this.toAzureRecord(e));\n const body = stringifyForLog(records);\n let reqUrl: URL;\n try {\n // Build from the user-configured DCE endpoint; encodeURIComponent-ing the\n // ruleId/streamName stops a hostile config from smuggling extra path\n // segments or query params into the request.\n reqUrl = new URL(\n `dataCollectionRules/${encodeURIComponent(this.ruleId)}/streams/${encodeURIComponent(this.streamName)}?api-version=2023-01-01`,\n this.endpoint.endsWith('/') ? this.endpoint : `${this.endpoint}/`\n );\n } catch (error) {\n internalError(\n `AzureMonitorTransport: invalid endpoint \"${scrubSecrets(this.endpoint, urlSecrets(this.endpoint))}\"`,\n scrubError(error, urlSecrets(this.endpoint))\n );\n return;\n }\n if (reqUrl.protocol !== 'https:') {\n internalError(`AzureMonitorTransport: endpoint must use https (got ${reqUrl.protocol})`);\n return;\n }\n\n await httpRequest({\n url: reqUrl,\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body,\n timeoutMs: this.requestTimeoutMs,\n label: 'Azure Monitor API',\n secrets: () => this.secretValues(token),\n });\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- const require_secret_config = require("./secret-config-OqmIPmpV.js");
1
+ const require_secret_config = require("./secret-config-DB84lWy4.js");
2
2
  const require_internal_log = require("./internal-log-DwqTjhpb.js");
3
3
  const require_safe_stringify = require("./safe-stringify-J3sh-7YM.js");
4
- const require_batch_buffer = require("./batch-buffer-C-ETEX3Y.js");
4
+ const require_batch_buffer = require("./batch-buffer-C6EmQcD5.js");
5
5
  // src/transports/azure-monitor.transport.ts
6
6
  /**
7
7
  * Azure AD tenant IDs are either a GUID or a verified domain (e.g. `contoso.onmicrosoft.com`).
@@ -177,4 +177,4 @@ Object.defineProperty(exports, "AzureMonitorTransport", {
177
177
  }
178
178
  });
179
179
 
180
- //# sourceMappingURL=azure-monitor.transport-Bl3xVvpN.js.map
180
+ //# sourceMappingURL=azure-monitor.transport-DTIgEZwg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"azure-monitor.transport-Bl3xVvpN.js","names":["BatchBuffer","stringifyForLog","urlSecrets","httpRequest","scrubSecrets","scrubError"],"sources":["../src/transports/azure-monitor.transport.ts"],"sourcesContent":["/**\n * logixia — Azure Monitor / Application Insights transport\n *\n * Sends log entries to Azure Monitor via the Data Collection Rules (DCR) API\n * using the Logs Ingestion API (the modern replacement for the legacy HTTP\n * Data Collector API).\n *\n * Maps logixia levels to Application Insights severity levels:\n * verbose → 0, debug → 1, info → 1, warn → 2, error → 3, critical → 4\n *\n * @example\n * ```ts\n * import { createLogger } from 'logixia';\n * import { AzureMonitorTransport } from 'logixia/azure';\n *\n * const logger = createLogger({\n * transports: {\n * custom: [\n * new AzureMonitorTransport({\n * // Logs Ingestion API endpoint (from your DCR)\n * endpoint: 'https://<dce>.ingest.monitor.azure.com',\n * ruleId: 'dcr-xxxxxxxxxxxxxxxx',\n * streamName: 'Custom-LogixiaLogs',\n * // Client credentials are required; managed identity is not supported.\n * tenantId: process.env.AZURE_TENANT_ID,\n * clientId: process.env.AZURE_CLIENT_ID,\n * clientSecret: process.env.AZURE_CLIENT_SECRET,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n httpRequest,\n} from './batch-buffer';\nimport { protectSecrets, scrubError, scrubSecrets, urlSecrets } from './secret-config';\n\n/**\n * Azure AD tenant IDs are either a GUID or a verified domain (e.g. `contoso.onmicrosoft.com`).\n * This pattern is deliberately permissive on structure but strict on character\n * set — it rejects the breakout characters (`/`, `@`, `:`, `?`, `#`, whitespace)\n * that would let a hostile config smuggle a different host, path, or query into\n * the OAuth token URL.\n */\nconst AZURE_TENANT_RE = /^[a-z0-9][a-z0-9.-]{1,253}$/i;\n\nexport interface AzureMonitorTransportConfig {\n /** DCE (Data Collection Endpoint) URL, e.g. `https://<name>.ingest.monitor.azure.com`. */\n endpoint: string;\n /** Data Collection Rule immutable ID (from Azure portal). */\n ruleId: string;\n /** Stream name defined in the DCR, e.g. `'Custom-LogixiaLogs_CL'`. */\n streamName: string;\n /** Azure AD tenant ID for OAuth2 client-credentials flow. */\n tenantId?: string;\n /** Azure AD app (service principal) client ID. */\n clientId?: string;\n /** Azure AD app client secret. */\n clientSecret?: string;\n /** Number of entries to batch. @default 200 */\n batchSize?: number;\n /** Flush interval in ms. @default 5000 */\n flushIntervalMs?: number;\n /** Abort a request that has not finished after this many ms. @default 10000 */\n requestTimeoutMs?: number;\n /** Max buffered entries; the oldest are dropped past this. @default 10000 */\n maxBufferSize?: number;\n level?: string;\n}\n\nconst SEVERITY_MAP: Record<string, number> = {\n verbose: 0,\n trace: 0,\n debug: 1,\n info: 1,\n log: 1,\n warn: 2,\n warning: 2,\n error: 3,\n critical: 4,\n fatal: 4,\n};\n\n/**\n * Azure Monitor Logs Ingestion transport.\n */\nexport class AzureMonitorTransport implements IAsyncTransport {\n public readonly name = 'azure-monitor';\n public readonly batchSize: number;\n public readonly flushInterval: number;\n public readonly level: string | undefined;\n\n private readonly buffer: BatchBuffer<TransportLogEntry>;\n private readonly requestTimeoutMs: number;\n private flushTimer: NodeJS.Timeout | null = null;\n private accessToken: string | null = null;\n private tokenExpiry = 0;\n\n private readonly endpoint: string;\n private readonly ruleId: string;\n private readonly streamName: string;\n private readonly tenantId: string | undefined;\n private readonly clientId: string | undefined;\n private readonly clientSecret: string | undefined;\n\n constructor(config: AzureMonitorTransportConfig) {\n protectSecrets(this, ['clientSecret', 'accessToken']);\n this.endpoint = config.endpoint.replace(/\\/$/, '');\n this.ruleId = config.ruleId;\n this.streamName = config.streamName;\n this.tenantId = config.tenantId ?? process.env['AZURE_TENANT_ID'];\n this.clientId = config.clientId ?? process.env['AZURE_CLIENT_ID'];\n this.clientSecret = config.clientSecret ?? process.env['AZURE_CLIENT_SECRET'];\n this.batchSize = config.batchSize ?? 200;\n this.flushInterval = config.flushIntervalMs ?? 5000;\n this.level = config.level;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.buffer = new BatchBuffer<TransportLogEntry>({\n name: 'AzureMonitorTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (entries) => this.sendEntries(entries),\n secrets: () => this.secretValues(),\n });\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushInterval);\n if (this.flushTimer.unref) this.flushTimer.unref();\n }\n\n write(entry: TransportLogEntry): void {\n this.buffer.push(entry);\n this.buffer.flushIfFull();\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n /**\n * Flush remaining entries and stop the interval timer on shutdown. Without\n * this the manager's close() skipped the transport (close was undefined) and\n * buffered entries were lost on deploy. Waits for any in-flight send first.\n */\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.buffer.close();\n }\n\n private toAzureRecord(entry: TransportLogEntry): Record<string, unknown> {\n return {\n TimeGenerated: entry.timestamp ?? new Date().toISOString(),\n SeverityLevel: SEVERITY_MAP[entry.level.toLowerCase()] ?? 1,\n Message: entry.message,\n AppName: entry.appName,\n Context: entry.context ?? '',\n TraceId: entry.traceId ?? '',\n Properties: stringifyForLog(entry.data ?? {}),\n };\n }\n\n private secretValues(token?: string): (string | undefined)[] {\n return [this.clientSecret, this.accessToken ?? undefined, token, ...urlSecrets(this.endpoint)];\n }\n\n private async getAccessToken(): Promise<string | null> {\n if (this.accessToken && Date.now() < this.tokenExpiry) return this.accessToken;\n\n if (!this.tenantId || !this.clientId || !this.clientSecret) {\n internalWarn(\n 'AzureMonitorTransport: missing credentials — set tenantId, clientId, clientSecret'\n );\n return null;\n }\n\n const body = new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n scope: 'https://monitor.azure.com/.default',\n }).toString();\n\n const tenantId = this.tenantId;\n if (!AZURE_TENANT_RE.test(tenantId)) {\n internalWarn(\n `AzureMonitorTransport: refusing to request token — tenantId \"${tenantId}\" is not a valid GUID or domain`\n );\n return null;\n }\n\n const reqUrl = new URL(\n `https://login.microsoftonline.com/${encodeURIComponent(tenantId)}/oauth2/v2.0/token`\n );\n try {\n const res = await httpRequest({\n url: reqUrl,\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body,\n timeoutMs: this.requestTimeoutMs,\n label: 'Azure token request',\n secrets: () => this.secretValues(),\n });\n const parsed = JSON.parse(res.body) as { access_token?: string; expires_in?: number };\n if (!parsed.access_token) return null;\n this.accessToken = parsed.access_token;\n this.tokenExpiry = Date.now() + (parsed.expires_in ?? 3600) * 1000 - 60_000;\n return this.accessToken;\n } catch (err) {\n internalError('AzureMonitorTransport token request failed', err);\n return null;\n }\n }\n\n private async sendEntries(entries: TransportLogEntry[]): Promise<void> {\n const token = await this.getAccessToken();\n if (!token) throw new Error('AzureMonitorTransport: no access token available');\n\n const records = entries.map((e) => this.toAzureRecord(e));\n const body = stringifyForLog(records);\n let reqUrl: URL;\n try {\n // Build from the user-configured DCE endpoint; encodeURIComponent-ing the\n // ruleId/streamName stops a hostile config from smuggling extra path\n // segments or query params into the request.\n reqUrl = new URL(\n `dataCollectionRules/${encodeURIComponent(this.ruleId)}/streams/${encodeURIComponent(this.streamName)}?api-version=2023-01-01`,\n this.endpoint.endsWith('/') ? this.endpoint : `${this.endpoint}/`\n );\n } catch (error) {\n internalError(\n `AzureMonitorTransport: invalid endpoint \"${scrubSecrets(this.endpoint, urlSecrets(this.endpoint))}\"`,\n scrubError(error, urlSecrets(this.endpoint))\n );\n return;\n }\n if (reqUrl.protocol !== 'https:') {\n internalError(`AzureMonitorTransport: endpoint must use https (got ${reqUrl.protocol})`);\n return;\n }\n\n await httpRequest({\n url: reqUrl,\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body,\n timeoutMs: this.requestTimeoutMs,\n label: 'Azure Monitor API',\n secrets: () => this.secretValues(token),\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;AAoDA,MAAM,kBAAkB;AA0BxB,MAAM,eAAuC;CAC3C,SAAS;CACT,OAAO;CACP,OAAO;CACP,MAAM;CACN,KAAK;CACL,MAAM;CACN,SAAS;CACT,OAAO;CACP,UAAU;CACV,OAAO;AACT;;;;AAKA,IAAa,wBAAb,MAA8D;CAC5D,OAAuB;CACvB;CACA;CACA;CAEA;CACA;CACA,aAA4C;CAC5C,cAAqC;CACrC,cAAsB;CAEtB;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,QAAqC;EAC/C,sBAAA,eAAe,MAAM,CAAC,gBAAgB,aAAa,CAAC;EACpD,KAAK,WAAW,OAAO,SAAS,QAAQ,OAAO,EAAE;EACjD,KAAK,SAAS,OAAO;EACrB,KAAK,aAAa,OAAO;EACzB,KAAK,WAAW,OAAO,YAAY,QAAQ,IAAI;EAC/C,KAAK,WAAW,OAAO,YAAY,QAAQ,IAAI;EAC/C,KAAK,eAAe,OAAO,gBAAgB,QAAQ,IAAI;EACvD,KAAK,YAAY,OAAO,aAAa;EACrC,KAAK,gBAAgB,OAAO,mBAAmB;EAC/C,KAAK,QAAQ,OAAO;EACpB,KAAK,mBAAmB,OAAO,oBAAA;EAC/B,KAAK,SAAS,IAAIA,qBAAAA,YAA+B;GAC/C,MAAM;GACN,WAAW,KAAK;GAChB,eAAe,OAAO,iBAAA;GACtB,OAAO,YAAY,KAAK,YAAY,OAAO;GAC3C,eAAe,KAAK,aAAa;EACnC,CAAC;EAED,KAAK,aAAa,kBAAkB;GAClC,KAAK,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;EAC7B,GAAG,KAAK,aAAa;EACrB,IAAI,KAAK,WAAW,OAAO,KAAK,WAAW,MAAM;CACnD;CAEA,MAAM,OAAgC;EACpC,KAAK,OAAO,KAAK,KAAK;EACtB,KAAK,OAAO,YAAY;CAC1B;CAEA,QAAuB;EACrB,OAAO,KAAK,OAAO,MAAM;CAC3B;;;;;;CAOA,MAAM,QAAuB;EAC3B,IAAI,KAAK,YAAY;GACnB,cAAc,KAAK,UAAU;GAC7B,KAAK,aAAa;EACpB;EACA,MAAM,KAAK,OAAO,MAAM;CAC1B;CAEA,cAAsB,OAAmD;EACvE,OAAO;GACL,eAAe,MAAM,8BAAa,IAAI,KAAK,EAAA,CAAE,YAAY;GACzD,eAAe,aAAa,MAAM,MAAM,YAAY,MAAM;GAC1D,SAAS,MAAM;GACf,SAAS,MAAM;GACf,SAAS,MAAM,WAAW;GAC1B,SAAS,MAAM,WAAW;GAC1B,YAAYC,uBAAAA,gBAAgB,MAAM,QAAQ,CAAC,CAAC;EAC9C;CACF;CAEA,aAAqB,OAAwC;EAC3D,OAAO;GAAC,KAAK;GAAc,KAAK,eAAe,KAAA;GAAW;GAAO,GAAGC,sBAAAA,WAAW,KAAK,QAAQ;EAAC;CAC/F;CAEA,MAAc,iBAAyC;EACrD,IAAI,KAAK,eAAe,KAAK,IAAI,IAAI,KAAK,aAAa,OAAO,KAAK;EAEnE,IAAI,CAAC,KAAK,YAAY,CAAC,KAAK,YAAY,CAAC,KAAK,cAAc;GAC1D,qBAAA,aACE,mFACF;GACA,OAAO;EACT;EAEA,MAAM,OAAO,IAAI,gBAAgB;GAC/B,YAAY;GACZ,WAAW,KAAK;GAChB,eAAe,KAAK;GACpB,OAAO;EACT,CAAC,CAAC,CAAC,SAAS;EAEZ,MAAM,WAAW,KAAK;EACtB,IAAI,CAAC,gBAAgB,KAAK,QAAQ,GAAG;GACnC,qBAAA,aACE,gEAAgE,SAAS,gCAC3E;GACA,OAAO;EACT;EAEA,MAAM,SAAS,IAAI,IACjB,qCAAqC,mBAAmB,QAAQ,EAAE,mBACpE;EACA,IAAI;GACF,MAAM,MAAM,MAAMC,qBAAAA,YAAY;IAC5B,KAAK;IACL,SAAS,EAAE,gBAAgB,oCAAoC;IAC/D;IACA,WAAW,KAAK;IAChB,OAAO;IACP,eAAe,KAAK,aAAa;GACnC,CAAC;GACD,MAAM,SAAS,KAAK,MAAM,IAAI,IAAI;GAClC,IAAI,CAAC,OAAO,cAAc,OAAO;GACjC,KAAK,cAAc,OAAO;GAC1B,KAAK,cAAc,KAAK,IAAI,KAAK,OAAO,cAAc,QAAQ,MAAO;GACrE,OAAO,KAAK;EACd,SAAS,KAAK;GACZ,qBAAA,cAAc,8CAA8C,GAAG;GAC/D,OAAO;EACT;CACF;CAEA,MAAc,YAAY,SAA6C;EACrE,MAAM,QAAQ,MAAM,KAAK,eAAe;EACxC,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,kDAAkD;EAE9E,MAAM,UAAU,QAAQ,KAAK,MAAM,KAAK,cAAc,CAAC,CAAC;EACxD,MAAM,OAAOF,uBAAAA,gBAAgB,OAAO;EACpC,IAAI;EACJ,IAAI;GAIF,SAAS,IAAI,IACX,uBAAuB,mBAAmB,KAAK,MAAM,EAAE,WAAW,mBAAmB,KAAK,UAAU,EAAE,0BACtG,KAAK,SAAS,SAAS,GAAG,IAAI,KAAK,WAAW,GAAG,KAAK,SAAS,EACjE;EACF,SAAS,OAAO;GACd,qBAAA,cACE,4CAA4CG,sBAAAA,aAAa,KAAK,UAAUF,sBAAAA,WAAW,KAAK,QAAQ,CAAC,EAAE,IACnGG,sBAAAA,WAAW,OAAOH,sBAAAA,WAAW,KAAK,QAAQ,CAAC,CAC7C;GACA;EACF;EACA,IAAI,OAAO,aAAa,UAAU;GAChC,qBAAA,cAAc,uDAAuD,OAAO,SAAS,EAAE;GACvF;EACF;EAEA,MAAMC,qBAAAA,YAAY;GAChB,KAAK;GACL,SAAS;IAAE,eAAe,UAAU;IAAS,gBAAgB;GAAmB;GAChF;GACA,WAAW,KAAK;GAChB,OAAO;GACP,eAAe,KAAK,aAAa,KAAK;EACxC,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"azure-monitor.transport-DTIgEZwg.js","names":["BatchBuffer","stringifyForLog","urlSecrets","httpRequest","scrubSecrets","scrubError"],"sources":["../src/transports/azure-monitor.transport.ts"],"sourcesContent":["/**\n * logixia — Azure Monitor / Application Insights transport\n *\n * Sends log entries to Azure Monitor via the Data Collection Rules (DCR) API\n * using the Logs Ingestion API (the modern replacement for the legacy HTTP\n * Data Collector API).\n *\n * Maps logixia levels to Application Insights severity levels:\n * verbose → 0, debug → 1, info → 1, warn → 2, error → 3, critical → 4\n *\n * @example\n * ```ts\n * import { createLogger } from 'logixia';\n * import { AzureMonitorTransport } from 'logixia/azure';\n *\n * const logger = createLogger({\n * transports: {\n * custom: [\n * new AzureMonitorTransport({\n * // Logs Ingestion API endpoint (from your DCR)\n * endpoint: 'https://<dce>.ingest.monitor.azure.com',\n * ruleId: 'dcr-xxxxxxxxxxxxxxxx',\n * streamName: 'Custom-LogixiaLogs',\n * // Client credentials are required; managed identity is not supported.\n * tenantId: process.env.AZURE_TENANT_ID,\n * clientId: process.env.AZURE_CLIENT_ID,\n * clientSecret: process.env.AZURE_CLIENT_SECRET,\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { stringifyForLog } from '../utils/safe-stringify';\nimport {\n BatchBuffer,\n DEFAULT_MAX_BUFFER_SIZE,\n DEFAULT_REQUEST_TIMEOUT_MS,\n httpRequest,\n} from './batch-buffer';\nimport { protectSecrets, scrubError, scrubSecrets, urlSecrets } from './secret-config';\n\n/**\n * Azure AD tenant IDs are either a GUID or a verified domain (e.g. `contoso.onmicrosoft.com`).\n * This pattern is deliberately permissive on structure but strict on character\n * set — it rejects the breakout characters (`/`, `@`, `:`, `?`, `#`, whitespace)\n * that would let a hostile config smuggle a different host, path, or query into\n * the OAuth token URL.\n */\nconst AZURE_TENANT_RE = /^[a-z0-9][a-z0-9.-]{1,253}$/i;\n\nexport interface AzureMonitorTransportConfig {\n /** DCE (Data Collection Endpoint) URL, e.g. `https://<name>.ingest.monitor.azure.com`. */\n endpoint: string;\n /** Data Collection Rule immutable ID (from Azure portal). */\n ruleId: string;\n /** Stream name defined in the DCR, e.g. `'Custom-LogixiaLogs_CL'`. */\n streamName: string;\n /** Azure AD tenant ID for OAuth2 client-credentials flow. */\n tenantId?: string;\n /** Azure AD app (service principal) client ID. */\n clientId?: string;\n /** Azure AD app client secret. */\n clientSecret?: string;\n /** Number of entries to batch. @default 200 */\n batchSize?: number;\n /** Flush interval in ms. @default 5000 */\n flushIntervalMs?: number;\n /** Abort a request that has not finished after this many ms. @default 10000 */\n requestTimeoutMs?: number;\n /** Max buffered entries; the oldest are dropped past this. @default 10000 */\n maxBufferSize?: number;\n level?: string;\n}\n\nconst SEVERITY_MAP: Record<string, number> = {\n verbose: 0,\n trace: 0,\n debug: 1,\n info: 1,\n log: 1,\n warn: 2,\n warning: 2,\n error: 3,\n critical: 4,\n fatal: 4,\n};\n\n/**\n * Azure Monitor Logs Ingestion transport.\n */\nexport class AzureMonitorTransport implements IAsyncTransport {\n public readonly name = 'azure-monitor';\n public readonly batchSize: number;\n public readonly flushInterval: number;\n public readonly level: string | undefined;\n\n private readonly buffer: BatchBuffer<TransportLogEntry>;\n private readonly requestTimeoutMs: number;\n private flushTimer: NodeJS.Timeout | null = null;\n private accessToken: string | null = null;\n private tokenExpiry = 0;\n\n private readonly endpoint: string;\n private readonly ruleId: string;\n private readonly streamName: string;\n private readonly tenantId: string | undefined;\n private readonly clientId: string | undefined;\n private readonly clientSecret: string | undefined;\n\n constructor(config: AzureMonitorTransportConfig) {\n protectSecrets(this, ['clientSecret', 'accessToken']);\n this.endpoint = config.endpoint.replace(/\\/$/, '');\n this.ruleId = config.ruleId;\n this.streamName = config.streamName;\n this.tenantId = config.tenantId ?? process.env['AZURE_TENANT_ID'];\n this.clientId = config.clientId ?? process.env['AZURE_CLIENT_ID'];\n this.clientSecret = config.clientSecret ?? process.env['AZURE_CLIENT_SECRET'];\n this.batchSize = config.batchSize ?? 200;\n this.flushInterval = config.flushIntervalMs ?? 5000;\n this.level = config.level;\n this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n this.buffer = new BatchBuffer<TransportLogEntry>({\n name: 'AzureMonitorTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (entries) => this.sendEntries(entries),\n secrets: () => this.secretValues(),\n });\n\n this.flushTimer = setInterval(() => {\n this.flush().catch(() => {});\n }, this.flushInterval);\n if (this.flushTimer.unref) this.flushTimer.unref();\n }\n\n write(entry: TransportLogEntry): void {\n this.buffer.push(entry);\n this.buffer.flushIfFull();\n }\n\n flush(): Promise<void> {\n return this.buffer.flush();\n }\n\n /**\n * Flush remaining entries and stop the interval timer on shutdown. Without\n * this the manager's close() skipped the transport (close was undefined) and\n * buffered entries were lost on deploy. Waits for any in-flight send first.\n */\n async close(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n await this.buffer.close();\n }\n\n private toAzureRecord(entry: TransportLogEntry): Record<string, unknown> {\n return {\n TimeGenerated: entry.timestamp ?? new Date().toISOString(),\n SeverityLevel: SEVERITY_MAP[entry.level.toLowerCase()] ?? 1,\n Message: entry.message,\n AppName: entry.appName,\n Context: entry.context ?? '',\n TraceId: entry.traceId ?? '',\n Properties: stringifyForLog(entry.data ?? {}),\n };\n }\n\n private secretValues(token?: string): (string | undefined)[] {\n return [this.clientSecret, this.accessToken ?? undefined, token, ...urlSecrets(this.endpoint)];\n }\n\n private async getAccessToken(): Promise<string | null> {\n if (this.accessToken && Date.now() < this.tokenExpiry) return this.accessToken;\n\n if (!this.tenantId || !this.clientId || !this.clientSecret) {\n internalWarn(\n 'AzureMonitorTransport: missing credentials — set tenantId, clientId, clientSecret'\n );\n return null;\n }\n\n const body = new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: this.clientId,\n client_secret: this.clientSecret,\n scope: 'https://monitor.azure.com/.default',\n }).toString();\n\n const tenantId = this.tenantId;\n if (!AZURE_TENANT_RE.test(tenantId)) {\n internalWarn(\n `AzureMonitorTransport: refusing to request token — tenantId \"${tenantId}\" is not a valid GUID or domain`\n );\n return null;\n }\n\n const reqUrl = new URL(\n `https://login.microsoftonline.com/${encodeURIComponent(tenantId)}/oauth2/v2.0/token`\n );\n try {\n const res = await httpRequest({\n url: reqUrl,\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body,\n timeoutMs: this.requestTimeoutMs,\n label: 'Azure token request',\n secrets: () => this.secretValues(),\n });\n const parsed = JSON.parse(res.body) as { access_token?: string; expires_in?: number };\n if (!parsed.access_token) return null;\n this.accessToken = parsed.access_token;\n this.tokenExpiry = Date.now() + (parsed.expires_in ?? 3600) * 1000 - 60_000;\n return this.accessToken;\n } catch (err) {\n internalError('AzureMonitorTransport token request failed', err);\n return null;\n }\n }\n\n private async sendEntries(entries: TransportLogEntry[]): Promise<void> {\n const token = await this.getAccessToken();\n if (!token) throw new Error('AzureMonitorTransport: no access token available');\n\n const records = entries.map((e) => this.toAzureRecord(e));\n const body = stringifyForLog(records);\n let reqUrl: URL;\n try {\n // Build from the user-configured DCE endpoint; encodeURIComponent-ing the\n // ruleId/streamName stops a hostile config from smuggling extra path\n // segments or query params into the request.\n reqUrl = new URL(\n `dataCollectionRules/${encodeURIComponent(this.ruleId)}/streams/${encodeURIComponent(this.streamName)}?api-version=2023-01-01`,\n this.endpoint.endsWith('/') ? this.endpoint : `${this.endpoint}/`\n );\n } catch (error) {\n internalError(\n `AzureMonitorTransport: invalid endpoint \"${scrubSecrets(this.endpoint, urlSecrets(this.endpoint))}\"`,\n scrubError(error, urlSecrets(this.endpoint))\n );\n return;\n }\n if (reqUrl.protocol !== 'https:') {\n internalError(`AzureMonitorTransport: endpoint must use https (got ${reqUrl.protocol})`);\n return;\n }\n\n await httpRequest({\n url: reqUrl,\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body,\n timeoutMs: this.requestTimeoutMs,\n label: 'Azure Monitor API',\n secrets: () => this.secretValues(token),\n });\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { _ as TransportLogEntry, l as IAsyncTransport } from "./transport.types-B1ejJJP6.js";
1
+ import { _ as TransportLogEntry, l as IAsyncTransport } from "./transport.types-MjMDjOQ7.js";
2
2
  interface AzureMonitorTransportConfig {
3
3
  /** DCE (Data Collection Endpoint) URL, e.g. `https://<name>.ingest.monitor.azure.com`. */
4
4
  endpoint: string;
@@ -56,4 +56,4 @@ declare class AzureMonitorTransport implements IAsyncTransport {
56
56
  private sendEntries;
57
57
  }
58
58
  export { AzureMonitorTransportConfig as n, AzureMonitorTransport as t };
59
- //# sourceMappingURL=azure-monitor.transport-CyHDjHS-.d.ts.map
59
+ //# sourceMappingURL=azure-monitor.transport-MDigkKsS.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"azure-monitor.transport-CyHDjHS-.d.ts","names":[],"sources":["../src/transports/azure-monitor.transport.ts"],"mappings":";UAsDiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;;;;;cAmBW,iCAAiC;WAC5B;WACA;WACA;WACA;mBAEC;mBACA;UACT;UACA;UACA;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;EAEL,YAAA,QAAQ;EA0BpB,MAAM,OAAO;EAKb,SAAS;;;;;;EASH,SAAS;UAQP;UAYA;UAIM;UAgDA"}
1
+ {"version":3,"file":"azure-monitor.transport-MDigkKsS.d.ts","names":[],"sources":["../src/transports/azure-monitor.transport.ts"],"mappings":";UAsDiB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;EACA;;;;;cAmBW,iCAAiC;WAC5B;WACA;WACA;WACA;mBAEC;mBACA;UACT;UACA;UACA;mBAES;mBACA;mBACA;mBACA;mBACA;mBACA;EAEL,YAAA,QAAQ;EA0BpB,MAAM,OAAO;EAKb,SAAS;;;;;;EASH,SAAS;UAQP;UAYA;UAIM;UAgDA"}
package/dist/azure.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as AzureMonitorTransportConfig, t as AzureMonitorTransport } from "./azure-monitor.transport-C5VjvREZ.mjs";
1
+ import { n as AzureMonitorTransportConfig, t as AzureMonitorTransport } from "./azure-monitor.transport-BZDaQjy8.mjs";
2
2
  export { AzureMonitorTransport, AzureMonitorTransportConfig };
package/dist/azure.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as AzureMonitorTransportConfig, t as AzureMonitorTransport } from "./azure-monitor.transport-CyHDjHS-.js";
1
+ import { n as AzureMonitorTransportConfig, t as AzureMonitorTransport } from "./azure-monitor.transport-MDigkKsS.js";
2
2
  export { AzureMonitorTransport, AzureMonitorTransportConfig };
package/dist/azure.js CHANGED
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_azure_monitor_transport = require("./azure-monitor.transport-Bl3xVvpN.js");
2
+ const require_azure_monitor_transport = require("./azure-monitor.transport-DTIgEZwg.js");
3
3
  exports.AzureMonitorTransport = require_azure_monitor_transport.AzureMonitorTransport;
package/dist/azure.mjs CHANGED
@@ -1 +1 @@
1
- export { t as AzureMonitorTransport } from "./azure-monitor.transport-B_dqSV_o.mjs";
1
+ export { t as AzureMonitorTransport } from "./azure-monitor.transport-DNccn1vp.mjs";
@@ -1,4 +1,4 @@
1
- const require_secret_config = require("./secret-config-OqmIPmpV.js");
1
+ const require_secret_config = require("./secret-config-DB84lWy4.js");
2
2
  const require_internal_log = require("./internal-log-DwqTjhpb.js");
3
3
  let node_http = require("node:http");
4
4
  node_http = require_secret_config.__toESM(node_http);
@@ -184,4 +184,4 @@ Object.defineProperty(exports, "isPermanentHttpError", {
184
184
  }
185
185
  });
186
186
 
187
- //# sourceMappingURL=batch-buffer-C-ETEX3Y.js.map
187
+ //# sourceMappingURL=batch-buffer-C6EmQcD5.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"batch-buffer-C-ETEX3Y.js","names":["scrubbing","http","https"],"sources":["../src/transports/batch-buffer.ts"],"sourcesContent":["/**\n * Shared batching core for the network transports (OTLP, CloudWatch, GCP, Azure).\n *\n * - one send in flight at a time; flush() and close() join it instead of\n * racing it, so close() cannot return while a request is still on the wire\n * - a failed batch goes back to the front in its original order\n * - 4xx responses other than 429 drop the batch with a warning instead of\n * retrying a request the server will never accept\n * - the buffer is capped; when full the oldest entries are dropped\n */\n\nimport * as http from 'node:http';\nimport * as https from 'node:https';\n\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { scrubbing, scrubError } from './secret-config';\n\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;\nexport const DEFAULT_MAX_BUFFER_SIZE = 10_000;\nconst WARN_INTERVAL_MS = 10_000;\nconst MAX_ERROR_BODY = 500;\nconst MAX_BACKOFF_MS = 30_000;\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n label: string\n ) {\n const detail = body ? `: ${body.slice(0, MAX_ERROR_BODY)}` : '';\n super(`${label} returned HTTP ${status}${detail}`);\n this.name = 'HttpError';\n }\n}\n\nexport function isPermanentHttpError(err: unknown): boolean {\n return err instanceof HttpError && err.status >= 400 && err.status < 500 && err.status !== 429;\n}\n\nexport interface HttpRequestOptions {\n url: URL;\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n timeoutMs: number;\n label: string;\n /** Secret values to scrub from any error this request raises. URL userinfo is always scrubbed. */\n secrets?: () => (string | undefined)[];\n}\n\nexport interface HttpResult {\n status: number;\n body: string;\n}\n\n/** One request with a hard timeout. Rejects with HttpError on any non-2xx status. */\nexport function httpRequest(opts: HttpRequestOptions): Promise<HttpResult> {\n return sendRequest(opts).catch(scrubbing(opts.secrets ?? (() => [])));\n}\n\nfunction sendRequest(opts: HttpRequestOptions): Promise<HttpResult> {\n const { url, method = 'POST', headers = {}, body, timeoutMs, label } = opts;\n const transport = url.protocol === 'http:' ? http : https;\n return new Promise<HttpResult>((resolve, reject) => {\n const req = transport.request(\n {\n protocol: url.protocol,\n hostname: url.hostname,\n port: url.port || undefined,\n path: `${url.pathname}${url.search}`,\n method,\n headers:\n body === undefined ? headers : { ...headers, 'Content-Length': Buffer.byteLength(body) },\n },\n (res) => {\n let raw = '';\n res.on('data', (c: Buffer) => (raw += c.toString()));\n res.on('end', () => {\n const status = res.statusCode ?? 0;\n if (status >= 200 && status < 300) resolve({ status, body: raw });\n else reject(new HttpError(status, raw, label));\n });\n res.on('error', reject);\n }\n );\n req.setTimeout(timeoutMs, () => {\n req.destroy(new Error(`${label} timed out after ${timeoutMs}ms`));\n });\n req.on('error', reject);\n if (body !== undefined) req.write(body);\n req.end();\n });\n}\n\nexport interface BatchBufferOptions<T> {\n name: string;\n batchSize: number;\n maxBufferSize?: number;\n send: (items: T[]) => Promise<void>;\n /** Secret values to scrub from send errors before they are stored or logged. */\n secrets?: () => (string | undefined)[];\n /** Overrides the default drop-on-4xx rule. Return true to keep and retry the batch. */\n shouldRetry?: (err: unknown) => boolean;\n}\n\nexport class BatchBuffer<T> {\n private items: T[] = [];\n private inFlight: Promise<void> | undefined;\n private dropped = 0;\n private lastDropWarn = 0;\n private failures = 0;\n private retryAfter = 0;\n\n constructor(private readonly opts: BatchBufferOptions<T>) {}\n\n get length(): number {\n return this.items.length;\n }\n\n get batchSize(): number {\n return this.opts.batchSize;\n }\n\n push(item: T): void {\n this.items.push(item);\n this.enforceCap();\n }\n\n /** Threshold flush for write(): skipped while a send is running or the endpoint is backing off. */\n flushIfFull(): void {\n if (this.items.length < this.opts.batchSize || this.inFlight || Date.now() < this.retryAfter) {\n return;\n }\n this.flush().catch(() => {});\n }\n\n flush(): Promise<void> {\n if (!this.inFlight) {\n this.inFlight = this.drain().finally(() => {\n this.inFlight = undefined;\n });\n }\n return this.inFlight;\n }\n\n /** Flush up to `attempts` times, then report whatever is still undelivered. */\n async close(attempts = 3): Promise<void> {\n await this.inFlight;\n for (let i = 0; i < attempts && this.items.length > 0; i += 1) {\n await this.flush();\n }\n if (this.items.length > 0) {\n internalError(\n `${this.opts.name} closing with ${this.items.length} undelivered entr${this.items.length === 1 ? 'y' : 'ies'}`\n );\n }\n }\n\n private async drain(): Promise<void> {\n while (this.items.length > 0) {\n const batch = this.items.splice(0, this.opts.batchSize);\n try {\n await this.opts.send(batch);\n this.failures = 0;\n } catch (err) {\n scrubError(err, this.opts.secrets?.() ?? []);\n const retry = this.opts.shouldRetry\n ? this.opts.shouldRetry(err)\n : !isPermanentHttpError(err);\n if (!retry) {\n internalWarn(\n `${this.opts.name}: dropping ${batch.length} entr${batch.length === 1 ? 'y' : 'ies'} the server rejected (${err instanceof Error ? err.message : String(err)})`\n );\n continue;\n }\n internalError(`${this.opts.name} flush error`, err);\n this.items.unshift(...batch);\n this.enforceCap();\n this.failures += 1;\n this.retryAfter = Date.now() + Math.min(MAX_BACKOFF_MS, 1000 * 2 ** (this.failures - 1));\n return;\n }\n }\n }\n\n private enforceCap(): void {\n const max = this.opts.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;\n const excess = this.items.length - max;\n if (excess <= 0) return;\n this.items.splice(0, excess);\n this.dropped += excess;\n const now = Date.now();\n if (now - this.lastDropWarn >= WARN_INTERVAL_MS) {\n internalWarn(\n `${this.opts.name}: buffer full (${max}), dropped ${this.dropped} oldest entr${this.dropped === 1 ? 'y' : 'ies'}`\n );\n this.dropped = 0;\n this.lastDropWarn = now;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"batch-buffer-C6EmQcD5.js","names":["scrubbing","http","https"],"sources":["../src/transports/batch-buffer.ts"],"sourcesContent":["/**\n * Shared batching core for the network transports (OTLP, CloudWatch, GCP, Azure).\n *\n * - one send in flight at a time; flush() and close() join it instead of\n * racing it, so close() cannot return while a request is still on the wire\n * - a failed batch goes back to the front in its original order\n * - 4xx responses other than 429 drop the batch with a warning instead of\n * retrying a request the server will never accept\n * - the buffer is capped; when full the oldest entries are dropped\n */\n\nimport * as http from 'node:http';\nimport * as https from 'node:https';\n\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { scrubbing, scrubError } from './secret-config';\n\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;\nexport const DEFAULT_MAX_BUFFER_SIZE = 10_000;\nconst WARN_INTERVAL_MS = 10_000;\nconst MAX_ERROR_BODY = 500;\nconst MAX_BACKOFF_MS = 30_000;\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n label: string\n ) {\n const detail = body ? `: ${body.slice(0, MAX_ERROR_BODY)}` : '';\n super(`${label} returned HTTP ${status}${detail}`);\n this.name = 'HttpError';\n }\n}\n\nexport function isPermanentHttpError(err: unknown): boolean {\n return err instanceof HttpError && err.status >= 400 && err.status < 500 && err.status !== 429;\n}\n\nexport interface HttpRequestOptions {\n url: URL;\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n timeoutMs: number;\n label: string;\n /** Secret values to scrub from any error this request raises. URL userinfo is always scrubbed. */\n secrets?: () => (string | undefined)[];\n}\n\nexport interface HttpResult {\n status: number;\n body: string;\n}\n\n/** One request with a hard timeout. Rejects with HttpError on any non-2xx status. */\nexport function httpRequest(opts: HttpRequestOptions): Promise<HttpResult> {\n return sendRequest(opts).catch(scrubbing(opts.secrets ?? (() => [])));\n}\n\nfunction sendRequest(opts: HttpRequestOptions): Promise<HttpResult> {\n const { url, method = 'POST', headers = {}, body, timeoutMs, label } = opts;\n const transport = url.protocol === 'http:' ? http : https;\n return new Promise<HttpResult>((resolve, reject) => {\n const req = transport.request(\n {\n protocol: url.protocol,\n hostname: url.hostname,\n port: url.port || undefined,\n path: `${url.pathname}${url.search}`,\n method,\n headers:\n body === undefined ? headers : { ...headers, 'Content-Length': Buffer.byteLength(body) },\n },\n (res) => {\n let raw = '';\n res.on('data', (c: Buffer) => (raw += c.toString()));\n res.on('end', () => {\n const status = res.statusCode ?? 0;\n if (status >= 200 && status < 300) resolve({ status, body: raw });\n else reject(new HttpError(status, raw, label));\n });\n res.on('error', reject);\n }\n );\n req.setTimeout(timeoutMs, () => {\n req.destroy(new Error(`${label} timed out after ${timeoutMs}ms`));\n });\n req.on('error', reject);\n if (body !== undefined) req.write(body);\n req.end();\n });\n}\n\nexport interface BatchBufferOptions<T> {\n name: string;\n batchSize: number;\n maxBufferSize?: number;\n send: (items: T[]) => Promise<void>;\n /** Secret values to scrub from send errors before they are stored or logged. */\n secrets?: () => (string | undefined)[];\n /** Overrides the default drop-on-4xx rule. Return true to keep and retry the batch. */\n shouldRetry?: (err: unknown) => boolean;\n}\n\nexport class BatchBuffer<T> {\n private items: T[] = [];\n private inFlight: Promise<void> | undefined;\n private dropped = 0;\n private lastDropWarn = 0;\n private failures = 0;\n private retryAfter = 0;\n\n constructor(private readonly opts: BatchBufferOptions<T>) {}\n\n get length(): number {\n return this.items.length;\n }\n\n get batchSize(): number {\n return this.opts.batchSize;\n }\n\n push(item: T): void {\n this.items.push(item);\n this.enforceCap();\n }\n\n /** Threshold flush for write(): skipped while a send is running or the endpoint is backing off. */\n flushIfFull(): void {\n if (this.items.length < this.opts.batchSize || this.inFlight || Date.now() < this.retryAfter) {\n return;\n }\n this.flush().catch(() => {});\n }\n\n flush(): Promise<void> {\n if (!this.inFlight) {\n this.inFlight = this.drain().finally(() => {\n this.inFlight = undefined;\n });\n }\n return this.inFlight;\n }\n\n /** Flush up to `attempts` times, then report whatever is still undelivered. */\n async close(attempts = 3): Promise<void> {\n await this.inFlight;\n for (let i = 0; i < attempts && this.items.length > 0; i += 1) {\n await this.flush();\n }\n if (this.items.length > 0) {\n internalError(\n `${this.opts.name} closing with ${this.items.length} undelivered entr${this.items.length === 1 ? 'y' : 'ies'}`\n );\n }\n }\n\n private async drain(): Promise<void> {\n while (this.items.length > 0) {\n const batch = this.items.splice(0, this.opts.batchSize);\n try {\n await this.opts.send(batch);\n this.failures = 0;\n } catch (err) {\n scrubError(err, this.opts.secrets?.() ?? []);\n const retry = this.opts.shouldRetry\n ? this.opts.shouldRetry(err)\n : !isPermanentHttpError(err);\n if (!retry) {\n internalWarn(\n `${this.opts.name}: dropping ${batch.length} entr${batch.length === 1 ? 'y' : 'ies'} the server rejected (${err instanceof Error ? err.message : String(err)})`\n );\n continue;\n }\n internalError(`${this.opts.name} flush error`, err);\n this.items.unshift(...batch);\n this.enforceCap();\n this.failures += 1;\n this.retryAfter = Date.now() + Math.min(MAX_BACKOFF_MS, 1000 * 2 ** (this.failures - 1));\n return;\n }\n }\n }\n\n private enforceCap(): void {\n const max = this.opts.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;\n const excess = this.items.length - max;\n if (excess <= 0) return;\n this.items.splice(0, excess);\n this.dropped += excess;\n const now = Date.now();\n if (now - this.lastDropWarn >= WARN_INTERVAL_MS) {\n internalWarn(\n `${this.opts.name}: buffer full (${max}), dropped ${this.dropped} oldest entr${this.dropped === 1 ? 'y' : 'ies'}`\n );\n this.dropped = 0;\n this.lastDropWarn = now;\n }\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { a as scrubbing, r as scrubError } from "./secret-config-Cng7XPLx.mjs";
1
+ import { i as scrubError, o as scrubbing } from "./secret-config-BFKJrSYF.mjs";
2
2
  import { r as internalWarn, t as internalError } from "./internal-log-Cy5nF1ZL.mjs";
3
3
  import * as http from "node:http";
4
4
  import * as https from "node:https";
@@ -147,4 +147,4 @@ var BatchBuffer = class {
147
147
  };
148
148
  export { httpRequest as a, HttpError as i, DEFAULT_MAX_BUFFER_SIZE as n, isPermanentHttpError as o, DEFAULT_REQUEST_TIMEOUT_MS as r, BatchBuffer as t };
149
149
 
150
- //# sourceMappingURL=batch-buffer-BnmVCCwI.mjs.map
150
+ //# sourceMappingURL=batch-buffer-CcubZ2cW.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"batch-buffer-BnmVCCwI.mjs","names":[],"sources":["../src/transports/batch-buffer.ts"],"sourcesContent":["/**\n * Shared batching core for the network transports (OTLP, CloudWatch, GCP, Azure).\n *\n * - one send in flight at a time; flush() and close() join it instead of\n * racing it, so close() cannot return while a request is still on the wire\n * - a failed batch goes back to the front in its original order\n * - 4xx responses other than 429 drop the batch with a warning instead of\n * retrying a request the server will never accept\n * - the buffer is capped; when full the oldest entries are dropped\n */\n\nimport * as http from 'node:http';\nimport * as https from 'node:https';\n\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { scrubbing, scrubError } from './secret-config';\n\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;\nexport const DEFAULT_MAX_BUFFER_SIZE = 10_000;\nconst WARN_INTERVAL_MS = 10_000;\nconst MAX_ERROR_BODY = 500;\nconst MAX_BACKOFF_MS = 30_000;\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n label: string\n ) {\n const detail = body ? `: ${body.slice(0, MAX_ERROR_BODY)}` : '';\n super(`${label} returned HTTP ${status}${detail}`);\n this.name = 'HttpError';\n }\n}\n\nexport function isPermanentHttpError(err: unknown): boolean {\n return err instanceof HttpError && err.status >= 400 && err.status < 500 && err.status !== 429;\n}\n\nexport interface HttpRequestOptions {\n url: URL;\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n timeoutMs: number;\n label: string;\n /** Secret values to scrub from any error this request raises. URL userinfo is always scrubbed. */\n secrets?: () => (string | undefined)[];\n}\n\nexport interface HttpResult {\n status: number;\n body: string;\n}\n\n/** One request with a hard timeout. Rejects with HttpError on any non-2xx status. */\nexport function httpRequest(opts: HttpRequestOptions): Promise<HttpResult> {\n return sendRequest(opts).catch(scrubbing(opts.secrets ?? (() => [])));\n}\n\nfunction sendRequest(opts: HttpRequestOptions): Promise<HttpResult> {\n const { url, method = 'POST', headers = {}, body, timeoutMs, label } = opts;\n const transport = url.protocol === 'http:' ? http : https;\n return new Promise<HttpResult>((resolve, reject) => {\n const req = transport.request(\n {\n protocol: url.protocol,\n hostname: url.hostname,\n port: url.port || undefined,\n path: `${url.pathname}${url.search}`,\n method,\n headers:\n body === undefined ? headers : { ...headers, 'Content-Length': Buffer.byteLength(body) },\n },\n (res) => {\n let raw = '';\n res.on('data', (c: Buffer) => (raw += c.toString()));\n res.on('end', () => {\n const status = res.statusCode ?? 0;\n if (status >= 200 && status < 300) resolve({ status, body: raw });\n else reject(new HttpError(status, raw, label));\n });\n res.on('error', reject);\n }\n );\n req.setTimeout(timeoutMs, () => {\n req.destroy(new Error(`${label} timed out after ${timeoutMs}ms`));\n });\n req.on('error', reject);\n if (body !== undefined) req.write(body);\n req.end();\n });\n}\n\nexport interface BatchBufferOptions<T> {\n name: string;\n batchSize: number;\n maxBufferSize?: number;\n send: (items: T[]) => Promise<void>;\n /** Secret values to scrub from send errors before they are stored or logged. */\n secrets?: () => (string | undefined)[];\n /** Overrides the default drop-on-4xx rule. Return true to keep and retry the batch. */\n shouldRetry?: (err: unknown) => boolean;\n}\n\nexport class BatchBuffer<T> {\n private items: T[] = [];\n private inFlight: Promise<void> | undefined;\n private dropped = 0;\n private lastDropWarn = 0;\n private failures = 0;\n private retryAfter = 0;\n\n constructor(private readonly opts: BatchBufferOptions<T>) {}\n\n get length(): number {\n return this.items.length;\n }\n\n get batchSize(): number {\n return this.opts.batchSize;\n }\n\n push(item: T): void {\n this.items.push(item);\n this.enforceCap();\n }\n\n /** Threshold flush for write(): skipped while a send is running or the endpoint is backing off. */\n flushIfFull(): void {\n if (this.items.length < this.opts.batchSize || this.inFlight || Date.now() < this.retryAfter) {\n return;\n }\n this.flush().catch(() => {});\n }\n\n flush(): Promise<void> {\n if (!this.inFlight) {\n this.inFlight = this.drain().finally(() => {\n this.inFlight = undefined;\n });\n }\n return this.inFlight;\n }\n\n /** Flush up to `attempts` times, then report whatever is still undelivered. */\n async close(attempts = 3): Promise<void> {\n await this.inFlight;\n for (let i = 0; i < attempts && this.items.length > 0; i += 1) {\n await this.flush();\n }\n if (this.items.length > 0) {\n internalError(\n `${this.opts.name} closing with ${this.items.length} undelivered entr${this.items.length === 1 ? 'y' : 'ies'}`\n );\n }\n }\n\n private async drain(): Promise<void> {\n while (this.items.length > 0) {\n const batch = this.items.splice(0, this.opts.batchSize);\n try {\n await this.opts.send(batch);\n this.failures = 0;\n } catch (err) {\n scrubError(err, this.opts.secrets?.() ?? []);\n const retry = this.opts.shouldRetry\n ? this.opts.shouldRetry(err)\n : !isPermanentHttpError(err);\n if (!retry) {\n internalWarn(\n `${this.opts.name}: dropping ${batch.length} entr${batch.length === 1 ? 'y' : 'ies'} the server rejected (${err instanceof Error ? err.message : String(err)})`\n );\n continue;\n }\n internalError(`${this.opts.name} flush error`, err);\n this.items.unshift(...batch);\n this.enforceCap();\n this.failures += 1;\n this.retryAfter = Date.now() + Math.min(MAX_BACKOFF_MS, 1000 * 2 ** (this.failures - 1));\n return;\n }\n }\n }\n\n private enforceCap(): void {\n const max = this.opts.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;\n const excess = this.items.length - max;\n if (excess <= 0) return;\n this.items.splice(0, excess);\n this.dropped += excess;\n const now = Date.now();\n if (now - this.lastDropWarn >= WARN_INTERVAL_MS) {\n internalWarn(\n `${this.opts.name}: buffer full (${max}), dropped ${this.dropped} oldest entr${this.dropped === 1 ? 'y' : 'ies'}`\n );\n this.dropped = 0;\n this.lastDropWarn = now;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"batch-buffer-CcubZ2cW.mjs","names":[],"sources":["../src/transports/batch-buffer.ts"],"sourcesContent":["/**\n * Shared batching core for the network transports (OTLP, CloudWatch, GCP, Azure).\n *\n * - one send in flight at a time; flush() and close() join it instead of\n * racing it, so close() cannot return while a request is still on the wire\n * - a failed batch goes back to the front in its original order\n * - 4xx responses other than 429 drop the batch with a warning instead of\n * retrying a request the server will never accept\n * - the buffer is capped; when full the oldest entries are dropped\n */\n\nimport * as http from 'node:http';\nimport * as https from 'node:https';\n\nimport { internalError, internalWarn } from '../utils/internal-log';\nimport { scrubbing, scrubError } from './secret-config';\n\nexport const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;\nexport const DEFAULT_MAX_BUFFER_SIZE = 10_000;\nconst WARN_INTERVAL_MS = 10_000;\nconst MAX_ERROR_BODY = 500;\nconst MAX_BACKOFF_MS = 30_000;\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n label: string\n ) {\n const detail = body ? `: ${body.slice(0, MAX_ERROR_BODY)}` : '';\n super(`${label} returned HTTP ${status}${detail}`);\n this.name = 'HttpError';\n }\n}\n\nexport function isPermanentHttpError(err: unknown): boolean {\n return err instanceof HttpError && err.status >= 400 && err.status < 500 && err.status !== 429;\n}\n\nexport interface HttpRequestOptions {\n url: URL;\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n timeoutMs: number;\n label: string;\n /** Secret values to scrub from any error this request raises. URL userinfo is always scrubbed. */\n secrets?: () => (string | undefined)[];\n}\n\nexport interface HttpResult {\n status: number;\n body: string;\n}\n\n/** One request with a hard timeout. Rejects with HttpError on any non-2xx status. */\nexport function httpRequest(opts: HttpRequestOptions): Promise<HttpResult> {\n return sendRequest(opts).catch(scrubbing(opts.secrets ?? (() => [])));\n}\n\nfunction sendRequest(opts: HttpRequestOptions): Promise<HttpResult> {\n const { url, method = 'POST', headers = {}, body, timeoutMs, label } = opts;\n const transport = url.protocol === 'http:' ? http : https;\n return new Promise<HttpResult>((resolve, reject) => {\n const req = transport.request(\n {\n protocol: url.protocol,\n hostname: url.hostname,\n port: url.port || undefined,\n path: `${url.pathname}${url.search}`,\n method,\n headers:\n body === undefined ? headers : { ...headers, 'Content-Length': Buffer.byteLength(body) },\n },\n (res) => {\n let raw = '';\n res.on('data', (c: Buffer) => (raw += c.toString()));\n res.on('end', () => {\n const status = res.statusCode ?? 0;\n if (status >= 200 && status < 300) resolve({ status, body: raw });\n else reject(new HttpError(status, raw, label));\n });\n res.on('error', reject);\n }\n );\n req.setTimeout(timeoutMs, () => {\n req.destroy(new Error(`${label} timed out after ${timeoutMs}ms`));\n });\n req.on('error', reject);\n if (body !== undefined) req.write(body);\n req.end();\n });\n}\n\nexport interface BatchBufferOptions<T> {\n name: string;\n batchSize: number;\n maxBufferSize?: number;\n send: (items: T[]) => Promise<void>;\n /** Secret values to scrub from send errors before they are stored or logged. */\n secrets?: () => (string | undefined)[];\n /** Overrides the default drop-on-4xx rule. Return true to keep and retry the batch. */\n shouldRetry?: (err: unknown) => boolean;\n}\n\nexport class BatchBuffer<T> {\n private items: T[] = [];\n private inFlight: Promise<void> | undefined;\n private dropped = 0;\n private lastDropWarn = 0;\n private failures = 0;\n private retryAfter = 0;\n\n constructor(private readonly opts: BatchBufferOptions<T>) {}\n\n get length(): number {\n return this.items.length;\n }\n\n get batchSize(): number {\n return this.opts.batchSize;\n }\n\n push(item: T): void {\n this.items.push(item);\n this.enforceCap();\n }\n\n /** Threshold flush for write(): skipped while a send is running or the endpoint is backing off. */\n flushIfFull(): void {\n if (this.items.length < this.opts.batchSize || this.inFlight || Date.now() < this.retryAfter) {\n return;\n }\n this.flush().catch(() => {});\n }\n\n flush(): Promise<void> {\n if (!this.inFlight) {\n this.inFlight = this.drain().finally(() => {\n this.inFlight = undefined;\n });\n }\n return this.inFlight;\n }\n\n /** Flush up to `attempts` times, then report whatever is still undelivered. */\n async close(attempts = 3): Promise<void> {\n await this.inFlight;\n for (let i = 0; i < attempts && this.items.length > 0; i += 1) {\n await this.flush();\n }\n if (this.items.length > 0) {\n internalError(\n `${this.opts.name} closing with ${this.items.length} undelivered entr${this.items.length === 1 ? 'y' : 'ies'}`\n );\n }\n }\n\n private async drain(): Promise<void> {\n while (this.items.length > 0) {\n const batch = this.items.splice(0, this.opts.batchSize);\n try {\n await this.opts.send(batch);\n this.failures = 0;\n } catch (err) {\n scrubError(err, this.opts.secrets?.() ?? []);\n const retry = this.opts.shouldRetry\n ? this.opts.shouldRetry(err)\n : !isPermanentHttpError(err);\n if (!retry) {\n internalWarn(\n `${this.opts.name}: dropping ${batch.length} entr${batch.length === 1 ? 'y' : 'ies'} the server rejected (${err instanceof Error ? err.message : String(err)})`\n );\n continue;\n }\n internalError(`${this.opts.name} flush error`, err);\n this.items.unshift(...batch);\n this.enforceCap();\n this.failures += 1;\n this.retryAfter = Date.now() + Math.min(MAX_BACKOFF_MS, 1000 * 2 ** (this.failures - 1));\n return;\n }\n }\n }\n\n private enforceCap(): void {\n const max = this.opts.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;\n const excess = this.items.length - max;\n if (excess <= 0) return;\n this.items.splice(0, excess);\n this.dropped += excess;\n const now = Date.now();\n if (now - this.lastDropWarn >= WARN_INTERVAL_MS) {\n internalWarn(\n `${this.opts.name}: buffer full (${max}), dropped ${this.dropped} oldest entr${this.dropped === 1 ? 'y' : 'ies'}`\n );\n this.dropped = 0;\n this.lastDropWarn = now;\n }\n }\n}\n"],"mappings":"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"}
package/dist/browser.js CHANGED
@@ -174,9 +174,10 @@ function redactConfig(value, depth = 0, key = "") {
174
174
  const hiddenKeys = /* @__PURE__ */ new WeakMap();
175
175
  const hiddenValues = /* @__PURE__ */ new WeakMap();
176
176
  function viewOf(target) {
177
+ return buildView(target, hiddenKeys.get(target) ?? /* @__PURE__ */ new Set(), hiddenValues.get(target));
178
+ }
179
+ function buildView(target, hidden, values) {
177
180
  var _target$constructor;
178
- const hidden = hiddenKeys.get(target) ?? /* @__PURE__ */ new Set();
179
- const values = hiddenValues.get(target);
180
181
  const view = { class: (_target$constructor = target.constructor) === null || _target$constructor === void 0 ? void 0 : _target$constructor.name };
181
182
  for (const key of /* @__PURE__ */ new Set([...Object.keys(target), ...hidden])) {
182
183
  const inner = hidden.has(key) ? values === null || values === void 0 ? void 0 : values.get(key) : target[key];