logixia 2.0.0 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/README.md +172 -2597
  2. package/dist/async-context-2mbUKhcl.mjs +132 -0
  3. package/dist/async-context-2mbUKhcl.mjs.map +1 -0
  4. package/dist/async-context-DUFMDYAr.js +148 -0
  5. package/dist/async-context-DUFMDYAr.js.map +1 -0
  6. package/dist/{azure-monitor.transport-CrvfHd9C.js → azure-monitor.transport-Bu4S2I2q.js} +32 -20
  7. package/dist/azure-monitor.transport-Bu4S2I2q.js.map +1 -0
  8. package/dist/{azure-monitor.transport-Cn_3Usvu.d.ts → azure-monitor.transport-Bvf7Xegt.d.ts} +3 -6
  9. package/dist/azure-monitor.transport-Bvf7Xegt.d.ts.map +1 -0
  10. package/dist/{azure-monitor.transport-CHnjVRF-.mjs → azure-monitor.transport-C1RWAumT.mjs} +27 -15
  11. package/dist/azure-monitor.transport-C1RWAumT.mjs.map +1 -0
  12. package/dist/{azure-monitor.transport-D7Da4RAb.d.mts → azure-monitor.transport-CbSHOnNX.d.mts} +3 -6
  13. package/dist/azure-monitor.transport-CbSHOnNX.d.mts.map +1 -0
  14. package/dist/azure.d.mts +1 -2
  15. package/dist/azure.d.ts +1 -2
  16. package/dist/azure.js +3 -6
  17. package/dist/azure.mjs +1 -6
  18. package/dist/{batch-buffer-PGvSgbmd.mjs → batch-buffer-BvXLDPlV.mjs} +21 -7
  19. package/dist/batch-buffer-BvXLDPlV.mjs.map +1 -0
  20. package/dist/{batch-buffer-BY-mRWIK.js → batch-buffer-C7Am4Mw-.js} +53 -40
  21. package/dist/batch-buffer-C7Am4Mw-.js.map +1 -0
  22. package/dist/browser.d.mts +47 -27
  23. package/dist/browser.d.mts.map +1 -1
  24. package/dist/browser.d.ts +47 -27
  25. package/dist/browser.d.ts.map +1 -1
  26. package/dist/browser.js +464 -62
  27. package/dist/browser.js.map +1 -1
  28. package/dist/browser.mjs +465 -65
  29. package/dist/browser.mjs.map +1 -1
  30. package/dist/{builder-0gEzRopq.mjs → builder-DhNhvOkt.mjs} +307 -246
  31. package/dist/builder-DhNhvOkt.mjs.map +1 -0
  32. package/dist/{builder-D44QktIo.js → builder-yFvJrz7o.js} +435 -399
  33. package/dist/builder-yFvJrz7o.js.map +1 -0
  34. package/dist/cli/index.js +216 -125
  35. package/dist/cloudwatch.d.mts +1 -2
  36. package/dist/cloudwatch.d.ts +1 -2
  37. package/dist/cloudwatch.js +3 -7
  38. package/dist/cloudwatch.mjs +1 -7
  39. package/dist/{cloudwatch.transport-DsEq633o.d.ts → cloudwatch.transport-CQyT6U91.d.ts} +2 -6
  40. package/dist/cloudwatch.transport-CQyT6U91.d.ts.map +1 -0
  41. package/dist/{cloudwatch.transport-BO1ahxw-.js → cloudwatch.transport-Ch1Uae-0.js} +61 -36
  42. package/dist/cloudwatch.transport-Ch1Uae-0.js.map +1 -0
  43. package/dist/{cloudwatch.transport-UlyZFWMn.d.mts → cloudwatch.transport-CjVrI6S4.d.mts} +2 -6
  44. package/dist/cloudwatch.transport-CjVrI6S4.d.mts.map +1 -0
  45. package/dist/{cloudwatch.transport-DZ6ies7g.mjs → cloudwatch.transport-DZqF2xZO.mjs} +54 -28
  46. package/dist/cloudwatch.transport-DZqF2xZO.mjs.map +1 -0
  47. package/dist/{coerce.utils-Cu6IUTgX.mjs → coerce.utils-B_rEudCn.mjs} +3 -4
  48. package/dist/{coerce.utils-Cu6IUTgX.mjs.map → coerce.utils-B_rEudCn.mjs.map} +1 -1
  49. package/dist/{coerce.utils-B-LilukN.js → coerce.utils-C1C5sIX0.js} +8 -10
  50. package/dist/{coerce.utils-B-LilukN.js.map → coerce.utils-C1C5sIX0.js.map} +1 -1
  51. package/dist/correlation.d.mts +15 -69
  52. package/dist/correlation.d.mts.map +1 -1
  53. package/dist/correlation.d.ts +15 -69
  54. package/dist/correlation.d.ts.map +1 -1
  55. package/dist/correlation.js +69 -20
  56. package/dist/correlation.js.map +1 -1
  57. package/dist/correlation.mjs +67 -17
  58. package/dist/correlation.mjs.map +1 -1
  59. package/dist/{entry-fields-BvOLFlJ4.js → entry-fields-BSLkzw18.js} +16 -13
  60. package/dist/entry-fields-BSLkzw18.js.map +1 -0
  61. package/dist/{entry-fields-BJkuPnIg.mjs → entry-fields-J1OBi24o.mjs} +11 -7
  62. package/dist/entry-fields-J1OBi24o.mjs.map +1 -0
  63. package/dist/gcp.d.mts +1 -2
  64. package/dist/gcp.d.ts +1 -2
  65. package/dist/gcp.js +3 -7
  66. package/dist/gcp.mjs +1 -7
  67. package/dist/{gcp.transport-B7oc97sG.d.ts → gcp.transport-BCIvdFV-.d.ts} +5 -8
  68. package/dist/gcp.transport-BCIvdFV-.d.ts.map +1 -0
  69. package/dist/{gcp.transport-GrJurKo8.d.mts → gcp.transport-D9SdLPwP.d.mts} +5 -8
  70. package/dist/gcp.transport-D9SdLPwP.d.mts.map +1 -0
  71. package/dist/{gcp.transport-DBX5TPIy.mjs → gcp.transport-DVCmnA3b.mjs} +59 -16
  72. package/dist/gcp.transport-DVCmnA3b.mjs.map +1 -0
  73. package/dist/{gcp.transport-CHV8MGgz.js → gcp.transport-Dy2Lf8FI.js} +64 -22
  74. package/dist/gcp.transport-Dy2Lf8FI.js.map +1 -0
  75. package/dist/http-shapes-BnBkJcid.d.mts +32 -0
  76. package/dist/http-shapes-BnBkJcid.d.mts.map +1 -0
  77. package/dist/http-shapes-BnBkJcid.d.ts +32 -0
  78. package/dist/http-shapes-BnBkJcid.d.ts.map +1 -0
  79. package/dist/{index-3mw5gaCw.d.mts → index-BSTtFcQ6.d.mts} +142 -13
  80. package/dist/index-BSTtFcQ6.d.mts.map +1 -0
  81. package/dist/{index-DhIoploR.d.ts → index-CFDGkI8n.d.ts} +142 -13
  82. package/dist/index-CFDGkI8n.d.ts.map +1 -0
  83. package/dist/index.d.mts +114 -217
  84. package/dist/index.d.mts.map +1 -1
  85. package/dist/index.d.ts +113 -216
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +182 -279
  88. package/dist/index.js.map +1 -1
  89. package/dist/index.mjs +160 -256
  90. package/dist/index.mjs.map +1 -1
  91. package/dist/internal-log-Bg4V1cPg.mjs +170 -0
  92. package/dist/internal-log-Bg4V1cPg.mjs.map +1 -0
  93. package/dist/internal-log-By4Govmu.js +223 -0
  94. package/dist/internal-log-By4Govmu.js.map +1 -0
  95. package/dist/{logitron-logger-UVeN-mQm.d.ts → logitron-logger-D22Yb61K.d.mts} +121 -100
  96. package/dist/logitron-logger-D22Yb61K.d.mts.map +1 -0
  97. package/dist/{logitron-logger-BFDSO8Io.d.mts → logitron-logger-D_MYYrQt.d.ts} +121 -100
  98. package/dist/logitron-logger-D_MYYrQt.d.ts.map +1 -0
  99. package/dist/middleware.d.mts +13 -28
  100. package/dist/middleware.d.mts.map +1 -1
  101. package/dist/middleware.d.ts +13 -28
  102. package/dist/middleware.d.ts.map +1 -1
  103. package/dist/middleware.js +20 -81
  104. package/dist/middleware.js.map +1 -1
  105. package/dist/middleware.mjs +17 -79
  106. package/dist/middleware.mjs.map +1 -1
  107. package/dist/nest.d.mts +37 -48
  108. package/dist/nest.d.mts.map +1 -1
  109. package/dist/nest.d.ts +37 -48
  110. package/dist/nest.d.ts.map +1 -1
  111. package/dist/nest.js +124 -135
  112. package/dist/nest.js.map +1 -1
  113. package/dist/nest.mjs +69 -78
  114. package/dist/nest.mjs.map +1 -1
  115. package/dist/{transport.manager-BiuCAJd2.mjs → otel-Dt5riXuE.mjs} +275 -117
  116. package/dist/otel-Dt5riXuE.mjs.map +1 -0
  117. package/dist/{transport.manager-CXbKKdVE.js → otel-DyeH6DVR.js} +373 -178
  118. package/dist/otel-DyeH6DVR.js.map +1 -0
  119. package/dist/own-property-BWkibepN.js +40 -0
  120. package/dist/own-property-BWkibepN.js.map +1 -0
  121. package/dist/own-property-CjFw9qwZ.mjs +29 -0
  122. package/dist/own-property-CjFw9qwZ.mjs.map +1 -0
  123. package/dist/{trace.utils-CvXgaiQT.mjs → request-trace-BgNykH9M.mjs} +40 -9
  124. package/dist/request-trace-BgNykH9M.mjs.map +1 -0
  125. package/dist/{trace.utils-DpBOLDtq.js → request-trace-ByujsUZk.js} +117 -77
  126. package/dist/request-trace-ByujsUZk.js.map +1 -0
  127. package/dist/rolldown-runtime-D3PmRqjj.mjs +4 -0
  128. package/dist/{safe-stringify-DJ66tW5I.mjs → safe-stringify-B5p5ktFe.mjs} +39 -15
  129. package/dist/safe-stringify-B5p5ktFe.mjs.map +1 -0
  130. package/dist/{safe-stringify-DWo3aZp_.js → safe-stringify-J3sh-7YM.js} +59 -36
  131. package/dist/safe-stringify-J3sh-7YM.js.map +1 -0
  132. package/dist/{search-C4V0OMEn.js → search-CRjhHxe9.js} +319 -92
  133. package/dist/search-CRjhHxe9.js.map +1 -0
  134. package/dist/{search-CNh9uVwR.d.ts → search-DA3ft3X5.d.mts} +5 -22
  135. package/dist/search-DA3ft3X5.d.mts.map +1 -0
  136. package/dist/{search-BFbu_oVn.mjs → search-DAXKE-P9.mjs} +289 -62
  137. package/dist/search-DAXKE-P9.mjs.map +1 -0
  138. package/dist/{search-DXANsQ2n.d.mts → search-DEl3Pe47.d.ts} +5 -22
  139. package/dist/search-DEl3Pe47.d.ts.map +1 -0
  140. package/dist/search.d.mts +2 -4
  141. package/dist/search.d.ts +2 -4
  142. package/dist/search.js +3 -4
  143. package/dist/search.mjs +1 -4
  144. package/dist/secret-config-CmK2wVkr.js +272 -0
  145. package/dist/secret-config-CmK2wVkr.js.map +1 -0
  146. package/dist/secret-config-Dhb6pqTU.mjs +210 -0
  147. package/dist/secret-config-Dhb6pqTU.mjs.map +1 -0
  148. package/dist/{slack.transport-CXjQYOes.d.ts → slack.transport-BDyENuR0.d.mts} +56 -9
  149. package/dist/slack.transport-BDyENuR0.d.mts.map +1 -0
  150. package/dist/{slack.transport-Bzw7sucI.mjs → slack.transport-BTEwsREY.mjs} +263 -31
  151. package/dist/slack.transport-BTEwsREY.mjs.map +1 -0
  152. package/dist/{slack.transport-BZzoXDPn.d.mts → slack.transport-C6E0l4kw.d.ts} +56 -9
  153. package/dist/slack.transport-C6E0l4kw.d.ts.map +1 -0
  154. package/dist/{slack.transport-BfJRouB2.js → slack.transport-UbYvOdKx.js} +286 -42
  155. package/dist/slack.transport-UbYvOdKx.js.map +1 -0
  156. package/dist/testing.d.mts +13 -11
  157. package/dist/testing.d.mts.map +1 -1
  158. package/dist/testing.d.ts +13 -11
  159. package/dist/testing.d.ts.map +1 -1
  160. package/dist/testing.js +41 -7
  161. package/dist/testing.js.map +1 -1
  162. package/dist/testing.mjs +40 -6
  163. package/dist/testing.mjs.map +1 -1
  164. package/dist/{transport.types-CGoHpWQR.d.ts → transport.types-CzJq4IrF.d.mts} +14 -5
  165. package/dist/transport.types-CzJq4IrF.d.mts.map +1 -0
  166. package/dist/{transport.types-BMnurPcc.d.mts → transport.types-CzJq4IrF.d.ts} +14 -5
  167. package/dist/transport.types-CzJq4IrF.d.ts.map +1 -0
  168. package/dist/transports.d.mts +39 -41
  169. package/dist/transports.d.mts.map +1 -1
  170. package/dist/transports.d.ts +39 -41
  171. package/dist/transports.d.ts.map +1 -1
  172. package/dist/transports.js +19 -22
  173. package/dist/transports.mjs +7 -13
  174. package/dist/worker-entry.js +438 -107
  175. package/dist/worker.d.mts +1 -2
  176. package/dist/worker.d.ts +1 -2
  177. package/dist/worker.js +3 -4
  178. package/dist/worker.mjs +1 -4
  179. package/dist/{worker.transport-JvGPog3C.d.mts → worker.transport-B9mCQSM1.d.ts} +7 -6
  180. package/dist/worker.transport-B9mCQSM1.d.ts.map +1 -0
  181. package/dist/{worker.transport-CdzSZJmw.d.ts → worker.transport-Bl8lFRck.d.mts} +7 -6
  182. package/dist/worker.transport-Bl8lFRck.d.mts.map +1 -0
  183. package/dist/{worker.transport-lPxO2MVL.mjs → worker.transport-BmmOKb1-.mjs} +43 -10
  184. package/dist/worker.transport-BmmOKb1-.mjs.map +1 -0
  185. package/dist/{worker.transport-BdPxu3PN.js → worker.transport-DxxbqQCG.js} +48 -15
  186. package/dist/worker.transport-DxxbqQCG.js.map +1 -0
  187. package/package.json +51 -75
  188. package/dist/async-context-BzJrbfQs.mjs +0 -72
  189. package/dist/async-context-BzJrbfQs.mjs.map +0 -1
  190. package/dist/async-context-DyQQYq3I.js +0 -92
  191. package/dist/async-context-DyQQYq3I.js.map +0 -1
  192. package/dist/azure-monitor.transport-CHnjVRF-.mjs.map +0 -1
  193. package/dist/azure-monitor.transport-Cn_3Usvu.d.ts.map +0 -1
  194. package/dist/azure-monitor.transport-CrvfHd9C.js.map +0 -1
  195. package/dist/azure-monitor.transport-D7Da4RAb.d.mts.map +0 -1
  196. package/dist/batch-buffer-BY-mRWIK.js.map +0 -1
  197. package/dist/batch-buffer-PGvSgbmd.mjs.map +0 -1
  198. package/dist/builder-0gEzRopq.mjs.map +0 -1
  199. package/dist/builder-D44QktIo.js.map +0 -1
  200. package/dist/chunk-DLSkYcPk.mjs +0 -7
  201. package/dist/chunk-nOFOJqeH.js +0 -30
  202. package/dist/cloudwatch.transport-BO1ahxw-.js.map +0 -1
  203. package/dist/cloudwatch.transport-DZ6ies7g.mjs.map +0 -1
  204. package/dist/cloudwatch.transport-DsEq633o.d.ts.map +0 -1
  205. package/dist/cloudwatch.transport-UlyZFWMn.d.mts.map +0 -1
  206. package/dist/entry-fields-BJkuPnIg.mjs.map +0 -1
  207. package/dist/entry-fields-BvOLFlJ4.js.map +0 -1
  208. package/dist/gcp.transport-B7oc97sG.d.ts.map +0 -1
  209. package/dist/gcp.transport-CHV8MGgz.js.map +0 -1
  210. package/dist/gcp.transport-DBX5TPIy.mjs.map +0 -1
  211. package/dist/gcp.transport-GrJurKo8.d.mts.map +0 -1
  212. package/dist/index-3mw5gaCw.d.mts.map +0 -1
  213. package/dist/index-DhIoploR.d.ts.map +0 -1
  214. package/dist/internal-log-BUJYMZbE.js +0 -70
  215. package/dist/internal-log-BUJYMZbE.js.map +0 -1
  216. package/dist/internal-log-Cm8SOWSV.mjs +0 -52
  217. package/dist/internal-log-Cm8SOWSV.mjs.map +0 -1
  218. package/dist/logitron-logger-BFDSO8Io.d.mts.map +0 -1
  219. package/dist/logitron-logger-UVeN-mQm.d.ts.map +0 -1
  220. package/dist/safe-stringify-DJ66tW5I.mjs.map +0 -1
  221. package/dist/safe-stringify-DWo3aZp_.js.map +0 -1
  222. package/dist/search-BFbu_oVn.mjs.map +0 -1
  223. package/dist/search-C4V0OMEn.js.map +0 -1
  224. package/dist/search-CNh9uVwR.d.ts.map +0 -1
  225. package/dist/search-DXANsQ2n.d.mts.map +0 -1
  226. package/dist/slack.transport-BZzoXDPn.d.mts.map +0 -1
  227. package/dist/slack.transport-BfJRouB2.js.map +0 -1
  228. package/dist/slack.transport-Bzw7sucI.mjs.map +0 -1
  229. package/dist/slack.transport-CXjQYOes.d.ts.map +0 -1
  230. package/dist/trace.utils-CvXgaiQT.mjs.map +0 -1
  231. package/dist/trace.utils-DpBOLDtq.js.map +0 -1
  232. package/dist/transport.manager-BiuCAJd2.mjs.map +0 -1
  233. package/dist/transport.manager-CXbKKdVE.js.map +0 -1
  234. package/dist/transport.types-BMnurPcc.d.mts.map +0 -1
  235. package/dist/transport.types-CGoHpWQR.d.ts.map +0 -1
  236. package/dist/worker.transport-BdPxu3PN.js.map +0 -1
  237. package/dist/worker.transport-CdzSZJmw.d.ts.map +0 -1
  238. package/dist/worker.transport-JvGPog3C.d.mts.map +0 -1
  239. package/dist/worker.transport-lPxO2MVL.mjs.map +0 -1
