@webpieces/http-client-core 0.4.694 → 0.4.696
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/http-client-core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.696",
|
|
4
4
|
"description": "Isomorphic core of the webpieces HTTP client: the decorator-driven ProxyClient, error translation, and the Proxy trap shared by http-client-node and http-client-browser",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
"access": "public"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@webpieces/core-util": "0.4.
|
|
24
|
+
"@webpieces/core-util": "0.4.696"
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -13,6 +13,10 @@ import { TranslatedFailure } from './TranslatedFailure';
|
|
|
13
13
|
* This achieves symmetric error handling - server throws typed exceptions,
|
|
14
14
|
* client receives typed exceptions.
|
|
15
15
|
*
|
|
16
|
+
* The symmetry is in the TYPE and the structured fields, NOT in the prose: the server sends the real
|
|
17
|
+
* `Error.message` for `HttpUserError` alone and a generic reason phrase for everything else. See
|
|
18
|
+
* {@link builtInError} and, on the server, `HttpErrorWireMapper`.
|
|
19
|
+
*
|
|
16
20
|
* It returns a {@link TranslatedFailure} rather than a bare `Error` because the mapping is only HALF
|
|
17
21
|
* the decision. It is ISOMORPHIC — the same mapping runs in a browser and in a server — and the two
|
|
18
22
|
* environments must NOT do the same thing with a downstream 4xx (see
|
|
@@ -37,16 +41,34 @@ export declare class ClientErrorTranslator {
|
|
|
37
41
|
* The built-in status → error mapping (symmetric with the server's ExpressWrapper.handleError()):
|
|
38
42
|
* - 400 → HttpBadRequestError (with field, guiAlertMessage)
|
|
39
43
|
* - 266 → HttpUserError (with errorCode) - 2xx code for user validation
|
|
40
|
-
* - 401 → HttpUnauthorizedError
|
|
44
|
+
* - 401 → HttpUnauthorizedError (with subType)
|
|
41
45
|
* - 403 → HttpForbiddenError
|
|
42
46
|
* - 404 → HttpNotFoundError
|
|
43
47
|
* - 408 → HttpTimeoutError
|
|
48
|
+
* - 429 → HttpTooManyRequestsError
|
|
44
49
|
* - 500 → HttpInternalServerError
|
|
45
50
|
* - 502 → HttpBadGatewayError
|
|
46
51
|
* - 503 → HttpServiceUnavailableError
|
|
47
52
|
* - 504 → HttpGatewayTimeoutError
|
|
48
53
|
* - 598 → HttpVendorError (with waitSeconds) - custom status code
|
|
49
54
|
* - other → generic HttpError
|
|
55
|
+
*
|
|
56
|
+
* # What `message` means on THIS side of the wire
|
|
57
|
+
*
|
|
58
|
+
* The reconstructed error carries whatever text the wire carried, and for every status except
|
|
59
|
+
* **266** a webpieces server deliberately sends only the GENERIC reason phrase — 'Not Found',
|
|
60
|
+
* 'Internal Server Error', … See `HttpErrorWireMapper` (http-server) for why: `Error.message` is
|
|
61
|
+
* an operator-facing field that routinely quotes internal detail, so it stays in the server's log
|
|
62
|
+
* and never reaches a caller. `HttpUserError` (266) is the one type whose message was WRITTEN for
|
|
63
|
+
* a human to read, and it arrives verbatim.
|
|
64
|
+
*
|
|
65
|
+
* So: branch on the TYPE, on `subType`, on `errorCode`, or on `guiAlertMessage` — never on the
|
|
66
|
+
* prose of `message`. It is now a constant per status by design, and treating it as diagnostic
|
|
67
|
+
* information will not work against a current webpieces server. The diagnosis lives in the
|
|
68
|
+
* server's logs, correlated by request id.
|
|
69
|
+
*
|
|
70
|
+
* (An app that publishes richer text on purpose does it through
|
|
71
|
+
* `ClientRegistry.addErrorTranslation()`, which is consulted before this mapping on both sides.)
|
|
50
72
|
*/
|
|
51
73
|
private static builtInError;
|
|
52
74
|
}
|
|
@@ -16,6 +16,10 @@ const TranslatedFailure_1 = require("./TranslatedFailure");
|
|
|
16
16
|
* This achieves symmetric error handling - server throws typed exceptions,
|
|
17
17
|
* client receives typed exceptions.
|
|
18
18
|
*
|
|
19
|
+
* The symmetry is in the TYPE and the structured fields, NOT in the prose: the server sends the real
|
|
20
|
+
* `Error.message` for `HttpUserError` alone and a generic reason phrase for everything else. See
|
|
21
|
+
* {@link builtInError} and, on the server, `HttpErrorWireMapper`.
|
|
22
|
+
*
|
|
19
23
|
* It returns a {@link TranslatedFailure} rather than a bare `Error` because the mapping is only HALF
|
|
20
24
|
* the decision. It is ISOMORPHIC — the same mapping runs in a browser and in a server — and the two
|
|
21
25
|
* environments must NOT do the same thing with a downstream 4xx (see
|
|
@@ -48,16 +52,34 @@ class ClientErrorTranslator {
|
|
|
48
52
|
* The built-in status → error mapping (symmetric with the server's ExpressWrapper.handleError()):
|
|
49
53
|
* - 400 → HttpBadRequestError (with field, guiAlertMessage)
|
|
50
54
|
* - 266 → HttpUserError (with errorCode) - 2xx code for user validation
|
|
51
|
-
* - 401 → HttpUnauthorizedError
|
|
55
|
+
* - 401 → HttpUnauthorizedError (with subType)
|
|
52
56
|
* - 403 → HttpForbiddenError
|
|
53
57
|
* - 404 → HttpNotFoundError
|
|
54
58
|
* - 408 → HttpTimeoutError
|
|
59
|
+
* - 429 → HttpTooManyRequestsError
|
|
55
60
|
* - 500 → HttpInternalServerError
|
|
56
61
|
* - 502 → HttpBadGatewayError
|
|
57
62
|
* - 503 → HttpServiceUnavailableError
|
|
58
63
|
* - 504 → HttpGatewayTimeoutError
|
|
59
64
|
* - 598 → HttpVendorError (with waitSeconds) - custom status code
|
|
60
65
|
* - other → generic HttpError
|
|
66
|
+
*
|
|
67
|
+
* # What `message` means on THIS side of the wire
|
|
68
|
+
*
|
|
69
|
+
* The reconstructed error carries whatever text the wire carried, and for every status except
|
|
70
|
+
* **266** a webpieces server deliberately sends only the GENERIC reason phrase — 'Not Found',
|
|
71
|
+
* 'Internal Server Error', … See `HttpErrorWireMapper` (http-server) for why: `Error.message` is
|
|
72
|
+
* an operator-facing field that routinely quotes internal detail, so it stays in the server's log
|
|
73
|
+
* and never reaches a caller. `HttpUserError` (266) is the one type whose message was WRITTEN for
|
|
74
|
+
* a human to read, and it arrives verbatim.
|
|
75
|
+
*
|
|
76
|
+
* So: branch on the TYPE, on `subType`, on `errorCode`, or on `guiAlertMessage` — never on the
|
|
77
|
+
* prose of `message`. It is now a constant per status by design, and treating it as diagnostic
|
|
78
|
+
* information will not work against a current webpieces server. The diagnosis lives in the
|
|
79
|
+
* server's logs, correlated by request id.
|
|
80
|
+
*
|
|
81
|
+
* (An app that publishes richer text on purpose does it through
|
|
82
|
+
* `ClientRegistry.addErrorTranslation()`, which is consulted before this mapping on both sides.)
|
|
61
83
|
*/
|
|
62
84
|
// webpieces-disable no-function-outside-class -- private helper of the static above; same reason
|
|
63
85
|
static builtInError(response, protocolError) {
|
|
@@ -77,6 +99,14 @@ class ClientErrorTranslator {
|
|
|
77
99
|
return new core_util_1.HttpNotFoundError(message);
|
|
78
100
|
case 408:
|
|
79
101
|
return new core_util_1.HttpTimeoutError(message);
|
|
102
|
+
case 429:
|
|
103
|
+
// The server has always been able to throw this (HttpErrorWireMapper sends
|
|
104
|
+
// 'Too Many Requests' for it); the client had no case for it, so it arrived as a bare
|
|
105
|
+
// HttpError and callers were pushed back to `err.code === 429` — the untyped pattern
|
|
106
|
+
// this ladder exists to replace. That gap bites harder now that `message` is a
|
|
107
|
+
// constant per status: branching on the TYPE is the only thing left, so every status
|
|
108
|
+
// the server can emit needs one.
|
|
109
|
+
return new core_util_1.HttpTooManyRequestsError(message);
|
|
80
110
|
case 500:
|
|
81
111
|
return new core_util_1.HttpInternalServerError(message);
|
|
82
112
|
case 502:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientErrorTranslator.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-core/src/ClientErrorTranslator.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"ClientErrorTranslator.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-core/src/ClientErrorTranslator.ts"],"names":[],"mappings":";;;AAAA,oDAgB8B;AAC9B,2DAAwD;AAExD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,qBAAqB;IAC9B;;;;;;;;;;;OAWG;IACH,yNAAyN;IACzN,MAAM,CAAC,cAAc,CAAC,QAAkB,EAAE,aAA4B;QAClE,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;QAEnC,MAAM,MAAM,GAAG,0BAAc,CAAC,oBAAoB,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;QAC9E,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,IAAI,qCAAiB,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QAC3D,CAAC;QAED,OAAO,IAAI,qCAAiB,CACxB,qBAAqB,CAAC,YAAY,CAAC,QAAQ,EAAE,aAAa,CAAC,EAC3D,KAAK,EACL,UAAU,CACb,CAAC;IACN,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,iGAAiG;IACzF,MAAM,CAAC,YAAY,CAAC,QAAkB,EAAE,aAA4B;QACxE,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;QACnC,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,IAAI,QAAQ,CAAC,UAAU,IAAI,eAAe,CAAC;QAChF,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC;QAEtC,QAAQ,UAAU,EAAE,CAAC;YACjB,KAAK,GAAG;gBACJ,OAAO,IAAI,+BAAmB,CAC1B,OAAO,EACP,aAAa,CAAC,KAAK,EACnB,aAAa,CAAC,eAAe,CAChC,CAAC;YAEN,KAAK,GAAG,EAAE,sDAAsD;gBAC5D,OAAO,IAAI,yBAAa,CAAC,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;YAE/D,KAAK,GAAG;gBACJ,OAAO,IAAI,iCAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAEvD,KAAK,GAAG;gBACJ,OAAO,IAAI,8BAAkB,CAAC,OAAO,CAAC,CAAC;YAE3C,KAAK,GAAG;gBACJ,OAAO,IAAI,6BAAiB,CAAC,OAAO,CAAC,CAAC;YAE1C,KAAK,GAAG;gBACJ,OAAO,IAAI,4BAAgB,CAAC,OAAO,CAAC,CAAC;YAEzC,KAAK,GAAG;gBACJ,2EAA2E;gBAC3E,sFAAsF;gBACtF,qFAAqF;gBACrF,+EAA+E;gBAC/E,qFAAqF;gBACrF,iCAAiC;gBACjC,OAAO,IAAI,oCAAwB,CAAC,OAAO,CAAC,CAAC;YAEjD,KAAK,GAAG;gBACJ,OAAO,IAAI,mCAAuB,CAAC,OAAO,CAAC,CAAC;YAEhD,KAAK,GAAG;gBACJ,OAAO,IAAI,+BAAmB,CAAC,OAAO,CAAC,CAAC;YAE5C,KAAK,GAAG;gBACJ,OAAO,IAAI,uCAA2B,CAAC,OAAO,CAAC,CAAC;YAEpD,KAAK,GAAG;gBACJ,OAAO,IAAI,mCAAuB,CAAC,OAAO,CAAC,CAAC;YAEhD,KAAK,GAAG,EAAE,0EAA0E;gBAChF,OAAO,IAAI,2BAAe,CAAC,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;YAEnE;gBACI,oFAAoF;gBACpF,iFAAiF;gBACjF,OAAO,IAAI,qBAAS,CAChB,OAAO,IAAI,kCAAkC,UAAU,EAAE,EACzD,UAAU,EACV,OAAO,CACV,CAAC;QACV,CAAC;IACL,CAAC;CACJ;AA7HD,sDA6HC","sourcesContent":["import {\n ProtocolError,\n ClientRegistry,\n HttpError,\n HttpBadRequestError,\n HttpUserError,\n HttpVendorError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpNotFoundError,\n HttpTimeoutError,\n HttpInternalServerError,\n HttpBadGatewayError,\n HttpServiceUnavailableError,\n HttpGatewayTimeoutError,\n HttpTooManyRequestsError,\n} from '@webpieces/core-util';\nimport { TranslatedFailure } from './TranslatedFailure';\n\n/**\n * ClientErrorTranslator - Translates HTTP error responses to HttpError exceptions.\n *\n * This is the CLIENT-SIDE reverse of ExpressWrapper.handleError() on the server.\n * It reconstructs typed HttpError exceptions from ProtocolError JSON responses.\n *\n * Architecture:\n * - Server: HttpError → ExpressWrapper.handleError() → ProtocolError JSON\n * - Client: ProtocolError JSON → ClientErrorTranslator.translateError() → TranslatedFailure\n *\n * This achieves symmetric error handling - server throws typed exceptions,\n * client receives typed exceptions.\n *\n * The symmetry is in the TYPE and the structured fields, NOT in the prose: the server sends the real\n * `Error.message` for `HttpUserError` alone and a generic reason phrase for everything else. See\n * {@link builtInError} and, on the server, `HttpErrorWireMapper`.\n *\n * It returns a {@link TranslatedFailure} rather than a bare `Error` because the mapping is only HALF\n * the decision. It is ISOMORPHIC — the same mapping runs in a browser and in a server — and the two\n * environments must NOT do the same thing with a downstream 4xx (see\n * `ProxyClient.adaptDownstreamFailure`). The wrapper carries the one fact that hook cannot recover\n * on its own: whether the APP claimed this status, or the built-in default did.\n */\nexport class ClientErrorTranslator {\n /**\n * Parse an error response and decide which error the caller should see, and who decided it.\n *\n * App-registered translations win, so an app can reconstruct its OWN error types (e.g. a custom\n * 460) AND override built-ins. `undefined` means \"not mine\" — fall through to\n * {@link builtInError}, which stays the generic default. Symmetric with the server's\n * ExpressWrapper.handleError(), which consults ClientRegistry.tryTranslateToWire() first.\n *\n * @param response - Fetch Response object\n * @param protocolError - Parsed ProtocolError from response body\n * @returns the chosen error plus its provenance and the downstream status\n */\n // webpieces-disable no-function-outside-class -- pure, stateless status-to-type mapping with nothing to inject, called from a BROWSER bundle where no DI container exists; static is the established idiom of this class\n static translateError(response: Response, protocolError: ProtocolError): TranslatedFailure {\n const statusCode = response.status;\n\n const custom = ClientRegistry.tryTranslateFromWire(statusCode, protocolError);\n if (custom !== undefined) {\n return new TranslatedFailure(custom, true, statusCode);\n }\n\n return new TranslatedFailure(\n ClientErrorTranslator.builtInError(response, protocolError),\n false,\n statusCode,\n );\n }\n\n /**\n * The built-in status → error mapping (symmetric with the server's ExpressWrapper.handleError()):\n * - 400 → HttpBadRequestError (with field, guiAlertMessage)\n * - 266 → HttpUserError (with errorCode) - 2xx code for user validation\n * - 401 → HttpUnauthorizedError (with subType)\n * - 403 → HttpForbiddenError\n * - 404 → HttpNotFoundError\n * - 408 → HttpTimeoutError\n * - 429 → HttpTooManyRequestsError\n * - 500 → HttpInternalServerError\n * - 502 → HttpBadGatewayError\n * - 503 → HttpServiceUnavailableError\n * - 504 → HttpGatewayTimeoutError\n * - 598 → HttpVendorError (with waitSeconds) - custom status code\n * - other → generic HttpError\n *\n * # What `message` means on THIS side of the wire\n *\n * The reconstructed error carries whatever text the wire carried, and for every status except\n * **266** a webpieces server deliberately sends only the GENERIC reason phrase — 'Not Found',\n * 'Internal Server Error', … See `HttpErrorWireMapper` (http-server) for why: `Error.message` is\n * an operator-facing field that routinely quotes internal detail, so it stays in the server's log\n * and never reaches a caller. `HttpUserError` (266) is the one type whose message was WRITTEN for\n * a human to read, and it arrives verbatim.\n *\n * So: branch on the TYPE, on `subType`, on `errorCode`, or on `guiAlertMessage` — never on the\n * prose of `message`. It is now a constant per status by design, and treating it as diagnostic\n * information will not work against a current webpieces server. The diagnosis lives in the\n * server's logs, correlated by request id.\n *\n * (An app that publishes richer text on purpose does it through\n * `ClientRegistry.addErrorTranslation()`, which is consulted before this mapping on both sides.)\n */\n // webpieces-disable no-function-outside-class -- private helper of the static above; same reason\n private static builtInError(response: Response, protocolError: ProtocolError): Error {\n const statusCode = response.status;\n const message = protocolError.message || response.statusText || 'Unknown error';\n const subType = protocolError.subType;\n\n switch (statusCode) {\n case 400:\n return new HttpBadRequestError(\n message,\n protocolError.field,\n protocolError.guiAlertMessage,\n );\n\n case 266: // HttpUserError - 2xx code for user validation errors\n return new HttpUserError(message, protocolError.errorCode);\n\n case 401:\n return new HttpUnauthorizedError(message, subType);\n\n case 403:\n return new HttpForbiddenError(message);\n\n case 404:\n return new HttpNotFoundError(message);\n\n case 408:\n return new HttpTimeoutError(message);\n\n case 429:\n // The server has always been able to throw this (HttpErrorWireMapper sends\n // 'Too Many Requests' for it); the client had no case for it, so it arrived as a bare\n // HttpError and callers were pushed back to `err.code === 429` — the untyped pattern\n // this ladder exists to replace. That gap bites harder now that `message` is a\n // constant per status: branching on the TYPE is the only thing left, so every status\n // the server can emit needs one.\n return new HttpTooManyRequestsError(message);\n\n case 500:\n return new HttpInternalServerError(message);\n\n case 502:\n return new HttpBadGatewayError(message);\n\n case 503:\n return new HttpServiceUnavailableError(message);\n\n case 504:\n return new HttpGatewayTimeoutError(message);\n\n case 598: // HttpVendorError - custom status code for vendor/external service errors\n return new HttpVendorError(message, protocolError.waitSeconds);\n\n default:\n // Unknown status code and no app translation claimed it: still a real HttpError (so\n // `err instanceof HttpError` holds after the RPC hop), carrying the status code.\n return new HttpError(\n message || `could not translate statusCode=${statusCode}`,\n statusCode,\n subType,\n );\n }\n }\n}\n"]}
|