@webpieces/core-context 0.3.383 → 0.3.384

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-context",
3
- "version": "0.3.383",
3
+ "version": "0.3.384",
4
4
  "description": "AsyncLocalStorage-based context management for request-scoped data",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,7 +22,7 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@webpieces/core-util": "0.3.383",
25
+ "@webpieces/core-util": "0.3.384",
26
26
  "@inversifyjs/binding-decorators": "1.1.5",
27
27
  "inversify": "7.10.4",
28
28
  "reflect-metadata": "0.2.2"
@@ -37,6 +37,8 @@ declare class RequestContextImpl {
37
37
  runWithContext<T>(context: Map<string, any>, fn: () => T): T;
38
38
  getHeader<T = unknown>(key: ContextKey): T | undefined;
39
39
  putHeader(key: ContextKey, value: unknown): void;
40
+ /** Clear one context key. Used by the api-tag seam's set → log → remove span (see LogApiCall). */
41
+ removeHeader(key: ContextKey): void;
40
42
  hasHeader(key: ContextKey): boolean;
41
43
  /**
42
44
  /**
@@ -53,6 +55,16 @@ declare class RequestContextImpl {
53
55
  * log line is never worth crashing a request over.
54
56
  */
55
57
  buildLogFields(): Map<string, string>;
58
+ /**
59
+ * The STRUCTURED field map for the node logging backends: like {@link buildLogFields}, but values
60
+ * may be OBJECTS, so an object-valued logged key ({@link WebpiecesCoreHeaders.API_CALL_INFO} holding
61
+ * an {@link ApiCallInfo}) survives as an object and the winston/bunyan backends nest it into
62
+ * `jsonPayload.api`. Reads values UNTYPED (not `<string>`) so the object comes through intact.
63
+ *
64
+ * Returns an EMPTY map outside a `run(...)` block (a log line is never worth crashing over) — same
65
+ * as {@link buildLogFields}.
66
+ */
67
+ buildStructuredLogFields(): Map<string, string | object>;
56
68
  /**
57
69
  * Store the transport-neutral {@link HttpRequest} for this request. Called once, above the
58
70
  * api boundary, by whichever transport is driving the router (the express adapter, or the
@@ -61,6 +61,10 @@ class RequestContextImpl {
61
61
  putHeader(key, value) {
62
62
  this.put(key.name, value);
63
63
  }
64
+ /** Clear one context key. Used by the api-tag seam's set → log → remove span (see LogApiCall). */
65
+ removeHeader(key) {
66
+ this.remove(key.name);
67
+ }
64
68
  hasHeader(key) {
65
69
  return this.has(key.name);
66
70
  }
@@ -86,6 +90,21 @@ class RequestContextImpl {
86
90
  // WHERE to read from. The browser's ContextMgr calls the same method with its store's read.
87
91
  return core_util_1.HeaderRegistry.get().buildLogFields((key) => this.getHeader(key));
88
92
  }
93
+ /**
94
+ * The STRUCTURED field map for the node logging backends: like {@link buildLogFields}, but values
95
+ * may be OBJECTS, so an object-valued logged key ({@link WebpiecesCoreHeaders.API_CALL_INFO} holding
96
+ * an {@link ApiCallInfo}) survives as an object and the winston/bunyan backends nest it into
97
+ * `jsonPayload.api`. Reads values UNTYPED (not `<string>`) so the object comes through intact.
98
+ *
99
+ * Returns an EMPTY map outside a `run(...)` block (a log line is never worth crashing over) — same
100
+ * as {@link buildLogFields}.
101
+ */
102
+ buildStructuredLogFields() {
103
+ if (!this.isActive()) {
104
+ return new Map();
105
+ }
106
+ return core_util_1.HeaderRegistry.get().buildStructuredLogFields((key) => this.getHeader(key));
107
+ }
89
108
  /**
90
109
  * Store the transport-neutral {@link HttpRequest} for this request. Called once, above the
91
110
  * api boundary, by whichever transport is driving the router (the express adapter, or the
@@ -1 +1 @@
1
- {"version":3,"file":"RequestContext.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/RequestContext.ts"],"names":[],"mappings":";;;AAAA,6CAAgD;AAChD,oDAAkE;AAGlE,0EAA0E;AAC1E,MAAM,gBAAgB,GAAG,4BAA4B,CAAC;AAEtD;;;;;;;;;;;;;GAaG;AACH,MAAM,kBAAkB;IACZ,OAAO,CAAsC;IAErD;QACI,IAAI,CAAC,OAAO,GAAG,IAAI,+BAAiB,EAAoB,CAAC;IAC7D,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,GAAG,CAAI,EAAW;QACd,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CACX,qFAAqF;gBACrF,uFAAuF;gBACvF,+EAA+E,CAClF,CAAC;QACN,CAAC;QACD,yGAAyG;QACzG,MAAM,KAAK,GAAG,IAAI,GAAG,EAAe,CAAC;QACrC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACH,cAAc,CAAI,OAAyB,EAAE,EAAW;QACpD,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,yGAAyG;IACzG,SAAS,CAAc,GAAe;QAClC,OAAO,IAAI,CAAC,GAAG,CAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,yGAAyG;IACzG,SAAS,CAAC,GAAe,EAAE,KAAc;QACrC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,SAAS,CAAC,GAAe;QACrB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,cAAc;QACV,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;YACnB,OAAO,IAAI,GAAG,EAAkB,CAAC;QACrC,CAAC;QACD,qFAAqF;QACrF,4FAA4F;QAC5F,OAAO,0BAAc,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,GAAe,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAS,GAAG,CAAC,CAAC,CAAC;IACjG,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,OAAoB;QAC3B,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,sFAAsF;IACtF,UAAU;QACN,OAAO,IAAI,CAAC,GAAG,CAAc,gBAAgB,CAAC,CAAC;IACnD,CAAC;IAED,yGAAyG;IACzG,UAAU,CAAC,IAAkB;QACzB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,GAAW,EAAE,KAAU;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC/E,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,GAAG,CAAU,GAAW;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtC,OAAO,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,GAAW;QACd,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,KAAK;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtC,KAAK,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED;;;OAGG;IACH,WAAW;QACP,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO,IAAI,GAAG,EAAE,CAAC;QACrB,CAAC;QACD,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACH,UAAU,CAAC,OAAyB;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC/E,CAAC;QACD,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAC3B,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;OAEG;IACH,MAAM;QACF,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtC,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,GAAW;QACX,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtC,OAAO,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC;IACpC,CAAC;IAED;;;;;OAKG;IACH,QAAQ;QACJ,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,SAAS,CAAC;IACjD,CAAC;CAEJ;AAID;;;GAGG;AACU,QAAA,cAAc,GAAG,IAAI,kBAAkB,EAAE,CAAC","sourcesContent":["import { AsyncLocalStorage } from 'async_hooks';\nimport { ContextKey, HeaderRegistry } from '@webpieces/core-util';\nimport { HttpRequest } from './HttpRequest';\n\n/** Reserved context key under which the current HttpRequest is stored. */\nconst HTTP_REQUEST_KEY = '__webpieces_http_request__';\n\n/**\n * Context management using AsyncLocalStorage.\n * Similar to Java WebPieces Context class that uses ThreadLocal.\n *\n * This allows storing request-scoped data that is automatically available\n * throughout the async call chain, similar to MDC (Mapped Diagnostic Context).\n *\n * Example usage:\n * ```typescript\n * Context.put('REQUEST_ID', '12345');\n * await someAsyncOperation();\n * const id = Context.get('REQUEST_ID'); // Still available!\n * ```\n */\nclass RequestContextImpl {\n private storage: AsyncLocalStorage<Map<string, any>>;\n\n constructor() {\n this.storage = new AsyncLocalStorage<Map<string, any>>();\n }\n\n /**\n * Open THE request scope. A transport calls this once, at the beginning of a request.\n *\n * Nesting is a bug, not a feature, so it throws. AsyncLocalStorage would happily let a second\n * `run()` install a fresh empty Map that SHADOWS the outer one: every value the outer scope\n * holds becomes invisible, `fillFromRequest` mints a second request id, and the two halves of a\n * request end up in different traces. Nothing would tell you.\n *\n * With this guard the setup is right or it is loud. It mirrors\n * `RequestContextHeaders.fillFromRequest()`, which throws when there is NO active scope.\n *\n * @throws Error when a RequestContext is already active.\n */\n run<T>(fn: () => T): T {\n if (this.isActive()) {\n throw new Error(\n 'RequestContext.run(...) called inside an active RequestContext. Nesting installs a ' +\n 'fresh empty context that shadows the outer one: its values go invisible and a second ' +\n 'request id is minted. Exactly ONE scope per request — the transport opens it.',\n );\n }\n // webpieces-disable no-any-unknown -- context values are heterogeneous (strings, recorder, meta objects)\n const store = new Map<string, any>();\n return this.storage.run(store, fn);\n }\n\n /**\n * Run a function with a specific context.\n */\n runWithContext<T>(context: Map<string, any>, fn: () => T): T {\n return this.storage.run(context, fn);\n }\n\n // webpieces-disable no-any-unknown -- context values are heterogeneous (strings, recorder, meta objects)\n getHeader<T = unknown>(key: ContextKey): T | undefined {\n return this.get<T>(key.name);\n }\n\n // webpieces-disable no-any-unknown -- context values are heterogeneous (strings, recorder, meta objects)\n putHeader(key: ContextKey, value: unknown): void {\n this.put(key.name, value);\n }\n\n hasHeader(key: ContextKey): boolean {\n return this.has(key.name);\n }\n\n /**\n /**\n * Build the masked field map for LOGGING: every logged key in the global\n * {@link HeaderRegistry} read straight from this context, secured values\n * masked (via {@link ContextKey.maskIfSecured}), keyed by each key's `name`.\n *\n * Callers: RecordingFilter + NodeProxyClient.recordCall, which snapshot the context into a\n * test FIXTURE. The @webpieces/winston and @webpieces/bunyan backends also stamp these fields\n * onto every record, and they own the \"log emitted outside RequestContext.run(...)\" complaint —\n * reporting it HERE would recurse (the error line itself re-enters buildLogFields).\n *\n * Returns an EMPTY map outside a `run(...)` block rather than throwing: a fixture snapshot or a\n * log line is never worth crashing a request over.\n */\n buildLogFields(): Map<string, string> {\n if (!this.isActive()) {\n return new Map<string, string>();\n }\n // The registry owns the keys and each ContextKey masks its own value; we only supply\n // WHERE to read from. The browser's ContextMgr calls the same method with its store's read.\n return HeaderRegistry.get().buildLogFields((key: ContextKey) => this.getHeader<string>(key));\n }\n\n /**\n * Store the transport-neutral {@link HttpRequest} for this request. Called once, above the\n * api boundary, by whichever transport is driving the router (the express adapter, or the\n * in-process client). Filters/auth read it back via {@link getRequest} so they never touch\n * express — the same chain then runs over HTTP and in-process.\n */\n setRequest(request: HttpRequest): void {\n this.put(HTTP_REQUEST_KEY, request);\n }\n\n /** The current {@link HttpRequest}, or undefined if none was set for this context. */\n getRequest(): HttpRequest | undefined {\n return this.get<HttpRequest>(HTTP_REQUEST_KEY);\n }\n\n // webpieces-disable no-any-unknown -- context values are heterogeneous (strings, recorder, meta objects)\n getHeaders(keys: ContextKey[]): unknown[] {\n return keys.map(key => this.getHeader(key));\n }\n\n /**\n * Store a value in the current context.\n */\n put(key: string, value: any): void {\n const store = this.storage.getStore();\n if (!store) {\n throw new Error('No context available. Did you call Context.run() first?');\n }\n store.set(key, value);\n }\n\n /**\n * Retrieve a value from the current context.\n */\n get<T = any>(key: string): T | undefined {\n const store = this.storage.getStore();\n return store?.get(key);\n }\n\n /**\n * Remove a value from the current context.\n */\n remove(key: string): void {\n const store = this.storage.getStore();\n store?.delete(key);\n }\n\n /**\n * Clear all values from the current context.\n */\n clear(): void {\n const store = this.storage.getStore();\n store?.clear();\n }\n\n /**\n * Copy the current context to a new Map.\n * Used by XPromise to preserve context across async boundaries.\n */\n copyContext(): Map<string, any> {\n const store = this.storage.getStore();\n if (!store) {\n return new Map();\n }\n return new Map(store);\n }\n\n /**\n * Set the entire context from a Map.\n * Used by XPromise to restore context.\n */\n setContext(context: Map<string, any>): void {\n const store = this.storage.getStore();\n if (!store) {\n throw new Error('No context available. Did you call Context.run() first?');\n }\n store.clear();\n context.forEach((value, key) => {\n store.set(key, value);\n });\n }\n\n /**\n * Get all context entries.\n */\n getAll(): Map<string, any> {\n const store = this.storage.getStore();\n return store ? new Map(store) : new Map();\n }\n\n /**\n * Check if a key exists in the context.\n */\n has(key: string): boolean {\n const store = this.storage.getStore();\n return store?.has(key) ?? false;\n }\n\n /**\n * Check if RequestContext is currently active.\n * Returns true if we're inside a RequestContext.run() block, false otherwise.\n *\n * Useful for tests to verify context is set up before making API calls.\n */\n isActive(): boolean {\n return this.storage.getStore() !== undefined;\n }\n\n}\n\n\n\n/**\n * Global singleton instance of RequestContext.\n * Use this throughout your application.\n */\nexport const RequestContext = new RequestContextImpl();\n"]}
1
+ {"version":3,"file":"RequestContext.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/RequestContext.ts"],"names":[],"mappings":"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","sourcesContent":["import { AsyncLocalStorage } from 'async_hooks';\nimport { ContextKey, HeaderRegistry } from '@webpieces/core-util';\nimport { HttpRequest } from './HttpRequest';\n\n/** Reserved context key under which the current HttpRequest is stored. */\nconst HTTP_REQUEST_KEY = '__webpieces_http_request__';\n\n/**\n * Context management using AsyncLocalStorage.\n * Similar to Java WebPieces Context class that uses ThreadLocal.\n *\n * This allows storing request-scoped data that is automatically available\n * throughout the async call chain, similar to MDC (Mapped Diagnostic Context).\n *\n * Example usage:\n * ```typescript\n * Context.put('REQUEST_ID', '12345');\n * await someAsyncOperation();\n * const id = Context.get('REQUEST_ID'); // Still available!\n * ```\n */\nclass RequestContextImpl {\n private storage: AsyncLocalStorage<Map<string, any>>;\n\n constructor() {\n this.storage = new AsyncLocalStorage<Map<string, any>>();\n }\n\n /**\n * Open THE request scope. A transport calls this once, at the beginning of a request.\n *\n * Nesting is a bug, not a feature, so it throws. AsyncLocalStorage would happily let a second\n * `run()` install a fresh empty Map that SHADOWS the outer one: every value the outer scope\n * holds becomes invisible, `fillFromRequest` mints a second request id, and the two halves of a\n * request end up in different traces. Nothing would tell you.\n *\n * With this guard the setup is right or it is loud. It mirrors\n * `RequestContextHeaders.fillFromRequest()`, which throws when there is NO active scope.\n *\n * @throws Error when a RequestContext is already active.\n */\n run<T>(fn: () => T): T {\n if (this.isActive()) {\n throw new Error(\n 'RequestContext.run(...) called inside an active RequestContext. Nesting installs a ' +\n 'fresh empty context that shadows the outer one: its values go invisible and a second ' +\n 'request id is minted. Exactly ONE scope per request — the transport opens it.',\n );\n }\n // webpieces-disable no-any-unknown -- context values are heterogeneous (strings, recorder, meta objects)\n const store = new Map<string, any>();\n return this.storage.run(store, fn);\n }\n\n /**\n * Run a function with a specific context.\n */\n runWithContext<T>(context: Map<string, any>, fn: () => T): T {\n return this.storage.run(context, fn);\n }\n\n // webpieces-disable no-any-unknown -- context values are heterogeneous (strings, recorder, meta objects)\n getHeader<T = unknown>(key: ContextKey): T | undefined {\n return this.get<T>(key.name);\n }\n\n // webpieces-disable no-any-unknown -- context values are heterogeneous (strings, recorder, meta objects)\n putHeader(key: ContextKey, value: unknown): void {\n this.put(key.name, value);\n }\n\n /** Clear one context key. Used by the api-tag seam's set → log → remove span (see LogApiCall). */\n removeHeader(key: ContextKey): void {\n this.remove(key.name);\n }\n\n hasHeader(key: ContextKey): boolean {\n return this.has(key.name);\n }\n\n /**\n /**\n * Build the masked field map for LOGGING: every logged key in the global\n * {@link HeaderRegistry} read straight from this context, secured values\n * masked (via {@link ContextKey.maskIfSecured}), keyed by each key's `name`.\n *\n * Callers: RecordingFilter + NodeProxyClient.recordCall, which snapshot the context into a\n * test FIXTURE. The @webpieces/winston and @webpieces/bunyan backends also stamp these fields\n * onto every record, and they own the \"log emitted outside RequestContext.run(...)\" complaint —\n * reporting it HERE would recurse (the error line itself re-enters buildLogFields).\n *\n * Returns an EMPTY map outside a `run(...)` block rather than throwing: a fixture snapshot or a\n * log line is never worth crashing a request over.\n */\n buildLogFields(): Map<string, string> {\n if (!this.isActive()) {\n return new Map<string, string>();\n }\n // The registry owns the keys and each ContextKey masks its own value; we only supply\n // WHERE to read from. The browser's ContextMgr calls the same method with its store's read.\n return HeaderRegistry.get().buildLogFields((key: ContextKey) => this.getHeader<string>(key));\n }\n\n /**\n * The STRUCTURED field map for the node logging backends: like {@link buildLogFields}, but values\n * may be OBJECTS, so an object-valued logged key ({@link WebpiecesCoreHeaders.API_CALL_INFO} holding\n * an {@link ApiCallInfo}) survives as an object and the winston/bunyan backends nest it into\n * `jsonPayload.api`. Reads values UNTYPED (not `<string>`) so the object comes through intact.\n *\n * Returns an EMPTY map outside a `run(...)` block (a log line is never worth crashing over) — same\n * as {@link buildLogFields}.\n */\n buildStructuredLogFields(): Map<string, string | object> {\n if (!this.isActive()) {\n return new Map<string, string | object>();\n }\n return HeaderRegistry.get().buildStructuredLogFields((key: ContextKey) => this.getHeader(key));\n }\n\n\n /**\n * Store the transport-neutral {@link HttpRequest} for this request. Called once, above the\n * api boundary, by whichever transport is driving the router (the express adapter, or the\n * in-process client). Filters/auth read it back via {@link getRequest} so they never touch\n * express — the same chain then runs over HTTP and in-process.\n */\n setRequest(request: HttpRequest): void {\n this.put(HTTP_REQUEST_KEY, request);\n }\n\n /** The current {@link HttpRequest}, or undefined if none was set for this context. */\n getRequest(): HttpRequest | undefined {\n return this.get<HttpRequest>(HTTP_REQUEST_KEY);\n }\n\n // webpieces-disable no-any-unknown -- context values are heterogeneous (strings, recorder, meta objects)\n getHeaders(keys: ContextKey[]): unknown[] {\n return keys.map(key => this.getHeader(key));\n }\n\n /**\n * Store a value in the current context.\n */\n put(key: string, value: any): void {\n const store = this.storage.getStore();\n if (!store) {\n throw new Error('No context available. Did you call Context.run() first?');\n }\n store.set(key, value);\n }\n\n /**\n * Retrieve a value from the current context.\n */\n get<T = any>(key: string): T | undefined {\n const store = this.storage.getStore();\n return store?.get(key);\n }\n\n /**\n * Remove a value from the current context.\n */\n remove(key: string): void {\n const store = this.storage.getStore();\n store?.delete(key);\n }\n\n /**\n * Clear all values from the current context.\n */\n clear(): void {\n const store = this.storage.getStore();\n store?.clear();\n }\n\n /**\n * Copy the current context to a new Map.\n * Used by XPromise to preserve context across async boundaries.\n */\n copyContext(): Map<string, any> {\n const store = this.storage.getStore();\n if (!store) {\n return new Map();\n }\n return new Map(store);\n }\n\n /**\n * Set the entire context from a Map.\n * Used by XPromise to restore context.\n */\n setContext(context: Map<string, any>): void {\n const store = this.storage.getStore();\n if (!store) {\n throw new Error('No context available. Did you call Context.run() first?');\n }\n store.clear();\n context.forEach((value, key) => {\n store.set(key, value);\n });\n }\n\n /**\n * Get all context entries.\n */\n getAll(): Map<string, any> {\n const store = this.storage.getStore();\n return store ? new Map(store) : new Map();\n }\n\n /**\n * Check if a key exists in the context.\n */\n has(key: string): boolean {\n const store = this.storage.getStore();\n return store?.has(key) ?? false;\n }\n\n /**\n * Check if RequestContext is currently active.\n * Returns true if we're inside a RequestContext.run() block, false otherwise.\n *\n * Useful for tests to verify context is set up before making API calls.\n */\n isActive(): boolean {\n return this.storage.getStore() !== undefined;\n }\n\n}\n\n\n\n/**\n * Global singleton instance of RequestContext.\n * Use this throughout your application.\n */\nexport const RequestContext = new RequestContextImpl();\n"]}
@@ -0,0 +1,21 @@
1
+ import { ApiCallContext, ContextKey } from '@webpieces/core-util';
2
+ /**
3
+ * RequestContextApiCallContext - the SERVER (Node) implementation of the {@link ApiCallContext} seam,
4
+ * backing it with the ambient {@link RequestContext} (AsyncLocalStorage). {@link LogApiCall} — which
5
+ * lives in browser-safe core-util and cannot import RequestContext — stamps its `api` tag through this.
6
+ *
7
+ * Installed ONCE at server startup by `setupRuntime` (http-routing), beside `HeaderRegistry.configure`
8
+ * and `LogManager.setFactory` — the one place that runs on every server, so BOTH inbound (LogApiFilter)
9
+ * and outbound (clients) get the tag. A browser never runs setupRuntime, so it installs its own
10
+ * module-global impl. This is the same "impl in the env-specific package, bound to a core-util seam at
11
+ * startup" pattern as LogManager.setFactory + the winston/bunyan backends.
12
+ *
13
+ * WRITE-ONLY: the logging backends read the stamped key back off RequestContext
14
+ * (`buildStructuredLogFields`) on every record, so this seam only needs to set it.
15
+ */
16
+ export declare class RequestContextApiCallContext implements ApiCallContext {
17
+ /** A live request scope is required to stamp; LogApiCall checks this and throws when false. */
18
+ isActive(): boolean;
19
+ set(contextKey: ContextKey, value: unknown): void;
20
+ remove(contextKey: ContextKey): void;
21
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RequestContextApiCallContext = void 0;
4
+ const RequestContext_1 = require("./RequestContext");
5
+ /**
6
+ * RequestContextApiCallContext - the SERVER (Node) implementation of the {@link ApiCallContext} seam,
7
+ * backing it with the ambient {@link RequestContext} (AsyncLocalStorage). {@link LogApiCall} — which
8
+ * lives in browser-safe core-util and cannot import RequestContext — stamps its `api` tag through this.
9
+ *
10
+ * Installed ONCE at server startup by `setupRuntime` (http-routing), beside `HeaderRegistry.configure`
11
+ * and `LogManager.setFactory` — the one place that runs on every server, so BOTH inbound (LogApiFilter)
12
+ * and outbound (clients) get the tag. A browser never runs setupRuntime, so it installs its own
13
+ * module-global impl. This is the same "impl in the env-specific package, bound to a core-util seam at
14
+ * startup" pattern as LogManager.setFactory + the winston/bunyan backends.
15
+ *
16
+ * WRITE-ONLY: the logging backends read the stamped key back off RequestContext
17
+ * (`buildStructuredLogFields`) on every record, so this seam only needs to set it.
18
+ */
19
+ class RequestContextApiCallContext {
20
+ /** A live request scope is required to stamp; LogApiCall checks this and throws when false. */
21
+ isActive() {
22
+ return RequestContext_1.RequestContext.isActive();
23
+ }
24
+ // webpieces-disable no-any-unknown -- a context value is heterogeneous (the api struct here; strings elsewhere)
25
+ set(contextKey, value) {
26
+ RequestContext_1.RequestContext.putHeader(contextKey, value);
27
+ }
28
+ remove(contextKey) {
29
+ RequestContext_1.RequestContext.removeHeader(contextKey);
30
+ }
31
+ }
32
+ exports.RequestContextApiCallContext = RequestContextApiCallContext;
33
+ //# sourceMappingURL=RequestContextApiCallContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RequestContextApiCallContext.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/RequestContextApiCallContext.ts"],"names":[],"mappings":";;;AACA,qDAAkD;AAElD;;;;;;;;;;;;;GAaG;AACH,MAAa,4BAA4B;IACrC,+FAA+F;IAC/F,QAAQ;QACJ,OAAO,+BAAc,CAAC,QAAQ,EAAE,CAAC;IACrC,CAAC;IAED,gHAAgH;IAChH,GAAG,CAAC,UAAsB,EAAE,KAAc;QACtC,+BAAc,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,CAAC,UAAsB;QACzB,+BAAc,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;IAC5C,CAAC;CACJ;AAdD,oEAcC","sourcesContent":["import { ApiCallContext, ContextKey } from '@webpieces/core-util';\nimport { RequestContext } from './RequestContext';\n\n/**\n * RequestContextApiCallContext - the SERVER (Node) implementation of the {@link ApiCallContext} seam,\n * backing it with the ambient {@link RequestContext} (AsyncLocalStorage). {@link LogApiCall} — which\n * lives in browser-safe core-util and cannot import RequestContext — stamps its `api` tag through this.\n *\n * Installed ONCE at server startup by `setupRuntime` (http-routing), beside `HeaderRegistry.configure`\n * and `LogManager.setFactory` — the one place that runs on every server, so BOTH inbound (LogApiFilter)\n * and outbound (clients) get the tag. A browser never runs setupRuntime, so it installs its own\n * module-global impl. This is the same \"impl in the env-specific package, bound to a core-util seam at\n * startup\" pattern as LogManager.setFactory + the winston/bunyan backends.\n *\n * WRITE-ONLY: the logging backends read the stamped key back off RequestContext\n * (`buildStructuredLogFields`) on every record, so this seam only needs to set it.\n */\nexport class RequestContextApiCallContext implements ApiCallContext {\n /** A live request scope is required to stamp; LogApiCall checks this and throws when false. */\n isActive(): boolean {\n return RequestContext.isActive();\n }\n\n // webpieces-disable no-any-unknown -- a context value is heterogeneous (the api struct here; strings elsewhere)\n set(contextKey: ContextKey, value: unknown): void {\n RequestContext.putHeader(contextKey, value);\n }\n\n remove(contextKey: ContextKey): void {\n RequestContext.removeHeader(contextKey);\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { RequestContext } from './RequestContext';
2
+ export { RequestContextApiCallContext } from './RequestContextApiCallContext';
2
3
  export { HttpRequest } from './HttpRequest';
3
4
  export { provideSingleton, provideSingletonDefaultForApi, provideTransient } from './provide';
4
5
  export { Provider } from './provide';
package/src/index.js CHANGED
@@ -1,9 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RequestContextReader = exports.RequestContextHeaders = exports.buildFrameworkModule = exports.bindFrameworkProvider = exports.provideFrameworkTransient = exports.provideFrameworkSingletonDefaultForApi = exports.provideFrameworkSingleton = exports.Provider = exports.provideTransient = exports.provideSingletonDefaultForApi = exports.provideSingleton = exports.HttpRequest = exports.RequestContext = void 0;
3
+ exports.RequestContextReader = exports.RequestContextHeaders = exports.buildFrameworkModule = exports.bindFrameworkProvider = exports.provideFrameworkTransient = exports.provideFrameworkSingletonDefaultForApi = exports.provideFrameworkSingleton = exports.Provider = exports.provideTransient = exports.provideSingletonDefaultForApi = exports.provideSingleton = exports.HttpRequest = exports.RequestContextApiCallContext = exports.RequestContext = void 0;
4
4
  // Context management with AsyncLocalStorage
5
5
  var RequestContext_1 = require("./RequestContext");
6
6
  Object.defineProperty(exports, "RequestContext", { enumerable: true, get: function () { return RequestContext_1.RequestContext; } });
7
+ // SERVER impl of the core-util ApiCallContext seam, bound to RequestContext. Importing it here runs
8
+ // its install() side effect, so LogApiCall (core-util, browser-safe) stamps the real RequestContext on
9
+ // a Node server without importing it. A browser never loads core-context → keeps the no-op.
10
+ var RequestContextApiCallContext_1 = require("./RequestContextApiCallContext");
11
+ Object.defineProperty(exports, "RequestContextApiCallContext", { enumerable: true, get: function () { return RequestContextApiCallContext_1.RequestContextApiCallContext; } });
7
12
  // Transport-neutral request stored in the context (http-routing's request type; re-exported there)
8
13
  var HttpRequest_1 = require("./HttpRequest");
9
14
  Object.defineProperty(exports, "HttpRequest", { enumerable: true, get: function () { return HttpRequest_1.HttpRequest; } });
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/index.ts"],"names":[],"mappings":";;;AAAA,4CAA4C;AAC5C,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,mGAAmG;AACnG,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AAEpB,mFAAmF;AACnF,qCAA8F;AAArF,2GAAA,gBAAgB,OAAA;AAAE,wHAAA,6BAA6B,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAC1E,2FAA2F;AAC3F,qCAAqC;AAA5B,mGAAA,QAAQ,OAAA;AACjB,sFAAsF;AACtF,wEAAwE;AACxE,uDAM4B;AALxB,6HAAA,yBAAyB,OAAA;AACzB,0IAAA,sCAAsC,OAAA;AACtC,6HAAA,yBAAyB,OAAA;AACzB,yHAAA,qBAAqB,OAAA;AACrB,wHAAA,oBAAoB,OAAA;AAIxB,mFAAmF;AACnF,yFAAyF;AACzF,yBAAyB;AACzB,EAAE;AACF,8FAA8F;AAC9F,2FAA2F;AAC3F,uDAAuD;AACvD,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,oGAAoG;AACpG,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA","sourcesContent":["// Context management with AsyncLocalStorage\nexport { RequestContext } from './RequestContext';\n// Transport-neutral request stored in the context (http-routing's request type; re-exported there)\nexport { HttpRequest } from './HttpRequest';\n\n// DI provider decorators (shared DI seam; http-routing re-exports for back-compat)\nexport { provideSingleton, provideSingletonDefaultForApi, provideTransient } from './provide';\n// Guice-style Provider<T> — lazy singleton OR fresh-per-get, decided by T's binding scope.\nexport { Provider } from './provide';\n// Framework-only DI registry (packages/** use these; keeps framework classes out of a\n// client's buildProviderModule() global scan). See frameworkProvide.ts.\nexport {\n provideFrameworkSingleton,\n provideFrameworkSingletonDefaultForApi,\n provideFrameworkTransient,\n bindFrameworkProvider,\n buildFrameworkModule,\n} from './frameworkProvide';\nexport type { FrameworkScope } from './frameworkProvide';\n\n// Outbound headers for a SERVER: reads RequestContext directly, fails fast outside\n// RequestContext.run(...). Server-side clients (http-client-node, cloudtasks-client) and\n// http-routing use THIS.\n//\n// ContextMgr is deliberately NOT re-exported. It is the browser's answer (an app-held store),\n// and only @webpieces/http-client-browser may name it — importing it here would let a node\n// package reach for a ContextReader it has no use for.\nexport { RequestContextHeaders } from './RequestContextHeaders';\n// The browser store's server counterpart, still used by the logging packages + http-server filters.\nexport { RequestContextReader } from './RequestContextReader';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/index.ts"],"names":[],"mappings":";;;AAAA,4CAA4C;AAC5C,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,oGAAoG;AACpG,uGAAuG;AACvG,4FAA4F;AAC5F,+EAA8E;AAArE,4IAAA,4BAA4B,OAAA;AACrC,mGAAmG;AACnG,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AAEpB,mFAAmF;AACnF,qCAA8F;AAArF,2GAAA,gBAAgB,OAAA;AAAE,wHAAA,6BAA6B,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAC1E,2FAA2F;AAC3F,qCAAqC;AAA5B,mGAAA,QAAQ,OAAA;AACjB,sFAAsF;AACtF,wEAAwE;AACxE,uDAM4B;AALxB,6HAAA,yBAAyB,OAAA;AACzB,0IAAA,sCAAsC,OAAA;AACtC,6HAAA,yBAAyB,OAAA;AACzB,yHAAA,qBAAqB,OAAA;AACrB,wHAAA,oBAAoB,OAAA;AAIxB,mFAAmF;AACnF,yFAAyF;AACzF,yBAAyB;AACzB,EAAE;AACF,8FAA8F;AAC9F,2FAA2F;AAC3F,uDAAuD;AACvD,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,oGAAoG;AACpG,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA","sourcesContent":["// Context management with AsyncLocalStorage\nexport { RequestContext } from './RequestContext';\n// SERVER impl of the core-util ApiCallContext seam, bound to RequestContext. Importing it here runs\n// its install() side effect, so LogApiCall (core-util, browser-safe) stamps the real RequestContext on\n// a Node server without importing it. A browser never loads core-context → keeps the no-op.\nexport { RequestContextApiCallContext } from './RequestContextApiCallContext';\n// Transport-neutral request stored in the context (http-routing's request type; re-exported there)\nexport { HttpRequest } from './HttpRequest';\n\n// DI provider decorators (shared DI seam; http-routing re-exports for back-compat)\nexport { provideSingleton, provideSingletonDefaultForApi, provideTransient } from './provide';\n// Guice-style Provider<T> — lazy singleton OR fresh-per-get, decided by T's binding scope.\nexport { Provider } from './provide';\n// Framework-only DI registry (packages/** use these; keeps framework classes out of a\n// client's buildProviderModule() global scan). See frameworkProvide.ts.\nexport {\n provideFrameworkSingleton,\n provideFrameworkSingletonDefaultForApi,\n provideFrameworkTransient,\n bindFrameworkProvider,\n buildFrameworkModule,\n} from './frameworkProvide';\nexport type { FrameworkScope } from './frameworkProvide';\n\n// Outbound headers for a SERVER: reads RequestContext directly, fails fast outside\n// RequestContext.run(...). Server-side clients (http-client-node, cloudtasks-client) and\n// http-routing use THIS.\n//\n// ContextMgr is deliberately NOT re-exported. It is the browser's answer (an app-held store),\n// and only @webpieces/http-client-browser may name it — importing it here would let a node\n// package reach for a ContextReader it has no use for.\nexport { RequestContextHeaders } from './RequestContextHeaders';\n// The browser store's server counterpart, still used by the logging packages + http-server filters.\nexport { RequestContextReader } from './RequestContextReader';\n"]}