@@ -1,8 +1,59 @@
1
- import { d as sanitizeIncomingId } from "./trace.utils-CvXgaiQT.mjs";
2
- import { t as LogixiaContext } from "./async-context-BzJrbfQs.mjs";
1
+ import { p as sanitizeIncomingId } from "./request-trace-BgNykH9M.mjs";
2
+ import { t as LogixiaContext } from "./async-context-2mbUKhcl.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
-
5
- //#region src/correlation.ts
4
+ // src/correlation.ts
5
+ /**
6
+ * logixia — Microservices Correlation ID Propagation
7
+ *
8
+ * Provides zero-boilerplate correlation ID generation and propagation for
9
+ * distributed systems. A single correlation ID travels through every service
10
+ * in a request fan-out, making it trivial to join logs across services.
11
+ *
12
+ * Features:
13
+ * - UUID v4 correlation ID generation (via `crypto.randomUUID()`)
14
+ * - Express / Fastify / NestJS incoming-request middleware
15
+ * - `correlationFetch()` — wraps global `fetch`, auto-injects `X-Correlation-ID`
16
+ * - `createCorrelationAxiosInterceptor()` — axios request interceptor
17
+ * - `childFromRequest(req, logger)` — child logger with all request identifiers
18
+ * - Kafka / SQS / RabbitMQ message context helpers
19
+ * - Integration with `LogixiaContext` (AsyncLocalStorage) so every `logger.info()`
20
+ * call automatically includes the correlationId without per-call passing.
21
+ *
22
+ * @example Express middleware
23
+ * ```ts
24
+ * import { correlationMiddleware } from 'logixia/correlation';
25
+ * import { logger } from 'logixia';
26
+ *
27
+ * app.use(correlationMiddleware());
28
+ *
29
+ * app.get('/orders', (req, res) => {
30
+ * // correlationId is automatically in every log inside this request
31
+ * logger.info('Listing orders');
32
+ * res.json({ ok: true });
33
+ * });
34
+ * ```
35
+ *
36
+ * @example Outbound fetch with correlation
37
+ * ```ts
38
+ * import { correlationFetch } from 'logixia/correlation';
39
+ *
40
+ * // Reads correlationId from current LogixiaContext and injects it as header
41
+ * const data = await correlationFetch('https://payments.internal/charge', {
42
+ * method: 'POST',
43
+ * body: JSON.stringify({ amount: 100 }),
44
+ * });
45
+ * ```
46
+ *
47
+ * @example Manual correlation context
48
+ * ```ts
49
+ * import { withCorrelationId } from 'logixia/correlation';
50
+ *
51
+ * await withCorrelationId('trace-abc-123', async () => {
52
+ * // All logs and outbound fetch calls in here carry 'trace-abc-123'
53
+ * await processOrder();
54
+ * });
55
+ * ```
56
+ */
6
57
  /**
7
58
  * Generate a new correlation ID using `crypto.randomUUID()` (available in
8
59
  * Node.js 14.17+, all modern browsers, and Edge runtimes).
@@ -68,12 +119,12 @@ function withCorrelationId(correlationId, callback) {
68
119
  function correlationMiddleware(options = {}) {
69
120
  const { header = "x-correlation-id", originServiceHeader = "x-origin-service", generate = generateCorrelationId, trustIncoming = true, setResponseHeader = true } = options;
70
121
  return function logixiaCorrelationMiddleware(req, res, next) {
71
- const headers = req["headers"] ?? {};
122
+ const headers = req.headers ?? {};
72
123
  const correlationId = (trustIncoming ? validIncomingId(headers[header.toLowerCase()]) : void 0) ?? generate();
73
124
  const originService = validIncomingId(headers[originServiceHeader.toLowerCase()]);
74
125
  if (setResponseHeader) {
75
- const setHeader = res["setHeader"];
76
- if (typeof setHeader === "function") setHeader.call(res, header, correlationId);
126
+ var _res$setHeader;
127
+ (_res$setHeader = res.setHeader) === null || _res$setHeader === void 0 || _res$setHeader.call(res, header, correlationId);
77
128
  }
78
129
  const ctx = {
79
130
  correlationId,
@@ -95,12 +146,12 @@ function correlationMiddleware(options = {}) {
95
146
  function correlationFastifyHook(options = {}) {
96
147
  const { header = "x-correlation-id", originServiceHeader = "x-origin-service", generate = generateCorrelationId, trustIncoming = true, setResponseHeader = true } = options;
97
148
  return function logixiaCorrelationFastifyHook(request, reply, done) {
98
- const headers = request["headers"] ?? {};
149
+ const headers = request.headers ?? {};
99
150
  const correlationId = (trustIncoming ? validIncomingId(headers[header.toLowerCase()]) : void 0) ?? generate();
100
151
  const originService = validIncomingId(headers[originServiceHeader.toLowerCase()]);
101
152
  if (setResponseHeader) {
102
- const replyHeader = reply["header"];
103
- if (typeof replyHeader === "function") replyHeader.call(reply, header, correlationId);
153
+ var _reply$header;
154
+ (_reply$header = reply.header) === null || _reply$header === void 0 || _reply$header.call(reply, header, correlationId);
104
155
  }
105
156
  const ctx = {
106
157
  correlationId,
@@ -190,10 +241,10 @@ const MAX_IP_LENGTH = 64;
190
241
  */
