@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.
Files changed (40) hide show
  1. package/package.json +1 -1
  2. package/src/ContextKey.d.ts +33 -15
  3. package/src/ContextKey.js +37 -15
  4. package/src/ContextKey.js.map +1 -1
  5. package/src/http/ContextMgr.d.ts +17 -34
  6. package/src/http/ContextMgr.js +21 -33
  7. package/src/http/ContextMgr.js.map +1 -1
  8. package/src/http/ContextReader.d.ts +14 -23
  9. package/src/http/ContextReader.js.map +1 -1
  10. package/src/http/HeaderMethods.d.ts +16 -60
  11. package/src/http/HeaderMethods.js +25 -92
  12. package/src/http/HeaderMethods.js.map +1 -1
  13. package/src/http/HeaderRegistry.d.ts +44 -37
  14. package/src/http/HeaderRegistry.js +88 -70
  15. package/src/http/HeaderRegistry.js.map +1 -1
  16. package/src/http/RequestIdChainProcessor.js +3 -2
  17. package/src/http/RequestIdChainProcessor.js.map +1 -1
  18. package/src/http/WebpiecesCoreHeaders.d.ts +29 -38
  19. package/src/http/WebpiecesCoreHeaders.js +28 -48
  20. package/src/http/WebpiecesCoreHeaders.js.map +1 -1
  21. package/src/http/recorder/TestCaseRecorder.js +1 -1
  22. package/src/http/recorder/TestCaseRecorder.js.map +1 -1
  23. package/src/index.d.ts +0 -4
  24. package/src/index.js +2 -8
  25. package/src/index.js.map +1 -1
  26. package/src/logging/LogManager.d.ts +5 -0
  27. package/src/logging/LogManager.js +13 -0
  28. package/src/logging/LogManager.js.map +1 -1
  29. package/src/Header.d.ts +0 -22
  30. package/src/Header.js +0 -3
  31. package/src/Header.js.map +0 -1
  32. package/src/http/HeaderTypes.d.ts +0 -29
  33. package/src/http/HeaderTypes.js +0 -33
  34. package/src/http/HeaderTypes.js.map +0 -1
  35. package/src/http/PlatformHeader.d.ts +0 -51
  36. package/src/http/PlatformHeader.js +0 -63
  37. package/src/http/PlatformHeader.js.map +0 -1
  38. package/src/http/PlatformHeadersExtension.d.ts +0 -51
  39. package/src/http/PlatformHeadersExtension.js +0 -59
  40. 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 - Utility class for working with platform headers.
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
- * This class can be injected in both server (Node.js) and client (Angular/browser) environments.
8
- * It provides common operations for filtering and processing headers.
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
- * Filter headers to only those that should be transferred (isWantTransferred=true).
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
- * Get all headers that should be logged.
48
- * All headers are loggable - secure headers will be masked by formatHeadersForLogging.
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
- * Format headers for logging with secure masking.
73
- * Takes filtered PlatformHeaders and actual header values from request.
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
- formatHeadersForLogging(loggableHeaders, headerMap) {
85
- const result = {};
86
- for (const platformHeader of loggableHeaders) {
87
- // Look for header in the map (case-insensitive)
88
- const values = headerMap.get(platformHeader.headerName.toLowerCase());
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 = values[0]; // Take first value
93
- if (platformHeader.isSecured) {
94
- result[platformHeader.headerName] = this.maskSecureValue(value);
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 result;
35
+ return logMap;
101
36
  }
102
37
  /**
103
- * Mask a secure header value based on its length.
104
- *
105
- * @param value - The secure header value to mask
106
- * @returns Masked value
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":";;;AAIA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,aAAa;IACtB;;;;;OAKG;IACH,mBAAmB,CAAC,OAAyB;QACzC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACpD,CAAC;IAED;;;;;OAKG;IACH,aAAa,CAAC,OAAyB;QACnC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;OAMG;IACH,mBAAmB,CAAC,OAAyB;QACzC,OAAO,OAAO,CAAC,CAAC,uDAAuD;IAC3E,CAAC;IAED,qBAAqB,CAAC,eAAiC,EAAE,aAA4B;QACjF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAe,CAAC;QAEvC,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACzC,IAAG,KAAK,EAAE,CAAC;gBACP,8EAA8E;gBAC9E,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,UAAU,CAAC;gBACxD,IAAG,CAAC,MAAM,CAAC,SAAS;oBAChB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;;oBAE3B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;YACzD,CAAC;QACL,CAAC;QAED,OAAO,OAAO,CAAC;IACnB,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,uBAAuB,CAAC,eAAiC,EAAE,SAAgC;QACvF,MAAM,MAAM,GAA2B,EAAE,CAAC;QAE1C,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;YAC3C,gDAAgD;YAChD,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;YACtE,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjC,SAAS;YACb,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB;YAE5C,IAAI,cAAc,CAAC,SAAS,EAAE,CAAC;gBAC3B,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;YACpE,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC;YAC9C,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,wCAAwC;YACxC,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;QACzC,CAAC;aAAM,CAAC;YACJ,iDAAiD;YACjD,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;AAxGD,sCAwGC","sourcesContent":["import { PlatformHeader } from './PlatformHeader';\n// Single source of truth for the ContextReader interface (was duplicated here)\nimport { ContextReader } from './ContextReader';\n\n/**\n * HeaderMethods - Utility class for working with platform headers.\n *\n * This class can be injected in both server (Node.js) and client (Angular/browser) environments.\n * It provides common operations for filtering and processing headers.\n *\n * Pattern: Stateless utility class (pure functions, can be instantiated or injected)\n * - Server: Can inject empty instance, use static-like methods\n * - Client: new HeaderMethods() (no DI needed)\n *\n * Usage:\n * ```typescript\n * // Server-side (ContextFilter)\n * constructor(@inject() headerMethods: HeaderMethods) {\n * const allHeaders = [... flatten from extensions ...];\n * this.transferHeaders = headerMethods.findTransferHeaders(allHeaders);\n * }\n *\n * // Client-side (ClientFactory)\n * const headerMethods = new HeaderMethods();\n * const loggableHeaders = headerMethods.findLoggableHeaders(allHeaders, requestHeaders);\n * ```\n */\nexport class HeaderMethods {\n /**\n * Filter headers to only those that should be transferred (isWantTransferred=true).\n *\n * @param headers - Array of PlatformHeader definitions\n * @returns Filtered array of headers with isWantTransferred=true\n */\n findTransferHeaders(headers: PlatformHeader[]): PlatformHeader[] {\n return headers.filter(h => h.isWantTransferred);\n }\n\n /**\n * Split headers into secure and public categories.\n *\n * @param headers - Array of PlatformHeader definitions\n * @returns SplitHeaders with secureHeaders (isSecured=true) and publicHeaders (isSecured=false)\n */\n secureHeaders(headers: PlatformHeader[]): PlatformHeader[] {\n return headers.filter(h => h.isSecured);\n }\n\n /**\n * Get all headers that should be logged.\n * All headers are loggable - secure headers will be masked by formatHeadersForLogging.\n *\n * @param headers - Array of PlatformHeader definitions\n * @returns All headers (they're all loggable, just some are masked)\n */\n findLoggableHeaders(headers: PlatformHeader[]): PlatformHeader[] {\n return headers; // All headers are loggable, secure ones will be masked\n }\n\n buildSecureMapForLogs(platformHeaders: PlatformHeader[], contextReader: ContextReader): Map<string, any> {\n const headers = new Map<string, any>();\n\n for (const header of platformHeaders) {\n const value = contextReader.read(header);\n if(value) {\n // MDC-style key when defined (Java getLoggerMDCKey), else the raw header name\n const logKey = header.loggerMdcKey ?? header.headerName;\n if(!header.isSecured)\n headers.set(logKey, value);\n else\n headers.set(logKey, this.maskSecureValue(value));\n }\n }\n\n return headers;\n }\n\n /**\n * Format headers for logging with secure masking.\n * Takes filtered PlatformHeaders and actual header values from request.\n *\n * Masking rules for secure headers (isSecured=true):\n * - Length > 15: Show first 3 and last 3 characters with \"...\" between\n * - Length 8-15: Show first 2 characters with \"...\"\n * - Length < 8: Show \"<secure key too short to log>\"\n *\n * @param loggableHeaders - Filtered PlatformHeaders to log\n * @param headerMap - Map of header name (lowercase) -> array of values from request\n * @returns Record of header name -> masked or full value for logging\n */\n formatHeadersForLogging(loggableHeaders: PlatformHeader[], headerMap: Map<string, string[]>): Record<string, string> {\n const result: Record<string, string> = {};\n\n for (const platformHeader of loggableHeaders) {\n // Look for header in the map (case-insensitive)\n const values = headerMap.get(platformHeader.headerName.toLowerCase());\n if (!values || values.length === 0) {\n continue;\n }\n\n const value = values[0]; // Take first value\n\n if (platformHeader.isSecured) {\n result[platformHeader.headerName] = this.maskSecureValue(value);\n } else {\n result[platformHeader.headerName] = value;\n }\n }\n\n return result;\n }\n\n /**\n * Mask a secure header value based on its length.\n *\n * @param value - The secure header value to mask\n * @returns Masked value\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 // 8-15 characters: show first 2 + \"...\"\n return `${value.substring(0, 2)}...`;\n } else {\n // > 15 characters: show first 3 + \"...\" + last 3\n return `${value.substring(0, 3)}...${value.substring(len - 3)}`;\n }\n }\n}\n\n\n"]}
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 { PlatformHeader } from './PlatformHeader';
2
- import { PlatformHeadersExtension } from './PlatformHeadersExtension';
1
+ import { ContextKey } from '../ContextKey';
3
2
  /**
4
- * HeaderRegistry - The single source of truth for all PlatformHeaders known to
5
- * the platform. Port of Java webpieces' HeaderTranslation.
3
+ * HeaderRegistry - the single, GLOBAL source of truth for every ContextKey the
4
+ * platform knows about. Port of Java webpieces' HeaderTranslation.
6
5
  *
7
- * Every consumer (server filters, logging, metrics, outbound HTTP clients)
8
- * reads the header set from this registry, so externally-defined headers are
9
- * honored everywhere ("infinitely scalable magic context").
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
- * Constructible in BOTH environments:
12
- * - Server (Inversify): WebpiecesModule binds it via
13
- * `toDynamicValue(ctx => new HeaderRegistry(ctx.getAll(HEADER_TYPES.PlatformHeadersExtension)))`
14
- * so every module's PlatformHeadersExtension is collected automatically.
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
- * Duplicate validation (port of Java checkForDuplicates) runs at construction,
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 headers with the same headerName must agree on ALL flags and loggerMdcKey.
20
- * - Two headers with the same loggerMdcKey must agree on headerName (and therefore flags).
21
- * - Exact duplicates (same name, same flags) collapse to one entry.
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
- private readonly headers;
25
- constructor(extensions: PlatformHeadersExtension[]);
26
- /**
27
- * All registered headers (deduplicated).
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
- * Headers that transfer over the wire (inbound request -> context, and
32
- * context -> outbound request). isWantTransferred=true.
33
+ * Install the process-wide registry. Call once at startup, BEFORE
34
+ * LogManager.setFactory(...) (logging masks/keys off this registry).
33
35
  */
34
- getTransferredHeaders(): PlatformHeader[];
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
- * Header names whose values must be masked in logs. isSecured=true.
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
- * Headers exposed as MDC/structured-log dimensions (loggerMdcKey set).
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 headers sharing a headerName must agree on every flag and the MDC key,
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 - The single source of truth for all PlatformHeaders known to
6
- * the platform. Port of Java webpieces' HeaderTranslation.
6
+ * HeaderRegistry - the single, GLOBAL source of truth for every ContextKey the
7
+ * platform knows about. Port of Java webpieces' HeaderTranslation.
7
8
  *
8
- * Every consumer (server filters, logging, metrics, outbound HTTP clients)
9
- * reads the header set from this registry, so externally-defined headers are
10
- * honored everywhere ("infinitely scalable magic context").
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
- * Constructible in BOTH environments:
13
- * - Server (Inversify): WebpiecesModule binds it via
14
- * `toDynamicValue(ctx => new HeaderRegistry(ctx.getAll(HEADER_TYPES.PlatformHeadersExtension)))`
15
- * so every module's PlatformHeadersExtension is collected automatically.
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
- * Duplicate validation (port of Java checkForDuplicates) runs at construction,
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 headers with the same headerName must agree on ALL flags and loggerMdcKey.
21
- * - Two headers with the same loggerMdcKey must agree on headerName (and therefore flags).
22
- * - Exact duplicates (same name, same flags) collapse to one entry.
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
- headers;
26
- constructor(extensions) {
27
- const allHeaders = [];
28
- for (const extension of extensions) {
29
- allHeaders.push(...extension.getHeaders());
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
- * All registered headers (deduplicated).
38
+ * Install the process-wide registry. Call once at startup, BEFORE
39
+ * LogManager.setFactory(...) (logging masks/keys off this registry).
35
40
  */
36
- getHeaders() {
37
- return this.headers;
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
- * Headers that transfer over the wire (inbound request -> context, and
41
- * context -> outbound request). isWantTransferred=true.
42
- */
43
- getTransferredHeaders() {
44
- return this.headers.filter((h) => h.isWantTransferred);
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
- * Header names whose values must be masked in logs. isSecured=true.
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.headers
51
- .filter((h) => h.isSecured)
52
- .map((h) => h.headerName);
74
+ return this.keys
75
+ .filter((k) => k.isSecured)
76
+ .map((k) => k.name);
53
77
  }
54
- /**
55
- * Headers exposed as MDC/structured-log dimensions (loggerMdcKey set).
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
- * Look up a header definition by its HTTP name (case-insensitive).
62
- */
63
- findByName(headerName) {
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
- * Port of Java HeaderTranslation.checkForDuplicates: collapse exact
69
- * duplicates, throw on conflicting definitions sharing a name or MDC key.
88
+ * Collapse exact duplicates, throw on conflicting definitions sharing a `name`
89
+ * or an `httpHeader`.
70
90
  */
71
- checkForDuplicates(allHeaders) {
91
+ checkForDuplicates(allKeys) {
72
92
  const byName = new Map();
73
- const byMdcKey = new Map();
74
- for (const header of allHeaders) {
75
- const nameKey = header.headerName.toLowerCase();
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, header);
98
+ this.assertSameDefinition(existing, key);
79
99
  continue; // exact duplicate - collapse
80
100
  }
81
- byName.set(nameKey, header);
82
- if (header.loggerMdcKey !== undefined) {
83
- const mdcClash = byMdcKey.get(header.loggerMdcKey);
84
- if (mdcClash) {
85
- throw new Error(`Duplicate PlatformHeader loggerMdcKey '${header.loggerMdcKey}': ` +
86
- `defined by header '${mdcClash.headerName}' AND header '${header.headerName}'. ` +
87
- `Each MDC key must map to exactly one header.`);
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
- byMdcKey.set(header.loggerMdcKey, header);
110
+ byHttpHeader.set(headerKey, key);
90
111
  }
91
112
  }
92
113
  return Array.from(byName.values());
93
114
  }
94
115
  /**
95
- * Two headers sharing a headerName must agree on every flag and the MDC key,
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.isWantTransferred !== duplicate.isWantTransferred) {
102
- conflicts.push(`isWantTransferred (${existing.isWantTransferred} vs ${duplicate.isWantTransferred})`);
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.isDimensionForMetrics !== duplicate.isDimensionForMetrics) {
108
- conflicts.push(`isDimensionForMetrics (${existing.isDimensionForMetrics} vs ${duplicate.isDimensionForMetrics})`);
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 PlatformHeader definitions for '${existing.headerName}': ` +
132
+ throw new Error(`Conflicting ContextKey definitions for '${existing.name}': ` +
115
133
  `two modules registered it with different ${conflicts.join(', ')}. ` +
116
- `Headers sharing a name must agree on all flags.`);
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":"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","sourcesContent":["import { PlatformHeader } from './PlatformHeader';\nimport { PlatformHeadersExtension } from './PlatformHeadersExtension';\n\n/**\n * HeaderRegistry - The single source of truth for all PlatformHeaders known to\n * the platform. Port of Java webpieces' HeaderTranslation.\n *\n * Every consumer (server filters, logging, metrics, outbound HTTP clients)\n * reads the header set from this registry, so externally-defined headers are\n * honored everywhere (\"infinitely scalable magic context\").\n *\n * Constructible in BOTH environments:\n * - Server (Inversify): WebpiecesModule binds it via\n * `toDynamicValue(ctx => new HeaderRegistry(ctx.getAll(HEADER_TYPES.PlatformHeadersExtension)))`\n * so every module's PlatformHeadersExtension is collected automatically.\n * - Browser (no DI): `new HeaderRegistry([new PlatformHeadersExtension([...])])`.\n *\n * Duplicate validation (port of Java checkForDuplicates) runs at construction,\n * so conflicting definitions fail fast at startup:\n * - Two headers with the same headerName must agree on ALL flags and loggerMdcKey.\n * - Two headers with the same loggerMdcKey must agree on headerName (and therefore flags).\n * - Exact duplicates (same name, same flags) collapse to one entry.\n */\nexport class HeaderRegistry {\n private readonly headers: PlatformHeader[];\n\n constructor(extensions: PlatformHeadersExtension[]) {\n const allHeaders: PlatformHeader[] = [];\n for (const extension of extensions) {\n allHeaders.push(...extension.getHeaders());\n }\n this.headers = this.checkForDuplicates(allHeaders);\n }\n\n /**\n * All registered headers (deduplicated).\n */\n getHeaders(): PlatformHeader[] {\n return this.headers;\n }\n\n /**\n * Headers that transfer over the wire (inbound request -> context, and\n * context -> outbound request). isWantTransferred=true.\n */\n getTransferredHeaders(): PlatformHeader[] {\n return this.headers.filter((h: PlatformHeader) => h.isWantTransferred);\n }\n\n /**\n * Header names whose values must be masked in logs. isSecured=true.\n */\n getSecuredNames(): string[] {\n return this.headers\n .filter((h: PlatformHeader) => h.isSecured)\n .map((h: PlatformHeader) => h.headerName);\n }\n\n /**\n * Headers exposed as MDC/structured-log dimensions (loggerMdcKey set).\n */\n getMdcHeaders(): PlatformHeader[] {\n return this.headers.filter((h: PlatformHeader) => h.loggerMdcKey !== undefined);\n }\n\n /**\n * Look up a header definition by its HTTP name (case-insensitive).\n */\n findByName(headerName: string): PlatformHeader | undefined {\n const lower = headerName.toLowerCase();\n return this.headers.find((h: PlatformHeader) => h.headerName.toLowerCase() === lower);\n }\n\n /**\n * Port of Java HeaderTranslation.checkForDuplicates: collapse exact\n * duplicates, throw on conflicting definitions sharing a name or MDC key.\n */\n private checkForDuplicates(allHeaders: PlatformHeader[]): PlatformHeader[] {\n const byName = new Map<string, PlatformHeader>();\n const byMdcKey = new Map<string, PlatformHeader>();\n\n for (const header of allHeaders) {\n const nameKey = header.headerName.toLowerCase();\n const existing = byName.get(nameKey);\n if (existing) {\n this.assertSameDefinition(existing, header);\n continue; // exact duplicate - collapse\n }\n byName.set(nameKey, header);\n\n if (header.loggerMdcKey !== undefined) {\n const mdcClash = byMdcKey.get(header.loggerMdcKey);\n if (mdcClash) {\n throw new Error(\n `Duplicate PlatformHeader loggerMdcKey '${header.loggerMdcKey}': ` +\n `defined by header '${mdcClash.headerName}' AND header '${header.headerName}'. ` +\n `Each MDC key must map to exactly one header.`,\n );\n }\n byMdcKey.set(header.loggerMdcKey, header);\n }\n }\n\n return Array.from(byName.values());\n }\n\n /**\n * Two headers sharing a headerName must agree on every flag and the MDC key,\n * otherwise the platform would behave differently depending on which module's\n * definition happened to load first.\n */\n private assertSameDefinition(existing: PlatformHeader, duplicate: PlatformHeader): void {\n const conflicts: string[] = [];\n if (existing.isWantTransferred !== duplicate.isWantTransferred) {\n conflicts.push(`isWantTransferred (${existing.isWantTransferred} vs ${duplicate.isWantTransferred})`);\n }\n if (existing.isSecured !== duplicate.isSecured) {\n conflicts.push(`isSecured (${existing.isSecured} vs ${duplicate.isSecured})`);\n }\n if (existing.isDimensionForMetrics !== duplicate.isDimensionForMetrics) {\n conflicts.push(`isDimensionForMetrics (${existing.isDimensionForMetrics} vs ${duplicate.isDimensionForMetrics})`);\n }\n if (existing.loggerMdcKey !== duplicate.loggerMdcKey) {\n conflicts.push(`loggerMdcKey ('${existing.loggerMdcKey}' vs '${duplicate.loggerMdcKey}')`);\n }\n if (conflicts.length > 0) {\n throw new Error(\n `Conflicting PlatformHeader definitions for '${existing.headerName}': ` +\n `two modules registered it with different ${conflicts.join(', ')}. ` +\n `Headers 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 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
- const requestIdName = WebpiecesCoreHeaders_1.WebpiecesCoreHeaders.REQUEST_ID.headerName;
25
- const previousIdName = WebpiecesCoreHeaders_1.WebpiecesCoreHeaders.PREVIOUS_REQUEST_ID.headerName;
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,UAAU,CAAC;QACjE,MAAM,cAAc,GAAG,2CAAoB,CAAC,mBAAmB,CAAC,UAAU,CAAC;QAE3E,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;AAhBD,0DAgBC","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 const requestIdName = WebpiecesCoreHeaders.REQUEST_ID.headerName;\n const previousIdName = WebpiecesCoreHeaders.PREVIOUS_REQUEST_ID.headerName;\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"]}
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"]}