@webpieces/core-util 0.3.314 → 0.3.316
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 +8 -0
- package/src/ContextKey.js +22 -0
- package/src/ContextKey.js.map +1 -1
- package/src/http/HeaderMethods.d.ts +7 -13
- package/src/http/HeaderMethods.js +8 -29
- package/src/http/HeaderMethods.js.map +1 -1
- package/src/http/HeaderRegistry.d.ts +5 -1
- package/src/http/HeaderRegistry.js +20 -8
- package/src/http/HeaderRegistry.js.map +1 -1
package/package.json
CHANGED
package/src/ContextKey.d.ts
CHANGED
|
@@ -36,4 +36,12 @@ export declare class ContextKey {
|
|
|
36
36
|
constructor(name: string, httpHeader?: string, isSecured?: boolean, isLogged?: boolean);
|
|
37
37
|
/** True when this key is transferred over HTTP (has an httpHeader). */
|
|
38
38
|
isTransferred(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The value as it should appear in a log line: returned as-is for a normal
|
|
41
|
+
* key, partially masked when this key is secured. Masking is length-based:
|
|
42
|
+
* - Length > 15: first 3 + "..." + last 3
|
|
43
|
+
* - Length 8-15: first 2 + "..."
|
|
44
|
+
* - Length < 8: "<secure key too short to log>"
|
|
45
|
+
*/
|
|
46
|
+
maskIfSecured(value: string): string;
|
|
39
47
|
}
|
package/src/ContextKey.js
CHANGED
|
@@ -46,6 +46,28 @@ class ContextKey {
|
|
|
46
46
|
isTransferred() {
|
|
47
47
|
return this.httpHeader !== undefined;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* The value as it should appear in a log line: returned as-is for a normal
|
|
51
|
+
* key, partially masked when this key is secured. Masking is length-based:
|
|
52
|
+
* - Length > 15: first 3 + "..." + last 3
|
|
53
|
+
* - Length 8-15: first 2 + "..."
|
|
54
|
+
* - Length < 8: "<secure key too short to log>"
|
|
55
|
+
*/
|
|
56
|
+
maskIfSecured(value) {
|
|
57
|
+
if (!this.isSecured) {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
const len = value.length;
|
|
61
|
+
if (len < 8) {
|
|
62
|
+
return '<secure key too short to log>';
|
|
63
|
+
}
|
|
64
|
+
else if (len <= 15) {
|
|
65
|
+
return `${value.substring(0, 2)}...`;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
return `${value.substring(0, 3)}...${value.substring(len - 3)}`;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
49
71
|
}
|
|
50
72
|
exports.ContextKey = ContextKey;
|
|
51
73
|
//# sourceMappingURL=ContextKey.js.map
|
package/src/ContextKey.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextKey.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/ContextKey.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,UAAU;IACnB,qEAAqE;IAC5D,IAAI,CAAS;IAEtB;;;OAGG;IACM,UAAU,CAAU;IAE7B,2CAA2C;IAClC,SAAS,CAAU;IAE5B,6EAA6E;IACpE,QAAQ,CAAU;IAE3B,YACI,IAAY,EACZ,UAAmB,EACnB,SAAS,GAAG,KAAK,EACjB,QAAQ,GAAG,IAAI;QAEf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;IAED,uEAAuE;IACvE,aAAa;QACT,OAAO,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC;IACzC,CAAC;CACJ;
|
|
1
|
+
{"version":3,"file":"ContextKey.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/ContextKey.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,UAAU;IACnB,qEAAqE;IAC5D,IAAI,CAAS;IAEtB;;;OAGG;IACM,UAAU,CAAU;IAE7B,2CAA2C;IAClC,SAAS,CAAU;IAE5B,6EAA6E;IACpE,QAAQ,CAAU;IAE3B,YACI,IAAY,EACZ,UAAmB,EACnB,SAAS,GAAG,KAAK,EACjB,QAAQ,GAAG,IAAI;QAEf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;IAED,uEAAuE;IACvE,aAAa;QACT,OAAO,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC;IACzC,CAAC;IAED;;;;;;OAMG;IACH,aAAa,CAAC,KAAa;QACvB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAClB,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;QACzB,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;AArDD,gCAqDC","sourcesContent":["/**\n * ContextKey - a single key that travels in the request's \"magic context\"\n * (RequestContext on the server, MutableContextStore in the browser).\n *\n * This ONE class replaces the old split of `Header` (interface) + `PlatformHeader`\n * (class) + `ContextKey` (class). Every context value — whether it rides over HTTP\n * (request-id, tenant, authorization) or stays in-process (method-meta, the\n * TestCaseRecorder) — is a `ContextKey`.\n *\n * The fields are named for what they DO (flipped from the old model):\n * - `name` ALWAYS set. The context storage key, the log/MDC key, and the\n * recorder name. e.g. 'requestId', 'tenantId', 'authorization'.\n * - `httpHeader` OPTIONAL. When set, this key is transferred over the wire under\n * this HTTP header name (inbound request -> context, and context ->\n * outbound request). e.g. 'x-request-id'. When UNSET, the key is\n * context-only and never leaves the process (method-meta, recorder).\n * - `isSecured` When true, the value is masked (partially) in logs.\n * - `isLogged` Defaults to true. When false, the value is NEVER logged (used for\n * object-valued/internal keys like the recorder or method-meta that\n * must not be serialized into log lines).\n *\n * Per CLAUDE.md: data-only structures are classes, not interfaces.\n */\nexport class ContextKey {\n /** Context storage key + log/MDC key + recorder name. Always set. */\n readonly name: string;\n\n /**\n * HTTP header name when this key is transferred over the wire (e.g.\n * 'x-request-id'). Undefined = context-only, never transferred.\n */\n readonly httpHeader?: string;\n\n /** Mask this value (partially) in logs. */\n readonly isSecured: boolean;\n\n /** Whether this key is logged at all. Default true; false = never logged. */\n readonly isLogged: boolean;\n\n constructor(\n name: string,\n httpHeader?: string,\n isSecured = false,\n isLogged = true,\n ) {\n this.name = name;\n this.httpHeader = httpHeader;\n this.isSecured = isSecured;\n this.isLogged = isLogged;\n }\n\n /** True when this key is transferred over HTTP (has an httpHeader). */\n isTransferred(): boolean {\n return this.httpHeader !== undefined;\n }\n\n /**\n * The value as it should appear in a log line: returned as-is for a normal\n * key, partially masked when this key is secured. Masking is length-based:\n * - Length > 15: first 3 + \"...\" + last 3\n * - Length 8-15: first 2 + \"...\"\n * - Length < 8: \"<secure key too short to log>\"\n */\n maskIfSecured(value: string): string {\n if (!this.isSecured) {\n return value;\n }\n const len = value.length;\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,27 +1,21 @@
|
|
|
1
1
|
import { ContextKey } from '../ContextKey';
|
|
2
2
|
import { ContextReader } from './ContextReader';
|
|
3
3
|
/**
|
|
4
|
-
* HeaderMethods - stateless utility for
|
|
5
|
-
*
|
|
4
|
+
* HeaderMethods - stateless utility for building the masked LOG map from context
|
|
5
|
+
* keys + a ContextReader.
|
|
6
|
+
*
|
|
7
|
+
* This is the BROWSER-safe path: ContextMgr.buildHeadersForLogging (→ ProxyClient)
|
|
8
|
+
* uses it with the browser's MutableContextStore, where there is no RequestContext.
|
|
9
|
+
* The Node loggers (bunyan/winston) do NOT use this — they read RequestContext
|
|
10
|
+
* directly and mask via {@link ContextKey.maskIfSecured} inline.
|
|
6
11
|
*
|
|
7
12
|
* Works in both server (Node) and browser environments; a plain `new HeaderMethods()`
|
|
8
13
|
* (no DI). The set of keys is supplied by the caller (from HeaderRegistry).
|
|
9
14
|
*/
|
|
10
15
|
export declare class HeaderMethods {
|
|
11
|
-
/** Keys that transfer over the wire (httpHeader set). */
|
|
12
|
-
findTransferKeys(keys: ContextKey[]): ContextKey[];
|
|
13
|
-
/** Keys whose values are masked in logs (isSecured=true). */
|
|
14
|
-
securedKeys(keys: ContextKey[]): ContextKey[];
|
|
15
16
|
/**
|
|
16
17
|
* Build the map for LOGGING from the given keys: each logged key (isLogged=true)
|
|
17
18
|
* with a value present is added under its `name`, masked when isSecured.
|
|
18
19
|
*/
|
|
19
20
|
buildSecureMapForLogs(keys: ContextKey[], contextReader: ContextReader): Map<string, string>;
|
|
20
|
-
/**
|
|
21
|
-
* Mask a secure value based on its length.
|
|
22
|
-
* - Length > 15: first 3 + "..." + last 3
|
|
23
|
-
* - Length 8-15: first 2 + "..."
|
|
24
|
-
* - Length < 8: "<secure key too short to log>"
|
|
25
|
-
*/
|
|
26
|
-
private maskSecureValue;
|
|
27
21
|
}
|
|
@@ -2,21 +2,18 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.HeaderMethods = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* HeaderMethods - stateless utility for
|
|
6
|
-
*
|
|
5
|
+
* HeaderMethods - stateless utility for building the masked LOG map from context
|
|
6
|
+
* keys + a ContextReader.
|
|
7
|
+
*
|
|
8
|
+
* This is the BROWSER-safe path: ContextMgr.buildHeadersForLogging (→ ProxyClient)
|
|
9
|
+
* uses it with the browser's MutableContextStore, where there is no RequestContext.
|
|
10
|
+
* The Node loggers (bunyan/winston) do NOT use this — they read RequestContext
|
|
11
|
+
* directly and mask via {@link ContextKey.maskIfSecured} inline.
|
|
7
12
|
*
|
|
8
13
|
* Works in both server (Node) and browser environments; a plain `new HeaderMethods()`
|
|
9
14
|
* (no DI). The set of keys is supplied by the caller (from HeaderRegistry).
|
|
10
15
|
*/
|
|
11
16
|
class HeaderMethods {
|
|
12
|
-
/** Keys that transfer over the wire (httpHeader set). */
|
|
13
|
-
findTransferKeys(keys) {
|
|
14
|
-
return keys.filter(k => k.httpHeader !== undefined);
|
|
15
|
-
}
|
|
16
|
-
/** Keys whose values are masked in logs (isSecured=true). */
|
|
17
|
-
securedKeys(keys) {
|
|
18
|
-
return keys.filter(k => k.isSecured);
|
|
19
|
-
}
|
|
20
17
|
/**
|
|
21
18
|
* Build the map for LOGGING from the given keys: each logged key (isLogged=true)
|
|
22
19
|
* with a value present is added under its `name`, masked when isSecured.
|
|
@@ -29,29 +26,11 @@ class HeaderMethods {
|
|
|
29
26
|
}
|
|
30
27
|
const value = contextReader.read(key);
|
|
31
28
|
if (value) {
|
|
32
|
-
logMap.set(key.name, key.
|
|
29
|
+
logMap.set(key.name, key.maskIfSecured(value));
|
|
33
30
|
}
|
|
34
31
|
}
|
|
35
32
|
return logMap;
|
|
36
33
|
}
|
|
37
|
-
/**
|
|
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>"
|
|
42
|
-
*/
|
|
43
|
-
maskSecureValue(value) {
|
|
44
|
-
const len = value.length;
|
|
45
|
-
if (len < 8) {
|
|
46
|
-
return '<secure key too short to log>';
|
|
47
|
-
}
|
|
48
|
-
else if (len <= 15) {
|
|
49
|
-
return `${value.substring(0, 2)}...`;
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
return `${value.substring(0, 3)}...${value.substring(len - 3)}`;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
34
|
}
|
|
56
35
|
exports.HeaderMethods = HeaderMethods;
|
|
57
36
|
//# sourceMappingURL=HeaderMethods.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeaderMethods.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/HeaderMethods.ts"],"names":[],"mappings":";;;AAGA
|
|
1
|
+
{"version":3,"file":"HeaderMethods.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/HeaderMethods.ts"],"names":[],"mappings":";;;AAGA;;;;;;;;;;;GAWG;AACH,MAAa,aAAa;IACtB;;;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,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;YACnD,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ;AApBD,sCAoBC","sourcesContent":["import { ContextKey } from '../ContextKey';\nimport { ContextReader } from './ContextReader';\n\n/**\n * HeaderMethods - stateless utility for building the masked LOG map from context\n * keys + a ContextReader.\n *\n * This is the BROWSER-safe path: ContextMgr.buildHeadersForLogging (→ ProxyClient)\n * uses it with the browser's MutableContextStore, where there is no RequestContext.\n * The Node loggers (bunyan/winston) do NOT use this — they read RequestContext\n * directly and mask via {@link ContextKey.maskIfSecured} inline.\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 /**\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.maskIfSecured(value));\n }\n }\n\n return logMap;\n }\n}\n"]}
|
|
@@ -28,6 +28,10 @@ export declare class HeaderRegistry {
|
|
|
28
28
|
static readonly DEFAULT_HEADERS: ContextKey[];
|
|
29
29
|
private static instance;
|
|
30
30
|
private readonly keys;
|
|
31
|
+
private readonly transferredKeys;
|
|
32
|
+
private readonly securedNames;
|
|
33
|
+
private readonly loggedKeys;
|
|
34
|
+
private readonly byHttpHeader;
|
|
31
35
|
private constructor();
|
|
32
36
|
/**
|
|
33
37
|
* Install the process-wide registry. Call once at startup, BEFORE
|
|
@@ -49,7 +53,7 @@ export declare class HeaderRegistry {
|
|
|
49
53
|
getSecuredNames(): string[];
|
|
50
54
|
/** Keys that appear in logs. isLogged=true. */
|
|
51
55
|
getLoggedKeys(): ContextKey[];
|
|
52
|
-
/** Look up a key by its HTTP header name (case-insensitive). */
|
|
56
|
+
/** Look up a key by its HTTP header name (case-insensitive). O(1) via the precomputed map. */
|
|
53
57
|
findByHttpHeader(httpHeader: string): ContextKey | undefined;
|
|
54
58
|
/**
|
|
55
59
|
* Collapse exact duplicates, throw on conflicting definitions sharing a `name`
|
|
@@ -31,8 +31,23 @@ class HeaderRegistry {
|
|
|
31
31
|
static DEFAULT_HEADERS = WebpiecesCoreHeaders_1.WebpiecesCoreHeaders.getAllHeaders();
|
|
32
32
|
static instance;
|
|
33
33
|
keys;
|
|
34
|
+
// Derived collections precomputed ONCE, here in the constructor (i.e. at
|
|
35
|
+
// configure() time). The hot path — every log line calls getLoggedKeys(), every
|
|
36
|
+
// outbound request calls getTransferredKeys() — then returns the cached array
|
|
37
|
+
// instead of re-filtering the full key list on each call. These are reachable
|
|
38
|
+
// only through HeaderRegistry.get(), which throws until configure() has run.
|
|
39
|
+
transferredKeys;
|
|
40
|
+
securedNames;
|
|
41
|
+
loggedKeys;
|
|
42
|
+
byHttpHeader;
|
|
34
43
|
constructor(keys) {
|
|
35
44
|
this.keys = this.checkForDuplicates(keys);
|
|
45
|
+
this.transferredKeys = this.keys.filter((k) => k.httpHeader !== undefined);
|
|
46
|
+
this.securedNames = this.keys
|
|
47
|
+
.filter((k) => k.isSecured)
|
|
48
|
+
.map((k) => k.name);
|
|
49
|
+
this.loggedKeys = this.keys.filter((k) => k.isLogged);
|
|
50
|
+
this.byHttpHeader = new Map(this.transferredKeys.map((k) => [k.httpHeader.toLowerCase(), k]));
|
|
36
51
|
}
|
|
37
52
|
/**
|
|
38
53
|
* Install the process-wide registry. Call once at startup, BEFORE
|
|
@@ -67,22 +82,19 @@ class HeaderRegistry {
|
|
|
67
82
|
* outbound request): those with an httpHeader set.
|
|
68
83
|
*/
|
|
69
84
|
getTransferredKeys() {
|
|
70
|
-
return this.
|
|
85
|
+
return this.transferredKeys;
|
|
71
86
|
}
|
|
72
87
|
/** Names (log keys) whose values must be masked in logs. isSecured=true. */
|
|
73
88
|
getSecuredNames() {
|
|
74
|
-
return this.
|
|
75
|
-
.filter((k) => k.isSecured)
|
|
76
|
-
.map((k) => k.name);
|
|
89
|
+
return this.securedNames;
|
|
77
90
|
}
|
|
78
91
|
/** Keys that appear in logs. isLogged=true. */
|
|
79
92
|
getLoggedKeys() {
|
|
80
|
-
return this.
|
|
93
|
+
return this.loggedKeys;
|
|
81
94
|
}
|
|
82
|
-
/** Look up a key by its HTTP header name (case-insensitive). */
|
|
95
|
+
/** Look up a key by its HTTP header name (case-insensitive). O(1) via the precomputed map. */
|
|
83
96
|
findByHttpHeader(httpHeader) {
|
|
84
|
-
|
|
85
|
-
return this.keys.find((k) => k.httpHeader?.toLowerCase() === lower);
|
|
97
|
+
return this.byHttpHeader.get(httpHeader.toLowerCase());
|
|
86
98
|
}
|
|
87
99
|
/**
|
|
88
100
|
* Collapse exact duplicates, throw on conflicting definitions sharing a `name`
|
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
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 // Derived collections precomputed ONCE, here in the constructor (i.e. at\n // configure() time). The hot path — every log line calls getLoggedKeys(), every\n // outbound request calls getTransferredKeys() — then returns the cached array\n // instead of re-filtering the full key list on each call. These are reachable\n // only through HeaderRegistry.get(), which throws until configure() has run.\n private readonly transferredKeys: ContextKey[];\n private readonly securedNames: string[];\n private readonly loggedKeys: ContextKey[];\n private readonly byHttpHeader: Map<string, ContextKey>;\n\n private constructor(keys: ContextKey[]) {\n this.keys = this.checkForDuplicates(keys);\n this.transferredKeys = this.keys.filter((k: ContextKey) => k.httpHeader !== undefined);\n this.securedNames = this.keys\n .filter((k: ContextKey) => k.isSecured)\n .map((k: ContextKey) => k.name);\n this.loggedKeys = this.keys.filter((k: ContextKey) => k.isLogged);\n this.byHttpHeader = new Map(\n this.transferredKeys.map((k: ContextKey): [string, ContextKey] => [k.httpHeader!.toLowerCase(), k]),\n );\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.transferredKeys;\n }\n\n /** Names (log keys) whose values must be masked in logs. isSecured=true. */\n getSecuredNames(): string[] {\n return this.securedNames;\n }\n\n /** Keys that appear in logs. isLogged=true. */\n getLoggedKeys(): ContextKey[] {\n return this.loggedKeys;\n }\n\n /** Look up a key by its HTTP header name (case-insensitive). O(1) via the precomputed map. */\n findByHttpHeader(httpHeader: string): ContextKey | undefined {\n return this.byHttpHeader.get(httpHeader.toLowerCase());\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"]}
|