191
242
  function childFromRequest(req, logger, options = {}) {
192
243
  const correlationHeader = options.correlationHeader ?? "x-correlation-id";
193
- const headers = req["headers"] ?? {};
244
+ const headers = req.headers ?? {};
194
245
  const correlationId = validIncomingId(headers[correlationHeader.toLowerCase()]) ?? getCurrentCorrelationId() ?? generateCorrelationId();
195
- const method = req["method"];
196
- const url = req["url"] ?? req["originalUrl"];
246
+ const method = req.method;
247
+ const url = req.url ?? req.originalUrl;
197
248
  const rawUserAgent = headers["user-agent"];
198
249
  const userAgent = typeof rawUserAgent === "string" ? rawUserAgent.slice(0, MAX_USER_AGENT_LENGTH) : void 0;
199
250
  const ip = resolveClientIp(req, headers, options.trustProxy === true);
@@ -222,9 +273,9 @@ function resolveClientIp(req, headers, trustProxy) {
222
273
  const first = typeof forwarded === "string" ? (_forwarded$split$ = forwarded.split(",")[0]) === null || _forwarded$split$ === void 0 ? void 0 : _forwarded$split$.trim() : void 0;
223
274
  if (first) return first.slice(0, MAX_IP_LENGTH);
224
275
  }
225
- const fromReq = req["ip"];
276
+ const fromReq = req.ip;
226
277
  if (typeof fromReq === "string" && fromReq) return fromReq.slice(0, MAX_IP_LENGTH);
227
- const remote = (_req$socket = req["socket"]) === null || _req$socket === void 0 ? void 0 : _req$socket["remoteAddress"];
278
+ const remote = (_req$socket = req.socket) === null || _req$socket === void 0 ? void 0 : _req$socket.remoteAddress;
228
279
  return typeof remote === "string" ? remote.slice(0, MAX_IP_LENGTH) : void 0;
229
280
  }
