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.
- package/README.md +172 -2597
- package/dist/async-context-2mbUKhcl.mjs +132 -0
- package/dist/async-context-2mbUKhcl.mjs.map +1 -0
- package/dist/async-context-DUFMDYAr.js +148 -0
- package/dist/async-context-DUFMDYAr.js.map +1 -0
- package/dist/{azure-monitor.transport-CrvfHd9C.js → azure-monitor.transport-Bu4S2I2q.js} +32 -20
- package/dist/azure-monitor.transport-Bu4S2I2q.js.map +1 -0
- package/dist/{azure-monitor.transport-Cn_3Usvu.d.ts → azure-monitor.transport-Bvf7Xegt.d.ts} +3 -6
- package/dist/azure-monitor.transport-Bvf7Xegt.d.ts.map +1 -0
- package/dist/{azure-monitor.transport-CHnjVRF-.mjs → azure-monitor.transport-C1RWAumT.mjs} +27 -15
- package/dist/azure-monitor.transport-C1RWAumT.mjs.map +1 -0
- package/dist/{azure-monitor.transport-D7Da4RAb.d.mts → azure-monitor.transport-CbSHOnNX.d.mts} +3 -6
- package/dist/azure-monitor.transport-CbSHOnNX.d.mts.map +1 -0
- package/dist/azure.d.mts +1 -2
- package/dist/azure.d.ts +1 -2
- package/dist/azure.js +3 -6
- package/dist/azure.mjs +1 -6
- package/dist/{batch-buffer-PGvSgbmd.mjs → batch-buffer-BvXLDPlV.mjs} +21 -7
- package/dist/batch-buffer-BvXLDPlV.mjs.map +1 -0
- package/dist/{batch-buffer-BY-mRWIK.js → batch-buffer-C7Am4Mw-.js} +53 -40
- package/dist/batch-buffer-C7Am4Mw-.js.map +1 -0
- package/dist/browser.d.mts +47 -27
- package/dist/browser.d.mts.map +1 -1
- package/dist/browser.d.ts +47 -27
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +464 -62
- package/dist/browser.js.map +1 -1
- package/dist/browser.mjs +465 -65
- package/dist/browser.mjs.map +1 -1
- package/dist/{builder-0gEzRopq.mjs → builder-DhNhvOkt.mjs} +307 -246
- package/dist/builder-DhNhvOkt.mjs.map +1 -0
- package/dist/{builder-D44QktIo.js → builder-yFvJrz7o.js} +435 -399
- package/dist/builder-yFvJrz7o.js.map +1 -0
- package/dist/cli/index.js +216 -125
- package/dist/cloudwatch.d.mts +1 -2
- package/dist/cloudwatch.d.ts +1 -2
- package/dist/cloudwatch.js +3 -7
- package/dist/cloudwatch.mjs +1 -7
- package/dist/{cloudwatch.transport-DsEq633o.d.ts → cloudwatch.transport-CQyT6U91.d.ts} +2 -6
- package/dist/cloudwatch.transport-CQyT6U91.d.ts.map +1 -0
- package/dist/{cloudwatch.transport-BO1ahxw-.js → cloudwatch.transport-Ch1Uae-0.js} +61 -36
- package/dist/cloudwatch.transport-Ch1Uae-0.js.map +1 -0
- package/dist/{cloudwatch.transport-UlyZFWMn.d.mts → cloudwatch.transport-CjVrI6S4.d.mts} +2 -6
- package/dist/cloudwatch.transport-CjVrI6S4.d.mts.map +1 -0
- package/dist/{cloudwatch.transport-DZ6ies7g.mjs → cloudwatch.transport-DZqF2xZO.mjs} +54 -28
- package/dist/cloudwatch.transport-DZqF2xZO.mjs.map +1 -0
- package/dist/{coerce.utils-Cu6IUTgX.mjs → coerce.utils-B_rEudCn.mjs} +3 -4
- package/dist/{coerce.utils-Cu6IUTgX.mjs.map → coerce.utils-B_rEudCn.mjs.map} +1 -1
- package/dist/{coerce.utils-B-LilukN.js → coerce.utils-C1C5sIX0.js} +8 -10
- package/dist/{coerce.utils-B-LilukN.js.map → coerce.utils-C1C5sIX0.js.map} +1 -1
- package/dist/correlation.d.mts +15 -69
- package/dist/correlation.d.mts.map +1 -1
- package/dist/correlation.d.ts +15 -69
- package/dist/correlation.d.ts.map +1 -1
- package/dist/correlation.js +69 -20
- package/dist/correlation.js.map +1 -1
- package/dist/correlation.mjs +67 -17
- package/dist/correlation.mjs.map +1 -1
- package/dist/{entry-fields-BvOLFlJ4.js → entry-fields-BSLkzw18.js} +16 -13
- package/dist/entry-fields-BSLkzw18.js.map +1 -0
- package/dist/{entry-fields-BJkuPnIg.mjs → entry-fields-J1OBi24o.mjs} +11 -7
- package/dist/entry-fields-J1OBi24o.mjs.map +1 -0
- package/dist/gcp.d.mts +1 -2
- package/dist/gcp.d.ts +1 -2
- package/dist/gcp.js +3 -7
- package/dist/gcp.mjs +1 -7
- package/dist/{gcp.transport-B7oc97sG.d.ts → gcp.transport-BCIvdFV-.d.ts} +5 -8
- package/dist/gcp.transport-BCIvdFV-.d.ts.map +1 -0
- package/dist/{gcp.transport-GrJurKo8.d.mts → gcp.transport-D9SdLPwP.d.mts} +5 -8
- package/dist/gcp.transport-D9SdLPwP.d.mts.map +1 -0
- package/dist/{gcp.transport-DBX5TPIy.mjs → gcp.transport-DVCmnA3b.mjs} +59 -16
- package/dist/gcp.transport-DVCmnA3b.mjs.map +1 -0
- package/dist/{gcp.transport-CHV8MGgz.js → gcp.transport-Dy2Lf8FI.js} +64 -22
- package/dist/gcp.transport-Dy2Lf8FI.js.map +1 -0
- package/dist/http-shapes-BnBkJcid.d.mts +32 -0
- package/dist/http-shapes-BnBkJcid.d.mts.map +1 -0
- package/dist/http-shapes-BnBkJcid.d.ts +32 -0
- package/dist/http-shapes-BnBkJcid.d.ts.map +1 -0
- package/dist/{index-3mw5gaCw.d.mts → index-BSTtFcQ6.d.mts} +142 -13
- package/dist/index-BSTtFcQ6.d.mts.map +1 -0
- package/dist/{index-DhIoploR.d.ts → index-CFDGkI8n.d.ts} +142 -13
- package/dist/index-CFDGkI8n.d.ts.map +1 -0
- package/dist/index.d.mts +114 -217
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +113 -216
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +182 -279
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +160 -256
- package/dist/index.mjs.map +1 -1
- package/dist/internal-log-Bg4V1cPg.mjs +170 -0
- package/dist/internal-log-Bg4V1cPg.mjs.map +1 -0
- package/dist/internal-log-By4Govmu.js +223 -0
- package/dist/internal-log-By4Govmu.js.map +1 -0
- package/dist/{logitron-logger-UVeN-mQm.d.ts → logitron-logger-D22Yb61K.d.mts} +121 -100
- package/dist/logitron-logger-D22Yb61K.d.mts.map +1 -0
- package/dist/{logitron-logger-BFDSO8Io.d.mts → logitron-logger-D_MYYrQt.d.ts} +121 -100
- package/dist/logitron-logger-D_MYYrQt.d.ts.map +1 -0
- package/dist/middleware.d.mts +13 -28
- package/dist/middleware.d.mts.map +1 -1
- package/dist/middleware.d.ts +13 -28
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +20 -81
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +17 -79
- package/dist/middleware.mjs.map +1 -1
- package/dist/nest.d.mts +37 -48
- package/dist/nest.d.mts.map +1 -1
- package/dist/nest.d.ts +37 -48
- package/dist/nest.d.ts.map +1 -1
- package/dist/nest.js +124 -135
- package/dist/nest.js.map +1 -1
- package/dist/nest.mjs +69 -78
- package/dist/nest.mjs.map +1 -1
- package/dist/{transport.manager-BiuCAJd2.mjs → otel-Dt5riXuE.mjs} +275 -117
- package/dist/otel-Dt5riXuE.mjs.map +1 -0
- package/dist/{transport.manager-CXbKKdVE.js → otel-DyeH6DVR.js} +373 -178
- package/dist/otel-DyeH6DVR.js.map +1 -0
- package/dist/own-property-BWkibepN.js +40 -0
- package/dist/own-property-BWkibepN.js.map +1 -0
- package/dist/own-property-CjFw9qwZ.mjs +29 -0
- package/dist/own-property-CjFw9qwZ.mjs.map +1 -0
- package/dist/{trace.utils-CvXgaiQT.mjs → request-trace-BgNykH9M.mjs} +40 -9
- package/dist/request-trace-BgNykH9M.mjs.map +1 -0
- package/dist/{trace.utils-DpBOLDtq.js → request-trace-ByujsUZk.js} +117 -77
- package/dist/request-trace-ByujsUZk.js.map +1 -0
- package/dist/rolldown-runtime-D3PmRqjj.mjs +4 -0
- package/dist/{safe-stringify-DJ66tW5I.mjs → safe-stringify-B5p5ktFe.mjs} +39 -15
- package/dist/safe-stringify-B5p5ktFe.mjs.map +1 -0
- package/dist/{safe-stringify-DWo3aZp_.js → safe-stringify-J3sh-7YM.js} +59 -36
- package/dist/safe-stringify-J3sh-7YM.js.map +1 -0
- package/dist/{search-C4V0OMEn.js → search-CRjhHxe9.js} +319 -92
- package/dist/search-CRjhHxe9.js.map +1 -0
- package/dist/{search-CNh9uVwR.d.ts → search-DA3ft3X5.d.mts} +5 -22
- package/dist/search-DA3ft3X5.d.mts.map +1 -0
- package/dist/{search-BFbu_oVn.mjs → search-DAXKE-P9.mjs} +289 -62
- package/dist/search-DAXKE-P9.mjs.map +1 -0
- package/dist/{search-DXANsQ2n.d.mts → search-DEl3Pe47.d.ts} +5 -22
- package/dist/search-DEl3Pe47.d.ts.map +1 -0
- package/dist/search.d.mts +2 -4
- package/dist/search.d.ts +2 -4
- package/dist/search.js +3 -4
- package/dist/search.mjs +1 -4
- package/dist/secret-config-CmK2wVkr.js +272 -0
- package/dist/secret-config-CmK2wVkr.js.map +1 -0
- package/dist/secret-config-Dhb6pqTU.mjs +210 -0
- package/dist/secret-config-Dhb6pqTU.mjs.map +1 -0
- package/dist/{slack.transport-CXjQYOes.d.ts → slack.transport-BDyENuR0.d.mts} +56 -9
- package/dist/slack.transport-BDyENuR0.d.mts.map +1 -0
- package/dist/{slack.transport-Bzw7sucI.mjs → slack.transport-BTEwsREY.mjs} +263 -31
- package/dist/slack.transport-BTEwsREY.mjs.map +1 -0
- package/dist/{slack.transport-BZzoXDPn.d.mts → slack.transport-C6E0l4kw.d.ts} +56 -9
- package/dist/slack.transport-C6E0l4kw.d.ts.map +1 -0
- package/dist/{slack.transport-BfJRouB2.js → slack.transport-UbYvOdKx.js} +286 -42
- package/dist/slack.transport-UbYvOdKx.js.map +1 -0
- package/dist/testing.d.mts +13 -11
- package/dist/testing.d.mts.map +1 -1
- package/dist/testing.d.ts +13 -11
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +41 -7
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +40 -6
- package/dist/testing.mjs.map +1 -1
- package/dist/{transport.types-CGoHpWQR.d.ts → transport.types-CzJq4IrF.d.mts} +14 -5
- package/dist/transport.types-CzJq4IrF.d.mts.map +1 -0
- package/dist/{transport.types-BMnurPcc.d.mts → transport.types-CzJq4IrF.d.ts} +14 -5
- package/dist/transport.types-CzJq4IrF.d.ts.map +1 -0
- package/dist/transports.d.mts +39 -41
- package/dist/transports.d.mts.map +1 -1
- package/dist/transports.d.ts +39 -41
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports.js +19 -22
- package/dist/transports.mjs +7 -13
- package/dist/worker-entry.js +438 -107
- package/dist/worker.d.mts +1 -2
- package/dist/worker.d.ts +1 -2
- package/dist/worker.js +3 -4
- package/dist/worker.mjs +1 -4
- package/dist/{worker.transport-JvGPog3C.d.mts → worker.transport-B9mCQSM1.d.ts} +7 -6
- package/dist/worker.transport-B9mCQSM1.d.ts.map +1 -0
- package/dist/{worker.transport-CdzSZJmw.d.ts → worker.transport-Bl8lFRck.d.mts} +7 -6
- package/dist/worker.transport-Bl8lFRck.d.mts.map +1 -0
- package/dist/{worker.transport-lPxO2MVL.mjs → worker.transport-BmmOKb1-.mjs} +43 -10
- package/dist/worker.transport-BmmOKb1-.mjs.map +1 -0
- package/dist/{worker.transport-BdPxu3PN.js → worker.transport-DxxbqQCG.js} +48 -15
- package/dist/worker.transport-DxxbqQCG.js.map +1 -0
- package/package.json +51 -75
- package/dist/async-context-BzJrbfQs.mjs +0 -72
- package/dist/async-context-BzJrbfQs.mjs.map +0 -1
- package/dist/async-context-DyQQYq3I.js +0 -92
- package/dist/async-context-DyQQYq3I.js.map +0 -1
- package/dist/azure-monitor.transport-CHnjVRF-.mjs.map +0 -1
- package/dist/azure-monitor.transport-Cn_3Usvu.d.ts.map +0 -1
- package/dist/azure-monitor.transport-CrvfHd9C.js.map +0 -1
- package/dist/azure-monitor.transport-D7Da4RAb.d.mts.map +0 -1
- package/dist/batch-buffer-BY-mRWIK.js.map +0 -1
- package/dist/batch-buffer-PGvSgbmd.mjs.map +0 -1
- package/dist/builder-0gEzRopq.mjs.map +0 -1
- package/dist/builder-D44QktIo.js.map +0 -1
- package/dist/chunk-DLSkYcPk.mjs +0 -7
- package/dist/chunk-nOFOJqeH.js +0 -30
- package/dist/cloudwatch.transport-BO1ahxw-.js.map +0 -1
- package/dist/cloudwatch.transport-DZ6ies7g.mjs.map +0 -1
- package/dist/cloudwatch.transport-DsEq633o.d.ts.map +0 -1
- package/dist/cloudwatch.transport-UlyZFWMn.d.mts.map +0 -1
- package/dist/entry-fields-BJkuPnIg.mjs.map +0 -1
- package/dist/entry-fields-BvOLFlJ4.js.map +0 -1
- package/dist/gcp.transport-B7oc97sG.d.ts.map +0 -1
- package/dist/gcp.transport-CHV8MGgz.js.map +0 -1
- package/dist/gcp.transport-DBX5TPIy.mjs.map +0 -1
- package/dist/gcp.transport-GrJurKo8.d.mts.map +0 -1
- package/dist/index-3mw5gaCw.d.mts.map +0 -1
- package/dist/index-DhIoploR.d.ts.map +0 -1
- package/dist/internal-log-BUJYMZbE.js +0 -70
- package/dist/internal-log-BUJYMZbE.js.map +0 -1
- package/dist/internal-log-Cm8SOWSV.mjs +0 -52
- package/dist/internal-log-Cm8SOWSV.mjs.map +0 -1
- package/dist/logitron-logger-BFDSO8Io.d.mts.map +0 -1
- package/dist/logitron-logger-UVeN-mQm.d.ts.map +0 -1
- package/dist/safe-stringify-DJ66tW5I.mjs.map +0 -1
- package/dist/safe-stringify-DWo3aZp_.js.map +0 -1
- package/dist/search-BFbu_oVn.mjs.map +0 -1
- package/dist/search-C4V0OMEn.js.map +0 -1
- package/dist/search-CNh9uVwR.d.ts.map +0 -1
- package/dist/search-DXANsQ2n.d.mts.map +0 -1
- package/dist/slack.transport-BZzoXDPn.d.mts.map +0 -1
- package/dist/slack.transport-BfJRouB2.js.map +0 -1
- package/dist/slack.transport-Bzw7sucI.mjs.map +0 -1
- package/dist/slack.transport-CXjQYOes.d.ts.map +0 -1
- package/dist/trace.utils-CvXgaiQT.mjs.map +0 -1
- package/dist/trace.utils-DpBOLDtq.js.map +0 -1
- package/dist/transport.manager-BiuCAJd2.mjs.map +0 -1
- package/dist/transport.manager-CXbKKdVE.js.map +0 -1
- package/dist/transport.types-BMnurPcc.d.mts.map +0 -1
- package/dist/transport.types-CGoHpWQR.d.ts.map +0 -1
- package/dist/worker.transport-BdPxu3PN.js.map +0 -1
- package/dist/worker.transport-CdzSZJmw.d.ts.map +0 -1
- package/dist/worker.transport-JvGPog3C.d.mts.map +0 -1
- package/dist/worker.transport-lPxO2MVL.mjs.map +0 -1
package/dist/correlation.d.mts
CHANGED
|
@@ -1,58 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* logixia — Microservices Correlation ID Propagation
|
|
4
|
-
*
|
|
5
|
-
* Provides zero-boilerplate correlation ID generation and propagation for
|
|
6
|
-
* distributed systems. A single correlation ID travels through every service
|
|
7
|
-
* in a request fan-out, making it trivial to join logs across services.
|
|
8
|
-
*
|
|
9
|
-
* Features:
|
|
10
|
-
* - UUID v4 correlation ID generation (via `crypto.randomUUID()`)
|
|
11
|
-
* - Express / Fastify / NestJS incoming-request middleware
|
|
12
|
-
* - `correlationFetch()` — wraps global `fetch`, auto-injects `X-Correlation-ID`
|
|
13
|
-
* - `createCorrelationAxiosInterceptor()` — axios request interceptor
|
|
14
|
-
* - `childFromRequest(req, logger)` — child logger with all request identifiers
|
|
15
|
-
* - Kafka / SQS / RabbitMQ message context helpers
|
|
16
|
-
* - Integration with `LogixiaContext` (AsyncLocalStorage) so every `logger.info()`
|
|
17
|
-
* call automatically includes the correlationId without per-call passing.
|
|
18
|
-
*
|
|
19
|
-
* @example Express middleware
|
|
20
|
-
* ```ts
|
|
21
|
-
* import { correlationMiddleware } from 'logixia/correlation';
|
|
22
|
-
* import { logger } from 'logixia';
|
|
23
|
-
*
|
|
24
|
-
* app.use(correlationMiddleware());
|
|
25
|
-
*
|
|
26
|
-
* app.get('/orders', (req, res) => {
|
|
27
|
-
* // correlationId is automatically in every log inside this request
|
|
28
|
-
* logger.info('Listing orders');
|
|
29
|
-
* res.json({ ok: true });
|
|
30
|
-
* });
|
|
31
|
-
* ```
|
|
32
|
-
*
|
|
33
|
-
* @example Outbound fetch with correlation
|
|
34
|
-
* ```ts
|
|
35
|
-
* import { correlationFetch } from 'logixia/correlation';
|
|
36
|
-
*
|
|
37
|
-
* // Reads correlationId from current LogixiaContext and injects it as header
|
|
38
|
-
* const data = await correlationFetch('https://payments.internal/charge', {
|
|
39
|
-
* method: 'POST',
|
|
40
|
-
* body: JSON.stringify({ amount: 100 }),
|
|
41
|
-
* });
|
|
42
|
-
* ```
|
|
43
|
-
*
|
|
44
|
-
* @example Manual correlation context
|
|
45
|
-
* ```ts
|
|
46
|
-
* import { withCorrelationId } from 'logixia/correlation';
|
|
47
|
-
*
|
|
48
|
-
* await withCorrelationId('trace-abc-123', async () => {
|
|
49
|
-
* // All logs and outbound fetch calls in here carry 'trace-abc-123'
|
|
50
|
-
* await processOrder();
|
|
51
|
-
* });
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
1
|
+
import { n as HttpResponseShape, t as HttpRequestShape } from "./http-shapes-BnBkJcid.mjs";
|
|
54
2
|
/** Fields injected into the log context by the correlation middleware. */
|
|
55
|
-
interface CorrelationContext {
|
|
3
|
+
export interface CorrelationContext {
|
|
56
4
|
correlationId: string;
|
|
57
5
|
traceId?: string;
|
|
58
6
|
/** Service that originated the request (from `X-Origin-Service` header). */
|
|
@@ -60,7 +8,7 @@ interface CorrelationContext {
|
|
|
60
8
|
/** Index signature so CorrelationContext is assignable to LogContext. */
|
|
61
9
|
[key: string]: unknown;
|
|
62
10
|
}
|
|
63
|
-
interface CorrelationMiddlewareOptions {
|
|
11
|
+
export interface CorrelationMiddlewareOptions {
|
|
64
12
|
/**
|
|
65
13
|
* HTTP header to read/write the correlation ID from/to.
|
|
66
14
|
* @default 'x-correlation-id'
|
|
@@ -100,12 +48,12 @@ interface CorrelationMiddlewareOptions {
|
|
|
100
48
|
* function's JSDoc and README promise. `logixia/correlation` is Node-only
|
|
101
49
|
* (it builds on `AsyncLocalStorage`), so importing `node:crypto` is safe here.
|
|
102
50
|
*/
|
|
103
|
-
declare function generateCorrelationId(): string;
|
|
51
|
+
export declare function generateCorrelationId(): string;
|
|
104
52
|
/**
|
|
105
53
|
* Return the correlation ID from the current `LogixiaContext`, or `undefined`
|
|
106
54
|
* when called outside any context scope.
|
|
107
55
|
*/
|
|
108
|
-
declare function getCurrentCorrelationId(): string | undefined;
|
|
56
|
+
export declare function getCurrentCorrelationId(): string | undefined;
|
|
109
57
|
/**
|
|
110
58
|
* Run `callback` inside a correlation context scope.
|
|
111
59
|
*
|
|
@@ -119,7 +67,7 @@ declare function getCurrentCorrelationId(): string | undefined;
|
|
|
119
67
|
* });
|
|
120
68
|
* ```
|
|
121
69
|
*/
|
|
122
|
-
declare function withCorrelationId<T>(correlationId: string | undefined, callback: () => T): T;
|
|
70
|
+
export declare function withCorrelationId<T>(correlationId: string | undefined, callback: () => T): T;
|
|
123
71
|
/**
|
|
124
72
|
* Express-compatible middleware that:
|
|
125
73
|
* 1. Reads `X-Correlation-ID` from the incoming request (or generates a new one)
|
|
@@ -133,7 +81,7 @@ declare function withCorrelationId<T>(correlationId: string | undefined, callbac
|
|
|
133
81
|
* app.use(correlationMiddleware());
|
|
134
82
|
* ```
|
|
135
83
|
*/
|
|
136
|
-
declare function correlationMiddleware(options?: CorrelationMiddlewareOptions): (req:
|
|
84
|
+
export declare function correlationMiddleware(options?: CorrelationMiddlewareOptions): (req: HttpRequestShape, res: HttpResponseShape, next: () => void) => void;
|
|
137
85
|
/**
|
|
138
86
|
* Fastify `onRequest` lifecycle hook — same functionality as `correlationMiddleware`
|
|
139
87
|
* but adapted to Fastify's hook API.
|
|
@@ -144,7 +92,7 @@ declare function correlationMiddleware(options?: CorrelationMiddlewareOptions):
|
|
|
144
92
|
* fastify.addHook('onRequest', correlationFastifyHook());
|
|
145
93
|
* ```
|
|
146
94
|
*/
|
|
147
|
-
declare function correlationFastifyHook(options?: CorrelationMiddlewareOptions): (request:
|
|
95
|
+
export declare function correlationFastifyHook(options?: CorrelationMiddlewareOptions): (request: HttpRequestShape, reply: HttpResponseShape, done: () => void) => void;
|
|
148
96
|
/**
|
|
149
97
|
* A drop-in replacement for `fetch` that automatically injects the current
|
|
150
98
|
* `correlationId` from `LogixiaContext` as an `X-Correlation-ID` request header.
|
|
@@ -162,7 +110,7 @@ declare function correlationFastifyHook(options?: CorrelationMiddlewareOptions):
|
|
|
162
110
|
* });
|
|
163
111
|
* ```
|
|
164
112
|
*/
|
|
165
|
-
declare function correlationFetch(input: string | URL | Request, init?: RequestInit, options?: {
|
|
113
|
+
export declare function correlationFetch(input: string | URL | Request, init?: RequestInit, options?: {
|
|
166
114
|
header?: string;
|
|
167
115
|
}): Promise<Response>;
|
|
168
116
|
/**
|
|
@@ -177,10 +125,10 @@ declare function correlationFetch(input: string | URL | Request, init?: RequestI
|
|
|
177
125
|
* axios.interceptors.request.use(createCorrelationAxiosInterceptor());
|
|
178
126
|
* ```
|
|
179
127
|
*/
|
|
180
|
-
declare function createCorrelationAxiosInterceptor(options?: {
|
|
128
|
+
export declare function createCorrelationAxiosInterceptor(options?: {
|
|
181
129
|
header?: string;
|
|
182
130
|
}): (config: Record<string, unknown>) => Record<string, unknown>;
|
|
183
|
-
interface ChildFromRequestOptions {
|
|
131
|
+
export interface ChildFromRequestOptions {
|
|
184
132
|
/** Header to read the correlation ID from. @default 'x-correlation-id' */
|
|
185
133
|
correlationHeader?: string;
|
|
186
134
|
/**
|
|
@@ -222,9 +170,9 @@ interface ChildFromRequestOptions {
|
|
|
222
170
|
* });
|
|
223
171
|
* ```
|
|
224
172
|
*/
|
|
225
|
-
declare function childFromRequest<TLogger extends {
|
|
173
|
+
export declare function childFromRequest<TLogger extends {
|
|
226
174
|
child(...args: never[]): TLogger;
|
|
227
|
-
}>(req:
|
|
175
|
+
}>(req: HttpRequestShape, logger: TLogger, options?: ChildFromRequestOptions): TLogger;
|
|
228
176
|
/**
|
|
229
177
|
* Extract a correlation context from a Kafka / RabbitMQ / SQS message object.
|
|
230
178
|
*
|
|
@@ -243,7 +191,7 @@ declare function childFromRequest<TLogger extends {
|
|
|
243
191
|
* });
|
|
244
192
|
* ```
|
|
245
193
|
*/
|
|
246
|
-
declare function extractMessageCorrelationId(message: Record<string, unknown>): string | undefined;
|
|
194
|
+
export declare function extractMessageCorrelationId(message: Record<string, unknown>): string | undefined;
|
|
247
195
|
/**
|
|
248
196
|
* Build Kafka message headers containing the current correlation context.
|
|
249
197
|
*
|
|
@@ -257,7 +205,5 @@ declare function extractMessageCorrelationId(message: Record<string, unknown>):
|
|
|
257
205
|
* });
|
|
258
206
|
* ```
|
|
259
207
|
*/
|
|
260
|
-
declare function buildKafkaCorrelationHeaders(): Record<string, string>;
|
|
261
|
-
//#endregion
|
|
262
|
-
export { ChildFromRequestOptions, CorrelationContext, CorrelationMiddlewareOptions, buildKafkaCorrelationHeaders, childFromRequest, correlationFastifyHook, correlationFetch, correlationMiddleware, createCorrelationAxiosInterceptor, extractMessageCorrelationId, generateCorrelationId, getCurrentCorrelationId, withCorrelationId };
|
|
208
|
+
export declare function buildKafkaCorrelationHeaders(): Record<string, string>;
|
|
263
209
|
//# sourceMappingURL=correlation.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"correlation.d.mts","names":[],"sources":["../src/correlation.ts"],"
|
|
1
|
+
{"version":3,"file":"correlation.d.mts","names":[],"sources":["../src/correlation.ts"],"mappings":";;iBA8DiB;EACf;EACA;;EAEA;;GAEC;;iBAGc;;;;;EAKf;;;;;EAKA;;;;;EAKA;;;;;;;EAOA;;;;;;EAMA;;;;;;;;;;;;wBAec;;;;;wBAwBA;;;;;;;;;;;;;;wBAkBA,kBAAkB,GAAG,mCAAmC,gBAAgB,IAAI;;;;;;;;;;;;;;wBAoB5E,sBAAsB,UAAS,gCAU3C,KAAK,kBACL,KAAK,mBACL;;;;;;;;;;;wBAkCY,uBAAuB,UAAS,gCAU5C,SAAS,kBACT,OAAO,mBACP;;;;;;;;;;;;;;;;;;wBAyCkB,iBACpB,gBAAgB,MAAM,SACtB,OAAM,aACN;EAAW;IACV,QAAQ;;;;;;;;;;;;;wBAwCK,kCACd;EAAW;KACT,QAAQ,4BAA4B;iBAuBvB;;EAEf;;;;;;;EAOA;;;;;;;EAOA;;;;;;;;;;;;;;;;;;;;;;;;;;wBA2Bc,iBAAiB;EAAkB,SAAS,gBAAgB;GAC1E,KAAK,kBACL,QAAQ,SACR,UAAS,0BACR;;;;;;;;;;;;;;;;;;;wBA+Ea,4BAA4B,SAAS;;;;;;;;;;;;;;wBAyCrC,gCAAgC"}
|
package/dist/correlation.d.ts
CHANGED
|
@@ -1,58 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* logixia — Microservices Correlation ID Propagation
|
|
4
|
-
*
|
|
5
|
-
* Provides zero-boilerplate correlation ID generation and propagation for
|
|
6
|
-
* distributed systems. A single correlation ID travels through every service
|
|
7
|
-
* in a request fan-out, making it trivial to join logs across services.
|
|
8
|
-
*
|
|
9
|
-
* Features:
|
|
10
|
-
* - UUID v4 correlation ID generation (via `crypto.randomUUID()`)
|
|
11
|
-
* - Express / Fastify / NestJS incoming-request middleware
|
|
12
|
-
* - `correlationFetch()` — wraps global `fetch`, auto-injects `X-Correlation-ID`
|
|
13
|
-
* - `createCorrelationAxiosInterceptor()` — axios request interceptor
|
|
14
|
-
* - `childFromRequest(req, logger)` — child logger with all request identifiers
|
|
15
|
-
* - Kafka / SQS / RabbitMQ message context helpers
|
|
16
|
-
* - Integration with `LogixiaContext` (AsyncLocalStorage) so every `logger.info()`
|
|
17
|
-
* call automatically includes the correlationId without per-call passing.
|
|
18
|
-
*
|
|
19
|
-
* @example Express middleware
|
|
20
|
-
* ```ts
|
|
21
|
-
* import { correlationMiddleware } from 'logixia/correlation';
|
|
22
|
-
* import { logger } from 'logixia';
|
|
23
|
-
*
|
|
24
|
-
* app.use(correlationMiddleware());
|
|
25
|
-
*
|
|
26
|
-
* app.get('/orders', (req, res) => {
|
|
27
|
-
* // correlationId is automatically in every log inside this request
|
|
28
|
-
* logger.info('Listing orders');
|
|
29
|
-
* res.json({ ok: true });
|
|
30
|
-
* });
|
|
31
|
-
* ```
|
|
32
|
-
*
|
|
33
|
-
* @example Outbound fetch with correlation
|
|
34
|
-
* ```ts
|
|
35
|
-
* import { correlationFetch } from 'logixia/correlation';
|
|
36
|
-
*
|
|
37
|
-
* // Reads correlationId from current LogixiaContext and injects it as header
|
|
38
|
-
* const data = await correlationFetch('https://payments.internal/charge', {
|
|
39
|
-
* method: 'POST',
|
|
40
|
-
* body: JSON.stringify({ amount: 100 }),
|
|
41
|
-
* });
|
|
42
|
-
* ```
|
|
43
|
-
*
|
|
44
|
-
* @example Manual correlation context
|
|
45
|
-
* ```ts
|
|
46
|
-
* import { withCorrelationId } from 'logixia/correlation';
|
|
47
|
-
*
|
|
48
|
-
* await withCorrelationId('trace-abc-123', async () => {
|
|
49
|
-
* // All logs and outbound fetch calls in here carry 'trace-abc-123'
|
|
50
|
-
* await processOrder();
|
|
51
|
-
* });
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
1
|
+
import { n as HttpResponseShape, t as HttpRequestShape } from "./http-shapes-BnBkJcid.js";
|
|
54
2
|
/** Fields injected into the log context by the correlation middleware. */
|
|
55
|
-
interface CorrelationContext {
|
|
3
|
+
export interface CorrelationContext {
|
|
56
4
|
correlationId: string;
|
|
57
5
|
traceId?: string;
|
|
58
6
|
/** Service that originated the request (from `X-Origin-Service` header). */
|
|
@@ -60,7 +8,7 @@ interface CorrelationContext {
|
|
|
60
8
|
/** Index signature so CorrelationContext is assignable to LogContext. */
|
|
61
9
|
[key: string]: unknown;
|
|
62
10
|
}
|
|
63
|
-
interface CorrelationMiddlewareOptions {
|
|
11
|
+
export interface CorrelationMiddlewareOptions {
|
|
64
12
|
/**
|
|
65
13
|
* HTTP header to read/write the correlation ID from/to.
|
|
66
14
|
* @default 'x-correlation-id'
|
|
@@ -100,12 +48,12 @@ interface CorrelationMiddlewareOptions {
|
|
|
100
48
|
* function's JSDoc and README promise. `logixia/correlation` is Node-only
|
|
101
49
|
* (it builds on `AsyncLocalStorage`), so importing `node:crypto` is safe here.
|
|
102
50
|
*/
|
|
103
|
-
declare function generateCorrelationId(): string;
|
|
51
|
+
export declare function generateCorrelationId(): string;
|
|
104
52
|
/**
|
|
105
53
|
* Return the correlation ID from the current `LogixiaContext`, or `undefined`
|
|
106
54
|
* when called outside any context scope.
|
|
107
55
|
*/
|
|
108
|
-
declare function getCurrentCorrelationId(): string | undefined;
|
|
56
|
+
export declare function getCurrentCorrelationId(): string | undefined;
|
|
109
57
|
/**
|
|
110
58
|
* Run `callback` inside a correlation context scope.
|
|
111
59
|
*
|
|
@@ -119,7 +67,7 @@ declare function getCurrentCorrelationId(): string | undefined;
|
|
|
119
67
|
* });
|
|
120
68
|
* ```
|
|
121
69
|
*/
|
|
122
|
-
declare function withCorrelationId<T>(correlationId: string | undefined, callback: () => T): T;
|
|
70
|
+
export declare function withCorrelationId<T>(correlationId: string | undefined, callback: () => T): T;
|
|
123
71
|
/**
|
|
124
72
|
* Express-compatible middleware that:
|
|
125
73
|
* 1. Reads `X-Correlation-ID` from the incoming request (or generates a new one)
|
|
@@ -133,7 +81,7 @@ declare function withCorrelationId<T>(correlationId: string | undefined, callbac
|
|
|
133
81
|
* app.use(correlationMiddleware());
|
|
134
82
|
* ```
|
|
135
83
|
*/
|
|
136
|
-
declare function correlationMiddleware(options?: CorrelationMiddlewareOptions): (req:
|
|
84
|
+
export declare function correlationMiddleware(options?: CorrelationMiddlewareOptions): (req: HttpRequestShape, res: HttpResponseShape, next: () => void) => void;
|
|
137
85
|
/**
|
|
138
86
|
* Fastify `onRequest` lifecycle hook — same functionality as `correlationMiddleware`
|
|
139
87
|
* but adapted to Fastify's hook API.
|
|
@@ -144,7 +92,7 @@ declare function correlationMiddleware(options?: CorrelationMiddlewareOptions):
|
|
|
144
92
|
* fastify.addHook('onRequest', correlationFastifyHook());
|
|
145
93
|
* ```
|
|
146
94
|
*/
|
|
147
|
-
declare function correlationFastifyHook(options?: CorrelationMiddlewareOptions): (request:
|
|
95
|
+
export declare function correlationFastifyHook(options?: CorrelationMiddlewareOptions): (request: HttpRequestShape, reply: HttpResponseShape, done: () => void) => void;
|
|
148
96
|
/**
|
|
149
97
|
* A drop-in replacement for `fetch` that automatically injects the current
|
|
150
98
|
* `correlationId` from `LogixiaContext` as an `X-Correlation-ID` request header.
|
|
@@ -162,7 +110,7 @@ declare function correlationFastifyHook(options?: CorrelationMiddlewareOptions):
|
|
|
162
110
|
* });
|
|
163
111
|
* ```
|
|
164
112
|
*/
|
|
165
|
-
declare function correlationFetch(input: string | URL | Request, init?: RequestInit, options?: {
|
|
113
|
+
export declare function correlationFetch(input: string | URL | Request, init?: RequestInit, options?: {
|
|
166
114
|
header?: string;
|
|
167
115
|
}): Promise<Response>;
|
|
168
116
|
/**
|
|
@@ -177,10 +125,10 @@ declare function correlationFetch(input: string | URL | Request, init?: RequestI
|
|
|
177
125
|
* axios.interceptors.request.use(createCorrelationAxiosInterceptor());
|
|
178
126
|
* ```
|
|
179
127
|
*/
|
|
180
|
-
declare function createCorrelationAxiosInterceptor(options?: {
|
|
128
|
+
export declare function createCorrelationAxiosInterceptor(options?: {
|
|
181
129
|
header?: string;
|
|
182
130
|
}): (config: Record<string, unknown>) => Record<string, unknown>;
|
|
183
|
-
interface ChildFromRequestOptions {
|
|
131
|
+
export interface ChildFromRequestOptions {
|
|
184
132
|
/** Header to read the correlation ID from. @default 'x-correlation-id' */
|
|
185
133
|
correlationHeader?: string;
|
|
186
134
|
/**
|
|
@@ -222,9 +170,9 @@ interface ChildFromRequestOptions {
|
|
|
222
170
|
* });
|
|
223
171
|
* ```
|
|
224
172
|
*/
|
|
225
|
-
declare function childFromRequest<TLogger extends {
|
|
173
|
+
export declare function childFromRequest<TLogger extends {
|
|
226
174
|
child(...args: never[]): TLogger;
|
|
227
|
-
}>(req:
|
|
175
|
+
}>(req: HttpRequestShape, logger: TLogger, options?: ChildFromRequestOptions): TLogger;
|
|
228
176
|
/**
|
|
229
177
|
* Extract a correlation context from a Kafka / RabbitMQ / SQS message object.
|
|
230
178
|
*
|
|
@@ -243,7 +191,7 @@ declare function childFromRequest<TLogger extends {
|
|
|
243
191
|
* });
|
|
244
192
|
* ```
|
|
245
193
|
*/
|
|
246
|
-
declare function extractMessageCorrelationId(message: Record<string, unknown>): string | undefined;
|
|
194
|
+
export declare function extractMessageCorrelationId(message: Record<string, unknown>): string | undefined;
|
|
247
195
|
/**
|
|
248
196
|
* Build Kafka message headers containing the current correlation context.
|
|
249
197
|
*
|
|
@@ -257,7 +205,5 @@ declare function extractMessageCorrelationId(message: Record<string, unknown>):
|
|
|
257
205
|
* });
|
|
258
206
|
* ```
|
|
259
207
|
*/
|
|
260
|
-
declare function buildKafkaCorrelationHeaders(): Record<string, string>;
|
|
261
|
-
//#endregion
|
|
262
|
-
export { ChildFromRequestOptions, CorrelationContext, CorrelationMiddlewareOptions, buildKafkaCorrelationHeaders, childFromRequest, correlationFastifyHook, correlationFetch, correlationMiddleware, createCorrelationAxiosInterceptor, extractMessageCorrelationId, generateCorrelationId, getCurrentCorrelationId, withCorrelationId };
|
|
208
|
+
export declare function buildKafkaCorrelationHeaders(): Record<string, string>;
|
|
263
209
|
//# sourceMappingURL=correlation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"correlation.d.ts","names":[],"sources":["../src/correlation.ts"],"
|
|
1
|
+
{"version":3,"file":"correlation.d.ts","names":[],"sources":["../src/correlation.ts"],"mappings":";;iBA8DiB;EACf;EACA;;EAEA;;GAEC;;iBAGc;;;;;EAKf;;;;;EAKA;;;;;EAKA;;;;;;;EAOA;;;;;;EAMA;;;;;;;;;;;;wBAec;;;;;wBAwBA;;;;;;;;;;;;;;wBAkBA,kBAAkB,GAAG,mCAAmC,gBAAgB,IAAI;;;;;;;;;;;;;;wBAoB5E,sBAAsB,UAAS,gCAU3C,KAAK,kBACL,KAAK,mBACL;;;;;;;;;;;wBAkCY,uBAAuB,UAAS,gCAU5C,SAAS,kBACT,OAAO,mBACP;;;;;;;;;;;;;;;;;;wBAyCkB,iBACpB,gBAAgB,MAAM,SACtB,OAAM,aACN;EAAW;IACV,QAAQ;;;;;;;;;;;;;wBAwCK,kCACd;EAAW;KACT,QAAQ,4BAA4B;iBAuBvB;;EAEf;;;;;;;EAOA;;;;;;;EAOA;;;;;;;;;;;;;;;;;;;;;;;;;;wBA2Bc,iBAAiB;EAAkB,SAAS,gBAAgB;GAC1E,KAAK,kBACL,QAAQ,SACR,UAAS,0BACR;;;;;;;;;;;;;;;;;;;wBA+Ea,4BAA4B,SAAS;;;;;;;;;;;;;;wBAyCrC,gCAAgC"}
|
package/dist/correlation.js
CHANGED
|
@@ -1,10 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
const require_async_context = require(
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_request_trace = require("./request-trace-ByujsUZk.js");
|
|
3
|
+
const require_async_context = require("./async-context-DUFMDYAr.js");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
// src/correlation.ts
|
|
6
|
+
/**
|
|
7
|
+
* logixia — Microservices Correlation ID Propagation
|
|
8
|
+
*
|
|
9
|
+
* Provides zero-boilerplate correlation ID generation and propagation for
|
|
10
|
+
* distributed systems. A single correlation ID travels through every service
|
|
11
|
+
* in a request fan-out, making it trivial to join logs across services.
|
|
12
|
+
*
|
|
13
|
+
* Features:
|
|
14
|
+
* - UUID v4 correlation ID generation (via `crypto.randomUUID()`)
|
|
15
|
+
* - Express / Fastify / NestJS incoming-request middleware
|
|
16
|
+
* - `correlationFetch()` — wraps global `fetch`, auto-injects `X-Correlation-ID`
|
|
17
|
+
* - `createCorrelationAxiosInterceptor()` — axios request interceptor
|
|
18
|
+
* - `childFromRequest(req, logger)` — child logger with all request identifiers
|
|
19
|
+
* - Kafka / SQS / RabbitMQ message context helpers
|
|
20
|
+
* - Integration with `LogixiaContext` (AsyncLocalStorage) so every `logger.info()`
|
|
21
|
+
* call automatically includes the correlationId without per-call passing.
|
|
22
|
+
*
|
|
23
|
+
* @example Express middleware
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { correlationMiddleware } from 'logixia/correlation';
|
|
26
|
+
* import { logger } from 'logixia';
|
|
27
|
+
*
|
|
28
|
+
* app.use(correlationMiddleware());
|
|
29
|
+
*
|
|
30
|
+
* app.get('/orders', (req, res) => {
|
|
31
|
+
* // correlationId is automatically in every log inside this request
|
|
32
|
+
* logger.info('Listing orders');
|
|
33
|
+
* res.json({ ok: true });
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example Outbound fetch with correlation
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { correlationFetch } from 'logixia/correlation';
|
|
40
|
+
*
|
|
41
|
+
* // Reads correlationId from current LogixiaContext and injects it as header
|
|
42
|
+
* const data = await correlationFetch('https://payments.internal/charge', {
|
|
43
|
+
* method: 'POST',
|
|
44
|
+
* body: JSON.stringify({ amount: 100 }),
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @example Manual correlation context
|
|
49
|
+
* ```ts
|
|
50
|
+
* import { withCorrelationId } from 'logixia/correlation';
|
|
51
|
+
*
|
|
52
|
+
* await withCorrelationId('trace-abc-123', async () => {
|
|
53
|
+
* // All logs and outbound fetch calls in here carry 'trace-abc-123'
|
|
54
|
+
* await processOrder();
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
8
58
|
/**
|
|
9
59
|
* Generate a new correlation ID using `crypto.randomUUID()` (available in
|
|
10
60
|
* Node.js 14.17+, all modern browsers, and Edge runtimes).
|
|
@@ -27,7 +77,7 @@ function generateCorrelationId() {
|
|
|
27
77
|
*/
|
|
28
78
|
function validIncomingId(value) {
|
|
29
79
|
if (typeof value === "string" && value.trim() !== value) return void 0;
|
|
30
|
-
return
|
|
80
|
+
return require_request_trace.sanitizeIncomingId(value);
|
|
31
81
|
}
|
|
32
82
|
/**
|
|
33
83
|
* Return the correlation ID from the current `LogixiaContext`, or `undefined`
|
|
@@ -70,12 +120,12 @@ function withCorrelationId(correlationId, callback) {
|
|
|
70
120
|
function correlationMiddleware(options = {}) {
|
|
71
121
|
const { header = "x-correlation-id", originServiceHeader = "x-origin-service", generate = generateCorrelationId, trustIncoming = true, setResponseHeader = true } = options;
|
|
72
122
|
return function logixiaCorrelationMiddleware(req, res, next) {
|
|
73
|
-
const headers = req
|
|
123
|
+
const headers = req.headers ?? {};
|
|
74
124
|
const correlationId = (trustIncoming ? validIncomingId(headers[header.toLowerCase()]) : void 0) ?? generate();
|
|
75
125
|
const originService = validIncomingId(headers[originServiceHeader.toLowerCase()]);
|
|
76
126
|
if (setResponseHeader) {
|
|
77
|
-
|
|
78
|
-
|
|
127
|
+
var _res$setHeader;
|
|
128
|
+
(_res$setHeader = res.setHeader) === null || _res$setHeader === void 0 || _res$setHeader.call(res, header, correlationId);
|
|
79
129
|
}
|
|
80
130
|
const ctx = {
|
|
81
131
|
correlationId,
|
|
@@ -97,12 +147,12 @@ function correlationMiddleware(options = {}) {
|
|
|
97
147
|
function correlationFastifyHook(options = {}) {
|
|
98
148
|
const { header = "x-correlation-id", originServiceHeader = "x-origin-service", generate = generateCorrelationId, trustIncoming = true, setResponseHeader = true } = options;
|
|
99
149
|
return function logixiaCorrelationFastifyHook(request, reply, done) {
|
|
100
|
-
const headers = request
|
|
150
|
+
const headers = request.headers ?? {};
|
|
101
151
|
const correlationId = (trustIncoming ? validIncomingId(headers[header.toLowerCase()]) : void 0) ?? generate();
|
|
102
152
|
const originService = validIncomingId(headers[originServiceHeader.toLowerCase()]);
|
|
103
153
|
if (setResponseHeader) {
|
|
104
|
-
|
|
105
|
-
|
|
154
|
+
var _reply$header;
|
|
155
|
+
(_reply$header = reply.header) === null || _reply$header === void 0 || _reply$header.call(reply, header, correlationId);
|
|
106
156
|
}
|
|
107
157
|
const ctx = {
|
|
108
158
|
correlationId,
|
|
@@ -192,10 +242,10 @@ const MAX_IP_LENGTH = 64;
|
|
|
192
242
|
*/
|
|
193
243
|
function childFromRequest(req, logger, options = {}) {
|
|
194
244
|
const correlationHeader = options.correlationHeader ?? "x-correlation-id";
|
|
195
|
-
const headers = req
|
|
245
|
+
const headers = req.headers ?? {};
|
|
196
246
|
const correlationId = validIncomingId(headers[correlationHeader.toLowerCase()]) ?? getCurrentCorrelationId() ?? generateCorrelationId();
|
|
197
|
-
const method = req
|
|
198
|
-
const url = req
|
|
247
|
+
const method = req.method;
|
|
248
|
+
const url = req.url ?? req.originalUrl;
|
|
199
249
|
const rawUserAgent = headers["user-agent"];
|
|
200
250
|
const userAgent = typeof rawUserAgent === "string" ? rawUserAgent.slice(0, MAX_USER_AGENT_LENGTH) : void 0;
|
|
201
251
|
const ip = resolveClientIp(req, headers, options.trustProxy === true);
|
|
@@ -224,9 +274,9 @@ function resolveClientIp(req, headers, trustProxy) {
|
|
|
224
274
|
const first = typeof forwarded === "string" ? (_forwarded$split$ = forwarded.split(",")[0]) === null || _forwarded$split$ === void 0 ? void 0 : _forwarded$split$.trim() : void 0;
|
|
225
275
|
if (first) return first.slice(0, MAX_IP_LENGTH);
|
|
226
276
|
}
|
|
227
|
-
const fromReq = req
|
|
277
|
+
const fromReq = req.ip;
|
|
228
278
|
if (typeof fromReq === "string" && fromReq) return fromReq.slice(0, MAX_IP_LENGTH);
|
|
229
|
-
const remote = (_req$socket = req
|
|
279
|
+
const remote = (_req$socket = req.socket) === null || _req$socket === void 0 ? void 0 : _req$socket.remoteAddress;
|
|
230
280
|
return typeof remote === "string" ? remote.slice(0, MAX_IP_LENGTH) : void 0;
|
|
231
281
|
}
|
|
232
282
|
/**
|
|
@@ -282,8 +332,6 @@ function buildKafkaCorrelationHeaders() {
|
|
|
282
332
|
if (traceId) headers["x-trace-id"] = traceId;
|
|
283
333
|
return headers;
|
|
284
334
|
}
|
|
285
|
-
|
|
286
|
-
//#endregion
|
|
287
335
|
exports.buildKafkaCorrelationHeaders = buildKafkaCorrelationHeaders;
|
|
288
336
|
exports.childFromRequest = childFromRequest;
|
|
289
337
|
exports.correlationFastifyHook = correlationFastifyHook;
|
|
@@ -294,4 +342,5 @@ exports.extractMessageCorrelationId = extractMessageCorrelationId;
|
|
|
294
342
|
exports.generateCorrelationId = generateCorrelationId;
|
|
295
343
|
exports.getCurrentCorrelationId = getCurrentCorrelationId;
|
|
296
344
|
exports.withCorrelationId = withCorrelationId;
|
|
345
|
+
|
|
297
346
|
//# sourceMappingURL=correlation.js.map
|
package/dist/correlation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"correlation.js","names":["sanitizeIncomingId","LogixiaContext","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":";;;;;;;;;;;;;;;;;AAiHA,SAAgB,wBAAgC;AAC9C,KAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,WAChE,QAAO,OAAO,YAAY;AAE5B,qCAAmB;;;;;;;;AASrB,SAAS,gBAAgB,OAAoC;AAC3D,KAAI,OAAO,UAAU,YAAY,MAAM,MAAM,KAAK,MAAO,QAAO;AAChE,QAAOA,uCAAmB,MAAM;;;;;;AASlC,SAAgB,0BAA8C;CAC5D,MAAM,MAAMC,qCAAe,KAAK;AAChC,kDAAO,IAAK;;;;;;;;;;;;;;;AAgBd,SAAgB,kBAAqB,eAAmC,UAAsB;CAC5F,MAAM,KAAK,iBAAiB,uBAAuB;AACnD,QAAOA,qCAAe,IAAI,EAAE,eAAe,IAAI,EAAE,SAAS;;;;;;;;;;;;;;;AAkB5D,SAAgB,sBAAsB,UAAwC,EAAE,EAAE;CAChF,MAAM,EACJ,SAAS,oBACT,sBAAsB,oBACtB,WAAW,uBACX,gBAAgB,MAChB,oBAAoB,SAClB;AAEJ,QAAO,SAAS,6BACd,KACA,KACA,MACM;EACN,MAAM,UAAW,IAAI,cAAc,EAAE;EAIrC,MAAM,iBADa,gBAAgB,gBAAgB,QAAQ,OAAO,aAAa,EAAE,GAAG,WAChD,UAAU;EAC9C,MAAM,gBAAgB,gBAAgB,QAAQ,oBAAoB,aAAa,EAAE;AAEjF,MAAI,mBAAmB;GACrB,MAAM,YAAY,IAAI;AACtB,OAAI,OAAO,cAAc,WACvB,WAAU,KAAK,KAAK,QAAQ,cAAc;;EAI9C,MAAMC,MAA0B;GAC9B;GACA,GAAI,kBAAkB,SAAY,EAAE,eAAe,GAAG,EAAE;GACzD;AAED,uCAAe,IAAI,KAAK,KAAK;;;;;;;;;;;;;AAgBjC,SAAgB,uBAAuB,UAAwC,EAAE,EAAE;CACjF,MAAM,EACJ,SAAS,oBACT,sBAAsB,oBACtB,WAAW,uBACX,gBAAgB,MAChB,oBAAoB,SAClB;AAEJ,QAAO,SAAS,8BACd,SACA,OACA,MACM;EACN,MAAM,UAAW,QAAQ,cAAc,EAAE;EAIzC,MAAM,iBADa,gBAAgB,gBAAgB,QAAQ,OAAO,aAAa,EAAE,GAAG,WAChD,UAAU;EAC9C,MAAM,gBAAgB,gBAAgB,QAAQ,oBAAoB,aAAa,EAAE;AAEjF,MAAI,mBAAmB;GACrB,MAAM,cAAc,MAAM;AAC1B,OAAI,OAAO,gBAAgB,WACzB,aAAY,KAAK,OAAO,QAAQ,cAAc;;EAIlD,MAAMA,MAA0B;GAC9B;GACA,GAAI,kBAAkB,SAAY,EAAE,eAAe,GAAG,EAAE;GACzD;AAED,uCAAe,IAAI,KAAK,KAAK;;;;;;;;;;;;;;;;;;;;AAuBjC,eAAsB,iBACpB,OACA,OAAoB,EAAE,EACtB,UAA+B,EAAE,EACd;CACnB,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,gBAAgB,yBAAyB;CAa/C,MAAM,UAAU,IAAI,QAAQ,iBAAiB,UAAU,MAAM,UAAU,OAAU;AACjF,MAAK,MAAM,CAAC,MAAM,UAAU,IAAI,QAAQ,KAAK,QAAQ,CACnD,SAAQ,IAAI,MAAM,MAAM;AAE1B,KAAI,iBAAiB,CAAC,QAAQ,IAAI,OAAO,CACvC,SAAQ,IAAI,QAAQ,cAAc;AAGpC,QAAO,WAAW,MAAM,OAAO;EAAE,GAAG;EAAM;EAAS,CAAC;;;;;;;;;;;;;;AAiBtD,SAAgB,kCACd,UAA+B,EAAE,EAC6B;CAC9D,MAAM,SAAS,QAAQ,UAAU;AAEjC,QAAO,SAAS,4BACd,QACyB;EACzB,MAAM,gBAAgB,yBAAyB;AAC/C,MAAI,CAAC,cAAe,QAAO;EAE3B,MAAM,UAAW,OAAO,cAAc,EAAE;AACxC,MAAI,CAAC,QAAQ,QACX,SAAQ,UAAU;AAGpB,SAAO;GAAE,GAAG;GAAQ;GAAS;;;AAMjC,MAAM,wBAAwB;AAC9B,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;AA6CtB,SAAgB,iBACd,KACA,QACA,UAAmC,EAAE,EAC5B;CACT,MAAM,oBAAoB,QAAQ,qBAAqB;CAEvD,MAAM,UAAW,IAAI,cAAc,EAAE;CAErC,MAAM,gBACJ,gBAAgB,QAAQ,kBAAkB,aAAa,EAAE,IACzD,yBAAyB,IACzB,uBAAuB;CAEzB,MAAM,SAAS,IAAI;CACnB,MAAM,MAAO,IAAI,UAAkC,IAAI;CACvD,MAAM,eAAe,QAAQ;CAC7B,MAAM,YACJ,OAAO,iBAAiB,WAAW,aAAa,MAAM,GAAG,sBAAsB,GAAG;CACpF,MAAM,KAAK,gBAAgB,KAAK,SAAS,QAAQ,eAAe,KAAK;CAErE,MAAM,SAAS;EACb;EACA,GAAI,WAAW,SAAY,EAAE,QAAQ,GAAG,EAAE;EAC1C,GAAI,QAAQ,SAAY,EAAE,KAAK,GAAG,EAAE;EACpC,GAAI,cAAc,SAAY,EAAE,WAAW,GAAG,EAAE;EAChD,GAAI,OAAO,SAAY,EAAE,IAAI,GAAG,EAAE;EACnC;CAED,MAAM,QAAQ,OAAO;AACrB,KAAI,QAAQ,iBAAiB,OAC3B,QAAO,MAAM,KAAK,QAAQ,QAAQ,cAAc,OAAO;AAIzD,KAAI,kBAAkB,OAAO,CAC3B,QAAO,MAAM,KAAK,QAAQ,WAAW,OAAO;AAE9C,QAAO,MAAM,KAAK,QAAQ,OAAO;;;AAInC,SAAS,kBAAkB,QAAyB;CAClD,MAAM,IAAI;AACV,QAAO,OAAO,EAAE,kBAAkB,cAAc,OAAO,EAAE,8BAA8B;;AAGzF,SAAS,gBACP,KACA,SACA,YACoB;;AACpB,KAAI,YAAY;;EACd,MAAM,YAAY,QAAQ;EAC1B,MAAM,QAAQ,OAAO,cAAc,gCAAW,UAAU,MAAM,IAAI,CAAC,0EAAI,MAAM,GAAG;AAChF,MAAI,MAAO,QAAO,MAAM,MAAM,GAAG,cAAc;;CAEjD,MAAM,UAAU,IAAI;AACpB,KAAI,OAAO,YAAY,YAAY,QAAS,QAAO,QAAQ,MAAM,GAAG,cAAc;CAClF,MAAM,wBAAU,IAAI,qEAAoD;AACxE,QAAO,OAAO,WAAW,WAAW,OAAO,MAAM,GAAG,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;AAuBvE,SAAgB,4BAA4B,SAAsD;CAEhG,MAAM,eAAe,QAAQ;AAC7B,KAAI,cAAc;EAChB,MAAM,MACJ,aAAa,uBACb,aAAa,oBACb,aAAa;AACf,MAAI,QAAQ,OACV,QAAO,gBAAgB,OAAO,SAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,GAAG,IAAI,GAAG,OAAO,IAAI,CAAC;;CAK7F,MAAM,WAAW,QAAQ;AAEzB,KAAI,UAAU;;AACZ,SAAO,yCACL,SAAS,iGAAqB,0CAC5B,SAAS,gGAAkB,2CAC3B,SAAS,mGAAmB,aAC/B;;AAIH,QAAO,gBAAgB,QAAQ,oBAAoB,QAAQ,kBAAkB;;;;;;;;;;;;;;;AAgB/E,SAAgB,+BAAuD;CACrE,MAAM,MAAMD,qCAAe,KAAK;CAChC,MAAME,UAAkC,EAAE;CAE1C,MAAM,0DAAgB,IAAK;AAC3B,KAAI,cAAe,SAAQ,sBAAsB;CAEjD,MAAM,oDAAU,IAAK;AACrB,KAAI,QAAS,SAAQ,gBAAgB;AAErC,QAAO"}
|
|
1
|
+
{"version":3,"file":"correlation.js","names":["randomUUID","sanitizeIncomingId","LogixiaContext"],"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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"}
|