@webpieces/core-util 0.3.316 → 0.3.317

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/core-util",
3
- "version": "0.3.316",
3
+ "version": "0.3.317",
4
4
  "description": "Utility functions for WebPieces - works in browser and Node.js",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -1,66 +1,43 @@
1
1
  import { ContextReader } from './ContextReader';
2
2
  /**
3
- * ContextMgr - propagates the magic context onto outbound HTTP requests.
3
+ * ContextMgr - propagates the magic context onto outbound BROWSER requests.
4
4
  *
5
- * Passed to ClientHttpFactory's constructor: every transferred key (httpHeader set)
6
- * in the GLOBAL {@link HeaderRegistry} is read from the ContextReader and added to
7
- * outbound requests. The registry is a process global (configured once at startup,
8
- * like LogManager), so ContextMgr no longer takes a registry argument.
5
+ * BROWSER-ONLY. Only @webpieces/http-client-browser may name this class. The server reads
6
+ * `RequestContext` directly through `RequestContextHeaders` (in @webpieces/core-context) a
7
+ * `ContextReader` indirection buys a server nothing, because there is exactly one right answer there.
9
8
  *
10
- * Browser-safe (no AsyncLocalStorage): the server-side reader (RequestContextReader,
11
- * in @webpieces/core-context) and the browser store (MutableContextStore, in
12
- * @webpieces/http-client) both implement ContextReader.
9
+ * Browsers have no AsyncLocalStorage, so the app holds a `MutableContextStore` and sets values as
10
+ * they become known (login token, tenant). Every transferred key (httpHeader set) in the GLOBAL
11
+ * {@link HeaderRegistry} is read from it and added to outbound requests. The registry is a process
12
+ * global configured once at startup (like LogManager) and is browser-safe: it is the key SCHEMA,
13
+ * not the value store.
13
14
  *
14
15
  * Example usage:
15
16
  * ```typescript
16
- * // Node.js server-side (reads the magic context from RequestContext):
17
- * const contextMgr = new ContextMgr(new RequestContextReader());
17
+ * // startup, before bootstrap:
18
+ * HeaderRegistry.configure(AppHeaders.getAllHeaders(), CompanyHeaders.getAllHeaders(), true);
18
19
  *
19
- * // Browser client-side (app-managed store, no AsyncLocalStorage):
20
- * const contextMgr = new ContextMgr(new MutableContextStore());
21
- *
22
- * // Both cases — the ContextMgr is a factory dependency, the baseUrl is client state:
23
- * const factory = new ClientHttpFactory(contextMgr);
20
+ * const store = new MutableContextStore();
21
+ * const factory = new ClientHttpBrowserFactory(store);
24
22
  * const client = factory.createClient(SaveApi, new ClientConfig('http://api.example.com'));
25
23
  * ```
26
24
  */
27
25
  export declare class ContextMgr {
28
- /**
29
- * The context reader that provides context-key values.
30
- * Different implementations for Node.js vs browser.
31
- */
26
+ /** The app-held store that provides context-key values. */
32
27
  readonly contextReader: ContextReader;
33
- /**
34
- * When true (default), outbound calls send the current x-request-id as
35
- * x-previous-request-id (and drop x-request-id) so each hop in a
36
- * distributed trace gets its own id chained to its caller's.
37
- */
38
- readonly chainRequestIds: boolean;
39
- private chainProcessor;
40
- private headerMethods;
41
28
  constructor(
29
+ /** The app-held store that provides context-key values. */
30
+ contextReader: ContextReader);
42
31
  /**
43
- * The context reader that provides context-key values.
44
- * Different implementations for Node.js vs browser.
45
- */
46
- contextReader: ContextReader,
47
- /**
48
- * When true (default), outbound calls send the current x-request-id as
49
- * x-previous-request-id (and drop x-request-id) so each hop in a
50
- * distributed trace gets its own id chained to its caller's.
51
- */
52
- chainRequestIds?: boolean);
53
- /**
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).
32
+ * Build the headers to send on an outbound request: every transferred key (httpHeader set)
33
+ * with a non-empty value, emitted under its `httpHeader` wire name.
57
34
  *
58
- * Values are RAW (unmasked) - this map goes on the wire, not in logs.
35
+ * NO request-id chaining. A browser ORIGINATES a trace it has no inbound request to point
36
+ * back at. If the app puts an `x-request-id` on the store it goes out as-is, and the server's
37
+ * inbound transfer adopts it as hop 1's own id. Chaining is a server concern; see
38
+ * RequestContextHeaders.
39
+ *
40
+ * Values are RAW (unmasked) — this map goes on the wire, not in logs.
59
41
  */
60
42
  buildOutboundHeaders(): Map<string, string>;
61
- /**
62
- * Build the header map for LOGGING: secured values masked, keyed by each key's
63
- * `name`, only for keys with isLogged=true.
64
- */
65
- buildHeadersForLogging(): Map<string, string>;
66
43
  }
@@ -1,61 +1,47 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ContextMgr = void 0;
4
- const HeaderMethods_1 = require("./HeaderMethods");
5
4
  const HeaderRegistry_1 = require("./HeaderRegistry");
6
- const RequestIdChainProcessor_1 = require("./RequestIdChainProcessor");
7
5
  /**
8
- * ContextMgr - propagates the magic context onto outbound HTTP requests.
6
+ * ContextMgr - propagates the magic context onto outbound BROWSER requests.
9
7
  *
10
- * Passed to ClientHttpFactory's constructor: every transferred key (httpHeader set)
11
- * in the GLOBAL {@link HeaderRegistry} is read from the ContextReader and added to
12
- * outbound requests. The registry is a process global (configured once at startup,
13
- * like LogManager), so ContextMgr no longer takes a registry argument.
8
+ * BROWSER-ONLY. Only @webpieces/http-client-browser may name this class. The server reads
9
+ * `RequestContext` directly through `RequestContextHeaders` (in @webpieces/core-context) a
10
+ * `ContextReader` indirection buys a server nothing, because there is exactly one right answer there.
14
11
  *
15
- * Browser-safe (no AsyncLocalStorage): the server-side reader (RequestContextReader,
16
- * in @webpieces/core-context) and the browser store (MutableContextStore, in
17
- * @webpieces/http-client) both implement ContextReader.
12
+ * Browsers have no AsyncLocalStorage, so the app holds a `MutableContextStore` and sets values as
13
+ * they become known (login token, tenant). Every transferred key (httpHeader set) in the GLOBAL
14
+ * {@link HeaderRegistry} is read from it and added to outbound requests. The registry is a process
15
+ * global configured once at startup (like LogManager) and is browser-safe: it is the key SCHEMA,
16
+ * not the value store.
18
17
  *
19
18
  * Example usage:
20
19
  * ```typescript
21
- * // Node.js server-side (reads the magic context from RequestContext):
22
- * const contextMgr = new ContextMgr(new RequestContextReader());
20
+ * // startup, before bootstrap:
21
+ * HeaderRegistry.configure(AppHeaders.getAllHeaders(), CompanyHeaders.getAllHeaders(), true);
23
22
  *
24
- * // Browser client-side (app-managed store, no AsyncLocalStorage):
25
- * const contextMgr = new ContextMgr(new MutableContextStore());
26
- *
27
- * // Both cases — the ContextMgr is a factory dependency, the baseUrl is client state:
28
- * const factory = new ClientHttpFactory(contextMgr);
23
+ * const store = new MutableContextStore();
24
+ * const factory = new ClientHttpBrowserFactory(store);
29
25
  * const client = factory.createClient(SaveApi, new ClientConfig('http://api.example.com'));
30
26
  * ```
31
27
  */
32
28
  class ContextMgr {
33
29
  contextReader;
34
- chainRequestIds;
35
- chainProcessor;
36
- headerMethods = new HeaderMethods_1.HeaderMethods();
37
30
  constructor(
38
- /**
39
- * The context reader that provides context-key values.
40
- * Different implementations for Node.js vs browser.
41
- */
42
- contextReader,
43
- /**
44
- * When true (default), outbound calls send the current x-request-id as
45
- * x-previous-request-id (and drop x-request-id) so each hop in a
46
- * distributed trace gets its own id chained to its caller's.
47
- */
48
- chainRequestIds = true) {
31
+ /** The app-held store that provides context-key values. */
32
+ contextReader) {
49
33
  this.contextReader = contextReader;
50
- this.chainRequestIds = chainRequestIds;
51
- this.chainProcessor = new RequestIdChainProcessor_1.RequestIdChainProcessor();
52
34
  }
53
35
  /**
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).
36
+ * Build the headers to send on an outbound request: every transferred key (httpHeader set)
37
+ * with a non-empty value, emitted under its `httpHeader` wire name.
57
38
  *
58
- * Values are RAW (unmasked) - this map goes on the wire, not in logs.
39
+ * NO request-id chaining. A browser ORIGINATES a trace it has no inbound request to point
40
+ * back at. If the app puts an `x-request-id` on the store it goes out as-is, and the server's
41
+ * inbound transfer adopts it as hop 1's own id. Chaining is a server concern; see
42
+ * RequestContextHeaders.
43
+ *
44
+ * Values are RAW (unmasked) — this map goes on the wire, not in logs.
59
45
  */
60
46
  buildOutboundHeaders() {
61
47
  const outbound = new Map();
@@ -65,18 +51,8 @@ class ContextMgr {
65
51
  outbound.set(key.httpHeader, value);
66
52
  }
67
53
  }
68
- if (this.chainRequestIds) {
69
- this.chainProcessor.process(outbound);
70
- }
71
54
  return outbound;
72
55
  }
73
- /**
74
- * Build the header map for LOGGING: secured values masked, keyed by each key's
75
- * `name`, only for keys with isLogged=true.
76
- */
77
- buildHeadersForLogging() {
78
- return this.headerMethods.buildSecureMapForLogs(HeaderRegistry_1.HeaderRegistry.get().getLoggedKeys(), this.contextReader);
79
- }
80
56
  }
81
57
  exports.ContextMgr = ContextMgr;
