@webpieces/core-util 0.4.699 → 0.4.700

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/core-util",
3
- "version": "0.4.699",
3
+ "version": "0.4.700",
4
4
  "description": "Utility functions for WebPieces - works in browser and Node.js",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Service interface - Similar to Java WebPieces Service<REQ, RESP>.
3
+ * Represents any component that can process a request and return a response.
4
+ *
5
+ * Used for:
6
+ * - The final invocation at the end of a chain (a controller server-side, `fetch` client-side)
7
+ * - Wrapping filters as services in the chain
8
+ * - Functional composition of filters
9
+ */
10
+ export interface Service<REQ, RESP> {
11
+ /**
12
+ * Invoke the service with the given metadata.
13
+ * @param meta - Request metadata
14
+ * @returns Promise of the response
15
+ */
16
+ invoke(meta: REQ): Promise<RESP>;
17
+ }
18
+ /**
19
+ * Filter abstract class - Similar to Java WebPieces Filter<REQ, RESP>.
20
+ *
21
+ * Filters are STATELESS and can handle N concurrent requests.
22
+ * They wrap the execution of subsequent filters and the final service.
23
+ *
24
+ * Key principles:
25
+ * - STATELESS: No instance variables for request data
26
+ * - COMPOSABLE: Use chain() methods for functional composition
27
+ *
28
+ * ## Why this lives in core-util rather than beside either chain that uses it
29
+ *
30
+ * There are TWO chains in webpieces and they are the same concept pointed in opposite directions:
31
+ *
32
+ * - INBOUND, server side: `Filter<MethodMeta, WpResponse<unknown>>` (@webpieces/http-routing) wraps
33
+ * the controller invocation.
34
+ * - OUTBOUND, client side: `Filter<ClientRequest, Response>` (@webpieces/http-client-core) wraps the
35
+ * `fetch`, so a filter can re-point the URL, add headers, or sign the exact serialized bytes.
36
+ *
37
+ * Declaring the abstraction once, in the package both depend on, is what keeps them ONE concept.
38
+ * A second `Filter`/`Service` pair defined beside the client chain would be two spellings of one
39
+ * thing — the shim shape CLAUDE.md rejects — and the two would drift.
40
+ *
41
+ * core-util is browser-safe and dependency-free, and so is this file: it imports nothing.
42
+ */
43
+ export declare abstract class Filter<REQ, RESP> {
44
+ /**
45
+ * Filter method that wraps the next filter/service.
46
+ *
47
+ * @param meta - Metadata about the method being invoked
48
+ * @param nextFilter - Next filter/service as a Service
49
+ * @returns Promise of the response
50
+ */
51
+ abstract filter(meta: REQ, nextFilter: Service<REQ, RESP>): Promise<RESP>;
52
+ /**
53
+ * Chain this filter with another filter.
54
+ * Returns a new Filter that composes both filters.
55
+ *
56
+ * Similar to Java: filter1.chain(filter2)
57
+ *
58
+ * @param nextFilter - The filter to execute after this one
59
+ * @returns Composed filter
60
+ */
61
+ chain(nextFilter: Filter<REQ, RESP>): Filter<REQ, RESP>;
62
+ /**
63
+ * Chain this filter with a final service (controller).
64
+ * Returns a Service that can be invoked.
65
+ *
66
+ * Similar to Java: filter.chain(service)
67
+ *
68
+ * @param svc - The final service (controller) to execute
69
+ * @returns Service wrapping the entire filter chain
70
+ */
71
+ chainService(svc: Service<REQ, RESP>): Service<REQ, RESP>;
72
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Filter = void 0;
4
+ /**
5
+ * Filter abstract class - Similar to Java WebPieces Filter<REQ, RESP>.
6
+ *
7
+ * Filters are STATELESS and can handle N concurrent requests.
8
+ * They wrap the execution of subsequent filters and the final service.
9
+ *
10
+ * Key principles:
11
+ * - STATELESS: No instance variables for request data
12
+ * - COMPOSABLE: Use chain() methods for functional composition
13
+ *
14
+ * ## Why this lives in core-util rather than beside either chain that uses it
15
+ *
16
+ * There are TWO chains in webpieces and they are the same concept pointed in opposite directions:
17
+ *
18
+ * - INBOUND, server side: `Filter<MethodMeta, WpResponse<unknown>>` (@webpieces/http-routing) wraps
19
+ * the controller invocation.
20
+ * - OUTBOUND, client side: `Filter<ClientRequest, Response>` (@webpieces/http-client-core) wraps the
21
+ * `fetch`, so a filter can re-point the URL, add headers, or sign the exact serialized bytes.
22
+ *
23
+ * Declaring the abstraction once, in the package both depend on, is what keeps them ONE concept.
24
+ * A second `Filter`/`Service` pair defined beside the client chain would be two spellings of one
25
+ * thing — the shim shape CLAUDE.md rejects — and the two would drift.
26
+ *
27
+ * core-util is browser-safe and dependency-free, and so is this file: it imports nothing.
28
+ */
29
+ class Filter {
30
+ /**
31
+ * Chain this filter with another filter.
32
+ * Returns a new Filter that composes both filters.
33
+ *
34
+ * Similar to Java: filter1.chain(filter2)
35
+ *
36
+ * @param nextFilter - The filter to execute after this one
37
+ * @returns Composed filter
38
+ */
39
+ chain(nextFilter) {
40
+ const self = this;
41
+ return new (class extends Filter {
42
+ async filter(meta, nextService) {
43
+ // Call outer filter, passing next filter wrapped as a Service
44
+ return self.filter(meta, {
45
+ invoke: (m) => nextFilter.filter(m, nextService),
46
+ });
47
+ }
48
+ })();
49
+ }
50
+ /**
51
+ * Chain this filter with a final service (controller).
52
+ * Returns a Service that can be invoked.
53
+ *
54
+ * Similar to Java: filter.chain(service)
55
+ *
56
+ * @param svc - The final service (controller) to execute
57
+ * @returns Service wrapping the entire filter chain
58
+ */
59
+ chainService(svc) {
60
+ const self = this;
61
+ return {
62
+ invoke: (meta) => self.filter(meta, svc),
63
+ };
64
+ }
65
+ }
66
+ exports.Filter = Filter;
67
+ //# sourceMappingURL=Filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Filter.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/filters/Filter.ts"],"names":[],"mappings":";;;AAkBA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAsB,MAAM;IAaxB;;;;;;;;OAQG;IACH,KAAK,CAAC,UAA6B;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC;QAElB,OAAO,IAAI,CAAC,KAAM,SAAQ,MAAiB;YACvC,KAAK,CAAC,MAAM,CAAC,IAAS,EAAE,WAA+B;gBACnD,8DAA8D;gBAC9D,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;oBACrB,MAAM,EAAE,CAAC,CAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC;iBACxD,CAAC,CAAC;YACP,CAAC;SACJ,CAAC,EAAE,CAAC;IACT,CAAC;IAED;;;;;;;;OAQG;IACH,YAAY,CAAC,GAAuB;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC;QAElB,OAAO;YACH,MAAM,EAAE,CAAC,IAAS,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC;SAChD,CAAC;IACN,CAAC;CACJ;AAnDD,wBAmDC","sourcesContent":["/**\n * Service interface - Similar to Java WebPieces Service<REQ, RESP>.\n * Represents any component that can process a request and return a response.\n *\n * Used for:\n * - The final invocation at the end of a chain (a controller server-side, `fetch` client-side)\n * - Wrapping filters as services in the chain\n * - Functional composition of filters\n */\nexport interface Service<REQ, RESP> {\n /**\n * Invoke the service with the given metadata.\n * @param meta - Request metadata\n * @returns Promise of the response\n */\n invoke(meta: REQ): Promise<RESP>;\n}\n\n/**\n * Filter abstract class - Similar to Java WebPieces Filter<REQ, RESP>.\n *\n * Filters are STATELESS and can handle N concurrent requests.\n * They wrap the execution of subsequent filters and the final service.\n *\n * Key principles:\n * - STATELESS: No instance variables for request data\n * - COMPOSABLE: Use chain() methods for functional composition\n *\n * ## Why this lives in core-util rather than beside either chain that uses it\n *\n * There are TWO chains in webpieces and they are the same concept pointed in opposite directions:\n *\n * - INBOUND, server side: `Filter<MethodMeta, WpResponse<unknown>>` (@webpieces/http-routing) wraps\n * the controller invocation.\n * - OUTBOUND, client side: `Filter<ClientRequest, Response>` (@webpieces/http-client-core) wraps the\n * `fetch`, so a filter can re-point the URL, add headers, or sign the exact serialized bytes.\n *\n * Declaring the abstraction once, in the package both depend on, is what keeps them ONE concept.\n * A second `Filter`/`Service` pair defined beside the client chain would be two spellings of one\n * thing — the shim shape CLAUDE.md rejects — and the two would drift.\n *\n * core-util is browser-safe and dependency-free, and so is this file: it imports nothing.\n */\nexport abstract class Filter<REQ, RESP> {\n //priority is determined by how it is chained only here\n //DO NOT add priority here\n\n /**\n * Filter method that wraps the next filter/service.\n *\n * @param meta - Metadata about the method being invoked\n * @param nextFilter - Next filter/service as a Service\n * @returns Promise of the response\n */\n abstract filter(meta: REQ, nextFilter: Service<REQ, RESP>): Promise<RESP>;\n\n /**\n * Chain this filter with another filter.\n * Returns a new Filter that composes both filters.\n *\n * Similar to Java: filter1.chain(filter2)\n *\n * @param nextFilter - The filter to execute after this one\n * @returns Composed filter\n */\n chain(nextFilter: Filter<REQ, RESP>): Filter<REQ, RESP> {\n const self = this;\n\n return new (class extends Filter<REQ, RESP> {\n async filter(meta: REQ, nextService: Service<REQ, RESP>): Promise<RESP> {\n // Call outer filter, passing next filter wrapped as a Service\n return self.filter(meta, {\n invoke: (m: REQ) => nextFilter.filter(m, nextService),\n });\n }\n })();\n }\n\n /**\n * Chain this filter with a final service (controller).\n * Returns a Service that can be invoked.\n *\n * Similar to Java: filter.chain(service)\n *\n * @param svc - The final service (controller) to execute\n * @returns Service wrapping the entire filter chain\n */\n chainService(svc: Service<REQ, RESP>): Service<REQ, RESP> {\n const self = this;\n\n return {\n invoke: (meta: REQ) => self.filter(meta, svc),\n };\n }\n}\n"]}
@@ -0,0 +1,37 @@
1
+ import { Filter } from './Filter';
2
+ /**
3
+ * FilterChain - Manages execution of filters in priority order.
4
+ * Similar to Java servlet filter chains.
5
+ *
6
+ * Filters arrive ALREADY SORTED by priority (highest first) and each filter calls
7
+ * nextFilter.invoke() to invoke the next filter in the chain. Sorting is the caller's job because
8
+ * priority lives on the DEFINITION (server: `FilterDefinition`, client: `ClientFilterDefinition`),
9
+ * never on the filter itself — see {@link Filter}.
10
+ *
11
+ * The final "filter" in the chain is the `finalHandler` passed to {@link execute}: the controller
12
+ * method on the server, the `fetch` on the client.
13
+ *
14
+ * A filter may invoke the rest of the chain MORE THAN ONCE (the client-side SSRF guard re-invokes it
15
+ * to follow a validated redirect) or NOT AT ALL (an auth filter short-circuiting). Nothing here
16
+ * assumes exactly one pass.
17
+ */
18
+ export declare class FilterChain<REQ, RESP> {
19
+ private filters;
20
+ constructor(filters: Filter<REQ, RESP>[]);
21
+ /**
22
+ * Execute the filter chain.
23
+ *
24
+ * @param meta - Request metadata
25
+ * @param finalHandler - The controller method to execute at the end
26
+ * @returns Promise of the response
27
+ */
28
+ execute(meta: REQ, finalHandler: () => Promise<RESP>): Promise<RESP>;
29
+ /**
30
+ * Get all filters in the chain (sorted by priority).
31
+ */
32
+ getFilters(): Filter<REQ, RESP>[];
33
+ /**
34
+ * Get the number of filters in the chain.
35
+ */
36
+ size(): number;
37
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FilterChain = void 0;
4
+ /**
5
+ * FilterChain - Manages execution of filters in priority order.
6
+ * Similar to Java servlet filter chains.
7
+ *
8
+ * Filters arrive ALREADY SORTED by priority (highest first) and each filter calls
9
+ * nextFilter.invoke() to invoke the next filter in the chain. Sorting is the caller's job because
10
+ * priority lives on the DEFINITION (server: `FilterDefinition`, client: `ClientFilterDefinition`),
11
+ * never on the filter itself — see {@link Filter}.
12
+ *
13
+ * The final "filter" in the chain is the `finalHandler` passed to {@link execute}: the controller
14
+ * method on the server, the `fetch` on the client.
15
+ *
16
+ * A filter may invoke the rest of the chain MORE THAN ONCE (the client-side SSRF guard re-invokes it
17
+ * to follow a validated redirect) or NOT AT ALL (an auth filter short-circuiting). Nothing here
18
+ * assumes exactly one pass.
19
+ */
20
+ class FilterChain {
21
+ filters;
22
+ constructor(filters) {
23
+ // Filters are already sorted by priority from FilterMatcher
24
+ // No need to sort again (priority is in FilterDefinition, not Filter)
25
+ this.filters = filters;
26
+ }
27
+ /**
28
+ * Execute the filter chain.
29
+ *
30
+ * @param meta - Request metadata
31
+ * @param finalHandler - The controller method to execute at the end
32
+ * @returns Promise of the response
33
+ */
34
+ async execute(meta, finalHandler) {
35
+ const filters = this.filters;
36
+ // Create Service adapter that recursively calls filters
37
+ const createServiceForIndex = (currentIndex) => {
38
+ return {
39
+ invoke: async (m) => {
40
+ if (currentIndex < filters.length) {
41
+ const filter = filters[currentIndex];
42
+ const nextService = createServiceForIndex(currentIndex + 1);
43
+ return filter.filter(m, nextService);
44
+ }
45
+ else {
46
+ // All filters executed, now execute the controller
47
+ return finalHandler();
48
+ }
49
+ },
50
+ };
51
+ };
52
+ // Start execution with first filter
53
+ const service = createServiceForIndex(0);
54
+ return service.invoke(meta);
55
+ }
56
+ /**
57
+ * Get all filters in the chain (sorted by priority).
58
+ */
59
+ getFilters() {
60
+ return [...this.filters];
61
+ }
62
+ /**
63
+ * Get the number of filters in the chain.
64
+ */
65
+ size() {
66
+ return this.filters.length;
67
+ }
68
+ }
69
+ exports.FilterChain = FilterChain;
70
+ //# sourceMappingURL=FilterChain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FilterChain.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/filters/FilterChain.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAa,WAAW;IACZ,OAAO,CAAsB;IAErC,YAAY,OAA4B;QACpC,4DAA4D;QAC5D,sEAAsE;QACtE,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,CAAC,IAAS,EAAE,YAAiC;QACtD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAE7B,wDAAwD;QACxD,MAAM,qBAAqB,GAAG,CAAC,YAAoB,EAAsB,EAAE;YACvE,OAAO;gBACH,MAAM,EAAE,KAAK,EAAE,CAAM,EAAiB,EAAE;oBACpC,IAAI,YAAY,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;wBAChC,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;wBACrC,MAAM,WAAW,GAAG,qBAAqB,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;wBAC5D,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;oBACzC,CAAC;yBAAM,CAAC;wBACJ,mDAAmD;wBACnD,OAAO,YAAY,EAAE,CAAC;oBAC1B,CAAC;gBACL,CAAC;aACJ,CAAC;QACN,CAAC,CAAC;QAEF,oCAAoC;QACpC,MAAM,OAAO,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACzC,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,UAAU;QACN,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,IAAI;QACA,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IAC/B,CAAC;CACJ;AArDD,kCAqDC","sourcesContent":["import { Filter, Service } from './Filter';\n\n/**\n * FilterChain - Manages execution of filters in priority order.\n * Similar to Java servlet filter chains.\n *\n * Filters arrive ALREADY SORTED by priority (highest first) and each filter calls\n * nextFilter.invoke() to invoke the next filter in the chain. Sorting is the caller's job because\n * priority lives on the DEFINITION (server: `FilterDefinition`, client: `ClientFilterDefinition`),\n * never on the filter itself — see {@link Filter}.\n *\n * The final \"filter\" in the chain is the `finalHandler` passed to {@link execute}: the controller\n * method on the server, the `fetch` on the client.\n *\n * A filter may invoke the rest of the chain MORE THAN ONCE (the client-side SSRF guard re-invokes it\n * to follow a validated redirect) or NOT AT ALL (an auth filter short-circuiting). Nothing here\n * assumes exactly one pass.\n */\nexport class FilterChain<REQ, RESP> {\n private filters: Filter<REQ, RESP>[];\n\n constructor(filters: Filter<REQ, RESP>[]) {\n // Filters are already sorted by priority from FilterMatcher\n // No need to sort again (priority is in FilterDefinition, not Filter)\n this.filters = filters;\n }\n\n /**\n * Execute the filter chain.\n *\n * @param meta - Request metadata\n * @param finalHandler - The controller method to execute at the end\n * @returns Promise of the response\n */\n async execute(meta: REQ, finalHandler: () => Promise<RESP>): Promise<RESP> {\n const filters = this.filters;\n\n // Create Service adapter that recursively calls filters\n const createServiceForIndex = (currentIndex: number): Service<REQ, RESP> => {\n return {\n invoke: async (m: REQ): Promise<RESP> => {\n if (currentIndex < filters.length) {\n const filter = filters[currentIndex];\n const nextService = createServiceForIndex(currentIndex + 1);\n return filter.filter(m, nextService);\n } else {\n // All filters executed, now execute the controller\n return finalHandler();\n }\n },\n };\n };\n\n // Start execution with first filter\n const service = createServiceForIndex(0);\n return service.invoke(meta);\n }\n\n /**\n * Get all filters in the chain (sorted by priority).\n */\n getFilters(): Filter<REQ, RESP>[] {\n return [...this.filters];\n }\n\n /**\n * Get the number of filters in the chain.\n */\n size(): number {\n return this.filters.length;\n }\n}\n"]}
@@ -148,6 +148,38 @@ export declare class WebpiecesCoreHeaders {
148
148
  */
149
149
  static readonly CONTROLLER: ContextKey<string, "untrusted">;
150
150
  static readonly METHOD: ContextKey<string, "untrusted">;
151
+ /**
152
+ * The base URL ONE outbound call should go to, overriding whatever the client's `ClientConfig`
153
+ * bound at construction — the answer to "POST our published contract to a URL the PARTNER
154
+ * registered at runtime" (an `OrganizationWebhook.url` column, an OAuth callback, a per-tenant
155
+ * or self-hosted host). The destination is DATA, not deployment, so it cannot be a svcName and
156
+ * there is nothing to register in {@link ClientRegistry}.
157
+ *
158
+ * ```ts
159
+ * RequestContext.run(() => {
160
+ * RequestContext.putUntrusted(WebpiecesCoreHeaders.OVERRIDE_BASE_URL, webhook.url);
161
+ * return partnerWebhookClient.deliver(envelope);
162
+ * });
163
+ * ```
164
+ *
165
+ * ONLY a client whose `ClientConfig` names a runtime host policy reads it — `new
166
+ * ClientConfig('partner-webhooks', new RuntimeHostFromContext(new DnsAddressResolver()))`. A client bound to a deployed
167
+ * service (`new DeployedServiceHost()`) IGNORES this key entirely, which is what stops an
168
+ * ambient value re-pointing every other client in the same fan-out loop at a partner's server.
169
+ * Opting in is a named class at the construction site, so `grep -rn RuntimeHostFromContext`
170
+ * enumerates every client that can be re-pointed at all.
171
+ *
172
+ * - `httpHeader` UNDEFINED → NOT transferred over the wire, and that is load-bearing. This value
173
+ * names where THIS hop goes. If it travelled, the callee would inherit it and re-point ITS
174
+ * own outbound calls at the same host — one partner-supplied URL turning into an SSRF pivot
175
+ * across the whole call tree. It is per-hop, always.
176
+ * - `isLogged` TRUE → the destination of a partner delivery is exactly what you want in the log
177
+ * line when one fails.
178
+ *
179
+ * UNTRUSTED, necessarily: it comes from a database column a partner edited. That is precisely
180
+ * why {@link RuntimeHostFromContext} ships an SSRF policy on by default rather than trusting it.
181
+ */
182
+ static readonly OVERRIDE_BASE_URL: ContextKey<string, "untrusted">;
151
183
  /**
152
184
  * NO CREDENTIAL KEYS LIVE HERE.
153
185
  *
@@ -151,6 +151,41 @@ class WebpiecesCoreHeaders {
151
151
  */
152
152
  static CONTROLLER = ContextKey_1.ContextKey.untrusted('controller', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);
153
153
  static METHOD = ContextKey_1.ContextKey.untrusted('method', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);
154
+ /**
155
+ * The base URL ONE outbound call should go to, overriding whatever the client's `ClientConfig`
156
+ * bound at construction — the answer to "POST our published contract to a URL the PARTNER
157
+ * registered at runtime" (an `OrganizationWebhook.url` column, an OAuth callback, a per-tenant
158
+ * or self-hosted host). The destination is DATA, not deployment, so it cannot be a svcName and
159
+ * there is nothing to register in {@link ClientRegistry}.
160
+ *
161
+ * ```ts
162
+ * RequestContext.run(() => {
163
+ * RequestContext.putUntrusted(WebpiecesCoreHeaders.OVERRIDE_BASE_URL, webhook.url);
164
+ * return partnerWebhookClient.deliver(envelope);
165
+ * });
166
+ * ```
167
+ *
168
+ * ONLY a client whose `ClientConfig` names a runtime host policy reads it — `new
169
+ * ClientConfig('partner-webhooks', new RuntimeHostFromContext(new DnsAddressResolver()))`. A client bound to a deployed
170
+ * service (`new DeployedServiceHost()`) IGNORES this key entirely, which is what stops an
171
+ * ambient value re-pointing every other client in the same fan-out loop at a partner's server.
172
+ * Opting in is a named class at the construction site, so `grep -rn RuntimeHostFromContext`
173
+ * enumerates every client that can be re-pointed at all.
174
+ *
175
+ * - `httpHeader` UNDEFINED → NOT transferred over the wire, and that is load-bearing. This value
176
+ * names where THIS hop goes. If it travelled, the callee would inherit it and re-point ITS
177
+ * own outbound calls at the same host — one partner-supplied URL turning into an SSRF pivot
178
+ * across the whole call tree. It is per-hop, always.
179
+ * - `isLogged` TRUE → the destination of a partner delivery is exactly what you want in the log
180
+ * line when one fails.
181
+ *
182
+ * UNTRUSTED, necessarily: it comes from a database column a partner edited. That is precisely
183
+ * why {@link RuntimeHostFromContext} ships an SSRF policy on by default rather than trusting it.
184
+ */
185
+ static OVERRIDE_BASE_URL = ContextKey_1.ContextKey.untrusted('overrideBaseUrl',
186
+ /*httpHeader*/ undefined,
187
+ /*maskInLogs*/ false,
188
+ /*isLogged*/ true);
154
189
  /**
155
190
  * NO CREDENTIAL KEYS LIVE HERE.
156
191
  *
@@ -187,6 +222,7 @@ class WebpiecesCoreHeaders {
187
222
  WebpiecesCoreHeaders.REQUEST_PATH,
188
223
  WebpiecesCoreHeaders.CONTROLLER,
189
224
  WebpiecesCoreHeaders.METHOD,
225
+ WebpiecesCoreHeaders.OVERRIDE_BASE_URL,
190
226
  ];
191
227
  }
192
228
  exports.WebpiecesCoreHeaders = WebpiecesCoreHeaders;
@@ -1 +1 @@
1
- {"version":3,"file":"WebpiecesCoreHeaders.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/WebpiecesCoreHeaders.ts"],"names":[],"mappings":";;;AAAA,8CAA0D;AAG1D;;;;;;;;;;;;;;;GAeG;AACH,MAAa,oBAAoB;IAC7B;;;OAGG;IACH,MAAM,CAAU,UAAU,GAAG,uBAAU,CAAC,SAAS,CAAS,WAAW,EAAE,cAAc,CAAC,CAAC;IAEvF;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAU,iBAAiB,GAAG,uBAAU,CAAC,SAAS,CACpD,iBAAiB;IACjB,cAAc,CAAC,SAAS,CAC3B,CAAC;IAEF;;;;;;;;;;;;OAYG;IACH,MAAM,CAAU,cAAc,GAAG,uBAAU,CAAC,SAAS,CAAS,eAAe,EAAE,4BAA4B,CAAC,CAAC;IAE7G;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,MAAM,CAAU,SAAS,GAAG,uBAAU,CAAC,SAAS,CAAS,UAAU,EAAE,sBAAsB,CAAC,CAAC;IAE7F;;;;;;;;;;;;;;;;;;OAkBG;IACH,MAAM,CAAU,MAAM,GAAG,uBAAU,CAAC,OAAO,CACvC,OAAO,EACP,oGAAoG,EACpG,UAAU,CACb,CAAC;IAEF,MAAM,CAAU,OAAO,GAAG,uBAAU,CAAC,OAAO,CACxC,QAAQ,EACR,oGAAoG,EACpG,WAAW,CACd,CAAC;IAEF,MAAM,CAAU,UAAU,GAAG,uBAAU,CAAC,OAAO,CAC3C,OAAO,EACP,oGAAoG,EACpG,mBAAmB,CACtB,CAAC;IAEF;;;OAGG;IACH,MAAM,CAAU,SAAS,GAAG,uBAAU,CAAC,SAAS,CAAS,WAAW,EAAE,uBAAuB,CAAC,CAAC;IAE/F;;;;;;;;;;;OAWG;IACH,MAAM,CAAU,aAAa,GAAG,uBAAU,CAAC,SAAS,CAAc,KAAK,EAAE,cAAc,CAAC,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAE5I;;;;;;;;;;OAUG;IACH,MAAM,CAAU,WAAW,GAAG,uBAAU,CAAC,SAAS,CAAS,YAAY,EAAE,cAAc,CAAC,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAE5I,MAAM,CAAU,YAAY,GAAG,uBAAU,CAAC,SAAS,CAAS,aAAa,EAAE,cAAc,CAAC,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAE9I;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAU,UAAU,GAAG,uBAAU,CAAC,SAAS,CAAS,YAAY,EAAE,cAAc,CAAC,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAE3I,MAAM,CAAU,MAAM,GAAG,uBAAU,CAAC,SAAS,CAAS,QAAQ,EAAE,cAAc,CAAC,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAEnI;;;;;;;;;;;;;;;OAeG;IAEH;;;;;OAKG;IACH,MAAM,CAAU,WAAW,GAAoB;QAC3C,oBAAoB,CAAC,UAAU;QAC/B,oBAAoB,CAAC,iBAAiB;QACtC,oBAAoB,CAAC,cAAc;QACnC,oBAAoB,CAAC,SAAS;QAC9B,oBAAoB,CAAC,OAAO;QAC5B,oBAAoB,CAAC,MAAM;QAC3B,oBAAoB,CAAC,UAAU;QAC/B,oBAAoB,CAAC,SAAS;QAC9B,oBAAoB,CAAC,aAAa;QAClC,oBAAoB,CAAC,WAAW;QAChC,oBAAoB,CAAC,YAAY;QACjC,oBAAoB,CAAC,UAAU;QAC/B,oBAAoB,CAAC,MAAM;KAC9B,CAAC;;AArMN,oDAsMC","sourcesContent":["import { ContextKey, AnyContextKey } from '../ContextKey';\nimport { ApiCallInfo } from './ApiCallInfo';\n\n/**\n * Core framework context keys — the minimum the WebPieces framework needs to correlate one\n * request across every service it touches, and across every log line each of them writes.\n *\n * ONE id, propagated unchanged. The first service to see a request without an `x-request-id`\n * generates one (RequestContextHeaders.fillFromRequest); every hop copies it onward verbatim. Grep that id and you\n * have the whole call tree. There is no per-hop id and no parent pointer: a chain of ids you must\n * stitch back together buys nothing a single shared id does not already give you.\n *\n * Lives in core-util (browser-safe) so both the http clients and http-server can reference it.\n *\n * Exposed as {@link HeaderRegistry.DEFAULT_HEADERS} — a service opts into these by\n * passing `platformHeaders=true` to `HeaderRegistry.configure(...)`.\n *\n * Each key's `name` is the logical/log name; `httpHeader` is the wire name.\n */\nexport class WebpiecesCoreHeaders {\n /**\n * The id that correlates every hop of one request, and every log line of every hop.\n * Generated by the first service to see a request without one; propagated unchanged after that.\n */\n static readonly REQUEST_ID = ContextKey.untrusted<string>('requestId', 'x-request-id');\n\n /**\n * WHICH SERVICE MINTED {@link REQUEST_ID} — the name from {@link ServiceInfo}, stamped by\n * `RequestContextHeaders.fillFromRequest` ONLY on the branch that generates a new id (i.e. when\n * the inbound request carried no `x-request-id`). It answers the question the id alone cannot:\n * \"this trace starts here — is that right?\" An id appearing with no source means it came from\n * outside; an id sourced by a service that should never be an entry point is a routing bug.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire, and that is the WHOLE POINT. If it\n * travelled, hop 2 would inherit it, hop 3 would inherit it, and \"who started this trace\"\n * would be indistinguishable from \"who passed it along\" — the origin, the one fact this key\n * carries, would be lost. It is absent on every hop that did NOT mint the id, which is exactly\n * the signal: present == I am the origin.\n * - `isLogged` TRUE → emitted as a plain string at `jsonPayload.requestIdSource`.\n */\n static readonly REQUEST_ID_SOURCE = ContextKey.untrusted<string>(\n 'requestIdSource',\n /*httpHeader*/ undefined\n );\n\n /**\n * The CALLER's build version — so a downstream server's logs record which build of the client\n * called it (surfaces as `jsonPayload.clientVersion`). Distinct from the log line's own `version`\n * (this service's build): `version` answers \"which build wrote this line?\", `clientVersion`\n * answers \"which build asked us to?\".\n *\n * - `httpHeader` SET → transferred over the wire, BUT unlike a normal transferred key it is NOT\n * copied from the context onward. Each hop OVERWRITES it with its OWN `ServiceInfo.getVersion()`\n * as it becomes the client to the next hop (see `buildOutboundHeaders`), so on any given server\n * `clientVersion` is always the IMMEDIATE caller's version, never a stale grand-caller's.\n * - `isLogged` TRUE → the inbound value lands in the context and flows through the normal log\n * field map; no backend change needed.\n */\n static readonly CLIENT_VERSION = ContextKey.untrusted<string>('clientVersion', 'x-webpieces-client-version');\n\n /**\n * A frontend/app-minted correlation id that groups every request triggered by ONE user ACTION.\n *\n * An \"action\" is a single thing the user did in the GUI — a CLICK on a button/link, or TYPING in a\n * field — or a background poller tick: anything that may fan out into MULTIPLE remote calls. That one\n * action fires 1..N browser HTTP calls, each of which gets its own framework-minted {@link REQUEST_ID}\n * (one per HTTP call, shared within that call's server→server subtree). `actionId` sits ABOVE\n * `requestId` and is what stitches those N requests back to the single action that caused them:\n *\n * actionId (app-minted, ONE per user action, rides EVERY call of that action)\n * └── 1..N requestId (framework-minted, ONE per HTTP call)\n *\n * Grep one `actionId` in the logs → every `requestId` it spawned, and every log line of the whole\n * action. Minted and refreshed by the app (a UI concern), carried under `x-webpieces-actionid`.\n *\n * Browser/app-minted ONLY: unlike {@link REQUEST_ID}, the framework transfers and logs it but must\n * NOT auto-mint one server-side. Absent `actionId` ⇒ a non-action flow (system / cron / task), which\n * is the correct signal.\n *\n * - `httpHeader` SET → transferred: copied off the inbound request into context and re-emitted on\n * outbound hops, so the id follows the action across services.\n * - `isLogged` TRUE → emitted as a plain string on every log line of the request.\n */\n static readonly ACTION_ID = ContextKey.untrusted<string>('actionId', 'x-webpieces-actionid');\n\n /**\n * WHO the request is acting as, and WHAT they may do. All three are TRUSTED keys: they are the\n * inputs to authorization decisions, so a reader must be able to tell \"the framework proved\n * this\" from \"the caller typed this\" — see the trust section of the {@link ContextKey} doc.\n *\n * They keep their `httpHeader`, because propagating a verified identity to the next internal\n * service is the point. What makes that safe is not the header being absent, it is WHO is\n * allowed to have set it: an inbound value is held PENDING by\n * `RequestContextHeaders.fillFromRequest` and admitted by `AuthFilter` only on a route that\n * verified its CALLER (`@AuthOidc` / `@AuthSharedSecret`). On a browser-reachable route\n * (`@AuthJwt` / public) the value must match what the authenticator itself derived, or the\n * request is rejected.\n *\n * `provenance` says \"an app-bound JwtHook\" rather than naming one hook, because the framework\n * default ({@link DefaultJwtHook}) stamps NO context entries at all — an app supplies a hook that\n * returns {@link ContextTuple}s for the keys it can vouch for. Any of these three that an app's\n * hook does NOT stamp will be rejected when a caller supplies it, which is the correct and loud\n * outcome: nothing is vouching for it.\n */\n static readonly ORG_ID = ContextKey.trusted<string>(\n 'orgId',\n 'derived from a verified credential by an app-bound JwtHook (a ContextTuple in AuthenticatedCaller)',\n 'x-org-id',\n );\n\n static readonly USER_ID = ContextKey.trusted<string>(\n 'userId',\n 'derived from a verified credential by an app-bound JwtHook (a ContextTuple in AuthenticatedCaller)',\n 'x-user-id',\n );\n\n static readonly USER_ROLES = ContextKey.trusted<string>(\n 'roles',\n 'derived from a verified credential by an app-bound JwtHook (a ContextTuple in AuthenticatedCaller)',\n 'x-webpieces-roles',\n );\n\n /**\n * Turns on test-case recording for this request (Java: x-webpieces-recording).\n * Transferred so recording follows the request across service hops.\n */\n static readonly RECORDING = ContextKey.untrusted<string>('recording', 'x-webpieces-recording');\n\n /**\n * The structured API-call tag ({@link ApiCallInfo}) stamped by {@link LogApiCall} around every\n * outbound (client) / inbound (server) call. It rides the magic context so EVERY log line emitted\n * during the call inherits a filterable `api` object, surfacing in GCP as nested\n * `jsonPayload.api.{side,type,result,path,method}`.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire. Per-hop only: each server/client hop\n * stamps its own tag, so a downstream server records `side:'server'`, never the caller's `side:'client'`.\n * - `isLogged` TRUE → emitted by the logging backends. It carries an OBJECT value, so the backends\n * read it via {@link HeaderRegistry.buildStructuredLogFields} (object-aware); the flat\n * `buildLogFields()` string map deliberately skips it (typeof-string guard).\n */\n static readonly API_CALL_INFO = ContextKey.untrusted<ApiCallInfo>('api', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);\n\n /**\n * The inbound request's HTTP method and path, stamped ONCE from the {@link HttpRequest} by\n * `RequestContextHeaders.fillFromRequest` (the atomic inbound choke point every transport funnels\n * through). They surface as top-level `jsonPayload.httpMethod` / `jsonPayload.requestPath` so every\n * log line of the request carries them — they used to ride inside {@link ApiCallInfo} (`api.path` /\n * `api.method`) but that coupled a per-CALL logger to the per-REQUEST transport shape.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire: a downstream hop stamps its OWN inbound\n * method/path, never the caller's. Outbound client calls never set these (no inbound path).\n * - `isLogged` TRUE → emitted by the logging backends as plain strings.\n */\n static readonly HTTP_METHOD = ContextKey.untrusted<string>('httpMethod', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);\n\n static readonly REQUEST_PATH = ContextKey.untrusted<string>('requestPath', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);\n\n /**\n * The routed endpoint's IMPLEMENTATION identity: the concrete controller class name\n * ({@link RouteMetadata.controllerClassName}, e.g. `LoginController`) and the handler method NAME\n * ({@link RouteMetadata.methodName}, e.g. `login`), stamped once per request by {@link LogApiFilter}\n * after route matching so every subsequent log line of the request carries them.\n *\n * These say WHICH CODE ran, which is what you actually grep for — far more useful than the raw\n * `requestPath`. They are the top-level, filterable twin of what previously only lived nested in\n * {@link ApiCallInfo} (`api.method.controllerName` / `api.method.methodName`). The local console\n * formatters render them together as a compact `[Controller.method]` bracket; GCP keeps them as two\n * separate `jsonPayload.controller` / `jsonPayload.method` fields.\n *\n * NOTE: `method` here is the CODE method name (e.g. `login`), NOT the HTTP verb — that is\n * {@link HTTP_METHOD} (`httpMethod`).\n *\n * - `httpHeader` UNDEFINED → NOT transferred: each hop stamps its OWN routed controller/method.\n * - `isLogged` TRUE → emitted by the logging backends as plain strings.\n */\n static readonly CONTROLLER = ContextKey.untrusted<string>('controller', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);\n\n static readonly METHOD = ContextKey.untrusted<string>('method', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);\n\n /**\n * NO CREDENTIAL KEYS LIVE HERE.\n *\n * `authorization` and `x-webpieces-shared-secret` used to be ContextKeys. That made them\n * TRANSFERRED keys, so the inbound transfer copied them off the request into the\n * RequestContext, and every outbound RPC call and enqueued Cloud Task then carried the\n * caller's credential onward — to services that had no business seeing it.\n *\n * A credential belongs to ONE request hop. It is read straight off the {@link HttpRequest}\n * by the framework AuthFilter, and written straight onto the outbound request by the client\n * that mints it (NodeProxyClient, GcpTaskInvoker, InMemoryTaskInvoker). It never enters the\n * magic context, so nothing can propagate it by accident.\n *\n * An app that genuinely wants a credential to travel can still register its own ContextKey for\n * it — but that is now an explicit, visible decision rather than the default.\n */\n\n /**\n * All core context keys (the platform DEFAULT_HEADERS set). A `static readonly` CONSTANT, not a\n * method: it is compile-time data — a list of the key definitions above — read once at the startup\n * composition root (`HeaderRegistry.configure` / `HeaderRegistry.DEFAULT_HEADERS`). A method here\n * would be un-injectable behavior the DI design graph can't reach; a constant is honest data.\n */\n static readonly ALL_HEADERS: AnyContextKey[] = [\n WebpiecesCoreHeaders.REQUEST_ID,\n WebpiecesCoreHeaders.REQUEST_ID_SOURCE,\n WebpiecesCoreHeaders.CLIENT_VERSION,\n WebpiecesCoreHeaders.ACTION_ID,\n WebpiecesCoreHeaders.USER_ID,\n WebpiecesCoreHeaders.ORG_ID,\n WebpiecesCoreHeaders.USER_ROLES,\n WebpiecesCoreHeaders.RECORDING,\n WebpiecesCoreHeaders.API_CALL_INFO,\n WebpiecesCoreHeaders.HTTP_METHOD,\n WebpiecesCoreHeaders.REQUEST_PATH,\n WebpiecesCoreHeaders.CONTROLLER,\n WebpiecesCoreHeaders.METHOD,\n ];\n}\n"]}
1
+ {"version":3,"file":"WebpiecesCoreHeaders.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/WebpiecesCoreHeaders.ts"],"names":[],"mappings":"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","sourcesContent":["import { ContextKey, AnyContextKey } from '../ContextKey';\nimport { ApiCallInfo } from './ApiCallInfo';\n\n/**\n * Core framework context keys — the minimum the WebPieces framework needs to correlate one\n * request across every service it touches, and across every log line each of them writes.\n *\n * ONE id, propagated unchanged. The first service to see a request without an `x-request-id`\n * generates one (RequestContextHeaders.fillFromRequest); every hop copies it onward verbatim. Grep that id and you\n * have the whole call tree. There is no per-hop id and no parent pointer: a chain of ids you must\n * stitch back together buys nothing a single shared id does not already give you.\n *\n * Lives in core-util (browser-safe) so both the http clients and http-server can reference it.\n *\n * Exposed as {@link HeaderRegistry.DEFAULT_HEADERS} — a service opts into these by\n * passing `platformHeaders=true` to `HeaderRegistry.configure(...)`.\n *\n * Each key's `name` is the logical/log name; `httpHeader` is the wire name.\n */\nexport class WebpiecesCoreHeaders {\n /**\n * The id that correlates every hop of one request, and every log line of every hop.\n * Generated by the first service to see a request without one; propagated unchanged after that.\n */\n static readonly REQUEST_ID = ContextKey.untrusted<string>('requestId', 'x-request-id');\n\n /**\n * WHICH SERVICE MINTED {@link REQUEST_ID} — the name from {@link ServiceInfo}, stamped by\n * `RequestContextHeaders.fillFromRequest` ONLY on the branch that generates a new id (i.e. when\n * the inbound request carried no `x-request-id`). It answers the question the id alone cannot:\n * \"this trace starts here — is that right?\" An id appearing with no source means it came from\n * outside; an id sourced by a service that should never be an entry point is a routing bug.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire, and that is the WHOLE POINT. If it\n * travelled, hop 2 would inherit it, hop 3 would inherit it, and \"who started this trace\"\n * would be indistinguishable from \"who passed it along\" — the origin, the one fact this key\n * carries, would be lost. It is absent on every hop that did NOT mint the id, which is exactly\n * the signal: present == I am the origin.\n * - `isLogged` TRUE → emitted as a plain string at `jsonPayload.requestIdSource`.\n */\n static readonly REQUEST_ID_SOURCE = ContextKey.untrusted<string>(\n 'requestIdSource',\n /*httpHeader*/ undefined\n );\n\n /**\n * The CALLER's build version — so a downstream server's logs record which build of the client\n * called it (surfaces as `jsonPayload.clientVersion`). Distinct from the log line's own `version`\n * (this service's build): `version` answers \"which build wrote this line?\", `clientVersion`\n * answers \"which build asked us to?\".\n *\n * - `httpHeader` SET → transferred over the wire, BUT unlike a normal transferred key it is NOT\n * copied from the context onward. Each hop OVERWRITES it with its OWN `ServiceInfo.getVersion()`\n * as it becomes the client to the next hop (see `buildOutboundHeaders`), so on any given server\n * `clientVersion` is always the IMMEDIATE caller's version, never a stale grand-caller's.\n * - `isLogged` TRUE → the inbound value lands in the context and flows through the normal log\n * field map; no backend change needed.\n */\n static readonly CLIENT_VERSION = ContextKey.untrusted<string>('clientVersion', 'x-webpieces-client-version');\n\n /**\n * A frontend/app-minted correlation id that groups every request triggered by ONE user ACTION.\n *\n * An \"action\" is a single thing the user did in the GUI — a CLICK on a button/link, or TYPING in a\n * field — or a background poller tick: anything that may fan out into MULTIPLE remote calls. That one\n * action fires 1..N browser HTTP calls, each of which gets its own framework-minted {@link REQUEST_ID}\n * (one per HTTP call, shared within that call's server→server subtree). `actionId` sits ABOVE\n * `requestId` and is what stitches those N requests back to the single action that caused them:\n *\n * actionId (app-minted, ONE per user action, rides EVERY call of that action)\n * └── 1..N requestId (framework-minted, ONE per HTTP call)\n *\n * Grep one `actionId` in the logs → every `requestId` it spawned, and every log line of the whole\n * action. Minted and refreshed by the app (a UI concern), carried under `x-webpieces-actionid`.\n *\n * Browser/app-minted ONLY: unlike {@link REQUEST_ID}, the framework transfers and logs it but must\n * NOT auto-mint one server-side. Absent `actionId` ⇒ a non-action flow (system / cron / task), which\n * is the correct signal.\n *\n * - `httpHeader` SET → transferred: copied off the inbound request into context and re-emitted on\n * outbound hops, so the id follows the action across services.\n * - `isLogged` TRUE → emitted as a plain string on every log line of the request.\n */\n static readonly ACTION_ID = ContextKey.untrusted<string>('actionId', 'x-webpieces-actionid');\n\n /**\n * WHO the request is acting as, and WHAT they may do. All three are TRUSTED keys: they are the\n * inputs to authorization decisions, so a reader must be able to tell \"the framework proved\n * this\" from \"the caller typed this\" — see the trust section of the {@link ContextKey} doc.\n *\n * They keep their `httpHeader`, because propagating a verified identity to the next internal\n * service is the point. What makes that safe is not the header being absent, it is WHO is\n * allowed to have set it: an inbound value is held PENDING by\n * `RequestContextHeaders.fillFromRequest` and admitted by `AuthFilter` only on a route that\n * verified its CALLER (`@AuthOidc` / `@AuthSharedSecret`). On a browser-reachable route\n * (`@AuthJwt` / public) the value must match what the authenticator itself derived, or the\n * request is rejected.\n *\n * `provenance` says \"an app-bound JwtHook\" rather than naming one hook, because the framework\n * default ({@link DefaultJwtHook}) stamps NO context entries at all — an app supplies a hook that\n * returns {@link ContextTuple}s for the keys it can vouch for. Any of these three that an app's\n * hook does NOT stamp will be rejected when a caller supplies it, which is the correct and loud\n * outcome: nothing is vouching for it.\n */\n static readonly ORG_ID = ContextKey.trusted<string>(\n 'orgId',\n 'derived from a verified credential by an app-bound JwtHook (a ContextTuple in AuthenticatedCaller)',\n 'x-org-id',\n );\n\n static readonly USER_ID = ContextKey.trusted<string>(\n 'userId',\n 'derived from a verified credential by an app-bound JwtHook (a ContextTuple in AuthenticatedCaller)',\n 'x-user-id',\n );\n\n static readonly USER_ROLES = ContextKey.trusted<string>(\n 'roles',\n 'derived from a verified credential by an app-bound JwtHook (a ContextTuple in AuthenticatedCaller)',\n 'x-webpieces-roles',\n );\n\n /**\n * Turns on test-case recording for this request (Java: x-webpieces-recording).\n * Transferred so recording follows the request across service hops.\n */\n static readonly RECORDING = ContextKey.untrusted<string>('recording', 'x-webpieces-recording');\n\n /**\n * The structured API-call tag ({@link ApiCallInfo}) stamped by {@link LogApiCall} around every\n * outbound (client) / inbound (server) call. It rides the magic context so EVERY log line emitted\n * during the call inherits a filterable `api` object, surfacing in GCP as nested\n * `jsonPayload.api.{side,type,result,path,method}`.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire. Per-hop only: each server/client hop\n * stamps its own tag, so a downstream server records `side:'server'`, never the caller's `side:'client'`.\n * - `isLogged` TRUE → emitted by the logging backends. It carries an OBJECT value, so the backends\n * read it via {@link HeaderRegistry.buildStructuredLogFields} (object-aware); the flat\n * `buildLogFields()` string map deliberately skips it (typeof-string guard).\n */\n static readonly API_CALL_INFO = ContextKey.untrusted<ApiCallInfo>('api', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);\n\n /**\n * The inbound request's HTTP method and path, stamped ONCE from the {@link HttpRequest} by\n * `RequestContextHeaders.fillFromRequest` (the atomic inbound choke point every transport funnels\n * through). They surface as top-level `jsonPayload.httpMethod` / `jsonPayload.requestPath` so every\n * log line of the request carries them — they used to ride inside {@link ApiCallInfo} (`api.path` /\n * `api.method`) but that coupled a per-CALL logger to the per-REQUEST transport shape.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire: a downstream hop stamps its OWN inbound\n * method/path, never the caller's. Outbound client calls never set these (no inbound path).\n * - `isLogged` TRUE → emitted by the logging backends as plain strings.\n */\n static readonly HTTP_METHOD = ContextKey.untrusted<string>('httpMethod', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);\n\n static readonly REQUEST_PATH = ContextKey.untrusted<string>('requestPath', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);\n\n /**\n * The routed endpoint's IMPLEMENTATION identity: the concrete controller class name\n * ({@link RouteMetadata.controllerClassName}, e.g. `LoginController`) and the handler method NAME\n * ({@link RouteMetadata.methodName}, e.g. `login`), stamped once per request by {@link LogApiFilter}\n * after route matching so every subsequent log line of the request carries them.\n *\n * These say WHICH CODE ran, which is what you actually grep for — far more useful than the raw\n * `requestPath`. They are the top-level, filterable twin of what previously only lived nested in\n * {@link ApiCallInfo} (`api.method.controllerName` / `api.method.methodName`). The local console\n * formatters render them together as a compact `[Controller.method]` bracket; GCP keeps them as two\n * separate `jsonPayload.controller` / `jsonPayload.method` fields.\n *\n * NOTE: `method` here is the CODE method name (e.g. `login`), NOT the HTTP verb — that is\n * {@link HTTP_METHOD} (`httpMethod`).\n *\n * - `httpHeader` UNDEFINED → NOT transferred: each hop stamps its OWN routed controller/method.\n * - `isLogged` TRUE → emitted by the logging backends as plain strings.\n */\n static readonly CONTROLLER = ContextKey.untrusted<string>('controller', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);\n\n static readonly METHOD = ContextKey.untrusted<string>('method', /*httpHeader*/ undefined, /*maskInLogs*/ false, /*isLogged*/ true);\n\n /**\n * The base URL ONE outbound call should go to, overriding whatever the client's `ClientConfig`\n * bound at construction — the answer to \"POST our published contract to a URL the PARTNER\n * registered at runtime\" (an `OrganizationWebhook.url` column, an OAuth callback, a per-tenant\n * or self-hosted host). The destination is DATA, not deployment, so it cannot be a svcName and\n * there is nothing to register in {@link ClientRegistry}.\n *\n * ```ts\n * RequestContext.run(() => {\n * RequestContext.putUntrusted(WebpiecesCoreHeaders.OVERRIDE_BASE_URL, webhook.url);\n * return partnerWebhookClient.deliver(envelope);\n * });\n * ```\n *\n * ONLY a client whose `ClientConfig` names a runtime host policy reads it — `new\n * ClientConfig('partner-webhooks', new RuntimeHostFromContext(new DnsAddressResolver()))`. A client bound to a deployed\n * service (`new DeployedServiceHost()`) IGNORES this key entirely, which is what stops an\n * ambient value re-pointing every other client in the same fan-out loop at a partner's server.\n * Opting in is a named class at the construction site, so `grep -rn RuntimeHostFromContext`\n * enumerates every client that can be re-pointed at all.\n *\n * - `httpHeader` UNDEFINED → NOT transferred over the wire, and that is load-bearing. This value\n * names where THIS hop goes. If it travelled, the callee would inherit it and re-point ITS\n * own outbound calls at the same host — one partner-supplied URL turning into an SSRF pivot\n * across the whole call tree. It is per-hop, always.\n * - `isLogged` TRUE → the destination of a partner delivery is exactly what you want in the log\n * line when one fails.\n *\n * UNTRUSTED, necessarily: it comes from a database column a partner edited. That is precisely\n * why {@link RuntimeHostFromContext} ships an SSRF policy on by default rather than trusting it.\n */\n static readonly OVERRIDE_BASE_URL = ContextKey.untrusted<string>(\n 'overrideBaseUrl',\n /*httpHeader*/ undefined,\n /*maskInLogs*/ false,\n /*isLogged*/ true,\n );\n\n /**\n * NO CREDENTIAL KEYS LIVE HERE.\n *\n * `authorization` and `x-webpieces-shared-secret` used to be ContextKeys. That made them\n * TRANSFERRED keys, so the inbound transfer copied them off the request into the\n * RequestContext, and every outbound RPC call and enqueued Cloud Task then carried the\n * caller's credential onward — to services that had no business seeing it.\n *\n * A credential belongs to ONE request hop. It is read straight off the {@link HttpRequest}\n * by the framework AuthFilter, and written straight onto the outbound request by the client\n * that mints it (NodeProxyClient, GcpTaskInvoker, InMemoryTaskInvoker). It never enters the\n * magic context, so nothing can propagate it by accident.\n *\n * An app that genuinely wants a credential to travel can still register its own ContextKey for\n * it — but that is now an explicit, visible decision rather than the default.\n */\n\n /**\n * All core context keys (the platform DEFAULT_HEADERS set). A `static readonly` CONSTANT, not a\n * method: it is compile-time data — a list of the key definitions above — read once at the startup\n * composition root (`HeaderRegistry.configure` / `HeaderRegistry.DEFAULT_HEADERS`). A method here\n * would be un-injectable behavior the DI design graph can't reach; a constant is honest data.\n */\n static readonly ALL_HEADERS: AnyContextKey[] = [\n WebpiecesCoreHeaders.REQUEST_ID,\n WebpiecesCoreHeaders.REQUEST_ID_SOURCE,\n WebpiecesCoreHeaders.CLIENT_VERSION,\n WebpiecesCoreHeaders.ACTION_ID,\n WebpiecesCoreHeaders.USER_ID,\n WebpiecesCoreHeaders.ORG_ID,\n WebpiecesCoreHeaders.USER_ROLES,\n WebpiecesCoreHeaders.RECORDING,\n WebpiecesCoreHeaders.API_CALL_INFO,\n WebpiecesCoreHeaders.HTTP_METHOD,\n WebpiecesCoreHeaders.REQUEST_PATH,\n WebpiecesCoreHeaders.CONTROLLER,\n WebpiecesCoreHeaders.METHOD,\n WebpiecesCoreHeaders.OVERRIDE_BASE_URL,\n ];\n}\n"]}
package/src/index.d.ts CHANGED
@@ -61,3 +61,6 @@ export { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder
61
61
  export { RecordedEndpoint, RecordedError, RecordedTestCase } from './http/recorder/RecordedEndpoint';
62
62
  export { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';
63
63
  export { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';
64
+ export { Filter } from './filters/Filter';
65
+ export type { Service } from './filters/Filter';
66
+ export { FilterChain } from './filters/FilterChain';
package/src/index.js CHANGED
@@ -10,7 +10,7 @@
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.assertApiKind = exports.getApiKind = exports.ENDPOINT_KINDS_BY_API_KIND = exports.Queue = exports.PubSub = exports.Rpc = exports.AuthMeta = exports.RouteMetadata = exports.METADATA_KEYS = exports.validateNoConflictingDecorators = exports.assertEveryWebhookEndpointRetainsRawBody = exports.assertEveryExternalEndpointDeclaresCaller = exports.assertEveryEndpointHasAuthMode = exports.getAuthMode = exports.getAuthMeta = exports.isApiPath = exports.isRawBody = exports.isFormPost = exports.getMaskSpec = exports.getEndpointKinds = exports.getEndpointKind = exports.getEndpointOptions = exports.getEndpoints = exports.getApiPath = exports.MaskLog = exports.AuthLocalOnly = exports.AuthApiKey = exports.AuthWebhook = 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
12
  exports.ContextMgr = exports.DestinationTrust = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.WEBPIECES_DEFAULT_FAILURE_CLASSIFIER = exports.WebpiecesDefaultFailureClassifier = exports.KeyedFailureClassifier = exports.ErrorWireForm = exports.RuntimeLocality = 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.HttpServiceUnavailableError = 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 = exports.ExternalCaller = exports.DEFAULT_CALLER_KIND = exports.EXTERNAL_SYSTEM_KINDS = exports.getQueueName = exports.assertPubSubConventions = 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 = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = exports.LogApiCall = void 0;
13
+ exports.FilterChain = exports.Filter = exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiCallContextHolder = exports.ApiMethodInfo = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = exports.LogApiCall = 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");
@@ -206,4 +206,13 @@ var RecordSerializer_1 = require("./http/recorder/RecordSerializer");
206
206
  Object.defineProperty(exports, "RecordSerializer", { enumerable: true, get: function () { return RecordSerializer_1.RecordSerializer; } });
207
207
  Object.defineProperty(exports, "SerializedMap", { enumerable: true, get: function () { return RecordSerializer_1.SerializedMap; } });
208
208
  Object.defineProperty(exports, "SerializedError", { enumerable: true, get: function () { return RecordSerializer_1.SerializedError; } });
209
+ // ---------------------------------------------------------------------------------------------
210
+ // Filter-chain primitives, shared by BOTH chains: the inbound server chain
211
+ // (`Filter<MethodMeta, WpResponse<unknown>>`, @webpieces/http-routing) and the outbound client
212
+ // chain (`Filter<ClientRequest, Response>`, @webpieces/http-client-core). Declared once, here, in
213
+ // the package both depend on — see the class doc for why a second pair would be a shim.
214
+ var Filter_1 = require("./filters/Filter");
215
+ Object.defineProperty(exports, "Filter", { enumerable: true, get: function () { return Filter_1.Filter; } });
216
+ var FilterChain_1 = require("./filters/FilterChain");
217
+ Object.defineProperty(exports, "FilterChain", { enumerable: true, get: function () { return FilterChain_1.FilterChain; } });
209
218
  //# sourceMappingURL=index.js.map
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 AuthWebhook,\n AuthApiKey,\n AuthLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointOptions,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport type { EndpointKind, EndpointOptions, ExternalEndpointOptions } from './http/decorators';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport { AuthMeta } from './http/auth-mode';\nexport type { AuthMode, ApiKeyCredential, ApiKeyCredentials, JwtRoles, JwtRequirement } 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 @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 HttpServiceUnavailableError,\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// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @AuthLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\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"]}
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 AuthWebhook,\n AuthApiKey,\n AuthLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointOptions,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport type { EndpointKind, EndpointOptions, ExternalEndpointOptions } from './http/decorators';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport { AuthMeta } from './http/auth-mode';\nexport type { AuthMode, ApiKeyCredential, ApiKeyCredentials, JwtRoles, JwtRequirement } 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 @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 HttpServiceUnavailableError,\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// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @AuthLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\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\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"]}