@webpieces/core-util 0.3.286 → 0.3.288

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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/core-util",
3
- "version": "0.3.286",
3
+ "version": "0.3.288",
4
4
  "description": "Utility functions for WebPieces - works in browser and Node.js",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -1,21 +1,39 @@
1
- import { Header } from './Header';
2
1
  /**
3
- * ContextKey - Typed key for non-HTTP context values stored in RequestContext.
2
+ * ContextKey - a single key that travels in the request's "magic context"
3
+ * (RequestContext on the server, MutableContextStore in the browser).
4
4
  *
5
- * Similar to PlatformHeader but for context-only values that don't correspond
6
- * to HTTP headers (e.g., METHOD_META, REQUEST_PATH).
5
+ * This ONE class replaces the old split of `Header` (interface) + `PlatformHeader`
6
+ * (class) + `ContextKey` (class). Every context value — whether it rides over HTTP
7
+ * (request-id, tenant, authorization) or stays in-process (method-meta, the
8
+ * TestCaseRecorder) — is a `ContextKey`.
7
9
  *
8
- * Customers can define their own ContextKey instances for app-specific values.
10
+ * The fields are named for what they DO (flipped from the old model):
11
+ * - `name` ALWAYS set. The context storage key, the log/MDC key, and the
12
+ * recorder name. e.g. 'requestId', 'tenantId', 'authorization'.
13
+ * - `httpHeader` OPTIONAL. When set, this key is transferred over the wire under
14
+ * this HTTP header name (inbound request -> context, and context ->
15
+ * outbound request). e.g. 'x-request-id'. When UNSET, the key is
16
+ * context-only and never leaves the process (method-meta, recorder).
17
+ * - `isSecured` When true, the value is masked (partially) in logs.
18
+ * - `isLogged` Defaults to true. When false, the value is NEVER logged (used for
19
+ * object-valued/internal keys like the recorder or method-meta that
20
+ * must not be serialized into log lines).
9
21
  *
10
- * Usage:
11
- * ```typescript
12
- * const MY_KEY = new ContextKey('my-app-key');
13
- * RequestContext.putHeader(MY_KEY, someValue);
14
- * const value = RequestContext.getHeader(MY_KEY);
15
- * ```
22
+ * Per CLAUDE.md: data-only structures are classes, not interfaces.
16
23
  */
