@webpieces/core-util 0.3.286 → 0.3.287
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/package.json +1 -1
- package/src/ContextKey.d.ts +33 -15
- package/src/ContextKey.js +37 -15
- package/src/ContextKey.js.map +1 -1
- package/src/http/ContextMgr.d.ts +17 -34
- package/src/http/ContextMgr.js +21 -33
- package/src/http/ContextMgr.js.map +1 -1
- package/src/http/ContextReader.d.ts +14 -23
- package/src/http/ContextReader.js.map +1 -1
- package/src/http/HeaderMethods.d.ts +16 -60
- package/src/http/HeaderMethods.js +25 -92
- package/src/http/HeaderMethods.js.map +1 -1
- package/src/http/HeaderRegistry.d.ts +44 -37
- package/src/http/HeaderRegistry.js +88 -70
- package/src/http/HeaderRegistry.js.map +1 -1
- package/src/http/RequestIdChainProcessor.js +3 -2
- package/src/http/RequestIdChainProcessor.js.map +1 -1
- package/src/http/WebpiecesCoreHeaders.d.ts +29 -38
- package/src/http/WebpiecesCoreHeaders.js +28 -48
- package/src/http/WebpiecesCoreHeaders.js.map +1 -1
- package/src/http/recorder/TestCaseRecorder.js +1 -1
- package/src/http/recorder/TestCaseRecorder.js.map +1 -1
- package/src/index.d.ts +0 -4
- package/src/index.js +2 -8
- package/src/index.js.map +1 -1
- package/src/logging/LogManager.d.ts +5 -0
- package/src/logging/LogManager.js +13 -0
- package/src/logging/LogManager.js.map +1 -1
- package/src/Header.d.ts +0 -22
- package/src/Header.js +0 -3
- package/src/Header.js.map +0 -1
- package/src/http/HeaderTypes.d.ts +0 -29
- package/src/http/HeaderTypes.js +0 -33
- package/src/http/HeaderTypes.js.map +0 -1
- package/src/http/PlatformHeader.d.ts +0 -51
- package/src/http/PlatformHeader.js +0 -63
- package/src/http/PlatformHeader.js.map +0 -1
- package/src/http/PlatformHeadersExtension.d.ts +0 -51
- package/src/http/PlatformHeadersExtension.js +0 -59
- package/src/http/PlatformHeadersExtension.js.map +0 -1
|
@@ -2,108 +2,43 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.HeaderMethods = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* HeaderMethods -
|
|
5
|
+
* HeaderMethods - stateless utility for turning context keys + a ContextReader into
|
|
6
|
+
* the maps the framework needs (outbound transfer, masked log map).
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Pattern: Stateless utility class (pure functions, can be instantiated or injected)
|
|
11
|
-
* - Server: Can inject empty instance, use static-like methods
|
|
12
|
-
* - Client: new HeaderMethods() (no DI needed)
|
|
13
|
-
*
|
|
14
|
-
* Usage:
|
|
15
|
-
* ```typescript
|
|
16
|
-
* // Server-side (ContextFilter)
|
|
17
|
-
* constructor(@inject() headerMethods: HeaderMethods) {
|
|
18
|
-
* const allHeaders = [... flatten from extensions ...];
|
|
19
|
-
* this.transferHeaders = headerMethods.findTransferHeaders(allHeaders);
|
|
20
|
-
* }
|
|
21
|
-
*
|
|
22
|
-
* // Client-side (ClientFactory)
|
|
23
|
-
* const headerMethods = new HeaderMethods();
|
|
24
|
-
* const loggableHeaders = headerMethods.findLoggableHeaders(allHeaders, requestHeaders);
|
|
25
|
-
* ```
|
|
8
|
+
* Works in both server (Node) and browser environments; a plain `new HeaderMethods()`
|
|
9
|
+
* (no DI). The set of keys is supplied by the caller (from HeaderRegistry).
|
|
26
10
|
*/
|
|
27
11
|
class HeaderMethods {
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* @param headers - Array of PlatformHeader definitions
|
|
32
|
-
* @returns Filtered array of headers with isWantTransferred=true
|
|
33
|
-
*/
|
|
34
|
-
findTransferHeaders(headers) {
|
|
35
|
-
return headers.filter(h => h.isWantTransferred);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Split headers into secure and public categories.
|
|
39
|
-
*
|
|
40
|
-
* @param headers - Array of PlatformHeader definitions
|
|
41
|
-
* @returns SplitHeaders with secureHeaders (isSecured=true) and publicHeaders (isSecured=false)
|
|
42
|
-
*/
|
|
43
|
-
secureHeaders(headers) {
|
|
44
|
-
return headers.filter(h => h.isSecured);
|
|
12
|
+
/** Keys that transfer over the wire (httpHeader set). */
|
|
13
|
+
findTransferKeys(keys) {
|
|
14
|
+
return keys.filter(k => k.httpHeader !== undefined);
|
|
45
15
|
}
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
*
|
|
50
|
-
* @param headers - Array of PlatformHeader definitions
|
|
51
|
-
* @returns All headers (they're all loggable, just some are masked)
|
|
52
|
-
*/
|
|
53
|
-
findLoggableHeaders(headers) {
|
|
54
|
-
return headers; // All headers are loggable, secure ones will be masked
|
|
55
|
-
}
|
|
56
|
-
buildSecureMapForLogs(platformHeaders, contextReader) {
|
|
57
|
-
const headers = new Map();
|
|
58
|
-
for (const header of platformHeaders) {
|
|
59
|
-
const value = contextReader.read(header);
|
|
60
|
-
if (value) {
|
|
61
|
-
// MDC-style key when defined (Java getLoggerMDCKey), else the raw header name
|
|
62
|
-
const logKey = header.loggerMdcKey ?? header.headerName;
|
|
63
|
-
if (!header.isSecured)
|
|
64
|
-
headers.set(logKey, value);
|
|
65
|
-
else
|
|
66
|
-
headers.set(logKey, this.maskSecureValue(value));
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return headers;
|
|
16
|
+
/** Keys whose values are masked in logs (isSecured=true). */
|
|
17
|
+
securedKeys(keys) {
|
|
18
|
+
return keys.filter(k => k.isSecured);
|
|
70
19
|
}
|
|
71
20
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* Masking rules for secure headers (isSecured=true):
|
|
76
|
-
* - Length > 15: Show first 3 and last 3 characters with "..." between
|
|
77
|
-
* - Length 8-15: Show first 2 characters with "..."
|
|
78
|
-
* - Length < 8: Show "<secure key too short to log>"
|
|
79
|
-
*
|
|
80
|
-
* @param loggableHeaders - Filtered PlatformHeaders to log
|
|
81
|
-
* @param headerMap - Map of header name (lowercase) -> array of values from request
|
|
82
|
-
* @returns Record of header name -> masked or full value for logging
|
|
21
|
+
* Build the map for LOGGING from the given keys: each logged key (isLogged=true)
|
|
22
|
+
* with a value present is added under its `name`, masked when isSecured.
|
|
83
23
|
*/
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
for (const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (!values || values.length === 0) {
|
|
90
|
-
continue;
|
|
24
|
+
buildSecureMapForLogs(keys, contextReader) {
|
|
25
|
+
const logMap = new Map();
|
|
26
|
+
for (const key of keys) {
|
|
27
|
+
if (!key.isLogged) {
|
|
28
|
+
continue; // never logged (e.g. recorder, method-meta)
|
|
91
29
|
}
|
|
92
|
-
const value =
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
result[platformHeader.headerName] = value;
|
|
30
|
+
const value = contextReader.read(key);
|
|
31
|
+
if (value) {
|
|
32
|
+
logMap.set(key.name, key.isSecured ? this.maskSecureValue(value) : value);
|
|
98
33
|
}
|
|
99
34
|
}
|
|
100
|
-
return
|
|
35
|
+
return logMap;
|
|
101
36
|
}
|
|
102
37
|
/**
|
|
103
|
-
* Mask a secure
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
38
|
+
* Mask a secure value based on its length.
|
|
39
|
+
* - Length > 15: first 3 + "..." + last 3
|
|
40
|
+
* - Length 8-15: first 2 + "..."
|
|
41
|
+
* - Length < 8: "<secure key too short to log>"
|
|
107
42
|
*/
|
|
108
43
|
maskSecureValue(value) {
|
|
109
44
|
const len = value.length;
|
|
@@ -111,11 +46,9 @@ class HeaderMethods {
|
|
|
111
46
|
return '<secure key too short to log>';
|
|
112
47
|
}
|
|
113
48
|
else if (len <= 15) {
|
|
114
|
-
// 8-15 characters: show first 2 + "..."
|
|
115
49
|
return `${value.substring(0, 2)}...`;
|
|
116
50
|
}
|
|
117
51
|
else {
|
|
118
|
-
// > 15 characters: show first 3 + "..." + last 3
|
|
119
52
|
return `${value.substring(0, 3)}...${value.substring(len - 3)}`;
|
|
120
53
|
}
|
|
121
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeaderMethods.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/HeaderMethods.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"HeaderMethods.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/HeaderMethods.ts"],"names":[],"mappings":";;;AAGA;;;;;;GAMG;AACH,MAAa,aAAa;IACtB,yDAAyD;IACzD,gBAAgB,CAAC,IAAkB;QAC/B,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC;IACxD,CAAC;IAED,6DAA6D;IAC7D,WAAW,CAAC,IAAkB;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;IAED;;;OAGG;IACH,qBAAqB,CAAC,IAAkB,EAAE,aAA4B;QAClE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QAEzC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;gBAChB,SAAS,CAAC,4CAA4C;YAC1D,CAAC;YACD,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,IAAI,KAAK,EAAE,CAAC;gBACR,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC9E,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;IAED;;;;;OAKG;IACK,eAAe,CAAC,KAAa;QACjC,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;QAEzB,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACV,OAAO,+BAA+B,CAAC;QAC3C,CAAC;aAAM,IAAI,GAAG,IAAI,EAAE,EAAE,CAAC;YACnB,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;QACzC,CAAC;aAAM,CAAC;YACJ,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;QACpE,CAAC;IACL,CAAC;CACJ;AAhDD,sCAgDC","sourcesContent":["import { ContextKey } from '../ContextKey';\nimport { ContextReader } from './ContextReader';\n\n/**\n * HeaderMethods - stateless utility for turning context keys + a ContextReader into\n * the maps the framework needs (outbound transfer, masked log map).\n *\n * Works in both server (Node) and browser environments; a plain `new HeaderMethods()`\n * (no DI). The set of keys is supplied by the caller (from HeaderRegistry).\n */\nexport class HeaderMethods {\n /** Keys that transfer over the wire (httpHeader set). */\n findTransferKeys(keys: ContextKey[]): ContextKey[] {\n return keys.filter(k => k.httpHeader !== undefined);\n }\n\n /** Keys whose values are masked in logs (isSecured=true). */\n securedKeys(keys: ContextKey[]): ContextKey[] {\n return keys.filter(k => k.isSecured);\n }\n\n /**\n * Build the map for LOGGING from the given keys: each logged key (isLogged=true)\n * with a value present is added under its `name`, masked when isSecured.\n */\n buildSecureMapForLogs(keys: ContextKey[], contextReader: ContextReader): Map<string, string> {\n const logMap = new Map<string, string>();\n\n for (const key of keys) {\n if (!key.isLogged) {\n continue; // never logged (e.g. recorder, method-meta)\n }\n const value = contextReader.read(key);\n if (value) {\n logMap.set(key.name, key.isSecured ? this.maskSecureValue(value) : value);\n }\n }\n\n return logMap;\n }\n\n /**\n * Mask a secure value based on its length.\n * - Length > 15: first 3 + \"...\" + last 3\n * - Length 8-15: first 2 + \"...\"\n * - Length < 8: \"<secure key too short to log>\"\n */\n private maskSecureValue(value: string): string {\n const len = value.length;\n\n if (len < 8) {\n return '<secure key too short to log>';\n } else if (len <= 15) {\n return `${value.substring(0, 2)}...`;\n } else {\n return `${value.substring(0, 3)}...${value.substring(len - 3)}`;\n }\n }\n}\n"]}
|
|
@@ -1,56 +1,63 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { PlatformHeadersExtension } from './PlatformHeadersExtension';
|
|
1
|
+
import { ContextKey } from '../ContextKey';
|
|
3
2
|
/**
|
|
4
|
-
* HeaderRegistry -
|
|
5
|
-
*
|
|
3
|
+
* HeaderRegistry - the single, GLOBAL source of truth for every ContextKey the
|
|
4
|
+
* platform knows about. Port of Java webpieces' HeaderTranslation.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Configured exactly like {@link LogManager} — once, at process startup — and then
|
|
7
|
+
* globally accessible. There is NO DI wiring: filters/clients call
|
|
8
|
+
* `HeaderRegistry.get()` instead of injecting it.
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* - Browser (no DI): `new HeaderRegistry([new PlatformHeadersExtension([...])])`.
|
|
10
|
+
* ```ts
|
|
11
|
+
* // startup (server AND browser), BEFORE LogManager.setFactory(...):
|
|
12
|
+
* HeaderRegistry.configure(AppHeaders.getAllHeaders(), CompanyHeaders.getAllHeaders(), true);
|
|
13
|
+
* ```
|
|
16
14
|
*
|
|
17
|
-
*
|
|
15
|
+
* - `svrHeaders` this server's own keys.
|
|
16
|
+
* - `companyHeaders` keys from a shared company lib all services use.
|
|
17
|
+
* - `platformHeaders` when true, also include {@link HeaderRegistry.DEFAULT_HEADERS}
|
|
18
|
+
* (the webpieces common keys: request-id, correlation-id, ...).
|
|
19
|
+
*
|
|
20
|
+
* Duplicate validation (port of Java checkForDuplicates) runs at configure() time,
|
|
18
21
|
* so conflicting definitions fail fast at startup:
|
|
19
|
-
* - Two
|
|
20
|
-
* - Two
|
|
21
|
-
* - Exact duplicates
|
|
22
|
+
* - Two keys with the same `name` must agree on httpHeader/isSecured/isLogged.
|
|
23
|
+
* - Two keys with the same `httpHeader` must agree on `name`.
|
|
24
|
+
* - Exact duplicates collapse to one entry.
|
|
22
25
|
*/
|
|
23
26
|
export declare class HeaderRegistry {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
getHeaders(): PlatformHeader[];
|
|
27
|
+
/** The webpieces-supplied common keys; included when platformHeaders=true. */
|
|
28
|
+
static readonly DEFAULT_HEADERS: ContextKey[];
|
|
29
|
+
private static instance;
|
|
30
|
+
private readonly keys;
|
|
31
|
+
private constructor();
|
|
30
32
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
+
* Install the process-wide registry. Call once at startup, BEFORE
|
|
34
|
+
* LogManager.setFactory(...) (logging masks/keys off this registry).
|
|
33
35
|
*/
|
|
34
|
-
|
|
36
|
+
static configure(svrHeaders: ContextKey[], companyHeaders: ContextKey[], platformHeaders: boolean): void;
|
|
37
|
+
/** The configured registry. Throws if configure() has not been called. */
|
|
38
|
+
static get(): HeaderRegistry;
|
|
39
|
+
/** True once configure() has run. Used by LogManager.setFactory to fail fast. */
|
|
40
|
+
static isConfigured(): boolean;
|
|
41
|
+
/** All registered keys (deduplicated). */
|
|
42
|
+
getKeys(): ContextKey[];
|
|
35
43
|
/**
|
|
36
|
-
*
|
|
44
|
+
* Keys that transfer over the wire (inbound request -> context, and context ->
|
|
45
|
+
* outbound request): those with an httpHeader set.
|
|
37
46
|
*/
|
|
47
|
+
getTransferredKeys(): ContextKey[];
|
|
48
|
+
/** Names (log keys) whose values must be masked in logs. isSecured=true. */
|
|
38
49
|
getSecuredNames(): string[];
|
|
50
|
+
/** Keys that appear in logs. isLogged=true. */
|
|
51
|
+
getLoggedKeys(): ContextKey[];
|
|
52
|
+
/** Look up a key by its HTTP header name (case-insensitive). */
|
|
53
|
+
findByHttpHeader(httpHeader: string): ContextKey | undefined;
|
|
39
54
|
/**
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
getMdcHeaders(): PlatformHeader[];
|
|
43
|
-
/**
|
|
44
|
-
* Look up a header definition by its HTTP name (case-insensitive).
|
|
45
|
-
*/
|
|
46
|
-
findByName(headerName: string): PlatformHeader | undefined;
|
|
47
|
-
/**
|
|
48
|
-
* Port of Java HeaderTranslation.checkForDuplicates: collapse exact
|
|
49
|
-
* duplicates, throw on conflicting definitions sharing a name or MDC key.
|
|
55
|
+
* Collapse exact duplicates, throw on conflicting definitions sharing a `name`
|
|
56
|
+
* or an `httpHeader`.
|
|
50
57
|
*/
|
|
51
58
|
private checkForDuplicates;
|
|
52
59
|
/**
|
|
53
|
-
* Two
|
|
60
|
+
* Two keys sharing a `name` must agree on httpHeader/isSecured/isLogged,
|
|
54
61
|
* otherwise the platform would behave differently depending on which module's
|
|
55
62
|
* definition happened to load first.
|
|
56
63
|
*/
|
|
@@ -1,119 +1,137 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.HeaderRegistry = void 0;
|
|
4
|
+
const WebpiecesCoreHeaders_1 = require("./WebpiecesCoreHeaders");
|
|
4
5
|
/**
|
|
5
|
-
* HeaderRegistry -
|
|
6
|
-
*
|
|
6
|
+
* HeaderRegistry - the single, GLOBAL source of truth for every ContextKey the
|
|
7
|
+
* platform knows about. Port of Java webpieces' HeaderTranslation.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Configured exactly like {@link LogManager} — once, at process startup — and then
|
|
10
|
+
* globally accessible. There is NO DI wiring: filters/clients call
|
|
11
|
+
* `HeaderRegistry.get()` instead of injecting it.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* - Browser (no DI): `new HeaderRegistry([new PlatformHeadersExtension([...])])`.
|
|
13
|
+
* ```ts
|
|
14
|
+
* // startup (server AND browser), BEFORE LogManager.setFactory(...):
|
|
15
|
+
* HeaderRegistry.configure(AppHeaders.getAllHeaders(), CompanyHeaders.getAllHeaders(), true);
|
|
16
|
+
* ```
|
|
17
17
|
*
|
|
18
|
-
*
|
|
18
|
+
* - `svrHeaders` this server's own keys.
|
|
19
|
+
* - `companyHeaders` keys from a shared company lib all services use.
|
|
20
|
+
* - `platformHeaders` when true, also include {@link HeaderRegistry.DEFAULT_HEADERS}
|
|
21
|
+
* (the webpieces common keys: request-id, correlation-id, ...).
|
|
22
|
+
*
|
|
23
|
+
* Duplicate validation (port of Java checkForDuplicates) runs at configure() time,
|
|
19
24
|
* so conflicting definitions fail fast at startup:
|
|
20
|
-
* - Two
|
|
21
|
-
* - Two
|
|
22
|
-
* - Exact duplicates
|
|
25
|
+
* - Two keys with the same `name` must agree on httpHeader/isSecured/isLogged.
|
|
26
|
+
* - Two keys with the same `httpHeader` must agree on `name`.
|
|
27
|
+
* - Exact duplicates collapse to one entry.
|
|
23
28
|
*/
|
|
24
29
|
class HeaderRegistry {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
this.headers = this.checkForDuplicates(allHeaders);
|
|
30
|
+
/** The webpieces-supplied common keys; included when platformHeaders=true. */
|
|
31
|
+
static DEFAULT_HEADERS = WebpiecesCoreHeaders_1.WebpiecesCoreHeaders.getAllHeaders();
|
|
32
|
+
static instance;
|
|
33
|
+
keys;
|
|
34
|
+
constructor(keys) {
|
|
35
|
+
this.keys = this.checkForDuplicates(keys);
|
|
32
36
|
}
|
|
33
37
|
/**
|
|
34
|
-
*
|
|
38
|
+
* Install the process-wide registry. Call once at startup, BEFORE
|
|
39
|
+
* LogManager.setFactory(...) (logging masks/keys off this registry).
|
|
35
40
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
static configure(svrHeaders, companyHeaders, platformHeaders) {
|
|
42
|
+
const all = [
|
|
43
|
+
...(platformHeaders ? HeaderRegistry.DEFAULT_HEADERS : []),
|
|
44
|
+
...companyHeaders,
|
|
45
|
+
...svrHeaders,
|
|
46
|
+
];
|
|
47
|
+
HeaderRegistry.instance = new HeaderRegistry(all);
|
|
38
48
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
/** The configured registry. Throws if configure() has not been called. */
|
|
50
|
+
static get() {
|
|
51
|
+
if (!HeaderRegistry.instance) {
|
|
52
|
+
throw new Error('HeaderRegistry.configure(...) has not been called. Configure the registry ' +
|
|
53
|
+
'at startup (before LogManager.setFactory) so filters/logging know the context keys.');
|
|
54
|
+
}
|
|
55
|
+
return HeaderRegistry.instance;
|
|
56
|
+
}
|
|
57
|
+
/** True once configure() has run. Used by LogManager.setFactory to fail fast. */
|
|
58
|
+
static isConfigured() {
|
|
59
|
+
return HeaderRegistry.instance !== undefined;
|
|
60
|
+
}
|
|
61
|
+
/** All registered keys (deduplicated). */
|
|
62
|
+
getKeys() {
|
|
63
|
+
return this.keys;
|
|
45
64
|
}
|
|
46
65
|
/**
|
|
47
|
-
*
|
|
66
|
+
* Keys that transfer over the wire (inbound request -> context, and context ->
|
|
67
|
+
* outbound request): those with an httpHeader set.
|
|
48
68
|
*/
|
|
69
|
+
getTransferredKeys() {
|
|
70
|
+
return this.keys.filter((k) => k.httpHeader !== undefined);
|
|
71
|
+
}
|
|
72
|
+
/** Names (log keys) whose values must be masked in logs. isSecured=true. */
|
|
49
73
|
getSecuredNames() {
|
|
50
|
-
return this.
|
|
51
|
-
.filter((
|
|
52
|
-
.map((
|
|
74
|
+
return this.keys
|
|
75
|
+
.filter((k) => k.isSecured)
|
|
76
|
+
.map((k) => k.name);
|
|
53
77
|
}
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
getMdcHeaders() {
|
|
58
|
-
return this.headers.filter((h) => h.loggerMdcKey !== undefined);
|
|
78
|
+
/** Keys that appear in logs. isLogged=true. */
|
|
79
|
+
getLoggedKeys() {
|
|
80
|
+
return this.keys.filter((k) => k.isLogged);
|
|
59
81
|
}
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const lower = headerName.toLowerCase();
|
|
65
|
-
return this.headers.find((h) => h.headerName.toLowerCase() === lower);
|
|
82
|
+
/** Look up a key by its HTTP header name (case-insensitive). */
|
|
83
|
+
findByHttpHeader(httpHeader) {
|
|
84
|
+
const lower = httpHeader.toLowerCase();
|
|
85
|
+
return this.keys.find((k) => k.httpHeader?.toLowerCase() === lower);
|
|
66
86
|
}
|
|
67
87
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
88
|
+
* Collapse exact duplicates, throw on conflicting definitions sharing a `name`
|
|
89
|
+
* or an `httpHeader`.
|
|
70
90
|
*/
|
|
71
|
-
checkForDuplicates(
|
|
91
|
+
checkForDuplicates(allKeys) {
|
|
72
92
|
const byName = new Map();
|
|
73
|
-
const
|
|
74
|
-
for (const
|
|
75
|
-
const nameKey =
|
|
93
|
+
const byHttpHeader = new Map();
|
|
94
|
+
for (const key of allKeys) {
|
|
95
|
+
const nameKey = key.name.toLowerCase();
|
|
76
96
|
const existing = byName.get(nameKey);
|
|
77
97
|
if (existing) {
|
|
78
|
-
this.assertSameDefinition(existing,
|
|
98
|
+
this.assertSameDefinition(existing, key);
|
|
79
99
|
continue; // exact duplicate - collapse
|
|
80
100
|
}
|
|
81
|
-
byName.set(nameKey,
|
|
82
|
-
if (
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
`
|
|
101
|
+
byName.set(nameKey, key);
|
|
102
|
+
if (key.httpHeader !== undefined) {
|
|
103
|
+
const headerKey = key.httpHeader.toLowerCase();
|
|
104
|
+
const clash = byHttpHeader.get(headerKey);
|
|
105
|
+
if (clash) {
|
|
106
|
+
throw new Error(`Duplicate ContextKey httpHeader '${key.httpHeader}': ` +
|
|
107
|
+
`defined by key '${clash.name}' AND key '${key.name}'. ` +
|
|
108
|
+
`Each HTTP header must map to exactly one context key.`);
|
|
88
109
|
}
|
|
89
|
-
|
|
110
|
+
byHttpHeader.set(headerKey, key);
|
|
90
111
|
}
|
|
91
112
|
}
|
|
92
113
|
return Array.from(byName.values());
|
|
93
114
|
}
|
|
94
115
|
/**
|
|
95
|
-
* Two
|
|
116
|
+
* Two keys sharing a `name` must agree on httpHeader/isSecured/isLogged,
|
|
96
117
|
* otherwise the platform would behave differently depending on which module's
|
|
97
118
|
* definition happened to load first.
|
|
98
119
|
*/
|
|
99
120
|
assertSameDefinition(existing, duplicate) {
|
|
100
121
|
const conflicts = [];
|
|
101
|
-
if (existing.
|
|
102
|
-
conflicts.push(`
|
|
122
|
+
if (existing.httpHeader !== duplicate.httpHeader) {
|
|
123
|
+
conflicts.push(`httpHeader ('${existing.httpHeader}' vs '${duplicate.httpHeader}')`);
|
|
103
124
|
}
|
|
104
125
|
if (existing.isSecured !== duplicate.isSecured) {
|
|
105
126
|
conflicts.push(`isSecured (${existing.isSecured} vs ${duplicate.isSecured})`);
|
|
106
127
|
}
|
|
107
|
-
if (existing.
|
|
108
|
-
conflicts.push(`
|
|
109
|
-
}
|
|
110
|
-
if (existing.loggerMdcKey !== duplicate.loggerMdcKey) {
|
|
111
|
-
conflicts.push(`loggerMdcKey ('${existing.loggerMdcKey}' vs '${duplicate.loggerMdcKey}')`);
|
|
128
|
+
if (existing.isLogged !== duplicate.isLogged) {
|
|
129
|
+
conflicts.push(`isLogged (${existing.isLogged} vs ${duplicate.isLogged})`);
|
|
112
130
|
}
|
|
113
131
|
if (conflicts.length > 0) {
|
|
114
|
-
throw new Error(`Conflicting
|
|
132
|
+
throw new Error(`Conflicting ContextKey definitions for '${existing.name}': ` +
|
|
115
133
|
`two modules registered it with different ${conflicts.join(', ')}. ` +
|
|
116
|
-
`
|
|
134
|
+
`Keys sharing a name must agree on all flags.`);
|
|
117
135
|
}
|
|
118
136
|
}
|
|
119
137
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeaderRegistry.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/HeaderRegistry.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"HeaderRegistry.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/HeaderRegistry.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContextKey } from '../ContextKey';\nimport { WebpiecesCoreHeaders } from './WebpiecesCoreHeaders';\n\n/**\n * HeaderRegistry - the single, GLOBAL source of truth for every ContextKey the\n * platform knows about. Port of Java webpieces' HeaderTranslation.\n *\n * Configured exactly like {@link LogManager} — once, at process startup — and then\n * globally accessible. There is NO DI wiring: filters/clients call\n * `HeaderRegistry.get()` instead of injecting it.\n *\n * ```ts\n * // startup (server AND browser), BEFORE LogManager.setFactory(...):\n * HeaderRegistry.configure(AppHeaders.getAllHeaders(), CompanyHeaders.getAllHeaders(), true);\n * ```\n *\n * - `svrHeaders` this server's own keys.\n * - `companyHeaders` keys from a shared company lib all services use.\n * - `platformHeaders` when true, also include {@link HeaderRegistry.DEFAULT_HEADERS}\n * (the webpieces common keys: request-id, correlation-id, ...).\n *\n * Duplicate validation (port of Java checkForDuplicates) runs at configure() time,\n * so conflicting definitions fail fast at startup:\n * - Two keys with the same `name` must agree on httpHeader/isSecured/isLogged.\n * - Two keys with the same `httpHeader` must agree on `name`.\n * - Exact duplicates collapse to one entry.\n */\nexport class HeaderRegistry {\n /** The webpieces-supplied common keys; included when platformHeaders=true. */\n static readonly DEFAULT_HEADERS: ContextKey[] = WebpiecesCoreHeaders.getAllHeaders();\n\n private static instance: HeaderRegistry | undefined;\n\n private readonly keys: ContextKey[];\n\n private constructor(keys: ContextKey[]) {\n this.keys = this.checkForDuplicates(keys);\n }\n\n /**\n * Install the process-wide registry. Call once at startup, BEFORE\n * LogManager.setFactory(...) (logging masks/keys off this registry).\n */\n static configure(svrHeaders: ContextKey[], companyHeaders: ContextKey[], platformHeaders: boolean): void {\n const all: ContextKey[] = [\n ...(platformHeaders ? HeaderRegistry.DEFAULT_HEADERS : []),\n ...companyHeaders,\n ...svrHeaders,\n ];\n HeaderRegistry.instance = new HeaderRegistry(all);\n }\n\n /** The configured registry. Throws if configure() has not been called. */\n static get(): HeaderRegistry {\n if (!HeaderRegistry.instance) {\n throw new Error(\n 'HeaderRegistry.configure(...) has not been called. Configure the registry ' +\n 'at startup (before LogManager.setFactory) so filters/logging know the context keys.',\n );\n }\n return HeaderRegistry.instance;\n }\n\n /** True once configure() has run. Used by LogManager.setFactory to fail fast. */\n static isConfigured(): boolean {\n return HeaderRegistry.instance !== undefined;\n }\n\n /** All registered keys (deduplicated). */\n getKeys(): ContextKey[] {\n return this.keys;\n }\n\n /**\n * Keys that transfer over the wire (inbound request -> context, and context ->\n * outbound request): those with an httpHeader set.\n */\n getTransferredKeys(): ContextKey[] {\n return this.keys.filter((k: ContextKey) => k.httpHeader !== undefined);\n }\n\n /** Names (log keys) whose values must be masked in logs. isSecured=true. */\n getSecuredNames(): string[] {\n return this.keys\n .filter((k: ContextKey) => k.isSecured)\n .map((k: ContextKey) => k.name);\n }\n\n /** Keys that appear in logs. isLogged=true. */\n getLoggedKeys(): ContextKey[] {\n return this.keys.filter((k: ContextKey) => k.isLogged);\n }\n\n /** Look up a key by its HTTP header name (case-insensitive). */\n findByHttpHeader(httpHeader: string): ContextKey | undefined {\n const lower = httpHeader.toLowerCase();\n return this.keys.find((k: ContextKey) => k.httpHeader?.toLowerCase() === lower);\n }\n\n /**\n * Collapse exact duplicates, throw on conflicting definitions sharing a `name`\n * or an `httpHeader`.\n */\n private checkForDuplicates(allKeys: ContextKey[]): ContextKey[] {\n const byName = new Map<string, ContextKey>();\n const byHttpHeader = new Map<string, ContextKey>();\n\n for (const key of allKeys) {\n const nameKey = key.name.toLowerCase();\n const existing = byName.get(nameKey);\n if (existing) {\n this.assertSameDefinition(existing, key);\n continue; // exact duplicate - collapse\n }\n byName.set(nameKey, key);\n\n if (key.httpHeader !== undefined) {\n const headerKey = key.httpHeader.toLowerCase();\n const clash = byHttpHeader.get(headerKey);\n if (clash) {\n throw new Error(\n `Duplicate ContextKey httpHeader '${key.httpHeader}': ` +\n `defined by key '${clash.name}' AND key '${key.name}'. ` +\n `Each HTTP header must map to exactly one context key.`,\n );\n }\n byHttpHeader.set(headerKey, key);\n }\n }\n\n return Array.from(byName.values());\n }\n\n /**\n * Two keys sharing a `name` must agree on httpHeader/isSecured/isLogged,\n * otherwise the platform would behave differently depending on which module's\n * definition happened to load first.\n */\n private assertSameDefinition(existing: ContextKey, duplicate: ContextKey): void {\n const conflicts: string[] = [];\n if (existing.httpHeader !== duplicate.httpHeader) {\n conflicts.push(`httpHeader ('${existing.httpHeader}' vs '${duplicate.httpHeader}')`);\n }\n if (existing.isSecured !== duplicate.isSecured) {\n conflicts.push(`isSecured (${existing.isSecured} vs ${duplicate.isSecured})`);\n }\n if (existing.isLogged !== duplicate.isLogged) {\n conflicts.push(`isLogged (${existing.isLogged} vs ${duplicate.isLogged})`);\n }\n if (conflicts.length > 0) {\n throw new Error(\n `Conflicting ContextKey definitions for '${existing.name}': ` +\n `two modules registered it with different ${conflicts.join(', ')}. ` +\n `Keys sharing a name must agree on all flags.`,\n );\n }\n }\n}\n"]}
|
|
@@ -21,8 +21,9 @@ class RequestIdChainProcessor {
|
|
|
21
21
|
* Rewrite the outbound header map in place: x-request-id -> x-previous-request-id.
|
|
22
22
|
*/
|
|
23
23
|
process(outboundHeaders) {
|
|
24
|
-
|
|
25
|
-
const
|
|
24
|
+
// The outbound map is keyed by wire (HTTP header) name.
|
|
25
|
+
const requestIdName = WebpiecesCoreHeaders_1.WebpiecesCoreHeaders.REQUEST_ID.httpHeader;
|
|
26
|
+
const previousIdName = WebpiecesCoreHeaders_1.WebpiecesCoreHeaders.PREVIOUS_REQUEST_ID.httpHeader;
|
|
26
27
|
const currentRequestId = outboundHeaders.get(requestIdName);
|
|
27
28
|
if (currentRequestId === undefined) {
|
|
28
29
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestIdChainProcessor.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/RequestIdChainProcessor.ts"],"names":[],"mappings":";;;AAAA,iEAA8D;AAE9D;;;;;;;;;;;;;GAaG;AACH,MAAa,uBAAuB;IAChC;;OAEG;IACH,OAAO,CAAC,eAAoC;QACxC,MAAM,aAAa,GAAG,2CAAoB,CAAC,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"RequestIdChainProcessor.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/RequestIdChainProcessor.ts"],"names":[],"mappings":";;;AAAA,iEAA8D;AAE9D;;;;;;;;;;;;;GAaG;AACH,MAAa,uBAAuB;IAChC;;OAEG;IACH,OAAO,CAAC,eAAoC;QACxC,wDAAwD;QACxD,MAAM,aAAa,GAAG,2CAAoB,CAAC,UAAU,CAAC,UAAW,CAAC;QAClE,MAAM,cAAc,GAAG,2CAAoB,CAAC,mBAAmB,CAAC,UAAW,CAAC;QAE5E,MAAM,gBAAgB,GAAG,eAAe,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QAC5D,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;YACjC,OAAO;QACX,CAAC;QAED,eAAe,CAAC,GAAG,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;QACtD,eAAe,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAC1C,CAAC;CACJ;AAjBD,0DAiBC","sourcesContent":["import { WebpiecesCoreHeaders } from './WebpiecesCoreHeaders';\n\n/**\n * RequestIdChainProcessor - Builds the per-hop distributed-trace chain.\n *\n * TS equivalent of the Java MicroSvcHeader REQUEST_ID/PREVIOUS_REQUEST_ID flow:\n * when a server makes an outbound call, its CURRENT request id is sent to the\n * downstream service as x-previous-request-id, and x-request-id is NOT sent -\n * the downstream ContextFilter then generates a fresh id for its own hop.\n * Result: every hop has its own id plus a pointer to its caller's id, forming\n * a trace tree.\n *\n * Invoked by ContextMgr.buildOutboundHeaders() after the transferred headers\n * are collected. Opt out via `new ContextMgr(reader, registry, false)` if you\n * want raw pass-through of x-request-id instead.\n */\nexport class RequestIdChainProcessor {\n /**\n * Rewrite the outbound header map in place: x-request-id -> x-previous-request-id.\n */\n process(outboundHeaders: Map<string, string>): void {\n // The outbound map is keyed by wire (HTTP header) name.\n const requestIdName = WebpiecesCoreHeaders.REQUEST_ID.httpHeader!;\n const previousIdName = WebpiecesCoreHeaders.PREVIOUS_REQUEST_ID.httpHeader!;\n\n const currentRequestId = outboundHeaders.get(requestIdName);\n if (currentRequestId === undefined) {\n return;\n }\n\n outboundHeaders.set(previousIdName, currentRequestId);\n outboundHeaders.delete(requestIdName);\n }\n}\n"]}
|