230
281
  /**
@@ -280,7 +331,6 @@ function buildKafkaCorrelationHeaders() {
280
331
  if (traceId) headers["x-trace-id"] = traceId;
281
332
  return headers;
282
333
  }
283
-
284
- //#endregion
285
334
  export { buildKafkaCorrelationHeaders, childFromRequest, correlationFastifyHook, correlationFetch, correlationMiddleware, createCorrelationAxiosInterceptor, extractMessageCorrelationId, generateCorrelationId, getCurrentCorrelationId, withCorrelationId };
335
+
286
336
  //# sourceMappingURL=correlation.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"correlation.mjs","names":["ctx: CorrelationContext","headers: Record<string, string>"],"sources":["../src/correlation.ts"],"sourcesContent":["/**\n * logixia — Microservices Correlation ID Propagation\n *\n * Provides zero-boilerplate correlation ID generation and propagation for\n * distributed systems. A single correlation ID travels through every service\n * in a request fan-out, making it trivial to join logs across services.\n *\n * Features:\n * - UUID v4 correlation ID generation (via `crypto.randomUUID()`)\n * - Express / Fastify / NestJS incoming-request middleware\n * - `correlationFetch()` — wraps global `fetch`, auto-injects `X-Correlation-ID`\n * - `createCorrelationAxiosInterceptor()` — axios request interceptor\n * - `childFromRequest(req, logger)` — child logger with all request identifiers\n * - Kafka / SQS / RabbitMQ message context helpers\n * - Integration with `LogixiaContext` (AsyncLocalStorage) so every `logger.info()`\n * call automatically includes the correlationId without per-call passing.\n *\n * @example Express middleware\n * ```ts\n * import { correlationMiddleware } from 'logixia/correlation';\n * import { logger } from 'logixia';\n *\n * app.use(correlationMiddleware());\n *\n * app.get('/orders', (req, res) => {\n * // correlationId is automatically in every log inside this request\n * logger.info('Listing orders');\n * res.json({ ok: true });\n * });\n * ```\n *\n * @example Outbound fetch with correlation\n * ```ts\n * import { correlationFetch } from 'logixia/correlation';\n *\n * // Reads correlationId from current LogixiaContext and injects it as header\n * const data = await correlationFetch('https://payments.internal/charge', {\n * method: 'POST',\n * body: JSON.stringify({ amount: 100 }),\n * });\n * ```\n *\n * @example Manual correlation context\n * ```ts\n * import { withCorrelationId } from 'logixia/correlation';\n *\n * await withCorrelationId('trace-abc-123', async () => {\n * // All logs and outbound fetch calls in here carry 'trace-abc-123'\n * await processOrder();\n * });\n * ```\n */\n\nimport { randomUUID } from 'node:crypto';\n\nimport { LogixiaContext } from './context/async-context';\nimport { sanitizeIncomingId } from './utils/trace.utils';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\n/** Fields injected into the log context by the correlation middleware. */\nexport interface CorrelationContext {\n correlationId: string;\n traceId?: string;\n /** Service that originated the request (from `X-Origin-Service` header). */\n originService?: string;\n /** Index signature so CorrelationContext is assignable to LogContext. */\n [key: string]: unknown;\n}\n\nexport interface CorrelationMiddlewareOptions {\n /**\n * HTTP header to read/write the correlation ID from/to.\n * @default 'x-correlation-id'\n */\n header?: string;\n /**\n * HTTP header for the originating service name.\n * @default 'x-origin-service'\n */\n originServiceHeader?: string;\n /**\n * Function to generate a new correlation ID when none is present in the request.\n * @default () => crypto.randomUUID()\n */\n generate?: () => string;\n /**\n * When `false`, the incoming correlation header is ignored and a new ID is\n * always generated. Use this at the edge when client-supplied IDs should not\n * be trusted.\n * @default true\n */\n trustIncoming?: boolean;\n /**\n * When `true`, the response will include the correlation ID header so\n * clients can log it client-side.\n * @default true\n */\n setResponseHeader?: boolean;\n}\n\n// ── ID generation ─────────────────────────────────────────────────────────────\n\n/**\n * Generate a new correlation ID using `crypto.randomUUID()` (available in\n * Node.js 14.17+, all modern browsers, and Edge runtimes).\n *\n * When the Web Crypto global is unavailable (e.g. Node 18 without\n * `--experimental-global-webcrypto`), fall back to `node:crypto`'s\n * `randomUUID`, which returns a proper UUID v4 — matching the shape this\n * function's JSDoc and README promise. `logixia/correlation` is Node-only\n * (it builds on `AsyncLocalStorage`), so importing `node:crypto` is safe here.\n */\nexport function generateCorrelationId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return randomUUID();\n}\n\n/**\n * Return an incoming correlation ID only if it is short and made of safe\n * characters. Anything else (over-long, empty, or containing newlines or other\n * control characters) is dropped so the caller generates a fresh ID instead of\n * writing client-supplied text into every log line.\n */\nfunction validIncomingId(value: unknown): string | undefined {\n if (typeof value === 'string' && value.trim() !== value) return undefined;\n return sanitizeIncomingId(value);\n}\n\n// ── Context helpers ───────────────────────────────────────────────────────────\n\n/**\n * Return the correlation ID from the current `LogixiaContext`, or `undefined`\n * when called outside any context scope.\n */\nexport function getCurrentCorrelationId(): string | undefined {\n const ctx = LogixiaContext.get();\n return ctx?.correlationId as string | undefined;\n}\n\n/**\n * Run `callback` inside a correlation context scope.\n *\n * If a correlation ID is not provided, a new UUID is generated.\n *\n * @example\n * ```ts\n * await withCorrelationId('cid-abc', async () => {\n * logger.info('Processing message'); // → { correlationId: 'cid-abc', ... }\n * await callDownstream(); // correlationFetch() picks up cid-abc\n * });\n * ```\n */\nexport function withCorrelationId<T>(correlationId: string | undefined, callback: () => T): T {\n const id = correlationId ?? generateCorrelationId();\n return LogixiaContext.run({ correlationId: id }, callback);\n}\n\n// ── Express / Connect middleware ──────────────────────────────────────────────\n\n/**\n * Express-compatible middleware that:\n * 1. Reads `X-Correlation-ID` from the incoming request (or generates a new one)\n * 2. Wraps the request in a `LogixiaContext` so all loggers in the handler\n * automatically include `correlationId`\n * 3. Writes the correlation ID back to the response header (opt-out via options)\n *\n * @example\n * ```ts\n * import { correlationMiddleware } from 'logixia/correlation';\n * app.use(correlationMiddleware());\n * ```\n */\nexport function correlationMiddleware(options: CorrelationMiddlewareOptions = {}) {\n const {\n header = 'x-correlation-id',\n originServiceHeader = 'x-origin-service',\n generate = generateCorrelationId,\n trustIncoming = true,\n setResponseHeader = true,\n } = options;\n\n return function logixiaCorrelationMiddleware(\n req: Record<string, unknown>,\n res: Record<string, unknown>,\n next: () => void\n ): void {\n const headers = (req['headers'] ?? {}) as Record<string, string | undefined>;\n\n // Node lower-cases incoming header names, so look up with lower-cased keys.\n const incomingId = trustIncoming ? validIncomingId(headers[header.toLowerCase()]) : undefined;\n const correlationId = incomingId ?? generate();\n const originService = validIncomingId(headers[originServiceHeader.toLowerCase()]);\n\n if (setResponseHeader) {\n const setHeader = res['setHeader'] as ((name: string, value: string) => void) | undefined;\n if (typeof setHeader === 'function') {\n setHeader.call(res, header, correlationId);\n }\n }\n\n const ctx: CorrelationContext = {\n correlationId,\n ...(originService !== undefined ? { originService } : {}),\n };\n\n LogixiaContext.run(ctx, next);\n };\n}\n\n// ── Fastify hook ──────────────────────────────────────────────────────────────\n\n/**\n * Fastify `onRequest` lifecycle hook — same functionality as `correlationMiddleware`\n * but adapted to Fastify's hook API.\n *\n * @example\n * ```ts\n * import { correlationFastifyHook } from 'logixia/correlation';\n * fastify.addHook('onRequest', correlationFastifyHook());\n * ```\n */\nexport function correlationFastifyHook(options: CorrelationMiddlewareOptions = {}) {\n const {\n header = 'x-correlation-id',\n originServiceHeader = 'x-origin-service',\n generate = generateCorrelationId,\n trustIncoming = true,\n setResponseHeader = true,\n } = options;\n\n return function logixiaCorrelationFastifyHook(\n request: Record<string, unknown>,\n reply: Record<string, unknown>,\n done: () => void\n ): void {\n const headers = (request['headers'] ?? {}) as Record<string, string | undefined>;\n\n // Node lower-cases incoming header names, so look up with lower-cased keys.\n const incomingId = trustIncoming ? validIncomingId(headers[header.toLowerCase()]) : undefined;\n const correlationId = incomingId ?? generate();\n const originService = validIncomingId(headers[originServiceHeader.toLowerCase()]);\n\n if (setResponseHeader) {\n const replyHeader = reply['header'] as ((name: string, value: string) => void) | undefined;\n if (typeof replyHeader === 'function') {\n replyHeader.call(reply, header, correlationId);\n }\n }\n\n const ctx: CorrelationContext = {\n correlationId,\n ...(originService !== undefined ? { originService } : {}),\n };\n\n LogixiaContext.run(ctx, done);\n };\n}\n\n// ── Outbound fetch wrapper ────────────────────────────────────────────────────\n\n/**\n * A drop-in replacement for `fetch` that automatically injects the current\n * `correlationId` from `LogixiaContext` as an `X-Correlation-ID` request header.\n *\n * Falls back to the global `fetch` when no correlation context is active.\n *\n * @example\n * ```ts\n * import { correlationFetch } from 'logixia/correlation';\n *\n * // Inside a request handler with correlationMiddleware active:\n * const response = await correlationFetch('https://payments.internal/v1/charge', {\n * method: 'POST',\n * body: JSON.stringify({ amount: 100 }),\n * });\n * ```\n */\nexport async function correlationFetch(\n input: string | URL | Request,\n init: RequestInit = {},\n options: { header?: string } = {}\n): Promise<Response> {\n const header = options.header ?? 'x-correlation-id';\n const correlationId = getCurrentCorrelationId();\n\n // `init.headers` can be a plain object, a `Headers` instance, or an array of\n // `[name, value]` tuples. Spreading it into a plain object loses everything\n // but the object form: a `Headers` instance has no own enumerable properties,\n // and an array spread yields numeric indices. `Headers` accepts all three, is\n // case-insensitive, and is the shape `fetch` expects back.\n //\n // A `Request` carries headers of its own, and `fetch(request, { headers })`\n // replaces them rather than merging them, so they are seeded first and the\n // caller's `init.headers` is applied on top. Without this, accepting a\n // `Request` here would drop its headers, which is the same defect one input\n // shape over.\n const headers = new Headers(input instanceof Request ? input.headers : undefined);\n for (const [name, value] of new Headers(init.headers)) {\n headers.set(name, value);\n }\n if (correlationId && !headers.has(header)) {\n headers.set(header, correlationId);\n }\n\n return globalThis.fetch(input, { ...init, headers });\n}\n\n// ── Axios interceptor factory ─────────────────────────────────────────────────\n\n/**\n * Returns an axios request interceptor that automatically injects the current\n * correlation ID into outgoing axios requests.\n *\n * @example\n * ```ts\n * import axios from 'axios';\n * import { createCorrelationAxiosInterceptor } from 'logixia/correlation';\n *\n * axios.interceptors.request.use(createCorrelationAxiosInterceptor());\n * ```\n */\nexport function createCorrelationAxiosInterceptor(\n options: { header?: string } = {}\n): (config: Record<string, unknown>) => Record<string, unknown> {\n const header = options.header ?? 'x-correlation-id';\n\n return function correlationAxiosInterceptor(\n config: Record<string, unknown>\n ): Record<string, unknown> {\n const correlationId = getCurrentCorrelationId();\n if (!correlationId) return config;\n\n const headers = (config['headers'] ?? {}) as Record<string, unknown>;\n if (!headers[header]) {\n headers[header] = correlationId;\n }\n\n return { ...config, headers };\n };\n}\n\n// ── Child logger from request ─────────────────────────────────────────────────\n\nconst MAX_USER_AGENT_LENGTH = 512;\nconst MAX_IP_LENGTH = 64;\n\nexport interface ChildFromRequestOptions {\n /** Header to read the correlation ID from. @default 'x-correlation-id' */\n correlationHeader?: string;\n /**\n * Take the client IP from the first `X-Forwarded-For` entry. Only enable this\n * when the app sits behind a proxy that overwrites the header; otherwise any\n * client can choose the logged IP.\n * @default false\n */\n trustProxy?: boolean;\n /**\n * Context name passed as the first argument to `logger.child(context, data)`.\n * When omitted, logixia loggers (detected by their `getContext` /\n * `getAvailableTransports` methods) get `child('request', fields)` and any other\n * logger gets `child(fields)`.\n */\n childContext?: string;\n}\n\n/**\n * Create a child logger enriched with all correlation-relevant identifiers\n * extracted from the incoming HTTP request.\n *\n * The child logger's every call automatically includes:\n * `correlationId`, `method`, `url`, `userAgent`, `ip`\n *\n * The IP comes from `req.ip` / the socket address unless `trustProxy` is set.\n *\n * @example\n * ```ts\n * import { childFromRequest } from 'logixia/correlation';\n * import { logger } from 'logixia';\n *\n * app.use((req, res, next) => {\n * req.logger = childFromRequest(req, logger);\n * next();\n * });\n *\n * app.get('/orders', (req, res) => {\n * req.logger.info('Listing orders'); // → correlationId, requestId, method, url ...\n * });\n * ```\n */\nexport function childFromRequest<TLogger extends { child(...args: never[]): TLogger }>(\n req: Record<string, unknown>,\n logger: TLogger,\n options: ChildFromRequestOptions = {}\n): TLogger {\n const correlationHeader = options.correlationHeader ?? 'x-correlation-id';\n\n const headers = (req['headers'] ?? {}) as Record<string, string | undefined>;\n // Node lower-cases incoming header names, so look up with a lower-cased key.\n const correlationId =\n validIncomingId(headers[correlationHeader.toLowerCase()]) ??\n getCurrentCorrelationId() ??\n generateCorrelationId();\n\n const method = req['method'] as string | undefined;\n const url = (req['url'] as string | undefined) ?? (req['originalUrl'] as string | undefined);\n const rawUserAgent = headers['user-agent'];\n const userAgent =\n typeof rawUserAgent === 'string' ? rawUserAgent.slice(0, MAX_USER_AGENT_LENGTH) : undefined;\n const ip = resolveClientIp(req, headers, options.trustProxy === true);\n\n const fields = {\n correlationId,\n ...(method !== undefined ? { method } : {}),\n ...(url !== undefined ? { url } : {}),\n ...(userAgent !== undefined ? { userAgent } : {}),\n ...(ip !== undefined ? { ip } : {}),\n };\n\n const child = logger.child as (...args: unknown[]) => TLogger;\n if (options.childContext !== undefined) {\n return child.call(logger, options.childContext, fields);\n }\n // logixia's ILogger.child(context: string, data?) takes a string namespace;\n // pino/winston-style loggers take a bindings object.\n if (usesStringContext(logger)) {\n return child.call(logger, 'request', fields);\n }\n return child.call(logger, fields);\n}\n\n/** ILogger implementations (LogixiaLogger, LogixiaLoggerService, mock logger) expose these methods. */\nfunction usesStringContext(logger: object): boolean {\n const l = logger as Record<string, unknown>;\n return typeof l['getContext'] === 'function' || typeof l['getAvailableTransports'] === 'function';\n}\n\nfunction resolveClientIp(\n req: Record<string, unknown>,\n headers: Record<string, string | undefined>,\n trustProxy: boolean\n): string | undefined {\n if (trustProxy) {\n const forwarded = headers['x-forwarded-for'];\n const first = typeof forwarded === 'string' ? forwarded.split(',')[0]?.trim() : undefined;\n if (first) return first.slice(0, MAX_IP_LENGTH);\n }\n const fromReq = req['ip'];\n if (typeof fromReq === 'string' && fromReq) return fromReq.slice(0, MAX_IP_LENGTH);\n const remote = (req['socket'] as Record<string, unknown> | undefined)?.['remoteAddress'];\n return typeof remote === 'string' ? remote.slice(0, MAX_IP_LENGTH) : undefined;\n}\n\n// ── Message queue helpers ─────────────────────────────────────────────────────\n\n/**\n * Extract a correlation context from a Kafka / RabbitMQ / SQS message object.\n *\n * Looks for `correlationId`, `correlation_id`, or `x-correlation-id` in\n * `message.headers` (Kafka) or `message.MessageAttributes` (SQS).\n *\n * @example Kafka consumer\n * ```ts\n * import { extractMessageCorrelationId, withCorrelationId } from 'logixia/correlation';\n *\n * consumer.run({\n * eachMessage: async ({ message }) => {\n * const correlationId = extractMessageCorrelationId(message);\n * await withCorrelationId(correlationId, () => processMessage(message));\n * },\n * });\n * ```\n */\nexport function extractMessageCorrelationId(message: Record<string, unknown>): string | undefined {\n // Kafka: message.headers is Record<string, Buffer | string>\n const kafkaHeaders = message['headers'] as Record<string, Buffer | string> | undefined;\n if (kafkaHeaders) {\n const raw =\n kafkaHeaders['x-correlation-id'] ??\n kafkaHeaders['correlationId'] ??\n kafkaHeaders['correlation_id'];\n if (raw !== undefined) {\n return validIncomingId(Buffer.isBuffer(raw) ? raw.toString('utf8', 0, 512) : String(raw));\n }\n }\n\n // SQS: message.MessageAttributes\n const sqsAttrs = message['MessageAttributes'] as\n Record<string, { StringValue?: string }> | undefined;\n if (sqsAttrs) {\n return validIncomingId(\n sqsAttrs['x-correlation-id']?.StringValue ??\n sqsAttrs['correlationId']?.StringValue ??\n sqsAttrs['correlation_id']?.StringValue\n );\n }\n\n // AMQP / generic envelope\n return validIncomingId(message['correlationId'] ?? message['correlation_id']);\n}\n\n/**\n * Build Kafka message headers containing the current correlation context.\n *\n * @example\n * ```ts\n * import { buildKafkaCorrelationHeaders } from 'logixia/correlation';\n *\n * await producer.send({\n * topic: 'orders',\n * messages: [{ value: JSON.stringify(order), headers: buildKafkaCorrelationHeaders() }],\n * });\n * ```\n */\nexport function buildKafkaCorrelationHeaders(): Record<string, string> {\n const ctx = LogixiaContext.get();\n const headers: Record<string, string> = {};\n\n const correlationId = ctx?.correlationId as string | undefined;\n if (correlationId) headers['x-correlation-id'] = correlationId;\n\n const traceId = ctx?.traceId as string | undefined;\n if (traceId) headers['x-trace-id'] = traceId;\n\n return headers;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"correlation.mjs","names":[],"sources":["../src/correlation.ts"],"sourcesContent":["/**\n * logixia — Microservices Correlation ID Propagation\n *\n * Provides zero-boilerplate correlation ID generation and propagation for\n * distributed systems. A single correlation ID travels through every service\n * in a request fan-out, making it trivial to join logs across services.\n *\n * Features:\n * - UUID v4 correlation ID generation (via `crypto.randomUUID()`)\n * - Express / Fastify / NestJS incoming-request middleware\n * - `correlationFetch()` — wraps global `fetch`, auto-injects `X-Correlation-ID`\n * - `createCorrelationAxiosInterceptor()` — axios request interceptor\n * - `childFromRequest(req, logger)` — child logger with all request identifiers\n * - Kafka / SQS / RabbitMQ message context helpers\n * - Integration with `LogixiaContext` (AsyncLocalStorage) so every `logger.info()`\n * call automatically includes the correlationId without per-call passing.\n *\n * @example Express middleware\n * ```ts\n * import { correlationMiddleware } from 'logixia/correlation';\n * import { logger } from 'logixia';\n *\n * app.use(correlationMiddleware());\n *\n * app.get('/orders', (req, res) => {\n * // correlationId is automatically in every log inside this request\n * logger.info('Listing orders');\n * res.json({ ok: true });\n * });\n * ```\n *\n * @example Outbound fetch with correlation\n * ```ts\n * import { correlationFetch } from 'logixia/correlation';\n *\n * // Reads correlationId from current LogixiaContext and injects it as header\n * const data = await correlationFetch('https://payments.internal/charge', {\n * method: 'POST',\n * body: JSON.stringify({ amount: 100 }),\n * });\n * ```\n *\n * @example Manual correlation context\n * ```ts\n * import { withCorrelationId } from 'logixia/correlation';\n *\n * await withCorrelationId('trace-abc-123', async () => {\n * // All logs and outbound fetch calls in here carry 'trace-abc-123'\n * await processOrder();\n * });\n * ```\n */\n\nimport { randomUUID } from 'node:crypto';\n\nimport { LogixiaContext } from './context/async-context';\nimport type { HttpRequestShape, HttpResponseShape } from './types/http-shapes';\nimport { sanitizeIncomingId } from './utils/trace.utils';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\n/** Fields injected into the log context by the correlation middleware. */\nexport interface CorrelationContext {\n correlationId: string;\n traceId?: string;\n /** Service that originated the request (from `X-Origin-Service` header). */\n originService?: string;\n /** Index signature so CorrelationContext is assignable to LogContext. */\n [key: string]: unknown;\n}\n\nexport interface CorrelationMiddlewareOptions {\n /**\n * HTTP header to read/write the correlation ID from/to.\n * @default 'x-correlation-id'\n */\n header?: string;\n /**\n * HTTP header for the originating service name.\n * @default 'x-origin-service'\n */\n originServiceHeader?: string;\n /**\n * Function to generate a new correlation ID when none is present in the request.\n * @default () => crypto.randomUUID()\n */\n generate?: () => string;\n /**\n * When `false`, the incoming correlation header is ignored and a new ID is\n * always generated. Use this at the edge when client-supplied IDs should not\n * be trusted.\n * @default true\n */\n trustIncoming?: boolean;\n /**\n * When `true`, the response will include the correlation ID header so\n * clients can log it client-side.\n * @default true\n */\n setResponseHeader?: boolean;\n}\n\n// ── ID generation ─────────────────────────────────────────────────────────────\n\n/**\n * Generate a new correlation ID using `crypto.randomUUID()` (available in\n * Node.js 14.17+, all modern browsers, and Edge runtimes).\n *\n * When the Web Crypto global is unavailable (e.g. Node 18 without\n * `--experimental-global-webcrypto`), fall back to `node:crypto`'s\n * `randomUUID`, which returns a proper UUID v4 — matching the shape this\n * function's JSDoc and README promise. `logixia/correlation` is Node-only\n * (it builds on `AsyncLocalStorage`), so importing `node:crypto` is safe here.\n */\nexport function generateCorrelationId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return randomUUID();\n}\n\n/**\n * Return an incoming correlation ID only if it is short and made of safe\n * characters. Anything else (over-long, empty, or containing newlines or other\n * control characters) is dropped so the caller generates a fresh ID instead of\n * writing client-supplied text into every log line.\n */\nfunction validIncomingId(value: unknown): string | undefined {\n if (typeof value === 'string' && value.trim() !== value) return undefined;\n return sanitizeIncomingId(value);\n}\n\n// ── Context helpers ───────────────────────────────────────────────────────────\n\n/**\n * Return the correlation ID from the current `LogixiaContext`, or `undefined`\n * when called outside any context scope.\n */\nexport function getCurrentCorrelationId(): string | undefined {\n const ctx = LogixiaContext.get();\n return ctx?.correlationId as string | undefined;\n}\n\n/**\n * Run `callback` inside a correlation context scope.\n *\n * If a correlation ID is not provided, a new UUID is generated.\n *\n * @example\n * ```ts\n * await withCorrelationId('cid-abc', async () => {\n * logger.info('Processing message'); // → { correlationId: 'cid-abc', ... }\n * await callDownstream(); // correlationFetch() picks up cid-abc\n * });\n * ```\n */\nexport function withCorrelationId<T>(correlationId: string | undefined, callback: () => T): T {\n const id = correlationId ?? generateCorrelationId();\n return LogixiaContext.run({ correlationId: id }, callback);\n}\n\n// ── Express / Connect middleware ──────────────────────────────────────────────\n\n/**\n * Express-compatible middleware that:\n * 1. Reads `X-Correlation-ID` from the incoming request (or generates a new one)\n * 2. Wraps the request in a `LogixiaContext` so all loggers in the handler\n * automatically include `correlationId`\n * 3. Writes the correlation ID back to the response header (opt-out via options)\n *\n * @example\n * ```ts\n * import { correlationMiddleware } from 'logixia/correlation';\n * app.use(correlationMiddleware());\n * ```\n */\nexport function correlationMiddleware(options: CorrelationMiddlewareOptions = {}) {\n const {\n header = 'x-correlation-id',\n originServiceHeader = 'x-origin-service',\n generate = generateCorrelationId,\n trustIncoming = true,\n setResponseHeader = true,\n } = options;\n\n return function logixiaCorrelationMiddleware(\n req: HttpRequestShape,\n res: HttpResponseShape,\n next: () => void\n ): void {\n const headers = req.headers ?? {};\n\n // Node lower-cases incoming header names, so look up with lower-cased keys.\n const incomingId = trustIncoming ? validIncomingId(headers[header.toLowerCase()]) : undefined;\n const correlationId = incomingId ?? generate();\n const originService = validIncomingId(headers[originServiceHeader.toLowerCase()]);\n\n if (setResponseHeader) {\n res.setHeader?.(header, correlationId);\n }\n\n const ctx: CorrelationContext = {\n correlationId,\n ...(originService !== undefined ? { originService } : {}),\n };\n\n LogixiaContext.run(ctx, next);\n };\n}\n\n// ── Fastify hook ──────────────────────────────────────────────────────────────\n\n/**\n * Fastify `onRequest` lifecycle hook — same functionality as `correlationMiddleware`\n * but adapted to Fastify's hook API.\n *\n * @example\n * ```ts\n * import { correlationFastifyHook } from 'logixia/correlation';\n * fastify.addHook('onRequest', correlationFastifyHook());\n * ```\n */\nexport function correlationFastifyHook(options: CorrelationMiddlewareOptions = {}) {\n const {\n header = 'x-correlation-id',\n originServiceHeader = 'x-origin-service',\n generate = generateCorrelationId,\n trustIncoming = true,\n setResponseHeader = true,\n } = options;\n\n return function logixiaCorrelationFastifyHook(\n request: HttpRequestShape,\n reply: HttpResponseShape,\n done: () => void\n ): void {\n const headers = request.headers ?? {};\n\n // Node lower-cases incoming header names, so look up with lower-cased keys.\n const incomingId = trustIncoming ? validIncomingId(headers[header.toLowerCase()]) : undefined;\n const correlationId = incomingId ?? generate();\n const originService = validIncomingId(headers[originServiceHeader.toLowerCase()]);\n\n if (setResponseHeader) {\n reply.header?.(header, correlationId);\n }\n\n const ctx: CorrelationContext = {\n correlationId,\n ...(originService !== undefined ? { originService } : {}),\n };\n\n LogixiaContext.run(ctx, done);\n };\n}\n\n// ── Outbound fetch wrapper ────────────────────────────────────────────────────\n\n/**\n * A drop-in replacement for `fetch` that automatically injects the current\n * `correlationId` from `LogixiaContext` as an `X-Correlation-ID` request header.\n *\n * Falls back to the global `fetch` when no correlation context is active.\n *\n * @example\n * ```ts\n * import { correlationFetch } from 'logixia/correlation';\n *\n * // Inside a request handler with correlationMiddleware active:\n * const response = await correlationFetch('https://payments.internal/v1/charge', {\n * method: 'POST',\n * body: JSON.stringify({ amount: 100 }),\n * });\n * ```\n */\nexport async function correlationFetch(\n input: string | URL | Request,\n init: RequestInit = {},\n options: { header?: string } = {}\n): Promise<Response> {\n const header = options.header ?? 'x-correlation-id';\n const correlationId = getCurrentCorrelationId();\n\n // `init.headers` can be a plain object, a `Headers` instance, or an array of\n // `[name, value]` tuples. Spreading it into a plain object loses everything\n // but the object form: a `Headers` instance has no own enumerable properties,\n // and an array spread yields numeric indices. `Headers` accepts all three, is\n // case-insensitive, and is the shape `fetch` expects back.\n //\n // A `Request` carries headers of its own, and `fetch(request, { headers })`\n // replaces them rather than merging them, so they are seeded first and the\n // caller's `init.headers` is applied on top. Without this, accepting a\n // `Request` here would drop its headers, which is the same defect one input\n // shape over.\n const headers = new Headers(input instanceof Request ? input.headers : undefined);\n for (const [name, value] of new Headers(init.headers)) {\n headers.set(name, value);\n }\n if (correlationId && !headers.has(header)) {\n headers.set(header, correlationId);\n }\n\n return globalThis.fetch(input, { ...init, headers });\n}\n\n// ── Axios interceptor factory ─────────────────────────────────────────────────\n\n/**\n * Returns an axios request interceptor that automatically injects the current\n * correlation ID into outgoing axios requests.\n *\n * @example\n * ```ts\n * import axios from 'axios';\n * import { createCorrelationAxiosInterceptor } from 'logixia/correlation';\n *\n * axios.interceptors.request.use(createCorrelationAxiosInterceptor());\n * ```\n */\nexport function createCorrelationAxiosInterceptor(\n options: { header?: string } = {}\n): (config: Record<string, unknown>) => Record<string, unknown> {\n const header = options.header ?? 'x-correlation-id';\n\n return function correlationAxiosInterceptor(\n config: Record<string, unknown>\n ): Record<string, unknown> {\n const correlationId = getCurrentCorrelationId();\n if (!correlationId) return config;\n\n const headers = (config['headers'] ?? {}) as Record<string, unknown>;\n if (!headers[header]) {\n headers[header] = correlationId;\n }\n\n return { ...config, headers };\n };\n}\n\n// ── Child logger from request ─────────────────────────────────────────────────\n\nconst MAX_USER_AGENT_LENGTH = 512;\nconst MAX_IP_LENGTH = 64;\n\nexport interface ChildFromRequestOptions {\n /** Header to read the correlation ID from. @default 'x-correlation-id' */\n correlationHeader?: string;\n /**\n * Take the client IP from the first `X-Forwarded-For` entry. Only enable this\n * when the app sits behind a proxy that overwrites the header; otherwise any\n * client can choose the logged IP.\n * @default false\n */\n trustProxy?: boolean;\n /**\n * Context name passed as the first argument to `logger.child(context, data)`.\n * When omitted, logixia loggers (detected by their `getContext` /\n * `getAvailableTransports` methods) get `child('request', fields)` and any other\n * logger gets `child(fields)`.\n */\n childContext?: string;\n}\n\n/**\n * Create a child logger enriched with all correlation-relevant identifiers\n * extracted from the incoming HTTP request.\n *\n * The child logger's every call automatically includes:\n * `correlationId`, `method`, `url`, `userAgent`, `ip`\n *\n * The IP comes from `req.ip` / the socket address unless `trustProxy` is set.\n *\n * @example\n * ```ts\n * import { childFromRequest } from 'logixia/correlation';\n * import { logger } from 'logixia';\n *\n * app.use((req, res, next) => {\n * req.logger = childFromRequest(req, logger);\n * next();\n * });\n *\n * app.get('/orders', (req, res) => {\n * req.logger.info('Listing orders'); // → correlationId, requestId, method, url ...\n * });\n * ```\n */\nexport function childFromRequest<TLogger extends { child(...args: never[]): TLogger }>(\n req: HttpRequestShape,\n logger: TLogger,\n options: ChildFromRequestOptions = {}\n): TLogger {\n const correlationHeader = options.correlationHeader ?? 'x-correlation-id';\n\n const headers = req.headers ?? {};\n // Node lower-cases incoming header names, so look up with a lower-cased key.\n const correlationId =\n validIncomingId(headers[correlationHeader.toLowerCase()]) ??\n getCurrentCorrelationId() ??\n generateCorrelationId();\n\n const method = req.method;\n const url = req.url ?? req.originalUrl;\n const rawUserAgent = headers['user-agent'];\n const userAgent =\n typeof rawUserAgent === 'string' ? rawUserAgent.slice(0, MAX_USER_AGENT_LENGTH) : undefined;\n const ip = resolveClientIp(req, headers, options.trustProxy === true);\n\n const fields = {\n correlationId,\n ...(method !== undefined ? { method } : {}),\n ...(url !== undefined ? { url } : {}),\n ...(userAgent !== undefined ? { userAgent } : {}),\n ...(ip !== undefined ? { ip } : {}),\n };\n\n const child = logger.child as (...args: unknown[]) => TLogger;\n if (options.childContext !== undefined) {\n return child.call(logger, options.childContext, fields);\n }\n // logixia's ILogger.child(context: string, data?) takes a string namespace;\n // pino/winston-style loggers take a bindings object.\n if (usesStringContext(logger)) {\n return child.call(logger, 'request', fields);\n }\n return child.call(logger, fields);\n}\n\n/** ILogger implementations (LogixiaLogger, LogixiaLoggerService, mock logger) expose these methods. */\nfunction usesStringContext(logger: object): boolean {\n const l = logger as Record<string, unknown>;\n return typeof l['getContext'] === 'function' || typeof l['getAvailableTransports'] === 'function';\n}\n\nfunction resolveClientIp(\n req: HttpRequestShape,\n headers: Record<string, string | string[] | undefined>,\n trustProxy: boolean\n): string | undefined {\n if (trustProxy) {\n const forwarded = headers['x-forwarded-for'];\n const first = typeof forwarded === 'string' ? forwarded.split(',')[0]?.trim() : undefined;\n if (first) return first.slice(0, MAX_IP_LENGTH);\n }\n const fromReq = req.ip;\n if (typeof fromReq === 'string' && fromReq) return fromReq.slice(0, MAX_IP_LENGTH);\n const remote = req.socket?.remoteAddress;\n return typeof remote === 'string' ? remote.slice(0, MAX_IP_LENGTH) : undefined;\n}\n\n// ── Message queue helpers ─────────────────────────────────────────────────────\n\n/**\n * Extract a correlation context from a Kafka / RabbitMQ / SQS message object.\n *\n * Looks for `correlationId`, `correlation_id`, or `x-correlation-id` in\n * `message.headers` (Kafka) or `message.MessageAttributes` (SQS).\n *\n * @example Kafka consumer\n * ```ts\n * import { extractMessageCorrelationId, withCorrelationId } from 'logixia/correlation';\n *\n * consumer.run({\n * eachMessage: async ({ message }) => {\n * const correlationId = extractMessageCorrelationId(message);\n * await withCorrelationId(correlationId, () => processMessage(message));\n * },\n * });\n * ```\n */\nexport function extractMessageCorrelationId(message: Record<string, unknown>): string | undefined {\n // Kafka: message.headers is Record<string, Buffer | string>\n const kafkaHeaders = message['headers'] as Record<string, Buffer | string> | undefined;\n if (kafkaHeaders) {\n const raw =\n kafkaHeaders['x-correlation-id'] ??\n kafkaHeaders['correlationId'] ??\n kafkaHeaders['correlation_id'];\n if (raw !== undefined) {\n return validIncomingId(Buffer.isBuffer(raw) ? raw.toString('utf8', 0, 512) : String(raw));\n }\n }\n\n // SQS: message.MessageAttributes\n const sqsAttrs = message['MessageAttributes'] as\n Record<string, { StringValue?: string }> | undefined;\n if (sqsAttrs) {\n return validIncomingId(\n sqsAttrs['x-correlation-id']?.StringValue ??\n sqsAttrs['correlationId']?.StringValue ??\n sqsAttrs['correlation_id']?.StringValue\n );\n }\n\n // AMQP / generic envelope\n return validIncomingId(message['correlationId'] ?? message['correlation_id']);\n}\n\n/**\n * Build Kafka message headers containing the current correlation context.\n *\n * @example\n * ```ts\n * import { buildKafkaCorrelationHeaders } from 'logixia/correlation';\n *\n * await producer.send({\n * topic: 'orders',\n * messages: [{ value: JSON.stringify(order), headers: buildKafkaCorrelationHeaders() }],\n * });\n * ```\n */\nexport function buildKafkaCorrelationHeaders(): Record<string, string> {\n const ctx = LogixiaContext.get();\n const headers: Record<string, string> = {};\n\n const correlationId = ctx?.correlationId as string | undefined;\n if (correlationId) headers['x-correlation-id'] = correlationId;\n\n const traceId = ctx?.traceId as string | undefined;\n if (traceId) headers['x-trace-id'] = traceId;\n\n return headers;\n}\n"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