17
- export declare class ContextKey implements Header {
18
- private readonly keyName;
19
- constructor(keyName: string);
20
- getHeaderName(): string;
24
+ export declare class ContextKey {
25
+ /** Context storage key + log/MDC key + recorder name. Always set. */
26
+ readonly name: string;
27
+ /**
28
+ * HTTP header name when this key is transferred over the wire (e.g.
29
+ * 'x-request-id'). Undefined = context-only, never transferred.
30
+ */
31
+ readonly httpHeader?: string;
32
+ /** Mask this value (partially) in logs. */
33
+ readonly isSecured: boolean;
34
+ /** Whether this key is logged at all. Default true; false = never logged. */
35
+ readonly isLogged: boolean;
36
+ constructor(name: string, httpHeader?: string, isSecured?: boolean, isLogged?: boolean);
37
+ /** True when this key is transferred over HTTP (has an httpHeader). */
38
+ isTransferred(): boolean;
21
39
  }
package/src/ContextKey.js CHANGED
@@ -2,27 +2,49 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ContextKey = void 0;
4
4
  /**
5
- * ContextKey - Typed key for non-HTTP context values stored in RequestContext.
5
+ * ContextKey - a single key that travels in the request's "magic context"
6
+ * (RequestContext on the server, MutableContextStore in the browser).
6
7
  *
7
- * Similar to PlatformHeader but for context-only values that don't correspond
8
- * to HTTP headers (e.g., METHOD_META, REQUEST_PATH).
8
+ * This ONE class replaces the old split of `Header` (interface) + `PlatformHeader`
9
+ * (class) + `ContextKey` (class). Every context value — whether it rides over HTTP
10
+ * (request-id, tenant, authorization) or stays in-process (method-meta, the
11
+ * TestCaseRecorder) — is a `ContextKey`.
9
12
  *
10
- * Customers can define their own ContextKey instances for app-specific values.
13
+ * The fields are named for what they DO (flipped from the old model):
14
+ * - `name` ALWAYS set. The context storage key, the log/MDC key, and the
15
+ * recorder name. e.g. 'requestId', 'tenantId', 'authorization'.
16
+ * - `httpHeader` OPTIONAL. When set, this key is transferred over the wire under
17
+ * this HTTP header name (inbound request -> context, and context ->
18
+ * outbound request). e.g. 'x-request-id'. When UNSET, the key is
19
+ * context-only and never leaves the process (method-meta, recorder).
20
+ * - `isSecured` When true, the value is masked (partially) in logs.
21
+ * - `isLogged` Defaults to true. When false, the value is NEVER logged (used for
22
+ * object-valued/internal keys like the recorder or method-meta that
23
+ * must not be serialized into log lines).
11
24
  *
12
- * Usage:
13
- * ```typescript
14
- * const MY_KEY = new ContextKey('my-app-key');
15
- * RequestContext.putHeader(MY_KEY, someValue);
16
- * const value = RequestContext.getHeader(MY_KEY);
17
- * ```
25
+ * Per CLAUDE.md: data-only structures are classes, not interfaces.
18
26
  */
19
27
  class ContextKey {
20
- keyName;
21
- constructor(keyName) {
22
- this.keyName = keyName;
28
+ /** Context storage key + log/MDC key + recorder name. Always set. */
29
+ name;
30
+ /**
31
+ * HTTP header name when this key is transferred over the wire (e.g.
32
+ * 'x-request-id'). Undefined = context-only, never transferred.
33
+ */
34
+ httpHeader;
35
+ /** Mask this value (partially) in logs. */
36
+ isSecured;
37
+ /** Whether this key is logged at all. Default true; false = never logged. */
38
+ isLogged;
39
+ constructor(name, httpHeader, isSecured = false, isLogged = true) {
40
+ this.name = name;
41
+ this.httpHeader = httpHeader;
42
+ this.isSecured = isSecured;
43
+ this.isLogged = isLogged;
23
44
  }
24
- getHeaderName() {
25
- return this.keyName;
45
+ /** True when this key is transferred over HTTP (has an httpHeader). */
46
+ isTransferred() {
47
+ return this.httpHeader !== undefined;
26
48
  }
27
49
  }
28
50
  exports.ContextKey = ContextKey;
@@ -1 +1 @@
1
- {"version":3,"file":"ContextKey.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/ContextKey.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAa,UAAU;IACU;IAA7B,YAA6B,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;IAAG,CAAC;IAEhD,aAAa;QACT,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;CACJ;AAND,gCAMC","sourcesContent":["import { Header } from './Header';\n\n/**\n * ContextKey - Typed key for non-HTTP context values stored in RequestContext.\n *\n * Similar to PlatformHeader but for context-only values that don't correspond\n * to HTTP headers (e.g., METHOD_META, REQUEST_PATH).\n *\n * Customers can define their own ContextKey instances for app-specific values.\n *\n * Usage:\n * ```typescript\n * const MY_KEY = new ContextKey('my-app-key');\n * RequestContext.putHeader(MY_KEY, someValue);\n * const value = RequestContext.getHeader(MY_KEY);\n * ```\n */\nexport class ContextKey implements Header {\n constructor(private readonly keyName: string) {}\n\n getHeaderName(): string {\n return this.keyName;\n }\n}\n"]}
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;AAhCD,gCAgCC","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"]}
@@ -1,31 +1,24 @@
1
1
  import { ContextReader } from './ContextReader';
2
2
  import { HeaderMethods } from './HeaderMethods';
3
- import { HeaderRegistry } from './HeaderRegistry';
4
3
  /**
5
- * ContextMgr - Manages context reader + header registry for HTTP clients.
4
+ * ContextMgr - propagates the magic context onto outbound HTTP requests.
6
5
  *
7
- * Passed to createApiClient() via ClientConfig.contextMgr to enable automatic
8
- * header propagation: every header in the registry with isWantTransferred=true
9
- * is read from the ContextReader and added to outbound requests.
6
+ * Passed to createApiClient() via ClientConfig.contextMgr: every transferred key
7
+ * (httpHeader set) in the GLOBAL {@link HeaderRegistry} is read from the ContextReader
8
+ * and added to outbound requests. The registry is a process global (configured once at
9
+ * startup, like LogManager), so ContextMgr no longer takes a registry argument.
10
10
  *
11
- * Browser-safe (no AsyncLocalStorage): the server-side reader
12
- * (RequestContextReader, in @webpieces/core-context) and the browser store
13
- * (MutableContextStore, in @webpieces/http-client) both implement ContextReader,
14
- * so ContextMgr itself lives here in browser+node core-util.
15
- *
16
- * BREAKING (migration from the PlatformHeader[] constructor):
17
- * new ContextMgr(reader, headerArray)
18
- * becomes
19
- * new ContextMgr(reader, new HeaderRegistry([new PlatformHeadersExtension(headerArray)]))
11
+ * Browser-safe (no AsyncLocalStorage): the server-side reader (RequestContextReader,
12
+ * in @webpieces/core-context) and the browser store (MutableContextStore, in
13
+ * @webpieces/http-client) both implement ContextReader.
20
14
  *
21
15
  * Example usage:
22
16
  * ```typescript
23
17
  * // Node.js server-side (reads the magic context from RequestContext):
24
- * const contextMgr = new ContextMgr(new RequestContextReader(), registry);
18
+ * const contextMgr = new ContextMgr(new RequestContextReader());
25
19
  *
26
20
  * // Browser client-side (app-managed store, no AsyncLocalStorage):
27
- * const store = new MutableContextStore();
28
- * const contextMgr = new ContextMgr(store, registry);
21
+ * const contextMgr = new ContextMgr(new MutableContextStore());
29
22
  *
30
23
  * // Both cases:
31
24
  * const config = new ClientConfig('http://api.example.com', contextMgr);
@@ -34,15 +27,10 @@ import { HeaderRegistry } from './HeaderRegistry';
34
27
  */
35
28
  export declare class ContextMgr {
36
29
  /**
37
- * The context reader that provides header values.
30
+ * The context reader that provides context-key values.
38
31
  * Different implementations for Node.js vs browser.
39
32
  */
40
33
  readonly contextReader: ContextReader;
41
- /**
42
- * The single source of truth for which headers exist and how they behave
43
- * (transferred/secured/MDC). Shared with the server-side filters.
44
- */
45
- readonly registry: HeaderRegistry;
46
34
  /**
47
35
  * When true (default), outbound calls send the current x-request-id as
48
36
  * x-previous-request-id (and drop x-request-id) so each hop in a
@@ -52,15 +40,10 @@ export declare class ContextMgr {
52
40
  private chainProcessor;
53
41
  constructor(
54
42
  /**
55
- * The context reader that provides header values.
43
+ * The context reader that provides context-key values.
56
44
  * Different implementations for Node.js vs browser.
57
45
  */
58
46
  contextReader: ContextReader,
59
- /**
60
- * The single source of truth for which headers exist and how they behave
61
- * (transferred/secured/MDC). Shared with the server-side filters.
62
- */
63
- registry: HeaderRegistry,
64
47
  /**
65
48
  * When true (default), outbound calls send the current x-request-id as
66
49
  * x-previous-request-id (and drop x-request-id) so each hop in a
@@ -68,16 +51,16 @@ export declare class ContextMgr {
68
51
  */
69
52
  chainRequestIds?: boolean);
70
53
  /**
71
- * Build the headers to send on an outbound request: every transferred
72
- * header (isWantTransferred=true) with a non-empty value in the context,
73
- * then request-id chaining applied (unless opted out).
54
+ * Build the headers to send on an outbound request: every transferred key
55
+ * (httpHeader set) with a non-empty value in the context, emitted under its
56
+ * `httpHeader` wire name, then request-id chaining applied (unless opted out).
74
57
  *
75
58
  * Values are RAW (unmasked) - this map goes on the wire, not in logs.
76
59
  */
77
60
  buildOutboundHeaders(): Map<string, string>;
78
61
  /**
79
- * Build the header map for LOGGING: secured header values are masked,
80
- * and headers are keyed by loggerMdcKey when defined.
62
+ * Build the header map for LOGGING: secured values masked, keyed by each key's
63
+ * `name`, only for keys with isLogged=true.
81
64
  */
82
65
  buildHeadersForLogging(headerMethods: HeaderMethods): Map<string, string>;
83
66
  }
@@ -1,32 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ContextMgr = void 0;
4
+ const HeaderRegistry_1 = require("./HeaderRegistry");
4
5
  const RequestIdChainProcessor_1 = require("./RequestIdChainProcessor");
5
6
  /**
6
- * ContextMgr - Manages context reader + header registry for HTTP clients.
7
+ * ContextMgr - propagates the magic context onto outbound HTTP requests.
7
8
  *
8
- * Passed to createApiClient() via ClientConfig.contextMgr to enable automatic
9
- * header propagation: every header in the registry with isWantTransferred=true
10
- * is read from the ContextReader and added to outbound requests.
9
+ * Passed to createApiClient() via ClientConfig.contextMgr: every transferred key
10
+ * (httpHeader set) in the GLOBAL {@link HeaderRegistry} is read from the ContextReader
11
+ * and added to outbound requests. The registry is a process global (configured once at
12
+ * startup, like LogManager), so ContextMgr no longer takes a registry argument.
11
13
  *
12
- * Browser-safe (no AsyncLocalStorage): the server-side reader
13
- * (RequestContextReader, in @webpieces/core-context) and the browser store
14
- * (MutableContextStore, in @webpieces/http-client) both implement ContextReader,
15
- * so ContextMgr itself lives here in browser+node core-util.
16
- *
17
- * BREAKING (migration from the PlatformHeader[] constructor):
18
- * new ContextMgr(reader, headerArray)
19
- * becomes
20
- * new ContextMgr(reader, new HeaderRegistry([new PlatformHeadersExtension(headerArray)]))
14
+ * Browser-safe (no AsyncLocalStorage): the server-side reader (RequestContextReader,
15
+ * in @webpieces/core-context) and the browser store (MutableContextStore, in
16
+ * @webpieces/http-client) both implement ContextReader.
21
17
  *
22
18
  * Example usage:
23
19
  * ```typescript
24
20
  * // Node.js server-side (reads the magic context from RequestContext):
25
- * const contextMgr = new ContextMgr(new RequestContextReader(), registry);
21
+ * const contextMgr = new ContextMgr(new RequestContextReader());
26
22
  *
27
23
  * // Browser client-side (app-managed store, no AsyncLocalStorage):
28
- * const store = new MutableContextStore();
29
- * const contextMgr = new ContextMgr(store, registry);
24
+ * const contextMgr = new ContextMgr(new MutableContextStore());
30
25
  *
31
26
  * // Both cases:
32
27
  * const config = new ClientConfig('http://api.example.com', contextMgr);
@@ -35,20 +30,14 @@ const RequestIdChainProcessor_1 = require("./RequestIdChainProcessor");
35
30
  */
36
31
  class ContextMgr {
37
32
  contextReader;
38
- registry;
39
33
  chainRequestIds;
40
34
  chainProcessor;
41
35
  constructor(
42
36
  /**
43
- * The context reader that provides header values.
37
+ * The context reader that provides context-key values.
44
38
  * Different implementations for Node.js vs browser.
45
39
  */
46
40
  contextReader,
47
- /**
48
- * The single source of truth for which headers exist and how they behave
49
- * (transferred/secured/MDC). Shared with the server-side filters.
50
- */
51
- registry,
52
41
  /**
53
42
  * When true (default), outbound calls send the current x-request-id as
54
43
  * x-previous-request-id (and drop x-request-id) so each hop in a
@@ -56,23 +45,22 @@ class ContextMgr {
56
45
  */
57
46
  chainRequestIds = true) {
58
47
  this.contextReader = contextReader;
59
- this.registry = registry;
60
48
  this.chainRequestIds = chainRequestIds;
61
49
  this.chainProcessor = new RequestIdChainProcessor_1.RequestIdChainProcessor();
62
50
  }
63
51
  /**
64
- * Build the headers to send on an outbound request: every transferred
65
- * header (isWantTransferred=true) with a non-empty value in the context,
66
- * then request-id chaining applied (unless opted out).
52
+ * Build the headers to send on an outbound request: every transferred key
53
+ * (httpHeader set) with a non-empty value in the context, emitted under its
54
+ * `httpHeader` wire name, then request-id chaining applied (unless opted out).
67
55
  *
68
56
  * Values are RAW (unmasked) - this map goes on the wire, not in logs.
69
57
  */
70
58
  buildOutboundHeaders() {
71
59
  const outbound = new Map();
72
- for (const header of this.registry.getTransferredHeaders()) {
73
- const value = this.contextReader.read(header);
60
+ for (const key of HeaderRegistry_1.HeaderRegistry.get().getTransferredKeys()) {
61
+ const value = this.contextReader.read(key);
74
62
  if (value !== undefined && value !== null && value !== '') {
75
- outbound.set(header.headerName, value);
63
+ outbound.set(key.httpHeader, value);
76
64
  }
77
65
  }
78
66
  if (this.chainRequestIds) {
@@ -81,11 +69,11 @@ class ContextMgr {
81
69
  return outbound;
82
70
  }
83
71
  /**
84
- * Build the header map for LOGGING: secured header values are masked,
85
- * and headers are keyed by loggerMdcKey when defined.
72
+ * Build the header map for LOGGING: secured values masked, keyed by each key's
73
+ * `name`, only for keys with isLogged=true.
86
74
  */
87
75
  buildHeadersForLogging(headerMethods) {
88
- return headerMethods.buildSecureMapForLogs(this.registry.getHeaders(), this.contextReader);
76
+ return headerMethods.buildSecureMapForLogs(HeaderRegistry_1.HeaderRegistry.get().getLoggedKeys(), this.contextReader);
89
77
  }
90
78
  }
91
79
  exports.ContextMgr = ContextMgr;
@@ -1 +1 @@
1
- {"version":3,"file":"ContextMgr.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ContextMgr.ts"],"names":[],"mappings":";;;AAGA,uEAAoE;AAEpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAa,UAAU;IAQC;IAMA;IAOA;IApBZ,cAAc,CAA0B;IAEhD;IACI;;;OAGG;IACa,aAA4B;IAE5C;;;OAGG;IACa,QAAwB;IAExC;;;;OAIG;IACa,kBAA2B,IAAI;QAb/B,kBAAa,GAAb,aAAa,CAAe;QAM5B,aAAQ,GAAR,QAAQ,CAAgB;QAOxB,oBAAe,GAAf,eAAe,CAAgB;QAE/C,IAAI,CAAC,cAAc,GAAG,IAAI,iDAAuB,EAAE,CAAC;IACxD,CAAC;IAED;;;;;;OAMG;IACH,oBAAoB;QAChB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;QAE3C,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,qBAAqB,EAAE,EAAE,CAAC;YACzD,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC9C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;gBACxD,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;YAC3C,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QAED,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED;;;OAGG;IACH,sBAAsB,CAAC,aAA4B;QAC/C,OAAO,aAAa,CAAC,qBAAqB,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAC/F,CAAC;CACJ;AAzDD,gCAyDC","sourcesContent":["import { ContextReader } from './ContextReader';\nimport { HeaderMethods } from './HeaderMethods';\nimport { HeaderRegistry } from './HeaderRegistry';\nimport { RequestIdChainProcessor } from './RequestIdChainProcessor';\n\n/**\n * ContextMgr - Manages context reader + header registry for HTTP clients.\n *\n * Passed to createApiClient() via ClientConfig.contextMgr to enable automatic\n * header propagation: every header in the registry with isWantTransferred=true\n * is read from the ContextReader and added to outbound requests.\n *\n * Browser-safe (no AsyncLocalStorage): the server-side reader\n * (RequestContextReader, in @webpieces/core-context) and the browser store\n * (MutableContextStore, in @webpieces/http-client) both implement ContextReader,\n * so ContextMgr itself lives here in browser+node core-util.\n *\n * BREAKING (migration from the PlatformHeader[] constructor):\n * new ContextMgr(reader, headerArray)\n * becomes\n * new ContextMgr(reader, new HeaderRegistry([new PlatformHeadersExtension(headerArray)]))\n *\n * Example usage:\n * ```typescript\n * // Node.js server-side (reads the magic context from RequestContext):\n * const contextMgr = new ContextMgr(new RequestContextReader(), registry);\n *\n * // Browser client-side (app-managed store, no AsyncLocalStorage):\n * const store = new MutableContextStore();\n * const contextMgr = new ContextMgr(store, registry);\n *\n * // Both cases:\n * const config = new ClientConfig('http://api.example.com', contextMgr);\n * const client = createApiClient(SaveApi, config);\n * ```\n */\nexport class ContextMgr {\n private chainProcessor: RequestIdChainProcessor;\n\n constructor(\n /**\n * The context reader that provides header values.\n * Different implementations for Node.js vs browser.\n */\n public readonly contextReader: ContextReader,\n\n /**\n * The single source of truth for which headers exist and how they behave\n * (transferred/secured/MDC). Shared with the server-side filters.\n */\n public readonly registry: HeaderRegistry,\n\n /**\n * When true (default), outbound calls send the current x-request-id as\n * x-previous-request-id (and drop x-request-id) so each hop in a\n * distributed trace gets its own id chained to its caller's.\n */\n public readonly chainRequestIds: boolean = true,\n ) {\n this.chainProcessor = new RequestIdChainProcessor();\n }\n\n /**\n * Build the headers to send on an outbound request: every transferred\n * header (isWantTransferred=true) with a non-empty value in the context,\n * then request-id chaining applied (unless opted out).\n *\n * Values are RAW (unmasked) - this map goes on the wire, not in logs.\n */\n buildOutboundHeaders(): Map<string, string> {\n const outbound = new Map<string, string>();\n\n for (const header of this.registry.getTransferredHeaders()) {\n const value = this.contextReader.read(header);\n if (value !== undefined && value !== null && value !== '') {\n outbound.set(header.headerName, value);\n }\n }\n\n if (this.chainRequestIds) {\n this.chainProcessor.process(outbound);\n }\n\n return outbound;\n }\n\n /**\n * Build the header map for LOGGING: secured header values are masked,\n * and headers are keyed by loggerMdcKey when defined.\n */\n buildHeadersForLogging(headerMethods: HeaderMethods): Map<string, string> {\n return headerMethods.buildSecureMapForLogs(this.registry.getHeaders(), this.contextReader);\n }\n}\n"]}
1
+ {"version":3,"file":"ContextMgr.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ContextMgr.ts"],"names":[],"mappings":";;;AAEA,qDAAkD;AAClD,uEAAoE;AAEpE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAa,UAAU;IAQC;IAOA;IAdZ,cAAc,CAA0B;IAEhD;IACI;;;OAGG;IACa,aAA4B;IAE5C;;;;OAIG;IACa,kBAA2B,IAAI;QAP/B,kBAAa,GAAb,aAAa,CAAe;QAO5B,oBAAe,GAAf,eAAe,CAAgB;QAE/C,IAAI,CAAC,cAAc,GAAG,IAAI,iDAAuB,EAAE,CAAC;IACxD,CAAC;IAED;;;;;;OAMG;IACH,oBAAoB;QAChB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;QAE3C,KAAK,MAAM,GAAG,IAAI,+BAAc,CAAC,GAAG,EAAE,CAAC,kBAAkB,EAAE,EAAE,CAAC;YAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;gBACxD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,UAAW,EAAE,KAAK,CAAC,CAAC;YACzC,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QAED,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED;;;OAGG;IACH,sBAAsB,CAAC,aAA4B;QAC/C,OAAO,aAAa,CAAC,qBAAqB,CAAC,+BAAc,CAAC,GAAG,EAAE,CAAC,aAAa,EAAE,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IACzG,CAAC;CACJ;AAnDD,gCAmDC","sourcesContent":["import { ContextReader } from './ContextReader';\nimport { HeaderMethods } from './HeaderMethods';\nimport { HeaderRegistry } from './HeaderRegistry';\nimport { RequestIdChainProcessor } from './RequestIdChainProcessor';\n\n/**\n * ContextMgr - propagates the magic context onto outbound HTTP requests.\n *\n * Passed to createApiClient() via ClientConfig.contextMgr: every transferred key\n * (httpHeader set) in the GLOBAL {@link HeaderRegistry} is read from the ContextReader\n * and added to outbound requests. The registry is a process global (configured once at\n * startup, like LogManager), so ContextMgr no longer takes a registry argument.\n *\n * Browser-safe (no AsyncLocalStorage): the server-side reader (RequestContextReader,\n * in @webpieces/core-context) and the browser store (MutableContextStore, in\n * @webpieces/http-client) both implement ContextReader.\n *\n * Example usage:\n * ```typescript\n * // Node.js server-side (reads the magic context from RequestContext):\n * const contextMgr = new ContextMgr(new RequestContextReader());\n *\n * // Browser client-side (app-managed store, no AsyncLocalStorage):\n * const contextMgr = new ContextMgr(new MutableContextStore());\n *\n * // Both cases:\n * const config = new ClientConfig('http://api.example.com', contextMgr);\n * const client = createApiClient(SaveApi, config);\n * ```\n */\nexport class ContextMgr {\n private chainProcessor: RequestIdChainProcessor;\n\n constructor(\n /**\n * The context reader that provides context-key values.\n * Different implementations for Node.js vs browser.\n */\n public readonly contextReader: ContextReader,\n\n /**\n * When true (default), outbound calls send the current x-request-id as\n * x-previous-request-id (and drop x-request-id) so each hop in a\n * distributed trace gets its own id chained to its caller's.\n */\n public readonly chainRequestIds: boolean = true,\n ) {\n this.chainProcessor = new RequestIdChainProcessor();\n }\n\n /**\n * Build the headers to send on an outbound request: every transferred key\n * (httpHeader set) with a non-empty value in the context, emitted under its\n * `httpHeader` wire name, then request-id chaining applied (unless opted out).\n *\n * Values are RAW (unmasked) - this map goes on the wire, not in logs.\n */\n buildOutboundHeaders(): Map<string, string> {\n const outbound = new Map<string, string>();\n\n for (const key of HeaderRegistry.get().getTransferredKeys()) {\n const value = this.contextReader.read(key);\n if (value !== undefined && value !== null && value !== '') {\n outbound.set(key.httpHeader!, value);\n }\n }\n\n if (this.chainRequestIds) {\n this.chainProcessor.process(outbound);\n }\n\n return outbound;\n }\n\n /**\n * Build the header map for LOGGING: secured values masked, keyed by each key's\n * `name`, only for keys with isLogged=true.\n */\n buildHeadersForLogging(headerMethods: HeaderMethods): Map<string, string> {\n return headerMethods.buildSecureMapForLogs(HeaderRegistry.get().getLoggedKeys(), this.contextReader);\n }\n}\n"]}
@@ -1,37 +1,28 @@
1
1
  import { ContextKey } from '../ContextKey';
2
- import { PlatformHeader } from './PlatformHeader';
3
2
  /**
4
- * ContextReader - Interface for reading header values from context.
3
+ * ContextReader - reads context-key values from the ambient magic context.
5
4
  *
6
- * Different implementations for different environments:
7
- * - RequestContextReader: Node.js with AsyncLocalStorage (in @webpieces/http-routing, server-side only)
8
- * - MutableContextStore / StaticContextReader: Browser or testing with manual header
9
- * management (in @webpieces/http-client)
10
- * - CompositeContextReader: Combines multiple readers with priority (in @webpieces/http-client)
5
+ * There are exactly TWO implementations, one per environment:
6
+ * - Node/server: `RequestContextReader` (in @webpieces/core-context) reads the
7
+ * AsyncLocalStorage-backed RequestContext.
8
+ * - Browser: `MutableContextStore` (in @webpieces/http-client) — a mutable in-memory
9
+ * store the app sets as values become known (login token, tenant, ...).
11
10
  *
12
- * This interface is defined in @webpieces/http-api so both http-routing and http-client
13
- * can use it without creating circular dependencies. It is DI-independent so it works in
14
- * both server (Inversify) and client (Angular/React, browser) environments.
11
+ * Defined in core-util (browser + Node safe, DI-independent) so both sides can use it
12
+ * without a circular dependency.
15
13
  *
16
14
  * This is a business-logic interface (per CLAUDE.md: behavior = interface).
17
15
  */
18
16
  export interface ContextReader {
19
17
  /**
20
- * Read the value of a platform header.
21
- * Returns undefined if header not available.
22
- *
23
- * @param header - The platform header to read
24
- * @returns The header value, or undefined if not present
18
+ * Read the string value of a context key. Returns undefined if not present.
25
19
  */
26
- read(header: PlatformHeader): string | undefined;
20
+ read(key: ContextKey): string | undefined;
27
21
  /**
28
- * OPTIONAL: read a non-header context value (e.g. the active
29
- * TestCaseRecorder under RecorderKeys.RECORDER).
30
- *
31
- * Server-side readers (RequestContextReader) implement this over the
32
- * RequestContext; browser readers may omit it (no server-side recording
33
- * in browsers - same as Java). This keeps http-client free of any
34
- * Node-only imports while still letting it find the recorder.
22
+ * OPTIONAL: read a non-string context value (e.g. the active TestCaseRecorder
23
+ * under RecorderKeys.RECORDER). Server-side readers implement this over the
24
+ * RequestContext; browser readers may omit it (no server-side recording in
25
+ * browsers same as Java).
35
26
  */
36
27
  readValue?(key: ContextKey): unknown;
37
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ContextReader.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ContextReader.ts"],"names":[],"mappings":"","sourcesContent":["import { ContextKey } from '../ContextKey';\nimport { PlatformHeader } from './PlatformHeader';\n\n/**\n * ContextReader - Interface for reading header values from context.\n *\n * Different implementations for different environments:\n * - RequestContextReader: Node.js with AsyncLocalStorage (in @webpieces/http-routing, server-side only)\n * - MutableContextStore / StaticContextReader: Browser or testing with manual header\n * management (in @webpieces/http-client)\n * - CompositeContextReader: Combines multiple readers with priority (in @webpieces/http-client)\n *\n * This interface is defined in @webpieces/http-api so both http-routing and http-client\n * can use it without creating circular dependencies. It is DI-independent so it works in\n * both server (Inversify) and client (Angular/React, browser) environments.\n *\n * This is a business-logic interface (per CLAUDE.md: behavior = interface).\n */\nexport interface ContextReader {\n /**\n * Read the value of a platform header.\n * Returns undefined if header not available.\n *\n * @param header - The platform header to read\n * @returns The header value, or undefined if not present\n */\n read(header: PlatformHeader): string | undefined;\n\n /**\n * OPTIONAL: read a non-header context value (e.g. the active\n * TestCaseRecorder under RecorderKeys.RECORDER).\n *\n * Server-side readers (RequestContextReader) implement this over the\n * RequestContext; browser readers may omit it (no server-side recording\n * in browsers - same as Java). This keeps http-client free of any\n * Node-only imports while still letting it find the recorder.\n */\n // webpieces-disable no-any-unknown -- context values are heterogeneous (recorder, meta objects)\n readValue?(key: ContextKey): unknown;\n}\n"]}
1
+ {"version":3,"file":"ContextReader.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ContextReader.ts"],"names":[],"mappings":"","sourcesContent":["import { ContextKey } from '../ContextKey';\n\n/**\n * ContextReader - reads context-key values from the ambient magic context.\n *\n * There are exactly TWO implementations, one per environment:\n * - Node/server: `RequestContextReader` (in @webpieces/core-context) reads the\n * AsyncLocalStorage-backed RequestContext.\n * - Browser: `MutableContextStore` (in @webpieces/http-client) a mutable in-memory\n * store the app sets as values become known (login token, tenant, ...).\n *\n * Defined in core-util (browser + Node safe, DI-independent) so both sides can use it\n * without a circular dependency.\n *\n * This is a business-logic interface (per CLAUDE.md: behavior = interface).\n */\nexport interface ContextReader {\n /**\n * Read the string value of a context key. Returns undefined if not present.\n */\n read(key: ContextKey): string | undefined;\n\n /**\n * OPTIONAL: read a non-string context value (e.g. the active TestCaseRecorder\n * under RecorderKeys.RECORDER). Server-side readers implement this over the\n * RequestContext; browser readers may omit it (no server-side recording in\n * browsers same as Java).\n */\n // webpieces-disable no-any-unknown -- context values are heterogeneous (recorder, meta objects)\n readValue?(key: ContextKey): unknown;\n}\n"]}
@@ -1,71 +1,27 @@
1
- import { PlatformHeader } from './PlatformHeader';
1
+ import { ContextKey } from '../ContextKey';
2
2
  import { ContextReader } from './ContextReader';
3
3
  /**
4
- * HeaderMethods - Utility class for working with platform headers.
4
+ * HeaderMethods - stateless utility for turning context keys + a ContextReader into
5
+ * the maps the framework needs (outbound transfer, masked log map).
5
6
  *
6
- * This class can be injected in both server (Node.js) and client (Angular/browser) environments.
7
- * It provides common operations for filtering and processing headers.
8
- *
9
- * Pattern: Stateless utility class (pure functions, can be instantiated or injected)
10
- * - Server: Can inject empty instance, use static-like methods
11
- * - Client: new HeaderMethods() (no DI needed)
12
- *
13
- * Usage:
14
- * ```typescript
15
- * // Server-side (ContextFilter)
16
- * constructor(@inject() headerMethods: HeaderMethods) {
17
- * const allHeaders = [... flatten from extensions ...];
18
- * this.transferHeaders = headerMethods.findTransferHeaders(allHeaders);
19
- * }
20
- *
21
- * // Client-side (ClientFactory)
22
- * const headerMethods = new HeaderMethods();
23
- * const loggableHeaders = headerMethods.findLoggableHeaders(allHeaders, requestHeaders);
24
- * ```
7
+ * Works in both server (Node) and browser environments; a plain `new HeaderMethods()`
8
+ * (no DI). The set of keys is supplied by the caller (from HeaderRegistry).
25
9
  */
26
10
  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[];
27
15
  /**
28
- * Filter headers to only those that should be transferred (isWantTransferred=true).
29
- *
30
- * @param headers - Array of PlatformHeader definitions
31
- * @returns Filtered array of headers with isWantTransferred=true
32
- */
33
- findTransferHeaders(headers: PlatformHeader[]): PlatformHeader[];
34
- /**
35
- * Split headers into secure and public categories.
36
- *
37
- * @param headers - Array of PlatformHeader definitions
38
- * @returns SplitHeaders with secureHeaders (isSecured=true) and publicHeaders (isSecured=false)
39
- */
40
- secureHeaders(headers: PlatformHeader[]): PlatformHeader[];
41
- /**
42
- * Get all headers that should be logged.
43
- * All headers are loggable - secure headers will be masked by formatHeadersForLogging.
44
- *
45
- * @param headers - Array of PlatformHeader definitions
46
- * @returns All headers (they're all loggable, just some are masked)
47
- */
48
- findLoggableHeaders(headers: PlatformHeader[]): PlatformHeader[];
49
- buildSecureMapForLogs(platformHeaders: PlatformHeader[], contextReader: ContextReader): Map<string, any>;
50
- /**
51
- * Format headers for logging with secure masking.
52
- * Takes filtered PlatformHeaders and actual header values from request.
53
- *
54
- * Masking rules for secure headers (isSecured=true):
55
- * - Length > 15: Show first 3 and last 3 characters with "..." between
56
- * - Length 8-15: Show first 2 characters with "..."
57
- * - Length < 8: Show "<secure key too short to log>"
58
- *
59
- * @param loggableHeaders - Filtered PlatformHeaders to log
60
- * @param headerMap - Map of header name (lowercase) -> array of values from request
61
- * @returns Record of header name -> masked or full value for logging
16
+ * Build the map for LOGGING from the given keys: each logged key (isLogged=true)
17
+ * with a value present is added under its `name`, masked when isSecured.
62
18
  */
63
- formatHeadersForLogging(loggableHeaders: PlatformHeader[], headerMap: Map<string, string[]>): Record<string, string>;
19
+ buildSecureMapForLogs(keys: ContextKey[], contextReader: ContextReader): Map<string, string>;
64
20
  /**
65
- * Mask a secure header value based on its length.
66
- *
67
- * @param value - The secure header value to mask
68
- * @returns Masked value
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>"
69
25
  */
70
26
  private maskSecureValue;
71
27
  }