@webpieces/core-util 0.4.936 → 0.4.937
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/http/LogApiCall.d.ts +76 -0
- package/src/http/LogApiCall.js +128 -3
- package/src/http/LogApiCall.js.map +1 -1
- package/src/index.d.ts +3 -2
- package/src/index.js +8 -2
- package/src/index.js.map +1 -1
package/package.json
CHANGED
package/src/http/LogApiCall.d.ts
CHANGED
|
@@ -1,5 +1,80 @@
|
|
|
1
1
|
import { ApiMethodInfo } from './ApiMethodInfo';
|
|
2
2
|
import { ApiCallContext } from './ApiCallContext';
|
|
3
|
+
/**
|
|
4
|
+
* The startup configuration for {@link LogApiCall.configure}.
|
|
5
|
+
*
|
|
6
|
+
* A class rather than an object literal (CLAUDE.md: data-only structures are classes), so the one call
|
|
7
|
+
* site reads `LogApiCall.configure(new LogApiCallConfig(2000))`.
|
|
8
|
+
*/
|
|
9
|
+
export declare class LogApiCallConfig {
|
|
10
|
+
readonly maxLoggedBodyChars?: number | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* @param maxLoggedBodyChars - the most characters of a serialized request/response body that one
|
|
13
|
+
* `[API-*]` line may carry. `undefined` keeps the runtime default
|
|
14
|
+
* ({@link LogApiCall.BROWSER_DEFAULT_MAX_BODY_CHARS} / {@link LogApiCall.NODE_DEFAULT_MAX_BODY_CHARS});
|
|
15
|
+
* `0` or `Infinity` mean NO cap, for local debugging.
|
|
16
|
+
*/
|
|
17
|
+
constructor(maxLoggedBodyChars?: number | undefined);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Process-wide settings for the `[API-*]` lines {@link LogApiCallImpl} writes — today, how much of a
|
|
21
|
+
* body one line may carry (#1300).
|
|
22
|
+
*
|
|
23
|
+
* WHY a cap: the size of a body is already on the same line as a structured tag
|
|
24
|
+
* (`api.requestSize` / `api.responseSize` / `api.responseCount`, computed on the FULL body), so the body
|
|
25
|
+
* text adds no measurement, only bytes. Unbounded, it blew a consumer's 4,000-char browser message
|
|
26
|
+
* trimmer (a Sentry report per line) and on a server it risks Cloud Logging's 256 KB per-entry limit,
|
|
27
|
+
* where an oversize entry is LOST, not shortened.
|
|
28
|
+
*
|
|
29
|
+
* WHY the default depends on the RUNTIME, not on `methodInfo.side`: a node server is also a CLIENT when
|
|
30
|
+
* it calls another service, and those lines go to Cloud Logging too. The runtime is detected ONCE, at
|
|
31
|
+
* module load, by `process.versions.node` only — never `typeof window`, which React Native defines and
|
|
32
|
+
* some SSR setups fake.
|
|
33
|
+
*
|
|
34
|
+
* A static, process-wide setting in the style of {@link ClientRegistry} / `HeaderRegistry`: set once at
|
|
35
|
+
* startup (an app's `main.ts`, a server's bootstrap), never DI-injected, because the browser-side
|
|
36
|
+
* {@link LogApiCallImpl} is constructed by a transport with no DI container.
|
|
37
|
+
*/
|
|
38
|
+
export declare class LogApiCall {
|
|
39
|
+
/**
|
|
40
|
+
* Browser and React Native default: well under a 4,000-char shipped-log message cap, leaving room
|
|
41
|
+
* for the `[API-…] Class.method response=` prefix and the truncation marker.
|
|
42
|
+
*/
|
|
43
|
+
static readonly BROWSER_DEFAULT_MAX_BODY_CHARS = 2000;
|
|
44
|
+
/**
|
|
45
|
+
* Node default: even at a worst-case 4 UTF-8 bytes per char this is ~200 KB, under Cloud Logging's
|
|
46
|
+
* 256 KB per-entry limit with room for the entry's other fields.
|
|
47
|
+
*/
|
|
48
|
+
static readonly NODE_DEFAULT_MAX_BODY_CHARS = 50000;
|
|
49
|
+
/** The default for the runtime this module was loaded in, decided once. */
|
|
50
|
+
static readonly RUNTIME_DEFAULT_MAX_BODY_CHARS: number;
|
|
51
|
+
private static configuredMaxBodyChars;
|
|
52
|
+
/**
|
|
53
|
+
* Override the runtime default, once at startup. A config whose `maxLoggedBodyChars` is
|
|
54
|
+
* `undefined` restores the runtime default; `0` or `Infinity` turn the cap off.
|
|
55
|
+
*/
|
|
56
|
+
static configure(config: LogApiCallConfig): void;
|
|
57
|
+
/** The cap in force: the configured value, else the runtime default. 0 or Infinity mean no cap. */
|
|
58
|
+
static maxLoggedBodyChars(): number;
|
|
59
|
+
/**
|
|
60
|
+
* The default for a runtime: {@link NODE_DEFAULT_MAX_BODY_CHARS} on node, else
|
|
61
|
+
* {@link BROWSER_DEFAULT_MAX_BODY_CHARS} (browser and React Native alike).
|
|
62
|
+
*/
|
|
63
|
+
static defaultMaxBodyChars(isNode: boolean): number;
|
|
64
|
+
/**
|
|
65
|
+
* True only when a node `process` with `versions.node` exists. Read through `globalThis` because
|
|
66
|
+
* this browser-safe package compiles with no node types.
|
|
67
|
+
*/
|
|
68
|
+
static isNodeRuntime(): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Cut a serialized body to its first `max` chars and append
|
|
71
|
+
* `…[truncated: logged <kept> of <total> chars]`. Run on the ALREADY-MASKED string, so a cut can
|
|
72
|
+
* never reveal a masked field. Never splits a surrogate pair: when the cut would land between the
|
|
73
|
+
* two halves of one code point, that code point is dropped whole. A body within the cap, an
|
|
74
|
+
* undefined body, and a cap of 0 / Infinity are returned unchanged.
|
|
75
|
+
*/
|
|
76
|
+
static truncateForLog(body: string | undefined, max: number): string | undefined;
|
|
77
|
+
}
|
|
3
78
|
/**
|
|
4
79
|
* LogApiCallImpl - Generic API call logging utility, used by BOTH server-side (LogApiFilter) and
|
|
5
80
|
* client-side (ProxyClient) for one consistent logging shape across the framework.
|
|
@@ -27,6 +102,7 @@ import { ApiCallContext } from './ApiCallContext';
|
|
|
27
102
|
*
|
|
28
103
|
* Logging format patterns:
|
|
29
104
|
* - [API-{side}-req] ClassName.methodName request={...}
|
|
105
|
+
* (request and response bodies are capped at {@link LogApiCall.maxLoggedBodyChars} chars, #1300)
|
|
30
106
|
* - [API-{side}-resp-SUCCESS] ClassName.methodName response={...}
|
|
31
107
|
* - [API-{side}-resp-OTHER] ClassName.methodName errorType={...} error={...} (CallOutcome.EXPECTED)
|
|
32
108
|
* - [API-{side}-resp-FAIL] ClassName.methodName error={...} (TRANSIENT_FAILURE or FAILURE — final by position; each retried attempt is logged by CallRegistry as [API-client-attempt-TRANSIENT])
|
package/src/http/LogApiCall.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LogApiCallImpl = void 0;
|
|
3
|
+
exports.LogApiCallImpl = exports.LogApiCall = exports.LogApiCallConfig = void 0;
|
|
4
4
|
const errorUtils_1 = require("../lib/errorUtils");
|
|
5
5
|
const LogManager_1 = require("../logging/LogManager");
|
|
6
6
|
const ApiCallInfo_1 = require("./ApiCallInfo");
|
|
@@ -11,6 +11,127 @@ const ErrorClassifier_1 = require("./ErrorClassifier");
|
|
|
11
11
|
// The console backends special-case THIS logger name into a self-describing [API.{side}.{phase}]
|
|
12
12
|
// bracket (see ApiCallLogName) — so the name here and the name they match are the one constant.
|
|
13
13
|
const log = LogManager_1.LogManager.getLogger(ApiCallLogName_1.LOG_API_CALL_LOGGER_NAME);
|
|
14
|
+
/** The slice of node's `process.versions` that runtime detection reads (core-util has no node types). */
|
|
15
|
+
class NodeVersionsProbe {
|
|
16
|
+
node;
|
|
17
|
+
}
|
|
18
|
+
/** The slice of node's `process` that runtime detection reads. */
|
|
19
|
+
class NodeProcessProbe {
|
|
20
|
+
versions;
|
|
21
|
+
}
|
|
22
|
+
/** `globalThis`, as seen by runtime detection: a node `process` may or may not exist. */
|
|
23
|
+
class NodeGlobalProbe {
|
|
24
|
+
process;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The startup configuration for {@link LogApiCall.configure}.
|
|
28
|
+
*
|
|
29
|
+
* A class rather than an object literal (CLAUDE.md: data-only structures are classes), so the one call
|
|
30
|
+
* site reads `LogApiCall.configure(new LogApiCallConfig(2000))`.
|
|
31
|
+
*/
|
|
32
|
+
class LogApiCallConfig {
|
|
33
|
+
maxLoggedBodyChars;
|
|
34
|
+
/**
|
|
35
|
+
* @param maxLoggedBodyChars - the most characters of a serialized request/response body that one
|
|
36
|
+
* `[API-*]` line may carry. `undefined` keeps the runtime default
|
|
37
|
+
* ({@link LogApiCall.BROWSER_DEFAULT_MAX_BODY_CHARS} / {@link LogApiCall.NODE_DEFAULT_MAX_BODY_CHARS});
|
|
38
|
+
* `0` or `Infinity` mean NO cap, for local debugging.
|
|
39
|
+
*/
|
|
40
|
+
constructor(maxLoggedBodyChars) {
|
|
41
|
+
this.maxLoggedBodyChars = maxLoggedBodyChars;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.LogApiCallConfig = LogApiCallConfig;
|
|
45
|
+
/**
|
|
46
|
+
* Process-wide settings for the `[API-*]` lines {@link LogApiCallImpl} writes — today, how much of a
|
|
47
|
+
* body one line may carry (#1300).
|
|
48
|
+
*
|
|
49
|
+
* WHY a cap: the size of a body is already on the same line as a structured tag
|
|
50
|
+
* (`api.requestSize` / `api.responseSize` / `api.responseCount`, computed on the FULL body), so the body
|
|
51
|
+
* text adds no measurement, only bytes. Unbounded, it blew a consumer's 4,000-char browser message
|
|
52
|
+
* trimmer (a Sentry report per line) and on a server it risks Cloud Logging's 256 KB per-entry limit,
|
|
53
|
+
* where an oversize entry is LOST, not shortened.
|
|
54
|
+
*
|
|
55
|
+
* WHY the default depends on the RUNTIME, not on `methodInfo.side`: a node server is also a CLIENT when
|
|
56
|
+
* it calls another service, and those lines go to Cloud Logging too. The runtime is detected ONCE, at
|
|
57
|
+
* module load, by `process.versions.node` only — never `typeof window`, which React Native defines and
|
|
58
|
+
* some SSR setups fake.
|
|
59
|
+
*
|
|
60
|
+
* A static, process-wide setting in the style of {@link ClientRegistry} / `HeaderRegistry`: set once at
|
|
61
|
+
* startup (an app's `main.ts`, a server's bootstrap), never DI-injected, because the browser-side
|
|
62
|
+
* {@link LogApiCallImpl} is constructed by a transport with no DI container.
|
|
63
|
+
*/
|
|
64
|
+
class LogApiCall {
|
|
65
|
+
/**
|
|
66
|
+
* Browser and React Native default: well under a 4,000-char shipped-log message cap, leaving room
|
|
67
|
+
* for the `[API-…] Class.method response=` prefix and the truncation marker.
|
|
68
|
+
*/
|
|
69
|
+
static BROWSER_DEFAULT_MAX_BODY_CHARS = 2000;
|
|
70
|
+
/**
|
|
71
|
+
* Node default: even at a worst-case 4 UTF-8 bytes per char this is ~200 KB, under Cloud Logging's
|
|
72
|
+
* 256 KB per-entry limit with room for the entry's other fields.
|
|
73
|
+
*/
|
|
74
|
+
static NODE_DEFAULT_MAX_BODY_CHARS = 50000;
|
|
75
|
+
/** The default for the runtime this module was loaded in, decided once. */
|
|
76
|
+
static RUNTIME_DEFAULT_MAX_BODY_CHARS = LogApiCall.defaultMaxBodyChars(LogApiCall.isNodeRuntime());
|
|
77
|
+
static configuredMaxBodyChars;
|
|
78
|
+
/**
|
|
79
|
+
* Override the runtime default, once at startup. A config whose `maxLoggedBodyChars` is
|
|
80
|
+
* `undefined` restores the runtime default; `0` or `Infinity` turn the cap off.
|
|
81
|
+
*/
|
|
82
|
+
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); set once at startup, never DI-injected
|
|
83
|
+
static configure(config) {
|
|
84
|
+
const max = config.maxLoggedBodyChars;
|
|
85
|
+
if (max !== undefined && (Number.isNaN(max) || max < 0)) {
|
|
86
|
+
throw new Error(`LogApiCall.configure: maxLoggedBodyChars must be a non-negative number, 0 or Infinity (no cap), or undefined (runtime default); got ${max}`);
|
|
87
|
+
}
|
|
88
|
+
LogApiCall.configuredMaxBodyChars = max;
|
|
89
|
+
}
|
|
90
|
+
/** The cap in force: the configured value, else the runtime default. 0 or Infinity mean no cap. */
|
|
91
|
+
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); set once at startup, never DI-injected
|
|
92
|
+
static maxLoggedBodyChars() {
|
|
93
|
+
return LogApiCall.configuredMaxBodyChars ?? LogApiCall.RUNTIME_DEFAULT_MAX_BODY_CHARS;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The default for a runtime: {@link NODE_DEFAULT_MAX_BODY_CHARS} on node, else
|
|
97
|
+
* {@link BROWSER_DEFAULT_MAX_BODY_CHARS} (browser and React Native alike).
|
|
98
|
+
*/
|
|
99
|
+
// webpieces-disable no-function-outside-class -- pure static helper of the static LogApiCall settings
|
|
100
|
+
static defaultMaxBodyChars(isNode) {
|
|
101
|
+
return isNode
|
|
102
|
+
? LogApiCall.NODE_DEFAULT_MAX_BODY_CHARS
|
|
103
|
+
: LogApiCall.BROWSER_DEFAULT_MAX_BODY_CHARS;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* True only when a node `process` with `versions.node` exists. Read through `globalThis` because
|
|
107
|
+
* this browser-safe package compiles with no node types.
|
|
108
|
+
*/
|
|
109
|
+
// webpieces-disable no-function-outside-class -- pure static helper of the static LogApiCall settings
|
|
110
|
+
static isNodeRuntime() {
|
|
111
|
+
const g = globalThis;
|
|
112
|
+
return typeof g.process !== 'undefined' && !!g.process.versions?.node;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Cut a serialized body to its first `max` chars and append
|
|
116
|
+
* `…[truncated: logged <kept> of <total> chars]`. Run on the ALREADY-MASKED string, so a cut can
|
|
117
|
+
* never reveal a masked field. Never splits a surrogate pair: when the cut would land between the
|
|
118
|
+
* two halves of one code point, that code point is dropped whole. A body within the cap, an
|
|
119
|
+
* undefined body, and a cap of 0 / Infinity are returned unchanged.
|
|
120
|
+
*/
|
|
121
|
+
// webpieces-disable no-function-outside-class -- pure static helper of the static LogApiCall settings
|
|
122
|
+
static truncateForLog(body, max) {
|
|
123
|
+
if (body === undefined || max === 0 || !Number.isFinite(max) || body.length <= max) {
|
|
124
|
+
return body;
|
|
125
|
+
}
|
|
126
|
+
let end = Math.floor(max);
|
|
127
|
+
const last = body.charCodeAt(end - 1);
|
|
128
|
+
if (last >= 0xd800 && last <= 0xdbff) {
|
|
129
|
+
end -= 1; // a high surrogate whose low half would be cut off — drop the pair
|
|
130
|
+
}
|
|
131
|
+
return `${body.slice(0, end)}…[truncated: logged ${end} of ${body.length} chars]`;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.LogApiCall = LogApiCall;
|
|
14
135
|
/**
|
|
15
136
|
* LogApiCallImpl - Generic API call logging utility, used by BOTH server-side (LogApiFilter) and
|
|
16
137
|
* client-side (ProxyClient) for one consistent logging shape across the framework.
|
|
@@ -38,6 +159,7 @@ const log = LogManager_1.LogManager.getLogger(ApiCallLogName_1.LOG_API_CALL_LOGG
|
|
|
38
159
|
*
|
|
39
160
|
* Logging format patterns:
|
|
40
161
|
* - [API-{side}-req] ClassName.methodName request={...}
|
|
162
|
+
* (request and response bodies are capped at {@link LogApiCall.maxLoggedBodyChars} chars, #1300)
|
|
41
163
|
* - [API-{side}-resp-SUCCESS] ClassName.methodName response={...}
|
|
42
164
|
* - [API-{side}-resp-OTHER] ClassName.methodName errorType={...} error={...} (CallOutcome.EXPECTED)
|
|
43
165
|
* - [API-{side}-resp-FAIL] ClassName.methodName error={...} (TRANSIENT_FAILURE or FAILURE — final by position; each retried attempt is logged by CallRegistry as [API-client-attempt-TRANSIENT])
|
|
@@ -80,12 +202,15 @@ class LogApiCallImpl {
|
|
|
80
202
|
// JSON.stringify fast path, unchanged for every existing caller.
|
|
81
203
|
const requestBody = this.serialize(requestDto, methodInfo);
|
|
82
204
|
const requestSize = this.byteSize(requestBody);
|
|
205
|
+
// The size above is on the FULL body; only the log TEXT is capped (#1300), and only after
|
|
206
|
+
// serialize() has masked it.
|
|
207
|
+
const maxBodyChars = LogApiCall.maxLoggedBodyChars();
|
|
83
208
|
// Declared out here so the catch below can read it too. Reassigned just before the call, so
|
|
84
209
|
// the number times ONLY the call and not our own request-logging.
|
|
85
210
|
let startMs = Date.now();
|
|
86
211
|
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- LogApiCall logs errors before re-throwing to caller
|
|
87
212
|
try {
|
|
88
|
-
stamp(new ApiCallInfo_1.ApiCallInfo(methodInfo, 'request', undefined, undefined, requestSize), () => log.info(`[API-${side}-req] ${id} request=${requestBody}`));
|
|
213
|
+
stamp(new ApiCallInfo_1.ApiCallInfo(methodInfo, 'request', undefined, undefined, requestSize), () => log.info(`[API-${side}-req] ${id} request=${LogApiCall.truncateForLog(requestBody, maxBodyChars)}`));
|
|
89
214
|
if (!requestDto)
|
|
90
215
|
throw new Error(`Request cannot be null and was from ${id}`);
|
|
91
216
|
startMs = Date.now();
|
|
@@ -93,7 +218,7 @@ class LogApiCallImpl {
|
|
|
93
218
|
const durationMs = Date.now() - startMs;
|
|
94
219
|
const responseBody = this.serialize(response, methodInfo);
|
|
95
220
|
const count = this.selectResponseCount(response, responseCount, side, id);
|
|
96
|
-
stamp(new ApiCallInfo_1.ApiCallInfo(methodInfo, 'response', 'success', durationMs, requestSize, this.byteSize(responseBody), count), () => log.info(`[API-${side}-resp-SUCCESS] ${id} response=${responseBody}`));
|
|
221
|
+
stamp(new ApiCallInfo_1.ApiCallInfo(methodInfo, 'response', 'success', durationMs, requestSize, this.byteSize(responseBody), count), () => log.info(`[API-${side}-resp-SUCCESS] ${id} response=${LogApiCall.truncateForLog(responseBody, maxBodyChars)}`));
|
|
97
222
|
return response;
|
|
98
223
|
}
|
|
99
224
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LogApiCall.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/LogApiCall.ts"],"names":[],"mappings":"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","sourcesContent":["import { toError } from '../lib/errorUtils';\nimport { LogManager } from '../logging/LogManager';\nimport { ApiCallInfo } from './ApiCallInfo';\nimport { ApiMethodInfo } from './ApiMethodInfo';\nimport { ApiCallContext } from './ApiCallContext';\nimport { WebpiecesCoreHeaders } from './WebpiecesCoreHeaders';\nimport { LOG_API_CALL_LOGGER_NAME } from './ApiCallLogName';\nimport { ClientRegistry } from './ClientRegistry';\nimport { CallOutcome } from './ErrorClassifier';\n\n// The console backends special-case THIS logger name into a self-describing [API.{side}.{phase}]\n// bracket (see ApiCallLogName) — so the name here and the name they match are the one constant.\nconst log = LogManager.getLogger(LOG_API_CALL_LOGGER_NAME);\n\n/**\n * LogApiCallImpl - Generic API call logging utility, used by BOTH server-side (LogApiFilter) and\n * client-side (ProxyClient) for one consistent logging shape across the framework.\n *\n * TWO things happen around each call:\n * 1. Text lines are emitted (the human-readable `[API-...]` patterns below).\n * 2. A structured {@link ApiCallInfo} tag is stamped into the ambient request context via the\n * {@link ApiCallContext} seam, so EVERY log line emitted during the call (not just the\n * req/resp lines) inherits a filterable `api` object — surfacing in GCP as\n * `jsonPayload.api.{method.{side,apiClass,methodName,controllerName},type,result}`.\n *\n * BROWSER-SAFE: this lives in core-util and runs in the browser bundle (via ProxyClient →\n * BrowserProxyClient), so it MUST NOT import `RequestContext` (Node async_hooks, and a circular dep).\n * It stamps through the {@link ApiCallContext} seam instead, and takes that seam as a REQUIRED\n * CONSTRUCTOR ARGUMENT — there is no process-global holder to install and none to forget. Each\n * environment-specific package constructs its own:\n *\n * LogApiFilter (@webpieces/http-routing) -> new LogApiCallImpl(new RequestContextApiCallContext())\n * NodeProxyClient (@webpieces/http-client-node) -> new LogApiCallImpl(new RequestContextApiCallContext())\n * TaskProxyClient (@webpieces/cloudtasks-client) -> new LogApiCallImpl(new RequestContextApiCallContext())\n * BrowserProxyClient (@webpieces/http-client-browser) -> new LogApiCallImpl(new BrowserApiCallContext())\n *\n * NOT a singleton, deliberately: a shared instance would need a shared context, which is the global\n * this constructor replaced. Construct one where you know which environment you are in.\n *\n * Logging format patterns:\n * - [API-{side}-req] ClassName.methodName request={...}\n * - [API-{side}-resp-SUCCESS] ClassName.methodName response={...}\n * - [API-{side}-resp-OTHER] ClassName.methodName errorType={...} error={...} (CallOutcome.EXPECTED)\n * - [API-{side}-resp-FAIL] ClassName.methodName error={...} (TRANSIENT_FAILURE or FAILURE — final by position; each retried attempt is logged by CallRegistry as [API-client-attempt-TRANSIENT])\n */\nexport class LogApiCallImpl {\n /**\n * @param ctx - the environment's {@link ApiCallContext}. REQUIRED, with no default: that is what\n * turns \"nobody bootstrapped the context\" into a compile error instead of a throw on the first\n * real call in production.\n */\n constructor(private readonly ctx: ApiCallContext) {}\n\n /**\n * Execute an API call with logging + `api` context-tagging around it.\n *\n * @param methodInfo - The transport-neutral call identity (side, apiClass, methodName,\n * controllerName?). `apiClass` is what matches a client call to its server handler in the logs.\n * @param requestDto - The request DTO (external multi-param callers synthesize a small object)\n * @param method - The method to execute\n *\n * Correlation fields (requestId, tenantId, ...) are NOT stamped here — a logging BACKEND owns that,\n * reading RequestContext on every record. What IS stamped here is the per-call `api` tag, and only\n * for the SYNCHRONOUS span of each log line: set → log → remove. Because the tag is never held across\n * `await method(...)`, a concurrent browser call (single-threaded, one global slot) can never clobber\n * it. Cost: only the `[API-*]` req/resp lines carry `api`, not lines emitted mid-call — which is\n * exactly what the GCP filters (`jsonPayload.api.*`) want.\n */\n public async execute<Q, R>(\n methodInfo: ApiMethodInfo,\n requestDto: Q,\n method: (dto: Q) => Promise<R>,\n responseCount?: (response: R) => number | undefined,\n ): Promise<R> {\n if (methodInfo.noLogging) {\n return this.executeUnlogged(methodInfo, requestDto, method);\n }\n const side = methodInfo.side;\n const id = `${methodInfo.apiClass}.${methodInfo.methodName}`;\n const stamp = this.stamper(this.activeContext());\n\n // Stringify ONCE and reuse for both the log text and the size — a second JSON.stringify of a\n // large DTO purely to measure it would double the cost of the thing we are measuring.\n // Only take the field-masking hit when this call declared sensitive fields; otherwise the plain\n // JSON.stringify fast path, unchanged for every existing caller.\n const requestBody = this.serialize(requestDto, methodInfo);\n const requestSize = this.byteSize(requestBody);\n // Declared out here so the catch below can read it too. Reassigned just before the call, so\n // the number times ONLY the call and not our own request-logging.\n let startMs = Date.now();\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- LogApiCall logs errors before re-throwing to caller\n try {\n stamp(new ApiCallInfo(methodInfo, 'request', undefined, undefined, requestSize), () =>\n log.info(`[API-${side}-req] ${id} request=${requestBody}`),\n );\n\n if (!requestDto) throw new Error(`Request cannot be null and was from ${id}`);\n\n startMs = Date.now();\n const response = await method(requestDto);\n const durationMs = Date.now() - startMs;\n\n const responseBody = this.serialize(response, methodInfo);\n const count = this.selectResponseCount(response, responseCount, side, id);\n stamp(\n new ApiCallInfo(\n methodInfo,\n 'response',\n 'success',\n durationMs,\n requestSize,\n this.byteSize(responseBody),\n count,\n ),\n () => log.info(`[API-${side}-resp-SUCCESS] ${id} response=${responseBody}`),\n );\n\n return response;\n } catch (err: unknown) {\n const error = toError(err);\n // Duration comes off the SAME start as the success path, so a slow failure (a timeout, a\n // hung dependency) reports its real cost rather than nothing.\n this.logFailure(error, methodInfo, Date.now() - startMs, requestSize, stamp);\n throw err;\n }\n }\n\n /**\n * Build the tag-and-emit span used around every `[API-*]` line.\n *\n * set → emit → remove, as ONE synchronous span: the tag is live only while the logger reads it,\n * never across an await, so a single browser global slot can never be clobbered by a concurrent\n * call. `ctx` is passed in (rather than read from `this`) so the caller has already proven it is\n * ACTIVE before anything is stamped.\n */\n private stamper(ctx: ApiCallContext): (info: ApiCallInfo, emit: () => void) => void {\n const key = WebpiecesCoreHeaders.API_CALL_INFO;\n return (info: ApiCallInfo, emit: () => void): void => {\n ctx.set(key, info);\n // webpieces-disable no-unmanaged-exceptions -- cleanup must run when a logging backend throws\n try {\n emit();\n } finally {\n ctx.remove(key);\n }\n };\n }\n\n /**\n * The `@Endpoint(..., { noLogging: true })` path: run the call and say NOTHING about it.\n *\n * No `[API-*]` line, no {@link ApiCallInfo} stamp, and no serialization of either DTO — a\n * noLogging route is a log shipper or a log-ingest endpoint, so a line describing the call IS the\n * thing being shipped, and emitting it feeds the next batch its own payload (#976).\n *\n * It deliberately does NOT call {@link activeContext}: that guard exists because a call with\n * nowhere to STAMP a tag is a bug, and this path stamps nothing. A noLogging call is therefore\n * legal outside a RequestContext scope, which is exactly where a flusher tends to run.\n *\n * The null-request check is kept, because it is a contract error and not observability — a\n * noLogging route deserves the same error as every other route, and the throw propagates to the caller\n * unlogged here, exactly as the logged path re-throws.\n *\n * `responseCount` is not taken: it feeds only the `api.responseCount` tag, which is not emitted.\n */\n private async executeUnlogged<Q, R>(\n methodInfo: ApiMethodInfo,\n requestDto: Q,\n method: (dto: Q) => Promise<R>,\n ): Promise<R> {\n if (!requestDto) {\n throw new Error(\n `Request cannot be null and was from ${methodInfo.apiClass}.${methodInfo.methodName}`,\n );\n }\n return method(requestDto);\n }\n\n /**\n * Evaluate an opt-in logical-item count without allowing observability code to affect the call.\n * Warnings deliberately contain neither request nor response bodies (nor the invalid value).\n */\n private selectResponseCount<R>(\n response: R,\n selector: ((response: R) => number | undefined) | undefined,\n side: string,\n id: string,\n ): number | undefined {\n if (!selector) {\n return undefined;\n }\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- an observability callback must not fail the API call\n try {\n const count = selector(response);\n if (\n count === undefined ||\n (Number.isFinite(count) && Number.isInteger(count) && count >= 0)\n ) {\n return count;\n }\n this.warnInvalidResponseCount(side, id, 'returned an invalid value');\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n this.warnInvalidResponseCount(side, id, 'threw');\n }\n return undefined;\n }\n\n private warnInvalidResponseCount(side: string, id: string, reason: string): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- a warning backend must not replace a successful API response\n try {\n log.warn(\n `[API-${side}-resp-COUNT-WARN] ${id} responseCount selector ${reason}; omitting responseCount`,\n );\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n // The API response is authoritative; observability failures are intentionally ignored here.\n }\n }\n\n /**\n * Serialize a DTO for the LOG LINE ONLY. With no mask on the call, this is a plain JSON.stringify\n * (byte-for-byte the old behavior, no walk) so existing callers pay nothing. With a mask, it runs\n * {@link MaskSpec.stringify}, which produces a masked STRING without ever mutating the DTO — so the\n * object handed to the transport, and thus the value ON THE WIRE, is unchanged.\n */\n private serialize(\n // webpieces-disable no-any-unknown -- DTO types are erased at the api/proxy boundary (matches execute)\n dto: unknown,\n methodInfo: ApiMethodInfo,\n ): string | undefined {\n return methodInfo.mask ? methodInfo.mask.stringify(dto) : JSON.stringify(dto);\n }\n\n /**\n * The ApiCallContext to stamp into. It cannot be MISSING (it is a constructor argument), but it\n * can be INACTIVE — a Node context used outside any `RequestContext.run(...)` scope. That throws:\n * an api call with nowhere to tag is a bug.\n */\n private activeContext(): ApiCallContext {\n const ctx = this.ctx;\n if (!ctx.isActive()) {\n throw new Error(\n 'LogApiCall requires an ACTIVE ApiCallContext. On a Node server, run inside a ' +\n 'RequestContext.run(...) scope — a server filter opens one per request, and a ' +\n 'non-webpieces host must open one around the work that calls a webpieces client. ' +\n '(A BrowserApiCallContext is always active, so this can only be the Node side.)',\n );\n }\n return ctx;\n }\n\n /**\n * Tag + log a thrown call. There is no responseSize — a throw produced no response body to measure.\n */\n private logFailure(\n error: Error,\n methodInfo: ApiMethodInfo,\n durationMs: number,\n requestSize: number | undefined,\n stamp: (info: ApiCallInfo, emit: () => void) => void,\n ): void {\n const side = methodInfo.side;\n const id = `${methodInfo.apiClass}.${methodInfo.methodName}`;\n const errorType = error.constructor.name;\n // The MESSAGE is on BOTH branches, and that is load-bearing: this is the ONLY operator line\n // per failure now that `ApiErrorBoundary` writes none. An operator error message is never\n // published to a caller (see `ApiErrorCodec.publicMessage`), so the log is the one place it\n // exists at all — dropping it on the caller-error branch would lose it outright.\n // Pluggable classification (ClientRegistry): a per-apiClass EXTERNAL-client classifier wins,\n // else the app default, else the webpieces built-in — which is side-dependent (a 4xx the SERVER\n // raised is a handled non-failure; the same 4xx a CLIENT receives means its call FAILED; 266 is\n // never a failure either side). This line is FINAL BY POSITION: it only sees what escaped every\n // retry, so a TRANSIENT_FAILURE here was never retried (no strategy, or a WRITE) or gave up —\n // it logs FAIL exactly like a FAILURE. Only EXPECTED is \"OTHER / result:'success'\".\n const isUser = ClientRegistry.classify(error, methodInfo) === CallOutcome.EXPECTED;\n\n stamp(\n new ApiCallInfo(\n methodInfo,\n 'response',\n isUser ? 'success' : 'failure',\n durationMs,\n requestSize,\n ),\n () =>\n isUser\n ? log.warn(\n `[API-${side}-resp-OTHER] ${id} errorType=${errorType} error=${error.message}`,\n )\n : log.error(\n `[API-${side}-resp-FAIL] ${id} errorType=${errorType} error=${error.message}`,\n ),\n );\n }\n\n /**\n * UTF-8 byte size without platform-specific encoding globals: LogApiCall runs in the\n * browser bundle. Undefined in, undefined out — a `Promise<void>` method has no body to measure,\n * and a 0 there would be a lie (JSON.stringify(undefined) returns undefined, not '').\n */\n private byteSize(serialized: string | undefined): number | undefined {\n if (serialized === undefined) {\n return undefined;\n }\n let bytes = 0;\n for (const character of serialized) {\n const code = character.codePointAt(0)!;\n bytes += code <= 0x7f ? 1 : code <= 0x7ff ? 2 : code <= 0xffff ? 3 : 4;\n }\n return bytes;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"LogApiCall.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/LogApiCall.ts"],"names":[],"mappings":"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","sourcesContent":["import { toError } from '../lib/errorUtils';\nimport { LogManager } from '../logging/LogManager';\nimport { ApiCallInfo } from './ApiCallInfo';\nimport { ApiMethodInfo } from './ApiMethodInfo';\nimport { ApiCallContext } from './ApiCallContext';\nimport { WebpiecesCoreHeaders } from './WebpiecesCoreHeaders';\nimport { LOG_API_CALL_LOGGER_NAME } from './ApiCallLogName';\nimport { ClientRegistry } from './ClientRegistry';\nimport { CallOutcome } from './ErrorClassifier';\n\n// The console backends special-case THIS logger name into a self-describing [API.{side}.{phase}]\n// bracket (see ApiCallLogName) — so the name here and the name they match are the one constant.\nconst log = LogManager.getLogger(LOG_API_CALL_LOGGER_NAME);\n\n/** The slice of node's `process.versions` that runtime detection reads (core-util has no node types). */\nclass NodeVersionsProbe {\n node?: string;\n}\n\n/** The slice of node's `process` that runtime detection reads. */\nclass NodeProcessProbe {\n versions?: NodeVersionsProbe;\n}\n\n/** `globalThis`, as seen by runtime detection: a node `process` may or may not exist. */\nclass NodeGlobalProbe {\n process?: NodeProcessProbe;\n}\n\n/**\n * The startup configuration for {@link LogApiCall.configure}.\n *\n * A class rather than an object literal (CLAUDE.md: data-only structures are classes), so the one call\n * site reads `LogApiCall.configure(new LogApiCallConfig(2000))`.\n */\nexport class LogApiCallConfig {\n /**\n * @param maxLoggedBodyChars - the most characters of a serialized request/response body that one\n * `[API-*]` line may carry. `undefined` keeps the runtime default\n * ({@link LogApiCall.BROWSER_DEFAULT_MAX_BODY_CHARS} / {@link LogApiCall.NODE_DEFAULT_MAX_BODY_CHARS});\n * `0` or `Infinity` mean NO cap, for local debugging.\n */\n constructor(readonly maxLoggedBodyChars?: number) {}\n}\n\n/**\n * Process-wide settings for the `[API-*]` lines {@link LogApiCallImpl} writes — today, how much of a\n * body one line may carry (#1300).\n *\n * WHY a cap: the size of a body is already on the same line as a structured tag\n * (`api.requestSize` / `api.responseSize` / `api.responseCount`, computed on the FULL body), so the body\n * text adds no measurement, only bytes. Unbounded, it blew a consumer's 4,000-char browser message\n * trimmer (a Sentry report per line) and on a server it risks Cloud Logging's 256 KB per-entry limit,\n * where an oversize entry is LOST, not shortened.\n *\n * WHY the default depends on the RUNTIME, not on `methodInfo.side`: a node server is also a CLIENT when\n * it calls another service, and those lines go to Cloud Logging too. The runtime is detected ONCE, at\n * module load, by `process.versions.node` only — never `typeof window`, which React Native defines and\n * some SSR setups fake.\n *\n * A static, process-wide setting in the style of {@link ClientRegistry} / `HeaderRegistry`: set once at\n * startup (an app's `main.ts`, a server's bootstrap), never DI-injected, because the browser-side\n * {@link LogApiCallImpl} is constructed by a transport with no DI container.\n */\nexport class LogApiCall {\n /**\n * Browser and React Native default: well under a 4,000-char shipped-log message cap, leaving room\n * for the `[API-…] Class.method response=` prefix and the truncation marker.\n */\n static readonly BROWSER_DEFAULT_MAX_BODY_CHARS = 2000;\n\n /**\n * Node default: even at a worst-case 4 UTF-8 bytes per char this is ~200 KB, under Cloud Logging's\n * 256 KB per-entry limit with room for the entry's other fields.\n */\n static readonly NODE_DEFAULT_MAX_BODY_CHARS = 50000;\n\n /** The default for the runtime this module was loaded in, decided once. */\n static readonly RUNTIME_DEFAULT_MAX_BODY_CHARS = LogApiCall.defaultMaxBodyChars(\n LogApiCall.isNodeRuntime(),\n );\n\n private static configuredMaxBodyChars: number | undefined;\n\n /**\n * Override the runtime default, once at startup. A config whose `maxLoggedBodyChars` is\n * `undefined` restores the runtime default; `0` or `Infinity` turn the cap off.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); set once at startup, never DI-injected\n static configure(config: LogApiCallConfig): void {\n const max = config.maxLoggedBodyChars;\n if (max !== undefined && (Number.isNaN(max) || max < 0)) {\n throw new Error(\n `LogApiCall.configure: maxLoggedBodyChars must be a non-negative number, 0 or Infinity (no cap), or undefined (runtime default); got ${max}`,\n );\n }\n LogApiCall.configuredMaxBodyChars = max;\n }\n\n /** The cap in force: the configured value, else the runtime default. 0 or Infinity mean no cap. */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); set once at startup, never DI-injected\n static maxLoggedBodyChars(): number {\n return LogApiCall.configuredMaxBodyChars ?? LogApiCall.RUNTIME_DEFAULT_MAX_BODY_CHARS;\n }\n\n /**\n * The default for a runtime: {@link NODE_DEFAULT_MAX_BODY_CHARS} on node, else\n * {@link BROWSER_DEFAULT_MAX_BODY_CHARS} (browser and React Native alike).\n */\n // webpieces-disable no-function-outside-class -- pure static helper of the static LogApiCall settings\n static defaultMaxBodyChars(isNode: boolean): number {\n return isNode\n ? LogApiCall.NODE_DEFAULT_MAX_BODY_CHARS\n : LogApiCall.BROWSER_DEFAULT_MAX_BODY_CHARS;\n }\n\n /**\n * True only when a node `process` with `versions.node` exists. Read through `globalThis` because\n * this browser-safe package compiles with no node types.\n */\n // webpieces-disable no-function-outside-class -- pure static helper of the static LogApiCall settings\n static isNodeRuntime(): boolean {\n const g = globalThis as NodeGlobalProbe;\n return typeof g.process !== 'undefined' && !!g.process.versions?.node;\n }\n\n /**\n * Cut a serialized body to its first `max` chars and append\n * `…[truncated: logged <kept> of <total> chars]`. Run on the ALREADY-MASKED string, so a cut can\n * never reveal a masked field. Never splits a surrogate pair: when the cut would land between the\n * two halves of one code point, that code point is dropped whole. A body within the cap, an\n * undefined body, and a cap of 0 / Infinity are returned unchanged.\n */\n // webpieces-disable no-function-outside-class -- pure static helper of the static LogApiCall settings\n static truncateForLog(body: string | undefined, max: number): string | undefined {\n if (body === undefined || max === 0 || !Number.isFinite(max) || body.length <= max) {\n return body;\n }\n let end = Math.floor(max);\n const last = body.charCodeAt(end - 1);\n if (last >= 0xd800 && last <= 0xdbff) {\n end -= 1; // a high surrogate whose low half would be cut off — drop the pair\n }\n return `${body.slice(0, end)}…[truncated: logged ${end} of ${body.length} chars]`;\n }\n}\n\n/**\n * LogApiCallImpl - Generic API call logging utility, used by BOTH server-side (LogApiFilter) and\n * client-side (ProxyClient) for one consistent logging shape across the framework.\n *\n * TWO things happen around each call:\n * 1. Text lines are emitted (the human-readable `[API-...]` patterns below).\n * 2. A structured {@link ApiCallInfo} tag is stamped into the ambient request context via the\n * {@link ApiCallContext} seam, so EVERY log line emitted during the call (not just the\n * req/resp lines) inherits a filterable `api` object — surfacing in GCP as\n * `jsonPayload.api.{method.{side,apiClass,methodName,controllerName},type,result}`.\n *\n * BROWSER-SAFE: this lives in core-util and runs in the browser bundle (via ProxyClient →\n * BrowserProxyClient), so it MUST NOT import `RequestContext` (Node async_hooks, and a circular dep).\n * It stamps through the {@link ApiCallContext} seam instead, and takes that seam as a REQUIRED\n * CONSTRUCTOR ARGUMENT — there is no process-global holder to install and none to forget. Each\n * environment-specific package constructs its own:\n *\n * LogApiFilter (@webpieces/http-routing) -> new LogApiCallImpl(new RequestContextApiCallContext())\n * NodeProxyClient (@webpieces/http-client-node) -> new LogApiCallImpl(new RequestContextApiCallContext())\n * TaskProxyClient (@webpieces/cloudtasks-client) -> new LogApiCallImpl(new RequestContextApiCallContext())\n * BrowserProxyClient (@webpieces/http-client-browser) -> new LogApiCallImpl(new BrowserApiCallContext())\n *\n * NOT a singleton, deliberately: a shared instance would need a shared context, which is the global\n * this constructor replaced. Construct one where you know which environment you are in.\n *\n * Logging format patterns:\n * - [API-{side}-req] ClassName.methodName request={...}\n * (request and response bodies are capped at {@link LogApiCall.maxLoggedBodyChars} chars, #1300)\n * - [API-{side}-resp-SUCCESS] ClassName.methodName response={...}\n * - [API-{side}-resp-OTHER] ClassName.methodName errorType={...} error={...} (CallOutcome.EXPECTED)\n * - [API-{side}-resp-FAIL] ClassName.methodName error={...} (TRANSIENT_FAILURE or FAILURE — final by position; each retried attempt is logged by CallRegistry as [API-client-attempt-TRANSIENT])\n */\nexport class LogApiCallImpl {\n /**\n * @param ctx - the environment's {@link ApiCallContext}. REQUIRED, with no default: that is what\n * turns \"nobody bootstrapped the context\" into a compile error instead of a throw on the first\n * real call in production.\n */\n constructor(private readonly ctx: ApiCallContext) {}\n\n /**\n * Execute an API call with logging + `api` context-tagging around it.\n *\n * @param methodInfo - The transport-neutral call identity (side, apiClass, methodName,\n * controllerName?). `apiClass` is what matches a client call to its server handler in the logs.\n * @param requestDto - The request DTO (external multi-param callers synthesize a small object)\n * @param method - The method to execute\n *\n * Correlation fields (requestId, tenantId, ...) are NOT stamped here — a logging BACKEND owns that,\n * reading RequestContext on every record. What IS stamped here is the per-call `api` tag, and only\n * for the SYNCHRONOUS span of each log line: set → log → remove. Because the tag is never held across\n * `await method(...)`, a concurrent browser call (single-threaded, one global slot) can never clobber\n * it. Cost: only the `[API-*]` req/resp lines carry `api`, not lines emitted mid-call — which is\n * exactly what the GCP filters (`jsonPayload.api.*`) want.\n */\n public async execute<Q, R>(\n methodInfo: ApiMethodInfo,\n requestDto: Q,\n method: (dto: Q) => Promise<R>,\n responseCount?: (response: R) => number | undefined,\n ): Promise<R> {\n if (methodInfo.noLogging) {\n return this.executeUnlogged(methodInfo, requestDto, method);\n }\n const side = methodInfo.side;\n const id = `${methodInfo.apiClass}.${methodInfo.methodName}`;\n const stamp = this.stamper(this.activeContext());\n\n // Stringify ONCE and reuse for both the log text and the size — a second JSON.stringify of a\n // large DTO purely to measure it would double the cost of the thing we are measuring.\n // Only take the field-masking hit when this call declared sensitive fields; otherwise the plain\n // JSON.stringify fast path, unchanged for every existing caller.\n const requestBody = this.serialize(requestDto, methodInfo);\n const requestSize = this.byteSize(requestBody);\n // The size above is on the FULL body; only the log TEXT is capped (#1300), and only after\n // serialize() has masked it.\n const maxBodyChars = LogApiCall.maxLoggedBodyChars();\n // Declared out here so the catch below can read it too. Reassigned just before the call, so\n // the number times ONLY the call and not our own request-logging.\n let startMs = Date.now();\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- LogApiCall logs errors before re-throwing to caller\n try {\n stamp(new ApiCallInfo(methodInfo, 'request', undefined, undefined, requestSize), () =>\n log.info(\n `[API-${side}-req] ${id} request=${LogApiCall.truncateForLog(requestBody, maxBodyChars)}`,\n ),\n );\n\n if (!requestDto) throw new Error(`Request cannot be null and was from ${id}`);\n\n startMs = Date.now();\n const response = await method(requestDto);\n const durationMs = Date.now() - startMs;\n\n const responseBody = this.serialize(response, methodInfo);\n const count = this.selectResponseCount(response, responseCount, side, id);\n stamp(\n new ApiCallInfo(\n methodInfo,\n 'response',\n 'success',\n durationMs,\n requestSize,\n this.byteSize(responseBody),\n count,\n ),\n () =>\n log.info(\n `[API-${side}-resp-SUCCESS] ${id} response=${LogApiCall.truncateForLog(responseBody, maxBodyChars)}`,\n ),\n );\n\n return response;\n } catch (err: unknown) {\n const error = toError(err);\n // Duration comes off the SAME start as the success path, so a slow failure (a timeout, a\n // hung dependency) reports its real cost rather than nothing.\n this.logFailure(error, methodInfo, Date.now() - startMs, requestSize, stamp);\n throw err;\n }\n }\n\n /**\n * Build the tag-and-emit span used around every `[API-*]` line.\n *\n * set → emit → remove, as ONE synchronous span: the tag is live only while the logger reads it,\n * never across an await, so a single browser global slot can never be clobbered by a concurrent\n * call. `ctx` is passed in (rather than read from `this`) so the caller has already proven it is\n * ACTIVE before anything is stamped.\n */\n private stamper(ctx: ApiCallContext): (info: ApiCallInfo, emit: () => void) => void {\n const key = WebpiecesCoreHeaders.API_CALL_INFO;\n return (info: ApiCallInfo, emit: () => void): void => {\n ctx.set(key, info);\n // webpieces-disable no-unmanaged-exceptions -- cleanup must run when a logging backend throws\n try {\n emit();\n } finally {\n ctx.remove(key);\n }\n };\n }\n\n /**\n * The `@Endpoint(..., { noLogging: true })` path: run the call and say NOTHING about it.\n *\n * No `[API-*]` line, no {@link ApiCallInfo} stamp, and no serialization of either DTO — a\n * noLogging route is a log shipper or a log-ingest endpoint, so a line describing the call IS the\n * thing being shipped, and emitting it feeds the next batch its own payload (#976).\n *\n * It deliberately does NOT call {@link activeContext}: that guard exists because a call with\n * nowhere to STAMP a tag is a bug, and this path stamps nothing. A noLogging call is therefore\n * legal outside a RequestContext scope, which is exactly where a flusher tends to run.\n *\n * The null-request check is kept, because it is a contract error and not observability — a\n * noLogging route deserves the same error as every other route, and the throw propagates to the caller\n * unlogged here, exactly as the logged path re-throws.\n *\n * `responseCount` is not taken: it feeds only the `api.responseCount` tag, which is not emitted.\n */\n private async executeUnlogged<Q, R>(\n methodInfo: ApiMethodInfo,\n requestDto: Q,\n method: (dto: Q) => Promise<R>,\n ): Promise<R> {\n if (!requestDto) {\n throw new Error(\n `Request cannot be null and was from ${methodInfo.apiClass}.${methodInfo.methodName}`,\n );\n }\n return method(requestDto);\n }\n\n /**\n * Evaluate an opt-in logical-item count without allowing observability code to affect the call.\n * Warnings deliberately contain neither request nor response bodies (nor the invalid value).\n */\n private selectResponseCount<R>(\n response: R,\n selector: ((response: R) => number | undefined) | undefined,\n side: string,\n id: string,\n ): number | undefined {\n if (!selector) {\n return undefined;\n }\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- an observability callback must not fail the API call\n try {\n const count = selector(response);\n if (\n count === undefined ||\n (Number.isFinite(count) && Number.isInteger(count) && count >= 0)\n ) {\n return count;\n }\n this.warnInvalidResponseCount(side, id, 'returned an invalid value');\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n this.warnInvalidResponseCount(side, id, 'threw');\n }\n return undefined;\n }\n\n private warnInvalidResponseCount(side: string, id: string, reason: string): void {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- a warning backend must not replace a successful API response\n try {\n log.warn(\n `[API-${side}-resp-COUNT-WARN] ${id} responseCount selector ${reason}; omitting responseCount`,\n );\n } catch (err: unknown) {\n const error = toError(err);\n void error;\n // The API response is authoritative; observability failures are intentionally ignored here.\n }\n }\n\n /**\n * Serialize a DTO for the LOG LINE ONLY. With no mask on the call, this is a plain JSON.stringify\n * (byte-for-byte the old behavior, no walk) so existing callers pay nothing. With a mask, it runs\n * {@link MaskSpec.stringify}, which produces a masked STRING without ever mutating the DTO — so the\n * object handed to the transport, and thus the value ON THE WIRE, is unchanged.\n */\n private serialize(\n // webpieces-disable no-any-unknown -- DTO types are erased at the api/proxy boundary (matches execute)\n dto: unknown,\n methodInfo: ApiMethodInfo,\n ): string | undefined {\n return methodInfo.mask ? methodInfo.mask.stringify(dto) : JSON.stringify(dto);\n }\n\n /**\n * The ApiCallContext to stamp into. It cannot be MISSING (it is a constructor argument), but it\n * can be INACTIVE — a Node context used outside any `RequestContext.run(...)` scope. That throws:\n * an api call with nowhere to tag is a bug.\n */\n private activeContext(): ApiCallContext {\n const ctx = this.ctx;\n if (!ctx.isActive()) {\n throw new Error(\n 'LogApiCall requires an ACTIVE ApiCallContext. On a Node server, run inside a ' +\n 'RequestContext.run(...) scope — a server filter opens one per request, and a ' +\n 'non-webpieces host must open one around the work that calls a webpieces client. ' +\n '(A BrowserApiCallContext is always active, so this can only be the Node side.)',\n );\n }\n return ctx;\n }\n\n /**\n * Tag + log a thrown call. There is no responseSize — a throw produced no response body to measure.\n */\n private logFailure(\n error: Error,\n methodInfo: ApiMethodInfo,\n durationMs: number,\n requestSize: number | undefined,\n stamp: (info: ApiCallInfo, emit: () => void) => void,\n ): void {\n const side = methodInfo.side;\n const id = `${methodInfo.apiClass}.${methodInfo.methodName}`;\n const errorType = error.constructor.name;\n // The MESSAGE is on BOTH branches, and that is load-bearing: this is the ONLY operator line\n // per failure now that `ApiErrorBoundary` writes none. An operator error message is never\n // published to a caller (see `ApiErrorCodec.publicMessage`), so the log is the one place it\n // exists at all — dropping it on the caller-error branch would lose it outright.\n // Pluggable classification (ClientRegistry): a per-apiClass EXTERNAL-client classifier wins,\n // else the app default, else the webpieces built-in — which is side-dependent (a 4xx the SERVER\n // raised is a handled non-failure; the same 4xx a CLIENT receives means its call FAILED; 266 is\n // never a failure either side). This line is FINAL BY POSITION: it only sees what escaped every\n // retry, so a TRANSIENT_FAILURE here was never retried (no strategy, or a WRITE) or gave up —\n // it logs FAIL exactly like a FAILURE. Only EXPECTED is \"OTHER / result:'success'\".\n const isUser = ClientRegistry.classify(error, methodInfo) === CallOutcome.EXPECTED;\n\n stamp(\n new ApiCallInfo(\n methodInfo,\n 'response',\n isUser ? 'success' : 'failure',\n durationMs,\n requestSize,\n ),\n () =>\n isUser\n ? log.warn(\n `[API-${side}-resp-OTHER] ${id} errorType=${errorType} error=${error.message}`,\n )\n : log.error(\n `[API-${side}-resp-FAIL] ${id} errorType=${errorType} error=${error.message}`,\n ),\n );\n }\n\n /**\n * UTF-8 byte size without platform-specific encoding globals: LogApiCall runs in the\n * browser bundle. Undefined in, undefined out — a `Promise<void>` method has no body to measure,\n * and a 0 there would be a lie (JSON.stringify(undefined) returns undefined, not '').\n */\n private byteSize(serialized: string | undefined): number | undefined {\n if (serialized === undefined) {\n return undefined;\n }\n let bytes = 0;\n for (const character of serialized) {\n const code = character.codePointAt(0)!;\n bytes += code <= 0x7f ? 1 : code <= 0x7ff ? 2 : code <= 0xffff ? 3 : 4;\n }\n return bytes;\n }\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export { WpInternal, WpIpcEndpoint, isInternalApi, getIpcApiId, getIpcEndpoints,
|
|
|
27
27
|
export type { IpcApiType, IpcEndpointKind, IpcEndpointOptions } from './ipc/IpcDecorators';
|
|
28
28
|
export { RouteMetadata } from './http/RouteMetadata';
|
|
29
29
|
export { RouteMetadataFactory } from './http/RouteMetadataFactory';
|
|
30
|
-
export { AuthorizationType, AuthorizationDeclaration, WpAuthorization, getAuthorization } from './http/authorization';
|
|
30
|
+
export { AuthorizationType, AuthorizationDeclaration, WpAuthorization, getAuthorization, } from './http/authorization';
|
|
31
31
|
export type { AuthorizationRequirement } from './http/authorization';
|
|
32
32
|
export { StreamingSchemaCatalog, registerStreamingCatalog } from './http/StreamingSchemaCatalog';
|
|
33
33
|
export { StreamErrorControl, StreamProtocolError, StreamDisconnectedError, } from './http/StreamErrorControl';
|
|
@@ -46,7 +46,7 @@ export type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';
|
|
|
46
46
|
export { McpToolCatalogFile, McpToolCatalogError, McpToolDefinition, } from './mcp/McpToolCatalogFile';
|
|
47
47
|
export { GeneratedApiDocsLayout, GenerateLayoutProblem, GenerateOutputLookup, GenerateOutputTarget, } from './mcp/GeneratedApiDocsLayout';
|
|
48
48
|
export type { LayoutDependsOn, LayoutDependsOnObject, LayoutTarget, LayoutTargets, } from './mcp/GeneratedApiDocsLayout';
|
|
49
|
-
export { AuthMeta, AuthDeclaration, jwt, oidcSameProject, oidcSvcs, sharedSecret, webhook, apiKey } from './http/auth-mode';
|
|
49
|
+
export { AuthMeta, AuthDeclaration, jwt, oidcSameProject, oidcSvcs, sharedSecret, webhook, apiKey, } from './http/auth-mode';
|
|
50
50
|
export type { AuthMode, ApiKeyCredential, ApiKeyCredentials, AuthMethod, AuthMethods, OidcAuthMethod, OidcAuthMethods, OidcCallers, } from './http/auth-mode';
|
|
51
51
|
export { Rpc, PubSub, Queue, ENDPOINT_KINDS_BY_API_KIND, getApiKind, assertApiKind, assertPubSubConventions, getQueueName, } from './http/api-kind';
|
|
52
52
|
export type { ApiKind } from './http/api-kind';
|
|
@@ -87,6 +87,7 @@ export { ContextWriter, ContextStore } from './http/ContextWriter';
|
|
|
87
87
|
export { DestinationTrust } from './http/DestinationTrust';
|
|
88
88
|
export { ContextMgr } from './http/ContextMgr';
|
|
89
89
|
export { LogApiCallImpl } from './http/LogApiCall';
|
|
90
|
+
export { LogApiCall, LogApiCallConfig } from './http/LogApiCall';
|
|
90
91
|
export { MaskSpec } from './http/LogFieldMask';
|
|
91
92
|
export type { MaskMode } from './http/LogFieldMask';
|
|
92
93
|
export { ApiCallInfo } from './http/ApiCallInfo';
|
package/src/index.js
CHANGED
|
@@ -11,8 +11,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
11
11
|
exports.MCP = exports.EXTERNAL_CUSTOMER = exports.SVC_TO_SVC = exports.ApiType = exports.NUMERIC_METADATA_KEYS = exports.WpMax = exports.WpMin = exports.WpInt = exports.getHttpParameterDeclarations = exports.QueryParam = exports.PathParam = exports.METADATA_KEYS = exports.validateNoConflictingDecorators = exports.assertEveryWebhookEndpointRetainsRawBody = exports.assertEveryExternalEndpointDeclaresCaller = exports.assertEveryEndpointHasAuthMode = exports.getAuthMeta = exports.isApiPath = exports.isRawBody = exports.isFormPost = exports.getMaskSpec = exports.getEndpointKinds = exports.getEndpointKind = exports.getEndpointOperation = exports.getEndpointOptions = exports.getEndpointHttpMethod = exports.getEndpoints = exports.getApiPath = exports.MaskLog = exports.isLocalOnly = exports.WpLocalOnly = exports.MISSING_AUTH_DECORATOR_FIX = exports.WpAuth = exports.WpAuthPublic = exports.Endpoint = exports.ApiPath = exports.GCP_LOG_BUDGET_BYTES = exports.MAX_GCP_LOG_BYTES = exports.LogChunkInfo = exports.LogChunkerImpl = exports.LogChunker = exports.LogManager = exports.ConsoleLoggerFactory = exports.ConsoleLogger = exports.DESIGN_METADATA_KEYS = exports.isDocumentDesign = exports.DocumentDesign = exports.ContextTuple = exports.ContextKey = exports.toError = void 0;
|
|
12
12
|
exports.InvalidEndpointForMcp = exports.WRITE = exports.WRITE_IDEMPOTENT = exports.READ = exports.EXTERNAL = exports.CRON = exports.CLOUDTASKS = exports.RPC = exports.POST = exports.GET = exports.registerStreamingSchemas = exports.getStreamingEndpoint = exports.WpStream = exports.StreamDirection = exports.StreamWriter = exports.StreamTransportError = exports.StreamEventValidator = exports.StreamingEndpointMetadata = exports.StreamEnvelope = exports.StreamCorrelation = exports.HttpParameterDeclaration = exports.HttpParameterBinding = exports.HttpContractMapper = exports.BoundHttpRequest = exports.StreamDisconnectedError = exports.StreamProtocolError = exports.StreamErrorControl = exports.registerStreamingCatalog = exports.StreamingSchemaCatalog = exports.getAuthorization = exports.WpAuthorization = exports.AuthorizationDeclaration = exports.AuthorizationType = exports.RouteMetadataFactory = exports.RouteMetadata = exports.IPC_METADATA_KEYS = exports.assertNotInternalApi = exports.assertInternalApi = exports.getIpcMaskSpec = exports.getIpcEndpointKind = exports.getIpcEndpoints = exports.getIpcApiId = exports.isInternalApi = exports.WpIpcEndpoint = exports.WpInternal = exports.assertApiTypeMatchesMcpTools = exports.getApiTypeEndpointNames = exports.getApiTypes = exports.DEFAULT_API_TYPES = exports.API_TYPE_METADATA_KEY = void 0;
|
|
13
13
|
exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = exports.ENTITY_NOT_FOUND = exports.SECRETS = exports.Secrets = exports.getEndpointCaller = exports.isExternalSystemKind = exports.ExternalCaller = exports.DEFAULT_CALLER_KIND = exports.EXTERNAL_SYSTEM_KINDS = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.ENDPOINT_KINDS_BY_API_KIND = exports.Queue = exports.PubSub = exports.Rpc = exports.apiKey = exports.webhook = exports.sharedSecret = exports.oidcSvcs = exports.oidcSameProject = exports.jwt = exports.AuthDeclaration = exports.AuthMeta = exports.GenerateOutputTarget = exports.GenerateOutputLookup = exports.GenerateLayoutProblem = exports.GeneratedApiDocsLayout = exports.McpToolDefinition = exports.McpToolCatalogError = exports.McpToolCatalogFile = exports.ObjectSchemaBuilder = exports.DtoValidationFailure = exports.ApiJsonSchemaValidator = exports.ApiJsonSchemaDiscriminator = exports.ApiJsonSchema = exports.McpToolName = exports.mcpHintsForOperation = exports.getWpMcpTools = exports.getInvalidEndpointsForMcp = exports.WpMcpToolMetadata = exports.WpMcpToolHints = exports.WpMcpTool = exports.InvalidEndpointForMcpMetadata = void 0;
|
|
14
|
-
exports.
|
|
15
|
-
exports.
|
|
14
|
+
exports.Filter = exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiMethodInfo = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallConfig = exports.LogApiCall = exports.LogApiCallImpl = exports.ContextMgr = exports.DestinationTrust = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.WEBPIECES_DEFAULT_ERROR_CLASSIFIER = exports.WebpiecesDefaultErrorClassifier = exports.KeyedErrorClassifier = exports.CallOutcome = exports.ReceivedApiErrorRule = exports.SurfaceEndUserStatus = exports.WEBPIECES_DEFAULT_ERROR_TRANSLATOR = exports.WebpiecesDefaultErrorTranslator = exports.HttpResponseDto = exports.HttpResponseStatus = exports.HttpHeader = exports.RuntimeLocality = exports.ServiceInfo = exports.ApiCallTimeoutError = exports.CallContext = exports.CallDeadline = exports.RetryStrategy = exports.TransportDefault = exports.CallRegistry = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NetworkRejectClassifier = exports.NO_REG_CODE = void 0;
|
|
15
|
+
exports.Mcp = exports.OAuthAuthorizationErrorRedirect = exports.OAuthErrorResponse = exports.OAuthErrorBody = exports.OAuthBearerChallenge = exports.OAuthErrorExtras = exports.OAuthProtocolError = exports.OAuthText = exports.OAuthErrorStatus = exports.OAuthErrorCode = exports.ApiErrorHttpStatus = exports.RpcCallFailed = exports.ReportableApiFailure = exports.CallModeMarker = exports.CallMode = exports.ApiCallSite = exports.ApiErrorBoundary = exports.ApiErrorPayload = exports.ApiErrorCodec = exports.ClientRole = exports.EndUserErrorRegistry = exports.EndUserErrorCode = exports.ApiEndUserBadRequestError = exports.ApiEndUserForbiddenError = exports.ApiEndUserNotFoundError = exports.ApiClientTooOldError = exports.ApiCodedError = exports.ApiNotImplementedError = exports.ApiUnsupportedMediaTypeError = exports.ApiPreconditionFailedError = exports.ApiUnprocessableError = exports.ApiConflictError = exports.ApiEndpointNotFoundError = exports.ApiConnectionError = exports.ApiRetriesExhaustedError = exports.ApiDependencyBackoffError = exports.ApiUnauthorizedError = exports.ApiBadRequestError = exports.ApiEndUserError = exports.ApiRequestTimeoutError = exports.ApiForbiddenError = exports.ApiRateLimitedError = exports.ApiImplementationError = exports.ApiDependencyTimeoutError = exports.ApiUnavailableError = exports.ApiBadGatewayError = exports.ApiDependencyError = exports.ApiNotFoundError = exports.ApiError = exports.FilterChain = void 0;
|
|
16
|
+
exports.McpToolProfilesOptions = void 0;
|
|
16
17
|
var errorUtils_1 = require("./lib/errorUtils");
|
|
17
18
|
Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return errorUtils_1.toError; } });
|
|
18
19
|
var ContextKey_1 = require("./ContextKey");
|
|
@@ -290,6 +291,11 @@ Object.defineProperty(exports, "ContextMgr", { enumerable: true, get: function (
|
|
|
290
291
|
// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.
|
|
291
292
|
var LogApiCall_1 = require("./http/LogApiCall");
|
|
292
293
|
Object.defineProperty(exports, "LogApiCallImpl", { enumerable: true, get: function () { return LogApiCall_1.LogApiCallImpl; } });
|
|
294
|
+
// The logged-body cap (#1300): a per-runtime default (browser 2,000 / node 50,000 chars), overridable
|
|
295
|
+
// once at startup with `LogApiCall.configure(new LogApiCallConfig(n))`.
|
|
296
|
+
var LogApiCall_2 = require("./http/LogApiCall");
|
|
297
|
+
Object.defineProperty(exports, "LogApiCall", { enumerable: true, get: function () { return LogApiCall_2.LogApiCall; } });
|
|
298
|
+
Object.defineProperty(exports, "LogApiCallConfig", { enumerable: true, get: function () { return LogApiCall_2.LogApiCallConfig; } });
|
|
293
299
|
// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets
|
|
294
300
|
// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.
|
|
295
301
|
var LogFieldMask_1 = require("./http/LogFieldMask");
|
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":"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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 {\n AnyContextKey,\n AnyTrustedContextKey,\n AnyUntrustedContextKey,\n Trust,\n} 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 {\n LogChunker,\n LogChunkerImpl,\n LogChunkInfo,\n MAX_GCP_LOG_BYTES,\n GCP_LOG_BUDGET_BYTES,\n} 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 API error taxonomy, 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 // Auth mode decorators (clean service-to-service + user JWT model)\n WpAuthPublic,\n WpAuth,\n MISSING_AUTH_DECORATOR_FIX,\n WpLocalOnly,\n isLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointHttpMethod,\n getEndpointOptions,\n getEndpointOperation,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\nexport {\n PathParam,\n QueryParam,\n getHttpParameterDeclarations,\n} from './http/http-parameter-decorators';\nexport { WpInt, WpMin, WpMax, NUMERIC_METADATA_KEYS } from './http/numeric-field-decorators';\nexport type { Integer } from './http/numeric-field-decorators';\nexport {\n ApiType,\n SVC_TO_SVC,\n EXTERNAL_CUSTOMER,\n MCP,\n API_TYPE_METADATA_KEY,\n DEFAULT_API_TYPES,\n getApiTypes,\n getApiTypeEndpointNames,\n assertApiTypeMatchesMcpTools,\n} from './http/api-type';\nexport type { ApiTypeKind } from './http/api-type';\nexport {\n WpInternal,\n WpIpcEndpoint,\n isInternalApi,\n getIpcApiId,\n getIpcEndpoints,\n getIpcEndpointKind,\n getIpcMaskSpec,\n assertInternalApi,\n assertNotInternalApi,\n IPC_METADATA_KEYS,\n} from './ipc/IpcDecorators';\nexport type { IpcApiType, IpcEndpointKind, IpcEndpointOptions } from './ipc/IpcDecorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport { RouteMetadataFactory } from './http/RouteMetadataFactory';\nexport { AuthorizationType, AuthorizationDeclaration, WpAuthorization, getAuthorization } from './http/authorization';\nexport type { AuthorizationRequirement } from './http/authorization';\nexport { StreamingSchemaCatalog, registerStreamingCatalog } from './http/StreamingSchemaCatalog';\nexport {\n StreamErrorControl,\n StreamProtocolError,\n StreamDisconnectedError,\n} from './http/StreamErrorControl';\nexport {\n BoundHttpRequest,\n HttpContractMapper,\n HttpParameterBinding,\n HttpParameterDeclaration,\n} from './http/HttpContract';\nexport {\n StreamCorrelation,\n StreamEnvelope,\n StreamingEndpointMetadata,\n StreamEventValidator,\n StreamTransportError,\n StreamWriter,\n StreamDirection,\n WpStream,\n getStreamingEndpoint,\n registerStreamingSchemas,\n} from './http/StreamingContract';\nexport type { RequestStream, ResponseStream, StreamEnvelopeKind } from './http/StreamingContract';\nexport type {\n EndpointResponseType,\n HttpMethod,\n HttpParameterSource,\n HttpParameterValueType,\n} from './http/HttpContract';\nexport { GET, POST } from './http/HttpContract';\nexport type {\n EndpointKind,\n EndpointOperation,\n EndpointOptions,\n ExternalEndpointOptions,\n} from './http/decorators';\nexport { RPC, CLOUDTASKS, CRON, EXTERNAL, READ, WRITE_IDEMPOTENT, WRITE } from './http/decorators';\nexport {\n InvalidEndpointForMcp,\n InvalidEndpointForMcpMetadata,\n WpMcpTool,\n WpMcpToolHints,\n WpMcpToolMetadata,\n getInvalidEndpointsForMcp,\n getWpMcpTools,\n mcpHintsForOperation,\n} from './mcp/McpMetadata';\nexport type { WpMcpMethodDecorator } from './mcp/McpMetadata';\nexport { McpToolName } from './mcp/McpToolName';\nexport {\n ApiJsonSchema,\n ApiJsonSchemaDiscriminator,\n ApiJsonSchemaValidator,\n DtoValidationFailure,\n ObjectSchemaBuilder,\n} from './mcp/DtoSchema';\nexport type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';\nexport {\n McpToolCatalogFile,\n McpToolCatalogError,\n McpToolDefinition,\n} from './mcp/McpToolCatalogFile';\nexport {\n GeneratedApiDocsLayout,\n GenerateLayoutProblem,\n GenerateOutputLookup,\n GenerateOutputTarget,\n} from './mcp/GeneratedApiDocsLayout';\nexport type {\n LayoutDependsOn,\n LayoutDependsOnObject,\n LayoutTarget,\n LayoutTargets,\n} from './mcp/GeneratedApiDocsLayout';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport { AuthMeta, AuthDeclaration, jwt, oidcSameProject, oidcSvcs, sharedSecret, webhook, apiKey } from './http/auth-mode';\nexport type {\n AuthMode,\n ApiKeyCredential,\n ApiKeyCredentials,\n AuthMethod,\n AuthMethods,\n OidcAuthMethod,\n OidcAuthMethods,\n OidcCallers,\n} from './http/auth-mode';\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming. Split out of decorators.ts for file size only;\n// one-way dependency api-kind -> decorators, and the barrel keeps the surface identical.\nexport {\n Rpc,\n PubSub,\n Queue,\n ENDPOINT_KINDS_BY_API_KIND,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n} from './http/api-kind';\nexport type { ApiKind } from './http/api-kind';\n// WHO calls an `external` endpoint — the caller declaration\n// @Endpoint(POST, ..., WRITE, EXTERNAL, { calledBy })\n// requires, and the reader for it.\nexport {\n EXTERNAL_SYSTEM_KINDS,\n DEFAULT_CALLER_KIND,\n ExternalCaller,\n isExternalSystemKind,\n getEndpointCaller,\n} from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per sharedSecret(...) key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// Authentication subtype constants.\nexport {\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 './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 { CallRegistry } from './http/CallRegistry';\nexport type { CallScope } from './http/CallRegistry';\nexport { TransportDefault } from './http/TransportDefault';\nexport { RetryStrategy } from './http/RetryStrategy';\nexport { CallDeadline } from './http/CallDeadline';\nexport { CallContext } from './http/CallStrategy';\nexport type { Attempt, CallStrategy } from './http/CallStrategy';\nexport { ApiCallTimeoutError } from './http/ApiCallTimeoutError';\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// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @WpLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\n// The ENTIRE HTTP response as pure data — the ONE form both transports (express, fetch) are\n// normalised into, so an ErrorTranslator implementation is written once and serves both.\nexport { HttpHeader, HttpResponseStatus, HttpResponseDto } from './http/HttpResponseDto';\n// Pluggable, bidirectional error translation (app exception <-> the WHOLE response). Set on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport type { ErrorTranslator } from './http/ErrorTranslator';\n// THE webpieces default translator — always installed, and what an app's own translator delegates to\n// in order to decline an error. ONE class, both halves, node and browser alike.\nexport {\n WebpiecesDefaultErrorTranslator,\n WEBPIECES_DEFAULT_ERROR_TRANSLATOR,\n} from './http/WebpiecesDefaultErrorTranslator';\n// WHICH KIND OF CALLER drove this request (gui / llm / public-api), and the per-request end-user\n// status derived from it — the replacement for the deleted per-router EndUserStatus.\nexport { SurfaceEndUserStatus } from './http/Surface';\nexport type { Surface } from './http/Surface';\n// The ONE rule for an error RECEIVED from another process, shared verbatim by HTTP and IPC.\nexport { ReceivedApiErrorRule } from './errors/ReceivedApiErrorRule';\n// Pluggable per-client error classification: one CallOutcome (EXPECTED / TRANSIENT_FAILURE /\n// FAILURE) per thrown API-call error. Registered on ClientRegistry at startup; read by RetryStrategy,\n// CallRegistry's per-attempt log line, LogApiCall and the MCP renderer's retry advice.\nexport type { ErrorClassifier } from './http/ErrorClassifier';\nexport { CallOutcome, KeyedErrorClassifier } from './http/ErrorClassifier';\nexport {\n WebpiecesDefaultErrorClassifier,\n WEBPIECES_DEFAULT_ERROR_CLASSIFIER,\n} from './http/WebpiecesDefaultErrorClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport { ContextWriter, ContextStore } from './http/ContextWriter';\n\n// The OUTBOUND half of the trust model: whether a TRUSTED context key may ride to the endpoint being\n// called. Built ONLY from the destination endpoint's AuthMode — see the class doc.\nexport { DestinationTrust } from './http/DestinationTrust';\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). NOT a singleton: construct one per environment\n// with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`\n// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.\nexport { LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl lives in @webpieces/core-context, the browser one in\n// @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiCallDirection, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport {\n ApiCallLogName,\n ApiCallLogNameImpl,\n LOG_API_CALL_LOGGER_NAME,\n} from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\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 {\n RecordedEndpoint,\n RecordedError,\n RecordedTestCase,\n} from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n\n// ---------------------------------------------------------------------------------------------\n// Filter-chain primitives, shared by BOTH chains: the inbound server chain\n// (`Filter<MethodMeta, WpResponse<unknown>>`, @webpieces/http-routing) and the outbound client\n// chain (`Filter<ClientRequest, Response>`, @webpieces/http-client-core). Declared once, here, in\n// the package both depend on — see the class doc for why a second pair would be a shim.\nexport { Filter } from './filters/Filter';\nexport type { Service } from './filters/Filter';\nexport { FilterChain } from './filters/FilterChain';\n\nexport {\n ApiError,\n ApiNotFoundError,\n ApiDependencyError,\n ApiBadGatewayError,\n ApiUnavailableError,\n ApiDependencyTimeoutError,\n ApiImplementationError,\n ApiRateLimitedError,\n ApiForbiddenError,\n ApiRequestTimeoutError,\n ApiEndUserError,\n ApiBadRequestError,\n ApiUnauthorizedError,\n ApiDependencyBackoffError,\n ApiRetriesExhaustedError,\n ApiConnectionError,\n ApiEndpointNotFoundError,\n ApiConflictError,\n ApiUnprocessableError,\n ApiPreconditionFailedError,\n ApiUnsupportedMediaTypeError,\n ApiNotImplementedError,\n ApiCodedError,\n ApiClientTooOldError,\n ApiEndUserNotFoundError,\n ApiEndUserForbiddenError,\n ApiEndUserBadRequestError,\n EndUserErrorCode,\n EndUserErrorRegistry,\n ClientRole,\n ApiErrorCodec,\n ApiErrorPayload,\n ApiErrorBoundary,\n ApiCallSite,\n CallMode,\n CallModeMarker,\n ReportableApiFailure,\n RpcCallFailed,\n} from './errors';\nexport type {\n ApiErrorKind,\n ApiStatusCode,\n EdgeHttpStatus,\n EndUserErrorFactory,\n CallSiteBoundary,\n} from './errors';\nexport { ApiErrorHttpStatus } from './http/ApiErrorHttpStatus';\n\n// RFC 6749 / 6750 / 7591 OAuth refusals for app-hosted OAuth endpoints (#1176): the typed error,\n// its one renderer, the Bearer challenge builder and the authorization-endpoint error redirect.\nexport { OAuthErrorCode, OAuthErrorStatus, OAuthText } from './oauth/OAuthErrorCode';\nexport type {\n OAuthHttpStatus,\n InvalidClientStatus,\n OAuthFixedStatusErrorCode,\n BearerErrorCode,\n AuthorizationEndpointErrorCode,\n} from './oauth/OAuthErrorCode';\nexport { OAuthProtocolError, OAuthErrorExtras } from './oauth/OAuthProtocolError';\nexport { OAuthBearerChallenge } from './oauth/OAuthBearerChallenge';\nexport { OAuthErrorBody, OAuthErrorResponse } from './oauth/OAuthErrorResponse';\nexport { OAuthAuthorizationErrorRedirect } from './oauth/OAuthAuthorizationErrorRedirect';\nexport type { PublishedKind, PublishedApiError } from './http/ApiErrorHttpStatus';\n\nexport { Mcp, McpToolProfilesOptions } from './mcp/Mcp';\nexport type { McpToolProfiles } from './mcp/Mcp';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type {\n AnyContextKey,\n AnyTrustedContextKey,\n AnyUntrustedContextKey,\n Trust,\n} 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 {\n LogChunker,\n LogChunkerImpl,\n LogChunkInfo,\n MAX_GCP_LOG_BYTES,\n GCP_LOG_BUDGET_BYTES,\n} 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 API error taxonomy, 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 // Auth mode decorators (clean service-to-service + user JWT model)\n WpAuthPublic,\n WpAuth,\n MISSING_AUTH_DECORATOR_FIX,\n WpLocalOnly,\n isLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointHttpMethod,\n getEndpointOptions,\n getEndpointOperation,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\nexport {\n PathParam,\n QueryParam,\n getHttpParameterDeclarations,\n} from './http/http-parameter-decorators';\nexport { WpInt, WpMin, WpMax, NUMERIC_METADATA_KEYS } from './http/numeric-field-decorators';\nexport type { Integer } from './http/numeric-field-decorators';\nexport {\n ApiType,\n SVC_TO_SVC,\n EXTERNAL_CUSTOMER,\n MCP,\n API_TYPE_METADATA_KEY,\n DEFAULT_API_TYPES,\n getApiTypes,\n getApiTypeEndpointNames,\n assertApiTypeMatchesMcpTools,\n} from './http/api-type';\nexport type { ApiTypeKind } from './http/api-type';\nexport {\n WpInternal,\n WpIpcEndpoint,\n isInternalApi,\n getIpcApiId,\n getIpcEndpoints,\n getIpcEndpointKind,\n getIpcMaskSpec,\n assertInternalApi,\n assertNotInternalApi,\n IPC_METADATA_KEYS,\n} from './ipc/IpcDecorators';\nexport type { IpcApiType, IpcEndpointKind, IpcEndpointOptions } from './ipc/IpcDecorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport { RouteMetadataFactory } from './http/RouteMetadataFactory';\nexport {\n AuthorizationType,\n AuthorizationDeclaration,\n WpAuthorization,\n getAuthorization,\n} from './http/authorization';\nexport type { AuthorizationRequirement } from './http/authorization';\nexport { StreamingSchemaCatalog, registerStreamingCatalog } from './http/StreamingSchemaCatalog';\nexport {\n StreamErrorControl,\n StreamProtocolError,\n StreamDisconnectedError,\n} from './http/StreamErrorControl';\nexport {\n BoundHttpRequest,\n HttpContractMapper,\n HttpParameterBinding,\n HttpParameterDeclaration,\n} from './http/HttpContract';\nexport {\n StreamCorrelation,\n StreamEnvelope,\n StreamingEndpointMetadata,\n StreamEventValidator,\n StreamTransportError,\n StreamWriter,\n StreamDirection,\n WpStream,\n getStreamingEndpoint,\n registerStreamingSchemas,\n} from './http/StreamingContract';\nexport type { RequestStream, ResponseStream, StreamEnvelopeKind } from './http/StreamingContract';\nexport type {\n EndpointResponseType,\n HttpMethod,\n HttpParameterSource,\n HttpParameterValueType,\n} from './http/HttpContract';\nexport { GET, POST } from './http/HttpContract';\nexport type {\n EndpointKind,\n EndpointOperation,\n EndpointOptions,\n ExternalEndpointOptions,\n} from './http/decorators';\nexport { RPC, CLOUDTASKS, CRON, EXTERNAL, READ, WRITE_IDEMPOTENT, WRITE } from './http/decorators';\nexport {\n InvalidEndpointForMcp,\n InvalidEndpointForMcpMetadata,\n WpMcpTool,\n WpMcpToolHints,\n WpMcpToolMetadata,\n getInvalidEndpointsForMcp,\n getWpMcpTools,\n mcpHintsForOperation,\n} from './mcp/McpMetadata';\nexport type { WpMcpMethodDecorator } from './mcp/McpMetadata';\nexport { McpToolName } from './mcp/McpToolName';\nexport {\n ApiJsonSchema,\n ApiJsonSchemaDiscriminator,\n ApiJsonSchemaValidator,\n DtoValidationFailure,\n ObjectSchemaBuilder,\n} from './mcp/DtoSchema';\nexport type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';\nexport {\n McpToolCatalogFile,\n McpToolCatalogError,\n McpToolDefinition,\n} from './mcp/McpToolCatalogFile';\nexport {\n GeneratedApiDocsLayout,\n GenerateLayoutProblem,\n GenerateOutputLookup,\n GenerateOutputTarget,\n} from './mcp/GeneratedApiDocsLayout';\nexport type {\n LayoutDependsOn,\n LayoutDependsOnObject,\n LayoutTarget,\n LayoutTargets,\n} from './mcp/GeneratedApiDocsLayout';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport {\n AuthMeta,\n AuthDeclaration,\n jwt,\n oidcSameProject,\n oidcSvcs,\n sharedSecret,\n webhook,\n apiKey,\n} from './http/auth-mode';\nexport type {\n AuthMode,\n ApiKeyCredential,\n ApiKeyCredentials,\n AuthMethod,\n AuthMethods,\n OidcAuthMethod,\n OidcAuthMethods,\n OidcCallers,\n} from './http/auth-mode';\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming. Split out of decorators.ts for file size only;\n// one-way dependency api-kind -> decorators, and the barrel keeps the surface identical.\nexport {\n Rpc,\n PubSub,\n Queue,\n ENDPOINT_KINDS_BY_API_KIND,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n} from './http/api-kind';\nexport type { ApiKind } from './http/api-kind';\n// WHO calls an `external` endpoint — the caller declaration\n// @Endpoint(POST, ..., WRITE, EXTERNAL, { calledBy })\n// requires, and the reader for it.\nexport {\n EXTERNAL_SYSTEM_KINDS,\n DEFAULT_CALLER_KIND,\n ExternalCaller,\n isExternalSystemKind,\n getEndpointCaller,\n} from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per sharedSecret(...) key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// Authentication subtype constants.\nexport {\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 './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 { CallRegistry } from './http/CallRegistry';\nexport type { CallScope } from './http/CallRegistry';\nexport { TransportDefault } from './http/TransportDefault';\nexport { RetryStrategy } from './http/RetryStrategy';\nexport { CallDeadline } from './http/CallDeadline';\nexport { CallContext } from './http/CallStrategy';\nexport type { Attempt, CallStrategy } from './http/CallStrategy';\nexport { ApiCallTimeoutError } from './http/ApiCallTimeoutError';\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// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @WpLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\n// The ENTIRE HTTP response as pure data — the ONE form both transports (express, fetch) are\n// normalised into, so an ErrorTranslator implementation is written once and serves both.\nexport { HttpHeader, HttpResponseStatus, HttpResponseDto } from './http/HttpResponseDto';\n// Pluggable, bidirectional error translation (app exception <-> the WHOLE response). Set on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport type { ErrorTranslator } from './http/ErrorTranslator';\n// THE webpieces default translator — always installed, and what an app's own translator delegates to\n// in order to decline an error. ONE class, both halves, node and browser alike.\nexport {\n WebpiecesDefaultErrorTranslator,\n WEBPIECES_DEFAULT_ERROR_TRANSLATOR,\n} from './http/WebpiecesDefaultErrorTranslator';\n// WHICH KIND OF CALLER drove this request (gui / llm / public-api), and the per-request end-user\n// status derived from it — the replacement for the deleted per-router EndUserStatus.\nexport { SurfaceEndUserStatus } from './http/Surface';\nexport type { Surface } from './http/Surface';\n// The ONE rule for an error RECEIVED from another process, shared verbatim by HTTP and IPC.\nexport { ReceivedApiErrorRule } from './errors/ReceivedApiErrorRule';\n// Pluggable per-client error classification: one CallOutcome (EXPECTED / TRANSIENT_FAILURE /\n// FAILURE) per thrown API-call error. Registered on ClientRegistry at startup; read by RetryStrategy,\n// CallRegistry's per-attempt log line, LogApiCall and the MCP renderer's retry advice.\nexport type { ErrorClassifier } from './http/ErrorClassifier';\nexport { CallOutcome, KeyedErrorClassifier } from './http/ErrorClassifier';\nexport {\n WebpiecesDefaultErrorClassifier,\n WEBPIECES_DEFAULT_ERROR_CLASSIFIER,\n} from './http/WebpiecesDefaultErrorClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport { ContextWriter, ContextStore } from './http/ContextWriter';\n\n// The OUTBOUND half of the trust model: whether a TRUSTED context key may ride to the endpoint being\n// called. Built ONLY from the destination endpoint's AuthMode — see the class doc.\nexport { DestinationTrust } from './http/DestinationTrust';\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). NOT a singleton: construct one per environment\n// with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`\n// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.\nexport { LogApiCallImpl } from './http/LogApiCall';\n// The logged-body cap (#1300): a per-runtime default (browser 2,000 / node 50,000 chars), overridable\n// once at startup with `LogApiCall.configure(new LogApiCallConfig(n))`.\nexport { LogApiCall, LogApiCallConfig } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl lives in @webpieces/core-context, the browser one in\n// @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiCallDirection, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport {\n ApiCallLogName,\n ApiCallLogNameImpl,\n LOG_API_CALL_LOGGER_NAME,\n} from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\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 {\n RecordedEndpoint,\n RecordedError,\n RecordedTestCase,\n} from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n\n// ---------------------------------------------------------------------------------------------\n// Filter-chain primitives, shared by BOTH chains: the inbound server chain\n// (`Filter<MethodMeta, WpResponse<unknown>>`, @webpieces/http-routing) and the outbound client\n// chain (`Filter<ClientRequest, Response>`, @webpieces/http-client-core). Declared once, here, in\n// the package both depend on — see the class doc for why a second pair would be a shim.\nexport { Filter } from './filters/Filter';\nexport type { Service } from './filters/Filter';\nexport { FilterChain } from './filters/FilterChain';\n\nexport {\n ApiError,\n ApiNotFoundError,\n ApiDependencyError,\n ApiBadGatewayError,\n ApiUnavailableError,\n ApiDependencyTimeoutError,\n ApiImplementationError,\n ApiRateLimitedError,\n ApiForbiddenError,\n ApiRequestTimeoutError,\n ApiEndUserError,\n ApiBadRequestError,\n ApiUnauthorizedError,\n ApiDependencyBackoffError,\n ApiRetriesExhaustedError,\n ApiConnectionError,\n ApiEndpointNotFoundError,\n ApiConflictError,\n ApiUnprocessableError,\n ApiPreconditionFailedError,\n ApiUnsupportedMediaTypeError,\n ApiNotImplementedError,\n ApiCodedError,\n ApiClientTooOldError,\n ApiEndUserNotFoundError,\n ApiEndUserForbiddenError,\n ApiEndUserBadRequestError,\n EndUserErrorCode,\n EndUserErrorRegistry,\n ClientRole,\n ApiErrorCodec,\n ApiErrorPayload,\n ApiErrorBoundary,\n ApiCallSite,\n CallMode,\n CallModeMarker,\n ReportableApiFailure,\n RpcCallFailed,\n} from './errors';\nexport type {\n ApiErrorKind,\n ApiStatusCode,\n EdgeHttpStatus,\n EndUserErrorFactory,\n CallSiteBoundary,\n} from './errors';\nexport { ApiErrorHttpStatus } from './http/ApiErrorHttpStatus';\n\n// RFC 6749 / 6750 / 7591 OAuth refusals for app-hosted OAuth endpoints (#1176): the typed error,\n// its one renderer, the Bearer challenge builder and the authorization-endpoint error redirect.\nexport { OAuthErrorCode, OAuthErrorStatus, OAuthText } from './oauth/OAuthErrorCode';\nexport type {\n OAuthHttpStatus,\n InvalidClientStatus,\n OAuthFixedStatusErrorCode,\n BearerErrorCode,\n AuthorizationEndpointErrorCode,\n} from './oauth/OAuthErrorCode';\nexport { OAuthProtocolError, OAuthErrorExtras } from './oauth/OAuthProtocolError';\nexport { OAuthBearerChallenge } from './oauth/OAuthBearerChallenge';\nexport { OAuthErrorBody, OAuthErrorResponse } from './oauth/OAuthErrorResponse';\nexport { OAuthAuthorizationErrorRedirect } from './oauth/OAuthAuthorizationErrorRedirect';\nexport type { PublishedKind, PublishedApiError } from './http/ApiErrorHttpStatus';\n\nexport { Mcp, McpToolProfilesOptions } from './mcp/Mcp';\nexport type { McpToolProfiles } from './mcp/Mcp';\n"]}
|