82
58
  //# sourceMappingURL=ContextMgr.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ContextMgr.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ContextMgr.ts"],"names":[],"mappings":";;;AACA,mDAAgD;AAChD,qDAAkD;AAClD,uEAAoE;AAEpE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAa,UAAU;IASC;IAOA;IAfZ,cAAc,CAA0B;IACxC,aAAa,GAAkB,IAAI,6BAAa,EAAE,CAAC;IAE3D;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;QAClB,OAAO,IAAI,CAAC,aAAa,CAAC,qBAAqB,CAAC,+BAAc,CAAC,GAAG,EAAE,CAAC,aAAa,EAAE,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAC9G,CAAC;CACJ;AApDD,gCAoDC","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 ClientHttpFactory's constructor: every transferred key (httpHeader set)\n * in the GLOBAL {@link HeaderRegistry} is read from the ContextReader and added to\n * outbound requests. The registry is a process global (configured once at startup,\n * 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 — the ContextMgr is a factory dependency, the baseUrl is client state:\n * const factory = new ClientHttpFactory(contextMgr);\n * const client = factory.createClient(SaveApi, new ClientConfig('http://api.example.com'));\n * ```\n */\nexport class ContextMgr {\n private chainProcessor: RequestIdChainProcessor;\n private headerMethods: HeaderMethods = new HeaderMethods();\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(): Map<string, string> {\n return this.headerMethods.buildSecureMapForLogs(HeaderRegistry.get().getLoggedKeys(), 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;AAElD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,UAAU;IAIC;IAFpB;IACI,2DAA2D;IAC3C,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAC7C,CAAC;IAEJ;;;;;;;;;;OAUG;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,OAAO,QAAQ,CAAC;IACpB,CAAC;CACJ;AA9BD,gCA8BC","sourcesContent":["import { ContextKey } from '../ContextKey';\nimport { ContextReader } from './ContextReader';\nimport { HeaderRegistry } from './HeaderRegistry';\n\n/**\n * ContextMgr - propagates the magic context onto outbound BROWSER requests.\n *\n * BROWSER-ONLY. Only @webpieces/http-client-browser may name this class. The server reads\n * `RequestContext` directly through `RequestContextHeaders` (in @webpieces/core-context) — a\n * `ContextReader` indirection buys a server nothing, because there is exactly one right answer there.\n *\n * Browsers have no AsyncLocalStorage, so the app holds a `MutableContextStore` and sets values as\n * they become known (login token, tenant). Every transferred key (httpHeader set) in the GLOBAL\n * {@link HeaderRegistry} is read from it and added to outbound requests. The registry is a process\n * global configured once at startup (like LogManager) and is browser-safe: it is the key SCHEMA,\n * not the value store.\n *\n * Example usage:\n * ```typescript\n * // startup, before bootstrap:\n * HeaderRegistry.configure(AppHeaders.getAllHeaders(), CompanyHeaders.getAllHeaders(), true);\n *\n * const store = new MutableContextStore();\n * const factory = new ClientHttpBrowserFactory(store);\n * const client = factory.createClient(SaveApi, new ClientConfig('http://api.example.com'));\n * ```\n */\nexport class ContextMgr {\n\n constructor(\n /** The app-held store that provides context-key values. */\n public readonly contextReader: ContextReader,\n ) {}\n\n /**\n * Build the headers to send on an outbound request: every transferred key (httpHeader set)\n * with a non-empty value, emitted under its `httpHeader` wire name.\n *\n * NO request-id chaining. A browser ORIGINATES a trace it has no inbound request to point\n * back at. If the app puts an `x-request-id` on the store it goes out as-is, and the server's\n * inbound transfer adopts it as hop 1's own id. Chaining is a server concern; see\n * RequestContextHeaders.\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 return outbound;\n }\n}\n"]}
@@ -1,15 +1,23 @@
1
1
  import { ContextKey } from '../ContextKey';
2
2
  /**
3
- * ContextReader - reads context-key values from the ambient magic context.
3
+ * Reads one context key's string value. The ONE seam between the two environments:
4
+ * the server passes `RequestContext.getHeader`, a browser passes its store's read.
4
5
  *
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, ...).
6
+ * A lambda, not an object nothing here needs an implementation to hold.
7
+ */
8
+ export type ContextRead = (key: ContextKey) => string | undefined;
9
+ /**
10
+ * ContextReader - reads context-key values from an app-held store.
11
+ *
12
+ * BROWSER-ONLY. Browsers have no AsyncLocalStorage and therefore no ambient request scope, so the
13
+ * app holds a `MutableContextStore` (in @webpieces/http-client-browser) and sets values as they
14
+ * become known (login token, tenant, ...).
15
+ *
16
+ * The server has no use for this: there is exactly one right answer there, so `RequestContextHeaders`
17
+ * (in @webpieces/core-context) reads `RequestContext` directly rather than through a reader object.
10
18
  *
11
- * Defined in core-util (browser + Node safe, DI-independent) so both sides can use it
12
- * without a circular dependency.
19
+ * Note this is only about where VALUES live. The key SCHEMA which keys exist, which transfer, which
20
+ * are secured is the global {@link HeaderRegistry}, and that is browser-safe and shared by both.
13
21
  *
14
22
  * This is a business-logic interface (per CLAUDE.md: behavior = interface).
15
23
  */
