@webpieces/core-util 0.4.738 → 0.4.740

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.738",
3
+ "version": "0.4.740",
4
4
  "description": "Utility functions for WebPieces - works in browser and Node.js",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -1,5 +1,5 @@
1
- import { ErrorTranslation, ErrorWireForm } from './ErrorTranslation';
2
- import { ProtocolError } from './errors';
1
+ import { ErrorTranslators } from './ErrorTranslators';
2
+ import { HttpResponseDto } from './HttpResponseDto';
3
3
  import { FailureClassifier } from './FailureClassifier';
4
4
  import { ApiMethodInfo } from './ApiMethodInfo';
5
5
  /**
@@ -55,13 +55,15 @@ export declare class ClientRegistry {
55
55
  /** The fallback for svcNames with no mapping. Undefined = no derivation in this environment. */
56
56
  private static deriver;
57
57
  /**
58
- * App-supplied error translations, consulted BEFORE webpieces' built-in error mapping (both
59
- * directions). Process-global, populated once at startup on the SERVER and in the BROWSER — the
60
- * same no-DI pattern as {@link ClientRegistry.mappings} above. Consulted in registration order,
61
- * first match wins, so a later-registered app type AND an override of a built-in status both
62
- * work. See {@link ErrorTranslation}.
58
+ * The app's ONE {@link ErrorTranslators}, consulted BEFORE webpieces' built-in error mapping in
59
+ * BOTH directions. Process-global, set once at startup on the SERVER and in the BROWSER — the
60
+ * same no-DI pattern as {@link ClientRegistry.mappings} above.
61
+ *
62
+ * ONE, not a list, on purpose: an app that has several layers of error policy composes them
63
+ * INSIDE its own `toWire`, where the precedence is written down, instead of leaving it implicit
64
+ * in the order two unrelated startup paths happened to register.
63
65
  */
64
- private static readonly errorTranslations;
66
+ private static errorTranslators;
65
67
  /**
66
68
  * The app/company DEFAULT {@link FailureClassifier} — ONE per process, reads {@link ApiMethodInfo.side}
67
69
  * so a single strategy covers the server router AND all internal clients. Undefined = use the
@@ -113,23 +115,26 @@ export declare class ClientRegistry {
113
115
  */
114
116
  static resolve(svcName: string): Promise<string>;
115
117
  /**
116
- * Register an app error translation. Consulted BEFORE webpieces' built-in mapping, in
117
- * registration order (first match wins), so later app types AND overrides of built-ins both
118
- * work. Call ONCE at startup on the server AND in the browser mirroring
119
- * {@link ClientRegistry.addMapping}. See {@link ErrorTranslation}.
118
+ * Install the app's {@link ErrorTranslators} the ONE symmetric owner of error translation for
119
+ * this process, server side AND every client side. Consulted BEFORE webpieces' built-in mapping
120
+ * in both directions. Call ONCE at startup, on the server AND in the browser, mirroring
121
+ * {@link ClientRegistry.addMapping}.
122
+ *
123
+ * `set`, not `add`: see the field doc above for why a registry LIST is not wanted.
120
124
  */
121
- static addErrorTranslation(translation: ErrorTranslation): void;
125
+ static setErrorTranslators(translators: ErrorTranslators): void;
122
126
  /**
123
- * exception → wire (SERVER side). The first registered translation that claims `error` wins;
124
- * `undefined` if none does — the caller then falls through to the generic webpieces mapping.
127
+ * exception → the WHOLE response (SERVER side), or `undefined` when no translators are installed
128
+ * or the installed ones do not claim `error` — the caller then falls through to the webpieces
129
+ * default.
125
130
  */
126
- static tryTranslateToWire(error: Error): ErrorWireForm | undefined;
131
+ static tryTranslateToWire(error: Error): HttpResponseDto | undefined;
127
132
  /**
128
- * wire → exception (CLIENT side). The first registered translation that claims
129
- * `(statusCode, protocolError)` wins; `undefined` if none does — the caller then falls through
130
- * to the generic webpieces switch.
133
+ * the WHOLE response → exception (CLIENT side), or `undefined` when no translators are installed
134
+ * or the installed ones do not claim `response` — the caller then falls through to the built-in
135
+ * webpieces status-to-type mapping.
131
136
  */
132
- static tryTranslateFromWire(statusCode: number, protocolError: ProtocolError): Error | undefined;
137
+ static tryTranslateFromWire(response: HttpResponseDto): Error | undefined;
133
138
  /**
134
139
  * Set the app/company DEFAULT failure classifier — ONE per process, covering the server router and
135
140
  * all internal clients (it branches on {@link ApiMethodInfo.side}). Call ONCE at startup, on the
@@ -153,8 +158,8 @@ export declare class ClientRegistry {
153
158
  */
154
159
  static classifyFailure(error: Error, methodInfo: ApiMethodInfo): boolean;
155
160
  /**
156
- * Reset mappings, the deriver, error translations, AND failure classifiers. For tests, so the
157
- * process-globals do not leak across specs.
161
+ * Reset mappings, the deriver, the error translators, AND failure classifiers. For tests, so
162
+ * the process-globals do not leak across specs.
158
163
  */
159
164
  static clear(): void;
160
165
  }
