@webpieces/core-util 0.4.604 → 0.4.606
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/ContextMgr.d.ts +15 -2
- package/src/http/ContextMgr.js +17 -2
- package/src/http/ContextMgr.js.map +1 -1
- package/src/http/DestinationTrust.d.ts +58 -0
- package/src/http/DestinationTrust.js +78 -0
- package/src/http/DestinationTrust.js.map +1 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +6 -2
- package/src/index.js.map +1 -1
package/package.json
CHANGED
package/src/http/ContextMgr.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ContextReader } from './ContextReader';
|
|
2
|
+
import { DestinationTrust } from './DestinationTrust';
|
|
2
3
|
/**
|
|
3
4
|
* ContextMgr - propagates the magic context onto outbound BROWSER requests.
|
|
4
5
|
*
|
|
@@ -30,14 +31,26 @@ export declare class ContextMgr {
|
|
|
30
31
|
contextReader: ContextReader);
|
|
31
32
|
/**
|
|
32
33
|
* Build the headers to send on an outbound request: every transferred key (httpHeader set)
|
|
33
|
-
* with a non-empty value, emitted under its `httpHeader`
|
|
34
|
+
* with a non-empty value THAT THIS DESTINATION MAY RECEIVE, emitted under its `httpHeader`
|
|
35
|
+
* wire name.
|
|
34
36
|
*
|
|
35
37
|
* NO request-id chaining. A browser ORIGINATES a trace — it has no inbound request to point
|
|
36
38
|
* back at. If the app puts an `x-request-id` on the store it goes out as-is, and the server's
|
|
37
39
|
* inbound transfer adopts it as hop 1's own id. Chaining is a server concern; see
|
|
38
40
|
* RequestContextHeaders.
|
|
39
41
|
*
|
|
42
|
+
* `destination` gates TRUSTED keys exactly as it does on the server side (see
|
|
43
|
+
* {@link DestinationTrust}). In practice a browser never reaches the permissive branch — twice
|
|
44
|
+
* over: `BrowserProxyClient.assertEndpointSupported` refuses to bind an `@AuthOidc` /
|
|
45
|
+
* `@AuthSharedSecret` contract at all, so every browser destination is `@AuthJwt` or `@Public`.
|
|
46
|
+
* The rule is applied here anyway rather than argued away, because the OTHER guarantee people
|
|
47
|
+
* reach for — "`MutableContextStore.set` only accepts an untrusted key, so a browser store
|
|
48
|
+
* cannot HOLD a trusted value" — is true of that store and NOT of the seam: {@link ContextMgr}
|
|
49
|
+
* takes any app-supplied {@link ContextReader}, whose `read` is handed an `AnyContextKey`. One
|
|
50
|
+
* enforced rule in both builders beats a browser-only exemption resting on an implementation
|
|
51
|
+
* detail of one implementation.
|
|
52
|
+
*
|
|
40
53
|
* Values are RAW (unmasked) — this map goes on the wire, not in logs.
|
|
41
54
|
*/
|
|
42
|
-
buildOutboundHeaders(): Map<string, string>;
|
|
55
|
+
buildOutboundHeaders(destination: DestinationTrust): Map<string, string>;
|
|
43
56
|
}
|
package/src/http/ContextMgr.js
CHANGED
|
@@ -36,18 +36,33 @@ class ContextMgr {
|
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
38
|
* Build the headers to send on an outbound request: every transferred key (httpHeader set)
|
|
39
|
-
* with a non-empty value, emitted under its `httpHeader`
|
|
39
|
+
* with a non-empty value THAT THIS DESTINATION MAY RECEIVE, emitted under its `httpHeader`
|
|
40
|
+
* wire name.
|
|
40
41
|
*
|
|
41
42
|
* NO request-id chaining. A browser ORIGINATES a trace — it has no inbound request to point
|
|
42
43
|
* back at. If the app puts an `x-request-id` on the store it goes out as-is, and the server's
|
|
43
44
|
* inbound transfer adopts it as hop 1's own id. Chaining is a server concern; see
|
|
44
45
|
* RequestContextHeaders.
|
|
45
46
|
*
|
|
47
|
+
* `destination` gates TRUSTED keys exactly as it does on the server side (see
|
|
48
|
+
* {@link DestinationTrust}). In practice a browser never reaches the permissive branch — twice
|
|
49
|
+
* over: `BrowserProxyClient.assertEndpointSupported` refuses to bind an `@AuthOidc` /
|
|
50
|
+
* `@AuthSharedSecret` contract at all, so every browser destination is `@AuthJwt` or `@Public`.
|
|
51
|
+
* The rule is applied here anyway rather than argued away, because the OTHER guarantee people
|
|
52
|
+
* reach for — "`MutableContextStore.set` only accepts an untrusted key, so a browser store
|
|
53
|
+
* cannot HOLD a trusted value" — is true of that store and NOT of the seam: {@link ContextMgr}
|
|
54
|
+
* takes any app-supplied {@link ContextReader}, whose `read` is handed an `AnyContextKey`. One
|
|
55
|
+
* enforced rule in both builders beats a browser-only exemption resting on an implementation
|
|
56
|
+
* detail of one implementation.
|
|
57
|
+
*
|
|
46
58
|
* Values are RAW (unmasked) — this map goes on the wire, not in logs.
|
|
47
59
|
*/
|
|
48
|
-
buildOutboundHeaders() {
|
|
60
|
+
buildOutboundHeaders(destination) {
|
|
49
61
|
const outbound = new Map();
|
|
50
62
|
for (const key of HeaderRegistry_1.HeaderRegistry.get().getTransferredKeys()) {
|
|
63
|
+
if (!destination.allows(key)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
51
66
|
const value = this.contextReader.read(key);
|
|
52
67
|
if (value !== undefined && value !== null && value !== '') {
|
|
53
68
|
outbound.set(key.httpHeader, value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextMgr.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ContextMgr.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"ContextMgr.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ContextMgr.ts"],"names":[],"mappings":";;;AAGA,qDAAkD;AAClD,+CAA4C;AAC5C,iEAA8D;AAE9D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,UAAU;IAIC;IAFpB;IACI,2DAA2D;IAC3C,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAC7C,CAAC;IAEJ;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,oBAAoB,CAAC,WAA6B;QAC9C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;QAE3C,KAAK,MAAM,GAAG,IAAI,+BAAc,CAAC,GAAG,EAAE,CAAC,kBAAkB,EAAE,EAAE,CAAC;YAC1D,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC3B,SAAS;YACb,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;gBACxD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,UAAW,EAAE,KAAK,CAAC,CAAC;YACzC,CAAC;QACL,CAAC;QAED,2FAA2F;QAC3F,+FAA+F;QAC/F,gGAAgG;QAChG,MAAM,SAAS,GAAG,yBAAW,CAAC,UAAU,EAAE,CAAC;QAC3C,MAAM,mBAAmB,GAAG,2CAAoB,CAAC,cAAc,CAAC,UAAW,CAAC;QAC5E,IAAI,SAAS,EAAE,CAAC;YACZ,QAAQ,CAAC,GAAG,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC;QACjD,CAAC;aAAM,CAAC;YACJ,QAAQ,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,QAAQ,CAAC;IACpB,CAAC;CACJ;AAxDD,gCAwDC","sourcesContent":["import { ContextKey } from '../ContextKey';\nimport { ContextReader } from './ContextReader';\nimport { DestinationTrust } from './DestinationTrust';\nimport { HeaderRegistry } from './HeaderRegistry';\nimport { ServiceInfo } from './ServiceInfo';\nimport { WebpiecesCoreHeaders } from './WebpiecesCoreHeaders';\n\n/**\n * ContextMgr - propagates the magic context onto outbound BROWSER requests.\n *\n * BROWSER-ONLY. Only @webpieces/http-client-browser may name this class. The server reads\n * `RequestContext` directly through `RequestContextHeaders` (in @webpieces/core-context) — a\n * `ContextReader` indirection buys a server nothing, because there is exactly one right answer there.\n *\n * Browsers have no AsyncLocalStorage, so the app holds a `MutableContextStore` and sets values as\n * they become known (login token, tenant). Every transferred key (httpHeader set) in the GLOBAL\n * {@link HeaderRegistry} is read from it and added to outbound requests. The registry is a process\n * global configured once at startup (like LogManager) and is browser-safe: it is the key SCHEMA,\n * not the value store.\n *\n * Example usage:\n * ```typescript\n * // startup, before bootstrap:\n * HeaderRegistry.configure(CompanyHeaders.ALL_HEADERS, true);\n *\n * const store = new MutableContextStore();\n * const factory = new ClientHttpBrowserFactory(store);\n * const client = factory.createRpcClient(SaveApi, new ClientConfig('http://api.example.com'));\n * ```\n */\nexport class ContextMgr {\n\n constructor(\n /** The app-held store that provides context-key values. */\n public readonly contextReader: ContextReader,\n ) {}\n\n /**\n * Build the headers to send on an outbound request: every transferred key (httpHeader set)\n * with a non-empty value THAT THIS DESTINATION MAY RECEIVE, emitted under its `httpHeader`\n * wire name.\n *\n * NO request-id chaining. A browser ORIGINATES a trace — it has no inbound request to point\n * back at. If the app puts an `x-request-id` on the store it goes out as-is, and the server's\n * inbound transfer adopts it as hop 1's own id. Chaining is a server concern; see\n * RequestContextHeaders.\n *\n * `destination` gates TRUSTED keys exactly as it does on the server side (see\n * {@link DestinationTrust}). In practice a browser never reaches the permissive branch — twice\n * over: `BrowserProxyClient.assertEndpointSupported` refuses to bind an `@AuthOidc` /\n * `@AuthSharedSecret` contract at all, so every browser destination is `@AuthJwt` or `@Public`.\n * The rule is applied here anyway rather than argued away, because the OTHER guarantee people\n * reach for — \"`MutableContextStore.set` only accepts an untrusted key, so a browser store\n * cannot HOLD a trusted value\" — is true of that store and NOT of the seam: {@link ContextMgr}\n * takes any app-supplied {@link ContextReader}, whose `read` is handed an `AnyContextKey`. One\n * enforced rule in both builders beats a browser-only exemption resting on an implementation\n * detail of one implementation.\n *\n * Values are RAW (unmasked) — this map goes on the wire, not in logs.\n */\n buildOutboundHeaders(destination: DestinationTrust): Map<string, string> {\n const outbound = new Map<string, string>();\n\n for (const key of HeaderRegistry.get().getTransferredKeys()) {\n if (!destination.allows(key)) {\n continue;\n }\n const value = this.contextReader.read(key);\n if (value !== undefined && value !== null && value !== '') {\n outbound.set(key.httpHeader!, value);\n }\n }\n\n // CLIENT_VERSION: a browser ORIGINATES a call, so it sends its OWN app build version (from\n // ServiceInfo) and the server logs which client build called it — same rule as the server-side\n // RequestContextHeaders. Our version wins; absent if this app was never identified via setInfo.\n const myVersion = ServiceInfo.getVersion();\n const clientVersionHeader = WebpiecesCoreHeaders.CLIENT_VERSION.httpHeader!;\n if (myVersion) {\n outbound.set(clientVersionHeader, myVersion);\n } else {\n outbound.delete(clientVersionHeader);\n }\n\n return outbound;\n }\n}\n"]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AnyContextKey } from '../ContextKey';
|
|
2
|
+
import { AuthMode } from './decorators';
|
|
3
|
+
/**
|
|
4
|
+
* DestinationTrust - the OUTBOUND half of the trust model: may a TRUSTED context key
|
|
5
|
+
* ({@link ContextKey.trusted}) ride to the endpoint we are about to call?
|
|
6
|
+
*
|
|
7
|
+
* ## Why the client has to answer this at all
|
|
8
|
+
*
|
|
9
|
+
* The server already decided (see `PendingWireTrust`): an inbound `x-user-id` is admitted only on a
|
|
10
|
+
* route that verified WHO called it — `@AuthOidc` / `@AuthSharedSecret`. On a `@AuthJwt` or `@Public`
|
|
11
|
+
* route the same header must match what the authenticator independently derived, or the request is
|
|
12
|
+
* REJECTED with a 401.
|
|
13
|
+
*
|
|
14
|
+
* That rule is correct, and it means a client that ships `x-user-id` to a `@Public` endpoint is
|
|
15
|
+
* building a request the callee is obliged to reject. Before this class the outbound builders
|
|
16
|
+
* forwarded EVERY transferred key with no idea what the destination was, so an internal service
|
|
17
|
+
* calling another service's public or JWT endpoint 401'd itself. The fix belongs on the producing
|
|
18
|
+
* side: don't send what cannot possibly be believed.
|
|
19
|
+
*
|
|
20
|
+
* ## Why it is a class with a private constructor and no boolean parameter
|
|
21
|
+
*
|
|
22
|
+
* `buildOutboundHeaders(sendTrusted = true)` would have been three characters of work and exactly the
|
|
23
|
+
* "widening that is an ABSENCE rather than a token" CLAUDE.md rejects — the permissive answer would be
|
|
24
|
+
* what you get by not typing anything. There is no way to build a DestinationTrust except from the
|
|
25
|
+
* destination endpoint's own {@link AuthMode}, so the caller cannot assert a posture the route does
|
|
26
|
+
* not actually have, and `grep -rn DestinationTrust.forAuthMode` lists every place the question is
|
|
27
|
+
* asked. The two instances are PRIVATE for the same reason: exposing them would be a second spelling
|
|
28
|
+
* that skips the derivation.
|
|
29
|
+
*
|
|
30
|
+
* Per CLAUDE.md: data-only structure, so a class rather than an interface or a bare boolean.
|
|
31
|
+
*/
|
|
32
|
+
export declare class DestinationTrust {
|
|
33
|
+
private readonly verifiesCaller;
|
|
34
|
+
/**
|
|
35
|
+
* The destination authenticates its CALLER (@AuthOidc / @AuthSharedSecret), so it is entitled to
|
|
36
|
+
* believe context WE vouch for — this is the service-to-service identity propagation that trusted
|
|
37
|
+
* keys keep an `httpHeader` for.
|
|
38
|
+
*/
|
|
39
|
+
private static readonly VERIFIES_CALLER;
|
|
40
|
+
/**
|
|
41
|
+
* The destination cannot tell us from a browser with curl (@AuthJwt / @Public / an endpoint with
|
|
42
|
+
* no declared mode), so trusted keys are omitted. Untrusted keys still travel.
|
|
43
|
+
*/
|
|
44
|
+
private static readonly CANNOT_VERIFY_CALLER;
|
|
45
|
+
private constructor();
|
|
46
|
+
/**
|
|
47
|
+
* The ONLY way to obtain one: state the destination endpoint's auth mode. `undefined` (an
|
|
48
|
+
* endpoint that declared no mode) is treated as un-verifying, i.e. the SAFE answer — an absent
|
|
49
|
+
* declaration must never be the widest one.
|
|
50
|
+
*/
|
|
51
|
+
static forAuthMode(mode: AuthMode | undefined): DestinationTrust;
|
|
52
|
+
/**
|
|
53
|
+
* May this key go on the wire to this destination? Untrusted keys always may — nobody was ever
|
|
54
|
+
* going to make a security decision on them. A trusted key may only when the destination will
|
|
55
|
+
* authenticate US, because that is the only case its `AuthFilter` will admit it.
|
|
56
|
+
*/
|
|
57
|
+
allows(key: AnyContextKey): boolean;
|
|
58
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DestinationTrust = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* DestinationTrust - the OUTBOUND half of the trust model: may a TRUSTED context key
|
|
6
|
+
* ({@link ContextKey.trusted}) ride to the endpoint we are about to call?
|
|
7
|
+
*
|
|
8
|
+
* ## Why the client has to answer this at all
|
|
9
|
+
*
|
|
10
|
+
* The server already decided (see `PendingWireTrust`): an inbound `x-user-id` is admitted only on a
|
|
11
|
+
* route that verified WHO called it — `@AuthOidc` / `@AuthSharedSecret`. On a `@AuthJwt` or `@Public`
|
|
12
|
+
* route the same header must match what the authenticator independently derived, or the request is
|
|
13
|
+
* REJECTED with a 401.
|
|
14
|
+
*
|
|
15
|
+
* That rule is correct, and it means a client that ships `x-user-id` to a `@Public` endpoint is
|
|
16
|
+
* building a request the callee is obliged to reject. Before this class the outbound builders
|
|
17
|
+
* forwarded EVERY transferred key with no idea what the destination was, so an internal service
|
|
18
|
+
* calling another service's public or JWT endpoint 401'd itself. The fix belongs on the producing
|
|
19
|
+
* side: don't send what cannot possibly be believed.
|
|
20
|
+
*
|
|
21
|
+
* ## Why it is a class with a private constructor and no boolean parameter
|
|
22
|
+
*
|
|
23
|
+
* `buildOutboundHeaders(sendTrusted = true)` would have been three characters of work and exactly the
|
|
24
|
+
* "widening that is an ABSENCE rather than a token" CLAUDE.md rejects — the permissive answer would be
|
|
25
|
+
* what you get by not typing anything. There is no way to build a DestinationTrust except from the
|
|
26
|
+
* destination endpoint's own {@link AuthMode}, so the caller cannot assert a posture the route does
|
|
27
|
+
* not actually have, and `grep -rn DestinationTrust.forAuthMode` lists every place the question is
|
|
28
|
+
* asked. The two instances are PRIVATE for the same reason: exposing them would be a second spelling
|
|
29
|
+
* that skips the derivation.
|
|
30
|
+
*
|
|
31
|
+
* Per CLAUDE.md: data-only structure, so a class rather than an interface or a bare boolean.
|
|
32
|
+
*/
|
|
33
|
+
class DestinationTrust {
|
|
34
|
+
verifiesCaller;
|
|
35
|
+
/**
|
|
36
|
+
* The destination authenticates its CALLER (@AuthOidc / @AuthSharedSecret), so it is entitled to
|
|
37
|
+
* believe context WE vouch for — this is the service-to-service identity propagation that trusted
|
|
38
|
+
* keys keep an `httpHeader` for.
|
|
39
|
+
*/
|
|
40
|
+
static VERIFIES_CALLER = new DestinationTrust(true);
|
|
41
|
+
/**
|
|
42
|
+
* The destination cannot tell us from a browser with curl (@AuthJwt / @Public / an endpoint with
|
|
43
|
+
* no declared mode), so trusted keys are omitted. Untrusted keys still travel.
|
|
44
|
+
*/
|
|
45
|
+
static CANNOT_VERIFY_CALLER = new DestinationTrust(false);
|
|
46
|
+
constructor(verifiesCaller) {
|
|
47
|
+
this.verifiesCaller = verifiesCaller;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The ONLY way to obtain one: state the destination endpoint's auth mode. `undefined` (an
|
|
51
|
+
* endpoint that declared no mode) is treated as un-verifying, i.e. the SAFE answer — an absent
|
|
52
|
+
* declaration must never be the widest one.
|
|
53
|
+
*/
|
|
54
|
+
// webpieces-disable no-function-outside-class -- static factory replacing the (now private) constructor, exactly as ContextKey.trusted/untrusted do: the destination's auth mode must be part of the CALL, and a DI-injected instance method would let a caller hold one without ever naming a route
|
|
55
|
+
static forAuthMode(mode) {
|
|
56
|
+
if (mode === undefined) {
|
|
57
|
+
return DestinationTrust.CANNOT_VERIFY_CALLER;
|
|
58
|
+
}
|
|
59
|
+
switch (mode.kind) {
|
|
60
|
+
case 'oidc':
|
|
61
|
+
case 'shared-secret':
|
|
62
|
+
return DestinationTrust.VERIFIES_CALLER;
|
|
63
|
+
case 'jwt':
|
|
64
|
+
case 'public':
|
|
65
|
+
return DestinationTrust.CANNOT_VERIFY_CALLER;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* May this key go on the wire to this destination? Untrusted keys always may — nobody was ever
|
|
70
|
+
* going to make a security decision on them. A trusted key may only when the destination will
|
|
71
|
+
* authenticate US, because that is the only case its `AuthFilter` will admit it.
|
|
72
|
+
*/
|
|
73
|
+
allows(key) {
|
|
74
|
+
return this.verifiesCaller || !key.isTrusted();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.DestinationTrust = DestinationTrust;
|
|
78
|
+
//# sourceMappingURL=DestinationTrust.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DestinationTrust.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/DestinationTrust.ts"],"names":[],"mappings":";;;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAa,gBAAgB;IAcY;IAbrC;;;;OAIG;IACK,MAAM,CAAU,eAAe,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAErE;;;OAGG;IACK,MAAM,CAAU,oBAAoB,GAAG,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAE3E,YAAqC,cAAuB;QAAvB,mBAAc,GAAd,cAAc,CAAS;IAAG,CAAC;IAEhE;;;;OAIG;IACH,qSAAqS;IACrS,MAAM,CAAC,WAAW,CAAC,IAA0B;QACzC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACrB,OAAO,gBAAgB,CAAC,oBAAoB,CAAC;QACjD,CAAC;QACD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,KAAK,MAAM,CAAC;YACZ,KAAK,eAAe;gBAChB,OAAO,gBAAgB,CAAC,eAAe,CAAC;YAC5C,KAAK,KAAK,CAAC;YACX,KAAK,QAAQ;gBACT,OAAO,gBAAgB,CAAC,oBAAoB,CAAC;QACrD,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,GAAkB;QACrB,OAAO,IAAI,CAAC,cAAc,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;IACnD,CAAC;;AA3CL,4CA4CC","sourcesContent":["import { AnyContextKey } from '../ContextKey';\nimport { AuthMode } from './decorators';\n\n/**\n * DestinationTrust - the OUTBOUND half of the trust model: may a TRUSTED context key\n * ({@link ContextKey.trusted}) ride to the endpoint we are about to call?\n *\n * ## Why the client has to answer this at all\n *\n * The server already decided (see `PendingWireTrust`): an inbound `x-user-id` is admitted only on a\n * route that verified WHO called it — `@AuthOidc` / `@AuthSharedSecret`. On a `@AuthJwt` or `@Public`\n * route the same header must match what the authenticator independently derived, or the request is\n * REJECTED with a 401.\n *\n * That rule is correct, and it means a client that ships `x-user-id` to a `@Public` endpoint is\n * building a request the callee is obliged to reject. Before this class the outbound builders\n * forwarded EVERY transferred key with no idea what the destination was, so an internal service\n * calling another service's public or JWT endpoint 401'd itself. The fix belongs on the producing\n * side: don't send what cannot possibly be believed.\n *\n * ## Why it is a class with a private constructor and no boolean parameter\n *\n * `buildOutboundHeaders(sendTrusted = true)` would have been three characters of work and exactly the\n * \"widening that is an ABSENCE rather than a token\" CLAUDE.md rejects — the permissive answer would be\n * what you get by not typing anything. There is no way to build a DestinationTrust except from the\n * destination endpoint's own {@link AuthMode}, so the caller cannot assert a posture the route does\n * not actually have, and `grep -rn DestinationTrust.forAuthMode` lists every place the question is\n * asked. The two instances are PRIVATE for the same reason: exposing them would be a second spelling\n * that skips the derivation.\n *\n * Per CLAUDE.md: data-only structure, so a class rather than an interface or a bare boolean.\n */\nexport class DestinationTrust {\n /**\n * The destination authenticates its CALLER (@AuthOidc / @AuthSharedSecret), so it is entitled to\n * believe context WE vouch for — this is the service-to-service identity propagation that trusted\n * keys keep an `httpHeader` for.\n */\n private static readonly VERIFIES_CALLER = new DestinationTrust(true);\n\n /**\n * The destination cannot tell us from a browser with curl (@AuthJwt / @Public / an endpoint with\n * no declared mode), so trusted keys are omitted. Untrusted keys still travel.\n */\n private static readonly CANNOT_VERIFY_CALLER = new DestinationTrust(false);\n\n private constructor(private readonly verifiesCaller: boolean) {}\n\n /**\n * The ONLY way to obtain one: state the destination endpoint's auth mode. `undefined` (an\n * endpoint that declared no mode) is treated as un-verifying, i.e. the SAFE answer — an absent\n * declaration must never be the widest one.\n */\n // webpieces-disable no-function-outside-class -- static factory replacing the (now private) constructor, exactly as ContextKey.trusted/untrusted do: the destination's auth mode must be part of the CALL, and a DI-injected instance method would let a caller hold one without ever naming a route\n static forAuthMode(mode: AuthMode | undefined): DestinationTrust {\n if (mode === undefined) {\n return DestinationTrust.CANNOT_VERIFY_CALLER;\n }\n switch (mode.kind) {\n case 'oidc':\n case 'shared-secret':\n return DestinationTrust.VERIFIES_CALLER;\n case 'jwt':\n case 'public':\n return DestinationTrust.CANNOT_VERIFY_CALLER;\n }\n }\n\n /**\n * May this key go on the wire to this destination? Untrusted keys always may — nobody was ever\n * going to make a security decision on them. A trusted key may only when the destination will\n * authenticate US, because that is the only case its `AuthFilter` will admit it.\n */\n allows(key: AnyContextKey): boolean {\n return this.verifiesCaller || !key.isTrusted();\n }\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export { WebpiecesDefaultFailureClassifier, WEBPIECES_DEFAULT_FAILURE_CLASSIFIER
|
|
|
38
38
|
export { templateDeriver } from './http/templateDeriver';
|
|
39
39
|
export { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';
|
|
40
40
|
export { ContextReader } from './http/ContextReader';
|
|
41
|
+
export { DestinationTrust } from './http/DestinationTrust';
|
|
41
42
|
export { ContextMgr } from './http/ContextMgr';
|
|
42
43
|
export { LogApiCall, LogApiCallImpl } from './http/LogApiCall';
|
|
43
44
|
export { MaskSpec } from './http/LogFieldMask';
|
package/src/index.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.ExternalCaller = exports.DEFAULT_CALLER_KIND = exports.EXTERNAL_SYSTEM_KINDS = exports.METADATA_KEYS = exports.RouteMetadata = exports.AuthMeta = exports.validateNoConflictingDecorators = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.assertEveryExternalEndpointDeclaresCaller = exports.assertEveryEndpointHasAuthMode = exports.getAuthMode = exports.getAuthMeta = exports.isApiPath = exports.isFormPost = exports.getMaskSpec = exports.ENDPOINT_KINDS_BY_API_KIND = exports.getEndpointKinds = exports.getEndpointKind = exports.getEndpointOptions = exports.getEndpoints = exports.getApiPath = exports.MaskLog = exports.Queue = exports.PubSub = exports.Rpc = exports.AuthSharedSecret = exports.AuthOidc = exports.MISSING_AUTH_DECORATOR_FIX = exports.rolesRequired = exports.AuthJwt = exports.Public = 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
|
-
exports.
|
|
13
|
-
exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiCallContextHolder = void 0;
|
|
12
|
+
exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = exports.LogApiCall = exports.ContextMgr = exports.DestinationTrust = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.WEBPIECES_DEFAULT_FAILURE_CLASSIFIER = exports.WebpiecesDefaultFailureClassifier = exports.KeyedFailureClassifier = exports.ErrorWireForm = exports.ServiceInfo = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NetworkRejectClassifier = exports.NO_REG_CODE = 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.OfflineError = exports.HttpUserError = exports.HttpVendorError = exports.HttpTooManyRequestsError = exports.HttpInternalServerError = exports.HttpGatewayTimeoutError = exports.HttpBadGatewayError = exports.HttpTimeoutError = exports.HttpForbiddenError = exports.HttpUnauthorizedError = exports.HttpBadRequestError = exports.EndpointNotFoundError = exports.HttpNotFoundError = exports.HttpError = exports.ProtocolError = exports.SECRETS = exports.Secrets = exports.getEndpointCaller = exports.isExternalSystemKind = void 0;
|
|
13
|
+
exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiCallContextHolder = exports.ApiMethodInfo = void 0;
|
|
14
14
|
var errorUtils_1 = require("./lib/errorUtils");
|
|
15
15
|
Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return errorUtils_1.toError; } });
|
|
16
16
|
var ContextKey_1 = require("./ContextKey");
|
|
@@ -146,6 +146,10 @@ var templateDeriver_1 = require("./http/templateDeriver");
|
|
|
146
146
|
Object.defineProperty(exports, "templateDeriver", { enumerable: true, get: function () { return templateDeriver_1.templateDeriver; } });
|
|
147
147
|
var WebpiecesCoreHeaders_1 = require("./http/WebpiecesCoreHeaders");
|
|
148
148
|
Object.defineProperty(exports, "WebpiecesCoreHeaders", { enumerable: true, get: function () { return WebpiecesCoreHeaders_1.WebpiecesCoreHeaders; } });
|
|
149
|
+
// The OUTBOUND half of the trust model: whether a TRUSTED context key may ride to the endpoint being
|
|
150
|
+
// called. Built ONLY from the destination endpoint's AuthMode — see the class doc.
|
|
151
|
+
var DestinationTrust_1 = require("./http/DestinationTrust");
|
|
152
|
+
Object.defineProperty(exports, "DestinationTrust", { enumerable: true, get: function () { return DestinationTrust_1.DestinationTrust; } });
|
|
149
153
|
// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).
|
|
150
154
|
// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via
|
|
151
155
|
// RequestContextHeaders in the Node-only @webpieces/core-context.
|
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 { AnyContextKey, AnyTrustedContextKey, AnyUntrustedContextKey, Trust } from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport { LogChunker, LogChunkerImpl, LogChunkInfo, MAX_GCP_LOG_BYTES, GCP_LOG_BUDGET_BYTES } from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the HttpError hierarchy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n // Auth mode decorators (clean service-to-service + user JWT model)\n Public,\n AuthJwt,\n rolesRequired,\n MISSING_AUTH_DECORATOR_FIX,\n AuthOidc,\n AuthSharedSecret,\n // API kind (RPC vs PubSub/Cloud Tasks) + queue naming\n Rpc,\n PubSub,\n Queue,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointOptions,\n getEndpointKind,\n getEndpointKinds,\n ENDPOINT_KINDS_BY_API_KIND,\n getMaskSpec,\n isFormPost,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n validateNoConflictingDecorators,\n AuthMeta,\n RouteMetadata,\n METADATA_KEYS,\n} from './http/decorators';\nexport type { AuthMode, ApiKind, EndpointKind, JwtRoles, JwtRequirement, EndpointOptions, ExternalEndpointOptions } from './http/decorators';\n// WHO calls an `external` endpoint — the caller declaration @Endpoint(..., 'external', {calledBy})\n// requires, and the reader for it.\nexport { EXTERNAL_SYSTEM_KINDS, DEFAULT_CALLER_KIND, ExternalCaller, isExternalSystemKind, getEndpointCaller } from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// HTTP errors\nexport {\n ProtocolError,\n HttpError,\n HttpNotFoundError,\n EndpointNotFoundError,\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpTimeoutError,\n HttpBadGatewayError,\n HttpGatewayTimeoutError,\n HttpInternalServerError,\n HttpTooManyRequestsError,\n HttpVendorError,\n HttpUserError,\n OfflineError,\n // Error subtype constants\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './http/errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// Pluggable, bidirectional error translation (app exception <-> wire form). Registered on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport { ErrorWireForm } from './http/ErrorTranslation';\nexport type { ErrorTranslation } from './http/ErrorTranslation';\n// Pluggable per-client failure classification (is a thrown API-call error a real failure or an\n// expected non-failure?). Registered on ClientRegistry at startup; consulted by LogApiCall.\nexport type { FailureClassifier } from './http/FailureClassifier';\nexport { KeyedFailureClassifier } from './http/FailureClassifier';\nexport {\n WebpiecesDefaultFailureClassifier,\n WEBPIECES_DEFAULT_FAILURE_CLASSIFIER,\n} from './http/WebpiecesDefaultFailureClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above). Singleton: use the LogApiCall constant, not `new`.\nexport { LogApiCall, LogApiCallImpl } from './http/LogApiCall';\n\n// 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 is installed by @webpieces/core-context; the browser gets the no-op.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiType, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport { ApiCallLogName, ApiCallLogNameImpl, LOG_API_CALL_LOGGER_NAME } from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport { ApiCallContextHolder } from './http/ApiCallContext';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport { RecordedEndpoint, RecordedError, RecordedTestCase } from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type { AnyContextKey, AnyTrustedContextKey, AnyUntrustedContextKey, Trust } from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport { LogChunker, LogChunkerImpl, LogChunkInfo, MAX_GCP_LOG_BYTES, GCP_LOG_BUDGET_BYTES } from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the HttpError hierarchy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n // Auth mode decorators (clean service-to-service + user JWT model)\n Public,\n AuthJwt,\n rolesRequired,\n MISSING_AUTH_DECORATOR_FIX,\n AuthOidc,\n AuthSharedSecret,\n // API kind (RPC vs PubSub/Cloud Tasks) + queue naming\n Rpc,\n PubSub,\n Queue,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointOptions,\n getEndpointKind,\n getEndpointKinds,\n ENDPOINT_KINDS_BY_API_KIND,\n getMaskSpec,\n isFormPost,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n validateNoConflictingDecorators,\n AuthMeta,\n RouteMetadata,\n METADATA_KEYS,\n} from './http/decorators';\nexport type { AuthMode, ApiKind, EndpointKind, JwtRoles, JwtRequirement, EndpointOptions, ExternalEndpointOptions } from './http/decorators';\n// WHO calls an `external` endpoint — the caller declaration @Endpoint(..., 'external', {calledBy})\n// requires, and the reader for it.\nexport { EXTERNAL_SYSTEM_KINDS, DEFAULT_CALLER_KIND, ExternalCaller, isExternalSystemKind, getEndpointCaller } from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// HTTP errors\nexport {\n ProtocolError,\n HttpError,\n HttpNotFoundError,\n EndpointNotFoundError,\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpTimeoutError,\n HttpBadGatewayError,\n HttpGatewayTimeoutError,\n HttpInternalServerError,\n HttpTooManyRequestsError,\n HttpVendorError,\n HttpUserError,\n OfflineError,\n // Error subtype constants\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './http/errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// Pluggable, bidirectional error translation (app exception <-> wire form). Registered on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport { ErrorWireForm } from './http/ErrorTranslation';\nexport type { ErrorTranslation } from './http/ErrorTranslation';\n// Pluggable per-client failure classification (is a thrown API-call error a real failure or an\n// expected non-failure?). Registered on ClientRegistry at startup; consulted by LogApiCall.\nexport type { FailureClassifier } from './http/FailureClassifier';\nexport { KeyedFailureClassifier } from './http/FailureClassifier';\nexport {\n WebpiecesDefaultFailureClassifier,\n WEBPIECES_DEFAULT_FAILURE_CLASSIFIER,\n} from './http/WebpiecesDefaultFailureClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\n\n// 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). Singleton: use the LogApiCall constant, not `new`.\nexport { LogApiCall, 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 is installed by @webpieces/core-context; the browser gets the no-op.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiType, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport { ApiCallLogName, ApiCallLogNameImpl, LOG_API_CALL_LOGGER_NAME } from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport { ApiCallContextHolder } from './http/ApiCallContext';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport { RecordedEndpoint, RecordedError, RecordedTestCase } from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n"]}
|