@@ -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';\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
+ {"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 * Reads one context key's string value. The ONE seam between the two environments:\n * the server passes `RequestContext.getHeader`, a browser passes its store's read.\n *\n * A lambda, not an object nothing here needs an implementation to hold.\n */\nexport type ContextRead = (key: ContextKey) => string | undefined;\n\n/**\n * ContextReader - reads context-key values from an app-held store.\n *\n * BROWSER-ONLY. Browsers have no AsyncLocalStorage and therefore no ambient request scope, so the\n * app holds a `MutableContextStore` (in @webpieces/http-client-browser) and sets values as they\n * become known (login token, tenant, ...).\n *\n * The server has no use for this: there is exactly one right answer there, so `RequestContextHeaders`\n * (in @webpieces/core-context) reads `RequestContext` directly rather than through a reader object.\n *\n * Note this is only about where VALUES live. The key SCHEMA — which keys exist, which transfer, which\n * are secured is the global {@link HeaderRegistry}, and that is browser-safe and shared by both.\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,4 +1,5 @@
1
1
  import { ContextKey } from '../ContextKey';
2
+ import { ContextRead } from './ContextReader';
2
3
  /**
3
4
  * HeaderRegistry - the single, GLOBAL source of truth for every ContextKey the
4
5
  * platform knows about. Port of Java webpieces' HeaderTranslation.
@@ -53,6 +54,17 @@ export declare class HeaderRegistry {
53
54
  getSecuredNames(): string[];
54
55
  /** Keys that appear in logs. isLogged=true. */
55
56
  getLoggedKeys(): ContextKey[];
57
+ /**
58
+ * The log/MDC field map: every logged key with a value, under its `name`, secured values masked.
59
+ *
60
+ * The ONE implementation, because the registry owns the keys and each {@link ContextKey} knows
61
+ * how to mask its own value. Callers differ only in WHERE a value is read from:
62
+ * `RequestContext.buildLogFields()` passes its own getHeader (server), and `ContextMgr` passes
63
+ * the app-held store's read (browser).
64
+ *
65
+ * getLoggedKeys() is precomputed at configure() time, so this is hot-path safe.
66
+ */
67
+ buildLogFields(read: ContextRead): Map<string, string>;
56
68
  /** Look up a key by its HTTP header name (case-insensitive). O(1) via the precomputed map. */
57
69
  findByHttpHeader(httpHeader: string): ContextKey | undefined;
58
70
  /**
@@ -92,6 +92,26 @@ class HeaderRegistry {
92
92
  getLoggedKeys() {
93
93
  return this.loggedKeys;
94
94
  }
95
+ /**
96
+ * The log/MDC field map: every logged key with a value, under its `name`, secured values masked.
97
+ *
98
+ * The ONE implementation, because the registry owns the keys and each {@link ContextKey} knows
99
+ * how to mask its own value. Callers differ only in WHERE a value is read from:
100
+ * `RequestContext.buildLogFields()` passes its own getHeader (server), and `ContextMgr` passes
101
+ * the app-held store's read (browser).
102
+ *
103
+ * getLoggedKeys() is precomputed at configure() time, so this is hot-path safe.
104
+ */
105
+ buildLogFields(read) {
106
+ const fields = new Map();
107
+ for (const key of this.getLoggedKeys()) {
108
+ const value = read(key);
109
+ if (value) {
110
+ fields.set(key.name, key.maskIfSecured(value));
111
+ }
112
+ }
113
+ return fields;
114
+ }
95
115
  /** Look up a key by its HTTP header name (case-insensitive). O(1) via the precomputed map. */
96
116
  findByHttpHeader(httpHeader) {
97
117
  return this.byHttpHeader.get(httpHeader.toLowerCase());
@@ -1 +1 @@
1
- {"version":3,"file":"HeaderRegistry.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/HeaderRegistry.ts"],"names":[],"mappings":";;;AACA,iEAA8D;AAE9D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAa,cAAc;IACvB,8EAA8E;IAC9E,MAAM,CAAU,eAAe,GAAiB,2CAAoB,CAAC,aAAa,EAAE,CAAC;IAE7E,MAAM,CAAC,QAAQ,CAA6B;IAEnC,IAAI,CAAe;IAEpC,yEAAyE;IACzE,gFAAgF;IAChF,8EAA8E;IAC9E,8EAA8E;IAC9E,6EAA6E;IAC5D,eAAe,CAAe;IAC9B,YAAY,CAAW;IACvB,UAAU,CAAe;IACzB,YAAY,CAA0B;IAEvD,YAAoB,IAAkB;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAa,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC;QACvF,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI;aACxB,MAAM,CAAC,CAAC,CAAa,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;aACtC,GAAG,CAAC,CAAC,CAAa,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAa,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAClE,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,CACvB,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAa,EAAwB,EAAE,CAAC,CAAC,CAAC,CAAC,UAAW,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CACtG,CAAC;IACN,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,SAAS,CAAC,UAAwB,EAAE,cAA4B,EAAE,eAAwB;QAC7F,MAAM,GAAG,GAAiB;YACtB,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,GAAG,cAAc;YACjB,GAAG,UAAU;SAChB,CAAC;QACF,cAAc,CAAC,QAAQ,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC;IACtD,CAAC;IAED,0EAA0E;IAC1E,MAAM,CAAC,GAAG;QACN,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACX,4EAA4E;gBAC5E,qFAAqF,CACxF,CAAC;QACN,CAAC;QACD,OAAO,cAAc,CAAC,QAAQ,CAAC;IACnC,CAAC;IAED,iFAAiF;IACjF,MAAM,CAAC,YAAY;QACf,OAAO,cAAc,CAAC,QAAQ,KAAK,SAAS,CAAC;IACjD,CAAC;IAED,0CAA0C;IAC1C,OAAO;QACH,OAAO,IAAI,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;;OAGG;IACH,kBAAkB;QACd,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAED,4EAA4E;IAC5E,eAAe;QACX,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IAED,+CAA+C;IAC/C,aAAa;QACT,OAAO,IAAI,CAAC,UAAU,CAAC;IAC3B,CAAC;IAED,8FAA8F;IAC9F,gBAAgB,CAAC,UAAkB;QAC/B,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED;;;OAGG;IACK,kBAAkB,CAAC,OAAqB;QAC5C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;QAC7C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAsB,CAAC;QAEnD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACrC,IAAI,QAAQ,EAAE,CAAC;gBACX,IAAI,CAAC,oBAAoB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;gBACzC,SAAS,CAAC,6BAA6B;YAC3C,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAEzB,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC/B,MAAM,SAAS,GAAG,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;gBAC/C,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC1C,IAAI,KAAK,EAAE,CAAC;oBACR,MAAM,IAAI,KAAK,CACX,oCAAoC,GAAG,CAAC,UAAU,KAAK;wBACvD,mBAAmB,KAAK,CAAC,IAAI,cAAc,GAAG,CAAC,IAAI,KAAK;wBACxD,uDAAuD,CAC1D,CAAC;gBACN,CAAC;gBACD,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;YACrC,CAAC;QACL,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACvC,CAAC;IAED;;;;OAIG;IACK,oBAAoB,CAAC,QAAoB,EAAE,SAAqB;QACpE,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,CAAC,UAAU,EAAE,CAAC;YAC/C,SAAS,CAAC,IAAI,CAAC,gBAAgB,QAAQ,CAAC,UAAU,SAAS,SAAS,CAAC,UAAU,IAAI,CAAC,CAAC;QACzF,CAAC;QACD,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,CAAC,SAAS,EAAE,CAAC;YAC7C,SAAS,CAAC,IAAI,CAAC,cAAc,QAAQ,CAAC,SAAS,OAAO,SAAS,CAAC,SAAS,GAAG,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,QAAQ,CAAC,QAAQ,KAAK,SAAS,CAAC,QAAQ,EAAE,CAAC;YAC3C,SAAS,CAAC,IAAI,CAAC,aAAa,QAAQ,CAAC,QAAQ,OAAO,SAAS,CAAC,QAAQ,GAAG,CAAC,CAAC;QAC/E,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACX,2CAA2C,QAAQ,CAAC,IAAI,KAAK;gBAC7D,4CAA4C,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;gBACpE,8CAA8C,CACjD,CAAC;QACN,CAAC;IACL,CAAC;;AAhJL,wCAiJC","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"]}
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 { ContextRead } from './ContextReader';\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 /**\n * The log/MDC field map: every logged key with a value, under its `name`, secured values masked.\n *\n * The ONE implementation, because the registry owns the keys and each {@link ContextKey} knows\n * how to mask its own value. Callers differ only in WHERE a value is read from:\n * `RequestContext.buildLogFields()` passes its own getHeader (server), and `ContextMgr` passes\n * the app-held store's read (browser).\n *\n * getLoggedKeys() is precomputed at configure() time, so this is hot-path safe.\n */\n buildLogFields(read: ContextRead): Map<string, string> {\n const fields = new Map<string, string>();\n for (const key of this.getLoggedKeys()) {\n const value = read(key);\n if (value) {\n fields.set(key.name, key.maskIfSecured(value));\n }\n }\n return fields;\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"]}
@@ -18,11 +18,13 @@ export declare class LogApiCall {
18
18
  * @param type - 'SVR' or 'CLIENT'
19
19
  * @param meta - Route metadata with controllerClassName and methodName
20
20
  * @param requestDto - The request DTO
21
- * @param headers - Map of header name -> values
22
- * @param splitHeaders - SplitHeaders with secureHeaders and publicHeaders for masking
23
21
  * @param method - The method to execute
22
+ *
23
+ * Context fields (requestId, tenantId, ...) are NOT stamped here. A logging BACKEND owns that:
24
+ * bunyan/winston read RequestContext.buildLogFields() on every record. The bootstrap
25
+ * ConsoleLogger deliberately carries no context.
24
26
  */
25
- execute(type: string, meta: RouteMetadata, requestDto: any, headers: Map<string, any>, method: (dto: any) => Promise<any>): Promise<any>;
27
+ execute(type: string, meta: RouteMetadata, requestDto: any, method: (dto: any) => Promise<any>): Promise<any>;
26
28
  /**
27
29
  * Check if an error is a user error (expected behavior from server perspective).
28
30
  * User errors are NOT failures - just users making mistakes or validation issues.
@@ -24,14 +24,14 @@ class LogApiCall {
24
24
  * @param type - 'SVR' or 'CLIENT'
25
25
  * @param meta - Route metadata with controllerClassName and methodName
26
26
  * @param requestDto - The request DTO
27
- * @param headers - Map of header name -> values
28
- * @param splitHeaders - SplitHeaders with secureHeaders and publicHeaders for masking
29
27
  * @param method - The method to execute
28
+ *
29
+ * Context fields (requestId, tenantId, ...) are NOT stamped here. A logging BACKEND owns that:
30
+ * bunyan/winston read RequestContext.buildLogFields() on every record. The bootstrap
31
+ * ConsoleLogger deliberately carries no context.
30
32
  */
31
- async execute(type, meta, requestDto, headers, method) {
32
- // Log request - convert Map to Object for JSON serialization
33
- const headersObj = Object.fromEntries(headers);
34
- log.info(`[API-${type}-req] ${meta.controllerClassName}.${meta.methodName} ${meta.path} request=${JSON.stringify(requestDto)} headers=${JSON.stringify(headersObj)}`);
33
+ async execute(type, meta, requestDto, method) {
34
+ log.info(`[API-${type}-req] ${meta.controllerClassName}.${meta.methodName} ${meta.path} request=${JSON.stringify(requestDto)}`);
35
35
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- LogApiCall logs errors before re-throwing to caller
36
36
  try {
37
37
  if (!requestDto)
@@ -1 +1 @@
1
- {"version":3,"file":"LogApiCall.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/LogApiCall.ts"],"names":[],"mappings":";;;AACA,qCAMkB;AAClB,kDAA0C;AAC1C,sDAAiD;AAEjD,MAAM,GAAG,GAAG,uBAAU,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;AAG/C;;;;;;;;;;;GAWG;AACH,MAAa,UAAU;IAEnB;;;;;;;;;OASG;IACI,KAAK,CAAC,OAAO,CAChB,IAAY,EACZ,IAAmB,EACnB,UAAe,EACf,OAAyB,EACzB,MAAkC;QAElC,6DAA6D;QAC7D,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC/C,GAAG,CAAC,IAAI,CACJ,QAAQ,IAAI,SAAS,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,YAAY,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAC9J,CAAC;QAEF,qHAAqH;QACrH,IAAI,CAAC;YACD,IAAG,CAAC,UAAU;gBACV,MAAM,IAAI,KAAK,CAAC,uCAAuC,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YAE1G,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YAE1C,IAAG,CAAC,QAAQ;gBACR,MAAM,IAAI,KAAK,CAAC,wCAAwC,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YAE3G,uBAAuB;YACvB,GAAG,CAAC,IAAI,CACJ,QAAQ,IAAI,kBAAkB,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,aAAa,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CACnH,CAAC;YAEF,OAAO,QAAQ,CAAC;QACpB,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,oBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC;YACzC,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC;YAEnC,uCAAuC;YACvC,IAAI,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;gBAChC,GAAG,CAAC,IAAI,CACJ,QAAQ,IAAI,gBAAgB,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,cAAc,SAAS,EAAE,CACnG,CAAC;YACN,CAAC;iBAAM,CAAC;gBACJ,GAAG,CAAC,KAAK,CACL,QAAQ,IAAI,eAAe,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,cAAc,SAAS,UAAU,YAAY,EAAE,CACxH,CAAC;YACN,CAAC;YACD,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,WAAW,CAAC,KAAc;QAC7B,OAAO,CACH,KAAK,YAAY,4BAAmB;YACpC,KAAK,YAAY,8BAAqB;YACtC,KAAK,YAAY,2BAAkB;YACnC,KAAK,YAAY,0BAAiB;YAClC,KAAK,YAAY,sBAAa,CACjC,CAAC;IACN,CAAC;CACJ;AAnFD,gCAmFC","sourcesContent":["import {RouteMetadata} from \"./decorators\";\nimport {\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpNotFoundError,\n HttpUserError,\n} from './errors';\nimport {toError} from \"../lib/errorUtils\";\nimport {LogManager} from \"../logging/LogManager\";\n\nconst log = LogManager.getLogger('LogApiCall');\n\n\n/**\n * LogApiCall - Generic API call logging utility.\n *\n * Used by both server-side (LogApiFilter) and client-side (ClientFactory) for\n * consistent logging patterns across the framework.\n *\n * Logging format patterns:\n * - [API-{type}-req] ClassName.methodName request={...} headers={...}\n * - [API-{type}-resp-SUCCESS] ClassName.methodName response={...}\n * - [API-{type}-resp-OTHER] ClassName.methodName errorType={...} (user errors)\n * - [API-{type}-resp-FAIL] ClassName.methodName error={...} (server errors)\n */\nexport class LogApiCall {\n\n /**\n * Execute an API call with logging around it.\n *\n * @param type - 'SVR' or 'CLIENT'\n * @param meta - Route metadata with controllerClassName and methodName\n * @param requestDto - The request DTO\n * @param headers - Map of header name -> values\n * @param splitHeaders - SplitHeaders with secureHeaders and publicHeaders for masking\n * @param method - The method to execute\n */\n public async execute(\n type: string,\n meta: RouteMetadata,\n requestDto: any,\n headers: Map<string, any>,\n method: (dto: any) => Promise<any>\n ): Promise<any> {\n // Log request - convert Map to Object for JSON serialization\n const headersObj = Object.fromEntries(headers);\n log.info(\n `[API-${type}-req] ${meta.controllerClassName}.${meta.methodName} ${meta.path} request=${JSON.stringify(requestDto)} headers=${JSON.stringify(headersObj)}`\n );\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- LogApiCall logs errors before re-throwing to caller\n try {\n if(!requestDto)\n throw new Error(`Request cannot be null and was from ${meta.controllerClassName}.${meta.methodName}`);\n \n const response = await method(requestDto);\n\n if(!response)\n throw new Error(`Response cannot be null and was from ${meta.controllerClassName}.${meta.methodName}`);\n\n // Log success response\n log.info(\n `[API-${type}-resp-SUCCESS] ${meta.controllerClassName}.${meta.methodName} response=${JSON.stringify(response)}`\n );\n\n return response;\n } catch (err: unknown) {\n const error = toError(err);\n const errorType = error.constructor.name;\n const errorMessage = error.message;\n\n // Log error based on type and re-throw\n if (LogApiCall.isUserError(error)) {\n log.warn(\n `[API-${type}-resp-OTHER] ${meta.controllerClassName}.${meta.methodName} errorType=${errorType}`\n );\n } else {\n log.error(\n `[API-${type}-resp-FAIL] ${meta.controllerClassName}.${meta.methodName} errorType=${errorType} error=${errorMessage}`\n );\n }\n throw error;\n }\n }\n\n /**\n * Check if an error is a user error (expected behavior from server perspective).\n * User errors are NOT failures - just users making mistakes or validation issues.\n *\n * User errors (logged as OTHER, no stack trace):\n * - HttpBadRequestError (400)\n * - HttpUnauthorizedError (401)\n * - HttpForbiddenError (403)\n * - HttpNotFoundError (404)\n * - HttpUserError (266)\n *\n * @param error - The error to check\n * @returns true if this is a user error, false for server errors\n */\n static isUserError(error: unknown): boolean {\n return (\n error instanceof HttpBadRequestError ||\n error instanceof HttpUnauthorizedError ||\n error instanceof HttpForbiddenError ||\n error instanceof HttpNotFoundError ||\n error instanceof HttpUserError\n );\n }\n}\n"]}
1
+ {"version":3,"file":"LogApiCall.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/LogApiCall.ts"],"names":[],"mappings":";;;AACA,qCAMkB;AAClB,kDAA0C;AAC1C,sDAAiD;AAEjD,MAAM,GAAG,GAAG,uBAAU,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;AAG/C;;;;;;;;;;;GAWG;AACH,MAAa,UAAU;IAEnB;;;;;;;;;;;OAWG;IACI,KAAK,CAAC,OAAO,CAChB,IAAY,EACZ,IAAmB,EACnB,UAAe,EACf,MAAkC;QAElC,GAAG,CAAC,IAAI,CACJ,QAAQ,IAAI,SAAS,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,YAAY,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CACxH,CAAC;QAEF,qHAAqH;QACrH,IAAI,CAAC;YACD,IAAG,CAAC,UAAU;gBACV,MAAM,IAAI,KAAK,CAAC,uCAAuC,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YAE1G,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YAE1C,IAAG,CAAC,QAAQ;gBACR,MAAM,IAAI,KAAK,CAAC,wCAAwC,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YAE3G,uBAAuB;YACvB,GAAG,CAAC,IAAI,CACJ,QAAQ,IAAI,kBAAkB,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,aAAa,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CACnH,CAAC;YAEF,OAAO,QAAQ,CAAC;QACpB,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,oBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC;YACzC,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC;YAEnC,uCAAuC;YACvC,IAAI,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;gBAChC,GAAG,CAAC,IAAI,CACJ,QAAQ,IAAI,gBAAgB,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,cAAc,SAAS,EAAE,CACnG,CAAC;YACN,CAAC;iBAAM,CAAC;gBACJ,GAAG,CAAC,KAAK,CACL,QAAQ,IAAI,eAAe,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,UAAU,cAAc,SAAS,UAAU,YAAY,EAAE,CACxH,CAAC;YACN,CAAC;YACD,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,WAAW,CAAC,KAAc;QAC7B,OAAO,CACH,KAAK,YAAY,4BAAmB;YACpC,KAAK,YAAY,8BAAqB;YACtC,KAAK,YAAY,2BAAkB;YACnC,KAAK,YAAY,0BAAiB;YAClC,KAAK,YAAY,sBAAa,CACjC,CAAC;IACN,CAAC;CACJ;AAlFD,gCAkFC","sourcesContent":["import {RouteMetadata} from \"./decorators\";\nimport {\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpNotFoundError,\n HttpUserError,\n} from './errors';\nimport {toError} from \"../lib/errorUtils\";\nimport {LogManager} from \"../logging/LogManager\";\n\nconst log = LogManager.getLogger('LogApiCall');\n\n\n/**\n * LogApiCall - Generic API call logging utility.\n *\n * Used by both server-side (LogApiFilter) and client-side (ClientFactory) for\n * consistent logging patterns across the framework.\n *\n * Logging format patterns:\n * - [API-{type}-req] ClassName.methodName request={...} headers={...}\n * - [API-{type}-resp-SUCCESS] ClassName.methodName response={...}\n * - [API-{type}-resp-OTHER] ClassName.methodName errorType={...} (user errors)\n * - [API-{type}-resp-FAIL] ClassName.methodName error={...} (server errors)\n */\nexport class LogApiCall {\n\n /**\n * Execute an API call with logging around it.\n *\n * @param type - 'SVR' or 'CLIENT'\n * @param meta - Route metadata with controllerClassName and methodName\n * @param requestDto - The request DTO\n * @param method - The method to execute\n *\n * Context fields (requestId, tenantId, ...) are NOT stamped here. A logging BACKEND owns that:\n * bunyan/winston read RequestContext.buildLogFields() on every record. The bootstrap\n * ConsoleLogger deliberately carries no context.\n */\n public async execute(\n type: string,\n meta: RouteMetadata,\n requestDto: any,\n method: (dto: any) => Promise<any>\n ): Promise<any> {\n log.info(\n `[API-${type}-req] ${meta.controllerClassName}.${meta.methodName} ${meta.path} request=${JSON.stringify(requestDto)}`\n );\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- LogApiCall logs errors before re-throwing to caller\n try {\n if(!requestDto)\n throw new Error(`Request cannot be null and was from ${meta.controllerClassName}.${meta.methodName}`);\n \n const response = await method(requestDto);\n\n if(!response)\n throw new Error(`Response cannot be null and was from ${meta.controllerClassName}.${meta.methodName}`);\n\n // Log success response\n log.info(\n `[API-${type}-resp-SUCCESS] ${meta.controllerClassName}.${meta.methodName} response=${JSON.stringify(response)}`\n );\n\n return response;\n } catch (err: unknown) {\n const error = toError(err);\n const errorType = error.constructor.name;\n const errorMessage = error.message;\n\n // Log error based on type and re-throw\n if (LogApiCall.isUserError(error)) {\n log.warn(\n `[API-${type}-resp-OTHER] ${meta.controllerClassName}.${meta.methodName} errorType=${errorType}`\n );\n } else {\n log.error(\n `[API-${type}-resp-FAIL] ${meta.controllerClassName}.${meta.methodName} errorType=${errorType} error=${errorMessage}`\n );\n }\n throw error;\n }\n }\n\n /**\n * Check if an error is a user error (expected behavior from server perspective).\n * User errors are NOT failures - just users making mistakes or validation issues.\n *\n * User errors (logged as OTHER, no stack trace):\n * - HttpBadRequestError (400)\n * - HttpUnauthorizedError (401)\n * - HttpForbiddenError (403)\n * - HttpNotFoundError (404)\n * - HttpUserError (266)\n *\n * @param error - The error to check\n * @returns true if this is a user error, false for server errors\n */\n static isUserError(error: unknown): boolean {\n return (\n error instanceof HttpBadRequestError ||\n error instanceof HttpUnauthorizedError ||\n error instanceof HttpForbiddenError ||\n error instanceof HttpNotFoundError ||\n error instanceof HttpUserError\n );\n }\n}\n"]}
@@ -1,14 +1,14 @@
1
1
  import { ContextKey } from '../ContextKey';
2
2
  /**
3
- * Core framework context keys for distributed tracing and request correlation.
3
+ * Core framework context keys the minimum the WebPieces framework needs to correlate one
4
+ * request across every service it touches, and across every log line each of them writes.
4
5
  *
5
- * These are the minimal keys the WebPieces framework needs for:
6
- * - Request tracking across services (REQUEST_ID -> PREVIOUS_REQUEST_ID chaining per hop)
7
- * - Request correlation
8
- * - Log correlation (each key logs under its `name`)
6
+ * ONE id, propagated unchanged. The first service to see a request without an `x-request-id`
7
+ * generates one (RequestContextHeaders.fillFromRequest); every hop copies it onward verbatim. Grep that id and you
8
+ * have the whole call tree. There is no per-hop id and no parent pointer: a chain of ids you must
9
+ * stitch back together buys nothing a single shared id does not already give you.
9
10
  *
10
- * Pattern inspired by Java MicroSvcHeader enum. Lives in core-util (browser-safe)
11
- * so BOTH the http-client (request-id chaining) and http-server can reference it.
11
+ * Lives in core-util (browser-safe) so both the http clients and http-server can reference it.
12
12
  *
13
13
  * Exposed as {@link HeaderRegistry.DEFAULT_HEADERS} — a service opts into these by
14
14
  * passing `platformHeaders=true` to `HeaderRegistry.configure(...)`.
@@ -17,35 +17,34 @@ import { ContextKey } from '../ContextKey';
17
17
  */
18
18
  export declare class WebpiecesCoreHeaders {
19
19
  /**
20
- * Unique ID for this request. Generated by the server if not provided.
21
- * Transferred (propagates downstream) and logged under 'requestId'.
20
+ * The id that correlates every hop of one request, and every log line of every hop.
21
+ * Generated by the first service to see a request without one; propagated unchanged after that.
22
22
  */
23
23
  static readonly REQUEST_ID: ContextKey;
24
- /**
25
- * ID of the previous request in the call chain. When service A calls B, B
26
- * receives A's REQUEST_ID as PREVIOUS_REQUEST_ID (builds the trace tree).
27
- */
28
- static readonly PREVIOUS_REQUEST_ID: ContextKey;
29
- /**
30
- * Correlation ID spanning multiple related requests. Set by the gateway or
31
- * first service; all hops in the chain share it.
32
- */
33
- static readonly CORRELATION_ID: ContextKey;
24
+ static readonly ORG_ID: ContextKey;
25
+ static readonly USER_ID: ContextKey;
26
+ static readonly USER_ROLES: ContextKey;
34
27
  /**
35
28
  * Turns on test-case recording for this request (Java: x-webpieces-recording).
36
29
  * Transferred so recording follows the request across service hops.
37
30
  */
38
31
  static readonly RECORDING: ContextKey;
39
32
  /**
40
- * The bearer credential for an authenticated request (user JWT or service OIDC
41
- * token). Transferred and SECURED — masked in logs.
42
- */
43
- static readonly AUTHORIZATION: ContextKey;
44
- /**
45
- * Shared-secret credential for internal callers that cannot mint OIDC tokens.
46
- * Transferred and SECURED — masked in logs.
33
+ * NO CREDENTIAL KEYS LIVE HERE.
34
+ *
35
+ * `authorization` and `x-webpieces-shared-secret` used to be ContextKeys. That made them
36
+ * TRANSFERRED keys, so the inbound transfer copied them off the request into the
37
+ * RequestContext, and every outbound RPC call and enqueued Cloud Task then carried the
38
+ * caller's credential onward to services that had no business seeing it.
39
+ *
40
+ * A credential belongs to ONE request hop. It is read straight off the {@link HttpRequest}
41
+ * by the framework AuthFilter, and written straight onto the outbound request by the client
42
+ * that mints it (NodeProxyClient, GcpTaskInvoker, InMemoryTaskInvoker). It never enters the
43
+ * magic context, so nothing can propagate it by accident.
44
+ *
45
+ * An app that genuinely wants a credential to travel can still register its own ContextKey for
46
+ * it — but that is now an explicit, visible decision rather than the default.
47
47
  */
48
- static readonly SHARED_SECRET: ContextKey;
49
48
  /**
50
49
  * Get all core context keys as an array (the platform DEFAULT_HEADERS set).
51
50
  */
@@ -3,15 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WebpiecesCoreHeaders = void 0;
4
4
  const ContextKey_1 = require("../ContextKey");
5
5
  /**
6
- * Core framework context keys for distributed tracing and request correlation.
6
+ * Core framework context keys the minimum the WebPieces framework needs to correlate one
7
+ * request across every service it touches, and across every log line each of them writes.
7
8
  *
8
- * These are the minimal keys the WebPieces framework needs for:
9
- * - Request tracking across services (REQUEST_ID -> PREVIOUS_REQUEST_ID chaining per hop)
10
- * - Request correlation
11
- * - Log correlation (each key logs under its `name`)
9
+ * ONE id, propagated unchanged. The first service to see a request without an `x-request-id`
10
+ * generates one (RequestContextHeaders.fillFromRequest); every hop copies it onward verbatim. Grep that id and you
11
+ * have the whole call tree. There is no per-hop id and no parent pointer: a chain of ids you must
12
+ * stitch back together buys nothing a single shared id does not already give you.
12
13
  *
13
- * Pattern inspired by Java MicroSvcHeader enum. Lives in core-util (browser-safe)
14
- * so BOTH the http-client (request-id chaining) and http-server can reference it.
14
+ * Lives in core-util (browser-safe) so both the http clients and http-server can reference it.
15
15
  *
16
16
  * Exposed as {@link HeaderRegistry.DEFAULT_HEADERS} — a service opts into these by
17
17
  * passing `platformHeaders=true` to `HeaderRegistry.configure(...)`.
@@ -20,46 +20,44 @@ const ContextKey_1 = require("../ContextKey");
20
20
  */
21
21
  class WebpiecesCoreHeaders {
22
22
  /**
23
- * Unique ID for this request. Generated by the server if not provided.
24
- * Transferred (propagates downstream) and logged under 'requestId'.
23
+ * The id that correlates every hop of one request, and every log line of every hop.
24
+ * Generated by the first service to see a request without one; propagated unchanged after that.
25
25
  */
26
26
  static REQUEST_ID = new ContextKey_1.ContextKey('requestId', 'x-request-id');
27
- /**
28
- * ID of the previous request in the call chain. When service A calls B, B
29
- * receives A's REQUEST_ID as PREVIOUS_REQUEST_ID (builds the trace tree).
30
- */
31
- static PREVIOUS_REQUEST_ID = new ContextKey_1.ContextKey('previousId', 'x-previous-request-id');
32
- /**
33
- * Correlation ID spanning multiple related requests. Set by the gateway or
34
- * first service; all hops in the chain share it.
35
- */
36
- static CORRELATION_ID = new ContextKey_1.ContextKey('correlationId', 'x-correlation-id');
27
+ static ORG_ID = new ContextKey_1.ContextKey('orgId', 'x-org-id');
28
+ static USER_ID = new ContextKey_1.ContextKey('userId', 'x-user-id');
29
+ static USER_ROLES = new ContextKey_1.ContextKey('roles', 'x-webpieces-roles');
37
30
  /**
38
31
  * Turns on test-case recording for this request (Java: x-webpieces-recording).
39
32
  * Transferred so recording follows the request across service hops.
40
33
  */
41
34
  static RECORDING = new ContextKey_1.ContextKey('recording', 'x-webpieces-recording');
42
35
  /**
43
- * The bearer credential for an authenticated request (user JWT or service OIDC
44
- * token). Transferred and SECURED — masked in logs.
45
- */
46
- static AUTHORIZATION = new ContextKey_1.ContextKey('authorization', 'authorization', /*isSecured*/ true);
47
- /**
48
- * Shared-secret credential for internal callers that cannot mint OIDC tokens.
49
- * Transferred and SECURED — masked in logs.
36
+ * NO CREDENTIAL KEYS LIVE HERE.
37
+ *
38
+ * `authorization` and `x-webpieces-shared-secret` used to be ContextKeys. That made them
39
+ * TRANSFERRED keys, so the inbound transfer copied them off the request into the
40
+ * RequestContext, and every outbound RPC call and enqueued Cloud Task then carried the
41
+ * caller's credential onward to services that had no business seeing it.
42
+ *
43
+ * A credential belongs to ONE request hop. It is read straight off the {@link HttpRequest}
44
+ * by the framework AuthFilter, and written straight onto the outbound request by the client
45
+ * that mints it (NodeProxyClient, GcpTaskInvoker, InMemoryTaskInvoker). It never enters the
46
+ * magic context, so nothing can propagate it by accident.
47
+ *
48
+ * An app that genuinely wants a credential to travel can still register its own ContextKey for
49
+ * it — but that is now an explicit, visible decision rather than the default.
50
50
  */
51
- static SHARED_SECRET = new ContextKey_1.ContextKey('sharedSecret', 'x-webpieces-shared-secret', /*isSecured*/ true);
52
51
  /**
53
52
  * Get all core context keys as an array (the platform DEFAULT_HEADERS set).
54
53
  */
55
54
  static getAllHeaders() {
56
55
  return [
57
56
  WebpiecesCoreHeaders.REQUEST_ID,
58
- WebpiecesCoreHeaders.PREVIOUS_REQUEST_ID,
59
- WebpiecesCoreHeaders.CORRELATION_ID,
57
+ WebpiecesCoreHeaders.USER_ID,
58
+ WebpiecesCoreHeaders.ORG_ID,
59
+ WebpiecesCoreHeaders.USER_ROLES,
60
60
  WebpiecesCoreHeaders.RECORDING,
61
- WebpiecesCoreHeaders.AUTHORIZATION,
62
- WebpiecesCoreHeaders.SHARED_SECRET,
63
61
  ];
64
62
  }
65
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"WebpiecesCoreHeaders.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/WebpiecesCoreHeaders.ts"],"names":[],"mappings":";;;AAAA,8CAA2C;AAE3C;;;;;;;;;;;;;;;GAeG;AACH,MAAa,oBAAoB;IAC7B;;;OAGG;IACH,MAAM,CAAU,UAAU,GAAG,IAAI,uBAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IAEzE;;;OAGG;IACH,MAAM,CAAU,mBAAmB,GAAG,IAAI,uBAAU,CAAC,YAAY,EAAE,uBAAuB,CAAC,CAAC;IAE5F;;;OAGG;IACH,MAAM,CAAU,cAAc,GAAG,IAAI,uBAAU,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAC;IAErF;;;OAGG;IACH,MAAM,CAAU,SAAS,GAAG,IAAI,uBAAU,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC;IAEjF;;;OAGG;IACH,MAAM,CAAU,aAAa,GAAG,IAAI,uBAAU,CAAC,eAAe,EAAE,eAAe,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAErG;;;OAGG;IACH,MAAM,CAAU,aAAa,GAAG,IAAI,uBAAU,CAAC,cAAc,EAAE,2BAA2B,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAEhH;;OAEG;IACH,MAAM,CAAC,aAAa;QAChB,OAAO;YACH,oBAAoB,CAAC,UAAU;YAC/B,oBAAoB,CAAC,mBAAmB;YACxC,oBAAoB,CAAC,cAAc;YACnC,oBAAoB,CAAC,SAAS;YAC9B,oBAAoB,CAAC,aAAa;YAClC,oBAAoB,CAAC,aAAa;SACrC,CAAC;IACN,CAAC;;AAjDL,oDAkDC","sourcesContent":["import { ContextKey } from '../ContextKey';\n\n/**\n * Core framework context keys for distributed tracing and request correlation.\n *\n * These are the minimal keys the WebPieces framework needs for:\n * - Request tracking across services (REQUEST_ID -> PREVIOUS_REQUEST_ID chaining per hop)\n * - Request correlation\n * - Log correlation (each key logs under its `name`)\n *\n * Pattern inspired by Java MicroSvcHeader enum. Lives in core-util (browser-safe)\n * so BOTH the http-client (request-id chaining) and http-server can reference it.\n *\n * Exposed as {@link HeaderRegistry.DEFAULT_HEADERS} — a service opts into these by\n * passing `platformHeaders=true` to `HeaderRegistry.configure(...)`.\n *\n * Each key's `name` is the logical/log name; `httpHeader` is the wire name.\n */\nexport class WebpiecesCoreHeaders {\n /**\n * Unique ID for this request. Generated by the server if not provided.\n * Transferred (propagates downstream) and logged under 'requestId'.\n */\n static readonly REQUEST_ID = new ContextKey('requestId', 'x-request-id');\n\n /**\n * ID of the previous request in the call chain. When service A calls B, B\n * receives A's REQUEST_ID as PREVIOUS_REQUEST_ID (builds the trace tree).\n */\n static readonly PREVIOUS_REQUEST_ID = new ContextKey('previousId', 'x-previous-request-id');\n\n /**\n * Correlation ID spanning multiple related requests. Set by the gateway or\n * first service; all hops in the chain share it.\n */\n static readonly CORRELATION_ID = new ContextKey('correlationId', 'x-correlation-id');\n\n /**\n * Turns on test-case recording for this request (Java: x-webpieces-recording).\n * Transferred so recording follows the request across service hops.\n */\n static readonly RECORDING = new ContextKey('recording', 'x-webpieces-recording');\n\n /**\n * The bearer credential for an authenticated request (user JWT or service OIDC\n * token). Transferred and SECUREDmasked in logs.\n */\n static readonly AUTHORIZATION = new ContextKey('authorization', 'authorization', /*isSecured*/ true);\n\n /**\n * Shared-secret credential for internal callers that cannot mint OIDC tokens.\n * Transferred and SECURED masked in logs.\n */\n static readonly SHARED_SECRET = new ContextKey('sharedSecret', 'x-webpieces-shared-secret', /*isSecured*/ true);\n\n /**\n * Get all core context keys as an array (the platform DEFAULT_HEADERS set).\n */\n static getAllHeaders(): ContextKey[] {\n return [\n WebpiecesCoreHeaders.REQUEST_ID,\n WebpiecesCoreHeaders.PREVIOUS_REQUEST_ID,\n WebpiecesCoreHeaders.CORRELATION_ID,\n WebpiecesCoreHeaders.RECORDING,\n WebpiecesCoreHeaders.AUTHORIZATION,\n WebpiecesCoreHeaders.SHARED_SECRET,\n ];\n }\n}\n"]}
1
+ {"version":3,"file":"WebpiecesCoreHeaders.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/WebpiecesCoreHeaders.ts"],"names":[],"mappings":";;;AAAA,8CAA2C;AAE3C;;;;;;;;;;;;;;;GAeG;AACH,MAAa,oBAAoB;IAC7B;;;OAGG;IACH,MAAM,CAAU,UAAU,GAAG,IAAI,uBAAU,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IAEzE,MAAM,CAAU,MAAM,GAAG,IAAI,uBAAU,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAE7D,MAAM,CAAU,OAAO,GAAG,IAAI,uBAAU,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAEhE,MAAM,CAAU,UAAU,GAAG,IAAI,uBAAU,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;IAE1E;;;OAGG;IACH,MAAM,CAAU,SAAS,GAAG,IAAI,uBAAU,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC;IAEjF;;;;;;;;;;;;;;;OAeG;IAEH;;OAEG;IACH,MAAM,CAAC,aAAa;QAChB,OAAO;YACH,oBAAoB,CAAC,UAAU;YAC/B,oBAAoB,CAAC,OAAO;YAC5B,oBAAoB,CAAC,MAAM;YAC3B,oBAAoB,CAAC,UAAU;YAC/B,oBAAoB,CAAC,SAAS;SACjC,CAAC;IACN,CAAC;;AA/CL,oDAgDC","sourcesContent":["import { ContextKey } from '../ContextKey';\n\n/**\n * Core framework context keys the minimum the WebPieces framework needs to correlate one\n * request across every service it touches, and across every log line each of them writes.\n *\n * ONE id, propagated unchanged. The first service to see a request without an `x-request-id`\n * generates one (RequestContextHeaders.fillFromRequest); every hop copies it onward verbatim. Grep that id and you\n * have the whole call tree. There is no per-hop id and no parent pointer: a chain of ids you must\n * stitch back together buys nothing a single shared id does not already give you.\n *\n * Lives in core-util (browser-safe) so both the http clients and http-server can reference it.\n *\n * Exposed as {@link HeaderRegistry.DEFAULT_HEADERS} — a service opts into these by\n * passing `platformHeaders=true` to `HeaderRegistry.configure(...)`.\n *\n * Each key's `name` is the logical/log name; `httpHeader` is the wire name.\n */\nexport class WebpiecesCoreHeaders {\n /**\n * The id that correlates every hop of one request, and every log line of every hop.\n * Generated by the first service to see a request without one; propagated unchanged after that.\n */\n static readonly REQUEST_ID = new ContextKey('requestId', 'x-request-id');\n\n static readonly ORG_ID = new ContextKey('orgId', 'x-org-id');\n\n static readonly USER_ID = new ContextKey('userId', 'x-user-id');\n\n static readonly USER_ROLES = new ContextKey('roles', 'x-webpieces-roles');\n\n /**\n * Turns on test-case recording for this request (Java: x-webpieces-recording).\n * Transferred so recording follows the request across service hops.\n */\n static readonly RECORDING = new ContextKey('recording', 'x-webpieces-recording');\n\n /**\n * NO CREDENTIAL KEYS LIVE HERE.\n *\n * `authorization` and `x-webpieces-shared-secret` used to be ContextKeys. That made them\n * TRANSFERRED keys, so the inbound transfer copied them off the request into the\n * RequestContext, and every outbound RPC call and enqueued Cloud Task then carried the\n * caller's credential onward to services that had no business seeing it.\n *\n * A credential belongs to ONE request hop. It is read straight off the {@link HttpRequest}\n * by the framework AuthFilter, and written straight onto the outbound request by the client\n * that mints it (NodeProxyClient, GcpTaskInvoker, InMemoryTaskInvoker). It never enters the\n * magic context, so nothing can propagate it by accident.\n *\n * An app that genuinely wants a credential to travel can still register its own ContextKey for\n * it — but that is now an explicit, visible decision rather than the default.\n */\n\n /**\n * Get all core context keys as an array (the platform DEFAULT_HEADERS set).\n */\n static getAllHeaders(): ContextKey[] {\n return [\n WebpiecesCoreHeaders.REQUEST_ID,\n WebpiecesCoreHeaders.USER_ID,\n WebpiecesCoreHeaders.ORG_ID,\n WebpiecesCoreHeaders.USER_ROLES,\n WebpiecesCoreHeaders.RECORDING,\n ];\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -23,10 +23,9 @@ export { ProtocolError, HttpError, HttpNotFoundError, EndpointNotFoundError, Htt
23
23
  export { InstantDto, DateDto, TimeDto, DateTimeDto, InstantUtil, DateUtil, TimeUtil, DateTimeUtil, } from './http/datetime';
24
24
  export { HeaderRegistry } from './http/HeaderRegistry';
25
25
  export { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';
26
- export { HeaderMethods } from './http/HeaderMethods';
27
26
  export { ContextReader } from './http/ContextReader';
27
+ export type { ContextRead } from './http/ContextReader';
28
28
  export { ContextMgr } from './http/ContextMgr';
29
- export { RequestIdChainProcessor } from './http/RequestIdChainProcessor';
30
29
  export { LogApiCall } from './http/LogApiCall';
31
30
  export { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';
32
31
  export { RecordedEndpoint, RecordedError, RecordedTestCase } from './http/recorder/RecordedEndpoint';
package/src/index.js CHANGED
@@ -9,7 +9,7 @@
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.ENTITY_NOT_FOUND = exports.HttpUserError = exports.HttpVendorError = exports.HttpInternalServerError = exports.HttpGatewayTimeoutError = exports.HttpBadGatewayError = exports.HttpTimeoutError = exports.HttpForbiddenError = exports.HttpUnauthorizedError = exports.HttpBadRequestError = exports.EndpointNotFoundError = exports.HttpNotFoundError = exports.HttpError = exports.ProtocolError = exports.Secrets = exports.METADATA_KEYS = exports.RouteMetadata = exports.AuthMeta = exports.validateNoConflictingDecorators = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.assertEveryEndpointHasAuthMode = exports.getAuthMode = exports.getAuthMeta = exports.isApiPath = exports.getEndpoints = exports.getApiPath = exports.Queue = exports.PubSub = exports.Rpc = exports.AuthSharedSecret = exports.AuthOidc = exports.Auth = exports.AuthJwt = exports.Public = exports.AuthenticationConfig = exports.Authentication = exports.Endpoint = exports.ApiPath = exports.LogManager = exports.ConsoleLoggerFactory = exports.ConsoleLogger = exports.DESIGN_METADATA_KEYS = exports.isDocumentDesign = exports.DocumentDesign = exports.ContextTuple = exports.ContextKey = exports.toError = void 0;
12
- exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.LogApiCall = exports.RequestIdChainProcessor = exports.ContextMgr = exports.HeaderMethods = exports.WebpiecesCoreHeaders = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = void 0;
12
+ exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.LogApiCall = exports.ContextMgr = exports.WebpiecesCoreHeaders = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = void 0;
13
13
  var errorUtils_1 = require("./lib/errorUtils");
14
14
  Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return errorUtils_1.toError; } });
15
15
  var ContextKey_1 = require("./ContextKey");
@@ -102,16 +102,11 @@ var HeaderRegistry_1 = require("./http/HeaderRegistry");
102
102
  Object.defineProperty(exports, "HeaderRegistry", { enumerable: true, get: function () { return HeaderRegistry_1.HeaderRegistry; } });
103
103
  var WebpiecesCoreHeaders_1 = require("./http/WebpiecesCoreHeaders");
104
104
  Object.defineProperty(exports, "WebpiecesCoreHeaders", { enumerable: true, get: function () { return WebpiecesCoreHeaders_1.WebpiecesCoreHeaders; } });
105
- var HeaderMethods_1 = require("./http/HeaderMethods");
106
- Object.defineProperty(exports, "HeaderMethods", { enumerable: true, get: function () { return HeaderMethods_1.HeaderMethods; } });
107
- // Outbound-header machinery (context reader + registry -> outbound HTTP headers).
108
- // Browser-safe — the server-side reader (RequestContextReader) and browser store
109
- // (MutableContextStore) both implement ContextReader, so this lives here rather
110
- // than in the Node-only @webpieces/core-context (which re-exports for back-compat).
105
+ // BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).
106
+ // Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via
107
+ // RequestContextHeaders in the Node-only @webpieces/core-context.
111
108
  var ContextMgr_1 = require("./http/ContextMgr");
112
109
  Object.defineProperty(exports, "ContextMgr", { enumerable: true, get: function () { return ContextMgr_1.ContextMgr; } });
113
- var RequestIdChainProcessor_1 = require("./http/RequestIdChainProcessor");
114
- Object.defineProperty(exports, "RequestIdChainProcessor", { enumerable: true, get: function () { return RequestIdChainProcessor_1.RequestIdChainProcessor; } });
115
110
  // API-call logging helper (uses LogManager above)
116
111
  var LogApiCall_1 = require("./http/LogApiCall");
117
112
  Object.defineProperty(exports, "LogApiCall", { enumerable: true, get: function () { return LogApiCall_1.LogApiCall; } });
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;AAEH,+CAA2C;AAAlC,qGAAA,OAAO,OAAA;AAChB,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,+EAA+E;AAC/E,kFAAkF;AAClF,yCAAyC;AACzC,mDAA0F;AAAjF,gHAAA,cAAc,OAAA;AAAE,kHAAA,gBAAgB,OAAA;AAAE,sHAAA,oBAAoB,OAAA;AAO/D,yDAAwD;AAA/C,8GAAA,aAAa,OAAA;AACtB,uEAAsE;AAA7D,4HAAA,oBAAoB,OAAA;AAC7B,mDAAkD;AAAzC,wGAAA,UAAU,OAAA;AAEnB,8DAA8D;AAC9D,sEAAsE;AACtE,sEAAsE;AACtE,uEAAuE;AACvE,kEAAkE;AAElE,4BAA4B;AAC5B,gDA6B2B;AA5BvB,qGAAA,OAAO,OAAA;AACP,sGAAA,QAAQ,OAAA;AACR,4GAAA,cAAc,OAAA;AACd,kHAAA,oBAAoB,OAAA;AACpB,mEAAmE;AACnE,oGAAA,MAAM,OAAA;AACN,qGAAA,OAAO,OAAA;AACP,kGAAA,IAAI,OAAA;AACJ,sGAAA,QAAQ,OAAA;AACR,8GAAA,gBAAgB,OAAA;AAChB,sDAAsD;AACtD,iGAAA,GAAG,OAAA;AACH,oGAAA,MAAM,OAAA;AACN,mGAAA,KAAK,OAAA;AACL,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AACZ,uGAAA,SAAS,OAAA;AACT,yGAAA,WAAW,OAAA;AACX,yGAAA,WAAW,OAAA;AACX,4HAAA,8BAA8B,OAAA;AAC9B,wGAAA,UAAU,OAAA;AACV,2GAAA,aAAa,OAAA;AACb,qHAAA,uBAAuB,OAAA;AACvB,0GAAA,YAAY,OAAA;AACZ,6HAAA,+BAA+B,OAAA;AAC/B,sGAAA,QAAQ,OAAA;AACR,2GAAA,aAAa,OAAA;AACb,2GAAA,aAAa,OAAA;AAGjB,4FAA4F;AAC5F,0CAAyC;AAAhC,kGAAA,OAAO,OAAA;AAKhB,cAAc;AACd,wCAuBuB;AAtBnB,uGAAA,aAAa,OAAA;AACb,mGAAA,SAAS,OAAA;AACT,2GAAA,iBAAiB,OAAA;AACjB,+GAAA,qBAAqB,OAAA;AACrB,6GAAA,mBAAmB,OAAA;AACnB,+GAAA,qBAAqB,OAAA;AACrB,4GAAA,kBAAkB,OAAA;AAClB,0GAAA,gBAAgB,OAAA;AAChB,6GAAA,mBAAmB,OAAA;AACnB,iHAAA,uBAAuB,OAAA;AACvB,iHAAA,uBAAuB,OAAA;AACvB,yGAAA,eAAe,OAAA;AACf,uGAAA,aAAa,OAAA;AACb,0BAA0B;AAC1B,0GAAA,gBAAgB,OAAA;AAChB,0GAAA,gBAAgB,OAAA;AAChB,qGAAA,WAAW,OAAA;AACX,sGAAA,YAAY,OAAA;AACZ,6GAAA,mBAAmB,OAAA;AACnB,sGAAA,YAAY,OAAA;AACZ,uGAAA,aAAa,OAAA;AACb,qGAAA,WAAW,OAAA;AAGf,iEAAiE;AACjE,4CASyB;AAJrB,uGAAA,WAAW,OAAA;AACX,oGAAA,QAAQ,OAAA;AACR,oGAAA,QAAQ,OAAA;AACR,wGAAA,YAAY,OAAA;AAGhB,mEAAmE;AACnE,wDAAuD;AAA9C,gHAAA,cAAc,OAAA;AACvB,oEAAmE;AAA1D,4HAAA,oBAAoB,OAAA;AAC7B,sDAAqD;AAA5C,8GAAA,aAAa,OAAA;AAGtB,kFAAkF;AAClF,iFAAiF;AACjF,gFAAgF;AAChF,oFAAoF;AACpF,gDAA+C;AAAtC,wGAAA,UAAU,OAAA;AACnB,0EAAyE;AAAhE,kIAAA,uBAAuB,OAAA;AAEhC,kDAAkD;AAClD,gDAA+C;AAAtC,wGAAA,UAAU,OAAA;AAEnB,iFAAiF;AACjF,qEAAkF;AAAvD,gHAAA,YAAY,OAAA;AACvC,qEAAqG;AAA5F,oHAAA,gBAAgB,OAAA;AAAE,iHAAA,aAAa,OAAA;AAAE,oHAAA,gBAAgB,OAAA;AAC1D,2DAAgF;AAAvE,0GAAA,WAAW,OAAA;AAAE,mHAAA,oBAAoB,OAAA;AAC1C,qEAAoG;AAA3F,oHAAA,gBAAgB,OAAA;AAAE,iHAAA,aAAa,OAAA;AAAE,mHAAA,eAAe,OAAA","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the HttpError hierarchy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n Authentication,\n AuthenticationConfig,\n // Auth mode decorators (clean service-to-service + user JWT model)\n Public,\n AuthJwt,\n Auth,\n AuthOidc,\n AuthSharedSecret,\n // API kind (RPC vs PubSub/Cloud Tasks) + queue naming\n Rpc,\n PubSub,\n Queue,\n getApiPath,\n getEndpoints,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n validateNoConflictingDecorators,\n AuthMeta,\n RouteMetadata,\n METADATA_KEYS,\n} from './http/decorators';\nexport type { AuthMode, ApiKind, JwtRequirement } from './http/decorators';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { Secrets } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// HTTP errors\nexport {\n ProtocolError,\n HttpError,\n HttpNotFoundError,\n EndpointNotFoundError,\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpTimeoutError,\n HttpBadGatewayError,\n HttpGatewayTimeoutError,\n HttpInternalServerError,\n HttpVendorError,\n HttpUserError,\n // Error subtype constants\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './http/errors';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { HeaderMethods } from './http/HeaderMethods';\nexport { ContextReader } from './http/ContextReader';\n\n// Outbound-header machinery (context reader + registry -> outbound HTTP headers).\n// Browser-safe — the server-side reader (RequestContextReader) and browser store\n// (MutableContextStore) both implement ContextReader, so this lives here rather\n// than in the Node-only @webpieces/core-context (which re-exports for back-compat).\nexport { ContextMgr } from './http/ContextMgr';\nexport { RequestIdChainProcessor } from './http/RequestIdChainProcessor';\n\n// API-call logging helper (uses LogManager above)\nexport { LogApiCall } from './http/LogApiCall';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport { RecordedEndpoint, RecordedError, RecordedTestCase } from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the HttpError hierarchy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n Authentication,\n AuthenticationConfig,\n // Auth mode decorators (clean service-to-service + user JWT model)\n Public,\n AuthJwt,\n Auth,\n AuthOidc,\n AuthSharedSecret,\n // API kind (RPC vs PubSub/Cloud Tasks) + queue naming\n Rpc,\n PubSub,\n Queue,\n getApiPath,\n getEndpoints,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n validateNoConflictingDecorators,\n AuthMeta,\n RouteMetadata,\n METADATA_KEYS,\n} from './http/decorators';\nexport type { AuthMode, ApiKind, JwtRequirement } from './http/decorators';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { Secrets } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// HTTP errors\nexport {\n ProtocolError,\n HttpError,\n HttpNotFoundError,\n EndpointNotFoundError,\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpTimeoutError,\n HttpBadGatewayError,\n HttpGatewayTimeoutError,\n HttpInternalServerError,\n HttpVendorError,\n HttpUserError,\n // Error subtype constants\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './http/errors';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport type { ContextRead } from './http/ContextReader';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above)\nexport { LogApiCall } from './http/LogApiCall';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport { RecordedEndpoint, RecordedError, RecordedTestCase } from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n"]}
@@ -7,6 +7,10 @@ import { Logger } from './Logger';
7
7
  * name so multi-source logs stay greppable. An optional `Error` is forwarded to
8
8
  * `console.*` as a second argument so its stack trace is rendered.
9
9
  *
10
+ * It carries NO context fields (requestId, tenantId, …) — deliberately. A real logging backend
11
+ * (bunyan/winston) reads them off the RequestContext on every record; the bootstrap console
12
+ * cannot, because core-util is browser-safe and has no AsyncLocalStorage.
13
+ *
10
14
  * Level → console method mapping:
11
15
  * - trace/debug → console.debug
12
16
  * - info → console.log (stdout, matching conventional server logging)
@@ -22,6 +26,12 @@ export declare class ConsoleLogger implements Logger {
22
26
  * been installed (see {@link ConsoleLoggerFactory}); empty once one is.
23
27
  */
24
28
  constructor(name: string, bootstrapPrefix?: string);
29
+ /**
30
+ * Complain ONCE, at error level, that no logging backend was installed — a banner on every
31
+ * line is easy to scroll past, and an app running on the bootstrap console silently loses
32
+ * structured context fields and its GCP log payload. Every subsequent line keeps the banner.
33
+ */
34
+ private reportMissingFactoryOnce;
25
35
  private prefix;
26
36
  private errArg;
27
37
  trace(message: string, err?: Error): void;
@@ -1,6 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConsoleLogger = void 0;
4
+ /**
5
+ * Latch so the "you never called setFactory" complaint is emitted exactly ONCE per process,
6
+ * no matter how many bootstrap loggers or lines there are. Mirrors RequestContext's
7
+ * reportedMissingContext latch.
8
+ */
9
+ let reportedMissingFactory = false;
4
10
  /**
5
11
  * ConsoleLogger - the default, browser-safe {@link Logger} implementation.
6
12
  *
@@ -9,6 +15,10 @@ exports.ConsoleLogger = void 0;
9
15
  * name so multi-source logs stay greppable. An optional `Error` is forwarded to
10
16
  * `console.*` as a second argument so its stack trace is rendered.
11
17
  *
18
+ * It carries NO context fields (requestId, tenantId, …) — deliberately. A real logging backend
19
+ * (bunyan/winston) reads them off the RequestContext on every record; the bootstrap console
20
+ * cannot, because core-util is browser-safe and has no AsyncLocalStorage.
21
+ *
12
22
  * Level → console method mapping:
13
23
  * - trace/debug → console.debug
14
24
  * - info → console.log (stdout, matching conventional server logging)
@@ -27,7 +37,22 @@ class ConsoleLogger {
27
37
  this.name = name;
28
38
  this.bootstrapPrefix = bootstrapPrefix;
29
39
  }
40
+ /**
41
+ * Complain ONCE, at error level, that no logging backend was installed — a banner on every
42
+ * line is easy to scroll past, and an app running on the bootstrap console silently loses
43
+ * structured context fields and its GCP log payload. Every subsequent line keeps the banner.
44
+ */
45
+ reportMissingFactoryOnce() {
46
+ if (!this.bootstrapPrefix || reportedMissingFactory) {
47
+ return;
48
+ }
49
+ reportedMissingFactory = true; // set BEFORE logging: console.error must not re-enter
50
+ console.error('No logging backend installed. Call LogManager.setFactory(...) at startup — ' +
51
+ 'see .webpieces/instruct-ai/webpieces.logging.md. Until then every line goes to the ' +
52
+ 'bootstrap console with NO context fields (requestId, tenantId, …) and no structured payload.');
53
+ }
30
54
  prefix() {
55
+ this.reportMissingFactoryOnce();
31
56
  return `${this.bootstrapPrefix}[${this.name}]`;
32
57
  }
33
58
  errArg(err) {
@@ -1 +1 @@
1
- {"version":3,"file":"ConsoleLogger.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/logging/ConsoleLogger.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;GAaG;AACH,MAAa,aAAa;IACL,IAAI,CAAS;IACb,eAAe,CAAS;IAEzC;;;;OAIG;IACH,YAAY,IAAY,EAAE,eAAe,GAAG,EAAE;QAC1C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IAC3C,CAAC;IAEO,MAAM;QACV,OAAO,GAAG,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC;IACnD,CAAC;IAEO,MAAM,CAAC,GAAW;QACtB,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAW;QAC7B,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAW;QAC7B,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;CACJ;AAzCD,sCAyCC","sourcesContent":["import { Logger } from './Logger';\n\n/**\n * ConsoleLogger - the default, browser-safe {@link Logger} implementation.\n *\n * Backed purely by `console.*` (no Node imports), so it works unchanged in the\n * browser (Angular/React) and in Node. Each line is prefixed with the logger\n * name so multi-source logs stay greppable. An optional `Error` is forwarded to\n * `console.*` as a second argument so its stack trace is rendered.\n *\n * Level → console method mapping:\n * - trace/debug → console.debug\n * - info → console.log (stdout, matching conventional server logging)\n * - warn → console.warn\n * - error → console.error\n */\nexport class ConsoleLogger implements Logger {\n private readonly name: string;\n private readonly bootstrapPrefix: string;\n\n /**\n * @param name logger name (slf4j-style; usually the class/module)\n * @param bootstrapPrefix prepended to every line while no real backend has\n * been installed (see {@link ConsoleLoggerFactory}); empty once one is.\n */\n constructor(name: string, bootstrapPrefix = '') {\n this.name = name;\n this.bootstrapPrefix = bootstrapPrefix;\n }\n\n private prefix(): string {\n return `${this.bootstrapPrefix}[${this.name}]`;\n }\n\n private errArg(err?: Error): Error[] {\n return err ? [err] : [];\n }\n\n trace(message: string, err?: Error): void {\n console.debug(`${this.prefix()} ${message}`, ...this.errArg(err));\n }\n\n debug(message: string, err?: Error): void {\n console.debug(`${this.prefix()} ${message}`, ...this.errArg(err));\n }\n\n info(message: string, err?: Error): void {\n console.log(`${this.prefix()} ${message}`, ...this.errArg(err));\n }\n\n warn(message: string, err?: Error): void {\n console.warn(`${this.prefix()} ${message}`, ...this.errArg(err));\n }\n\n error(message: string, err?: Error): void {\n console.error(`${this.prefix()} ${message}`, ...this.errArg(err));\n }\n}\n"]}
1
+ {"version":3,"file":"ConsoleLogger.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/logging/ConsoleLogger.ts"],"names":[],"mappings":";;;AAEA;;;;GAIG;AACH,IAAI,sBAAsB,GAAG,KAAK,CAAC;AAEnC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAa,aAAa;IACL,IAAI,CAAS;IACb,eAAe,CAAS;IAEzC;;;;OAIG;IACH,YAAY,IAAY,EAAE,eAAe,GAAG,EAAE;QAC1C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IAC3C,CAAC;IAED;;;;OAIG;IACK,wBAAwB;QAC5B,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,sBAAsB,EAAE,CAAC;YAClD,OAAO;QACX,CAAC;QACD,sBAAsB,GAAG,IAAI,CAAC,CAAC,sDAAsD;QACrF,OAAO,CAAC,KAAK,CACT,6EAA6E;YAC7E,qFAAqF;YACrF,8FAA8F,CACjG,CAAC;IACN,CAAC;IAEO,MAAM;QACV,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAChC,OAAO,GAAG,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC;IACnD,CAAC;IAEO,MAAM,CAAC,GAAW;QACtB,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAW;QAC7B,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,GAAW;QAC7B,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,GAAW;QAC9B,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,CAAC;CACJ;AA3DD,sCA2DC","sourcesContent":["import { Logger } from './Logger';\n\n/**\n * Latch so the \"you never called setFactory\" complaint is emitted exactly ONCE per process,\n * no matter how many bootstrap loggers or lines there are. Mirrors RequestContext's\n * reportedMissingContext latch.\n */\nlet reportedMissingFactory = false;\n\n/**\n * ConsoleLogger - the default, browser-safe {@link Logger} implementation.\n *\n * Backed purely by `console.*` (no Node imports), so it works unchanged in the\n * browser (Angular/React) and in Node. Each line is prefixed with the logger\n * name so multi-source logs stay greppable. An optional `Error` is forwarded to\n * `console.*` as a second argument so its stack trace is rendered.\n *\n * It carries NO context fields (requestId, tenantId, …) — deliberately. A real logging backend\n * (bunyan/winston) reads them off the RequestContext on every record; the bootstrap console\n * cannot, because core-util is browser-safe and has no AsyncLocalStorage.\n *\n * Level → console method mapping:\n * - trace/debug → console.debug\n * - info → console.log (stdout, matching conventional server logging)\n * - warn → console.warn\n * - error → console.error\n */\nexport class ConsoleLogger implements Logger {\n private readonly name: string;\n private readonly bootstrapPrefix: string;\n\n /**\n * @param name logger name (slf4j-style; usually the class/module)\n * @param bootstrapPrefix prepended to every line while no real backend has\n * been installed (see {@link ConsoleLoggerFactory}); empty once one is.\n */\n constructor(name: string, bootstrapPrefix = '') {\n this.name = name;\n this.bootstrapPrefix = bootstrapPrefix;\n }\n\n /**\n * Complain ONCE, at error level, that no logging backend was installed — a banner on every\n * line is easy to scroll past, and an app running on the bootstrap console silently loses\n * structured context fields and its GCP log payload. Every subsequent line keeps the banner.\n */\n private reportMissingFactoryOnce(): void {\n if (!this.bootstrapPrefix || reportedMissingFactory) {\n return;\n }\n reportedMissingFactory = true; // set BEFORE logging: console.error must not re-enter\n console.error(\n 'No logging backend installed. Call LogManager.setFactory(...) at startup — ' +\n 'see .webpieces/instruct-ai/webpieces.logging.md. Until then every line goes to the ' +\n 'bootstrap console with NO context fields (requestId, tenantId, …) and no structured payload.',\n );\n }\n\n private prefix(): string {\n this.reportMissingFactoryOnce();\n return `${this.bootstrapPrefix}[${this.name}]`;\n }\n\n private errArg(err?: Error): Error[] {\n return err ? [err] : [];\n }\n\n trace(message: string, err?: Error): void {\n console.debug(`${this.prefix()} ${message}`, ...this.errArg(err));\n }\n\n debug(message: string, err?: Error): void {\n console.debug(`${this.prefix()} ${message}`, ...this.errArg(err));\n }\n\n info(message: string, err?: Error): void {\n console.log(`${this.prefix()} ${message}`, ...this.errArg(err));\n }\n\n warn(message: string, err?: Error): void {\n console.warn(`${this.prefix()} ${message}`, ...this.errArg(err));\n }\n\n error(message: string, err?: Error): void {\n console.error(`${this.prefix()} ${message}`, ...this.errArg(err));\n }\n}\n"]}
@@ -21,7 +21,7 @@ export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
21
21
  * There is deliberately no structured-fields / varargs argument — so nobody can
22
22
  * pass request ids, tenant ids, or other platform-header values into a log line.
23
23
  * Those are already emitted automatically by the framework (see
24
- * `HeaderMethods.buildSecureMapForLogs`); duplicating them here is impossible.
24
+ * `HeaderRegistry.buildLogFields`); duplicating them here is impossible.
25
25
  * See `.webpieces/instruct-ai/webpieces.logging.md`.
26
26
  */
27
27
  export interface Logger {
@@ -1 +1 @@
1
- {"version":3,"file":"Logger.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/logging/Logger.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Log severity levels, ordered lowest → highest.\n *\n * Mirrors the common slf4j/bunyan/winston vocabulary so any of those backends\n * can be plugged in behind the {@link Logger} interface.\n */\nexport type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';\n\n/**\n * Logger - the pluggable logging contract for WebPieces.\n *\n * This is a BUSINESS-LOGIC interface (methods with behavior), so per the\n * webpieces guidelines it is an `interface`, not a class. Different projects\n * plug in different backends (bunyan, winston, pino, browser console, a\n * file writer, ...) by supplying an implementation via a {@link LoggerFactory}.\n *\n * Implementations MUST stay browser-safe if they are to be used from Angular /\n * React. Node-only backends (bunyan, file writers, ...) are wired in by\n * `framework:express` apps at startup, never in browser-safe libraries.\n *\n * KISS by design: every method takes ONLY a message plus an OPTIONAL `Error`.\n * There is deliberately no structured-fields / varargs argument — so nobody can\n * pass request ids, tenant ids, or other platform-header values into a log line.\n * Those are already emitted automatically by the framework (see\n * `HeaderMethods.buildSecureMapForLogs`); duplicating them here is impossible.\n * See `.webpieces/instruct-ai/webpieces.logging.md`.\n */\nexport interface Logger {\n trace(message: string, err?: Error): void;\n debug(message: string, err?: Error): void;\n info(message: string, err?: Error): void;\n warn(message: string, err?: Error): void;\n error(message: string, err?: Error): void;\n}\n"]}
1
+ {"version":3,"file":"Logger.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/logging/Logger.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Log severity levels, ordered lowest → highest.\n *\n * Mirrors the common slf4j/bunyan/winston vocabulary so any of those backends\n * can be plugged in behind the {@link Logger} interface.\n */\nexport type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';\n\n/**\n * Logger - the pluggable logging contract for WebPieces.\n *\n * This is a BUSINESS-LOGIC interface (methods with behavior), so per the\n * webpieces guidelines it is an `interface`, not a class. Different projects\n * plug in different backends (bunyan, winston, pino, browser console, a\n * file writer, ...) by supplying an implementation via a {@link LoggerFactory}.\n *\n * Implementations MUST stay browser-safe if they are to be used from Angular /\n * React. Node-only backends (bunyan, file writers, ...) are wired in by\n * `framework:express` apps at startup, never in browser-safe libraries.\n *\n * KISS by design: every method takes ONLY a message plus an OPTIONAL `Error`.\n * There is deliberately no structured-fields / varargs argument — so nobody can\n * pass request ids, tenant ids, or other platform-header values into a log line.\n * Those are already emitted automatically by the framework (see\n * `HeaderRegistry.buildLogFields`); duplicating them here is impossible.\n * See `.webpieces/instruct-ai/webpieces.logging.md`.\n */\nexport interface Logger {\n trace(message: string, err?: Error): void;\n debug(message: string, err?: Error): void;\n info(message: string, err?: Error): void;\n warn(message: string, err?: Error): void;\n error(message: string, err?: Error): void;\n}\n"]}
@@ -1,21 +0,0 @@
1
- import { ContextKey } from '../ContextKey';
2
- import { ContextReader } from './ContextReader';
3
- /**
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.
11
- *
12
- * Works in both server (Node) and browser environments; a plain `new HeaderMethods()`
13
- * (no DI). The set of keys is supplied by the caller (from HeaderRegistry).
14
- */
15
- export declare class HeaderMethods {
16
- /**
17
- * Build the map for LOGGING from the given keys: each logged key (isLogged=true)
18
- * with a value present is added under its `name`, masked when isSecured.
19
- */
20
- buildSecureMapForLogs(keys: ContextKey[], contextReader: ContextReader): Map<string, string>;
21
- }
@@ -1,36 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HeaderMethods = void 0;
4
- /**
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.
12
- *
13
- * Works in both server (Node) and browser environments; a plain `new HeaderMethods()`
14
- * (no DI). The set of keys is supplied by the caller (from HeaderRegistry).
15
- */
16
- class HeaderMethods {
17
- /**
18
- * Build the map for LOGGING from the given keys: each logged key (isLogged=true)
19
- * with a value present is added under its `name`, masked when isSecured.
20
- */
21
- buildSecureMapForLogs(keys, contextReader) {
22
- const logMap = new Map();
23
- for (const key of keys) {
24
- if (!key.isLogged) {
25
- continue; // never logged (e.g. recorder, method-meta)
26
- }
27
- const value = contextReader.read(key);
28
- if (value) {
29
- logMap.set(key.name, key.maskIfSecured(value));
30
- }
31
- }
32
- return logMap;
33
- }
34
- }
35
- exports.HeaderMethods = HeaderMethods;
36
- //# sourceMappingURL=HeaderMethods.js.map
@@ -1 +0,0 @@
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"]}
@@ -1,20 +0,0 @@
1
- /**
2
- * RequestIdChainProcessor - Builds the per-hop distributed-trace chain.
3
- *
4
- * TS equivalent of the Java MicroSvcHeader REQUEST_ID/PREVIOUS_REQUEST_ID flow:
5
- * when a server makes an outbound call, its CURRENT request id is sent to the
6
- * downstream service as x-previous-request-id, and x-request-id is NOT sent -
7
- * the downstream ContextFilter then generates a fresh id for its own hop.
8
- * Result: every hop has its own id plus a pointer to its caller's id, forming
9
- * a trace tree.
10
- *
11
- * Invoked by ContextMgr.buildOutboundHeaders() after the transferred headers
12
- * are collected. Opt out via `new ContextMgr(reader, registry, false)` if you
13
- * want raw pass-through of x-request-id instead.
14
- */
15
- export declare class RequestIdChainProcessor {
16
- /**
17
- * Rewrite the outbound header map in place: x-request-id -> x-previous-request-id.
18
- */
19
- process(outboundHeaders: Map<string, string>): void;
20
- }
@@ -1,36 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RequestIdChainProcessor = void 0;
4
- const WebpiecesCoreHeaders_1 = require("./WebpiecesCoreHeaders");
5
- /**
6
- * RequestIdChainProcessor - Builds the per-hop distributed-trace chain.
7
- *
8
- * TS equivalent of the Java MicroSvcHeader REQUEST_ID/PREVIOUS_REQUEST_ID flow:
9
- * when a server makes an outbound call, its CURRENT request id is sent to the
10
- * downstream service as x-previous-request-id, and x-request-id is NOT sent -
11
- * the downstream ContextFilter then generates a fresh id for its own hop.
12
- * Result: every hop has its own id plus a pointer to its caller's id, forming
13
- * a trace tree.
14
- *
15
- * Invoked by ContextMgr.buildOutboundHeaders() after the transferred headers
16
- * are collected. Opt out via `new ContextMgr(reader, registry, false)` if you
17
- * want raw pass-through of x-request-id instead.
18
- */
19
- class RequestIdChainProcessor {
20
- /**
21
- * Rewrite the outbound header map in place: x-request-id -> x-previous-request-id.
22
- */
23
- process(outboundHeaders) {
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;
27
- const currentRequestId = outboundHeaders.get(requestIdName);
28
- if (currentRequestId === undefined) {
29
- return;
30
- }
31
- outboundHeaders.set(previousIdName, currentRequestId);
32
- outboundHeaders.delete(requestIdName);
33
- }
34
- }
35
- exports.RequestIdChainProcessor = RequestIdChainProcessor;
36
- //# sourceMappingURL=RequestIdChainProcessor.js.map
@@ -1 +0,0 @@
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"]}