-
2
- //#region src/transports/entry-fields.ts
1
+ const require_own_property = require("./own-property-BWkibepN.js");
2
+ // src/transports/entry-fields.ts
3
3
  const PAYLOAD_PREFIX = "data_";
4
- const UNSAFE_KEYS = new Set(["__proto__"]);
5
4
  /**
6
5
  * Copies `core` and then the user payload into one object. The core fields stay
7
6
  * authoritative: a payload key that matches a core key (even one whose value is
@@ -9,14 +8,19 @@ const UNSAFE_KEYS = new Set(["__proto__"]);
9
8
  * prefix is repeated until the name is free of core keys and of every other
10
9
  * payload key, so the result does not depend on key order and a payload's own
11
10
  * `data_level` stays where it is.
11
+ *
12
+ * Exception: a payload `traceId` equal to the core `traceId` is a duplicate (the
13
+ * context middleware copies the trace id into the payload) and is dropped rather
14
+ * than emitted again as `data_traceId`. A different value is still kept.
12
15
  */
13
16
  function mergeCoreFields(core, data, extraReserved = []) {
14
17
  const merged = { ...core };
15
18
  if (!data) return merged;
16
- const reserved = new Set([...Object.keys(core), ...extraReserved]);
19
+ const reserved = /* @__PURE__ */ new Set([...Object.keys(core), ...extraReserved]);
17
20
  const payloadKeys = new Set(Object.keys(data));
18
21
  for (const key of payloadKeys) {
19
- if (UNSAFE_KEYS.has(key)) continue;
22
+ if (require_own_property.isProtoKey(key)) continue;
23
+ if (key === "traceId" && core["traceId"] !== void 0 && data[key] === core["traceId"]) continue;
20
24
  let target = key;
21
25
  if (reserved.has(target)) do
22
26
  target = `${PAYLOAD_PREFIX}${target}`;
@@ -25,12 +29,11 @@ function mergeCoreFields(core, data, extraReserved = []) {
25
29
  }
26
30
  return merged;
27
31
  }
28
-
29
- //#endregion
30
- Object.defineProperty(exports, 'mergeCoreFields', {
31
- enumerable: true,
32
- get: function () {
33
- return mergeCoreFields;
34
- }
32
+ Object.defineProperty(exports, "mergeCoreFields", {
33
+ enumerable: true,
34
+ get: function() {
35
+ return mergeCoreFields;
36
+ }
35
37
  });
36
- //# sourceMappingURL=entry-fields-BvOLFlJ4.js.map
38
+
39
+ //# sourceMappingURL=entry-fields-BSLkzw18.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-fields-BSLkzw18.js","names":["isProtoKey"],"sources":["../src/transports/entry-fields.ts"],"sourcesContent":["import { isProtoKey } from '../utils/own-property';\n\nconst PAYLOAD_PREFIX = 'data_';\n\n/**\n * Copies `core` and then the user payload into one object. The core fields stay\n * authoritative: a payload key that matches a core key (even one whose value is\n * undefined) or a reserved name is kept under a `data_` prefix instead. The\n * prefix is repeated until the name is free of core keys and of every other\n * payload key, so the result does not depend on key order and a payload's own\n * `data_level` stays where it is.\n *\n * Exception: a payload `traceId` equal to the core `traceId` is a duplicate (the\n * context middleware copies the trace id into the payload) and is dropped rather\n * than emitted again as `data_traceId`. A different value is still kept.\n */\nexport function mergeCoreFields(\n core: Record<string, unknown>,\n data: Record<string, unknown> | undefined,\n extraReserved: readonly string[] = []\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...core };\n if (!data) return merged;\n const reserved = new Set([...Object.keys(core), ...extraReserved]);\n const payloadKeys = new Set(Object.keys(data));\n for (const key of payloadKeys) {\n if (isProtoKey(key)) continue;\n if (key === 'traceId' && core['traceId'] !== undefined && data[key] === core['traceId'])\n continue;\n let target = key;\n if (reserved.has(target)) {\n do {\n target = `${PAYLOAD_PREFIX}${target}`;\n } while (reserved.has(target) || payloadKeys.has(target));\n }\n merged[target] = data[key];\n }\n return merged;\n}\n"],"mappings":";;AAEA,MAAM,iBAAiB;;;;;;;;;;;;;AAcvB,SAAgB,gBACd,MACA,MACA,gBAAmC,CAAC,GACX;CACzB,MAAM,SAAkC,EAAE,GAAG,KAAK;CAClD,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,2BAAW,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,IAAI,GAAG,GAAG,aAAa,CAAC;CACjE,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,IAAI,CAAC;CAC7C,KAAK,MAAM,OAAO,aAAa;EAC7B,IAAIA,qBAAAA,WAAW,GAAG,GAAG;EACrB,IAAI,QAAQ,aAAa,KAAK,eAAe,KAAA,KAAa,KAAK,SAAS,KAAK,YAC3E;EACF,IAAI,SAAS;EACb,IAAI,SAAS,IAAI,MAAM,GACrB;GACE,SAAS,GAAG,iBAAiB;SACtB,SAAS,IAAI,MAAM,KAAK,YAAY,IAAI,MAAM;EAEzD,OAAO,UAAU,KAAK;CACxB;CACA,OAAO;AACT"}
@@ -1,6 +1,6 @@
1
- //#region src/transports/entry-fields.ts
1
+ import { n as isProtoKey } from "./own-property-CjFw9qwZ.mjs";
2
+ // src/transports/entry-fields.ts
2
3
  const PAYLOAD_PREFIX = "data_";
3
- const UNSAFE_KEYS = new Set(["__proto__"]);
4
4
  /**
5
5
  * Copies `core` and then the user payload into one object. The core fields stay
6
6
  * authoritative: a payload key that matches a core key (even one whose value is
@@ -8,14 +8,19 @@ const UNSAFE_KEYS = new Set(["__proto__"]);
8
8
  * prefix is repeated until the name is free of core keys and of every other
9
9
  * payload key, so the result does not depend on key order and a payload's own
10
10
  * `data_level` stays where it is.
11
+ *
12
+ * Exception: a payload `traceId` equal to the core `traceId` is a duplicate (the
13
+ * context middleware copies the trace id into the payload) and is dropped rather
14
+ * than emitted again as `data_traceId`. A different value is still kept.
11
15
  */
12
16
  function mergeCoreFields(core, data, extraReserved = []) {
13
17
  const merged = { ...core };
14
18
  if (!data) return merged;
15
- const reserved = new Set([...Object.keys(core), ...extraReserved]);
19
+ const reserved = /* @__PURE__ */ new Set([...Object.keys(core), ...extraReserved]);
16
20
  const payloadKeys = new Set(Object.keys(data));
17
21
  for (const key of payloadKeys) {
18
- if (UNSAFE_KEYS.has(key)) continue;
22
+ if (isProtoKey(key)) continue;
23
+ if (key === "traceId" && core["traceId"] !== void 0 && data[key] === core["traceId"]) continue;
19
24
  let target = key;
20
25
  if (reserved.has(target)) do
21
26
  target = `${PAYLOAD_PREFIX}${target}`;
@@ -24,7 +29,6 @@ function mergeCoreFields(core, data, extraReserved = []) {
24
29
  }
25
30
  return merged;
26
31
  }
27
-
28
- //#endregion
29
32
  export { mergeCoreFields as t };
30
- //# sourceMappingURL=entry-fields-BJkuPnIg.mjs.map
33
+
34
+ //# sourceMappingURL=entry-fields-J1OBi24o.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-fields-J1OBi24o.mjs","names":[],"sources":["../src/transports/entry-fields.ts"],"sourcesContent":["import { isProtoKey } from '../utils/own-property';\n\nconst PAYLOAD_PREFIX = 'data_';\n\n/**\n * Copies `core` and then the user payload into one object. The core fields stay\n * authoritative: a payload key that matches a core key (even one whose value is\n * undefined) or a reserved name is kept under a `data_` prefix instead. The\n * prefix is repeated until the name is free of core keys and of every other\n * payload key, so the result does not depend on key order and a payload's own\n * `data_level` stays where it is.\n *\n * Exception: a payload `traceId` equal to the core `traceId` is a duplicate (the\n * context middleware copies the trace id into the payload) and is dropped rather\n * than emitted again as `data_traceId`. A different value is still kept.\n */\nexport function mergeCoreFields(\n core: Record<string, unknown>,\n data: Record<string, unknown> | undefined,\n extraReserved: readonly string[] = []\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...core };\n if (!data) return merged;\n const reserved = new Set([...Object.keys(core), ...extraReserved]);\n const payloadKeys = new Set(Object.keys(data));\n for (const key of payloadKeys) {\n if (isProtoKey(key)) continue;\n if (key === 'traceId' && core['traceId'] !== undefined && data[key] === core['traceId'])\n continue;\n let target = key;\n if (reserved.has(target)) {\n do {\n target = `${PAYLOAD_PREFIX}${target}`;\n } while (reserved.has(target) || payloadKeys.has(target));\n }\n merged[target] = data[key];\n }\n return merged;\n}\n"],"mappings":";;AAEA,MAAM,iBAAiB;;;;;;;;;;;;;AAcvB,SAAgB,gBACd,MACA,MACA,gBAAmC,CAAC,GACX;CACzB,MAAM,SAAkC,EAAE,GAAG,KAAK;CAClD,IAAI,CAAC,MAAM,OAAO;CAClB,MAAM,2BAAW,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,IAAI,GAAG,GAAG,aAAa,CAAC;CACjE,MAAM,cAAc,IAAI,IAAI,OAAO,KAAK,IAAI,CAAC;CAC7C,KAAK,MAAM,OAAO,aAAa;EAC7B,IAAI,WAAW,GAAG,GAAG;EACrB,IAAI,QAAQ,aAAa,KAAK,eAAe,KAAA,KAAa,KAAK,SAAS,KAAK,YAC3E;EACF,IAAI,SAAS;EACb,IAAI,SAAS,IAAI,MAAM,GACrB;GACE,SAAS,GAAG,iBAAiB;SACtB,SAAS,IAAI,MAAM,KAAK,YAAY,IAAI,MAAM;EAEzD,OAAO,UAAU,KAAK;CACxB;CACA,OAAO;AACT"}
package/dist/gcp.d.mts CHANGED
@@ -1,3 +1,2 @@
1
- import "./transport.types-BMnurPcc.mjs";
2
- import { n as GCPTransport, r as GCPTransportConfig, t as GCPMonitoredResource } from "./gcp.transport-GrJurKo8.mjs";
1
+ import { n as GCPTransport, r as GCPTransportConfig, t as GCPMonitoredResource } from "./gcp.transport-D9SdLPwP.mjs";
3
2
  export { GCPMonitoredResource, GCPTransport, GCPTransportConfig };
package/dist/gcp.d.ts CHANGED
@@ -1,3 +1,2 @@
1
- import "./transport.types-CGoHpWQR.js";
2
- import { n as GCPTransport, r as GCPTransportConfig, t as GCPMonitoredResource } from "./gcp.transport-B7oc97sG.js";
1
+ import { n as GCPTransport, r as GCPTransportConfig, t as GCPMonitoredResource } from "./gcp.transport-BCIvdFV-.js";
3
2
  export { GCPMonitoredResource, GCPTransport, GCPTransportConfig };
package/dist/gcp.js CHANGED
@@ -1,7 +1,3 @@
1
- require('./internal-log-BUJYMZbE.js');
2
- require('./safe-stringify-DWo3aZp_.js');
3
- require('./batch-buffer-BY-mRWIK.js');
4
- require('./entry-fields-BvOLFlJ4.js');
5
- const require_gcp_transport = require('./gcp.transport-CHV8MGgz.js');
6
-
7
- exports.GCPTransport = require_gcp_transport.GCPTransport;
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_gcp_transport = require("./gcp.transport-Dy2Lf8FI.js");
3
+ exports.GCPTransport = require_gcp_transport.GCPTransport;
package/dist/gcp.mjs CHANGED
@@ -1,7 +1 @@
1
- import "./internal-log-Cm8SOWSV.mjs";
2
- import "./safe-stringify-DJ66tW5I.mjs";
3
- import "./batch-buffer-PGvSgbmd.mjs";
4
- import "./entry-fields-BJkuPnIg.mjs";
5
- import { t as GCPTransport } from "./gcp.transport-DBX5TPIy.mjs";
6
-
7
- export { GCPTransport };
1
+ export { t as GCPTransport } from "./gcp.transport-DVCmnA3b.mjs";
@@ -1,7 +1,4 @@
1
- import { p as TransportLogEntry, s as IAsyncTransport } from "./transport.types-CGoHpWQR.js";
2
-
3
- //#region src/transports/gcp.transport.d.ts
4
-
1
+ import { _ as TransportLogEntry, l as IAsyncTransport } from "./transport.types-CzJq4IrF.js";
5
2
  interface GCPMonitoredResource {
6
3
  type: string;
7
4
  labels?: Record<string, string>;
@@ -17,8 +14,8 @@ interface GCPTransportConfig {
17
14
  /** Monitored resource descriptor. Defaults to `global`. */
18
15
  resource?: GCPMonitoredResource;
19
16
  /**
20
- * Service account key JSON (parsed). If omitted, ADC is used
21
- * (GOOGLE_APPLICATION_CREDENTIALS env var or metadata server).
17
+ * Service account key JSON (parsed). If omitted, only the metadata server
18
+ * (GCE/GKE/Cloud Run) is tried; the key-file env var is not read.
22
19
  */
23
20
  credentials?: {
24
21
  client_email: string;
@@ -64,8 +61,8 @@ declare class GCPTransport implements IAsyncTransport {
64
61
  private getAccessToken;
65
62
  private fetchFromMetadataServer;
66
63
  private fetchServiceAccountToken;
64
+ private secretValues;
67
65
  private writeEntries;
68
66
  }
69
- //#endregion
70
67
  export { GCPTransport as n, GCPTransportConfig as r, GCPMonitoredResource as t };
71
- //# sourceMappingURL=gcp.transport-B7oc97sG.d.ts.map
68
+ //# sourceMappingURL=gcp.transport-BCIvdFV-.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcp.transport-BCIvdFV-.d.ts","names":[],"sources":["../src/transports/gcp.transport.ts"],"mappings":";UA+CiB;EACf;EACA,SAAS;;UAGM;;EAEf;;;;;EAKA;;EAEA,WAAW;;;;;EAKX;IACE;IACA;;;EAGF;;EAEA;;EAEA;;EAEA;EACA;;;;;cAuCW,wBAAwB;WACnB;WACA;WACA;WACA;mBAEC;mBACA;UACT;UACA;UACA;mBAES;mBACA;mBACA;mBACA;EAEL,YAAA,QAAQ;EA4BpB,MAAM,OAAO;EAKb,SAAS;;;;;;EASH,SAAS;UAQP;UAqBM;UAgCN;UAmCA;UA+CA;UASM"}
@@ -1,7 +1,4 @@
1
- import { p as TransportLogEntry, s as IAsyncTransport } from "./transport.types-BMnurPcc.mjs";
2
-
3
- //#region src/transports/gcp.transport.d.ts
4
-
1
+ import { _ as TransportLogEntry, l as IAsyncTransport } from "./transport.types-CzJq4IrF.mjs";
5
2
  interface GCPMonitoredResource {
6
3
  type: string;
7
4
  labels?: Record<string, string>;
@@ -17,8 +14,8 @@ interface GCPTransportConfig {
17
14
  /** Monitored resource descriptor. Defaults to `global`. */
18
15
  resource?: GCPMonitoredResource;
19
16
  /**
20
- * Service account key JSON (parsed). If omitted, ADC is used
21
- * (GOOGLE_APPLICATION_CREDENTIALS env var or metadata server).
17
+ * Service account key JSON (parsed). If omitted, only the metadata server
18
+ * (GCE/GKE/Cloud Run) is tried; the key-file env var is not read.
22
19
  */
23
20
  credentials?: {
24
21
  client_email: string;
@@ -64,8 +61,8 @@ declare class GCPTransport implements IAsyncTransport {
64
61
  private getAccessToken;
65
62
  private fetchFromMetadataServer;
66
63
  private fetchServiceAccountToken;
64
+ private secretValues;
67
65
  private writeEntries;
68
66
  }
69
- //#endregion
70
67
  export { GCPTransport as n, GCPTransportConfig as r, GCPMonitoredResource as t };
71
- //# sourceMappingURL=gcp.transport-GrJurKo8.d.mts.map
68
+ //# sourceMappingURL=gcp.transport-D9SdLPwP.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcp.transport-D9SdLPwP.d.mts","names":[],"sources":["../src/transports/gcp.transport.ts"],"mappings":";UA+CiB;EACf;EACA,SAAS;;UAGM;;EAEf;;;;;EAKA;;EAEA,WAAW;;;;;EAKX;IACE;IACA;;;EAGF;;EAEA;;EAEA;;EAEA;EACA;;;;;cAuCW,wBAAwB;WACnB;WACA;WACA;WACA;mBAEC;mBACA;UACT;UACA;UACA;mBAES;mBACA;mBACA;mBACA;EAEL,YAAA,QAAQ;EA4BpB,MAAM,OAAO;EAKb,SAAS;;;;;;EASH,SAAS;UAQP;UAqBM;UAgCN;UAmCA;UA+CA;UASM"}
@@ -1,11 +1,43 @@
1
- import { t as __require } from "./chunk-DLSkYcPk.mjs";
2
- import { t as internalError } from "./internal-log-Cm8SOWSV.mjs";
3
- import { i as stringifyForLog } from "./safe-stringify-DJ66tW5I.mjs";
4
- import { a as httpRequest, n as DEFAULT_MAX_BUFFER_SIZE, r as DEFAULT_REQUEST_TIMEOUT_MS, t as BatchBuffer } from "./batch-buffer-PGvSgbmd.mjs";
5
- import { t as mergeCoreFields } from "./entry-fields-BJkuPnIg.mjs";
1
+ import { t as __require } from "./rolldown-runtime-D3PmRqjj.mjs";
2
+ import { t as internalError } from "./internal-log-Bg4V1cPg.mjs";
3
+ import { n as protectSecrets } from "./secret-config-Dhb6pqTU.mjs";
4
+ import { i as stringifyForLog } from "./safe-stringify-B5p5ktFe.mjs";
5
+ import { t as mergeCoreFields } from "./entry-fields-J1OBi24o.mjs";
6
+ import { a as httpRequest, t as BatchBuffer } from "./batch-buffer-BvXLDPlV.mjs";
6
7
  import * as https from "node:https";
7
-
8
- //#region src/transports/gcp.transport.ts
8
+ // src/transports/gcp.transport.ts
9
+ /**
10
+ * logixia — Google Cloud Logging (Stackdriver) transport
11
+ *
12
+ * Maps logixia log entries to the GCP Logging REST API format:
13
+ * - Maps `level` → `severity` (INFO, WARNING, ERROR, CRITICAL…)
14
+ * - Injects `logging.googleapis.com/trace` for Cloud Trace correlation
15
+ * - Sets `traceSampled: true` whenever a trace id is present
16
+ * (`logging.googleapis.com/spanId` and `sourceLocation` are not populated)
17
+ * - Supports structured `jsonPayload` and plain `textPayload`
18
+ *
19
+ * Auth: the metadata server on GCE/GKE/Cloud Run, or a service-account key passed
20
+ * as `credentials`. The `GOOGLE_APPLICATION_CREDENTIALS` key-file variable is not
21
+ * read; full ADC is not supported.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { createLogger } from 'logixia';
26
+ * import { GCPTransport } from 'logixia/gcp';
27
+ *
28
+ * const logger = createLogger({
29
+ * transports: {
30
+ * custom: [
31
+ * new GCPTransport({
32
+ * projectId: process.env.GOOGLE_CLOUD_PROJECT,
33
+ * logName: 'projects/my-project/logs/api',
34
+ * resource: { type: 'gce_instance', labels: { instance_id: '123' } },
35
+ * }),
36
+ * ],
37
+ * },
38
+ * });
39
+ * ```
40
+ */
9
41
  function toGcpSeverity(level) {
10
42
  switch (level.toLowerCase()) {
11
43
  case "trace":
@@ -30,6 +62,7 @@ var GCPTransport = class {
30
62
  this.flushTimer = null;
31
63
  this.accessToken = null;
32
64
  this.tokenExpiry = 0;
65
+ protectSecrets(this, ["credentials", "accessToken"]);
33
66
  this.projectId = config.projectId ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"] ?? "unknown-project";
34
67
  this.logName = config.logName ?? `projects/${this.projectId}/logs/logixia`;
35
68
  this.resource = config.resource ?? { type: "global" };
@@ -37,12 +70,13 @@ var GCPTransport = class {
37
70
  this.batchSize = config.batchSize ?? 200;
38
71
  this.flushInterval = config.flushIntervalMs ?? 5e3;
39
72
  this.level = config.level;
40
- this.requestTimeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
73
+ this.requestTimeoutMs = config.requestTimeoutMs ?? 1e4;
41
74
  this.buffer = new BatchBuffer({
42
75
  name: "GCPTransport",
43
76
  batchSize: this.batchSize,
44
- maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,
45
- send: (entries) => this.writeEntries(entries)
77
+ maxBufferSize: config.maxBufferSize ?? 1e4,
78
+ send: (entries) => this.writeEntries(entries),
79
+ secrets: () => this.secretValues()
46
80
  });
47
81
  this.flushTimer = setInterval(() => {
48
82
  this.flush().catch(() => {});
@@ -92,7 +126,7 @@ var GCPTransport = class {
92
126
  const token = await this.fetchFromMetadataServer();
93
127
  if (token) {
94
128
  this.accessToken = token;
95
- this.tokenExpiry = Date.now() + 3300 * 1e3;
129
+ this.tokenExpiry = Date.now() + 33e5;
96
130
  return this.accessToken;
97
131
  }
98
132
  } catch {}
@@ -100,7 +134,7 @@ var GCPTransport = class {
100
134
  const token = await this.fetchServiceAccountToken();
101
135
  if (token) {
102
136
  this.accessToken = token;
103
- this.tokenExpiry = Date.now() + 3300 * 1e3;
137
+ this.tokenExpiry = Date.now() + 33e5;
104
138
  return this.accessToken;
105
139
  }
106
140
  } catch (err) {
@@ -166,6 +200,15 @@ var GCPTransport = class {
166
200
  label: "GCP token request"
167
201
  }).then((res) => JSON.parse(res.body).access_token ?? null, () => null);
168
202
  }
203
+ secretValues(token) {
204
+ var _this$credentials, _this$credentials2;
205
+ return [
206
+ ...((_this$credentials = this.credentials) === null || _this$credentials === void 0 ? void 0 : _this$credentials.private_key.split("\n")) ?? [],
207
+ (_this$credentials2 = this.credentials) === null || _this$credentials2 === void 0 ? void 0 : _this$credentials2.private_key,
208
+ this.accessToken ?? void 0,
209
+ token
210
+ ];
211
+ }
169
212
  async writeEntries(entries) {
170
213
  const token = await this.getAccessToken();
171
214
  if (!token) throw new Error("GCPTransport: no access token available");
@@ -182,11 +225,11 @@ var GCPTransport = class {
182
225
  },
183
226
  body,
184
227
  timeoutMs: this.requestTimeoutMs,
185
- label: "GCP Logging API"
228
+ label: "GCP Logging API",
229
+ secrets: () => this.secretValues(token)
186
230
  });
187
231
  }
188
232
  };
189
-
190
- //#endregion
191
233
  export { GCPTransport as t };
192
- //# sourceMappingURL=gcp.transport-DBX5TPIy.mjs.map
234
+
235
+ //# sourceMappingURL=gcp.transport-DVCmnA3b.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcp.transport-DVCmnA3b.mjs","names":[],"sources":["../src/transports/gcp.transport.ts"],"sourcesContent":["/**\n * logixia — Google Cloud Logging (Stackdriver) transport\n *\n * Maps logixia log entries to the GCP Logging REST API format:\n * - Maps `level` → `severity` (INFO, WARNING, ERROR, CRITICAL…)\n * - Injects `logging.googleapis.com/trace` for Cloud Trace correlation\n * - Sets `traceSampled: true` whenever a trace id is present\n * (`logging.googleapis.com/spanId` and `sourceLocation` are not populated)\n * - Supports structured `jsonPayload` and plain `textPayload`\n *\n * Auth: the metadata server on GCE/GKE/Cloud Run, or a service-account key passed\n * as `credentials`. The `GOOGLE_APPLICATION_CREDENTIALS` key-file variable is not\n * read; full ADC is not supported.\n *\n * @example\n * ```ts\n * import { createLogger } from 'logixia';\n * import { GCPTransport } from 'logixia/gcp';\n *\n * const logger = createLogger({\n * transports: {\n * custom: [\n * new GCPTransport({\n * projectId: process.env.GOOGLE_CLOUD_PROJECT,\n * logName: 'projects/my-project/logs/api',\n * resource: { type: 'gce_instance', labels: { instance_id: '123' } },\n * }),\n * ],\n * },\n * });\n * ```\n */\n\nimport * as https from 'node:https';\n\nimport type { IAsyncTransport, TransportLogEntry } from '../types/transport.types';\nimport { internalError } 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 { mergeCoreFields } from './entry-fields';\nimport { protectSecrets } from './secret-config';\n\nexport interface GCPMonitoredResource {\n type: string;\n labels?: Record<string, string>;\n}\n\nexport interface GCPTransportConfig {\n /** GCP project ID. Falls back to `GOOGLE_CLOUD_PROJECT` or `GCLOUD_PROJECT` env vars. */\n projectId?: string;\n /**\n * Full log name, e.g. `'projects/my-project/logs/api'`.\n * If omitted, defaults to `projects/{projectId}/logs/logixia`.\n */\n logName?: string;\n /** Monitored resource descriptor. Defaults to `global`. */\n resource?: GCPMonitoredResource;\n /**\n * Service account key JSON (parsed). If omitted, only the metadata server\n * (GCE/GKE/Cloud Run) is tried; the key-file env var is not read.\n */\n credentials?: {\n client_email: string;\n private_key: string;\n };\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\ntype GcpSeverity =\n | 'DEFAULT'\n | 'DEBUG'\n | 'INFO'\n | 'NOTICE'\n | 'WARNING'\n | 'ERROR'\n | 'CRITICAL'\n | 'ALERT'\n | 'EMERGENCY';\n\nfunction toGcpSeverity(level: string): GcpSeverity {\n switch (level.toLowerCase()) {\n case 'trace':\n case 'verbose':\n case 'debug':\n return 'DEBUG';\n case 'info':\n case 'log':\n return 'INFO';\n case 'warn':\n case 'warning':\n return 'WARNING';\n case 'error':\n return 'ERROR';\n case 'critical':\n case 'fatal':\n return 'CRITICAL';\n default:\n return 'DEFAULT';\n }\n}\n\n/**\n * Google Cloud Logging transport with trace correlation and GCP severity mapping.\n */\nexport class GCPTransport implements IAsyncTransport {\n public readonly name = 'gcp';\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 projectId: string;\n private readonly logName: string;\n private readonly resource: GCPMonitoredResource;\n private readonly credentials: { client_email: string; private_key: string } | undefined;\n\n constructor(config: GCPTransportConfig) {\n protectSecrets(this, ['credentials', 'accessToken']);\n this.projectId =\n config.projectId ??\n process.env['GOOGLE_CLOUD_PROJECT'] ??\n process.env['GCLOUD_PROJECT'] ??\n 'unknown-project';\n this.logName = config.logName ?? `projects/${this.projectId}/logs/logixia`;\n this.resource = config.resource ?? { type: 'global' };\n this.credentials = config.credentials;\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: 'GCPTransport',\n batchSize: this.batchSize,\n maxBufferSize: config.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,\n send: (entries) => this.writeEntries(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 buildLogEntry(entry: TransportLogEntry): Record<string, unknown> {\n const gcpEntry: Record<string, unknown> = {\n logName: this.logName,\n resource: this.resource,\n severity: toGcpSeverity(entry.level),\n timestamp: entry.timestamp ?? new Date().toISOString(),\n jsonPayload: mergeCoreFields(\n { message: entry.message, appName: entry.appName, context: entry.context },\n entry.data\n ),\n };\n\n // GCP Cloud Trace correlation\n if (entry.traceId) {\n gcpEntry['trace'] = `projects/${this.projectId}/traces/${entry.traceId}`;\n gcpEntry['traceSampled'] = true;\n }\n\n return gcpEntry;\n }\n\n private async getAccessToken(): Promise<string | null> {\n if (this.accessToken && Date.now() < this.tokenExpiry) return this.accessToken;\n\n // Try ADC metadata server (GCE/GKE/Cloud Run)\n try {\n const token = await this.fetchFromMetadataServer();\n if (token) {\n this.accessToken = token;\n this.tokenExpiry = Date.now() + 55 * 60 * 1000; // 55 minutes\n return this.accessToken;\n }\n } catch {\n /* fall through to service account */\n }\n\n // Try service account key\n if (this.credentials) {\n try {\n const token = await this.fetchServiceAccountToken();\n if (token) {\n this.accessToken = token;\n this.tokenExpiry = Date.now() + 55 * 60 * 1000;\n return this.accessToken;\n }\n } catch (err) {\n internalError('GCPTransport service account token error', err);\n }\n }\n\n return null;\n }\n\n private fetchFromMetadataServer(): Promise<string | null> {\n return new Promise((resolve, reject) => {\n const req = https.request(\n {\n hostname: 'metadata.google.internal',\n path: '/computeMetadata/v1/instance/service-accounts/default/token',\n headers: { 'Metadata-Flavor': 'Google' },\n timeout: 3000,\n },\n (res) => {\n let raw = '';\n res.on('data', (c: Buffer) => (raw += c.toString()));\n res.on('end', () => {\n if (res.statusCode === 200) {\n try {\n const parsed = JSON.parse(raw) as { access_token?: string };\n resolve(parsed.access_token ?? null);\n } catch {\n resolve(null);\n }\n } else {\n resolve(null);\n }\n });\n }\n );\n req.on('error', reject);\n req.on('timeout', () => {\n req.destroy();\n reject(new Error('metadata timeout'));\n });\n req.end();\n });\n }\n\n private fetchServiceAccountToken(): Promise<string | null> {\n // Minimal JWT creation for service account — only for environments without\n // the full Google Auth Library. For production workloads, prefer ADC.\n if (!this.credentials) return Promise.resolve(null);\n\n const iat = Math.floor(Date.now() / 1000);\n const exp = iat + 3600;\n const scope = 'https://www.googleapis.com/auth/logging.write';\n\n const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).toString('base64url');\n const payload = Buffer.from(\n JSON.stringify({\n iss: this.credentials.client_email,\n scope,\n aud: 'https://oauth2.googleapis.com/token',\n exp,\n iat,\n })\n ).toString('base64url');\n\n const signingInput = `${header}.${payload}`;\n\n let signature: string;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const sign = require('node:crypto').createSign('RSA-SHA256');\n sign.update(signingInput);\n signature = sign.sign(this.credentials.private_key, 'base64url') as string;\n } catch {\n return Promise.resolve(null);\n }\n\n const jwt = `${signingInput}.${signature}`;\n const body = `grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=${jwt}`;\n\n return httpRequest({\n url: new URL('https://oauth2.googleapis.com/token'),\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body,\n timeoutMs: this.requestTimeoutMs,\n label: 'GCP token request',\n }).then(\n (res) => (JSON.parse(res.body) as { access_token?: string }).access_token ?? null,\n () => null\n );\n }\n\n private secretValues(token?: string): (string | undefined)[] {\n return [\n ...(this.credentials?.private_key.split('\\n') ?? []),\n this.credentials?.private_key,\n this.accessToken ?? undefined,\n token,\n ];\n }\n\n private async writeEntries(entries: TransportLogEntry[]): Promise<void> {\n const token = await this.getAccessToken();\n if (!token) {\n throw new Error('GCPTransport: no access token available');\n }\n\n const body = stringifyForLog({\n logName: this.logName,\n resource: this.resource,\n entries: entries.map((e) => this.buildLogEntry(e)),\n });\n\n await httpRequest({\n url: new URL('https://logging.googleapis.com/v2/entries:write'),\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n body,\n timeoutMs: this.requestTimeoutMs,\n label: 'GCP Logging API',\n secrets: () => this.secretValues(token),\n });\n }\n}\n"],"mappings":"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"}