@webpieces/core-util 0.3.380 → 0.3.382
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/ClientRegistry.d.ts +88 -19
- package/src/http/ClientRegistry.js +120 -19
- package/src/http/ClientRegistry.js.map +1 -1
- package/src/http/ErrorTranslation.d.ts +47 -0
- package/src/http/ErrorTranslation.js +21 -0
- package/src/http/ErrorTranslation.js.map +1 -0
- package/src/http/templateDeriver.d.ts +20 -0
- package/src/http/templateDeriver.js +40 -0
- package/src/http/templateDeriver.js.map +1 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +7 -1
- package/src/index.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,44 +1,113 @@
|
|
|
1
|
+
import { ErrorTranslation, ErrorWireForm } from './ErrorTranslation';
|
|
2
|
+
import { ProtocolError } from './errors';
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
+
* Derives a base URL for a service name that has NO registered mapping — the pluggable half of
|
|
5
|
+
* {@link ClientRegistry} resolution. One per environment, installed once at startup:
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* - `gcpCloudRunDeriver()` (@webpieces/gcp-identity) — the Cloud Run formula, read from the metadata
|
|
8
|
+
* server. For code running ON GCP.
|
|
9
|
+
* - `gcpCloudRunDeriver(new GcpCloudRunTarget(projectNumber, region))` — the SAME formula with the
|
|
10
|
+
* values supplied from config, for code running OFF GCP that still calls Cloud Run (a CLI, CI).
|
|
11
|
+
* - {@link templateDeriver} (this package, browser-safe) — pure string substitution, for AWS or
|
|
12
|
+
* anything else with predictable DNS.
|
|
13
|
+
* - none at all — a browser goes same-origin, and localhost/tests hand-register their mappings.
|
|
14
|
+
*/
|
|
15
|
+
export type ServiceUrlDeriver = (svcName: string) => Promise<string>;
|
|
16
|
+
/**
|
|
17
|
+
* ClientRegistry - the ONE place a `svcName` becomes a base URL, for every outbound client in every
|
|
18
|
+
* environment. A client is built once but a URL is per-environment, so the URL belongs here rather
|
|
19
|
+
* than on the client: clients carry ONLY a svcName.
|
|
20
|
+
*
|
|
21
|
+
* Resolution is one precedence chain, identical in the browser, in node, and in Cloud Tasks:
|
|
11
22
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
23
|
+
* 1. a registered mapping wins — the localhost port table, AWS, an external API, another region
|
|
24
|
+
* or project, a host that is not Cloud Run at all. Populated at startup from per-env config.
|
|
25
|
+
* 2. else the installed {@link ServiceUrlDeriver}, if any — GCP's built-in, a DNS template, or
|
|
26
|
+
* your own. OPTIONAL on purpose: localhost is inherently a TABLE (helper-fsdb -> :8401,
|
|
27
|
+
* helper-portal -> :8201 have per-service ports), so explicit mappings must stay sufficient on
|
|
28
|
+
* their own.
|
|
29
|
+
* 3. else the caller's fallback: the BROWSER goes relative (same origin — see
|
|
30
|
+
* {@link ClientRegistry.tryResolve}), while node THROWS (see {@link ClientRegistry.resolve}),
|
|
31
|
+
* because a server has no "own origin" and an unresolvable peer is a setup bug.
|
|
17
32
|
*
|
|
18
33
|
* Configured like {@link HeaderRegistry} / LogManager — populated once at startup, then globally
|
|
19
34
|
* accessible with NO DI wiring. It is browser-safe (no `process.env`, no node-only deps), which is
|
|
20
35
|
* why it lives in core-util rather than gcp-identity.
|
|
21
36
|
*
|
|
22
37
|
* ```ts
|
|
23
|
-
* // startup,
|
|
38
|
+
* // startup, from the current environment's config:
|
|
24
39
|
* ClientRegistry.addMapping('server2', 8202); // -> http://localhost:8202
|
|
25
40
|
* ClientRegistry.addUrlMapping('email', 'https://email.other-region.example');
|
|
41
|
+
* ClientRegistry.setDeriver(gcpCloudRunDeriver()); // everything else, on GCP
|
|
26
42
|
* ```
|
|
27
43
|
*/
|
|
28
44
|
export declare class ClientRegistry {
|
|
29
45
|
/** svcName -> resolved base URL. Process-global; populated at startup per environment. */
|
|
30
46
|
private static readonly mappings;
|
|
47
|
+
/** The fallback for svcNames with no mapping. Undefined = no derivation in this environment. */
|
|
48
|
+
private static deriver;
|
|
49
|
+
/**
|
|
50
|
+
* App-supplied error translations, consulted BEFORE webpieces' built-in error mapping (both
|
|
51
|
+
* directions). Process-global, populated once at startup on the SERVER and in the BROWSER — the
|
|
52
|
+
* same no-DI pattern as {@link ClientRegistry.mappings} above. Consulted in registration order,
|
|
53
|
+
* first match wins, so a later-registered app type AND an override of a built-in status both
|
|
54
|
+
* work. See {@link ErrorTranslation}.
|
|
55
|
+
*/
|
|
56
|
+
private static readonly errorTranslations;
|
|
31
57
|
/** Map a service name to `http://localhost:<port>`. */
|
|
32
58
|
static addMapping(svcName: string, port: number): void;
|
|
33
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Map a service name to an explicit base URL (any host / any environment).
|
|
61
|
+
*
|
|
62
|
+
* The EMPTY STRING is a legal, meaningful mapping: it makes the service relative, i.e.
|
|
63
|
+
* same-origin, because the client builds its URL as `${baseUrl}${route.path}`.
|
|
64
|
+
*/
|
|
34
65
|
static addUrlMapping(svcName: string, url: string): void;
|
|
35
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Install the environment's {@link ServiceUrlDeriver} — how to resolve a svcName that has NO
|
|
68
|
+
* mapping. Optional: an environment that registers every svcName it calls needs none.
|
|
69
|
+
*/
|
|
70
|
+
static setDeriver(fn: ServiceUrlDeriver): void;
|
|
71
|
+
/** The registered override for `svcName`, or undefined if none. Registry only — no derivation. */
|
|
36
72
|
static tryLookup(svcName: string): string | undefined;
|
|
37
73
|
/**
|
|
38
|
-
* Resolve `svcName` to its registered base URL. THROWS
|
|
39
|
-
*
|
|
74
|
+
* Resolve `svcName` to its registered base URL. THROWS if the service was never registered.
|
|
75
|
+
* Registry only — it does NOT consult the deriver; prefer {@link ClientRegistry.resolve}.
|
|
40
76
|
*/
|
|
41
77
|
static lookup(svcName: string): string;
|
|
42
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Steps 1 + 2 of the chain: the registered mapping, else the installed deriver, else undefined.
|
|
80
|
+
* Non-throwing — the BROWSER uses this and treats undefined as "" (relative → same origin),
|
|
81
|
+
* which is what a browser app calling the backend it was served from wants by default.
|
|
82
|
+
*
|
|
83
|
+
* Note the `!== undefined` guard: an empty-string mapping is a legal answer ("this service is
|
|
84
|
+
* same-origin"), so it must NOT fall through to derivation.
|
|
85
|
+
*/
|
|
86
|
+
static tryResolve(svcName: string): Promise<string | undefined>;
|
|
87
|
+
/**
|
|
88
|
+
* The full chain, with node's fallback: mapping, else deriver, else THROW. A server has no "own
|
|
89
|
+
* origin" to go relative to, so an unresolvable peer is a setup bug and must fail loudly — and
|
|
90
|
+
* say how to fix it.
|
|
91
|
+
*/
|
|
92
|
+
static resolve(svcName: string): Promise<string>;
|
|
93
|
+
/**
|
|
94
|
+
* Register an app error translation. Consulted BEFORE webpieces' built-in mapping, in
|
|
95
|
+
* registration order (first match wins), so later app types AND overrides of built-ins both
|
|
96
|
+
* work. Call ONCE at startup — on the server AND in the browser — mirroring
|
|
97
|
+
* {@link ClientRegistry.addMapping}. See {@link ErrorTranslation}.
|
|
98
|
+
*/
|
|
99
|
+
static addErrorTranslation(translation: ErrorTranslation): void;
|
|
100
|
+
/**
|
|
101
|
+
* exception → wire (SERVER side). The first registered translation that claims `error` wins;
|
|
102
|
+
* `undefined` if none does — the caller then falls through to the generic webpieces mapping.
|
|
103
|
+
*/
|
|
104
|
+
static tryTranslateToWire(error: Error): ErrorWireForm | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* wire → exception (CLIENT side). The first registered translation that claims
|
|
107
|
+
* `(statusCode, protocolError)` wins; `undefined` if none does — the caller then falls through
|
|
108
|
+
* to the generic webpieces switch.
|
|
109
|
+
*/
|
|
110
|
+
static tryTranslateFromWire(statusCode: number, protocolError: ProtocolError): Error | undefined;
|
|
111
|
+
/** Reset mappings, the deriver, AND error translations. For tests, so the process-globals do not leak across specs. */
|
|
43
112
|
static clear(): void;
|
|
44
113
|
}
|
|
@@ -2,53 +2,77 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ClientRegistry = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* ClientRegistry - the
|
|
6
|
-
*
|
|
5
|
+
* ClientRegistry - the ONE place a `svcName` becomes a base URL, for every outbound client in every
|
|
6
|
+
* environment. A client is built once but a URL is per-environment, so the URL belongs here rather
|
|
7
|
+
* than on the client: clients carry ONLY a svcName.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
* (see gcp-identity `resolveServiceUrl`), so same-project/same-region services need NO registration.
|
|
10
|
-
* Everything the derivation cannot describe — a localhost port, another region, another project, a
|
|
11
|
-
* host that is not Cloud Run at all, or a browser that cannot read `K_SERVICE` — is registered here
|
|
12
|
-
* instead. Each environment populates the registry (from its own per-env config) with the URLs it
|
|
13
|
-
* needs; a registered mapping WINS over GCP derivation.
|
|
9
|
+
* Resolution is one precedence chain, identical in the browser, in node, and in Cloud Tasks:
|
|
14
10
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
11
|
+
* 1. a registered mapping wins — the localhost port table, AWS, an external API, another region
|
|
12
|
+
* or project, a host that is not Cloud Run at all. Populated at startup from per-env config.
|
|
13
|
+
* 2. else the installed {@link ServiceUrlDeriver}, if any — GCP's built-in, a DNS template, or
|
|
14
|
+
* your own. OPTIONAL on purpose: localhost is inherently a TABLE (helper-fsdb -> :8401,
|
|
15
|
+
* helper-portal -> :8201 have per-service ports), so explicit mappings must stay sufficient on
|
|
16
|
+
* their own.
|
|
17
|
+
* 3. else the caller's fallback: the BROWSER goes relative (same origin — see
|
|
18
|
+
* {@link ClientRegistry.tryResolve}), while node THROWS (see {@link ClientRegistry.resolve}),
|
|
19
|
+
* because a server has no "own origin" and an unresolvable peer is a setup bug.
|
|
20
20
|
*
|
|
21
21
|
* Configured like {@link HeaderRegistry} / LogManager — populated once at startup, then globally
|
|
22
22
|
* accessible with NO DI wiring. It is browser-safe (no `process.env`, no node-only deps), which is
|
|
23
23
|
* why it lives in core-util rather than gcp-identity.
|
|
24
24
|
*
|
|
25
25
|
* ```ts
|
|
26
|
-
* // startup,
|
|
26
|
+
* // startup, from the current environment's config:
|
|
27
27
|
* ClientRegistry.addMapping('server2', 8202); // -> http://localhost:8202
|
|
28
28
|
* ClientRegistry.addUrlMapping('email', 'https://email.other-region.example');
|
|
29
|
+
* ClientRegistry.setDeriver(gcpCloudRunDeriver()); // everything else, on GCP
|
|
29
30
|
* ```
|
|
30
31
|
*/
|
|
31
32
|
class ClientRegistry {
|
|
32
33
|
/** svcName -> resolved base URL. Process-global; populated at startup per environment. */
|
|
33
34
|
static mappings = new Map();
|
|
35
|
+
/** The fallback for svcNames with no mapping. Undefined = no derivation in this environment. */
|
|
36
|
+
static deriver;
|
|
37
|
+
/**
|
|
38
|
+
* App-supplied error translations, consulted BEFORE webpieces' built-in error mapping (both
|
|
39
|
+
* directions). Process-global, populated once at startup on the SERVER and in the BROWSER — the
|
|
40
|
+
* same no-DI pattern as {@link ClientRegistry.mappings} above. Consulted in registration order,
|
|
41
|
+
* first match wins, so a later-registered app type AND an override of a built-in status both
|
|
42
|
+
* work. See {@link ErrorTranslation}.
|
|
43
|
+
*/
|
|
44
|
+
static errorTranslations = [];
|
|
34
45
|
/** Map a service name to `http://localhost:<port>`. */
|
|
35
46
|
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
|
|
36
47
|
static addMapping(svcName, port) {
|
|
37
48
|
ClientRegistry.mappings.set(svcName, `http://localhost:${port}`);
|
|
38
49
|
}
|
|
39
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Map a service name to an explicit base URL (any host / any environment).
|
|
52
|
+
*
|
|
53
|
+
* The EMPTY STRING is a legal, meaningful mapping: it makes the service relative, i.e.
|
|
54
|
+
* same-origin, because the client builds its URL as `${baseUrl}${route.path}`.
|
|
55
|
+
*/
|
|
40
56
|
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
|
|
41
57
|
static addUrlMapping(svcName, url) {
|
|
42
58
|
ClientRegistry.mappings.set(svcName, url);
|
|
43
59
|
}
|
|
44
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Install the environment's {@link ServiceUrlDeriver} — how to resolve a svcName that has NO
|
|
62
|
+
* mapping. Optional: an environment that registers every svcName it calls needs none.
|
|
63
|
+
*/
|
|
64
|
+
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
|
|
65
|
+
static setDeriver(fn) {
|
|
66
|
+
ClientRegistry.deriver = fn;
|
|
67
|
+
}
|
|
68
|
+
/** The registered override for `svcName`, or undefined if none. Registry only — no derivation. */
|
|
45
69
|
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
|
|
46
70
|
static tryLookup(svcName) {
|
|
47
71
|
return ClientRegistry.mappings.get(svcName);
|
|
48
72
|
}
|
|
49
73
|
/**
|
|
50
|
-
* Resolve `svcName` to its registered base URL. THROWS
|
|
51
|
-
*
|
|
74
|
+
* Resolve `svcName` to its registered base URL. THROWS if the service was never registered.
|
|
75
|
+
* Registry only — it does NOT consult the deriver; prefer {@link ClientRegistry.resolve}.
|
|
52
76
|
*/
|
|
53
77
|
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
|
|
54
78
|
static lookup(svcName) {
|
|
@@ -60,10 +84,87 @@ class ClientRegistry {
|
|
|
60
84
|
}
|
|
61
85
|
return url;
|
|
62
86
|
}
|
|
63
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* Steps 1 + 2 of the chain: the registered mapping, else the installed deriver, else undefined.
|
|
89
|
+
* Non-throwing — the BROWSER uses this and treats undefined as "" (relative → same origin),
|
|
90
|
+
* which is what a browser app calling the backend it was served from wants by default.
|
|
91
|
+
*
|
|
92
|
+
* Note the `!== undefined` guard: an empty-string mapping is a legal answer ("this service is
|
|
93
|
+
* same-origin"), so it must NOT fall through to derivation.
|
|
94
|
+
*/
|
|
95
|
+
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
|
|
96
|
+
static tryResolve(svcName) {
|
|
97
|
+
const override = ClientRegistry.tryLookup(svcName);
|
|
98
|
+
if (override !== undefined) {
|
|
99
|
+
return Promise.resolve(override);
|
|
100
|
+
}
|
|
101
|
+
if (!ClientRegistry.deriver) {
|
|
102
|
+
return Promise.resolve(undefined);
|
|
103
|
+
}
|
|
104
|
+
return ClientRegistry.deriver(svcName);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The full chain, with node's fallback: mapping, else deriver, else THROW. A server has no "own
|
|
108
|
+
* origin" to go relative to, so an unresolvable peer is a setup bug and must fail loudly — and
|
|
109
|
+
* say how to fix it.
|
|
110
|
+
*/
|
|
111
|
+
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
|
|
112
|
+
static async resolve(svcName) {
|
|
113
|
+
const url = await ClientRegistry.tryResolve(svcName);
|
|
114
|
+
if (url === undefined) {
|
|
115
|
+
throw new Error(`No URL for service "${svcName}".\n` +
|
|
116
|
+
` - localhost/AWS: ClientRegistry.addMapping('${svcName}', 8401)\n` +
|
|
117
|
+
` or ClientRegistry.addUrlMapping('${svcName}', 'https://...')\n` +
|
|
118
|
+
` - GCP: install a deriver — ClientRegistry.setDeriver(gcpCloudRunDeriver())\n` +
|
|
119
|
+
` - typo? svcName must be the CLOUD RUN service name, not your project/module name`);
|
|
120
|
+
}
|
|
121
|
+
return url;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Register an app error translation. Consulted BEFORE webpieces' built-in mapping, in
|
|
125
|
+
* registration order (first match wins), so later app types AND overrides of built-ins both
|
|
126
|
+
* work. Call ONCE at startup — on the server AND in the browser — mirroring
|
|
127
|
+
* {@link ClientRegistry.addMapping}. See {@link ErrorTranslation}.
|
|
128
|
+
*/
|
|
129
|
+
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
|
|
130
|
+
static addErrorTranslation(translation) {
|
|
131
|
+
ClientRegistry.errorTranslations.push(translation);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* exception → wire (SERVER side). The first registered translation that claims `error` wins;
|
|
135
|
+
* `undefined` if none does — the caller then falls through to the generic webpieces mapping.
|
|
136
|
+
*/
|
|
137
|
+
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
|
|
138
|
+
static tryTranslateToWire(error) {
|
|
139
|
+
for (const translation of ClientRegistry.errorTranslations) {
|
|
140
|
+
const wire = translation.toWire(error);
|
|
141
|
+
if (wire !== undefined) {
|
|
142
|
+
return wire;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* wire → exception (CLIENT side). The first registered translation that claims
|
|
149
|
+
* `(statusCode, protocolError)` wins; `undefined` if none does — the caller then falls through
|
|
150
|
+
* to the generic webpieces switch.
|
|
151
|
+
*/
|
|
152
|
+
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
|
|
153
|
+
static tryTranslateFromWire(statusCode, protocolError) {
|
|
154
|
+
for (const translation of ClientRegistry.errorTranslations) {
|
|
155
|
+
const err = translation.fromWire(statusCode, protocolError);
|
|
156
|
+
if (err !== undefined) {
|
|
157
|
+
return err;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
/** Reset mappings, the deriver, AND error translations. For tests, so the process-globals do not leak across specs. */
|
|
64
163
|
// webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
|
|
65
164
|
static clear() {
|
|
66
165
|
ClientRegistry.mappings.clear();
|
|
166
|
+
ClientRegistry.deriver = undefined;
|
|
167
|
+
ClientRegistry.errorTranslations.length = 0;
|
|
67
168
|
}
|
|
68
169
|
}
|
|
69
170
|
exports.ClientRegistry = ClientRegistry;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientRegistry.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ClientRegistry.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAa,cAAc;IACvB,0FAA0F;IAClF,MAAM,CAAU,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE7D,uDAAuD;IACvD,wJAAwJ;IACxJ,MAAM,CAAC,UAAU,CAAC,OAAe,EAAE,IAAY;QAC3C,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,oBAAoB,IAAI,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,+EAA+E;IAC/E,wJAAwJ;IACxJ,MAAM,CAAC,aAAa,CAAC,OAAe,EAAE,GAAW;QAC7C,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;IAED,iFAAiF;IACjF,wJAAwJ;IACxJ,MAAM,CAAC,SAAS,CAAC,OAAe;QAC5B,OAAO,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAED;;;OAGG;IACH,wJAAwJ;IACxJ,MAAM,CAAC,MAAM,CAAC,OAAe;QACzB,MAAM,GAAG,GAAG,cAAc,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACX,kCAAkC,OAAO,6BAA6B;gBACtE,oEAAoE;gBACpE,iEAAiE,CACpE,CAAC;QACN,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;IAED,2FAA2F;IAC3F,wJAAwJ;IACxJ,MAAM,CAAC,KAAK;QACR,cAAc,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACpC,CAAC;;AA3CL,wCA4CC","sourcesContent":["/**\n * ClientRegistry - the process-global `svcName -> base URL` table of per-environment URL overrides\n * used to route outbound client calls.\n *\n * On GCP a client resolves a peer's base URL deterministically from `K_SERVICE` + project + region\n * (see gcp-identity `resolveServiceUrl`), so same-project/same-region services need NO registration.\n * Everything the derivation cannot describe — a localhost port, another region, another project, a\n * host that is not Cloud Run at all, or a browser that cannot read `K_SERVICE` — is registered here\n * instead. Each environment populates the registry (from its own per-env config) with the URLs it\n * needs; a registered mapping WINS over GCP derivation.\n *\n * - **http-client-node / cloudtasks-client** resolve through gcp-identity `resolveServiceUrl`: it\n * checks {@link ClientRegistry.tryLookup} first (override), then derives on GCP, then throws\n * off-GCP if unregistered — a missing mapping is a setup bug, not a silent mis-route.\n * - **http-client-browser** cannot derive GCP URLs, so it resolves purely via\n * {@link ClientRegistry.lookup}; the Angular app registers each svcName at startup.\n *\n * Configured like {@link HeaderRegistry} / LogManager — populated once at startup, then globally\n * accessible with NO DI wiring. It is browser-safe (no `process.env`, no node-only deps), which is\n * why it lives in core-util rather than gcp-identity.\n *\n * ```ts\n * // startup, populated from the current environment's config:\n * ClientRegistry.addMapping('server2', 8202); // -> http://localhost:8202\n * ClientRegistry.addUrlMapping('email', 'https://email.other-region.example');\n * ```\n */\nexport class ClientRegistry {\n /** svcName -> resolved base URL. Process-global; populated at startup per environment. */\n private static readonly mappings = new Map<string, string>();\n\n /** Map a service name to `http://localhost:<port>`. */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static addMapping(svcName: string, port: number): void {\n ClientRegistry.mappings.set(svcName, `http://localhost:${port}`);\n }\n\n /** Map a service name to an explicit base URL (any host / any environment). */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static addUrlMapping(svcName: string, url: string): void {\n ClientRegistry.mappings.set(svcName, url);\n }\n\n /** The registered override for `svcName`, or undefined if none. Non-throwing. */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static tryLookup(svcName: string): string | undefined {\n return ClientRegistry.mappings.get(svcName);\n }\n\n /**\n * Resolve `svcName` to its registered base URL. THROWS with actionable guidance if the service\n * was never registered — used where there is no GCP-derivation fallback (the browser).\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static lookup(svcName: string): string {\n const url = ClientRegistry.tryLookup(svcName);\n if (url === undefined) {\n throw new Error(\n `No URL registered for service \"${svcName}\". Register it at startup: ` +\n `ClientRegistry.addMapping(svcName, port) for a localhost port, or ` +\n `ClientRegistry.addUrlMapping(svcName, url) for an explicit URL.`,\n );\n }\n return url;\n }\n\n /** Reset the registry. For tests, so the process-global map does not leak across specs. */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static clear(): void {\n ClientRegistry.mappings.clear();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ClientRegistry.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ClientRegistry.ts"],"names":[],"mappings":"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","sourcesContent":["import { ErrorTranslation, ErrorWireForm } from './ErrorTranslation';\nimport { ProtocolError } from './errors';\n\n/**\n * Derives a base URL for a service name that has NO registered mapping — the pluggable half of\n * {@link ClientRegistry} resolution. One per environment, installed once at startup:\n *\n * - `gcpCloudRunDeriver()` (@webpieces/gcp-identity) — the Cloud Run formula, read from the metadata\n * server. For code running ON GCP.\n * - `gcpCloudRunDeriver(new GcpCloudRunTarget(projectNumber, region))` — the SAME formula with the\n * values supplied from config, for code running OFF GCP that still calls Cloud Run (a CLI, CI).\n * - {@link templateDeriver} (this package, browser-safe) — pure string substitution, for AWS or\n * anything else with predictable DNS.\n * - none at all — a browser goes same-origin, and localhost/tests hand-register their mappings.\n */\nexport type ServiceUrlDeriver = (svcName: string) => Promise<string>;\n\n/**\n * ClientRegistry - the ONE place a `svcName` becomes a base URL, for every outbound client in every\n * environment. A client is built once but a URL is per-environment, so the URL belongs here rather\n * than on the client: clients carry ONLY a svcName.\n *\n * Resolution is one precedence chain, identical in the browser, in node, and in Cloud Tasks:\n *\n * 1. a registered mapping wins — the localhost port table, AWS, an external API, another region\n * or project, a host that is not Cloud Run at all. Populated at startup from per-env config.\n * 2. else the installed {@link ServiceUrlDeriver}, if any — GCP's built-in, a DNS template, or\n * your own. OPTIONAL on purpose: localhost is inherently a TABLE (helper-fsdb -> :8401,\n * helper-portal -> :8201 have per-service ports), so explicit mappings must stay sufficient on\n * their own.\n * 3. else the caller's fallback: the BROWSER goes relative (same origin — see\n * {@link ClientRegistry.tryResolve}), while node THROWS (see {@link ClientRegistry.resolve}),\n * because a server has no \"own origin\" and an unresolvable peer is a setup bug.\n *\n * Configured like {@link HeaderRegistry} / LogManager — populated once at startup, then globally\n * accessible with NO DI wiring. It is browser-safe (no `process.env`, no node-only deps), which is\n * why it lives in core-util rather than gcp-identity.\n *\n * ```ts\n * // startup, from the current environment's config:\n * ClientRegistry.addMapping('server2', 8202); // -> http://localhost:8202\n * ClientRegistry.addUrlMapping('email', 'https://email.other-region.example');\n * ClientRegistry.setDeriver(gcpCloudRunDeriver()); // everything else, on GCP\n * ```\n */\nexport class ClientRegistry {\n /** svcName -> resolved base URL. Process-global; populated at startup per environment. */\n private static readonly mappings = new Map<string, string>();\n\n /** The fallback for svcNames with no mapping. Undefined = no derivation in this environment. */\n private static deriver: ServiceUrlDeriver | undefined;\n\n /**\n * App-supplied error translations, consulted BEFORE webpieces' built-in error mapping (both\n * directions). Process-global, populated once at startup on the SERVER and in the BROWSER — the\n * same no-DI pattern as {@link ClientRegistry.mappings} above. Consulted in registration order,\n * first match wins, so a later-registered app type AND an override of a built-in status both\n * work. See {@link ErrorTranslation}.\n */\n private static readonly errorTranslations: ErrorTranslation[] = [];\n\n /** Map a service name to `http://localhost:<port>`. */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static addMapping(svcName: string, port: number): void {\n ClientRegistry.mappings.set(svcName, `http://localhost:${port}`);\n }\n\n /**\n * Map a service name to an explicit base URL (any host / any environment).\n *\n * The EMPTY STRING is a legal, meaningful mapping: it makes the service relative, i.e.\n * same-origin, because the client builds its URL as `${baseUrl}${route.path}`.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static addUrlMapping(svcName: string, url: string): void {\n ClientRegistry.mappings.set(svcName, url);\n }\n\n /**\n * Install the environment's {@link ServiceUrlDeriver} — how to resolve a svcName that has NO\n * mapping. Optional: an environment that registers every svcName it calls needs none.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static setDeriver(fn: ServiceUrlDeriver): void {\n ClientRegistry.deriver = fn;\n }\n\n /** The registered override for `svcName`, or undefined if none. Registry only — no derivation. */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static tryLookup(svcName: string): string | undefined {\n return ClientRegistry.mappings.get(svcName);\n }\n\n /**\n * Resolve `svcName` to its registered base URL. THROWS if the service was never registered.\n * Registry only — it does NOT consult the deriver; prefer {@link ClientRegistry.resolve}.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static lookup(svcName: string): string {\n const url = ClientRegistry.tryLookup(svcName);\n if (url === undefined) {\n throw new Error(\n `No URL registered for service \"${svcName}\". Register it at startup: ` +\n `ClientRegistry.addMapping(svcName, port) for a localhost port, or ` +\n `ClientRegistry.addUrlMapping(svcName, url) for an explicit URL.`,\n );\n }\n return url;\n }\n\n /**\n * Steps 1 + 2 of the chain: the registered mapping, else the installed deriver, else undefined.\n * Non-throwing — the BROWSER uses this and treats undefined as \"\" (relative → same origin),\n * which is what a browser app calling the backend it was served from wants by default.\n *\n * Note the `!== undefined` guard: an empty-string mapping is a legal answer (\"this service is\n * same-origin\"), so it must NOT fall through to derivation.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static tryResolve(svcName: string): Promise<string | undefined> {\n const override = ClientRegistry.tryLookup(svcName);\n if (override !== undefined) {\n return Promise.resolve(override);\n }\n if (!ClientRegistry.deriver) {\n return Promise.resolve(undefined);\n }\n return ClientRegistry.deriver(svcName);\n }\n\n /**\n * The full chain, with node's fallback: mapping, else deriver, else THROW. A server has no \"own\n * origin\" to go relative to, so an unresolvable peer is a setup bug and must fail loudly — and\n * say how to fix it.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static async resolve(svcName: string): Promise<string> {\n const url = await ClientRegistry.tryResolve(svcName);\n if (url === undefined) {\n throw new Error(\n `No URL for service \"${svcName}\".\\n` +\n ` - localhost/AWS: ClientRegistry.addMapping('${svcName}', 8401)\\n` +\n ` or ClientRegistry.addUrlMapping('${svcName}', 'https://...')\\n` +\n ` - GCP: install a deriver — ClientRegistry.setDeriver(gcpCloudRunDeriver())\\n` +\n ` - typo? svcName must be the CLOUD RUN service name, not your project/module name`,\n );\n }\n return url;\n }\n\n /**\n * Register an app error translation. Consulted BEFORE webpieces' built-in mapping, in\n * registration order (first match wins), so later app types AND overrides of built-ins both\n * work. Call ONCE at startup — on the server AND in the browser — mirroring\n * {@link ClientRegistry.addMapping}. See {@link ErrorTranslation}.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static addErrorTranslation(translation: ErrorTranslation): void {\n ClientRegistry.errorTranslations.push(translation);\n }\n\n /**\n * exception → wire (SERVER side). The first registered translation that claims `error` wins;\n * `undefined` if none does — the caller then falls through to the generic webpieces mapping.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static tryTranslateToWire(error: Error): ErrorWireForm | undefined {\n for (const translation of ClientRegistry.errorTranslations) {\n const wire = translation.toWire(error);\n if (wire !== undefined) {\n return wire;\n }\n }\n return undefined;\n }\n\n /**\n * wire → exception (CLIENT side). The first registered translation that claims\n * `(statusCode, protocolError)` wins; `undefined` if none does — the caller then falls through\n * to the generic webpieces switch.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static tryTranslateFromWire(statusCode: number, protocolError: ProtocolError): Error | undefined {\n for (const translation of ClientRegistry.errorTranslations) {\n const err = translation.fromWire(statusCode, protocolError);\n if (err !== undefined) {\n return err;\n }\n }\n return undefined;\n }\n\n /** Reset mappings, the deriver, AND error translations. For tests, so the process-globals do not leak across specs. */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static clear(): void {\n ClientRegistry.mappings.clear();\n ClientRegistry.deriver = undefined;\n ClientRegistry.errorTranslations.length = 0;\n }\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ProtocolError } from './errors';
|
|
2
|
+
/**
|
|
3
|
+
* ErrorWireForm - the wire representation an error translates to: the HTTP status code plus the
|
|
4
|
+
* {@link ProtocolError} body fields. This is exactly what the server writes and the client reads,
|
|
5
|
+
* so the two directions of a translation agree on the same shape.
|
|
6
|
+
*
|
|
7
|
+
* Data-only structure (a class, not an inline object literal, per the webpieces guidelines) so
|
|
8
|
+
* every producer constructs it explicitly.
|
|
9
|
+
*/
|
|
10
|
+
export declare class ErrorWireForm {
|
|
11
|
+
readonly statusCode: number;
|
|
12
|
+
readonly protocolError: ProtocolError;
|
|
13
|
+
constructor(statusCode: number, protocolError: ProtocolError);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* ErrorTranslation - a bidirectional, app-supplied translation between one (or more) exception
|
|
17
|
+
* types and their wire form. An app registers translations ONCE at startup (server AND browser)
|
|
18
|
+
* via {@link ClientRegistry.addErrorTranslation}; they are consulted BEFORE the built-in webpieces
|
|
19
|
+
* mapping (the hard-coded status-code switch on the client, the `instanceof HttpError` ladder on
|
|
20
|
+
* the server).
|
|
21
|
+
*
|
|
22
|
+
* BOTH methods return `undefined` to mean "not mine — fall through to the next registered
|
|
23
|
+
* translation, then to generic webpieces." This single rule is what lets translations be additive
|
|
24
|
+
* (an app ADDS a new error type, e.g. a custom 460) AND override-capable (an app REPLACES how a
|
|
25
|
+
* built-in status like 400 is written/reconstructed): a translation that returns a value wins;
|
|
26
|
+
* one that returns undefined steps aside.
|
|
27
|
+
*
|
|
28
|
+
* Symmetry: `toWire` runs on the SERVER (exception → JSON) and `fromWire` runs on the CLIENT
|
|
29
|
+
* (JSON → exception). Because {@link ErrorTranslation} and {@link ClientRegistry} live in core-util
|
|
30
|
+
* (browser-safe, zero node deps), the identical translation object serves the node server and the
|
|
31
|
+
* Angular/browser client — the same halves the app supplies together.
|
|
32
|
+
*
|
|
33
|
+
* This is a business-logic contract (methods, not data), so it is an interface per the webpieces
|
|
34
|
+
* guidelines.
|
|
35
|
+
*/
|
|
36
|
+
export interface ErrorTranslation {
|
|
37
|
+
/**
|
|
38
|
+
* exception → JSON. Return the wire form for `error`, or `undefined` if this translation does
|
|
39
|
+
* not handle `error` (→ fall through to the next translation, then to generic webpieces).
|
|
40
|
+
*/
|
|
41
|
+
toWire(error: Error): ErrorWireForm | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* JSON → exception. Return the reconstructed, typed error for `(statusCode, protocolError)`, or
|
|
44
|
+
* `undefined` to fall through to the next translation, then to the generic webpieces switch.
|
|
45
|
+
*/
|
|
46
|
+
fromWire(statusCode: number, protocolError: ProtocolError): Error | undefined;
|
|
47
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ErrorWireForm = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* ErrorWireForm - the wire representation an error translates to: the HTTP status code plus the
|
|
6
|
+
* {@link ProtocolError} body fields. This is exactly what the server writes and the client reads,
|
|
7
|
+
* so the two directions of a translation agree on the same shape.
|
|
8
|
+
*
|
|
9
|
+
* Data-only structure (a class, not an inline object literal, per the webpieces guidelines) so
|
|
10
|
+
* every producer constructs it explicitly.
|
|
11
|
+
*/
|
|
12
|
+
class ErrorWireForm {
|
|
13
|
+
statusCode;
|
|
14
|
+
protocolError;
|
|
15
|
+
constructor(statusCode, protocolError) {
|
|
16
|
+
this.statusCode = statusCode;
|
|
17
|
+
this.protocolError = protocolError;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.ErrorWireForm = ErrorWireForm;
|
|
21
|
+
//# sourceMappingURL=ErrorTranslation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorTranslation.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ErrorTranslation.ts"],"names":[],"mappings":";;;AAEA;;;;;;;GAOG;AACH,MAAa,aAAa;IAEF;IACA;IAFpB,YACoB,UAAkB,EAClB,aAA4B;QAD5B,eAAU,GAAV,UAAU,CAAQ;QAClB,kBAAa,GAAb,aAAa,CAAe;IAC7C,CAAC;CACP;AALD,sCAKC","sourcesContent":["import { ProtocolError } from './errors';\n\n/**\n * ErrorWireForm - the wire representation an error translates to: the HTTP status code plus the\n * {@link ProtocolError} body fields. This is exactly what the server writes and the client reads,\n * so the two directions of a translation agree on the same shape.\n *\n * Data-only structure (a class, not an inline object literal, per the webpieces guidelines) so\n * every producer constructs it explicitly.\n */\nexport class ErrorWireForm {\n constructor(\n public readonly statusCode: number,\n public readonly protocolError: ProtocolError,\n ) {}\n}\n\n/**\n * ErrorTranslation - a bidirectional, app-supplied translation between one (or more) exception\n * types and their wire form. An app registers translations ONCE at startup (server AND browser)\n * via {@link ClientRegistry.addErrorTranslation}; they are consulted BEFORE the built-in webpieces\n * mapping (the hard-coded status-code switch on the client, the `instanceof HttpError` ladder on\n * the server).\n *\n * BOTH methods return `undefined` to mean \"not mine — fall through to the next registered\n * translation, then to generic webpieces.\" This single rule is what lets translations be additive\n * (an app ADDS a new error type, e.g. a custom 460) AND override-capable (an app REPLACES how a\n * built-in status like 400 is written/reconstructed): a translation that returns a value wins;\n * one that returns undefined steps aside.\n *\n * Symmetry: `toWire` runs on the SERVER (exception → JSON) and `fromWire` runs on the CLIENT\n * (JSON → exception). Because {@link ErrorTranslation} and {@link ClientRegistry} live in core-util\n * (browser-safe, zero node deps), the identical translation object serves the node server and the\n * Angular/browser client — the same halves the app supplies together.\n *\n * This is a business-logic contract (methods, not data), so it is an interface per the webpieces\n * guidelines.\n */\nexport interface ErrorTranslation {\n /**\n * exception → JSON. Return the wire form for `error`, or `undefined` if this translation does\n * not handle `error` (→ fall through to the next translation, then to generic webpieces).\n */\n toWire(error: Error): ErrorWireForm | undefined;\n\n /**\n * JSON → exception. Return the reconstructed, typed error for `(statusCode, protocolError)`, or\n * `undefined` to fall through to the next translation, then to the generic webpieces switch.\n */\n fromWire(statusCode: number, protocolError: ProtocolError): Error | undefined;\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ServiceUrlDeriver } from './ClientRegistry';
|
|
2
|
+
/**
|
|
3
|
+
* A {@link ServiceUrlDeriver} for any environment whose service URLs are a PREDICTABLE FORMULA —
|
|
4
|
+
* AWS behind Route53, a k8s cluster, a corp DNS zone, anything not Cloud Run. Pure string
|
|
5
|
+
* substitution, so it is browser-safe and pulls in no cloud SDK.
|
|
6
|
+
*
|
|
7
|
+
* `{svc}` is the service name being resolved; every other `{key}` comes from `vars`:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* ClientRegistry.setDeriver(templateDeriver('https://{svc}.example.com')); // prod
|
|
11
|
+
* ClientRegistry.setDeriver(templateDeriver('https://{svc}.{env}.example.com', { env: 'qa' })); // per-env
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Nothing about this is GCP-specific, which is the point: an AWS deployment installs THIS (or no
|
|
15
|
+
* deriver at all, and registers its mappings) and never pulls in gcp-metadata.
|
|
16
|
+
*
|
|
17
|
+
* @throws Error at DERIVE time if the pattern still holds an unsubstituted `{key}` — a typo in the
|
|
18
|
+
* pattern (or a var you forgot to pass) must not silently ship a bogus hostname.
|
|
19
|
+
*/
|
|
20
|
+
export declare function templateDeriver(pattern: string, vars?: Record<string, string>): ServiceUrlDeriver;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.templateDeriver = templateDeriver;
|
|
4
|
+
/**
|
|
5
|
+
* A {@link ServiceUrlDeriver} for any environment whose service URLs are a PREDICTABLE FORMULA —
|
|
6
|
+
* AWS behind Route53, a k8s cluster, a corp DNS zone, anything not Cloud Run. Pure string
|
|
7
|
+
* substitution, so it is browser-safe and pulls in no cloud SDK.
|
|
8
|
+
*
|
|
9
|
+
* `{svc}` is the service name being resolved; every other `{key}` comes from `vars`:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* ClientRegistry.setDeriver(templateDeriver('https://{svc}.example.com')); // prod
|
|
13
|
+
* ClientRegistry.setDeriver(templateDeriver('https://{svc}.{env}.example.com', { env: 'qa' })); // per-env
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Nothing about this is GCP-specific, which is the point: an AWS deployment installs THIS (or no
|
|
17
|
+
* deriver at all, and registers its mappings) and never pulls in gcp-metadata.
|
|
18
|
+
*
|
|
19
|
+
* @throws Error at DERIVE time if the pattern still holds an unsubstituted `{key}` — a typo in the
|
|
20
|
+
* pattern (or a var you forgot to pass) must not silently ship a bogus hostname.
|
|
21
|
+
*/
|
|
22
|
+
// webpieces-disable no-function-outside-class -- a deriver IS a function; this is the factory that closes over the pattern (see ServiceUrlDeriver)
|
|
23
|
+
function templateDeriver(pattern, vars = {}) {
|
|
24
|
+
// async, so a bad pattern REJECTS like every other resolution failure rather than throwing
|
|
25
|
+
// synchronously out of ClientRegistry.tryResolve.
|
|
26
|
+
return async (svcName) => {
|
|
27
|
+
const substitutions = { ...vars, svc: svcName };
|
|
28
|
+
const url = pattern.replace(/\{(\w+)\}/g, (placeholder, key) => {
|
|
29
|
+
const value = substitutions[key];
|
|
30
|
+
return value === undefined ? placeholder : value;
|
|
31
|
+
});
|
|
32
|
+
const leftover = /\{(\w+)\}/.exec(url);
|
|
33
|
+
if (leftover) {
|
|
34
|
+
throw new Error(`templateDeriver('${pattern}') cannot resolve "${svcName}": no value for {${leftover[1]}}. ` +
|
|
35
|
+
`Pass it in the vars argument, e.g. templateDeriver(pattern, { ${leftover[1]}: '...' }).`);
|
|
36
|
+
}
|
|
37
|
+
return url;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=templateDeriver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templateDeriver.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/templateDeriver.ts"],"names":[],"mappings":";;AAqBA,0CAmBC;AAtCD;;;;;;;;;;;;;;;;;GAiBG;AACH,mJAAmJ;AACnJ,SAAgB,eAAe,CAAC,OAAe,EAAE,OAA+B,EAAE;IAC9E,2FAA2F;IAC3F,kDAAkD;IAClD,OAAO,KAAK,EAAE,OAAe,EAAmB,EAAE;QAC9C,MAAM,aAAa,GAA2B,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;QACxE,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,WAAmB,EAAE,GAAW,EAAU,EAAE;YACnF,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;YACjC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC;QACrD,CAAC,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,QAAQ,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CACX,oBAAoB,OAAO,sBAAsB,OAAO,oBAAoB,QAAQ,CAAC,CAAC,CAAC,KAAK;gBAC5F,iEAAiE,QAAQ,CAAC,CAAC,CAAC,aAAa,CAC5F,CAAC;QACN,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC,CAAC;AACN,CAAC","sourcesContent":["import { ServiceUrlDeriver } from './ClientRegistry';\n\n/**\n * A {@link ServiceUrlDeriver} for any environment whose service URLs are a PREDICTABLE FORMULA —\n * AWS behind Route53, a k8s cluster, a corp DNS zone, anything not Cloud Run. Pure string\n * substitution, so it is browser-safe and pulls in no cloud SDK.\n *\n * `{svc}` is the service name being resolved; every other `{key}` comes from `vars`:\n *\n * ```ts\n * ClientRegistry.setDeriver(templateDeriver('https://{svc}.example.com')); // prod\n * ClientRegistry.setDeriver(templateDeriver('https://{svc}.{env}.example.com', { env: 'qa' })); // per-env\n * ```\n *\n * Nothing about this is GCP-specific, which is the point: an AWS deployment installs THIS (or no\n * deriver at all, and registers its mappings) and never pulls in gcp-metadata.\n *\n * @throws Error at DERIVE time if the pattern still holds an unsubstituted `{key}` — a typo in the\n * pattern (or a var you forgot to pass) must not silently ship a bogus hostname.\n */\n// webpieces-disable no-function-outside-class -- a deriver IS a function; this is the factory that closes over the pattern (see ServiceUrlDeriver)\nexport function templateDeriver(pattern: string, vars: Record<string, string> = {}): ServiceUrlDeriver {\n // async, so a bad pattern REJECTS like every other resolution failure rather than throwing\n // synchronously out of ClientRegistry.tryResolve.\n return async (svcName: string): Promise<string> => {\n const substitutions: Record<string, string> = { ...vars, svc: svcName };\n const url = pattern.replace(/\\{(\\w+)\\}/g, (placeholder: string, key: string): string => {\n const value = substitutions[key];\n return value === undefined ? placeholder : value;\n });\n\n const leftover = /\\{(\\w+)\\}/.exec(url);\n if (leftover) {\n throw new Error(\n `templateDeriver('${pattern}') cannot resolve \"${svcName}\": no value for {${leftover[1]}}. ` +\n `Pass it in the vars argument, e.g. templateDeriver(pattern, { ${leftover[1]}: '...' }).`,\n );\n }\n return url;\n };\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -23,6 +23,10 @@ export { ProtocolError, HttpError, HttpNotFoundError, EndpointNotFoundError, Htt
|
|
|
23
23
|
export { InstantDto, DateDto, TimeDto, DateTimeDto, InstantUtil, DateUtil, TimeUtil, DateTimeUtil, } from './http/datetime';
|
|
24
24
|
export { HeaderRegistry } from './http/HeaderRegistry';
|
|
25
25
|
export { ClientRegistry } from './http/ClientRegistry';
|
|
26
|
+
export type { ServiceUrlDeriver } from './http/ClientRegistry';
|
|
27
|
+
export { ErrorWireForm } from './http/ErrorTranslation';
|
|
28
|
+
export type { ErrorTranslation } from './http/ErrorTranslation';
|
|
29
|
+
export { templateDeriver } from './http/templateDeriver';
|
|
26
30
|
export { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';
|
|
27
31
|
export { ContextReader } from './http/ContextReader';
|
|
28
32
|
export type { ContextRead } from './http/ContextReader';
|
package/src/index.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.ENTITY_NOT_FOUND = exports.HttpUserError = exports.HttpVendorError = 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.METADATA_KEYS = exports.RouteMetadata = exports.AuthMeta = exports.validateNoConflictingDecorators = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.assertEveryEndpointHasAuthMode = exports.getAuthMode = exports.getAuthMeta = exports.isApiPath = exports.getEndpoints = exports.getApiPath = exports.Queue = exports.PubSub = exports.Rpc = exports.AuthSharedSecret = exports.AuthOidc = exports.Auth = exports.AuthJwt = exports.Public = exports.AuthenticationConfig = exports.Authentication = exports.Endpoint = exports.ApiPath = exports.LogManager = exports.ConsoleLoggerFactory = exports.ConsoleLogger = exports.DESIGN_METADATA_KEYS = exports.isDocumentDesign = exports.DocumentDesign = exports.ContextTuple = exports.ContextKey = exports.toError = void 0;
|
|
12
|
-
exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.LogApiCall = exports.ContextMgr = exports.WebpiecesCoreHeaders = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = void 0;
|
|
12
|
+
exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.LogApiCall = exports.ContextMgr = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.ErrorWireForm = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = void 0;
|
|
13
13
|
var errorUtils_1 = require("./lib/errorUtils");
|
|
14
14
|
Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return errorUtils_1.toError; } });
|
|
15
15
|
var ContextKey_1 = require("./ContextKey");
|
|
@@ -102,6 +102,12 @@ var HeaderRegistry_1 = require("./http/HeaderRegistry");
|
|
|
102
102
|
Object.defineProperty(exports, "HeaderRegistry", { enumerable: true, get: function () { return HeaderRegistry_1.HeaderRegistry; } });
|
|
103
103
|
var ClientRegistry_1 = require("./http/ClientRegistry");
|
|
104
104
|
Object.defineProperty(exports, "ClientRegistry", { enumerable: true, get: function () { return ClientRegistry_1.ClientRegistry; } });
|
|
105
|
+
// Pluggable, bidirectional error translation (app exception <-> wire form). Registered on
|
|
106
|
+
// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.
|
|
107
|
+
var ErrorTranslation_1 = require("./http/ErrorTranslation");
|
|
108
|
+
Object.defineProperty(exports, "ErrorWireForm", { enumerable: true, get: function () { return ErrorTranslation_1.ErrorWireForm; } });
|
|
109
|
+
var templateDeriver_1 = require("./http/templateDeriver");
|
|
110
|
+
Object.defineProperty(exports, "templateDeriver", { enumerable: true, get: function () { return templateDeriver_1.templateDeriver; } });
|
|
105
111
|
var WebpiecesCoreHeaders_1 = require("./http/WebpiecesCoreHeaders");
|
|
106
112
|
Object.defineProperty(exports, "WebpiecesCoreHeaders", { enumerable: true, get: function () { return WebpiecesCoreHeaders_1.WebpiecesCoreHeaders; } });
|
|
107
113
|
// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;AAEH,+CAA2C;AAAlC,qGAAA,OAAO,OAAA;AAChB,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,+EAA+E;AAC/E,kFAAkF;AAClF,yCAAyC;AACzC,mDAA0F;AAAjF,gHAAA,cAAc,OAAA;AAAE,kHAAA,gBAAgB,OAAA;AAAE,sHAAA,oBAAoB,OAAA;AAO/D,yDAAwD;AAA/C,8GAAA,aAAa,OAAA;AACtB,uEAAsE;AAA7D,4HAAA,oBAAoB,OAAA;AAC7B,mDAAkD;AAAzC,wGAAA,UAAU,OAAA;AAEnB,8DAA8D;AAC9D,sEAAsE;AACtE,sEAAsE;AACtE,uEAAuE;AACvE,kEAAkE;AAElE,4BAA4B;AAC5B,gDA6B2B;AA5BvB,qGAAA,OAAO,OAAA;AACP,sGAAA,QAAQ,OAAA;AACR,4GAAA,cAAc,OAAA;AACd,kHAAA,oBAAoB,OAAA;AACpB,mEAAmE;AACnE,oGAAA,MAAM,OAAA;AACN,qGAAA,OAAO,OAAA;AACP,kGAAA,IAAI,OAAA;AACJ,sGAAA,QAAQ,OAAA;AACR,8GAAA,gBAAgB,OAAA;AAChB,sDAAsD;AACtD,iGAAA,GAAG,OAAA;AACH,oGAAA,MAAM,OAAA;AACN,mGAAA,KAAK,OAAA;AACL,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AACZ,uGAAA,SAAS,OAAA;AACT,yGAAA,WAAW,OAAA;AACX,yGAAA,WAAW,OAAA;AACX,4HAAA,8BAA8B,OAAA;AAC9B,wGAAA,UAAU,OAAA;AACV,2GAAA,aAAa,OAAA;AACb,qHAAA,uBAAuB,OAAA;AACvB,0GAAA,YAAY,OAAA;AACZ,6HAAA,+BAA+B,OAAA;AAC/B,sGAAA,QAAQ,OAAA;AACR,2GAAA,aAAa,OAAA;AACb,2GAAA,aAAa,OAAA;AAGjB,4FAA4F;AAC5F,0CAAyC;AAAhC,kGAAA,OAAO,OAAA;AAKhB,cAAc;AACd,wCAuBuB;AAtBnB,uGAAA,aAAa,OAAA;AACb,mGAAA,SAAS,OAAA;AACT,2GAAA,iBAAiB,OAAA;AACjB,+GAAA,qBAAqB,OAAA;AACrB,6GAAA,mBAAmB,OAAA;AACnB,+GAAA,qBAAqB,OAAA;AACrB,4GAAA,kBAAkB,OAAA;AAClB,0GAAA,gBAAgB,OAAA;AAChB,6GAAA,mBAAmB,OAAA;AACnB,iHAAA,uBAAuB,OAAA;AACvB,iHAAA,uBAAuB,OAAA;AACvB,yGAAA,eAAe,OAAA;AACf,uGAAA,aAAa,OAAA;AACb,0BAA0B;AAC1B,0GAAA,gBAAgB,OAAA;AAChB,0GAAA,gBAAgB,OAAA;AAChB,qGAAA,WAAW,OAAA;AACX,sGAAA,YAAY,OAAA;AACZ,6GAAA,mBAAmB,OAAA;AACnB,sGAAA,YAAY,OAAA;AACZ,uGAAA,aAAa,OAAA;AACb,qGAAA,WAAW,OAAA;AAGf,iEAAiE;AACjE,4CASyB;AAJrB,uGAAA,WAAW,OAAA;AACX,oGAAA,QAAQ,OAAA;AACR,oGAAA,QAAQ,OAAA;AACR,wGAAA,YAAY,OAAA;AAGhB,mEAAmE;AACnE,wDAAuD;AAA9C,gHAAA,cAAc,OAAA;AACvB,wDAAuD;AAA9C,gHAAA,cAAc,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;AAEH,+CAA2C;AAAlC,qGAAA,OAAO,OAAA;AAChB,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,+EAA+E;AAC/E,kFAAkF;AAClF,yCAAyC;AACzC,mDAA0F;AAAjF,gHAAA,cAAc,OAAA;AAAE,kHAAA,gBAAgB,OAAA;AAAE,sHAAA,oBAAoB,OAAA;AAO/D,yDAAwD;AAA/C,8GAAA,aAAa,OAAA;AACtB,uEAAsE;AAA7D,4HAAA,oBAAoB,OAAA;AAC7B,mDAAkD;AAAzC,wGAAA,UAAU,OAAA;AAEnB,8DAA8D;AAC9D,sEAAsE;AACtE,sEAAsE;AACtE,uEAAuE;AACvE,kEAAkE;AAElE,4BAA4B;AAC5B,gDA6B2B;AA5BvB,qGAAA,OAAO,OAAA;AACP,sGAAA,QAAQ,OAAA;AACR,4GAAA,cAAc,OAAA;AACd,kHAAA,oBAAoB,OAAA;AACpB,mEAAmE;AACnE,oGAAA,MAAM,OAAA;AACN,qGAAA,OAAO,OAAA;AACP,kGAAA,IAAI,OAAA;AACJ,sGAAA,QAAQ,OAAA;AACR,8GAAA,gBAAgB,OAAA;AAChB,sDAAsD;AACtD,iGAAA,GAAG,OAAA;AACH,oGAAA,MAAM,OAAA;AACN,mGAAA,KAAK,OAAA;AACL,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AACZ,uGAAA,SAAS,OAAA;AACT,yGAAA,WAAW,OAAA;AACX,yGAAA,WAAW,OAAA;AACX,4HAAA,8BAA8B,OAAA;AAC9B,wGAAA,UAAU,OAAA;AACV,2GAAA,aAAa,OAAA;AACb,qHAAA,uBAAuB,OAAA;AACvB,0GAAA,YAAY,OAAA;AACZ,6HAAA,+BAA+B,OAAA;AAC/B,sGAAA,QAAQ,OAAA;AACR,2GAAA,aAAa,OAAA;AACb,2GAAA,aAAa,OAAA;AAGjB,4FAA4F;AAC5F,0CAAyC;AAAhC,kGAAA,OAAO,OAAA;AAKhB,cAAc;AACd,wCAuBuB;AAtBnB,uGAAA,aAAa,OAAA;AACb,mGAAA,SAAS,OAAA;AACT,2GAAA,iBAAiB,OAAA;AACjB,+GAAA,qBAAqB,OAAA;AACrB,6GAAA,mBAAmB,OAAA;AACnB,+GAAA,qBAAqB,OAAA;AACrB,4GAAA,kBAAkB,OAAA;AAClB,0GAAA,gBAAgB,OAAA;AAChB,6GAAA,mBAAmB,OAAA;AACnB,iHAAA,uBAAuB,OAAA;AACvB,iHAAA,uBAAuB,OAAA;AACvB,yGAAA,eAAe,OAAA;AACf,uGAAA,aAAa,OAAA;AACb,0BAA0B;AAC1B,0GAAA,gBAAgB,OAAA;AAChB,0GAAA,gBAAgB,OAAA;AAChB,qGAAA,WAAW,OAAA;AACX,sGAAA,YAAY,OAAA;AACZ,6GAAA,mBAAmB,OAAA;AACnB,sGAAA,YAAY,OAAA;AACZ,uGAAA,aAAa,OAAA;AACb,qGAAA,WAAW,OAAA;AAGf,iEAAiE;AACjE,4CASyB;AAJrB,uGAAA,WAAW,OAAA;AACX,oGAAA,QAAQ,OAAA;AACR,oGAAA,QAAQ,OAAA;AACR,wGAAA,YAAY,OAAA;AAGhB,mEAAmE;AACnE,wDAAuD;AAA9C,gHAAA,cAAc,OAAA;AACvB,wDAAuD;AAA9C,gHAAA,cAAc,OAAA;AAEvB,0FAA0F;AAC1F,4FAA4F;AAC5F,4DAAwD;AAA/C,iHAAA,aAAa,OAAA;AAEtB,0DAAyD;AAAhD,kHAAA,eAAe,OAAA;AACxB,oEAAmE;AAA1D,4HAAA,oBAAoB,OAAA;AAI7B,iGAAiG;AACjG,gGAAgG;AAChG,kEAAkE;AAClE,gDAA+C;AAAtC,wGAAA,UAAU,OAAA;AAEnB,kDAAkD;AAClD,gDAA+C;AAAtC,wGAAA,UAAU,OAAA;AAEnB,iFAAiF;AACjF,qEAAkF;AAAvD,gHAAA,YAAY,OAAA;AACvC,qEAAqG;AAA5F,oHAAA,gBAAgB,OAAA;AAAE,iHAAA,aAAa,OAAA;AAAE,oHAAA,gBAAgB,OAAA;AAC1D,2DAAgF;AAAvE,0GAAA,WAAW,OAAA;AAAE,mHAAA,oBAAoB,OAAA;AAC1C,qEAAoG;AAA3F,oHAAA,gBAAgB,OAAA;AAAE,iHAAA,aAAa,OAAA;AAAE,mHAAA,eAAe,OAAA","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 { 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';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the HttpError hierarchy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n Authentication,\n AuthenticationConfig,\n // Auth mode decorators (clean service-to-service + user JWT model)\n Public,\n AuthJwt,\n Auth,\n AuthOidc,\n AuthSharedSecret,\n // API kind (RPC vs PubSub/Cloud Tasks) + queue naming\n Rpc,\n PubSub,\n Queue,\n getApiPath,\n getEndpoints,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n validateNoConflictingDecorators,\n AuthMeta,\n RouteMetadata,\n METADATA_KEYS,\n} from './http/decorators';\nexport type { AuthMode, ApiKind, JwtRequirement } from './http/decorators';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { 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 HttpVendorError,\n HttpUserError,\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\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// 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';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport type { ContextRead } 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)\nexport { LogApiCall } from './http/LogApiCall';\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"]}
|