@webpieces/core-util 0.4.439 → 0.4.441
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/ContextKey.d.ts +19 -7
- package/src/ContextKey.js +0 -32
- package/src/ContextKey.js.map +1 -1
- package/src/ContextTuple.d.ts +3 -3
- package/src/ContextTuple.js.map +1 -1
- package/src/http/ApiCallContext.d.ts +3 -3
- package/src/http/ApiCallContext.js.map +1 -1
- package/src/http/ContextReader.d.ts +3 -3
- package/src/http/ContextReader.js.map +1 -1
- package/src/http/HeaderRegistry.d.ts +7 -7
- package/src/http/HeaderRegistry.js.map +1 -1
- package/src/http/WebpiecesCoreHeaders.d.ts +2 -2
- package/src/http/WebpiecesCoreHeaders.js +1 -0
- package/src/http/WebpiecesCoreHeaders.js.map +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.js.map +1 -1
package/package.json
CHANGED
package/src/ContextKey.d.ts
CHANGED
|
@@ -23,18 +23,30 @@
|
|
|
23
23
|
*
|
|
24
24
|
* The type parameter `V` is the TYPE OF THE VALUE stored under this key — `string` for the wire/log
|
|
25
25
|
* keys (requestId, tenantId, ...), `ApiCallInfo` for the structured api tag, `TestCaseRecorder` for
|
|
26
|
-
* the recorder. It
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
26
|
+
* the recorder. It is REQUIRED (no default): every key must state what it holds, so a legacy
|
|
27
|
+
* `new ContextKey('x')` fails to compile until it declares `new ContextKey<string>('x')` — the
|
|
28
|
+
* type system does the migration for you. A heterogeneous store CANNOT be a `Record<string, string>`
|
|
29
|
+
* — the recorder and the api payload are not strings — so instead each KEY carries its own value
|
|
30
|
+
* type, and `RequestContext.getHeader/putHeader` INFER it from the key. That keeps the backing Map
|
|
31
|
+
* honestly type-erased while the public surface stays fully typed: a caller never asserts a value
|
|
32
|
+
* type, the key already declares it. A genuinely mixed collection of keys is spelled explicitly as
|
|
33
|
+
* `AnyContextKey[]`, so "I mean a mixed bag" is a visible, deliberate statement, never a default.
|
|
31
34
|
*/
|
|
32
|
-
|
|
35
|
+
/**
|
|
36
|
+
* A ContextKey whose value type is intentionally UNCONSTRAINED — a "key of any value type". Use this
|
|
37
|
+
* (never a bare `ContextKey`, which no longer compiles) for genuinely mixed-bag collections and
|
|
38
|
+
* key-agnostic code: `getAllHeaders(): AnyContextKey[]`, the {@link HeaderRegistry}'s key arrays, a
|
|
39
|
+
* reader that takes whatever key it is handed. Naming the mixed case makes "I mean any key" a visible,
|
|
40
|
+
* deliberate statement, and confines the one sanctioned `unknown` to this single alias instead of
|
|
41
|
+
* scattering `ContextKey<unknown>` — and its disable comment — across the codebase.
|
|
42
|
+
*/
|
|
43
|
+
export type AnyContextKey = ContextKey<unknown>;
|
|
44
|
+
export declare class ContextKey<V> {
|
|
33
45
|
/**
|
|
34
46
|
* Phantom marker carrying the value type {@link V}. It has no runtime existence (`declare`, never
|
|
35
47
|
* assigned) — it exists ONLY so `getHeader(key)` returns `V` and `putHeader(key, value)` checks
|
|
36
48
|
* `value` against `V`, both inferred straight from the key. Optional, so `ContextKey<A>` stays
|
|
37
|
-
* assignable to `
|
|
49
|
+
* assignable to `AnyContextKey` (i.e. `ContextKey<unknown>`) — arrays of mixed keys keep working.
|
|
38
50
|
*/
|
|
39
51
|
readonly __valueType?: V;
|
|
40
52
|
/** Context storage key + log/MDC key + recorder name. Always set. */
|
package/src/ContextKey.js
CHANGED
|
@@ -1,38 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ContextKey = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* ContextKey - a single key that travels in the request's "magic context"
|
|
6
|
-
* (RequestContext on the server, MutableContextStore in the browser).
|
|
7
|
-
*
|
|
8
|
-
* This ONE class replaces the old split of `Header` (interface) + `PlatformHeader`
|
|
9
|
-
* (class) + `ContextKey` (class). Every context value — whether it rides over HTTP
|
|
10
|
-
* (request-id, tenant, authorization) or stays in-process (method-meta, the
|
|
11
|
-
* TestCaseRecorder) — is a `ContextKey`.
|
|
12
|
-
*
|
|
13
|
-
* The fields are named for what they DO (flipped from the old model):
|
|
14
|
-
* - `name` ALWAYS set. The context storage key, the log/MDC key, and the
|
|
15
|
-
* recorder name. e.g. 'requestId', 'tenantId', 'authorization'.
|
|
16
|
-
* - `httpHeader` OPTIONAL. When set, this key is transferred over the wire under
|
|
17
|
-
* this HTTP header name (inbound request -> context, and context ->
|
|
18
|
-
* outbound request). e.g. 'x-request-id'. When UNSET, the key is
|
|
19
|
-
* context-only and never leaves the process (method-meta, recorder).
|
|
20
|
-
* - `isSecured` When true, the value is masked (partially) in logs.
|
|
21
|
-
* - `isLogged` Defaults to true. When false, the value is NEVER logged (used for
|
|
22
|
-
* object-valued/internal keys like the recorder or method-meta that
|
|
23
|
-
* must not be serialized into log lines).
|
|
24
|
-
*
|
|
25
|
-
* Per CLAUDE.md: data-only structures are classes, not interfaces.
|
|
26
|
-
*
|
|
27
|
-
* The type parameter `V` is the TYPE OF THE VALUE stored under this key — `string` for the wire/log
|
|
28
|
-
* keys (requestId, tenantId, ...), `ApiCallInfo` for the structured api tag, `TestCaseRecorder` for
|
|
29
|
-
* the recorder. It defaults to `unknown` so an untyped `ContextKey` still works. A heterogeneous
|
|
30
|
-
* store CANNOT be a `Record<string, string>` — the recorder and the api payload are not strings —
|
|
31
|
-
* so instead each KEY carries its own value type, and `RequestContext.getHeader/putHeader` INFER it
|
|
32
|
-
* from the key. That keeps the backing Map honestly type-erased while the public surface stays fully
|
|
33
|
-
* typed: a caller never asserts a value type, the key already declares it.
|
|
34
|
-
*/
|
|
35
|
-
// webpieces-disable no-any-unknown -- `unknown` is the deliberate default value type for an untyped ContextKey (a heterogeneous store cannot be narrowed further); typed keys override it with string/ApiCallInfo/TestCaseRecorder
|
|
36
4
|
class ContextKey {
|
|
37
5
|
/** Context storage key + log/MDC key + recorder name. Always set. */
|
|
38
6
|
name;
|
package/src/ContextKey.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextKey.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/ContextKey.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"ContextKey.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/ContextKey.ts"],"names":[],"mappings":";;;AA6CA,MAAa,UAAU;IASnB,qEAAqE;IAC5D,IAAI,CAAS;IAEtB;;;OAGG;IACM,UAAU,CAAU;IAE7B,2CAA2C;IAClC,SAAS,CAAU;IAE5B,6EAA6E;IACpE,QAAQ,CAAU;IAE3B,YACI,IAAY,EACZ,UAAmB,EACnB,SAAS,GAAG,KAAK,EACjB,QAAQ,GAAG,IAAI;QAEf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;IAED,uEAAuE;IACvE,aAAa;QACT,OAAO,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC;IACzC,CAAC;IAED;;;;;;OAMG;IACH,aAAa,CAAC,KAAa;QACvB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAClB,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;QACzB,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACV,OAAO,+BAA+B,CAAC;QAC3C,CAAC;aAAM,IAAI,GAAG,IAAI,EAAE,EAAE,CAAC;YACnB,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;QACzC,CAAC;aAAM,CAAC;YACJ,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;QACpE,CAAC;IACL,CAAC;CACJ;AA7DD,gCA6DC","sourcesContent":["/**\n * ContextKey - a single key that travels in the request's \"magic context\"\n * (RequestContext on the server, MutableContextStore in the browser).\n *\n * This ONE class replaces the old split of `Header` (interface) + `PlatformHeader`\n * (class) + `ContextKey` (class). Every context value — whether it rides over HTTP\n * (request-id, tenant, authorization) or stays in-process (method-meta, the\n * TestCaseRecorder) — is a `ContextKey`.\n *\n * The fields are named for what they DO (flipped from the old model):\n * - `name` ALWAYS set. The context storage key, the log/MDC key, and the\n * recorder name. e.g. 'requestId', 'tenantId', 'authorization'.\n * - `httpHeader` OPTIONAL. When set, this key is transferred over the wire under\n * this HTTP header name (inbound request -> context, and context ->\n * outbound request). e.g. 'x-request-id'. When UNSET, the key is\n * context-only and never leaves the process (method-meta, recorder).\n * - `isSecured` When true, the value is masked (partially) in logs.\n * - `isLogged` Defaults to true. When false, the value is NEVER logged (used for\n * object-valued/internal keys like the recorder or method-meta that\n * must not be serialized into log lines).\n *\n * Per CLAUDE.md: data-only structures are classes, not interfaces.\n *\n * The type parameter `V` is the TYPE OF THE VALUE stored under this key — `string` for the wire/log\n * keys (requestId, tenantId, ...), `ApiCallInfo` for the structured api tag, `TestCaseRecorder` for\n * the recorder. It is REQUIRED (no default): every key must state what it holds, so a legacy\n * `new ContextKey('x')` fails to compile until it declares `new ContextKey<string>('x')` — the\n * type system does the migration for you. A heterogeneous store CANNOT be a `Record<string, string>`\n * — the recorder and the api payload are not strings — so instead each KEY carries its own value\n * type, and `RequestContext.getHeader/putHeader` INFER it from the key. That keeps the backing Map\n * honestly type-erased while the public surface stays fully typed: a caller never asserts a value\n * type, the key already declares it. A genuinely mixed collection of keys is spelled explicitly as\n * `AnyContextKey[]`, so \"I mean a mixed bag\" is a visible, deliberate statement, never a default.\n */\n/**\n * A ContextKey whose value type is intentionally UNCONSTRAINED — a \"key of any value type\". Use this\n * (never a bare `ContextKey`, which no longer compiles) for genuinely mixed-bag collections and\n * key-agnostic code: `getAllHeaders(): AnyContextKey[]`, the {@link HeaderRegistry}'s key arrays, a\n * reader that takes whatever key it is handed. Naming the mixed case makes \"I mean any key\" a visible,\n * deliberate statement, and confines the one sanctioned `unknown` to this single alias instead of\n * scattering `ContextKey<unknown>` — and its disable comment — across the codebase.\n */\n// webpieces-disable no-any-unknown -- the ONE sanctioned `unknown`: a key whose value type is deliberately unconstrained (mixed-bag collections / key-agnostic code). Every other site names AnyContextKey instead of repeating this.\nexport type AnyContextKey = ContextKey<unknown>;\n\nexport class ContextKey<V> {\n /**\n * Phantom marker carrying the value type {@link V}. It has no runtime existence (`declare`, never\n * assigned) — it exists ONLY so `getHeader(key)` returns `V` and `putHeader(key, value)` checks\n * `value` against `V`, both inferred straight from the key. Optional, so `ContextKey<A>` stays\n * assignable to `AnyContextKey` (i.e. `ContextKey<unknown>`) — arrays of mixed keys keep working.\n */\n declare readonly __valueType?: V;\n\n /** Context storage key + log/MDC key + recorder name. Always set. */\n readonly name: string;\n\n /**\n * HTTP header name when this key is transferred over the wire (e.g.\n * 'x-request-id'). Undefined = context-only, never transferred.\n */\n readonly httpHeader?: string;\n\n /** Mask this value (partially) in logs. */\n readonly isSecured: boolean;\n\n /** Whether this key is logged at all. Default true; false = never logged. */\n readonly isLogged: boolean;\n\n constructor(\n name: string,\n httpHeader?: string,\n isSecured = false,\n isLogged = true,\n ) {\n this.name = name;\n this.httpHeader = httpHeader;\n this.isSecured = isSecured;\n this.isLogged = isLogged;\n }\n\n /** True when this key is transferred over HTTP (has an httpHeader). */\n isTransferred(): boolean {\n return this.httpHeader !== undefined;\n }\n\n /**\n * The value as it should appear in a log line: returned as-is for a normal\n * key, partially masked when this key is secured. Masking is length-based:\n * - Length > 15: first 3 + \"...\" + last 3\n * - Length 8-15: first 2 + \"...\"\n * - Length < 8: \"<secure key too short to log>\"\n */\n maskIfSecured(value: string): string {\n if (!this.isSecured) {\n return value;\n }\n const len = value.length;\n if (len < 8) {\n return '<secure key too short to log>';\n } else if (len <= 15) {\n return `${value.substring(0, 2)}...`;\n } else {\n return `${value.substring(0, 3)}...${value.substring(len - 3)}`;\n }\n }\n}\n"]}
|
package/src/ContextTuple.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnyContextKey } from './ContextKey';
|
|
2
2
|
/**
|
|
3
3
|
* ContextTuple - one (ContextKey, value) pair to be stamped into the request's
|
|
4
4
|
* "magic context" (RequestContext on the server) — e.g. USER_ID, ORG_ID. The JWT
|
|
@@ -6,7 +6,7 @@ import { ContextKey } from './ContextKey';
|
|
|
6
6
|
* Data-only structure (a class, per the guidelines).
|
|
7
7
|
*/
|
|
8
8
|
export declare class ContextTuple {
|
|
9
|
-
readonly key:
|
|
9
|
+
readonly key: AnyContextKey;
|
|
10
10
|
readonly value: unknown;
|
|
11
|
-
constructor(key:
|
|
11
|
+
constructor(key: AnyContextKey, value: unknown);
|
|
12
12
|
}
|
package/src/ContextTuple.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextTuple.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/ContextTuple.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACH,MAAa,YAAY;IAED;IAEA;IAHpB,YACoB,
|
|
1
|
+
{"version":3,"file":"ContextTuple.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/ContextTuple.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACH,MAAa,YAAY;IAED;IAEA;IAHpB,YACoB,GAAkB;IAClC,oFAAoF;IACpE,KAAc;QAFd,QAAG,GAAH,GAAG,CAAe;QAElB,UAAK,GAAL,KAAK,CAAS;IAC/B,CAAC;CACP;AAND,oCAMC","sourcesContent":["import { ContextKey, AnyContextKey } from './ContextKey';\n\n/**\n * ContextTuple - one (ContextKey, value) pair to be stamped into the request's\n * \"magic context\" (RequestContext on the server) — e.g. USER_ID, ORG_ID. The JWT\n * parse plugin returns these so the framework can set them via RequestContext.putHeader.\n * Data-only structure (a class, per the guidelines).\n */\nexport class ContextTuple {\n constructor(\n public readonly key: AnyContextKey,\n // webpieces-disable no-any-unknown -- context values are arbitrary app-defined data\n public readonly value: unknown,\n ) {}\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnyContextKey } from '../ContextKey';
|
|
2
2
|
/**
|
|
3
3
|
* ApiCallContext - the tiny seam that lets {@link LogApiCall} (browser-safe, core-util) stamp a
|
|
4
4
|
* ContextKey (the `api` tag) into the ambient request context WITHOUT importing it.
|
|
@@ -27,13 +27,13 @@ export interface ApiCallContext {
|
|
|
27
27
|
* Stamp one ContextKey → value into the ambient context. The logger reads it back off the context
|
|
28
28
|
* (server: RequestContext.buildStructuredLogFields; browser: its own store) during the log emit.
|
|
29
29
|
*/
|
|
30
|
-
set(contextKey:
|
|
30
|
+
set(contextKey: AnyContextKey, value: unknown): void;
|
|
31
31
|
/**
|
|
32
32
|
* Clear one ContextKey. {@link LogApiCall} calls set → log → remove as one SYNCHRONOUS span, so the
|
|
33
33
|
* tag is never held across `await`. That is what makes a single browser global safe: single-threaded,
|
|
34
34
|
* nothing can interleave between set and remove, so a concurrent call can never clobber the slot.
|
|
35
35
|
*/
|
|
36
|
-
remove(contextKey:
|
|
36
|
+
remove(contextKey: AnyContextKey): void;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* ApiCallContextHolder - the process-wide holder for the active {@link ApiCallContext}.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiCallContext.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ApiCallContext.ts"],"names":[],"mappings":";;;AA0CA;;;;;;GAMG;AACH,MAAa,oBAAoB;IACrB,MAAM,CAAC,OAAO,CAA6B;IAEnD,sGAAsG;IACtG,6HAA6H;IAC7H,MAAM,CAAC,OAAO,CAAC,GAAmB;QAC9B,oBAAoB,CAAC,OAAO,GAAG,GAAG,CAAC;IACvC,CAAC;IAED,+FAA+F;IAC/F,gHAAgH;IAChH,MAAM,CAAC,WAAW;QACd,OAAO,oBAAoB,CAAC,OAAO,KAAK,SAAS,CAAC;IACtD,CAAC;IAED;;;OAGG;IACH,mIAAmI;IACnI,MAAM,CAAC,GAAG;QACN,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACX,wFAAwF;gBACxF,4FAA4F;gBAC5F,gFAAgF;gBAChF,oDAAoD,CACvD,CAAC;QACN,CAAC;QACD,OAAO,oBAAoB,CAAC,OAAO,CAAC;IACxC,CAAC;CACJ;AA/BD,oDA+BC","sourcesContent":["import { ContextKey } from '../ContextKey';\n\n/**\n * ApiCallContext - the tiny seam that lets {@link LogApiCall} (browser-safe, core-util) stamp a\n * ContextKey (the `api` tag) into the ambient request context WITHOUT importing it.\n *\n * WHY a seam instead of a direct call: the ambient context is `RequestContext` in\n * `@webpieces/core-context`, which is built on Node `async_hooks` (AsyncLocalStorage). core-util —\n * and `ProxyClient`, which runs in a BROWSER bundle — must never import that (it would be a circular\n * dependency, and it would drag Node vocabulary into a browser build). So core-util owns only this\n * interface + a global holder; each environment installs its own impl at startup:\n * - Node server: `setupRuntime` installs a RequestContext-backed impl.\n * - Browser: `ClientHttpBrowserFactory` installs a module-global impl.\n *\n * If NEITHER installs one, {@link ApiCallContextHolder.get} THROWS (loud misconfiguration, in the\n * webpieces spirit) — see its message. There is deliberately no silent no-op default.\n *\n * This mirrors the existing global-singleton-configured-at-startup pattern (LogManager,\n * HeaderRegistry): behavior is an interface (per CLAUDE.md), the holder is the global seam.\n */\nexport interface ApiCallContext {\n /**\n * True when there is a context to stamp into (a live Node RequestContext scope; a browser is always\n * active). {@link LogApiCall} throws if this is false — an api call with nowhere to tag is a bug.\n */\n isActive(): boolean;\n\n /**\n * Stamp one ContextKey → value into the ambient context. The logger reads it back off the context\n * (server: RequestContext.buildStructuredLogFields; browser: its own store) during the log emit.\n */\n // webpieces-disable no-any-unknown -- a context value is heterogeneous (the api struct here; strings elsewhere)\n set(contextKey:
|
|
1
|
+
{"version":3,"file":"ApiCallContext.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ApiCallContext.ts"],"names":[],"mappings":";;;AA0CA;;;;;;GAMG;AACH,MAAa,oBAAoB;IACrB,MAAM,CAAC,OAAO,CAA6B;IAEnD,sGAAsG;IACtG,6HAA6H;IAC7H,MAAM,CAAC,OAAO,CAAC,GAAmB;QAC9B,oBAAoB,CAAC,OAAO,GAAG,GAAG,CAAC;IACvC,CAAC;IAED,+FAA+F;IAC/F,gHAAgH;IAChH,MAAM,CAAC,WAAW;QACd,OAAO,oBAAoB,CAAC,OAAO,KAAK,SAAS,CAAC;IACtD,CAAC;IAED;;;OAGG;IACH,mIAAmI;IACnI,MAAM,CAAC,GAAG;QACN,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACX,wFAAwF;gBACxF,4FAA4F;gBAC5F,gFAAgF;gBAChF,oDAAoD,CACvD,CAAC;QACN,CAAC;QACD,OAAO,oBAAoB,CAAC,OAAO,CAAC;IACxC,CAAC;CACJ;AA/BD,oDA+BC","sourcesContent":["import { ContextKey, AnyContextKey } from '../ContextKey';\n\n/**\n * ApiCallContext - the tiny seam that lets {@link LogApiCall} (browser-safe, core-util) stamp a\n * ContextKey (the `api` tag) into the ambient request context WITHOUT importing it.\n *\n * WHY a seam instead of a direct call: the ambient context is `RequestContext` in\n * `@webpieces/core-context`, which is built on Node `async_hooks` (AsyncLocalStorage). core-util —\n * and `ProxyClient`, which runs in a BROWSER bundle — must never import that (it would be a circular\n * dependency, and it would drag Node vocabulary into a browser build). So core-util owns only this\n * interface + a global holder; each environment installs its own impl at startup:\n * - Node server: `setupRuntime` installs a RequestContext-backed impl.\n * - Browser: `ClientHttpBrowserFactory` installs a module-global impl.\n *\n * If NEITHER installs one, {@link ApiCallContextHolder.get} THROWS (loud misconfiguration, in the\n * webpieces spirit) — see its message. There is deliberately no silent no-op default.\n *\n * This mirrors the existing global-singleton-configured-at-startup pattern (LogManager,\n * HeaderRegistry): behavior is an interface (per CLAUDE.md), the holder is the global seam.\n */\nexport interface ApiCallContext {\n /**\n * True when there is a context to stamp into (a live Node RequestContext scope; a browser is always\n * active). {@link LogApiCall} throws if this is false — an api call with nowhere to tag is a bug.\n */\n isActive(): boolean;\n\n /**\n * Stamp one ContextKey → value into the ambient context. The logger reads it back off the context\n * (server: RequestContext.buildStructuredLogFields; browser: its own store) during the log emit.\n */\n // webpieces-disable no-any-unknown -- a context value is heterogeneous (the api struct here; strings elsewhere)\n set(contextKey: AnyContextKey, value: unknown): void;\n\n /**\n * Clear one ContextKey. {@link LogApiCall} calls set → log → remove as one SYNCHRONOUS span, so the\n * tag is never held across `await`. That is what makes a single browser global safe: single-threaded,\n * nothing can interleave between set and remove, so a concurrent call can never clobber the slot.\n */\n remove(contextKey: AnyContextKey): void;\n}\n\n/**\n * ApiCallContextHolder - the process-wide holder for the active {@link ApiCallContext}.\n *\n * Configured exactly like {@link LogManager}: the environment calls {@link ApiCallContextHolder.install}\n * at startup. Until then {@link get} THROWS, so a forgotten setup fails loudly rather than silently\n * dropping the `api` tag off every log line.\n */\nexport class ApiCallContextHolder {\n private static current: ApiCallContext | undefined;\n\n /** Install the environment's ApiCallContext (Node: RequestContext-backed; browser: module-global). */\n // webpieces-disable no-function-outside-class -- static global seam, configured once at startup (like LogManager.setFactory)\n static install(ctx: ApiCallContext): void {\n ApiCallContextHolder.current = ctx;\n }\n\n /** True once an ApiCallContext has been installed (used by tests to probe the unset state). */\n // webpieces-disable no-function-outside-class -- static global seam accessor (like HeaderRegistry.isConfigured)\n static isInstalled(): boolean {\n return ApiCallContextHolder.current !== undefined;\n }\n\n /**\n * The active ApiCallContext. Throws if nothing was installed — a one-time setup call is required:\n * `setupRuntime()` does it on a Node server; building `ClientHttpBrowserFactory` does it in a browser.\n */\n // webpieces-disable no-function-outside-class -- static global seam accessor (like LogManager/HeaderRegistry.get), not DI-injected\n static get(): ApiCallContext {\n if (!ApiCallContextHolder.current) {\n throw new Error(\n 'ApiCallContext is not installed — LogApiCall cannot tag API-call logs. Set it up ONCE ' +\n 'at startup: on a Node server, setupRuntime() installs it for you; in a browser, construct ' +\n 'ClientHttpBrowserFactory once at startup. (This is the same one-time setup as ' +\n 'HeaderRegistry.configure / LogManager.setFactory.)',\n );\n }\n return ApiCallContextHolder.current;\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnyContextKey } from '../ContextKey';
|
|
2
2
|
/**
|
|
3
3
|
* ContextReader - reads context-key values from an app-held store.
|
|
4
4
|
*
|
|
@@ -18,12 +18,12 @@ export interface ContextReader {
|
|
|
18
18
|
/**
|
|
19
19
|
* Read the string value of a context key. Returns undefined if not present.
|
|
20
20
|
*/
|
|
21
|
-
read(key:
|
|
21
|
+
read(key: AnyContextKey): string | undefined;
|
|
22
22
|
/**
|
|
23
23
|
* OPTIONAL: read a non-string context value (e.g. the active TestCaseRecorder
|
|
24
24
|
* under RecorderKeys.RECORDER). Server-side readers implement this over the
|
|
25
25
|
* RequestContext; browser readers may omit it (no server-side recording in
|
|
26
26
|
* browsers — same as Java).
|
|
27
27
|
*/
|
|
28
|
-
readValue?(key:
|
|
28
|
+
readValue?(key: AnyContextKey): unknown;
|
|
29
29
|
}
|
|
@@ -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 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:
|
|
1
|
+
{"version":3,"file":"ContextReader.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ContextReader.ts"],"names":[],"mappings":"","sourcesContent":["import { ContextKey, AnyContextKey } from '../ContextKey';\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: AnyContextKey): 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: AnyContextKey): unknown;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnyContextKey } from '../ContextKey';
|
|
2
2
|
/**
|
|
3
3
|
* HeaderRegistry - the single, GLOBAL source of truth for every ContextKey the
|
|
4
4
|
* platform knows about. Port of Java webpieces' HeaderTranslation.
|
|
@@ -25,7 +25,7 @@ import { ContextKey } from '../ContextKey';
|
|
|
25
25
|
*/
|
|
26
26
|
export declare class HeaderRegistry {
|
|
27
27
|
/** The webpieces-supplied common keys; included when platformHeaders=true. */
|
|
28
|
-
static readonly DEFAULT_HEADERS:
|
|
28
|
+
static readonly DEFAULT_HEADERS: AnyContextKey[];
|
|
29
29
|
private static instance;
|
|
30
30
|
private readonly keys;
|
|
31
31
|
private readonly transferredKeys;
|
|
@@ -37,18 +37,18 @@ export declare class HeaderRegistry {
|
|
|
37
37
|
* Install the process-wide registry. Call once at startup, BEFORE
|
|
38
38
|
* LogManager.setFactory(...) (logging masks/keys off this registry).
|
|
39
39
|
*/
|
|
40
|
-
static configure(svrHeaders:
|
|
40
|
+
static configure(svrHeaders: AnyContextKey[], platformHeaders: boolean): void;
|
|
41
41
|
/** The configured registry. Throws if configure() has not been called. */
|
|
42
42
|
static get(): HeaderRegistry;
|
|
43
43
|
/** True once configure() has run. Used by LogManager.setFactory to fail fast. */
|
|
44
44
|
static isConfigured(): boolean;
|
|
45
45
|
/** All registered keys (deduplicated). */
|
|
46
|
-
getKeys():
|
|
46
|
+
getKeys(): AnyContextKey[];
|
|
47
47
|
/**
|
|
48
48
|
* Keys that transfer over the wire (inbound request -> context, and context ->
|
|
49
49
|
* outbound request): those with an httpHeader set.
|
|
50
50
|
*/
|
|
51
|
-
getTransferredKeys():
|
|
51
|
+
getTransferredKeys(): AnyContextKey[];
|
|
52
52
|
/** Names (log keys) whose values must be masked in logs. isSecured=true. */
|
|
53
53
|
getSecuredNames(): string[];
|
|
54
54
|
/**
|
|
@@ -57,9 +57,9 @@ export declare class HeaderRegistry {
|
|
|
57
57
|
* `buildStructuredLogFields()`. (The registry used to own those two builders behind a read
|
|
58
58
|
* callback, but only the server ever called them, so the seam was inlined away.)
|
|
59
59
|
*/
|
|
60
|
-
getLoggedKeys():
|
|
60
|
+
getLoggedKeys(): AnyContextKey[];
|
|
61
61
|
/** Look up a key by its HTTP header name (case-insensitive). O(1) via the precomputed map. */
|
|
62
|
-
findByHttpHeader(httpHeader: string):
|
|
62
|
+
findByHttpHeader(httpHeader: string): AnyContextKey | undefined;
|
|
63
63
|
/**
|
|
64
64
|
* Collapse exact duplicates, throw on conflicting definitions sharing a `name`
|
|
65
65
|
* or an `httpHeader`.
|
|
@@ -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,2KAA2K;IAC3K,MAAM,CAAC,SAAS,CAAC,UAAwB,EAAE,eAAwB;QAC/D,MAAM,GAAG,GAAiB;YACtB,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,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;;;;;OAKG;IACH,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;;AArJL,wCAsJC","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(CompanyHeaders.getAllHeaders(), true);\n * ```\n *\n * - `svrHeaders` the context keys this process registers — by convention the whole\n * company-wide set (all keys across all servers), e.g. CompanyHeaders.\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 // webpieces-disable no-function-outside-class -- HeaderRegistry is a deliberately static global singleton (like LogManager); configured once at startup, never DI-injected\n static configure(svrHeaders: ContextKey[], platformHeaders: boolean): void {\n const all: ContextKey[] = [\n ...(platformHeaders ? HeaderRegistry.DEFAULT_HEADERS : []),\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 /**\n * Keys that appear in logs. isLogged=true. The node logging backends read THESE and build the log\n * field map directly from the active context — see `RequestContext.buildLogFields()` /\n * `buildStructuredLogFields()`. (The registry used to own those two builders behind a read\n * callback, but only the server ever called them, so the seam was inlined away.)\n */\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, AnyContextKey } 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(CompanyHeaders.getAllHeaders(), true);\n * ```\n *\n * - `svrHeaders` the context keys this process registers — by convention the whole\n * company-wide set (all keys across all servers), e.g. CompanyHeaders.\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: AnyContextKey[] = WebpiecesCoreHeaders.getAllHeaders();\n\n private static instance: HeaderRegistry | undefined;\n\n private readonly keys: AnyContextKey[];\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: AnyContextKey[];\n private readonly securedNames: string[];\n private readonly loggedKeys: AnyContextKey[];\n private readonly byHttpHeader: Map<string, AnyContextKey>;\n\n private constructor(keys: AnyContextKey[]) {\n this.keys = this.checkForDuplicates(keys);\n this.transferredKeys = this.keys.filter((k: AnyContextKey) => k.httpHeader !== undefined);\n this.securedNames = this.keys\n .filter((k: AnyContextKey) => k.isSecured)\n .map((k: AnyContextKey) => k.name);\n this.loggedKeys = this.keys.filter((k: AnyContextKey) => k.isLogged);\n this.byHttpHeader = new Map(\n this.transferredKeys.map((k: AnyContextKey): [string, AnyContextKey] => [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 // webpieces-disable no-function-outside-class -- HeaderRegistry is a deliberately static global singleton (like LogManager); configured once at startup, never DI-injected\n static configure(svrHeaders: AnyContextKey[], platformHeaders: boolean): void {\n const all: AnyContextKey[] = [\n ...(platformHeaders ? HeaderRegistry.DEFAULT_HEADERS : []),\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(): AnyContextKey[] {\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(): AnyContextKey[] {\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 /**\n * Keys that appear in logs. isLogged=true. The node logging backends read THESE and build the log\n * field map directly from the active context — see `RequestContext.buildLogFields()` /\n * `buildStructuredLogFields()`. (The registry used to own those two builders behind a read\n * callback, but only the server ever called them, so the seam was inlined away.)\n */\n getLoggedKeys(): AnyContextKey[] {\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): AnyContextKey | 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: AnyContextKey[]): AnyContextKey[] {\n const byName = new Map<string, AnyContextKey>();\n const byHttpHeader = new Map<string, AnyContextKey>();\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: AnyContextKey, duplicate: AnyContextKey): 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,4 +1,4 @@
|
|
|
1
|
-
import { ContextKey } from '../ContextKey';
|
|
1
|
+
import { ContextKey, AnyContextKey } from '../ContextKey';
|
|
2
2
|
import { ApiCallInfo } from './ApiCallInfo';
|
|
3
3
|
/**
|
|
4
4
|
* Core framework context keys — the minimum the WebPieces framework needs to correlate one
|
|
@@ -148,5 +148,5 @@ export declare class WebpiecesCoreHeaders {
|
|
|
148
148
|
/**
|
|
149
149
|
* Get all core context keys as an array (the platform DEFAULT_HEADERS set).
|
|
150
150
|
*/
|
|
151
|
-
static getAllHeaders():
|
|
151
|
+
static getAllHeaders(): AnyContextKey[];
|
|
152
152
|
}
|
|
@@ -151,6 +151,7 @@ class WebpiecesCoreHeaders {
|
|
|
151
151
|
/**
|
|
152
152
|
* Get all core context keys as an array (the platform DEFAULT_HEADERS set).
|
|
153
153
|
*/
|
|
154
|
+
// webpieces-disable no-function-outside-class -- pre-existing static key-registry accessor (a list of constants, not injectable behavior); pulled into modified-scope only by the ContextKey[] -> AnyContextKey[] return-type change
|
|
154
155
|
static getAllHeaders() {
|
|
155
156
|
return [
|
|
156
157
|
WebpiecesCoreHeaders.REQUEST_ID,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebpiecesCoreHeaders.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/WebpiecesCoreHeaders.ts"],"names":[],"mappings":";;;AAAA,8CAA2C;AAG3C;;;;;;;;;;;;;;;GAeG;AACH,MAAa,oBAAoB;IAC7B;;;OAGG;IACH,MAAM,CAAU,UAAU,GAAG,IAAI,uBAAU,CAAS,WAAW,EAAE,cAAc,CAAC,CAAC;IAEjF;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAU,iBAAiB,GAAG,IAAI,uBAAU,CAC9C,iBAAiB;IACjB,cAAc,CAAC,SAAS,CAC3B,CAAC;IAEF;;;;;;;;;;;;OAYG;IACH,MAAM,CAAU,cAAc,GAAG,IAAI,uBAAU,CAAS,eAAe,EAAE,4BAA4B,CAAC,CAAC;IAEvG;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,MAAM,CAAU,SAAS,GAAG,IAAI,uBAAU,CAAS,UAAU,EAAE,sBAAsB,CAAC,CAAC;IAEvF,MAAM,CAAU,MAAM,GAAG,IAAI,uBAAU,CAAS,OAAO,EAAE,UAAU,CAAC,CAAC;IAErE,MAAM,CAAU,OAAO,GAAG,IAAI,uBAAU,CAAS,QAAQ,EAAE,WAAW,CAAC,CAAC;IAExE,MAAM,CAAU,UAAU,GAAG,IAAI,uBAAU,CAAS,OAAO,EAAE,mBAAmB,CAAC,CAAC;IAElF;;;OAGG;IACH,MAAM,CAAU,SAAS,GAAG,IAAI,uBAAU,CAAS,WAAW,EAAE,uBAAuB,CAAC,CAAC;IAEzF;;;;;;;;;;;OAWG;IACH,MAAM,CAAU,aAAa,GAAG,IAAI,uBAAU,CAAc,KAAK,EAAE,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAErI;;;;;;;;;;OAUG;IACH,MAAM,CAAU,WAAW,GAAG,IAAI,uBAAU,CAAS,YAAY,EAAE,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAErI,MAAM,CAAU,YAAY,GAAG,IAAI,uBAAU,CAAS,aAAa,EAAE,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAEvI;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAU,UAAU,GAAG,IAAI,uBAAU,CAAS,YAAY,EAAE,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAEpI,MAAM,CAAU,MAAM,GAAG,IAAI,uBAAU,CAAS,QAAQ,EAAE,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAE5H;;;;;;;;;;;;;;;OAeG;IAEH;;OAEG;IACH,MAAM,CAAC,aAAa;QAChB,OAAO;YACH,oBAAoB,CAAC,UAAU;YAC/B,oBAAoB,CAAC,iBAAiB;YACtC,oBAAoB,CAAC,cAAc;YACnC,oBAAoB,CAAC,SAAS;YAC9B,oBAAoB,CAAC,OAAO;YAC5B,oBAAoB,CAAC,MAAM;YAC3B,oBAAoB,CAAC,UAAU;YAC/B,oBAAoB,CAAC,SAAS;YAC9B,oBAAoB,CAAC,aAAa;YAClC,oBAAoB,CAAC,WAAW;YAChC,oBAAoB,CAAC,YAAY;YACjC,oBAAoB,CAAC,UAAU;YAC/B,oBAAoB,CAAC,MAAM;SAC9B,CAAC;IACN,CAAC;;AArKL,oDAsKC","sourcesContent":["import { ContextKey } from '../ContextKey';\nimport { ApiCallInfo } from './ApiCallInfo';\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<string>('requestId', 'x-request-id');\n\n /**\n * WHICH SERVICE MINTED {@link REQUEST_ID} — the name from {@link ServiceInfo}, stamped by\n * `RequestContextHeaders.fillFromRequest` ONLY on the branch that generates a new id (i.e. when\n * the inbound request carried no `x-request-id`). It answers the question the id alone cannot:\n * \"this trace starts here — is that right?\" An id appearing with no source means it came from\n * outside; an id sourced by a service that should never be an entry point is a routing bug.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire, and that is the WHOLE POINT. If it\n * travelled, hop 2 would inherit it, hop 3 would inherit it, and \"who started this trace\"\n * would be indistinguishable from \"who passed it along\" — the origin, the one fact this key\n * carries, would be lost. It is absent on every hop that did NOT mint the id, which is exactly\n * the signal: present == I am the origin.\n * - `isLogged` TRUE → emitted as a plain string at `jsonPayload.requestIdSource`.\n */\n static readonly REQUEST_ID_SOURCE = new ContextKey<string>(\n 'requestIdSource',\n /*httpHeader*/ undefined\n );\n\n /**\n * The CALLER's build version — so a downstream server's logs record which build of the client\n * called it (surfaces as `jsonPayload.clientVersion`). Distinct from the log line's own `version`\n * (this service's build): `version` answers \"which build wrote this line?\", `clientVersion`\n * answers \"which build asked us to?\".\n *\n * - `httpHeader` SET → transferred over the wire, BUT unlike a normal transferred key it is NOT\n * copied from the context onward. Each hop OVERWRITES it with its OWN `ServiceInfo.getVersion()`\n * as it becomes the client to the next hop (see `buildOutboundHeaders`), so on any given server\n * `clientVersion` is always the IMMEDIATE caller's version, never a stale grand-caller's.\n * - `isLogged` TRUE → the inbound value lands in the context and flows through the normal log\n * field map; no backend change needed.\n */\n static readonly CLIENT_VERSION = new ContextKey<string>('clientVersion', 'x-webpieces-client-version');\n\n /**\n * A frontend/app-minted correlation id that groups every request triggered by ONE user ACTION.\n *\n * An \"action\" is a single thing the user did in the GUI — a CLICK on a button/link, or TYPING in a\n * field — or a background poller tick: anything that may fan out into MULTIPLE remote calls. That one\n * action fires 1..N browser HTTP calls, each of which gets its own framework-minted {@link REQUEST_ID}\n * (one per HTTP call, shared within that call's server→server subtree). `actionId` sits ABOVE\n * `requestId` and is what stitches those N requests back to the single action that caused them:\n *\n * actionId (app-minted, ONE per user action, rides EVERY call of that action)\n * └── 1..N requestId (framework-minted, ONE per HTTP call)\n *\n * Grep one `actionId` in the logs → every `requestId` it spawned, and every log line of the whole\n * action. Minted and refreshed by the app (a UI concern), carried under `x-webpieces-actionid`.\n *\n * Browser/app-minted ONLY: unlike {@link REQUEST_ID}, the framework transfers and logs it but must\n * NOT auto-mint one server-side. Absent `actionId` ⇒ a non-action flow (system / cron / task), which\n * is the correct signal.\n *\n * - `httpHeader` SET → transferred: copied off the inbound request into context and re-emitted on\n * outbound hops, so the id follows the action across services.\n * - `isLogged` TRUE → emitted as a plain string on every log line of the request.\n */\n static readonly ACTION_ID = new ContextKey<string>('actionId', 'x-webpieces-actionid');\n\n static readonly ORG_ID = new ContextKey<string>('orgId', 'x-org-id');\n\n static readonly USER_ID = new ContextKey<string>('userId', 'x-user-id');\n\n static readonly USER_ROLES = new ContextKey<string>('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<string>('recording', 'x-webpieces-recording');\n\n /**\n * The structured API-call tag ({@link ApiCallInfo}) stamped by {@link LogApiCall} around every\n * outbound (client) / inbound (server) call. It rides the magic context so EVERY log line emitted\n * during the call inherits a filterable `api` object, surfacing in GCP as nested\n * `jsonPayload.api.{side,type,result,path,method}`.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire. Per-hop only: each server/client hop\n * stamps its own tag, so a downstream server records `side:'server'`, never the caller's `side:'client'`.\n * - `isLogged` TRUE → emitted by the logging backends. It carries an OBJECT value, so the backends\n * read it via {@link HeaderRegistry.buildStructuredLogFields} (object-aware); the flat\n * `buildLogFields()` string map deliberately skips it (typeof-string guard).\n */\n static readonly API_CALL_INFO = new ContextKey<ApiCallInfo>('api', /*httpHeader*/ undefined, /*isSecured*/ false, /*isLogged*/ true);\n\n /**\n * The inbound request's HTTP method and path, stamped ONCE from the {@link HttpRequest} by\n * `RequestContextHeaders.fillFromRequest` (the atomic inbound choke point every transport funnels\n * through). They surface as top-level `jsonPayload.httpMethod` / `jsonPayload.requestPath` so every\n * log line of the request carries them — they used to ride inside {@link ApiCallInfo} (`api.path` /\n * `api.method`) but that coupled a per-CALL logger to the per-REQUEST transport shape.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire: a downstream hop stamps its OWN inbound\n * method/path, never the caller's. Outbound client calls never set these (no inbound path).\n * - `isLogged` TRUE → emitted by the logging backends as plain strings.\n */\n static readonly HTTP_METHOD = new ContextKey<string>('httpMethod', /*httpHeader*/ undefined, /*isSecured*/ false, /*isLogged*/ true);\n\n static readonly REQUEST_PATH = new ContextKey<string>('requestPath', /*httpHeader*/ undefined, /*isSecured*/ false, /*isLogged*/ true);\n\n /**\n * The routed endpoint's IMPLEMENTATION identity: the concrete controller class name\n * ({@link RouteMetadata.controllerClassName}, e.g. `LoginController`) and the handler method NAME\n * ({@link RouteMetadata.methodName}, e.g. `login`), stamped once per request by {@link LogApiFilter}\n * after route matching so every subsequent log line of the request carries them.\n *\n * These say WHICH CODE ran, which is what you actually grep for — far more useful than the raw\n * `requestPath`. They are the top-level, filterable twin of what previously only lived nested in\n * {@link ApiCallInfo} (`api.method.controllerName` / `api.method.methodName`). The local console\n * formatters render them together as a compact `[Controller.method]` bracket; GCP keeps them as two\n * separate `jsonPayload.controller` / `jsonPayload.method` fields.\n *\n * NOTE: `method` here is the CODE method name (e.g. `login`), NOT the HTTP verb — that is\n * {@link HTTP_METHOD} (`httpMethod`).\n *\n * - `httpHeader` UNDEFINED → NOT transferred: each hop stamps its OWN routed controller/method.\n * - `isLogged` TRUE → emitted by the logging backends as plain strings.\n */\n static readonly CONTROLLER = new ContextKey<string>('controller', /*httpHeader*/ undefined, /*isSecured*/ false, /*isLogged*/ true);\n\n static readonly METHOD = new ContextKey<string>('method', /*httpHeader*/ undefined, /*isSecured*/ false, /*isLogged*/ true);\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.REQUEST_ID_SOURCE,\n WebpiecesCoreHeaders.CLIENT_VERSION,\n WebpiecesCoreHeaders.ACTION_ID,\n WebpiecesCoreHeaders.USER_ID,\n WebpiecesCoreHeaders.ORG_ID,\n WebpiecesCoreHeaders.USER_ROLES,\n WebpiecesCoreHeaders.RECORDING,\n WebpiecesCoreHeaders.API_CALL_INFO,\n WebpiecesCoreHeaders.HTTP_METHOD,\n WebpiecesCoreHeaders.REQUEST_PATH,\n WebpiecesCoreHeaders.CONTROLLER,\n WebpiecesCoreHeaders.METHOD,\n ];\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"WebpiecesCoreHeaders.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/WebpiecesCoreHeaders.ts"],"names":[],"mappings":";;;AAAA,8CAA0D;AAG1D;;;;;;;;;;;;;;;GAeG;AACH,MAAa,oBAAoB;IAC7B;;;OAGG;IACH,MAAM,CAAU,UAAU,GAAG,IAAI,uBAAU,CAAS,WAAW,EAAE,cAAc,CAAC,CAAC;IAEjF;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAU,iBAAiB,GAAG,IAAI,uBAAU,CAC9C,iBAAiB;IACjB,cAAc,CAAC,SAAS,CAC3B,CAAC;IAEF;;;;;;;;;;;;OAYG;IACH,MAAM,CAAU,cAAc,GAAG,IAAI,uBAAU,CAAS,eAAe,EAAE,4BAA4B,CAAC,CAAC;IAEvG;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,MAAM,CAAU,SAAS,GAAG,IAAI,uBAAU,CAAS,UAAU,EAAE,sBAAsB,CAAC,CAAC;IAEvF,MAAM,CAAU,MAAM,GAAG,IAAI,uBAAU,CAAS,OAAO,EAAE,UAAU,CAAC,CAAC;IAErE,MAAM,CAAU,OAAO,GAAG,IAAI,uBAAU,CAAS,QAAQ,EAAE,WAAW,CAAC,CAAC;IAExE,MAAM,CAAU,UAAU,GAAG,IAAI,uBAAU,CAAS,OAAO,EAAE,mBAAmB,CAAC,CAAC;IAElF;;;OAGG;IACH,MAAM,CAAU,SAAS,GAAG,IAAI,uBAAU,CAAS,WAAW,EAAE,uBAAuB,CAAC,CAAC;IAEzF;;;;;;;;;;;OAWG;IACH,MAAM,CAAU,aAAa,GAAG,IAAI,uBAAU,CAAc,KAAK,EAAE,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAErI;;;;;;;;;;OAUG;IACH,MAAM,CAAU,WAAW,GAAG,IAAI,uBAAU,CAAS,YAAY,EAAE,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAErI,MAAM,CAAU,YAAY,GAAG,IAAI,uBAAU,CAAS,aAAa,EAAE,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAEvI;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAU,UAAU,GAAG,IAAI,uBAAU,CAAS,YAAY,EAAE,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAEpI,MAAM,CAAU,MAAM,GAAG,IAAI,uBAAU,CAAS,QAAQ,EAAE,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAE5H;;;;;;;;;;;;;;;OAeG;IAEH;;OAEG;IACH,qOAAqO;IACrO,MAAM,CAAC,aAAa;QAChB,OAAO;YACH,oBAAoB,CAAC,UAAU;YAC/B,oBAAoB,CAAC,iBAAiB;YACtC,oBAAoB,CAAC,cAAc;YACnC,oBAAoB,CAAC,SAAS;YAC9B,oBAAoB,CAAC,OAAO;YAC5B,oBAAoB,CAAC,MAAM;YAC3B,oBAAoB,CAAC,UAAU;YAC/B,oBAAoB,CAAC,SAAS;YAC9B,oBAAoB,CAAC,aAAa;YAClC,oBAAoB,CAAC,WAAW;YAChC,oBAAoB,CAAC,YAAY;YACjC,oBAAoB,CAAC,UAAU;YAC/B,oBAAoB,CAAC,MAAM;SAC9B,CAAC;IACN,CAAC;;AAtKL,oDAuKC","sourcesContent":["import { ContextKey, AnyContextKey } from '../ContextKey';\nimport { ApiCallInfo } from './ApiCallInfo';\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<string>('requestId', 'x-request-id');\n\n /**\n * WHICH SERVICE MINTED {@link REQUEST_ID} — the name from {@link ServiceInfo}, stamped by\n * `RequestContextHeaders.fillFromRequest` ONLY on the branch that generates a new id (i.e. when\n * the inbound request carried no `x-request-id`). It answers the question the id alone cannot:\n * \"this trace starts here — is that right?\" An id appearing with no source means it came from\n * outside; an id sourced by a service that should never be an entry point is a routing bug.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire, and that is the WHOLE POINT. If it\n * travelled, hop 2 would inherit it, hop 3 would inherit it, and \"who started this trace\"\n * would be indistinguishable from \"who passed it along\" — the origin, the one fact this key\n * carries, would be lost. It is absent on every hop that did NOT mint the id, which is exactly\n * the signal: present == I am the origin.\n * - `isLogged` TRUE → emitted as a plain string at `jsonPayload.requestIdSource`.\n */\n static readonly REQUEST_ID_SOURCE = new ContextKey<string>(\n 'requestIdSource',\n /*httpHeader*/ undefined\n );\n\n /**\n * The CALLER's build version — so a downstream server's logs record which build of the client\n * called it (surfaces as `jsonPayload.clientVersion`). Distinct from the log line's own `version`\n * (this service's build): `version` answers \"which build wrote this line?\", `clientVersion`\n * answers \"which build asked us to?\".\n *\n * - `httpHeader` SET → transferred over the wire, BUT unlike a normal transferred key it is NOT\n * copied from the context onward. Each hop OVERWRITES it with its OWN `ServiceInfo.getVersion()`\n * as it becomes the client to the next hop (see `buildOutboundHeaders`), so on any given server\n * `clientVersion` is always the IMMEDIATE caller's version, never a stale grand-caller's.\n * - `isLogged` TRUE → the inbound value lands in the context and flows through the normal log\n * field map; no backend change needed.\n */\n static readonly CLIENT_VERSION = new ContextKey<string>('clientVersion', 'x-webpieces-client-version');\n\n /**\n * A frontend/app-minted correlation id that groups every request triggered by ONE user ACTION.\n *\n * An \"action\" is a single thing the user did in the GUI — a CLICK on a button/link, or TYPING in a\n * field — or a background poller tick: anything that may fan out into MULTIPLE remote calls. That one\n * action fires 1..N browser HTTP calls, each of which gets its own framework-minted {@link REQUEST_ID}\n * (one per HTTP call, shared within that call's server→server subtree). `actionId` sits ABOVE\n * `requestId` and is what stitches those N requests back to the single action that caused them:\n *\n * actionId (app-minted, ONE per user action, rides EVERY call of that action)\n * └── 1..N requestId (framework-minted, ONE per HTTP call)\n *\n * Grep one `actionId` in the logs → every `requestId` it spawned, and every log line of the whole\n * action. Minted and refreshed by the app (a UI concern), carried under `x-webpieces-actionid`.\n *\n * Browser/app-minted ONLY: unlike {@link REQUEST_ID}, the framework transfers and logs it but must\n * NOT auto-mint one server-side. Absent `actionId` ⇒ a non-action flow (system / cron / task), which\n * is the correct signal.\n *\n * - `httpHeader` SET → transferred: copied off the inbound request into context and re-emitted on\n * outbound hops, so the id follows the action across services.\n * - `isLogged` TRUE → emitted as a plain string on every log line of the request.\n */\n static readonly ACTION_ID = new ContextKey<string>('actionId', 'x-webpieces-actionid');\n\n static readonly ORG_ID = new ContextKey<string>('orgId', 'x-org-id');\n\n static readonly USER_ID = new ContextKey<string>('userId', 'x-user-id');\n\n static readonly USER_ROLES = new ContextKey<string>('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<string>('recording', 'x-webpieces-recording');\n\n /**\n * The structured API-call tag ({@link ApiCallInfo}) stamped by {@link LogApiCall} around every\n * outbound (client) / inbound (server) call. It rides the magic context so EVERY log line emitted\n * during the call inherits a filterable `api` object, surfacing in GCP as nested\n * `jsonPayload.api.{side,type,result,path,method}`.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire. Per-hop only: each server/client hop\n * stamps its own tag, so a downstream server records `side:'server'`, never the caller's `side:'client'`.\n * - `isLogged` TRUE → emitted by the logging backends. It carries an OBJECT value, so the backends\n * read it via {@link HeaderRegistry.buildStructuredLogFields} (object-aware); the flat\n * `buildLogFields()` string map deliberately skips it (typeof-string guard).\n */\n static readonly API_CALL_INFO = new ContextKey<ApiCallInfo>('api', /*httpHeader*/ undefined, /*isSecured*/ false, /*isLogged*/ true);\n\n /**\n * The inbound request's HTTP method and path, stamped ONCE from the {@link HttpRequest} by\n * `RequestContextHeaders.fillFromRequest` (the atomic inbound choke point every transport funnels\n * through). They surface as top-level `jsonPayload.httpMethod` / `jsonPayload.requestPath` so every\n * log line of the request carries them — they used to ride inside {@link ApiCallInfo} (`api.path` /\n * `api.method`) but that coupled a per-CALL logger to the per-REQUEST transport shape.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire: a downstream hop stamps its OWN inbound\n * method/path, never the caller's. Outbound client calls never set these (no inbound path).\n * - `isLogged` TRUE → emitted by the logging backends as plain strings.\n */\n static readonly HTTP_METHOD = new ContextKey<string>('httpMethod', /*httpHeader*/ undefined, /*isSecured*/ false, /*isLogged*/ true);\n\n static readonly REQUEST_PATH = new ContextKey<string>('requestPath', /*httpHeader*/ undefined, /*isSecured*/ false, /*isLogged*/ true);\n\n /**\n * The routed endpoint's IMPLEMENTATION identity: the concrete controller class name\n * ({@link RouteMetadata.controllerClassName}, e.g. `LoginController`) and the handler method NAME\n * ({@link RouteMetadata.methodName}, e.g. `login`), stamped once per request by {@link LogApiFilter}\n * after route matching so every subsequent log line of the request carries them.\n *\n * These say WHICH CODE ran, which is what you actually grep for — far more useful than the raw\n * `requestPath`. They are the top-level, filterable twin of what previously only lived nested in\n * {@link ApiCallInfo} (`api.method.controllerName` / `api.method.methodName`). The local console\n * formatters render them together as a compact `[Controller.method]` bracket; GCP keeps them as two\n * separate `jsonPayload.controller` / `jsonPayload.method` fields.\n *\n * NOTE: `method` here is the CODE method name (e.g. `login`), NOT the HTTP verb — that is\n * {@link HTTP_METHOD} (`httpMethod`).\n *\n * - `httpHeader` UNDEFINED → NOT transferred: each hop stamps its OWN routed controller/method.\n * - `isLogged` TRUE → emitted by the logging backends as plain strings.\n */\n static readonly CONTROLLER = new ContextKey<string>('controller', /*httpHeader*/ undefined, /*isSecured*/ false, /*isLogged*/ true);\n\n static readonly METHOD = new ContextKey<string>('method', /*httpHeader*/ undefined, /*isSecured*/ false, /*isLogged*/ true);\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 // webpieces-disable no-function-outside-class -- pre-existing static key-registry accessor (a list of constants, not injectable behavior); pulled into modified-scope only by the ContextKey[] -> AnyContextKey[] return-type change\n static getAllHeaders(): AnyContextKey[] {\n return [\n WebpiecesCoreHeaders.REQUEST_ID,\n WebpiecesCoreHeaders.REQUEST_ID_SOURCE,\n WebpiecesCoreHeaders.CLIENT_VERSION,\n WebpiecesCoreHeaders.ACTION_ID,\n WebpiecesCoreHeaders.USER_ID,\n WebpiecesCoreHeaders.ORG_ID,\n WebpiecesCoreHeaders.USER_ROLES,\n WebpiecesCoreHeaders.RECORDING,\n WebpiecesCoreHeaders.API_CALL_INFO,\n WebpiecesCoreHeaders.HTTP_METHOD,\n WebpiecesCoreHeaders.REQUEST_PATH,\n WebpiecesCoreHeaders.CONTROLLER,\n WebpiecesCoreHeaders.METHOD,\n ];\n }\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export { toError } from './lib/errorUtils';
|
|
10
10
|
export { ContextKey } from './ContextKey';
|
|
11
|
+
export type { AnyContextKey } from './ContextKey';
|
|
11
12
|
export { ContextTuple } from './ContextTuple';
|
|
12
13
|
export { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';
|
|
13
14
|
export type { Logger, LogLevel } from './logging/Logger';
|
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;
|
|
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 type { AnyContextKey } 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';\nexport { LogChunker, LogChunkerImpl, LogChunkInfo, MAX_GCP_LOG_BYTES, GCP_LOG_BUDGET_BYTES } from './logging/LogChunker';\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 getEndpointOptions,\n isFormPost,\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, EndpointOptions } from './http/decorators';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { Secrets, 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 HttpTooManyRequestsError,\n HttpVendorError,\n HttpUserError,\n OfflineError,\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\nexport { NetworkRejectClassifier } from './http/networkReject';\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 { ClientRegistry } from './http/ClientRegistry';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// Pluggable, bidirectional error translation (app exception <-> wire form). Registered on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport { ErrorWireForm } from './http/ErrorTranslation';\nexport type { ErrorTranslation } from './http/ErrorTranslation';\n// Pluggable per-client failure classification (is a thrown API-call error a real failure or an\n// expected non-failure?). Registered on ClientRegistry at startup; consulted by LogApiCall.\nexport type { FailureClassifier } from './http/FailureClassifier';\nexport { KeyedFailureClassifier } from './http/FailureClassifier';\nexport {\n WebpiecesDefaultFailureClassifier,\n WEBPIECES_DEFAULT_FAILURE_CLASSIFIER,\n} from './http/WebpiecesDefaultFailureClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } 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). Singleton: use the LogApiCall constant, not `new`.\nexport { LogApiCall, LogApiCallImpl } from './http/LogApiCall';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl is installed by @webpieces/core-context; the browser gets the no-op.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiType, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport { ApiCallLogName, ApiCallLogNameImpl, LOG_API_CALL_LOGGER_NAME } from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport { ApiCallContextHolder } from './http/ApiCallContext';\nexport type { ApiCallContext } from './http/ApiCallContext';\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"]}
|