@@ -42,13 +42,15 @@ class ClientRegistry {
42
42
  /** The fallback for svcNames with no mapping. Undefined = no derivation in this environment. */
43
43
  static deriver;
44
44
  /**
45
- * App-supplied error translations, consulted BEFORE webpieces' built-in error mapping (both
46
- * directions). Process-global, populated once at startup on the SERVER and in the BROWSER — the
47
- * same no-DI pattern as {@link ClientRegistry.mappings} above. Consulted in registration order,
48
- * first match wins, so a later-registered app type AND an override of a built-in status both
49
- * work. See {@link ErrorTranslation}.
45
+ * The app's ONE {@link ErrorTranslators}, consulted BEFORE webpieces' built-in error mapping in
46
+ * BOTH directions. Process-global, set once at startup on the SERVER and in the BROWSER — the
47
+ * same no-DI pattern as {@link ClientRegistry.mappings} above.
48
+ *
49
+ * ONE, not a list, on purpose: an app that has several layers of error policy composes them
50
+ * INSIDE its own `toWire`, where the precedence is written down, instead of leaving it implicit
51
+ * in the order two unrelated startup paths happened to register.
50
52
  */
51
- static errorTranslations = [];
53
+ static errorTranslators;
52
54
  /**
53
55
  * The app/company DEFAULT {@link FailureClassifier} — ONE per process, reads {@link ApiMethodInfo.side}
54
56
  * so a single strategy covers the server router AND all internal clients. Undefined = use the
@@ -144,43 +146,34 @@ class ClientRegistry {
144
146
  return url;
145
147
  }
146
148
  /**
147
- * Register an app error translation. Consulted BEFORE webpieces' built-in mapping, in
148
- * registration order (first match wins), so later app types AND overrides of built-ins both
149
- * work. Call ONCE at startup on the server AND in the browser mirroring
150
- * {@link ClientRegistry.addMapping}. See {@link ErrorTranslation}.
149
+ * Install the app's {@link ErrorTranslators} the ONE symmetric owner of error translation for
150
+ * this process, server side AND every client side. Consulted BEFORE webpieces' built-in mapping
151
+ * in both directions. Call ONCE at startup, on the server AND in the browser, mirroring
152
+ * {@link ClientRegistry.addMapping}.
153
+ *
154
+ * `set`, not `add`: see the field doc above for why a registry LIST is not wanted.
151
155
  */
152
156
  // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
153
- static addErrorTranslation(translation) {
154
- ClientRegistry.errorTranslations.push(translation);
157
+ static setErrorTranslators(translators) {
158
+ ClientRegistry.errorTranslators = translators;
155
159
  }
156
160
  /**
157
- * exception → wire (SERVER side). The first registered translation that claims `error` wins;
158
- * `undefined` if none does — the caller then falls through to the generic webpieces mapping.
161
+ * exception → the WHOLE response (SERVER side), or `undefined` when no translators are installed
162
+ * or the installed ones do not claim `error` — the caller then falls through to the webpieces
163
+ * default.
159
164
  */
160
165
  // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
161
166
  static tryTranslateToWire(error) {
162
- for (const translation of ClientRegistry.errorTranslations) {
163
- const wire = translation.toWire(error);
164
- if (wire !== undefined) {
165
- return wire;
166
- }
167
- }
168
- return undefined;
167
+ return ClientRegistry.errorTranslators?.toWire(error);
169
168
  }
170
169
  /**
171
- * wire → exception (CLIENT side). The first registered translation that claims
172
- * `(statusCode, protocolError)` wins; `undefined` if none does — the caller then falls through
173
- * to the generic webpieces switch.
170
+ * the WHOLE response → exception (CLIENT side), or `undefined` when no translators are installed
171
+ * or the installed ones do not claim `response` — the caller then falls through to the built-in
172
+ * webpieces status-to-type mapping.
174
173
  */
175
174
  // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
176
- static tryTranslateFromWire(statusCode, protocolError) {
177
- for (const translation of ClientRegistry.errorTranslations) {
178
- const err = translation.fromWire(statusCode, protocolError);
179
- if (err !== undefined) {
180
- return err;
181
- }
182
- }
183
- return undefined;
175
+ static tryTranslateFromWire(response) {
176
+ return ClientRegistry.errorTranslators?.fromWire(response);
184
177
  }
185
178
  /**
186
179
  * Set the app/company DEFAULT failure classifier — ONE per process, covering the server router and
@@ -227,14 +220,14 @@ class ClientRegistry {
227
220
  return WebpiecesDefaultFailureClassifier_1.WEBPIECES_DEFAULT_FAILURE_CLASSIFIER.isFailure(error, methodInfo) ?? true;
228
221
  }
229
222
  /**
230
- * Reset mappings, the deriver, error translations, AND failure classifiers. For tests, so the
231
- * process-globals do not leak across specs.
223
+ * Reset mappings, the deriver, the error translators, AND failure classifiers. For tests, so
224
+ * the process-globals do not leak across specs.
232
225
  */
233
226
  // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected
234
227
  static clear() {
235
228
  ClientRegistry.mappings.clear();
236
229
  ClientRegistry.deriver = undefined;
237
- ClientRegistry.errorTranslations.length = 0;
230
+ ClientRegistry.errorTranslators = undefined;
238
231
  ClientRegistry.appDefaultFailureClassifier = undefined;
239
232
  ClientRegistry.failureClassifiersByApiClass.clear();
240
233
  }
@@ -1 +1 @@
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';\nimport { FailureClassifier } from './FailureClassifier';\nimport { WEBPIECES_DEFAULT_FAILURE_CLASSIFIER } from './WebpiecesDefaultFailureClassifier';\nimport { ApiMethodInfo } from './ApiMethodInfo';\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 * A `svcName` should be the nx MODULE name of the service you are calling — that is the one name\n * `architecture:validate-runtime-architecture` can verify actually exists, so a typo or a rename\n * fails the build instead of failing in production. When the DEPLOYED name differs (an environment\n * prefix like `tf-`, or a Cloud Run service named unlike its module), translate it in ONE place —\n * the deriver below, or a `serviceName` alias in the module's project.json — never at the call site.\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 /**\n * The app/company DEFAULT {@link FailureClassifier} — ONE per process, reads {@link ApiMethodInfo.side}\n * so a single strategy covers the server router AND all internal clients. Undefined = use the\n * webpieces built-in ({@link WEBPIECES_DEFAULT_FAILURE_CLASSIFIER}). Populated once at startup on the\n * SERVER and in the BROWSER — same no-DI pattern as {@link ClientRegistry.mappings}.\n */\n private static appDefaultFailureClassifier: FailureClassifier | undefined;\n\n /**\n * Per-EXTERNAL-client {@link FailureClassifier}s, keyed by {@link ApiMethodInfo.apiClass}\n * ('FirestoreAdminClient', 'ClaudeApi', ...). Consulted BEFORE the default tier, so an external\n * client overrides the default for its own apiClass only. Internal clients / the server register\n * nothing here and fall through to the default. See {@link ClientRegistry.classifyFailure}.\n */\n private static readonly failureClassifiersByApiClass = new Map<string, FailureClassifier>();\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 ` - deployed name differs from the module name (e.g. a 'tf-' prefix)? Translate it ONCE\\n` +\n ` in the deriver — setDeriver(s => gcpCloudRunDeriver()('tf-' + s)) — so every call\\n` +\n ` site keeps naming the MODULE, which architecture:validate-runtime-architecture checks`,\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 /**\n * Set the app/company DEFAULT failure classifier — ONE per process, covering the server router and\n * all internal clients (it branches on {@link ApiMethodInfo.side}). Call ONCE at startup, on the\n * server AND in the browser. Overrides the webpieces built-in for calls no per-apiClass classifier\n * claims. See {@link FailureClassifier}.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static setDefaultFailureClassifier(classifier: FailureClassifier): void {\n ClientRegistry.appDefaultFailureClassifier = classifier;\n }\n\n /**\n * Register a per-EXTERNAL-client failure classifier, keyed by {@link ApiMethodInfo.apiClass}\n * (e.g. 'FirestoreAdminClient'). Consulted BEFORE the default tier for calls with that apiClass.\n * Call ONCE at startup. See {@link FailureClassifier}.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static addFailureClassifier(apiClass: string, classifier: FailureClassifier): void {\n ClientRegistry.failureClassifiersByApiClass.set(apiClass, classifier);\n }\n\n /**\n * Is this thrown API-call error a real FAILURE (true) or an expected non-failure (false)?\n * Resolved most-specific-first, and ALWAYS definitive (returns a boolean):\n * 1. the per-apiClass classifier, if one is registered for `methodInfo.apiClass` — unless it defers;\n * 2. else the app default classifier, if one was set — unless it defers;\n * 3. else the webpieces built-in ({@link WEBPIECES_DEFAULT_FAILURE_CLASSIFIER}), which never defers.\n * A `?? true` backstop keeps an unexpectedly-deferring built-in fail-safe (treat as failure).\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static classifyFailure(error: Error, methodInfo: ApiMethodInfo): boolean {\n const perClient = ClientRegistry.failureClassifiersByApiClass.get(methodInfo.apiClass);\n if (perClient !== undefined) {\n const verdict = perClient.isFailure(error, methodInfo);\n if (verdict !== undefined) {\n return verdict;\n }\n }\n if (ClientRegistry.appDefaultFailureClassifier !== undefined) {\n const verdict = ClientRegistry.appDefaultFailureClassifier.isFailure(error, methodInfo);\n if (verdict !== undefined) {\n return verdict;\n }\n }\n return WEBPIECES_DEFAULT_FAILURE_CLASSIFIER.isFailure(error, methodInfo) ?? true;\n }\n\n /**\n * Reset mappings, the deriver, error translations, AND failure classifiers. For tests, so the\n * process-globals do not leak across specs.\n */\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 ClientRegistry.appDefaultFailureClassifier = undefined;\n ClientRegistry.failureClassifiersByApiClass.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 { ErrorTranslators } from './ErrorTranslators';\nimport { HttpResponseDto } from './HttpResponseDto';\nimport { FailureClassifier } from './FailureClassifier';\nimport { WEBPIECES_DEFAULT_FAILURE_CLASSIFIER } from './WebpiecesDefaultFailureClassifier';\nimport { ApiMethodInfo } from './ApiMethodInfo';\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 * A `svcName` should be the nx MODULE name of the service you are calling — that is the one name\n * `architecture:validate-runtime-architecture` can verify actually exists, so a typo or a rename\n * fails the build instead of failing in production. When the DEPLOYED name differs (an environment\n * prefix like `tf-`, or a Cloud Run service named unlike its module), translate it in ONE place —\n * the deriver below, or a `serviceName` alias in the module's project.json — never at the call site.\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 * The app's ONE {@link ErrorTranslators}, consulted BEFORE webpieces' built-in error mapping in\n * BOTH directions. Process-global, set once at startup on the SERVER and in the BROWSER — the\n * same no-DI pattern as {@link ClientRegistry.mappings} above.\n *\n * ONE, not a list, on purpose: an app that has several layers of error policy composes them\n * INSIDE its own `toWire`, where the precedence is written down, instead of leaving it implicit\n * in the order two unrelated startup paths happened to register.\n */\n private static errorTranslators: ErrorTranslators | undefined;\n\n /**\n * The app/company DEFAULT {@link FailureClassifier} — ONE per process, reads {@link ApiMethodInfo.side}\n * so a single strategy covers the server router AND all internal clients. Undefined = use the\n * webpieces built-in ({@link WEBPIECES_DEFAULT_FAILURE_CLASSIFIER}). Populated once at startup on the\n * SERVER and in the BROWSER — same no-DI pattern as {@link ClientRegistry.mappings}.\n */\n private static appDefaultFailureClassifier: FailureClassifier | undefined;\n\n /**\n * Per-EXTERNAL-client {@link FailureClassifier}s, keyed by {@link ApiMethodInfo.apiClass}\n * ('FirestoreAdminClient', 'ClaudeApi', ...). Consulted BEFORE the default tier, so an external\n * client overrides the default for its own apiClass only. Internal clients / the server register\n * nothing here and fall through to the default. See {@link ClientRegistry.classifyFailure}.\n */\n private static readonly failureClassifiersByApiClass = new Map<string, FailureClassifier>();\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 ` - deployed name differs from the module name (e.g. a 'tf-' prefix)? Translate it ONCE\\n` +\n ` in the deriver — setDeriver(s => gcpCloudRunDeriver()('tf-' + s)) — so every call\\n` +\n ` site keeps naming the MODULE, which architecture:validate-runtime-architecture checks`,\n );\n }\n return url;\n }\n\n /**\n * Install the app's {@link ErrorTranslators} — the ONE symmetric owner of error translation for\n * this process, server side AND every client side. Consulted BEFORE webpieces' built-in mapping\n * in both directions. Call ONCE at startup, on the server AND in the browser, mirroring\n * {@link ClientRegistry.addMapping}.\n *\n * `set`, not `add`: see the field doc above for why a registry LIST is not wanted.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static setErrorTranslators(translators: ErrorTranslators): void {\n ClientRegistry.errorTranslators = translators;\n }\n\n /**\n * exception → the WHOLE response (SERVER side), or `undefined` when no translators are installed\n * or the installed ones do not claim `error` — the caller then falls through to the webpieces\n * default.\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): HttpResponseDto | undefined {\n return ClientRegistry.errorTranslators?.toWire(error);\n }\n\n /**\n * the WHOLE response → exception (CLIENT side), or `undefined` when no translators are installed\n * or the installed ones do not claim `response` — the caller then falls through to the built-in\n * webpieces status-to-type mapping.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static tryTranslateFromWire(response: HttpResponseDto): Error | undefined {\n return ClientRegistry.errorTranslators?.fromWire(response);\n }\n\n /**\n * Set the app/company DEFAULT failure classifier — ONE per process, covering the server router and\n * all internal clients (it branches on {@link ApiMethodInfo.side}). Call ONCE at startup, on the\n * server AND in the browser. Overrides the webpieces built-in for calls no per-apiClass classifier\n * claims. See {@link FailureClassifier}.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static setDefaultFailureClassifier(classifier: FailureClassifier): void {\n ClientRegistry.appDefaultFailureClassifier = classifier;\n }\n\n /**\n * Register a per-EXTERNAL-client failure classifier, keyed by {@link ApiMethodInfo.apiClass}\n * (e.g. 'FirestoreAdminClient'). Consulted BEFORE the default tier for calls with that apiClass.\n * Call ONCE at startup. See {@link FailureClassifier}.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static addFailureClassifier(apiClass: string, classifier: FailureClassifier): void {\n ClientRegistry.failureClassifiersByApiClass.set(apiClass, classifier);\n }\n\n /**\n * Is this thrown API-call error a real FAILURE (true) or an expected non-failure (false)?\n * Resolved most-specific-first, and ALWAYS definitive (returns a boolean):\n * 1. the per-apiClass classifier, if one is registered for `methodInfo.apiClass` — unless it defers;\n * 2. else the app default classifier, if one was set — unless it defers;\n * 3. else the webpieces built-in ({@link WEBPIECES_DEFAULT_FAILURE_CLASSIFIER}), which never defers.\n * A `?? true` backstop keeps an unexpectedly-deferring built-in fail-safe (treat as failure).\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/LogManager); populated once at startup, never DI-injected\n static classifyFailure(error: Error, methodInfo: ApiMethodInfo): boolean {\n const perClient = ClientRegistry.failureClassifiersByApiClass.get(methodInfo.apiClass);\n if (perClient !== undefined) {\n const verdict = perClient.isFailure(error, methodInfo);\n if (verdict !== undefined) {\n return verdict;\n }\n }\n if (ClientRegistry.appDefaultFailureClassifier !== undefined) {\n const verdict = ClientRegistry.appDefaultFailureClassifier.isFailure(error, methodInfo);\n if (verdict !== undefined) {\n return verdict;\n }\n }\n return WEBPIECES_DEFAULT_FAILURE_CLASSIFIER.isFailure(error, methodInfo) ?? true;\n }\n\n /**\n * Reset mappings, the deriver, the error translators, AND failure classifiers. For tests, so\n * the process-globals do not leak across specs.\n */\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.errorTranslators = undefined;\n ClientRegistry.appDefaultFailureClassifier = undefined;\n ClientRegistry.failureClassifiersByApiClass.clear();\n }\n}\n"]}
@@ -0,0 +1,80 @@
1
+ import { HttpResponseDto } from './HttpResponseDto';
2
+ /**
3
+ * ErrorTranslators - ONE symmetric place an app owns error translation, in BOTH directions, over the
4
+ * WHOLE response.
5
+ *
6
+ * An app implements this ONCE and registers it ONCE per process via
7
+ * {@link ClientRegistry.setErrorTranslators} — on the server AND in the browser. Its `toWire` runs on
8
+ * the SERVER (`ExpressWrapper.handleError`) and its `fromWire` runs on every CLIENT in that process
9
+ * (`ClientErrorTranslator.translateError`, shared by `http-client-node` and `http-client-browser`).
10
+ * The payoff is type symmetry across the wire: the server throws `OrderNotFoundError` and the caller
11
+ * CATCHES `OrderNotFoundError`, instead of decoding a status code by hand at every call site.
12
+ *
13
+ * # Both halves speak {@link HttpResponseDto}, and that is the point
14
+ *
15
+ * `toWire` PRODUCES exactly what `fromWire` CONSUMES. Reading the two methods against each other in
16
+ * one file is what makes a mistake visible, which is why this is one object rather than two
17
+ * separately-registered functions.
18
+ *
19
+ * # The whole response, not a status plus a body
20
+ *
21
+ * The previous contract could express only `(statusCode, protocolError)`, so an app could not put its
22
+ * own trace id, `Retry-After`, `WWW-Authenticate` or a cookie on an error response. A translator now
23
+ * returns the status code, the reason phrase, the header LIST and the body — everything.
24
+ *
25
+ * # `undefined` means "not mine"
26
+ *
27
+ * Either method returns `undefined` to step aside and let the webpieces default answer. There is
28
+ * exactly ONE registered ErrorTranslators per process (a `set`, not an `add` to a list): precedence
29
+ * between an app's own layers is the APP's to compose explicitly inside its `toWire`, rather than
30
+ * something hidden in registration order inside the framework.
31
+ *
32
+ * # Not covered: 404 / unknown route
33
+ *
34
+ * A request that matches no route has no route context and never reaches this seam. That is
35
+ * deliberate and out of scope.
36
+ *
37
+ * This is a business-logic contract (methods, not data), so it is an interface per the webpieces
38
+ * guidelines.
39
+ *
40
+ * ```ts
41
+ * export class OrderErrorTranslators implements ErrorTranslators {
42
+ * toWire(error: Error): HttpResponseDto | undefined {
43
+ * if (!(error instanceof OrderNotFoundError)) {
44
+ * return undefined; // not mine -> webpieces default
45
+ * }
46
+ * const body = new ProtocolError();
47
+ * body.message = error.message;
48
+ * body.errorCode = 'ORDER_NOT_FOUND';
49
+ * return new HttpResponseDto(
50
+ * new HttpResponseStatus(460, 'Order Not Found'),
51
+ * [new HttpHeader('x-order-trace', error.traceId)],
52
+ * body,
53
+ * );
54
+ * }
55
+ *
56
+ * fromWire(response: HttpResponseDto): Error | undefined {
57
+ * if (response.status.code !== 460) {
58
+ * return undefined; // not mine -> webpieces default
59
+ * }
60
+ * return new OrderNotFoundError(String(response.body));
61
+ * }
62
+ * }
63
+ *
64
+ * // startup, ONCE per process (server AND browser)
65
+ * ClientRegistry.setErrorTranslators(new OrderErrorTranslators());
66
+ * ```
67
+ */
68
+ export interface ErrorTranslators {
69
+ /**
70
+ * SERVER: exception -> the ENTIRE response (status code, reason phrase, headers, body).
71
+ * `undefined` => this translator does not claim `error`; webpieces' default answers instead.
72
+ */
73
+ toWire(error: Error): HttpResponseDto | undefined;
74
+ /**
75
+ * CLIENT: the ENTIRE response -> a typed exception. Receives the SAME shape `toWire` produces,
76
+ * normalised from whichever transport read it. `undefined` => not claimed; webpieces' built-in
77
+ * status-to-type mapping answers instead.
78
+ */
79
+ fromWire(response: HttpResponseDto): Error | undefined;
80
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=ErrorTranslators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorTranslators.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ErrorTranslators.ts"],"names":[],"mappings":"","sourcesContent":["import { HttpResponseDto } from './HttpResponseDto';\n\n/**\n * ErrorTranslators - ONE symmetric place an app owns error translation, in BOTH directions, over the\n * WHOLE response.\n *\n * An app implements this ONCE and registers it ONCE per process via\n * {@link ClientRegistry.setErrorTranslators} — on the server AND in the browser. Its `toWire` runs on\n * the SERVER (`ExpressWrapper.handleError`) and its `fromWire` runs on every CLIENT in that process\n * (`ClientErrorTranslator.translateError`, shared by `http-client-node` and `http-client-browser`).\n * The payoff is type symmetry across the wire: the server throws `OrderNotFoundError` and the caller\n * CATCHES `OrderNotFoundError`, instead of decoding a status code by hand at every call site.\n *\n * # Both halves speak {@link HttpResponseDto}, and that is the point\n *\n * `toWire` PRODUCES exactly what `fromWire` CONSUMES. Reading the two methods against each other in\n * one file is what makes a mistake visible, which is why this is one object rather than two\n * separately-registered functions.\n *\n * # The whole response, not a status plus a body\n *\n * The previous contract could express only `(statusCode, protocolError)`, so an app could not put its\n * own trace id, `Retry-After`, `WWW-Authenticate` or a cookie on an error response. A translator now\n * returns the status code, the reason phrase, the header LIST and the body — everything.\n *\n * # `undefined` means \"not mine\"\n *\n * Either method returns `undefined` to step aside and let the webpieces default answer. There is\n * exactly ONE registered ErrorTranslators per process (a `set`, not an `add` to a list): precedence\n * between an app's own layers is the APP's to compose explicitly inside its `toWire`, rather than\n * something hidden in registration order inside the framework.\n *\n * # Not covered: 404 / unknown route\n *\n * A request that matches no route has no route context and never reaches this seam. That is\n * deliberate and out of scope.\n *\n * This is a business-logic contract (methods, not data), so it is an interface per the webpieces\n * guidelines.\n *\n * ```ts\n * export class OrderErrorTranslators implements ErrorTranslators {\n * toWire(error: Error): HttpResponseDto | undefined {\n * if (!(error instanceof OrderNotFoundError)) {\n * return undefined; // not mine -> webpieces default\n * }\n * const body = new ProtocolError();\n * body.message = error.message;\n * body.errorCode = 'ORDER_NOT_FOUND';\n * return new HttpResponseDto(\n * new HttpResponseStatus(460, 'Order Not Found'),\n * [new HttpHeader('x-order-trace', error.traceId)],\n * body,\n * );\n * }\n *\n * fromWire(response: HttpResponseDto): Error | undefined {\n * if (response.status.code !== 460) {\n * return undefined; // not mine -> webpieces default\n * }\n * return new OrderNotFoundError(String(response.body));\n * }\n * }\n *\n * // startup, ONCE per process (server AND browser)\n * ClientRegistry.setErrorTranslators(new OrderErrorTranslators());\n * ```\n */\nexport interface ErrorTranslators {\n /**\n * SERVER: exception -> the ENTIRE response (status code, reason phrase, headers, body).\n * `undefined` => this translator does not claim `error`; webpieces' default answers instead.\n */\n toWire(error: Error): HttpResponseDto | undefined;\n\n /**\n * CLIENT: the ENTIRE response -> a typed exception. Receives the SAME shape `toWire` produces,\n * normalised from whichever transport read it. `undefined` => not claimed; webpieces' built-in\n * status-to-type mapping answers instead.\n */\n fromWire(response: HttpResponseDto): Error | undefined;\n}\n"]}
@@ -9,7 +9,7 @@ import { ApiMethodInfo } from './ApiMethodInfo';
9
9
  * rather than a bare function — so "find usages" in an IDE lands on every implementation.
10
10
  *
11
11
  * Registered on {@link ClientRegistry} at startup — the same browser-safe, no-DI, populated-once
12
- * singleton that owns URL mappings and {@link ErrorTranslation}. TWO tiers, resolved most-specific
12
+ * singleton that owns URL mappings and {@link ErrorTranslators}. TWO tiers, resolved most-specific
13
13
  * first (see {@link ClientRegistry.classifyFailure}):
14
14
  *
15
15
  * 1. `ClientRegistry.setDefaultFailureClassifier(c)` — ONE per app/company. It reads
@@ -1 +1 @@
1
- {"version":3,"file":"FailureClassifier.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/FailureClassifier.ts"],"names":[],"mappings":";;;AAuCA;;;;;GAKG;AACH,MAAa,sBAAsB;IAEX;IACA;IAFpB,YACoB,QAAgB,EAChB,UAA6B;QAD7B,aAAQ,GAAR,QAAQ,CAAQ;QAChB,eAAU,GAAV,UAAU,CAAmB;IAC9C,CAAC;CACP;AALD,wDAKC","sourcesContent":["import { ApiMethodInfo } from './ApiMethodInfo';\n\n/**\n * Decides whether a thrown API-call error is a real FAILURE — the process not working, SURFACE it\n * (LogApiCall logs `[API-*-resp-FAIL]`, `jsonPayload.api.result='failure'` — what dashboards/alerts\n * count) — or an EXPECTED non-failure — the process working correctly, a handled condition\n * (`[API-*-resp-OTHER]`, `result='success'`).\n *\n * This is BEHAVIOR, so it is an interface (per CLAUDE.md), passed as an object with a NAMED method\n * rather than a bare function — so \"find usages\" in an IDE lands on every implementation.\n *\n * Registered on {@link ClientRegistry} at startup — the same browser-safe, no-DI, populated-once\n * singleton that owns URL mappings and {@link ErrorTranslation}. TWO tiers, resolved most-specific\n * first (see {@link ClientRegistry.classifyFailure}):\n *\n * 1. `ClientRegistry.setDefaultFailureClassifier(c)` — ONE per app/company. It reads\n * {@link ApiMethodInfo.side}, so a single strategy covers the SERVER router AND all INTERNAL\n * clients (webpieces http client, cloud tasks). Optional: if unset, webpieces uses\n * {@link WebpiecesDefaultFailureClassifier} (server 4xx = non-failure; client non-266 = failure).\n * 2. `ClientRegistry.addFailureClassifier(apiClass, c)` — per EXTERNAL client, keyed by\n * {@link ApiMethodInfo.apiClass} ('FirestoreAdminClient', 'ClaudeApi', 'TwilioApi'). Each external\n * API qualifies errors differently (a Firestore 404 miss, a Twilio 429 retry are EXPECTED, not\n * failures), so it overrides the default for that apiClass only.\n *\n * Internal clients and the server register NOTHING — the ABSENCE of an apiClass entry is the signal;\n * they fall through to tier 1. An external client that forgets to register is FAIL-SAFE: it also\n * falls to tier 1 (client side ⇒ every non-266 = failure) until it opts in.\n */\nexport interface FailureClassifier {\n /**\n * @param error - the already-normalized thrown error (callers pass `toError(err)`)\n * @param methodInfo - the call identity; `side` distinguishes server/client, `apiClass` the client\n * @returns `true` = real failure; `false` = expected/non-failure; `undefined` = DEFER\n * (a per-apiClass classifier defers to the app default; the app default defers to the\n * webpieces built-in). Deferring is what makes classifiers additive AND override-capable.\n */\n isFailure(error: Error, methodInfo: ApiMethodInfo): boolean | undefined;\n}\n\n/**\n * A per-external-client registration pair: the {@link ApiMethodInfo.apiClass} to key on, and the\n * {@link FailureClassifier} to apply for it. A data-only structure, so it is a class (not an inline\n * object literal), per CLAUDE.md — lets startup wiring carry a list of these and hand each to\n * {@link ClientRegistry.addFailureClassifier}.\n */\nexport class KeyedFailureClassifier {\n constructor(\n public readonly apiClass: string,\n public readonly classifier: FailureClassifier,\n ) {}\n}\n"]}
1
+ {"version":3,"file":"FailureClassifier.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/FailureClassifier.ts"],"names":[],"mappings":";;;AAuCA;;;;;GAKG;AACH,MAAa,sBAAsB;IAEX;IACA;IAFpB,YACoB,QAAgB,EAChB,UAA6B;QAD7B,aAAQ,GAAR,QAAQ,CAAQ;QAChB,eAAU,GAAV,UAAU,CAAmB;IAC9C,CAAC;CACP;AALD,wDAKC","sourcesContent":["import { ApiMethodInfo } from './ApiMethodInfo';\n\n/**\n * Decides whether a thrown API-call error is a real FAILURE — the process not working, SURFACE it\n * (LogApiCall logs `[API-*-resp-FAIL]`, `jsonPayload.api.result='failure'` — what dashboards/alerts\n * count) — or an EXPECTED non-failure — the process working correctly, a handled condition\n * (`[API-*-resp-OTHER]`, `result='success'`).\n *\n * This is BEHAVIOR, so it is an interface (per CLAUDE.md), passed as an object with a NAMED method\n * rather than a bare function — so \"find usages\" in an IDE lands on every implementation.\n *\n * Registered on {@link ClientRegistry} at startup — the same browser-safe, no-DI, populated-once\n * singleton that owns URL mappings and {@link ErrorTranslators}. TWO tiers, resolved most-specific\n * first (see {@link ClientRegistry.classifyFailure}):\n *\n * 1. `ClientRegistry.setDefaultFailureClassifier(c)` — ONE per app/company. It reads\n * {@link ApiMethodInfo.side}, so a single strategy covers the SERVER router AND all INTERNAL\n * clients (webpieces http client, cloud tasks). Optional: if unset, webpieces uses\n * {@link WebpiecesDefaultFailureClassifier} (server 4xx = non-failure; client non-266 = failure).\n * 2. `ClientRegistry.addFailureClassifier(apiClass, c)` — per EXTERNAL client, keyed by\n * {@link ApiMethodInfo.apiClass} ('FirestoreAdminClient', 'ClaudeApi', 'TwilioApi'). Each external\n * API qualifies errors differently (a Firestore 404 miss, a Twilio 429 retry are EXPECTED, not\n * failures), so it overrides the default for that apiClass only.\n *\n * Internal clients and the server register NOTHING — the ABSENCE of an apiClass entry is the signal;\n * they fall through to tier 1. An external client that forgets to register is FAIL-SAFE: it also\n * falls to tier 1 (client side ⇒ every non-266 = failure) until it opts in.\n */\nexport interface FailureClassifier {\n /**\n * @param error - the already-normalized thrown error (callers pass `toError(err)`)\n * @param methodInfo - the call identity; `side` distinguishes server/client, `apiClass` the client\n * @returns `true` = real failure; `false` = expected/non-failure; `undefined` = DEFER\n * (a per-apiClass classifier defers to the app default; the app default defers to the\n * webpieces built-in). Deferring is what makes classifiers additive AND override-capable.\n */\n isFailure(error: Error, methodInfo: ApiMethodInfo): boolean | undefined;\n}\n\n/**\n * A per-external-client registration pair: the {@link ApiMethodInfo.apiClass} to key on, and the\n * {@link FailureClassifier} to apply for it. A data-only structure, so it is a class (not an inline\n * object literal), per CLAUDE.md — lets startup wiring carry a list of these and hand each to\n * {@link ClientRegistry.addFailureClassifier}.\n */\nexport class KeyedFailureClassifier {\n constructor(\n public readonly apiClass: string,\n public readonly classifier: FailureClassifier,\n ) {}\n}\n"]}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * HttpResponseDto - the ENTIRE HTTP response, as pure data, in the ONE form webpieces speaks.
3
+ *
4
+ * Modelled on java webpieces' `http/http1_1-parser/.../api/dto/` (`HttpPayload` -> `HttpMessage` ->
5
+ * `HttpResponse` -> `HttpResponseStatusLine` -> `HttpResponseStatus`), flattened to the KISS subset
6
+ * this framework actually needs.
7
+ *
8
+ * # Why a DTO at all, instead of handing an app express's `res` or fetch's `Response`
9
+ *
10
+ * Node/express and browser fetch model a response completely differently, and an
11
+ * {@link ErrorTranslators} implementation is registered ONCE and serves BOTH — the server writing a
12
+ * response and every client in the process reading one. So neither transport's object can be the
13
+ * currency. webpieces normalises both into this DTO at its own boundary, and the app only ever sees
14
+ * this. One form, both transports, both directions.
15
+ *
16
+ * # The two properties copied from java deliberately
17
+ *
18
+ * - **Headers are a LIST of `{name, value}`, not a Map.** HTTP permits repeats (`Set-Cookie` is the
19
+ * everyday one) and a Map silently drops all but the last. The REQUEST side of webpieces-ts
20
+ * already respects this (`readExpressHeaders` returns `Map<string, string[]>`), so the response
21
+ * side must not be the half that loses data.
22
+ * - **Status is `{ code, reason }`, not a bare number.** The reason phrase is part of the response
23
+ * and an app may want its own ('Order Not Found' beside a 460).
24
+ *
25
+ * `HttpVersion` from the java DTO is deliberately OMITTED: express and fetch each own the version,
26
+ * and no app decision depends on it.
27
+ *
28
+ * Pure data, so these are CLASSES with explicit constructors (webpieces guideline: data => classes),
29
+ * and they live in core-util so the identical types reach a node server and a browser bundle.
30
+ */
31
+ export declare class HttpHeader {
32
+ readonly name: string;
33
+ readonly value: string;
34
+ constructor(name: string, value: string);
35
+ }
36
+ /** The status line's status: the numeric code AND the reason phrase that goes beside it. */
37
+ export declare class HttpResponseStatus {
38
+ readonly code: number;
39
+ readonly reason: string;
40
+ constructor(code: number, reason: string);
41
+ }
42
+ /**
43
+ * The whole response: status (code + reason), the header LIST, and the body.
44
+ *
45
+ * `body` is `unknown` because it is whatever the app chose to publish. webpieces' OWN default puts a
46
+ * {@link ProtocolError} there, and the built-in client mapping reads it back as one — but an app that
47
+ * owns the whole response owns the body shape too, so the framework does not constrain it.
48
+ */
49
+ export declare class HttpResponseDto {
50
+ readonly status: HttpResponseStatus;
51
+ readonly headers: readonly HttpHeader[];
52
+ readonly body: unknown;
53
+ constructor(status: HttpResponseStatus, headers: readonly HttpHeader[], body: unknown);
54
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpResponseDto = exports.HttpResponseStatus = exports.HttpHeader = void 0;
4
+ /**
5
+ * HttpResponseDto - the ENTIRE HTTP response, as pure data, in the ONE form webpieces speaks.
6
+ *
7
+ * Modelled on java webpieces' `http/http1_1-parser/.../api/dto/` (`HttpPayload` -> `HttpMessage` ->
8
+ * `HttpResponse` -> `HttpResponseStatusLine` -> `HttpResponseStatus`), flattened to the KISS subset
9
+ * this framework actually needs.
10
+ *
11
+ * # Why a DTO at all, instead of handing an app express's `res` or fetch's `Response`
12
+ *
13
+ * Node/express and browser fetch model a response completely differently, and an
14
+ * {@link ErrorTranslators} implementation is registered ONCE and serves BOTH — the server writing a
15
+ * response and every client in the process reading one. So neither transport's object can be the
16
+ * currency. webpieces normalises both into this DTO at its own boundary, and the app only ever sees
17
+ * this. One form, both transports, both directions.
18
+ *
19
+ * # The two properties copied from java deliberately
20
+ *
21
+ * - **Headers are a LIST of `{name, value}`, not a Map.** HTTP permits repeats (`Set-Cookie` is the
22
+ * everyday one) and a Map silently drops all but the last. The REQUEST side of webpieces-ts
23
+ * already respects this (`readExpressHeaders` returns `Map<string, string[]>`), so the response
24
+ * side must not be the half that loses data.
25
+ * - **Status is `{ code, reason }`, not a bare number.** The reason phrase is part of the response
26
+ * and an app may want its own ('Order Not Found' beside a 460).
27
+ *
28
+ * `HttpVersion` from the java DTO is deliberately OMITTED: express and fetch each own the version,
29
+ * and no app decision depends on it.
30
+ *
31
+ * Pure data, so these are CLASSES with explicit constructors (webpieces guideline: data => classes),
32
+ * and they live in core-util so the identical types reach a node server and a browser bundle.
33
+ */
34
+ class HttpHeader {
35
+ name;
36
+ value;
37
+ constructor(name, value) {
38
+ this.name = name;
39
+ this.value = value;
40
+ }
41
+ }
42
+ exports.HttpHeader = HttpHeader;
43
+ /** The status line's status: the numeric code AND the reason phrase that goes beside it. */
44
+ class HttpResponseStatus {
45
+ code;
46
+ reason;
47
+ constructor(code, reason) {
48
+ this.code = code;
49
+ this.reason = reason;
50
+ }
51
+ }
52
+ exports.HttpResponseStatus = HttpResponseStatus;
53
+ /**
54
+ * The whole response: status (code + reason), the header LIST, and the body.
55
+ *
56
+ * `body` is `unknown` because it is whatever the app chose to publish. webpieces' OWN default puts a
57
+ * {@link ProtocolError} there, and the built-in client mapping reads it back as one — but an app that
58
+ * owns the whole response owns the body shape too, so the framework does not constrain it.
59
+ */
60
+ class HttpResponseDto {
61
+ status;
62
+ headers;
63
+ body;
64
+ constructor(status, headers,
65
+ // webpieces-disable no-any-unknown -- the app owns the body shape when it owns the response; webpieces' own default puts a ProtocolError here, an app puts whatever it publishes
66
+ body) {
67
+ this.status = status;
68
+ this.headers = headers;
69
+ this.body = body;
70
+ }
71
+ }
72
+ exports.HttpResponseDto = HttpResponseDto;
73
+ //# sourceMappingURL=HttpResponseDto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpResponseDto.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/HttpResponseDto.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAa,UAAU;IAEC;IACA;IAFpB,YACoB,IAAY,EACZ,KAAa;QADb,SAAI,GAAJ,IAAI,CAAQ;QACZ,UAAK,GAAL,KAAK,CAAQ;IAC9B,CAAC;CACP;AALD,gCAKC;AAED,4FAA4F;AAC5F,MAAa,kBAAkB;IAEP;IACA;IAFpB,YACoB,IAAY,EACZ,MAAc;QADd,SAAI,GAAJ,IAAI,CAAQ;QACZ,WAAM,GAAN,MAAM,CAAQ;IAC/B,CAAC;CACP;AALD,gDAKC;AAED;;;;;;GAMG;AACH,MAAa,eAAe;IAEJ;IACA;IAEA;IAJpB,YACoB,MAA0B,EAC1B,OAA8B;IAC9C,iLAAiL;IACjK,IAAa;QAHb,WAAM,GAAN,MAAM,CAAoB;QAC1B,YAAO,GAAP,OAAO,CAAuB;QAE9B,SAAI,GAAJ,IAAI,CAAS;IAC9B,CAAC;CACP;AAPD,0CAOC","sourcesContent":["/**\n * HttpResponseDto - the ENTIRE HTTP response, as pure data, in the ONE form webpieces speaks.\n *\n * Modelled on java webpieces' `http/http1_1-parser/.../api/dto/` (`HttpPayload` -> `HttpMessage` ->\n * `HttpResponse` -> `HttpResponseStatusLine` -> `HttpResponseStatus`), flattened to the KISS subset\n * this framework actually needs.\n *\n * # Why a DTO at all, instead of handing an app express's `res` or fetch's `Response`\n *\n * Node/express and browser fetch model a response completely differently, and an\n * {@link ErrorTranslators} implementation is registered ONCE and serves BOTH — the server writing a\n * response and every client in the process reading one. So neither transport's object can be the\n * currency. webpieces normalises both into this DTO at its own boundary, and the app only ever sees\n * this. One form, both transports, both directions.\n *\n * # The two properties copied from java deliberately\n *\n * - **Headers are a LIST of `{name, value}`, not a Map.** HTTP permits repeats (`Set-Cookie` is the\n * everyday one) and a Map silently drops all but the last. The REQUEST side of webpieces-ts\n * already respects this (`readExpressHeaders` returns `Map<string, string[]>`), so the response\n * side must not be the half that loses data.\n * - **Status is `{ code, reason }`, not a bare number.** The reason phrase is part of the response\n * and an app may want its own ('Order Not Found' beside a 460).\n *\n * `HttpVersion` from the java DTO is deliberately OMITTED: express and fetch each own the version,\n * and no app decision depends on it.\n *\n * Pure data, so these are CLASSES with explicit constructors (webpieces guideline: data => classes),\n * and they live in core-util so the identical types reach a node server and a browser bundle.\n */\nexport class HttpHeader {\n constructor(\n public readonly name: string,\n public readonly value: string,\n ) {}\n}\n\n/** The status line's status: the numeric code AND the reason phrase that goes beside it. */\nexport class HttpResponseStatus {\n constructor(\n public readonly code: number,\n public readonly reason: string,\n ) {}\n}\n\n/**\n * The whole response: status (code + reason), the header LIST, and the body.\n *\n * `body` is `unknown` because it is whatever the app chose to publish. webpieces' OWN default puts a\n * {@link ProtocolError} there, and the built-in client mapping reads it back as one — but an app that\n * owns the whole response owns the body shape too, so the framework does not constrain it.\n */\nexport class HttpResponseDto {\n constructor(\n public readonly status: HttpResponseStatus,\n public readonly headers: readonly HttpHeader[],\n // webpieces-disable no-any-unknown -- the app owns the body shape when it owns the response; webpieces' own default puts a ProtocolError here, an app puts whatever it publishes\n public readonly body: unknown,\n ) {}\n}\n"]}
@@ -12,8 +12,8 @@
12
12
  * other type, because `Error.message` is an operator-facing field that routinely quotes downstream
13
13
  * urls, response bodies and internal ids. `name` is not filled by that ladder at all.
14
14
  *
15
- * An app that wants to publish more than that does it deliberately, by registering an
16
- * {@link ErrorTranslation} with {@link ClientRegistry}, whose `toWire()` result is sent verbatim.
15
+ * An app that wants to publish more than that does it deliberately, by installing an
16
+ * {@link ErrorTranslators} on {@link ClientRegistry}, whose `toWire()` response is sent verbatim.
17
17
  */
18
18
  export declare class ProtocolError {
19
19
  message?: string;
@@ -21,7 +21,7 @@ export declare class ProtocolError {
21
21
  field?: string;
22
22
  waitSeconds?: number;
23
23
  /**
24
- * Filled only by an app's own `ErrorTranslation.toWire()`. The built-in HttpError ladder does NOT
24
+ * Filled only by an app's own `ErrorTranslators.toWire()`. The built-in HttpError ladder does NOT
25
25
  * send it: nothing on the client reads it, and for a subclass it is an internal class name.
26
26
  */
27
27
  name?: string;
@@ -15,8 +15,8 @@ exports.OfflineError = exports.HttpUserError = exports.HttpVendorError = exports
15
15
  * other type, because `Error.message` is an operator-facing field that routinely quotes downstream
16
16
  * urls, response bodies and internal ids. `name` is not filled by that ladder at all.
17
17
  *
18
- * An app that wants to publish more than that does it deliberately, by registering an
19
- * {@link ErrorTranslation} with {@link ClientRegistry}, whose `toWire()` result is sent verbatim.
18
+ * An app that wants to publish more than that does it deliberately, by installing an
19
+ * {@link ErrorTranslators} on {@link ClientRegistry}, whose `toWire()` response is sent verbatim.
20
20
  */
21
21
  class ProtocolError {
22
22
  message;
@@ -24,7 +24,7 @@ class ProtocolError {
24
24
  field;
25
25
  waitSeconds;
26
26
  /**
27
- * Filled only by an app's own `ErrorTranslation.toWire()`. The built-in HttpError ladder does NOT
27
+ * Filled only by an app's own `ErrorTranslators.toWire()`. The built-in HttpError ladder does NOT
28
28
  * send it: nothing on the client reads it, and for a subclass it is an internal class name.
29
29
  */
30
30
  name;
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/errors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * HTTP Error classes for webpieces-ts.\n * These errors are used throughout the framework for consistent error handling.\n */\n\n/**\n * ProtocolError - Data class for error response body.\n * This is what gets serialized and sent to the client.\n *\n * EVERY field here is caller-facing, so the framework fills it CONSERVATIVELY. `HttpErrorWireMapper`\n * (http-server) copies `message` from the thrown error for {@link HttpUserError} ONLY — the one type\n * whose message is written for a human to read — and sends the generic HTTP reason phrase for every\n * other type, because `Error.message` is an operator-facing field that routinely quotes downstream\n * urls, response bodies and internal ids. `name` is not filled by that ladder at all.\n *\n * An app that wants to publish more than that does it deliberately, by registering an\n * {@link ErrorTranslation} with {@link ClientRegistry}, whose `toWire()` result is sent verbatim.\n */\nexport class ProtocolError {\n public message?: string;\n public subType?: string;\n public field?: string;\n public waitSeconds?: number;\n /**\n * Filled only by an app's own `ErrorTranslation.toWire()`. The built-in HttpError ladder does NOT\n * send it: nothing on the client reads it, and for a subclass it is an internal class name.\n */\n public name?: string;\n public guiAlertMessage?: string;\n public errorCode?: string;\n}\n\n/**\n * HttpError - Base error class with HTTP status code.\n * All specific HTTP errors extend this class.\n */\nexport class HttpError extends Error {\n public code: number;\n public subType?: string;\n public readonly httpCause?: Error;\n\n constructor(\n message: string,\n code: number,\n subType?: string,\n cause?: Error,\n ) {\n super(message);\n this.code = code;\n this.subType = subType;\n this.httpCause = cause;\n }\n}\n\n// Error subtype constants\nexport const ENTITY_NOT_FOUND = 'EntityNotFoundError';\nexport const WRONG_LOGIN_TYPE = 'wrongLoginType';\nexport const WRONG_LOGIN = 'wronglogin';\nexport const NOT_APPROVED = 'notapproved';\nexport const EMAIL_NOT_CONFIRMED = 'email_not_confirmed';\nexport const WRONG_DOMAIN = 'wrongdomain';\nexport const WRONG_COMPANY = 'wrongcompany';\nexport const NO_REG_CODE = 'noregcode';\n\n/**\n * HttpNotFoundError - 404 Not Found.\n */\nexport class HttpNotFoundError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 404, undefined, cause);\n this.name = ENTITY_NOT_FOUND;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * EndpointNotFoundError - 404 for missing endpoints.\n */\nexport class EndpointNotFoundError extends HttpNotFoundError {\n constructor(message: string, cause?: Error) {\n super(message, cause);\n this.name = 'EndpointNotFoundError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpBadRequestError - 400 Bad Request.\n * Used for validation errors with optional field and GUI message.\n */\nexport class HttpBadRequestError extends HttpError {\n public field?: string;\n public guiMessage?: string;\n\n constructor(message: string, field?: string, guiMessage?: string, cause?: Error) {\n super(message, 400, undefined, cause);\n this.name = 'BadRequest';\n this.field = field;\n this.guiMessage = guiMessage;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpUnauthorizedError - 401 Unauthorized.\n */\nexport class HttpUnauthorizedError extends HttpError {\n constructor(message: string, subType?: string, cause?: Error) {\n super(message, 401, subType, cause);\n this.name = 'Unauthorized';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpTooManyRequestsError - 429 Too Many Requests.\n *\n * The one member of the HttpError ladder that never made it over from the production service this ladder was ported from. Without it, apps are\n * forced back to `err.code === 429` — the exact untyped pattern this ladder exists to replace.\n */\nexport class HttpTooManyRequestsError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 429, undefined, cause);\n this.name = 'TooManyRequests';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpForbiddenError - 403 Forbidden.\n */\nexport class HttpForbiddenError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 403, undefined, cause);\n this.name = 'Forbidden';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpTimeoutError - 408 Request Timeout.\n */\nexport class HttpTimeoutError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 408, undefined, cause);\n this.name = 'Timeout';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpBadGatewayError - 502 Bad Gateway.\n */\nexport class HttpBadGatewayError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 502, undefined, cause);\n this.name = 'HttpBadGatewayError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpServiceUnavailableError - 503 Service Unavailable.\n *\n * The cold-start code: a scale-to-zero backend (Cloud Run `min_instance_count = 0`) whose instance\n * is still booting is answered by the load balancer with a 503 and ITS OWN HTML page — no\n * ProtocolError body at all. Without this member the client fell through to a generic `HttpError`,\n * so an app could not say \"the server is waking, retry\" without matching on `err.code === 503`,\n * which is the untyped pattern this ladder exists to replace.\n */\nexport class HttpServiceUnavailableError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 503, undefined, cause);\n this.name = 'HttpServiceUnavailableError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpGatewayTimeoutError - 504 Gateway Timeout.\n * SHOULD NOT BE USED SERVER SIDE SINCE ALBs will return 504 and it will not be translated\n * to json body 'ProtocolError'.\n */\nexport class HttpGatewayTimeoutError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 504, undefined, cause);\n this.name = 'HttpGatewayTimeoutError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpInternalServerError - 500 Internal Server Error.\n */\nexport class HttpInternalServerError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 500, undefined, cause);\n this.name = 'InternalServerError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpVendorError - 598 Vendor Error.\n * Custom status code for vendor/external service errors with retry hint.\n */\nexport class HttpVendorError extends HttpError {\n constructor(\n message: string,\n public waitSeconds = 30,\n cause?: Error,\n ) {\n super(message, 598, undefined, cause);\n this.name = 'VendorError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpUserError - User validation error with 2xx status code.\n *\n * Uses HTTP 266 (non-standard 2xx code) intentionally because:\n * 1. User validation errors are \"successful\" from server perspective - user just made a mistake\n * 2. Browser DevTools show 4xx/5xx codes in RED, which is confusing for user validation\n * 3. Allows error to propagate up the stack via throw without triggering error monitoring\n * 4. Avoids polluting logs with \"errors\" that are actually expected user behavior\n *\n * This is a deliberate design pattern - do NOT change to 4xx codes.\n * Examples: \"Email already exists\", \"Invalid password format\", \"Required field missing\"\n *\n * # It is also the ONLY type whose `message` reaches the caller\n *\n * `HttpErrorWireMapper` (http-server) copies `message` onto the response body for this type and no\n * other; every other subclass sends the generic HTTP reason phrase for its status. That is not an\n * arbitrary exception — it follows from the four points above. This type MEANS \"this text was written\n * for a human to read\", where `Error.message` everywhere else means \"this text was written for whoever\n * reads the logs\" and routinely quotes downstream urls, response bodies and internal ids.\n *\n * So: text the user must SEE goes in an `HttpUserError` (or in `HttpBadRequestError.guiMessage`,\n * which is the same idea one field down). Throwing `new HttpForbiddenError('you need the admin role\n * on tenant 4471')` shows the user 'Forbidden' and nothing else.\n */\nexport class HttpUserError extends HttpError {\n public errorCode?: string;\n\n constructor(message: string, errorCode?: string, cause?: Error) {\n super(message, 266, 'USER_ERROR', cause);\n this.name = 'UserError';\n this.errorCode = errorCode;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * OfflineError - the request never reached a server.\n *\n * Offline, DNS failure, connection refused, CORS preflight rejected: `fetch` itself rejected, so no\n * Response — and therefore no HTTP status — ever existed. That is precisely why this extends `Error`\n * and NOT `HttpError`: an `HttpError` carries a `code` and means \"the server replied with a failure\",\n * a different situation a caller usually retries differently. Subclassing it would give this a bogus\n * status and make it match `instanceof HttpError` ladders that must not catch a transport reject.\n *\n * The original failure (a raw `TypeError: Failed to fetch`, or undici's coded reject) is always\n * preserved as `cause`, so a caller that wants the underlying detail can still reach it.\n */\nexport class OfflineError extends Error {\n constructor(message: string, cause?: Error) {\n super(message, { cause });\n this.name = 'OfflineError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n"]}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/errors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * HTTP Error classes for webpieces-ts.\n * These errors are used throughout the framework for consistent error handling.\n */\n\n/**\n * ProtocolError - Data class for error response body.\n * This is what gets serialized and sent to the client.\n *\n * EVERY field here is caller-facing, so the framework fills it CONSERVATIVELY. `HttpErrorWireMapper`\n * (http-server) copies `message` from the thrown error for {@link HttpUserError} ONLY — the one type\n * whose message is written for a human to read — and sends the generic HTTP reason phrase for every\n * other type, because `Error.message` is an operator-facing field that routinely quotes downstream\n * urls, response bodies and internal ids. `name` is not filled by that ladder at all.\n *\n * An app that wants to publish more than that does it deliberately, by installing an\n * {@link ErrorTranslators} on {@link ClientRegistry}, whose `toWire()` response is sent verbatim.\n */\nexport class ProtocolError {\n public message?: string;\n public subType?: string;\n public field?: string;\n public waitSeconds?: number;\n /**\n * Filled only by an app's own `ErrorTranslators.toWire()`. The built-in HttpError ladder does NOT\n * send it: nothing on the client reads it, and for a subclass it is an internal class name.\n */\n public name?: string;\n public guiAlertMessage?: string;\n public errorCode?: string;\n}\n\n/**\n * HttpError - Base error class with HTTP status code.\n * All specific HTTP errors extend this class.\n */\nexport class HttpError extends Error {\n public code: number;\n public subType?: string;\n public readonly httpCause?: Error;\n\n constructor(\n message: string,\n code: number,\n subType?: string,\n cause?: Error,\n ) {\n super(message);\n this.code = code;\n this.subType = subType;\n this.httpCause = cause;\n }\n}\n\n// Error subtype constants\nexport const ENTITY_NOT_FOUND = 'EntityNotFoundError';\nexport const WRONG_LOGIN_TYPE = 'wrongLoginType';\nexport const WRONG_LOGIN = 'wronglogin';\nexport const NOT_APPROVED = 'notapproved';\nexport const EMAIL_NOT_CONFIRMED = 'email_not_confirmed';\nexport const WRONG_DOMAIN = 'wrongdomain';\nexport const WRONG_COMPANY = 'wrongcompany';\nexport const NO_REG_CODE = 'noregcode';\n\n/**\n * HttpNotFoundError - 404 Not Found.\n */\nexport class HttpNotFoundError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 404, undefined, cause);\n this.name = ENTITY_NOT_FOUND;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * EndpointNotFoundError - 404 for missing endpoints.\n */\nexport class EndpointNotFoundError extends HttpNotFoundError {\n constructor(message: string, cause?: Error) {\n super(message, cause);\n this.name = 'EndpointNotFoundError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpBadRequestError - 400 Bad Request.\n * Used for validation errors with optional field and GUI message.\n */\nexport class HttpBadRequestError extends HttpError {\n public field?: string;\n public guiMessage?: string;\n\n constructor(message: string, field?: string, guiMessage?: string, cause?: Error) {\n super(message, 400, undefined, cause);\n this.name = 'BadRequest';\n this.field = field;\n this.guiMessage = guiMessage;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpUnauthorizedError - 401 Unauthorized.\n */\nexport class HttpUnauthorizedError extends HttpError {\n constructor(message: string, subType?: string, cause?: Error) {\n super(message, 401, subType, cause);\n this.name = 'Unauthorized';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpTooManyRequestsError - 429 Too Many Requests.\n *\n * The one member of the HttpError ladder that never made it over from the production service this ladder was ported from. Without it, apps are\n * forced back to `err.code === 429` — the exact untyped pattern this ladder exists to replace.\n */\nexport class HttpTooManyRequestsError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 429, undefined, cause);\n this.name = 'TooManyRequests';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpForbiddenError - 403 Forbidden.\n */\nexport class HttpForbiddenError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 403, undefined, cause);\n this.name = 'Forbidden';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpTimeoutError - 408 Request Timeout.\n */\nexport class HttpTimeoutError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 408, undefined, cause);\n this.name = 'Timeout';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpBadGatewayError - 502 Bad Gateway.\n */\nexport class HttpBadGatewayError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 502, undefined, cause);\n this.name = 'HttpBadGatewayError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpServiceUnavailableError - 503 Service Unavailable.\n *\n * The cold-start code: a scale-to-zero backend (Cloud Run `min_instance_count = 0`) whose instance\n * is still booting is answered by the load balancer with a 503 and ITS OWN HTML page — no\n * ProtocolError body at all. Without this member the client fell through to a generic `HttpError`,\n * so an app could not say \"the server is waking, retry\" without matching on `err.code === 503`,\n * which is the untyped pattern this ladder exists to replace.\n */\nexport class HttpServiceUnavailableError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 503, undefined, cause);\n this.name = 'HttpServiceUnavailableError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpGatewayTimeoutError - 504 Gateway Timeout.\n * SHOULD NOT BE USED SERVER SIDE SINCE ALBs will return 504 and it will not be translated\n * to json body 'ProtocolError'.\n */\nexport class HttpGatewayTimeoutError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 504, undefined, cause);\n this.name = 'HttpGatewayTimeoutError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpInternalServerError - 500 Internal Server Error.\n */\nexport class HttpInternalServerError extends HttpError {\n constructor(message: string, cause?: Error) {\n super(message, 500, undefined, cause);\n this.name = 'InternalServerError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpVendorError - 598 Vendor Error.\n * Custom status code for vendor/external service errors with retry hint.\n */\nexport class HttpVendorError extends HttpError {\n constructor(\n message: string,\n public waitSeconds = 30,\n cause?: Error,\n ) {\n super(message, 598, undefined, cause);\n this.name = 'VendorError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * HttpUserError - User validation error with 2xx status code.\n *\n * Uses HTTP 266 (non-standard 2xx code) intentionally because:\n * 1. User validation errors are \"successful\" from server perspective - user just made a mistake\n * 2. Browser DevTools show 4xx/5xx codes in RED, which is confusing for user validation\n * 3. Allows error to propagate up the stack via throw without triggering error monitoring\n * 4. Avoids polluting logs with \"errors\" that are actually expected user behavior\n *\n * This is a deliberate design pattern - do NOT change to 4xx codes.\n * Examples: \"Email already exists\", \"Invalid password format\", \"Required field missing\"\n *\n * # It is also the ONLY type whose `message` reaches the caller\n *\n * `HttpErrorWireMapper` (http-server) copies `message` onto the response body for this type and no\n * other; every other subclass sends the generic HTTP reason phrase for its status. That is not an\n * arbitrary exception — it follows from the four points above. This type MEANS \"this text was written\n * for a human to read\", where `Error.message` everywhere else means \"this text was written for whoever\n * reads the logs\" and routinely quotes downstream urls, response bodies and internal ids.\n *\n * So: text the user must SEE goes in an `HttpUserError` (or in `HttpBadRequestError.guiMessage`,\n * which is the same idea one field down). Throwing `new HttpForbiddenError('you need the admin role\n * on tenant 4471')` shows the user 'Forbidden' and nothing else.\n */\nexport class HttpUserError extends HttpError {\n public errorCode?: string;\n\n constructor(message: string, errorCode?: string, cause?: Error) {\n super(message, 266, 'USER_ERROR', cause);\n this.name = 'UserError';\n this.errorCode = errorCode;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * OfflineError - the request never reached a server.\n *\n * Offline, DNS failure, connection refused, CORS preflight rejected: `fetch` itself rejected, so no\n * Response — and therefore no HTTP status — ever existed. That is precisely why this extends `Error`\n * and NOT `HttpError`: an `HttpError` carries a `code` and means \"the server replied with a failure\",\n * a different situation a caller usually retries differently. Subclassing it would give this a bogus\n * status and make it match `instanceof HttpError` ladders that must not catch a transport reject.\n *\n * The original failure (a raw `TypeError: Failed to fetch`, or undici's coded reject) is always\n * preserved as `cause`, so a caller that wants the underlying detail can still reach it.\n */\nexport class OfflineError extends Error {\n constructor(message: string, cause?: Error) {\n super(message, { cause });\n this.name = 'OfflineError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -37,8 +37,8 @@ export type { ServiceUrlDeriver } from './http/ClientRegistry';
37
37
  export { ServiceInfo } from './http/ServiceInfo';
38
38
  export { RuntimeLocality } from './http/RuntimeLocality';
39
39
  export type { Locality } from './http/RuntimeLocality';
40
- export { ErrorWireForm } from './http/ErrorTranslation';
41
- export type { ErrorTranslation } from './http/ErrorTranslation';
40
+ export { HttpHeader, HttpResponseStatus, HttpResponseDto } from './http/HttpResponseDto';
41
+ export type { ErrorTranslators } from './http/ErrorTranslators';
42
42
  export type { FailureClassifier } from './http/FailureClassifier';
43
43
  export { KeyedFailureClassifier } from './http/FailureClassifier';
44
44
  export { WebpiecesDefaultFailureClassifier, WEBPIECES_DEFAULT_FAILURE_CLASSIFIER, } from './http/WebpiecesDefaultFailureClassifier';
package/src/index.js CHANGED
@@ -9,8 +9,8 @@
9
9
  */
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
- 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.FilterChain = exports.Filter = exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiMethodInfo = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = void 0;
12
+ exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.WEBPIECES_DEFAULT_FAILURE_CLASSIFIER = exports.WebpiecesDefaultFailureClassifier = exports.KeyedFailureClassifier = exports.HttpResponseDto = exports.HttpResponseStatus = exports.HttpHeader = 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.FilterChain = exports.Filter = exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiMethodInfo = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = exports.ContextMgr = exports.DestinationTrust = 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");
@@ -149,10 +149,12 @@ Object.defineProperty(exports, "ServiceInfo", { enumerable: true, get: function
149
149
  // The ONE input to @AuthLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).
150
150
  var RuntimeLocality_1 = require("./http/RuntimeLocality");
151
151
  Object.defineProperty(exports, "RuntimeLocality", { enumerable: true, get: function () { return RuntimeLocality_1.RuntimeLocality; } });
152
- // Pluggable, bidirectional error translation (app exception <-> wire form). Registered on
153
- // ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.
154
- var ErrorTranslation_1 = require("./http/ErrorTranslation");
155
- Object.defineProperty(exports, "ErrorWireForm", { enumerable: true, get: function () { return ErrorTranslation_1.ErrorWireForm; } });
152
+ // The ENTIRE HTTP response as pure data the ONE form both transports (express, fetch) are
153
+ // normalised into, so an ErrorTranslators implementation is written once and serves both.
154
+ var HttpResponseDto_1 = require("./http/HttpResponseDto");
155
+ Object.defineProperty(exports, "HttpHeader", { enumerable: true, get: function () { return HttpResponseDto_1.HttpHeader; } });
156
+ Object.defineProperty(exports, "HttpResponseStatus", { enumerable: true, get: function () { return HttpResponseDto_1.HttpResponseStatus; } });
157
+ Object.defineProperty(exports, "HttpResponseDto", { enumerable: true, get: function () { return HttpResponseDto_1.HttpResponseDto; } });
156
158
  var FailureClassifier_1 = require("./http/FailureClassifier");
157
159
  Object.defineProperty(exports, "KeyedFailureClassifier", { enumerable: true, get: function () { return FailureClassifier_1.KeyedFailureClassifier; } });
158
160
  var WebpiecesDefaultFailureClassifier_1 = require("./http/WebpiecesDefaultFailureClassifier");
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). NOT a singleton: construct one per environment\n// with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`\n// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.\nexport { LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl lives in @webpieces/core-context, the browser one in\n// @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { 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 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"]}
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// The ENTIRE HTTP response as pure data — the ONE form both transports (express, fetch) are\n// normalised into, so an ErrorTranslators implementation is written once and serves both.\nexport { HttpHeader, HttpResponseStatus, HttpResponseDto } from './http/HttpResponseDto';\n// Pluggable, bidirectional error translation (app exception <-> the WHOLE response). Set on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport type { ErrorTranslators } from './http/ErrorTranslators';\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). NOT a singleton: construct one per environment\n// with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`\n// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.\nexport { LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl lives in @webpieces/core-context, the browser one in\n// @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { 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 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"]}
@@ -1,47 +0,0 @@
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
- }
@@ -1,21 +0,0 @@
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
@@ -1 +0,0 @@
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"]}