@cqrs-ddd/pipeline-correlation 0.5.0 → 0.5.2

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.
@@ -9,21 +9,25 @@ This Commercial License Agreement ("Agreement") is a legal agreement between You
9
9
  Upon acceptance of this Agreement and compliance with the Payment Terms in Section 2, the Licensor grants You a non-exclusive, worldwide, non-transferable license to use, modify, integrate, and distribute the Software in commercial applications WITHOUT the copyleft obligations of the GNU Affero General Public License v3 (AGPLv3). Under this Agreement, You are NOT required to open-source Your proprietary code that integrates with the Software.
10
10
 
11
11
  2. PAYMENT TERMS AND REVENUE TIERS
12
- This license requires an annual fee based on Your (or Your organization's) total gross global revenue for the most recently completed fiscal year.
12
+ This license requires an annual fee based on Your (or Your organization's) total gross global revenue for the most recently completed fiscal year.
13
13
 
14
- The annual fee is calculated using the following scaled brackets:
14
+ The annual fee is calculated using the following marginal brackets. Each rate applies only to the portion of gross revenue that falls within its bracket, and the annual fee is the sum of the amounts for all brackets:
15
15
 
16
- a) Tier 1: Gross revenue up to $500,000 USD
17
- Fee: $0 (Free Commercial Use)
16
+ a) Tier 1: The portion of gross revenue up to $500,000 USD
17
+ Rate: 0% (Free Commercial Use). If gross revenue does not exceed $500,000 USD, the fee is $0.
18
18
 
19
- b) Tier 2: Gross revenue between $500,001 and $10,000,000 USD
20
- Fee: 0.1% of total gross revenue.
19
+ b) Tier 2: The portion of gross revenue above $500,000 USD and up to $10,000,000 USD
20
+ Rate: 0.1% of that portion.
21
21
 
22
- c) Tier 3: Gross revenue between $10,000,001 and $50,000,000 USD
23
- Fee: 0.05% of total gross revenue.
22
+ c) Tier 3: The portion of gross revenue above $10,000,000 USD and up to $50,000,000 USD
23
+ Rate: 0.05% of that portion.
24
24
 
25
- d) Tier 4: Gross revenue exceeding $50,000,000 USD
26
- Fee: 0.01% of total gross revenue, up to a maximum hard cap of $50,000 USD per year.
25
+ d) Tier 4: The portion of gross revenue above $50,000,000 USD
26
+ Rate: 0.01% of that portion.
27
+
28
+ The total annual fee never exceeds a maximum hard cap of $50,000 USD per year.
29
+
30
+ Example: for gross revenue of $20,000,000 USD, the annual fee is 0.1% of $9,500,000 USD ($9,500) plus 0.05% of $10,000,000 USD ($5,000), a total of $14,500 USD.
27
31
 
28
32
  3. REPORTING AND REMITTANCE
29
33
  - Fees are calculated annually and must be remitted to the Licensor within ninety (90) days following the close of Your fiscal year.
@@ -4,11 +4,12 @@
4
4
  * `@cqrs-ddd/pipeline-job-context`. Unlike {@link getCorrelationId}, `current()`
5
5
  * generates nothing: it returns `undefined` when no id is set, so a pipeline
6
6
  * generates one and runs with it, and {@link getCorrelationId} inside the
7
- * handler returns that id. `create()` makes a new id, the one way this
8
- * package, pipelines and jobs generate one. `accepts(id)` tells whether an id received from
9
- * outside, such as in a job payload, is well formed: at most
10
- * {@link DEFAULT_CORRELATION_ID_MAX_LENGTH} characters matching
11
- * {@link DEFAULT_CORRELATION_ID_PATTERN}.
7
+ * handler returns that id. `run(id, fn)` runs `fn` with `id`, or with no id when `id`
8
+ * is not printable ASCII without surrounding spaces. `create()` makes a new id, the one
9
+ * way this package, pipelines and jobs generate one. `accepts(id)` tells whether an id
10
+ * received from outside, such as in a job payload, passes the rules
11
+ * `configureCorrelation` sets: by default at most 128 characters matching
12
+ * `DEFAULT_CORRELATION_ID_PATTERN`. It reports nothing to `onRejected`.
12
13
  *
13
14
  * @example
14
15
  * ```ts
@@ -28,11 +29,13 @@ export declare const correlationSource: {
28
29
  * WebSocket handlers, scheduled jobs) to propagate an external correlation ID
29
30
  * into everything the callback runs, wrapped operations included.
30
31
  *
31
- * If `correlationId` is falsy (`undefined` or empty), the id is resolved via
32
- * {@link getCorrelationId} (the current one, or a new one from
33
- * `correlationSource.create()`), so
34
- * `fn` always runs with a correlation id. A pipeline dispatched inside `fn`
35
- * takes it as its `context.correlationId`.
32
+ * When `correlationId` is missing, empty, not printable ASCII without surrounding
33
+ * spaces, or, once `configureCorrelation` has been called, rejected by its rules, `fn`
34
+ * runs with the id {@link getCorrelationId}
35
+ * returns (the current one, or a new one from `correlationSource.create()`), so it always
36
+ * runs with a correlation id. A rejected id is reported to `onRejected` with the source
37
+ * `'runWithCorrelationId'`. A pipeline dispatched inside `fn` takes the id as its
38
+ * `context.correlationId`.
36
39
  *
37
40
  * @example
38
41
  * ```ts
@@ -52,10 +55,12 @@ export declare const correlationSource: {
52
55
  * setInterval(() => runWithCorrelationId(undefined, () => hourlySync()), 3_600_000);
53
56
  * ```
54
57
  *
55
- * @param correlationId - The correlation ID to propagate. If falsy, falls back to {@link getCorrelationId}.
58
+ * @param correlationId - The correlation ID to propagate, as received from outside.
56
59
  * @param fn - The callback to execute within the correlation context.
57
60
  */
58
61
  export declare function runWithCorrelationId<T>(correlationId: string | undefined, fn: () => T): T;
62
+ /** Runs `fn` with `id` when the rules accept it, else with the current or a new id. */
63
+ export declare function runWithIncoming<T>(id: unknown, source: string, fn: () => T): T;
59
64
  /**
60
65
  * Read the current correlation ID.
61
66
  *
@@ -1 +1 @@
1
- {"version":3,"file":"correlation.store.d.ts","sourceRoot":"","sources":["../src/correlation.store.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,iBAAiB;aAC5B,OAAO,QAAM,MAAM,GAAG,SAAS;aAC/B,GAAG,GAAG,CAAC,SAAS,MAAM,GAAG,SAAS,MAAM,MAAM,CAAC,KAAG,CAAC;aAEnD,MAAM,QAAM,MAAM;aAClB,OAAO,OAAO,MAAM,KAAG,OAAO;CAGtB,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,EAAE,EAAE,MAAM,CAAC,GACV,CAAC,CAEH;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG;IAC/D,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChE,IAAI,EAAE,CAAC,GACN,iBAAiB,CAAC,CAAC,CAAC,CAYtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,SAA6B,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExB"}
1
+ {"version":3,"file":"correlation.store.d.ts","sourceRoot":"","sources":["../src/correlation.store.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iBAAiB;aAC5B,OAAO,QAAM,MAAM,GAAG,SAAS;aAC/B,GAAG,GAAG,CAAC,SAAS,MAAM,GAAG,SAAS,MAAM,MAAM,CAAC,KAAG,CAAC;aAEnD,MAAM,QAAM,MAAM;aAClB,OAAO,OAAO,MAAM,KAAG,OAAO;CACtB,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,EAAE,EAAE,MAAM,CAAC,GACV,CAAC,CAEH;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,CAAC,EAC/B,EAAE,EAAE,OAAO,EACX,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,CAAC,GACV,CAAC,CAKH;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG;IAC/D,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChE,IAAI,EAAE,CAAC,GACN,iBAAiB,CAAC,CAAC,CAAC,CAYtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,SAA6B,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExB"}
@@ -2,7 +2,7 @@
2
2
  import { AsyncLocalStorage } from 'node:async_hooks';
3
3
  import { uuidv7 } from '@cqrs-ddd/uuidv7';
4
4
  import { DEFAULT_CORRELATION_HEADER } from './constants/correlation.constants.js';
5
- import { DEFAULT_CORRELATION_ID_MAX_LENGTH, DEFAULT_CORRELATION_ID_PATTERN, } from './options/correlation.options.js';
5
+ import { admit, isAccepted, isPortable } from './policy/correlation.policy.js';
6
6
  const correlation = new AsyncLocalStorage();
7
7
  /**
8
8
  * The correlation id as a context source, for `createPipeline({ sources })`
@@ -10,11 +10,12 @@ const correlation = new AsyncLocalStorage();
10
10
  * `@cqrs-ddd/pipeline-job-context`. Unlike {@link getCorrelationId}, `current()`
11
11
  * generates nothing: it returns `undefined` when no id is set, so a pipeline
12
12
  * generates one and runs with it, and {@link getCorrelationId} inside the
13
- * handler returns that id. `create()` makes a new id, the one way this
14
- * package, pipelines and jobs generate one. `accepts(id)` tells whether an id received from
15
- * outside, such as in a job payload, is well formed: at most
16
- * {@link DEFAULT_CORRELATION_ID_MAX_LENGTH} characters matching
17
- * {@link DEFAULT_CORRELATION_ID_PATTERN}.
13
+ * handler returns that id. `run(id, fn)` runs `fn` with `id`, or with no id when `id`
14
+ * is not printable ASCII without surrounding spaces. `create()` makes a new id, the one
15
+ * way this package, pipelines and jobs generate one. `accepts(id)` tells whether an id
16
+ * received from outside, such as in a job payload, passes the rules
17
+ * `configureCorrelation` sets: by default at most 128 characters matching
18
+ * `DEFAULT_CORRELATION_ID_PATTERN`. It reports nothing to `onRejected`.
18
19
  *
19
20
  * @example
20
21
  * ```ts
@@ -23,10 +24,9 @@ const correlation = new AsyncLocalStorage();
23
24
  */
24
25
  export const correlationSource = {
25
26
  current: () => correlation.getStore(),
26
- run: (value, fn) => correlation.run(value, fn),
27
+ run: (value, fn) => correlation.run(isPortable(value) ? value : undefined, fn),
27
28
  create: () => uuidv7(),
28
- accepts: (id) => id.length <= DEFAULT_CORRELATION_ID_MAX_LENGTH &&
29
- DEFAULT_CORRELATION_ID_PATTERN.test(id),
29
+ accepts: (id) => isAccepted(id),
30
30
  };
31
31
  /**
32
32
  * Run a callback within a correlation context.
@@ -35,11 +35,13 @@ export const correlationSource = {
35
35
  * WebSocket handlers, scheduled jobs) to propagate an external correlation ID
36
36
  * into everything the callback runs, wrapped operations included.
37
37
  *
38
- * If `correlationId` is falsy (`undefined` or empty), the id is resolved via
39
- * {@link getCorrelationId} (the current one, or a new one from
40
- * `correlationSource.create()`), so
41
- * `fn` always runs with a correlation id. A pipeline dispatched inside `fn`
42
- * takes it as its `context.correlationId`.
38
+ * When `correlationId` is missing, empty, not printable ASCII without surrounding
39
+ * spaces, or, once `configureCorrelation` has been called, rejected by its rules, `fn`
40
+ * runs with the id {@link getCorrelationId}
41
+ * returns (the current one, or a new one from `correlationSource.create()`), so it always
42
+ * runs with a correlation id. A rejected id is reported to `onRejected` with the source
43
+ * `'runWithCorrelationId'`. A pipeline dispatched inside `fn` takes the id as its
44
+ * `context.correlationId`.
43
45
  *
44
46
  * @example
45
47
  * ```ts
@@ -59,11 +61,15 @@ export const correlationSource = {
59
61
  * setInterval(() => runWithCorrelationId(undefined, () => hourlySync()), 3_600_000);
60
62
  * ```
61
63
  *
62
- * @param correlationId - The correlation ID to propagate. If falsy, falls back to {@link getCorrelationId}.
64
+ * @param correlationId - The correlation ID to propagate, as received from outside.
63
65
  * @param fn - The callback to execute within the correlation context.
64
66
  */
65
67
  export function runWithCorrelationId(correlationId, fn) {
66
- return correlation.run(correlationId || getCorrelationId(), fn);
68
+ return runWithIncoming(correlationId, 'runWithCorrelationId', fn);
69
+ }
70
+ /** Runs `fn` with `id` when the rules accept it, else with the current or a new id. */
71
+ export function runWithIncoming(id, source, fn) {
72
+ return correlation.run(admit(id, source, 'unchecked') ?? getCorrelationId(), fn);
67
73
  }
68
74
  /**
69
75
  * Read the current correlation ID.
@@ -1 +1 @@
1
- {"version":3,"file":"correlation.store.js","sourceRoot":"","sources":["../src/correlation.store.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAClF,OAAO,EACL,iCAAiC,EACjC,8BAA8B,GAC/B,MAAM,kCAAkC,CAAC;AAE1C,MAAM,WAAW,GAAG,IAAI,iBAAiB,EAAsB,CAAC;AAEhE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,OAAO,EAAE,GAAuB,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE;IACzD,GAAG,EAAE,CAAI,KAAyB,EAAE,EAAW,EAAK,EAAE,CACpD,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC;IAC5B,MAAM,EAAE,GAAW,EAAE,CAAC,MAAM,EAAE;IAC9B,OAAO,EAAE,CAAC,EAAU,EAAW,EAAE,CAC/B,EAAE,CAAC,MAAM,IAAI,iCAAiC;QAC9C,8BAA8B,CAAC,IAAI,CAAC,EAAE,CAAC;CACjC,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,UAAU,oBAAoB,CAClC,aAAiC,EACjC,EAAW;IAEX,OAAO,WAAW,CAAC,GAAG,CAAC,aAAa,IAAI,gBAAgB,EAAE,EAAE,EAAE,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,WAAW,CAAC,QAAQ,EAAE,IAAI,iBAAiB,CAAC,MAAM,EAAE,CAAC;AAC9D,CAAC;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAO;IAEP,MAAM,SAAS,GACb,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QACvC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC;QAC7B,CAAC,CAAC,SAAS,CAAC;IAChB,IAAI,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CACjB,0DAA0D;YACxD,0DAA0D,CAC7D,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,aAAa,EAAE,gBAAgB,EAAE,EAAE,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAG,GAAG,0BAA0B;IAEhC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,gBAAgB,EAAE,EAAE,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"correlation.store.js","sourceRoot":"","sources":["../src/correlation.store.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAE/E,MAAM,WAAW,GAAG,IAAI,iBAAiB,EAAsB,CAAC;AAEhE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,OAAO,EAAE,GAAuB,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE;IACzD,GAAG,EAAE,CAAI,KAAyB,EAAE,EAAW,EAAK,EAAE,CACpD,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC;IAC5D,MAAM,EAAE,GAAW,EAAE,CAAC,MAAM,EAAE;IAC9B,OAAO,EAAE,CAAC,EAAU,EAAW,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;CACxC,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,UAAU,oBAAoB,CAClC,aAAiC,EACjC,EAAW;IAEX,OAAO,eAAe,CAAC,aAAa,EAAE,sBAAsB,EAAE,EAAE,CAAC,CAAC;AACpE,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,eAAe,CAC7B,EAAW,EACX,MAAc,EACd,EAAW;IAEX,OAAO,WAAW,CAAC,GAAG,CACpB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,gBAAgB,EAAE,EACpD,EAAE,CACH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,WAAW,CAAC,QAAQ,EAAE,IAAI,iBAAiB,CAAC,MAAM,EAAE,CAAC;AAC9D,CAAC;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAO;IAEP,MAAM,SAAS,GACb,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QACvC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC;QAC7B,CAAC,CAAC,SAAS,CAAC;IAChB,IAAI,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CACjB,0DAA0D;YACxD,0DAA0D,CAC7D,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,aAAa,EAAE,gBAAgB,EAAE,EAAE,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAG,GAAG,0BAA0B;IAEhC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,gBAAgB,EAAE,EAAE,CAAC;AACvC,CAAC"}
@@ -99,9 +99,11 @@ export interface CorrelationLogger {
99
99
  * not replace or interfere with them.
100
100
  *
101
101
  * It extracts the correlation ID from the method arguments (via `path` or
102
- * `extract`) and runs the method inside {@link runWithCorrelationId}, which
103
- * keeps the current id, or makes one with `correlationSource.create()`, when
104
- * no ID is found.
102
+ * `extract`) and runs the method with it when it is printable ASCII without
103
+ * surrounding spaces and, once `configureCorrelation` has been called, passes its
104
+ * rules. Otherwise the method keeps the current id, or gets a new one from
105
+ * `correlationSource.create()`, and a rejected id is reported to `onRejected` with the
106
+ * source `'Class.method'`.
105
107
  *
106
108
  * **⚠️ Array payloads:**
107
109
  * When using the default dot-path extraction, the first argument must be an
@@ -1 +1 @@
1
- {"version":3,"file":"with-correlation.decorator.d.ts","sourceRoot":"","sources":["../../src/decorators/with-correlation.decorator.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,gCAAgC,CAAC;AAGxC;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,MAAM,GAAG,SAAS,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAE7B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,yEAAyE;AACzE,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAEhF;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAC7D,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAC9D,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAChE,OAAO,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAClE,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAChE,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;CACjE;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAgB,eAAe,IAAI,mBAAmB,CAAC;AACvD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,CAAC;AACnE,wBAAgB,eAAe,CAC7B,OAAO,EAAE,2BAA2B,GACnC,mBAAmB,CAAC;AA+CvB;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,eAAe;IAC1B;;;;;;OAMG;qCACuC,2BAA2B;CAM7D,CAAC"}
1
+ {"version":3,"file":"with-correlation.decorator.d.ts","sourceRoot":"","sources":["../../src/decorators/with-correlation.decorator.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,gCAAgC,CAAC;AAGxC;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,MAAM,GAAG,SAAS,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAE7B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,yEAAyE;AACzE,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAEhF;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAC7D,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAC9D,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAChE,OAAO,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAClE,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAChE,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;CACjE;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,wBAAgB,eAAe,IAAI,mBAAmB,CAAC;AACvD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,CAAC;AACnE,wBAAgB,eAAe,CAC7B,OAAO,EAAE,2BAA2B,GACnC,mBAAmB,CAAC;AA0DvB;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,eAAe;IAC1B;;;;;;OAMG;qCACuC,2BAA2B;CAM7D,CAAC"}
@@ -1,7 +1,7 @@
1
1
  /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
2
  import { untyped } from '@cqrs-ddd/untyped';
3
3
  import { DEFAULT_CORRELATION_HEADER } from '../constants/correlation.constants.js';
4
- import { getCorrelationId, runWithCorrelationId, } from '../correlation.store.js';
4
+ import { getCorrelationId, runWithIncoming } from '../correlation.store.js';
5
5
  import { methodDecorator, } from '../helpers/method-decorator.js';
6
6
  import { dyn } from '../types/safe-typing.js';
7
7
  /**
@@ -24,31 +24,37 @@ export function WithCorrelation(pathOrOptions) {
24
24
  ? { path: pathOrOptions }
25
25
  : (pathOrOptions ?? {});
26
26
  const { path = 'data.correlationId', extract } = options;
27
- return methodDecorator((originalMethod, _propertyKey) => {
27
+ const wrap = (originalMethod, _propertyKey) => {
28
28
  return function (...args) {
29
29
  const logger = options.logger ??
30
30
  untyped(this)?.logger ??
31
31
  console;
32
+ const source = `${untyped(this).constructor?.name}.${String(_propertyKey)}`;
32
33
  if (!extract && Array.isArray(args[0])) {
33
- logger.warn(`${untyped(this).constructor?.name}.${String(_propertyKey)}: first argument is an array — ` +
34
+ logger.warn(`${source}: first argument is an array — ` +
34
35
  `dot-path "${path}" cannot extract a correlation ID from it. ` +
35
36
  `Use the 'extract' option for array payloads.`);
36
37
  }
37
38
  const correlationId = extract
38
39
  ? extract(...args)
39
40
  : getByPath(args[0], path);
40
- return runWithCorrelationId(correlationId, () => {
41
- if (options.logLevel !== 'none') {
42
- const level = options.logLevel ?? 'debug';
43
- const method = logger[level];
44
- if (typeof method === 'function') {
45
- method.call(logger, `🔗 Starting ${untyped(this).constructor?.name}.${String(_propertyKey)} with correlationId: ${getCorrelationId()}`);
46
- }
47
- }
41
+ return runWithIncoming(correlationId, source, () => {
42
+ const log = logAt(logger, options.logLevel);
43
+ log?.(`🔗 Starting ${source} with correlationId: ${getCorrelationId()}`);
48
44
  return originalMethod.apply(this, args);
49
45
  });
50
46
  };
51
- });
47
+ };
48
+ return methodDecorator(wrap);
49
+ }
50
+ /** The logger method of `logLevel` (default `'debug'`), unless it is `'none'` or missing. */
51
+ function logAt(logger, logLevel) {
52
+ if (logLevel === 'none')
53
+ return undefined;
54
+ const method = logger[(logLevel ?? 'debug')];
55
+ if (typeof method !== 'function')
56
+ return undefined;
57
+ return (message) => method.call(logger, message);
52
58
  }
53
59
  /**
54
60
  * Pre-built extraction presets for {@link WithCorrelation}, for transports whose
@@ -1 +1 @@
1
- {"version":3,"file":"with-correlation.decorator.js","sourceRoot":"","sources":["../../src/decorators/with-correlation.decorator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"with-correlation.decorator.js","sourceRoot":"","sources":["../../src/decorators/with-correlation.decorator.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -6,4 +6,6 @@ export { CorrelationFrom, WithCorrelation, } from './decorators/with-correlation
6
6
  export { type HttpMiddleware, httpCorrelation, } from './middlewares/http-correlation.middleware.js';
7
7
  export type { CorrelationOptions } from './options/correlation.options.js';
8
8
  export { DEFAULT_CORRELATION_ID_MAX_LENGTH, DEFAULT_CORRELATION_ID_PATTERN, } from './options/correlation.options.js';
9
+ export type { CorrelationPolicy, CorrelationRejection, CorrelationRejectionReason, } from './policy/correlation.policy.js';
10
+ export { configureCorrelation } from './policy/correlation.policy.js';
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAClF,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,2BAA2B,EAC3B,oBAAoB,EACpB,iBAAiB,EACjB,QAAQ,GACT,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACL,eAAe,EACf,eAAe,GAChB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACL,KAAK,cAAc,EACnB,eAAe,GAChB,MAAM,8CAA8C,CAAC;AACtD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EACL,iCAAiC,EACjC,8BAA8B,GAC/B,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAClF,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,2BAA2B,EAC3B,oBAAoB,EACpB,iBAAiB,EACjB,QAAQ,GACT,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACL,eAAe,EACf,eAAe,GAChB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EACL,KAAK,cAAc,EACnB,eAAe,GAChB,MAAM,8CAA8C,CAAC;AACtD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EACL,iCAAiC,EACjC,8BAA8B,GAC/B,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EACV,iBAAiB,EACjB,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC"}
package/dist/index.js CHANGED
@@ -4,4 +4,5 @@ export { addCorrelationId, correlationHeaders, correlationSource, getCorrelation
4
4
  export { CorrelationFrom, WithCorrelation, } from './decorators/with-correlation.decorator.js';
5
5
  export { httpCorrelation, } from './middlewares/http-correlation.middleware.js';
6
6
  export { DEFAULT_CORRELATION_ID_MAX_LENGTH, DEFAULT_CORRELATION_ID_PATTERN, } from './options/correlation.options.js';
7
+ export { configureCorrelation } from './policy/correlation.policy.js';
7
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAElF,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAOhC,OAAO,EACL,eAAe,EACf,eAAe,GAChB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAEL,eAAe,GAChB,MAAM,8CAA8C,CAAC;AAEtD,OAAO,EACL,iCAAiC,EACjC,8BAA8B,GAC/B,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAElF,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAOhC,OAAO,EACL,eAAe,EACf,eAAe,GAChB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAEL,eAAe,GAChB,MAAM,8CAA8C,CAAC;AAEtD,OAAO,EACL,iCAAiC,EACjC,8BAA8B,GAC/B,MAAM,kCAAkC,CAAC;AAM1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
- import { type CorrelationOptions } from '../options/correlation.options.js';
2
+ import type { CorrelationOptions } from '../options/correlation.options.js';
3
3
  /** A Node `http` middleware: Express and Connect take it as it is. */
4
4
  export type HttpMiddleware = (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
5
5
  /**
@@ -10,9 +10,12 @@ export type HttpMiddleware = (req: IncomingMessage, res: ServerResponse, next: (
10
10
  * The header name defaults to `x-correlation-id`. If `header` is omitted or is any
11
11
  * non-string value (including `false`), the default header is used.
12
12
  *
13
- * An incoming id longer than 128 characters or outside the default character set is
14
- * replaced by a local id. `acceptIncoming`, `trimIncoming`, `maxLength` and
15
- * `validateIncoming` adjust that policy.
13
+ * An incoming id that the rules `configureCorrelation` sets reject (by default, longer
14
+ * than 128 characters or outside the default character set) is replaced by a local id
15
+ * and reported to `onRejected` with the source `'http'`. `acceptIncoming`,
16
+ * `trimIncoming`, `maxLength` and `validateIncoming` adjust that policy for this
17
+ * middleware. None of them lets through an id that is not printable ASCII or that starts
18
+ * or ends with a space.
16
19
  *
17
20
  * @throws TypeError when `header` is not a valid HTTP field name, or `maxLength` is not
18
21
  * a positive safe integer.
@@ -1 +1 @@
1
- {"version":3,"file":"http-correlation.middleware.d.ts","sourceRoot":"","sources":["../../src/middlewares/http-correlation.middleware.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMjE,OAAO,EACL,KAAK,kBAAkB,EAGxB,MAAM,mCAAmC,CAAC;AAI3C,sEAAsE;AACtE,MAAM,MAAM,cAAc,GAAG,CAC3B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,IAAI,KACb,IAAI,CAAC;AAEV;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,cAAc,CA2C5E"}
1
+ {"version":3,"file":"http-correlation.middleware.d.ts","sourceRoot":"","sources":["../../src/middlewares/http-correlation.middleware.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAK5E,sEAAsE;AACtE,MAAM,MAAM,cAAc,GAAG,CAC3B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,IAAI,KACb,IAAI,CAAC;AAEV;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,cAAc,CAoB5E"}
@@ -1,7 +1,7 @@
1
1
  /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
2
  import { DEFAULT_CORRELATION_HEADER } from '../constants/correlation.constants.js';
3
- import { getCorrelationId, runWithCorrelationId, } from '../correlation.store.js';
4
- import { DEFAULT_CORRELATION_ID_MAX_LENGTH, DEFAULT_CORRELATION_ID_PATTERN, } from '../options/correlation.options.js';
3
+ import { correlationSource, getCorrelationId } from '../correlation.store.js';
4
+ import { admit, assertMaxLength } from '../policy/correlation.policy.js';
5
5
  const HTTP_FIELD_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
6
6
  /**
7
7
  * An HTTP middleware that takes the correlation id from the incoming request header and
@@ -11,9 +11,12 @@ const HTTP_FIELD_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
11
11
  * The header name defaults to `x-correlation-id`. If `header` is omitted or is any
12
12
  * non-string value (including `false`), the default header is used.
13
13
  *
14
- * An incoming id longer than 128 characters or outside the default character set is
15
- * replaced by a local id. `acceptIncoming`, `trimIncoming`, `maxLength` and
16
- * `validateIncoming` adjust that policy.
14
+ * An incoming id that the rules `configureCorrelation` sets reject (by default, longer
15
+ * than 128 characters or outside the default character set) is replaced by a local id
16
+ * and reported to `onRejected` with the source `'http'`. `acceptIncoming`,
17
+ * `trimIncoming`, `maxLength` and `validateIncoming` adjust that policy for this
18
+ * middleware. None of them lets through an id that is not printable ASCII or that starts
19
+ * or ends with a space.
17
20
  *
18
21
  * @throws TypeError when `header` is not a valid HTTP field name, or `maxLength` is not
19
22
  * a positive safe integer.
@@ -27,29 +30,9 @@ export function httpCorrelation(options) {
27
30
  if (typeof h === 'string' && !HTTP_FIELD_NAME.test(h)) {
28
31
  throw new TypeError(`Invalid correlation HTTP header name: "${h}".`);
29
32
  }
30
- if (options?.maxLength !== undefined &&
31
- (!Number.isSafeInteger(options.maxLength) || options.maxLength <= 0)) {
32
- throw new TypeError('Correlation maxLength must be a positive safe integer when provided.');
33
- }
33
+ assertMaxLength(options?.maxLength);
34
34
  const header = typeof h === 'string' ? h.toLowerCase() : DEFAULT_CORRELATION_HEADER;
35
- const acceptIncoming = options?.acceptIncoming ?? true;
36
- const trimIncoming = options?.trimIncoming ?? false;
37
- const maxLength = options?.maxLength ?? DEFAULT_CORRELATION_ID_MAX_LENGTH;
38
- const validateIncoming = options?.validateIncoming ??
39
- ((id) => DEFAULT_CORRELATION_ID_PATTERN.test(id));
40
- const resolveIncoming = (raw) => {
41
- if (!acceptIncoming || typeof raw !== 'string')
42
- return undefined;
43
- const value = trimIncoming ? raw.trim() : raw;
44
- if (value.length === 0 || value.length > maxLength)
45
- return undefined;
46
- try {
47
- return validateIncoming(value) ? value : undefined;
48
- }
49
- catch {
50
- return undefined;
51
- }
52
- };
35
+ const resolveIncoming = incomingResolver(options);
53
36
  return (req, res, next) => {
54
37
  const raw = req.headers?.[header];
55
38
  const candidate = Array.isArray(raw) ? raw[0] : raw;
@@ -57,7 +40,21 @@ export function httpCorrelation(options) {
57
40
  if (typeof res?.setHeader === 'function') {
58
41
  res.setHeader(header, correlationId);
59
42
  }
60
- runWithCorrelationId(correlationId, next);
43
+ correlationSource.run(correlationId, next);
44
+ };
45
+ }
46
+ /** Returns an incoming id the options accept, else `undefined`. */
47
+ function incomingResolver(options) {
48
+ const acceptIncoming = options?.acceptIncoming ?? true;
49
+ const trimIncoming = options?.trimIncoming ?? false;
50
+ const overrides = {
51
+ maxLength: options?.maxLength,
52
+ validateIncoming: options?.validateIncoming,
53
+ };
54
+ return (raw) => {
55
+ if (!acceptIncoming || typeof raw !== 'string')
56
+ return undefined;
57
+ return admit(trimIncoming ? raw.trim() : raw, 'http', 'checked', overrides);
61
58
  };
62
59
  }
63
60
  //# sourceMappingURL=http-correlation.middleware.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-correlation.middleware.js","sourceRoot":"","sources":["../../src/middlewares/http-correlation.middleware.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAGtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AACnF,OAAO,EACL,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEL,iCAAiC,EACjC,8BAA8B,GAC/B,MAAM,mCAAmC,CAAC;AAE3C,MAAM,eAAe,GAAG,gCAAgC,CAAC;AASzD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,eAAe,CAAC,OAA4B;IAC1D,MAAM,CAAC,GAAG,OAAO,EAAE,MAAM,CAAC;IAC1B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,IAAI,CAAC,CAAC;IACvE,CAAC;IACD,IACE,OAAO,EAAE,SAAS,KAAK,SAAS;QAChC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,EACpE,CAAC;QACD,MAAM,IAAI,SAAS,CACjB,sEAAsE,CACvE,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GACV,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,0BAA0B,CAAC;IACvE,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,IAAI,CAAC;IACvD,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,KAAK,CAAC;IACpD,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,iCAAiC,CAAC;IAC1E,MAAM,gBAAgB,GACpB,OAAO,EAAE,gBAAgB;QACzB,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,8BAA8B,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAE5D,MAAM,eAAe,GAAG,CAAC,GAAuB,EAAsB,EAAE;QACtE,IAAI,CAAC,cAAc,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACjE,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;QAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS;YAAE,OAAO,SAAS,CAAC;QACrE,IAAI,CAAC;YACH,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxB,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACpD,MAAM,aAAa,GAAG,eAAe,CAAC,SAAS,CAAC,IAAI,gBAAgB,EAAE,CAAC;QACvE,IAAI,OAAO,GAAG,EAAE,SAAS,KAAK,UAAU,EAAE,CAAC;YACzC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACvC,CAAC;QACD,oBAAoB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"http-correlation.middleware.js","sourceRoot":"","sources":["../../src/middlewares/http-correlation.middleware.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAGtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE9E,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAEzE,MAAM,eAAe,GAAG,gCAAgC,CAAC;AASzD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,eAAe,CAAC,OAA4B;IAC1D,MAAM,CAAC,GAAG,OAAO,EAAE,MAAM,CAAC;IAC1B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,IAAI,CAAC,CAAC;IACvE,CAAC;IACD,eAAe,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAEpC,MAAM,MAAM,GACV,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,0BAA0B,CAAC;IACvE,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAElD,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxB,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACpD,MAAM,aAAa,GAAG,eAAe,CAAC,SAAS,CAAC,IAAI,gBAAgB,EAAE,CAAC;QACvE,IAAI,OAAO,GAAG,EAAE,SAAS,KAAK,UAAU,EAAE,CAAC;YACzC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACvC,CAAC;QACD,iBAAiB,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC,CAAC;AACJ,CAAC;AAED,mEAAmE;AACnE,SAAS,gBAAgB,CACvB,OAA4B;IAE5B,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,IAAI,CAAC;IACvD,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,KAAK,CAAC;IACpD,MAAM,SAAS,GAAG;QAChB,SAAS,EAAE,OAAO,EAAE,SAAS;QAC7B,gBAAgB,EAAE,OAAO,EAAE,gBAAgB;KAC5C,CAAC;IAEF,OAAO,CAAC,GAAG,EAAE,EAAE;QACb,IAAI,CAAC,cAAc,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACjE,OAAO,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAC9E,CAAC,CAAC;AACJ,CAAC"}
@@ -13,10 +13,11 @@ export declare const DEFAULT_CORRELATION_ID_PATTERN: RegExp;
13
13
  * transports (Bull, RabbitMQ, etc.), use `runWithCorrelationId()` directly in
14
14
  * your processor/handler.
15
15
  *
16
- * An incoming header value is accepted only when it is at most
17
- * {@link DEFAULT_CORRELATION_ID_MAX_LENGTH} characters and matches
16
+ * An incoming header value is accepted only when it passes the rules
17
+ * `configureCorrelation` sets, by default at most
18
+ * {@link DEFAULT_CORRELATION_ID_MAX_LENGTH} characters matching
18
19
  * {@link DEFAULT_CORRELATION_ID_PATTERN}; otherwise a local ID is used.
19
- * `maxLength` and `validateIncoming` replace these defaults.
20
+ * `maxLength` and `validateIncoming` replace those rules for this middleware.
20
21
  *
21
22
  * @example
22
23
  * ```ts
@@ -70,14 +71,16 @@ export interface CorrelationOptions {
70
71
  * Maximum accepted incoming correlation-ID length. Longer values are
71
72
  * discarded and replaced by the locally resolved/generated ID.
72
73
  *
73
- * @default 128
74
+ * @default the `maxLength` of `configureCorrelation`, 128 unless configured
74
75
  */
75
76
  maxLength?: number;
76
77
  /**
77
- * Validation predicate for incoming IDs; replaces the default
78
- * {@link DEFAULT_CORRELATION_ID_PATTERN} check. Returning `false` (or
79
- * throwing) rejects the incoming value and falls back to the local
80
- * correlation ID. `maxLength` still applies.
78
+ * Validation predicate for incoming IDs; replaces the `validateIncoming` of
79
+ * `configureCorrelation`, by default the {@link DEFAULT_CORRELATION_ID_PATTERN}
80
+ * check. Returning `false` (or throwing) rejects the incoming value and falls
81
+ * back to the local correlation ID. `maxLength` still applies, and the
82
+ * predicate sees only values of printable ASCII (0x20 to 0x7E) that neither
83
+ * start nor end with a space.
81
84
  *
82
85
  * @example
83
86
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"correlation.options.d.ts","sourceRoot":"","sources":["../../src/options/correlation.options.ts"],"names":[],"mappings":"AAEA,mEAAmE;AACnE,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,QAA4B,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAExB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;;;OAYG;IACH,gBAAgB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC;CACvD"}
1
+ {"version":3,"file":"correlation.options.d.ts","sourceRoot":"","sources":["../../src/options/correlation.options.ts"],"names":[],"mappings":"AAEA,mEAAmE;AACnE,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,QAA4B,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAExB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;;;;;OAcG;IACH,gBAAgB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC;CACvD"}
@@ -0,0 +1,89 @@
1
+ /** Why an incoming correlation id was rejected. */
2
+ export type CorrelationRejectionReason = 'not-string' | 'too-long' | 'invalid-character' | 'surrounding-space' | 'rule';
3
+ /** An incoming correlation id that was replaced, as `onRejected` receives it. */
4
+ export interface CorrelationRejection {
5
+ reason: CorrelationRejectionReason;
6
+ /**
7
+ * Where the id arrived: `'http'` for `httpCorrelation`, `'Class.method'` for a
8
+ * `@WithCorrelation` method, `'runWithCorrelationId'` for a direct call.
9
+ */
10
+ source: string;
11
+ /**
12
+ * The rejected value's length in characters, `0` when it is not a string. The value
13
+ * itself is withheld, so a forged id never reaches a log through this report.
14
+ */
15
+ length: number;
16
+ }
17
+ /** The rules every incoming correlation id must pass, set with {@link configureCorrelation}. */
18
+ export interface CorrelationPolicy {
19
+ /**
20
+ * The longest incoming id accepted, in characters.
21
+ *
22
+ * @default 128
23
+ */
24
+ maxLength?: number;
25
+ /**
26
+ * The application's rule for incoming ids. It replaces the
27
+ * {@link DEFAULT_CORRELATION_ID_PATTERN} test and sees only ids that pass the built-in
28
+ * check and `maxLength`; returning `false` or throwing rejects the id. It must accept
29
+ * the UUIDv7 ids `correlationSource.create()` makes, because the jobs a request
30
+ * enqueues and the services it calls receive them.
31
+ */
32
+ validateIncoming?: (correlationId: string) => boolean;
33
+ /**
34
+ * Called once, synchronously, for each incoming id that is replaced. An error it
35
+ * throws is ignored, so a failing report never fails the work.
36
+ */
37
+ onRejected?: (rejection: CorrelationRejection) => void;
38
+ }
39
+ /** The two rules an entry point can replace for itself. */
40
+ type Rules = Required<Omit<CorrelationPolicy, 'onRejected'>>;
41
+ /**
42
+ * `'checked'`: the entry points that applied the default rule before
43
+ * `configureCorrelation` existed, `httpCorrelation` and `correlationSource.accepts`.
44
+ * `'unchecked'`: those that stored any id, `runWithCorrelationId` and `@WithCorrelation`.
45
+ */
46
+ export type EntryPoint = 'checked' | 'unchecked';
47
+ /**
48
+ * Sets the rules every incoming correlation id must pass, for the whole process:
49
+ * `httpCorrelation`, `runWithCorrelationId`, `@WithCorrelation` and
50
+ * `correlationSource.accepts`, which job contexts use. Call it once at startup, before
51
+ * the first request. Each call replaces every rule.
52
+ *
53
+ * Until it is called, `httpCorrelation` and `correlationSource.accepts` apply the
54
+ * default rules, and `runWithCorrelationId` and `@WithCorrelation` apply only the
55
+ * built-in check. Once it is called, even with an empty policy, every entry point
56
+ * applies its rules. A call without a policy restores the state before the first call.
57
+ *
58
+ * Whatever the rules, an id must first be printable ASCII (0x20 to 0x7E) that neither
59
+ * starts nor ends with a space, and nothing turns that check off. A rejected id is
60
+ * replaced by the current or a new one and reported to `onRejected`; a missing or empty
61
+ * id is replaced without a report. The `maxLength` and `validateIncoming` options of
62
+ * `httpCorrelation`, when given, replace these rules for that middleware.
63
+ *
64
+ * @param policy - The rules; an omitted field takes its default. Omit the policy to
65
+ * restore the state before the first call.
66
+ * @throws {TypeError} When `maxLength` is not a positive safe integer.
67
+ * @example
68
+ * ```ts
69
+ * configureCorrelation({
70
+ * validateIncoming: (id) => /^[0-9a-f-]{36}$/i.test(id),
71
+ * onRejected: ({ reason, source, length }) =>
72
+ * logger.warn({ reason, source, length }, 'correlation id replaced'),
73
+ * });
74
+ * ```
75
+ */
76
+ export declare function configureCorrelation(policy?: CorrelationPolicy): void;
77
+ /** Throws unless `maxLength` is absent or a positive safe integer. */
78
+ export declare function assertMaxLength(maxLength: number | undefined): void;
79
+ /** Whether `id` is printable ASCII that neither starts nor ends with a space. */
80
+ export declare function isPortable(id: unknown): id is string;
81
+ /** Whether the rules of a checked entry point accept `id`; reports nothing. */
82
+ export declare function isAccepted(id: string): boolean;
83
+ /**
84
+ * Returns `id` when the rules of `entryPoint` accept it. Otherwise returns `undefined`
85
+ * and, unless `id` is missing or empty, reports the rejection as coming from `source`.
86
+ */
87
+ export declare function admit(id: unknown, source: string, entryPoint: EntryPoint, overrides?: Partial<Rules>): string | undefined;
88
+ export {};
89
+ //# sourceMappingURL=correlation.policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlation.policy.d.ts","sourceRoot":"","sources":["../../src/policy/correlation.policy.ts"],"names":[],"mappings":"AAOA,mDAAmD;AACnD,MAAM,MAAM,0BAA0B,GAClC,YAAY,GACZ,UAAU,GACV,mBAAmB,GACnB,mBAAmB,GACnB,MAAM,CAAC;AAEX,iFAAiF;AACjF,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,0BAA0B,CAAC;IACnC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gGAAgG;AAChG,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC;IAEtD;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACxD;AAED,2DAA2D;AAC3D,KAAK,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC,CAAC;AAE7D;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,WAAW,CAAC;AAoBjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAarE;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CASnE;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,MAAM,CAEpD;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED;;;GAGG;AACH,wBAAgB,KAAK,CACnB,EAAE,EAAE,OAAO,EACX,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,SAAS,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,GACzB,MAAM,GAAG,SAAS,CAgBpB"}
@@ -0,0 +1,128 @@
1
+ /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
+ import { DEFAULT_CORRELATION_ID_MAX_LENGTH, DEFAULT_CORRELATION_ID_PATTERN, } from '../options/correlation.options.js';
3
+ const PRINTABLE_ASCII = /^[\x20-\x7E]*$/;
4
+ const matchesDefaultPattern = (id) => DEFAULT_CORRELATION_ID_PATTERN.test(id);
5
+ const DEFAULT_RULES = {
6
+ maxLength: DEFAULT_CORRELATION_ID_MAX_LENGTH,
7
+ validateIncoming: matchesDefaultPattern,
8
+ };
9
+ const BUILT_IN_CHECK_ONLY = {
10
+ maxLength: Number.POSITIVE_INFINITY,
11
+ validateIncoming: () => true,
12
+ };
13
+ let configured;
14
+ let onRejected;
15
+ /**
16
+ * Sets the rules every incoming correlation id must pass, for the whole process:
17
+ * `httpCorrelation`, `runWithCorrelationId`, `@WithCorrelation` and
18
+ * `correlationSource.accepts`, which job contexts use. Call it once at startup, before
19
+ * the first request. Each call replaces every rule.
20
+ *
21
+ * Until it is called, `httpCorrelation` and `correlationSource.accepts` apply the
22
+ * default rules, and `runWithCorrelationId` and `@WithCorrelation` apply only the
23
+ * built-in check. Once it is called, even with an empty policy, every entry point
24
+ * applies its rules. A call without a policy restores the state before the first call.
25
+ *
26
+ * Whatever the rules, an id must first be printable ASCII (0x20 to 0x7E) that neither
27
+ * starts nor ends with a space, and nothing turns that check off. A rejected id is
28
+ * replaced by the current or a new one and reported to `onRejected`; a missing or empty
29
+ * id is replaced without a report. The `maxLength` and `validateIncoming` options of
30
+ * `httpCorrelation`, when given, replace these rules for that middleware.
31
+ *
32
+ * @param policy - The rules; an omitted field takes its default. Omit the policy to
33
+ * restore the state before the first call.
34
+ * @throws {TypeError} When `maxLength` is not a positive safe integer.
35
+ * @example
36
+ * ```ts
37
+ * configureCorrelation({
38
+ * validateIncoming: (id) => /^[0-9a-f-]{36}$/i.test(id),
39
+ * onRejected: ({ reason, source, length }) =>
40
+ * logger.warn({ reason, source, length }, 'correlation id replaced'),
41
+ * });
42
+ * ```
43
+ */
44
+ export function configureCorrelation(policy) {
45
+ if (policy === undefined) {
46
+ configured = undefined;
47
+ onRejected = undefined;
48
+ return;
49
+ }
50
+ assertMaxLength(policy.maxLength);
51
+ configured = {
52
+ maxLength: policy.maxLength ?? DEFAULT_RULES.maxLength,
53
+ validateIncoming: policy.validateIncoming ?? DEFAULT_RULES.validateIncoming,
54
+ };
55
+ onRejected = policy.onRejected;
56
+ }
57
+ /** Throws unless `maxLength` is absent or a positive safe integer. */
58
+ export function assertMaxLength(maxLength) {
59
+ if (maxLength !== undefined &&
60
+ (!Number.isSafeInteger(maxLength) || maxLength <= 0)) {
61
+ throw new TypeError('Correlation maxLength must be a positive safe integer when provided.');
62
+ }
63
+ }
64
+ /** Whether `id` is printable ASCII that neither starts nor ends with a space. */
65
+ export function isPortable(id) {
66
+ return typeof id === 'string' && id !== '' && defectOf(id) === undefined;
67
+ }
68
+ /** Whether the rules of a checked entry point accept `id`; reports nothing. */
69
+ export function isAccepted(id) {
70
+ return id !== '' && rejectionOf(id, rulesFor('checked')) === undefined;
71
+ }
72
+ /**
73
+ * Returns `id` when the rules of `entryPoint` accept it. Otherwise returns `undefined`
74
+ * and, unless `id` is missing or empty, reports the rejection as coming from `source`.
75
+ */
76
+ export function admit(id, source, entryPoint, overrides) {
77
+ if (id === undefined || id === null || id === '')
78
+ return undefined;
79
+ const rules = rulesFor(entryPoint);
80
+ const reason = rejectionOf(id, {
81
+ maxLength: overrides?.maxLength ?? rules.maxLength,
82
+ validateIncoming: overrides?.validateIncoming ?? rules.validateIncoming,
83
+ });
84
+ if (reason === undefined)
85
+ return id;
86
+ report({
87
+ reason,
88
+ source,
89
+ length: typeof id === 'string' ? id.length : 0,
90
+ });
91
+ return undefined;
92
+ }
93
+ function rulesFor(entryPoint) {
94
+ if (configured !== undefined)
95
+ return configured;
96
+ return entryPoint === 'checked' ? DEFAULT_RULES : BUILT_IN_CHECK_ONLY;
97
+ }
98
+ function rejectionOf(id, { maxLength, validateIncoming }) {
99
+ if (typeof id !== 'string')
100
+ return 'not-string';
101
+ if (id.length > maxLength)
102
+ return 'too-long';
103
+ return defectOf(id) ?? (passes(validateIncoming, id) ? undefined : 'rule');
104
+ }
105
+ function defectOf(id) {
106
+ if (!PRINTABLE_ASCII.test(id))
107
+ return 'invalid-character';
108
+ if (id.startsWith(' ') || id.endsWith(' '))
109
+ return 'surrounding-space';
110
+ return undefined;
111
+ }
112
+ function passes(validate, id) {
113
+ try {
114
+ return validate(id);
115
+ }
116
+ catch {
117
+ return false;
118
+ }
119
+ }
120
+ function report(rejection) {
121
+ try {
122
+ onRejected?.(rejection);
123
+ }
124
+ catch {
125
+ // A failing report must not fail the request or message it describes.
126
+ }
127
+ }
128
+ //# sourceMappingURL=correlation.policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlation.policy.js","sourceRoot":"","sources":["../../src/policy/correlation.policy.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EACL,iCAAiC,EACjC,8BAA8B,GAC/B,MAAM,mCAAmC,CAAC;AA4D3C,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAEzC,MAAM,qBAAqB,GAAG,CAAC,EAAU,EAAE,EAAE,CAC3C,8BAA8B,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAE1C,MAAM,aAAa,GAAU;IAC3B,SAAS,EAAE,iCAAiC;IAC5C,gBAAgB,EAAE,qBAAqB;CACxC,CAAC;AAEF,MAAM,mBAAmB,GAAU;IACjC,SAAS,EAAE,MAAM,CAAC,iBAAiB;IACnC,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAI;CAC7B,CAAC;AAEF,IAAI,UAA6B,CAAC;AAClC,IAAI,UAA2C,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAA0B;IAC7D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,UAAU,GAAG,SAAS,CAAC;QACvB,UAAU,GAAG,SAAS,CAAC;QACvB,OAAO;IACT,CAAC;IAED,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,UAAU,GAAG;QACX,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,aAAa,CAAC,SAAS;QACtD,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,aAAa,CAAC,gBAAgB;KAC5E,CAAC;IACF,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AACjC,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,eAAe,CAAC,SAA6B;IAC3D,IACE,SAAS,KAAK,SAAS;QACvB,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,CAAC,EACpD,CAAC;QACD,MAAM,IAAI,SAAS,CACjB,sEAAsE,CACvE,CAAC;IACJ,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,UAAU,CAAC,EAAW;IACpC,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,EAAE,IAAI,QAAQ,CAAC,EAAE,CAAC,KAAK,SAAS,CAAC;AAC3E,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,UAAU,CAAC,EAAU;IACnC,OAAO,EAAE,KAAK,EAAE,IAAI,WAAW,CAAC,EAAE,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,SAAS,CAAC;AACzE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,KAAK,CACnB,EAAW,EACX,MAAc,EACd,UAAsB,EACtB,SAA0B;IAE1B,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAEnE,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,EAAE;QAC7B,SAAS,EAAE,SAAS,EAAE,SAAS,IAAI,KAAK,CAAC,SAAS;QAClD,gBAAgB,EAAE,SAAS,EAAE,gBAAgB,IAAI,KAAK,CAAC,gBAAgB;KACxE,CAAC,CAAC;IACH,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAY,CAAC;IAE9C,MAAM,CAAC;QACL,MAAM;QACN,MAAM;QACN,MAAM,EAAE,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;KAC/C,CAAC,CAAC;IACH,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,QAAQ,CAAC,UAAsB;IACtC,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC;IAEhD,OAAO,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,mBAAmB,CAAC;AACxE,CAAC;AAED,SAAS,WAAW,CAClB,EAAW,EACX,EAAE,SAAS,EAAE,gBAAgB,EAAS;IAEtC,IAAI,OAAO,EAAE,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC;IAChD,IAAI,EAAE,CAAC,MAAM,GAAG,SAAS;QAAE,OAAO,UAAU,CAAC;IAE7C,OAAO,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,QAAQ,CACf,EAAU;IAEV,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,mBAAmB,CAAC;IAC1D,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,mBAAmB,CAAC;IACvE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,MAAM,CAAC,QAAiC,EAAE,EAAU;IAC3D,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,SAA+B;IAC7C,IAAI,CAAC;QACH,UAAU,EAAE,CAAC,SAAS,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;IACxE,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cqrs-ddd/pipeline-correlation",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Correlation id propagation for any Node.js application: HTTP middleware, queue and message consumers, cron jobs",
5
5
  "author": "Aristotelis",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -17,6 +17,14 @@
17
17
  "bugs": {
18
18
  "url": "https://github.com/aristoteliss/ddd-cqrs/issues"
19
19
  },
20
+ "keywords": [
21
+ "pipeline",
22
+ "cqrs",
23
+ "correlation-id",
24
+ "request-id",
25
+ "tracing",
26
+ "asynclocalstorage"
27
+ ],
20
28
  "exports": {
21
29
  ".": {
22
30
  "types": "./dist/index.d.ts",
@@ -35,12 +43,12 @@
35
43
  "access": "public"
36
44
  },
37
45
  "devDependencies": {
38
- "@vitest/coverage-v8": "^5.0.3",
39
- "vitest": "^5.0.3"
46
+ "@vitest/coverage-v8": "^5.0.5",
47
+ "vitest": "^5.0.5"
40
48
  },
41
49
  "dependencies": {
42
- "@cqrs-ddd/uuidv7": "^0.5.0",
43
- "@cqrs-ddd/untyped": "^0.5.0"
50
+ "@cqrs-ddd/untyped": "^0.5.2",
51
+ "@cqrs-ddd/uuidv7": "^0.5.2"
44
52
  },
45
53
  "scripts": {
46
54
  "build": "tsc -p tsconfig.build.json",