@webpieces/core-util 0.4.788 → 0.4.790
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/errors/ApiErrorBoundary.d.ts +31 -33
- package/src/errors/ApiErrorBoundary.js +35 -79
- package/src/errors/ApiErrorBoundary.js.map +1 -1
- package/src/errors/ApiErrorCodec.d.ts +11 -1
- package/src/errors/ApiErrorCodec.js +20 -6
- package/src/errors/ApiErrorCodec.js.map +1 -1
- package/src/http/ApiErrorHttpStatus.d.ts +24 -3
- package/src/http/ApiErrorHttpStatus.js +15 -8
- package/src/http/ApiErrorHttpStatus.js.map +1 -1
- package/src/http/ApiErrorHttpStatusCompileAssertions.d.ts +15 -0
- package/src/http/ApiErrorHttpStatusCompileAssertions.js +30 -0
- package/src/http/ApiErrorHttpStatusCompileAssertions.js.map +1 -0
- package/src/http/ClientRegistry.d.ts +7 -11
- package/src/http/ClientRegistry.js +8 -14
- package/src/http/ClientRegistry.js.map +1 -1
- package/src/http/ErrorTranslators.d.ts +37 -13
- package/src/http/ErrorTranslators.js.map +1 -1
- package/src/http/LogApiCall.d.ts +1 -1
- package/src/http/LogApiCall.js +6 -2
- package/src/http/LogApiCall.js.map +1 -1
- package/src/http/StreamingContract.d.ts +30 -4
- package/src/http/StreamingContract.js.map +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.js.map +1 -1
- package/src/ipc/IpcConnection.js +8 -1
- package/src/ipc/IpcConnection.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,43 +1,41 @@
|
|
|
1
|
-
import { Logger } from '../logging/Logger';
|
|
2
1
|
import { ApiErrorPayload } from './ApiErrorCodec';
|
|
3
|
-
import { ApiError } from './ApiError';
|
|
4
2
|
/**
|
|
5
|
-
* The ONE rule every outbound protocol boundary (HTTP server, MCP) applies to a
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* The ONE rule every outbound protocol boundary (HTTP server, MCP, IPC, streams) applies to a
|
|
4
|
+
* thrown error before rendering it: an error in, the exact wire payload out.
|
|
5
|
+
*
|
|
6
|
+
* ONE method, because there is exactly one question — "what does THIS service publish for this
|
|
7
|
+
* error?" — and it used to be asked three times per failure (`statusFor`, `logOperatorDetail`,
|
|
8
|
+
* `encode`), each re-classifying the same throw. Everything an adapter needs afterwards is ON the
|
|
9
|
+
* returned {@link ApiErrorPayload}: `kind`, `message`, `statusCode`, `edgeHttpStatus` and
|
|
10
|
+
* `retryAfterSeconds`. No adapter re-derives anything from the raw error.
|
|
11
|
+
*
|
|
12
|
+
* Two rules, and they are the whole class:
|
|
13
|
+
*
|
|
14
|
+
* 1. CLASSIFICATION. A connection failure is OUR outbound call failing, so from our caller's seat
|
|
15
|
+
* THIS service is the thing that is broken: an {@link ApiConnectionError} publishes as
|
|
16
|
+
* `implementation`, never as `connection`. That rule is deliberately NOT in {@link ApiErrorCodec},
|
|
17
|
+
* which encodes a value faithfully; it belongs to the boundary that owns the failure.
|
|
18
|
+
* 2. DISCLOSURE. Only an `ApiEndUserError`'s own message is ever published; everything else gets
|
|
19
|
+
* allowlisted generic text by kind (see {@link ApiErrorCodec}).
|
|
8
20
|
*
|
|
9
21
|
* Nothing here SUBSTITUTES an object for the value that was thrown: the thrown error survives to
|
|
10
|
-
* every consumer, so an app can still `instanceof` its own error class
|
|
11
|
-
*
|
|
22
|
+
* every consumer, so an app can still `instanceof` its own error class. Classification is a VALUE
|
|
23
|
+
* computed from the thrown error, never a wrapper around it.
|
|
24
|
+
*
|
|
25
|
+
* OPERATOR LOGGING IS NOT HERE, on purpose. `LogApiCall` (via `LogApiFilter` on the server and
|
|
26
|
+
* `ProxyClient` on a client) already writes one `[API-{side}-resp-FAIL]` / `-OTHER` line per failed
|
|
27
|
+
* call, WITH the request, the identity and the timing. A second bare line at the protocol edge was
|
|
28
|
+
* the exact defect `ErrorLogFilter` was deleted for, one layer down. An edge with no filter above it
|
|
29
|
+
* (MCP bearer verification, Origin, a malformed body, `tools/list`) wraps its own work in
|
|
30
|
+
* `LogApiCall` instead.
|
|
12
31
|
*/
|
|
13
32
|
export declare class ApiErrorBoundary {
|
|
14
|
-
private readonly log;
|
|
15
|
-
constructor(log: Logger);
|
|
16
|
-
/**
|
|
17
|
-
* The classified API outcome `thrown` publishes as, or `undefined` when it is a bug or broken
|
|
18
|
-
* state of THIS service — a non-`ApiError`, a non-`Error` throw, or a caller-local
|
|
19
|
-
* `ApiConnectionError`.
|
|
20
|
-
*
|
|
21
|
-
* A connection failure is OUR outbound call failing, so from our caller's seat this service is
|
|
22
|
-
* the thing that is broken: it publishes as `implementation`, never as `connection`. That rule
|
|
23
|
-
* is deliberately NOT in `ApiErrorCodec`, which encodes a value faithfully; it belongs to the
|
|
24
|
-
* boundary that owns the failure.
|
|
25
|
-
*
|
|
26
|
-
* The returned error IS the thrown error — never a replacement.
|
|
27
|
-
*/
|
|
28
|
-
apiOutcome(thrown: unknown): ApiError | undefined;
|
|
29
|
-
/**
|
|
30
|
-
* The wire payload THIS service publishes for `thrown`. Only an {@link ApiEndUserError} ever
|
|
31
|
-
* publishes its own message; everything else gets allowlisted generic text by kind.
|
|
32
|
-
*/
|
|
33
|
-
encode(thrown: unknown): ApiErrorPayload;
|
|
34
33
|
/**
|
|
35
|
-
*
|
|
36
|
-
* at error. `correlation` is appended verbatim (for example `requestId=... tool=...`).
|
|
34
|
+
* The wire payload THIS service publishes for `error`.
|
|
37
35
|
*
|
|
38
|
-
*
|
|
39
|
-
* and
|
|
36
|
+
* A non-`ApiError` — an app's own `MyLibError extends Error`, a library's error, anything at all
|
|
37
|
+
* — and a caller-local {@link ApiConnectionError} both publish as a bare `implementation`
|
|
38
|
+
* failure, so neither the error's own text nor a downstream host name can reach the wire.
|
|
40
39
|
*/
|
|
41
|
-
|
|
42
|
-
private operatorDetail;
|
|
40
|
+
encode(error: Error): ApiErrorPayload;
|
|
43
41
|
}
|
|
@@ -1,95 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ApiErrorBoundary = void 0;
|
|
4
|
-
const errorUtils_1 = require("../lib/errorUtils");
|
|
5
4
|
const ApiErrorCodec_1 = require("./ApiErrorCodec");
|
|
6
5
|
const ApiError_1 = require("./ApiError");
|
|
7
6
|
/**
|
|
8
|
-
* The ONE rule every outbound protocol boundary (HTTP server, MCP) applies to a
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* The ONE rule every outbound protocol boundary (HTTP server, MCP, IPC, streams) applies to a
|
|
8
|
+
* thrown error before rendering it: an error in, the exact wire payload out.
|
|
9
|
+
*
|
|
10
|
+
* ONE method, because there is exactly one question — "what does THIS service publish for this
|
|
11
|
+
* error?" — and it used to be asked three times per failure (`statusFor`, `logOperatorDetail`,
|
|
12
|
+
* `encode`), each re-classifying the same throw. Everything an adapter needs afterwards is ON the
|
|
13
|
+
* returned {@link ApiErrorPayload}: `kind`, `message`, `statusCode`, `edgeHttpStatus` and
|
|
14
|
+
* `retryAfterSeconds`. No adapter re-derives anything from the raw error.
|
|
15
|
+
*
|
|
16
|
+
* Two rules, and they are the whole class:
|
|
17
|
+
*
|
|
18
|
+
* 1. CLASSIFICATION. A connection failure is OUR outbound call failing, so from our caller's seat
|
|
19
|
+
* THIS service is the thing that is broken: an {@link ApiConnectionError} publishes as
|
|
20
|
+
* `implementation`, never as `connection`. That rule is deliberately NOT in {@link ApiErrorCodec},
|
|
21
|
+
* which encodes a value faithfully; it belongs to the boundary that owns the failure.
|
|
22
|
+
* 2. DISCLOSURE. Only an `ApiEndUserError`'s own message is ever published; everything else gets
|
|
23
|
+
* allowlisted generic text by kind (see {@link ApiErrorCodec}).
|
|
11
24
|
*
|
|
12
25
|
* Nothing here SUBSTITUTES an object for the value that was thrown: the thrown error survives to
|
|
13
|
-
* every consumer, so an app can still `instanceof` its own error class
|
|
14
|
-
*
|
|
26
|
+
* every consumer, so an app can still `instanceof` its own error class. Classification is a VALUE
|
|
27
|
+
* computed from the thrown error, never a wrapper around it.
|
|
28
|
+
*
|
|
29
|
+
* OPERATOR LOGGING IS NOT HERE, on purpose. `LogApiCall` (via `LogApiFilter` on the server and
|
|
30
|
+
* `ProxyClient` on a client) already writes one `[API-{side}-resp-FAIL]` / `-OTHER` line per failed
|
|
31
|
+
* call, WITH the request, the identity and the timing. A second bare line at the protocol edge was
|
|
32
|
+
* the exact defect `ErrorLogFilter` was deleted for, one layer down. An edge with no filter above it
|
|
33
|
+
* (MCP bearer verification, Origin, a malformed body, `tools/list`) wraps its own work in
|
|
34
|
+
* `LogApiCall` instead.
|
|
15
35
|
*/
|
|
16
36
|
class ApiErrorBoundary {
|
|
17
|
-
log;
|
|
18
|
-
constructor(log) {
|
|
19
|
-
this.log = log;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* The classified API outcome `thrown` publishes as, or `undefined` when it is a bug or broken
|
|
23
|
-
* state of THIS service — a non-`ApiError`, a non-`Error` throw, or a caller-local
|
|
24
|
-
* `ApiConnectionError`.
|
|
25
|
-
*
|
|
26
|
-
* A connection failure is OUR outbound call failing, so from our caller's seat this service is
|
|
27
|
-
* the thing that is broken: it publishes as `implementation`, never as `connection`. That rule
|
|
28
|
-
* is deliberately NOT in `ApiErrorCodec`, which encodes a value faithfully; it belongs to the
|
|
29
|
-
* boundary that owns the failure.
|
|
30
|
-
*
|
|
31
|
-
* The returned error IS the thrown error — never a replacement.
|
|
32
|
-
*/
|
|
33
|
-
// webpieces-disable no-any-unknown -- thrown values are unknown until classified at this boundary
|
|
34
|
-
apiOutcome(thrown) {
|
|
35
|
-
if (thrown instanceof ApiError_1.ApiError && !(thrown instanceof ApiError_1.ApiConnectionError))
|
|
36
|
-
return thrown;
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
37
|
/**
|
|
40
|
-
* The wire payload THIS service publishes for `
|
|
41
|
-
* publishes its own message; everything else gets allowlisted generic text by kind.
|
|
42
|
-
*/
|
|
43
|
-
// webpieces-disable no-any-unknown -- thrown values are unknown until classified at this boundary
|
|
44
|
-
encode(thrown) {
|
|
45
|
-
const outcome = this.apiOutcome(thrown);
|
|
46
|
-
if (!outcome)
|
|
47
|
-
return new ApiErrorCodec_1.ApiErrorPayload('implementation', 'Internal Error');
|
|
48
|
-
return ApiErrorCodec_1.ApiErrorCodec.encode(outcome);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Logs one operator line at the per-kind level: expected caller outcomes at info, everything else
|
|
52
|
-
* at error. `correlation` is appended verbatim (for example `requestId=... tool=...`).
|
|
38
|
+
* The wire payload THIS service publishes for `error`.
|
|
53
39
|
*
|
|
54
|
-
*
|
|
55
|
-
* and
|
|
40
|
+
* A non-`ApiError` — an app's own `MyLibError extends Error`, a library's error, anything at all
|
|
41
|
+
* — and a caller-local {@link ApiConnectionError} both publish as a bare `implementation`
|
|
42
|
+
* failure, so neither the error's own text nor a downstream host name can reach the wire.
|
|
56
43
|
*/
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
this.log.info(`End User Error: ${detail}`);
|
|
63
|
-
else if (thrown instanceof ApiError_1.ApiBadRequestError)
|
|
64
|
-
this.log.info(`Bad Request: ${detail}`);
|
|
65
|
-
else if (thrown instanceof ApiError_1.ApiNotFoundError)
|
|
66
|
-
this.log.info(`Not Found: ${detail}`);
|
|
67
|
-
else if (thrown instanceof ApiError_1.ApiUnauthorizedError)
|
|
68
|
-
this.log.info(`Unauthorized: ${detail}`);
|
|
69
|
-
else if (thrown instanceof ApiError_1.ApiForbiddenError)
|
|
70
|
-
this.log.info(`Forbidden: ${detail}`);
|
|
71
|
-
else if (thrown instanceof ApiError_1.ApiConflictError)
|
|
72
|
-
this.log.info(`Conflict: ${detail}`);
|
|
73
|
-
else if (thrown instanceof ApiError_1.ApiUnprocessableError)
|
|
74
|
-
this.log.info(`Unprocessable: ${detail}`);
|
|
75
|
-
else if (thrown instanceof ApiError_1.ApiPreconditionFailedError)
|
|
76
|
-
this.log.info(`Precondition Failed: ${detail}`);
|
|
77
|
-
else if (thrown instanceof ApiError_1.ApiUnsupportedMediaTypeError)
|
|
78
|
-
this.log.info(`Unsupported Media Type: ${detail}`);
|
|
79
|
-
else if (thrown instanceof ApiError_1.ApiCodedError && thrown.isCallerError())
|
|
80
|
-
this.log.info(`Coded ${thrown.statusCode}: ${detail}`);
|
|
81
|
-
else if (thrown instanceof ApiError_1.ApiRequestTimeoutError)
|
|
82
|
-
this.log.error(`Request Timeout: ${detail}`);
|
|
83
|
-
else
|
|
84
|
-
this.log.error(`API failure: ${detail}`);
|
|
85
|
-
}
|
|
86
|
-
// webpieces-disable no-any-unknown -- thrown values are unknown until classified at this boundary
|
|
87
|
-
operatorDetail(thrown) {
|
|
88
|
-
const error = (0, errorUtils_1.toError)(thrown);
|
|
89
|
-
const kind = this.apiOutcome(thrown)?.kind ?? 'implementation';
|
|
90
|
-
const subType = thrown instanceof ApiError_1.ApiError ? thrown.subType : undefined;
|
|
91
|
-
const cause = error.cause instanceof Error ? ` cause=${error.cause.message}` : '';
|
|
92
|
-
return `[name=${error.name} kind=${kind} subType=${subType ?? 'none'}] ${error.message}${cause}`;
|
|
44
|
+
encode(error) {
|
|
45
|
+
if (error instanceof ApiError_1.ApiError && !(error instanceof ApiError_1.ApiConnectionError)) {
|
|
46
|
+
return ApiErrorCodec_1.ApiErrorCodec.encode(error);
|
|
47
|
+
}
|
|
48
|
+
return new ApiErrorCodec_1.ApiErrorPayload('implementation', 'Internal Error');
|
|
93
49
|
}
|
|
94
50
|
}
|
|
95
51
|
exports.ApiErrorBoundary = ApiErrorBoundary;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiErrorBoundary.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/ApiErrorBoundary.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"ApiErrorBoundary.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/ApiErrorBoundary.ts"],"names":[],"mappings":";;;AAAA,mDAAiE;AACjE,yCAA0D;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAa,gBAAgB;IACzB;;;;;;OAMG;IACH,MAAM,CAAC,KAAY;QACf,IAAI,KAAK,YAAY,mBAAQ,IAAI,CAAC,CAAC,KAAK,YAAY,6BAAkB,CAAC,EAAE,CAAC;YACtE,OAAO,6BAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,IAAI,+BAAe,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;IACnE,CAAC;CACJ;AAdD,4CAcC","sourcesContent":["import { ApiErrorCodec, ApiErrorPayload } from './ApiErrorCodec';\nimport { ApiConnectionError, ApiError } from './ApiError';\n\n/**\n * The ONE rule every outbound protocol boundary (HTTP server, MCP, IPC, streams) applies to a\n * thrown error before rendering it: an error in, the exact wire payload out.\n *\n * ONE method, because there is exactly one question — \"what does THIS service publish for this\n * error?\" — and it used to be asked three times per failure (`statusFor`, `logOperatorDetail`,\n * `encode`), each re-classifying the same throw. Everything an adapter needs afterwards is ON the\n * returned {@link ApiErrorPayload}: `kind`, `message`, `statusCode`, `edgeHttpStatus` and\n * `retryAfterSeconds`. No adapter re-derives anything from the raw error.\n *\n * Two rules, and they are the whole class:\n *\n * 1. CLASSIFICATION. A connection failure is OUR outbound call failing, so from our caller's seat\n * THIS service is the thing that is broken: an {@link ApiConnectionError} publishes as\n * `implementation`, never as `connection`. That rule is deliberately NOT in {@link ApiErrorCodec},\n * which encodes a value faithfully; it belongs to the boundary that owns the failure.\n * 2. DISCLOSURE. Only an `ApiEndUserError`'s own message is ever published; everything else gets\n * allowlisted generic text by kind (see {@link ApiErrorCodec}).\n *\n * Nothing here SUBSTITUTES an object for the value that was thrown: the thrown error survives to\n * every consumer, so an app can still `instanceof` its own error class. Classification is a VALUE\n * computed from the thrown error, never a wrapper around it.\n *\n * OPERATOR LOGGING IS NOT HERE, on purpose. `LogApiCall` (via `LogApiFilter` on the server and\n * `ProxyClient` on a client) already writes one `[API-{side}-resp-FAIL]` / `-OTHER` line per failed\n * call, WITH the request, the identity and the timing. A second bare line at the protocol edge was\n * the exact defect `ErrorLogFilter` was deleted for, one layer down. An edge with no filter above it\n * (MCP bearer verification, Origin, a malformed body, `tools/list`) wraps its own work in\n * `LogApiCall` instead.\n */\nexport class ApiErrorBoundary {\n /**\n * The wire payload THIS service publishes for `error`.\n *\n * A non-`ApiError` — an app's own `MyLibError extends Error`, a library's error, anything at all\n * — and a caller-local {@link ApiConnectionError} both publish as a bare `implementation`\n * failure, so neither the error's own text nor a downstream host name can reach the wire.\n */\n encode(error: Error): ApiErrorPayload {\n if (error instanceof ApiError && !(error instanceof ApiConnectionError)) {\n return ApiErrorCodec.encode(error);\n }\n return new ApiErrorPayload('implementation', 'Internal Error');\n }\n}\n"]}
|
|
@@ -16,7 +16,17 @@ export declare class ApiErrorPayload {
|
|
|
16
16
|
}
|
|
17
17
|
/** Bounded safe codec shared by HTTP, IPC, and other remote adapters. */
|
|
18
18
|
export declare class ApiErrorCodec {
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Encode a thrown error faithfully. The input is an `Error`, never `unknown`: every caller in
|
|
21
|
+
* the framework has already narrowed with `toError` at its catch, and the `unknown` this used to
|
|
22
|
+
* advertise leaked out of the PRIVATE `.cause` recursion below (`Error.cause` is typed `unknown`
|
|
23
|
+
* in TypeScript), which is an internal concern.
|
|
24
|
+
*
|
|
25
|
+
* This is the FAITHFUL encode: it publishes `kind` exactly as the error declares it. The
|
|
26
|
+
* boundary rule that a caller-local `ApiConnectionError` publishes as `implementation` belongs
|
|
27
|
+
* to {@link ApiErrorBoundary}, which every protocol edge uses instead of calling this directly.
|
|
28
|
+
*/
|
|
29
|
+
static encode(error: Error): ApiErrorPayload;
|
|
20
30
|
private static encodeDepth;
|
|
21
31
|
/** Decode a value received from another process. Implementation failures are marked remote. */
|
|
22
32
|
static decode(value: unknown): ApiError;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ApiErrorCodec = exports.ApiErrorPayload = void 0;
|
|
4
4
|
const ApiError_1 = require("./ApiError");
|
|
5
|
+
const errorUtils_1 = require("../lib/errorUtils");
|
|
5
6
|
/** Allowlisted transport-neutral envelope. It never contains stacks or arbitrary properties. */
|
|
6
7
|
class ApiErrorPayload {
|
|
7
8
|
kind;
|
|
@@ -23,11 +24,21 @@ class ApiErrorPayload {
|
|
|
23
24
|
exports.ApiErrorPayload = ApiErrorPayload;
|
|
24
25
|
/** Bounded safe codec shared by HTTP, IPC, and other remote adapters. */
|
|
25
26
|
class ApiErrorCodec {
|
|
26
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Encode a thrown error faithfully. The input is an `Error`, never `unknown`: every caller in
|
|
29
|
+
* the framework has already narrowed with `toError` at its catch, and the `unknown` this used to
|
|
30
|
+
* advertise leaked out of the PRIVATE `.cause` recursion below (`Error.cause` is typed `unknown`
|
|
31
|
+
* in TypeScript), which is an internal concern.
|
|
32
|
+
*
|
|
33
|
+
* This is the FAITHFUL encode: it publishes `kind` exactly as the error declares it. The
|
|
34
|
+
* boundary rule that a caller-local `ApiConnectionError` publishes as `implementation` belongs
|
|
35
|
+
* to {@link ApiErrorBoundary}, which every protocol edge uses instead of calling this directly.
|
|
36
|
+
*/
|
|
37
|
+
// webpieces-disable no-function-outside-class -- stateless public transport codec
|
|
27
38
|
static encode(error) {
|
|
28
39
|
return this.encodeDepth(error, 0);
|
|
29
40
|
}
|
|
30
|
-
// webpieces-disable no-function-outside-class -- recursive bounded transport codec
|
|
41
|
+
// webpieces-disable no-function-outside-class -- recursive bounded transport codec
|
|
31
42
|
static encodeDepth(error, depth) {
|
|
32
43
|
const kind = error instanceof ApiError_1.ApiError ? error.kind : 'implementation';
|
|
33
44
|
const payload = new ApiErrorPayload(kind, this.publicMessage(error, kind));
|
|
@@ -48,10 +59,13 @@ class ApiErrorCodec {
|
|
|
48
59
|
if (error instanceof ApiError_1.ApiDependencyBackoffError) {
|
|
49
60
|
payload.retryAfterSeconds = this.wait(error.retryAfterSeconds);
|
|
50
61
|
}
|
|
51
|
-
if (
|
|
62
|
+
if (depth < 3) {
|
|
63
|
+
// `Error.cause` is `unknown` by construction, so the recursion narrows it with `toError`
|
|
64
|
+
// exactly as a catch block would — and then deals only in `Error` like everything else.
|
|
52
65
|
const cause = Object.getOwnPropertyDescriptor(error, 'cause')?.value;
|
|
53
|
-
if (cause
|
|
54
|
-
payload.cause = this.encodeDepth(cause, depth + 1);
|
|
66
|
+
if (cause !== undefined && cause !== null) {
|
|
67
|
+
payload.cause = this.encodeDepth((0, errorUtils_1.toError)(cause), depth + 1);
|
|
68
|
+
}
|
|
55
69
|
}
|
|
56
70
|
return payload;
|
|
57
71
|
}
|
|
@@ -147,7 +161,7 @@ class ApiErrorCodec {
|
|
|
147
161
|
static remoteImplementationError() {
|
|
148
162
|
return new ApiError_1.ApiImplementationError('Internal Error', undefined, true);
|
|
149
163
|
}
|
|
150
|
-
// webpieces-disable no-function-outside-class -- caller-safe message selection
|
|
164
|
+
// webpieces-disable no-function-outside-class -- caller-safe message selection
|
|
151
165
|
static publicMessage(error, kind) {
|
|
152
166
|
if (error instanceof ApiError_1.ApiEndUserError)
|
|
153
167
|
return error.message.slice(0, 4096);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiErrorCodec.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/ApiErrorCodec.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ApiBadRequestError,\n ApiCodedError,\n ApiConflictError,\n ApiConnectionError,\n ApiDependencyBackoffError,\n ApiDependencyError,\n ApiDependencyTimeoutError,\n ApiEndpointNotFoundError,\n ApiEndUserError,\n ApiError,\n ApiErrorKind,\n ApiForbiddenError,\n ApiImplementationError,\n ApiNotFoundError,\n ApiNotImplementedError,\n ApiPreconditionFailedError,\n ApiRateLimitedError,\n ApiRequestTimeoutError,\n ApiUnauthorizedError,\n ApiUnavailableError,\n ApiUnprocessableError,\n ApiUnsupportedMediaTypeError,\n EdgeHttpStatus,\n} from './ApiError';\n\n/** Allowlisted transport-neutral envelope. It never contains stacks or arbitrary properties. */\nexport class ApiErrorPayload {\n public cause?: ApiErrorPayload;\n public subType?: string;\n public field?: string;\n public callerMessage?: string;\n public errorCode?: string;\n public retryAfterSeconds?: number;\n public statusCode?: number;\n /** {@link ApiEndUserError.edgeHttpStatus}; absent when the thrower (or an older peer) set none. */\n public edgeHttpStatus?: number;\n\n constructor(\n public kind: string = 'implementation',\n public message: string = 'Internal Error',\n ) {}\n}\n\n/** Bounded safe codec shared by HTTP, IPC, and other remote adapters. */\nexport class ApiErrorCodec {\n // webpieces-disable no-function-outside-class -- stateless public transport codec; webpieces-disable no-any-unknown -- thrown values are untrusted until narrowed\n static encode(error: unknown): ApiErrorPayload {\n return this.encodeDepth(error, 0);\n }\n\n // webpieces-disable no-function-outside-class -- recursive bounded transport codec; webpieces-disable no-any-unknown -- thrown causes require narrowing\n private static encodeDepth(error: unknown, depth: number): ApiErrorPayload {\n const kind = error instanceof ApiError ? error.kind : 'implementation';\n const payload = new ApiErrorPayload(kind, this.publicMessage(error, kind));\n if (error instanceof ApiError) payload.subType = this.text(error.subType);\n if (error instanceof ApiEndUserError) {\n payload.errorCode = this.text(error.errorCode);\n payload.edgeHttpStatus = error.edgeHttpStatus;\n }\n if (error instanceof ApiCodedError) {\n payload.statusCode = error.statusCode;\n payload.errorCode = this.text(error.errorCode);\n }\n if (error instanceof ApiBadRequestError) {\n payload.field = this.text(error.field);\n payload.callerMessage = this.text(error.callerMessage);\n }\n if (error instanceof ApiDependencyBackoffError) {\n payload.retryAfterSeconds = this.wait(error.retryAfterSeconds);\n }\n if (error instanceof Error && depth < 3) {\n const cause = Object.getOwnPropertyDescriptor(error, 'cause')?.value;\n if (cause instanceof Error) payload.cause = this.encodeDepth(cause, depth + 1);\n }\n return payload;\n }\n\n /** Decode a value received from another process. Implementation failures are marked remote. */\n // webpieces-disable no-function-outside-class -- stateless public transport codec; webpieces-disable no-any-unknown -- wire values require validation\n static decode(value: unknown): ApiError {\n return this.decodeDepth(value, 0);\n }\n\n /** Whether a body carries a recognized Webpieces semantic discriminator. */\n // webpieces-disable no-function-outside-class -- stateless wire discriminator check; webpieces-disable no-any-unknown -- wire values require validation\n static isPayload(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) return false;\n const kind = Object.getOwnPropertyDescriptor(value, 'kind')?.value;\n return typeof kind === 'string' && this.kinds.has(kind as ApiErrorKind);\n }\n\n // webpieces-disable no-function-outside-class -- recursive bounded transport codec; webpieces-disable no-any-unknown -- remote causes require validation\n private static decodeDepth(value: unknown, depth: number): ApiError {\n const error = this.decodeOne(value);\n if (typeof value === 'object' && value !== null && depth < 3) {\n const cause = Object.getOwnPropertyDescriptor(value, 'cause')?.value;\n if (typeof cause === 'object' && cause !== null) {\n // webpieces-disable no-anonymous-object-literals -- standard descriptor preserves Error.cause semantics\n Object.defineProperty(error, 'cause', {\n value: this.decodeDepth(cause, depth + 1),\n configurable: true,\n writable: true,\n });\n }\n }\n return error;\n }\n\n // webpieces-disable no-function-outside-class -- allowlisted wire codec; webpieces-disable no-any-unknown -- wire fields require validation\n private static decodeOne(value: unknown): ApiError {\n if (typeof value !== 'object' || value === null) return this.remoteImplementationError();\n // webpieces-disable no-any-unknown -- own wire fields remain untrusted until narrowed\n const field = (key: string): unknown => Object.getOwnPropertyDescriptor(value, key)?.value;\n const kind = field('kind');\n const message = this.text(field('message')) ?? this.message(kind);\n switch (kind) {\n case 'end-user':\n return new ApiEndUserError(\n message,\n this.text(field('errorCode')),\n this.edgeStatus(field('edgeHttpStatus')),\n );\n case 'bad-request':\n return new ApiBadRequestError(\n message,\n this.text(field('field')),\n this.text(field('callerMessage')),\n );\n case 'unauthorized':\n return new ApiUnauthorizedError(message, this.text(field('subType')));\n case 'forbidden':\n return new ApiForbiddenError(message);\n case 'not-found':\n return new ApiNotFoundError(message);\n case 'endpoint-not-found':\n return new ApiEndpointNotFoundError(message);\n case 'request-timeout':\n return new ApiRequestTimeoutError(message);\n case 'rate-limited':\n return new ApiRateLimitedError(message);\n case 'conflict':\n return new ApiConflictError(message);\n case 'unprocessable':\n return new ApiUnprocessableError(message);\n case 'precondition-failed':\n return new ApiPreconditionFailedError(message);\n case 'unsupported-media-type':\n return new ApiUnsupportedMediaTypeError(message);\n case 'not-implemented':\n return new ApiNotImplementedError(message);\n case 'coded':\n return this.coded(message, field('statusCode'), field('errorCode'));\n case 'dependency':\n return new ApiDependencyError(message);\n case 'unavailable':\n return new ApiUnavailableError(message);\n case 'dependency-timeout':\n return new ApiDependencyTimeoutError(message);\n case 'dependency-backoff':\n return new ApiDependencyBackoffError(\n message,\n this.wait(field('retryAfterSeconds')),\n );\n case 'connection':\n return new ApiConnectionError(message);\n case 'implementation':\n return new ApiImplementationError(message, undefined, true);\n default:\n return this.remoteImplementationError();\n }\n }\n\n // webpieces-disable no-function-outside-class -- coded wire decode; webpieces-disable no-any-unknown -- wire fields require validation\n private static coded(message: string, statusCode: unknown, errorCode: unknown): ApiError {\n if (!ApiCodedError.isStatusCode(statusCode)) return this.remoteImplementationError();\n return new ApiCodedError(message, statusCode, this.text(errorCode));\n }\n\n // webpieces-disable no-function-outside-class -- concrete normalization for malformed remote errors\n private static remoteImplementationError(): ApiImplementationError {\n return new ApiImplementationError('Internal Error', undefined, true);\n }\n\n // webpieces-disable no-function-outside-class -- caller-safe message selection; webpieces-disable no-any-unknown -- thrown values require narrowing\n private static publicMessage(error: unknown, kind: ApiErrorKind): string {\n if (error instanceof ApiEndUserError) return error.message.slice(0, 4096);\n return this.message(kind);\n }\n\n // webpieces-disable no-function-outside-class -- wire string bound; webpieces-disable no-any-unknown -- wire values require validation\n private static text(value: unknown): string | undefined {\n return typeof value === 'string' ? value.slice(0, 4096) : undefined;\n }\n\n /** Tolerant: a peer that sends nothing, or a status outside {@link EdgeHttpStatus}, decodes as undefined. */\n // webpieces-disable no-function-outside-class -- wire status bound; webpieces-disable no-any-unknown -- wire values require validation\n private static edgeStatus(value: unknown): EdgeHttpStatus | undefined {\n return ApiEndUserError.isEdgeHttpStatus(value) ? value : undefined;\n }\n\n // webpieces-disable no-function-outside-class -- wire number bound; webpieces-disable no-any-unknown -- wire values require validation\n private static wait(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0\n ? Math.min(value, 86400)\n : undefined;\n }\n\n // webpieces-disable no-function-outside-class -- allowlisted generic messages; webpieces-disable no-any-unknown -- discriminator is validated by switch\n private static message(kind: unknown): string {\n switch (kind) {\n case 'end-user':\n return 'End User Error';\n case 'bad-request':\n return 'Bad Request';\n case 'unauthorized':\n return 'Unauthorized';\n case 'forbidden':\n return 'Forbidden';\n case 'not-found':\n return 'Not Found';\n case 'endpoint-not-found':\n return 'Endpoint Not Found';\n case 'request-timeout':\n return 'Request Timeout';\n case 'rate-limited':\n return 'Rate Limited';\n case 'conflict':\n return 'Conflict';\n case 'unprocessable':\n return 'Unprocessable Content';\n case 'precondition-failed':\n return 'Precondition Failed';\n case 'unsupported-media-type':\n return 'Unsupported Media Type';\n case 'not-implemented':\n return 'Not Implemented';\n case 'coded':\n return 'Request Failed';\n case 'dependency':\n return 'Dependency Error';\n case 'unavailable':\n return 'Service Unavailable';\n case 'dependency-timeout':\n return 'Dependency Timeout';\n case 'dependency-backoff':\n return 'Dependency Unavailable';\n case 'connection':\n return 'Connection Error';\n default:\n return 'Internal Error';\n }\n }\n\n private static readonly kinds: ReadonlySet<ApiErrorKind> = new Set<ApiErrorKind>([\n 'end-user',\n 'bad-request',\n 'unauthorized',\n 'forbidden',\n 'not-found',\n 'endpoint-not-found',\n 'request-timeout',\n 'rate-limited',\n 'conflict',\n 'unprocessable',\n 'precondition-failed',\n 'unsupported-media-type',\n 'not-implemented',\n 'coded',\n 'implementation',\n 'dependency',\n 'unavailable',\n 'dependency-timeout',\n 'dependency-backoff',\n 'connection',\n ]);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ApiErrorCodec.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/ApiErrorCodec.ts"],"names":[],"mappings":";;;AAAA,yCAwBoB;AACpB,kDAA4C;AAE5C,gGAAgG;AAChG,MAAa,eAAe;IAYb;IACA;IAZJ,KAAK,CAAmB;IACxB,OAAO,CAAU;IACjB,KAAK,CAAU;IACf,aAAa,CAAU;IACvB,SAAS,CAAU;IACnB,iBAAiB,CAAU;IAC3B,UAAU,CAAU;IAC3B,mGAAmG;IAC5F,cAAc,CAAU;IAE/B,YACW,OAAe,gBAAgB,EAC/B,UAAkB,gBAAgB;QADlC,SAAI,GAAJ,IAAI,CAA2B;QAC/B,YAAO,GAAP,OAAO,CAA2B;IAC1C,CAAC;CACP;AAfD,0CAeC;AAED,yEAAyE;AACzE,MAAa,aAAa;IACtB;;;;;;;;;OASG;IACH,kFAAkF;IAClF,MAAM,CAAC,MAAM,CAAC,KAAY;QACtB,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC;IAED,mFAAmF;IAC3E,MAAM,CAAC,WAAW,CAAC,KAAY,EAAE,KAAa;QAClD,MAAM,IAAI,GAAG,KAAK,YAAY,mBAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC;QACvE,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QAC3E,IAAI,KAAK,YAAY,mBAAQ;YAAE,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1E,IAAI,KAAK,YAAY,0BAAe,EAAE,CAAC;YACnC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAC/C,OAAO,CAAC,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;QAClD,CAAC;QACD,IAAI,KAAK,YAAY,wBAAa,EAAE,CAAC;YACjC,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;YACtC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,KAAK,YAAY,6BAAkB,EAAE,CAAC;YACtC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,KAAK,YAAY,oCAAyB,EAAE,CAAC;YAC7C,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACZ,yFAAyF;YACzF,wFAAwF;YACxF,MAAM,KAAK,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC;YACrE,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACxC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,IAAA,oBAAO,EAAC,KAAK,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YAChE,CAAC;QACL,CAAC;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,+FAA+F;IAC/F,sJAAsJ;IACtJ,MAAM,CAAC,MAAM,CAAC,KAAc;QACxB,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC;IAED,4EAA4E;IAC5E,wJAAwJ;IACxJ,MAAM,CAAC,SAAS,CAAC,KAAc;QAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QAC9D,MAAM,IAAI,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC;QACnE,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAoB,CAAC,CAAC;IAC5E,CAAC;IAED,yJAAyJ;IACjJ,MAAM,CAAC,WAAW,CAAC,KAAc,EAAE,KAAa;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAC3D,MAAM,KAAK,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC;YACrE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBAC9C,wGAAwG;gBACxG,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE;oBAClC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC;oBACzC,YAAY,EAAE,IAAI;oBAClB,QAAQ,EAAE,IAAI;iBACjB,CAAC,CAAC;YACP,CAAC;QACL,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,4IAA4I;IACpI,MAAM,CAAC,SAAS,CAAC,KAAc;QACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,yBAAyB,EAAE,CAAC;QACzF,sFAAsF;QACtF,MAAM,KAAK,GAAG,CAAC,GAAW,EAAW,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC;QAC3F,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClE,QAAQ,IAAI,EAAE,CAAC;YACX,KAAK,UAAU;gBACX,OAAO,IAAI,0BAAe,CACtB,OAAO,EACP,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,EAC7B,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAC3C,CAAC;YACN,KAAK,aAAa;gBACd,OAAO,IAAI,6BAAkB,CACzB,OAAO,EACP,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EACzB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CACpC,CAAC;YACN,KAAK,cAAc;gBACf,OAAO,IAAI,+BAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAC1E,KAAK,WAAW;gBACZ,OAAO,IAAI,4BAAiB,CAAC,OAAO,CAAC,CAAC;YAC1C,KAAK,WAAW;gBACZ,OAAO,IAAI,2BAAgB,CAAC,OAAO,CAAC,CAAC;YACzC,KAAK,oBAAoB;gBACrB,OAAO,IAAI,mCAAwB,CAAC,OAAO,CAAC,CAAC;YACjD,KAAK,iBAAiB;gBAClB,OAAO,IAAI,iCAAsB,CAAC,OAAO,CAAC,CAAC;YAC/C,KAAK,cAAc;gBACf,OAAO,IAAI,8BAAmB,CAAC,OAAO,CAAC,CAAC;YAC5C,KAAK,UAAU;gBACX,OAAO,IAAI,2BAAgB,CAAC,OAAO,CAAC,CAAC;YACzC,KAAK,eAAe;gBAChB,OAAO,IAAI,gCAAqB,CAAC,OAAO,CAAC,CAAC;YAC9C,KAAK,qBAAqB;gBACtB,OAAO,IAAI,qCAA0B,CAAC,OAAO,CAAC,CAAC;YACnD,KAAK,wBAAwB;gBACzB,OAAO,IAAI,uCAA4B,CAAC,OAAO,CAAC,CAAC;YACrD,KAAK,iBAAiB;gBAClB,OAAO,IAAI,iCAAsB,CAAC,OAAO,CAAC,CAAC;YAC/C,KAAK,OAAO;gBACR,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;YACxE,KAAK,YAAY;gBACb,OAAO,IAAI,6BAAkB,CAAC,OAAO,CAAC,CAAC;YAC3C,KAAK,aAAa;gBACd,OAAO,IAAI,8BAAmB,CAAC,OAAO,CAAC,CAAC;YAC5C,KAAK,oBAAoB;gBACrB,OAAO,IAAI,oCAAyB,CAAC,OAAO,CAAC,CAAC;YAClD,KAAK,oBAAoB;gBACrB,OAAO,IAAI,oCAAyB,CAChC,OAAO,EACP,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,CACxC,CAAC;YACN,KAAK,YAAY;gBACb,OAAO,IAAI,6BAAkB,CAAC,OAAO,CAAC,CAAC;YAC3C,KAAK,gBAAgB;gBACjB,OAAO,IAAI,iCAAsB,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;YAChE;gBACI,OAAO,IAAI,CAAC,yBAAyB,EAAE,CAAC;QAChD,CAAC;IACL,CAAC;IAED,uIAAuI;IAC/H,MAAM,CAAC,KAAK,CAAC,OAAe,EAAE,UAAmB,EAAE,SAAkB;QACzE,IAAI,CAAC,wBAAa,CAAC,YAAY,CAAC,UAAU,CAAC;YAAE,OAAO,IAAI,CAAC,yBAAyB,EAAE,CAAC;QACrF,OAAO,IAAI,wBAAa,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,oGAAoG;IAC5F,MAAM,CAAC,yBAAyB;QACpC,OAAO,IAAI,iCAAsB,CAAC,gBAAgB,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IACzE,CAAC;IAED,+EAA+E;IACvE,MAAM,CAAC,aAAa,CAAC,KAAY,EAAE,IAAkB;QACzD,IAAI,KAAK,YAAY,0BAAe;YAAE,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1E,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,uIAAuI;IAC/H,MAAM,CAAC,IAAI,CAAC,KAAc;QAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxE,CAAC;IAED,6GAA6G;IAC7G,uIAAuI;IAC/H,MAAM,CAAC,UAAU,CAAC,KAAc;QACpC,OAAO,0BAAe,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,CAAC;IAED,uIAAuI;IAC/H,MAAM,CAAC,IAAI,CAAC,KAAc;QAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;YACpE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC;YACxB,CAAC,CAAC,SAAS,CAAC;IACpB,CAAC;IAED,wJAAwJ;IAChJ,MAAM,CAAC,OAAO,CAAC,IAAa;QAChC,QAAQ,IAAI,EAAE,CAAC;YACX,KAAK,UAAU;gBACX,OAAO,gBAAgB,CAAC;YAC5B,KAAK,aAAa;gBACd,OAAO,aAAa,CAAC;YACzB,KAAK,cAAc;gBACf,OAAO,cAAc,CAAC;YAC1B,KAAK,WAAW;gBACZ,OAAO,WAAW,CAAC;YACvB,KAAK,WAAW;gBACZ,OAAO,WAAW,CAAC;YACvB,KAAK,oBAAoB;gBACrB,OAAO,oBAAoB,CAAC;YAChC,KAAK,iBAAiB;gBAClB,OAAO,iBAAiB,CAAC;YAC7B,KAAK,cAAc;gBACf,OAAO,cAAc,CAAC;YAC1B,KAAK,UAAU;gBACX,OAAO,UAAU,CAAC;YACtB,KAAK,eAAe;gBAChB,OAAO,uBAAuB,CAAC;YACnC,KAAK,qBAAqB;gBACtB,OAAO,qBAAqB,CAAC;YACjC,KAAK,wBAAwB;gBACzB,OAAO,wBAAwB,CAAC;YACpC,KAAK,iBAAiB;gBAClB,OAAO,iBAAiB,CAAC;YAC7B,KAAK,OAAO;gBACR,OAAO,gBAAgB,CAAC;YAC5B,KAAK,YAAY;gBACb,OAAO,kBAAkB,CAAC;YAC9B,KAAK,aAAa;gBACd,OAAO,qBAAqB,CAAC;YACjC,KAAK,oBAAoB;gBACrB,OAAO,oBAAoB,CAAC;YAChC,KAAK,oBAAoB;gBACrB,OAAO,wBAAwB,CAAC;YACpC,KAAK,YAAY;gBACb,OAAO,kBAAkB,CAAC;YAC9B;gBACI,OAAO,gBAAgB,CAAC;QAChC,CAAC;IACL,CAAC;IAEO,MAAM,CAAU,KAAK,GAA8B,IAAI,GAAG,CAAe;QAC7E,UAAU;QACV,aAAa;QACb,cAAc;QACd,WAAW;QACX,WAAW;QACX,oBAAoB;QACpB,iBAAiB;QACjB,cAAc;QACd,UAAU;QACV,eAAe;QACf,qBAAqB;QACrB,wBAAwB;QACxB,iBAAiB;QACjB,OAAO;QACP,gBAAgB;QAChB,YAAY;QACZ,aAAa;QACb,oBAAoB;QACpB,oBAAoB;QACpB,YAAY;KACf,CAAC,CAAC;;AApPP,sCAqPC","sourcesContent":["import {\n ApiBadRequestError,\n ApiCodedError,\n ApiConflictError,\n ApiConnectionError,\n ApiDependencyBackoffError,\n ApiDependencyError,\n ApiDependencyTimeoutError,\n ApiEndpointNotFoundError,\n ApiEndUserError,\n ApiError,\n ApiErrorKind,\n ApiForbiddenError,\n ApiImplementationError,\n ApiNotFoundError,\n ApiNotImplementedError,\n ApiPreconditionFailedError,\n ApiRateLimitedError,\n ApiRequestTimeoutError,\n ApiUnauthorizedError,\n ApiUnavailableError,\n ApiUnprocessableError,\n ApiUnsupportedMediaTypeError,\n EdgeHttpStatus,\n} from './ApiError';\nimport { toError } from '../lib/errorUtils';\n\n/** Allowlisted transport-neutral envelope. It never contains stacks or arbitrary properties. */\nexport class ApiErrorPayload {\n public cause?: ApiErrorPayload;\n public subType?: string;\n public field?: string;\n public callerMessage?: string;\n public errorCode?: string;\n public retryAfterSeconds?: number;\n public statusCode?: number;\n /** {@link ApiEndUserError.edgeHttpStatus}; absent when the thrower (or an older peer) set none. */\n public edgeHttpStatus?: number;\n\n constructor(\n public kind: string = 'implementation',\n public message: string = 'Internal Error',\n ) {}\n}\n\n/** Bounded safe codec shared by HTTP, IPC, and other remote adapters. */\nexport class ApiErrorCodec {\n /**\n * Encode a thrown error faithfully. The input is an `Error`, never `unknown`: every caller in\n * the framework has already narrowed with `toError` at its catch, and the `unknown` this used to\n * advertise leaked out of the PRIVATE `.cause` recursion below (`Error.cause` is typed `unknown`\n * in TypeScript), which is an internal concern.\n *\n * This is the FAITHFUL encode: it publishes `kind` exactly as the error declares it. The\n * boundary rule that a caller-local `ApiConnectionError` publishes as `implementation` belongs\n * to {@link ApiErrorBoundary}, which every protocol edge uses instead of calling this directly.\n */\n // webpieces-disable no-function-outside-class -- stateless public transport codec\n static encode(error: Error): ApiErrorPayload {\n return this.encodeDepth(error, 0);\n }\n\n // webpieces-disable no-function-outside-class -- recursive bounded transport codec\n private static encodeDepth(error: Error, depth: number): ApiErrorPayload {\n const kind = error instanceof ApiError ? error.kind : 'implementation';\n const payload = new ApiErrorPayload(kind, this.publicMessage(error, kind));\n if (error instanceof ApiError) payload.subType = this.text(error.subType);\n if (error instanceof ApiEndUserError) {\n payload.errorCode = this.text(error.errorCode);\n payload.edgeHttpStatus = error.edgeHttpStatus;\n }\n if (error instanceof ApiCodedError) {\n payload.statusCode = error.statusCode;\n payload.errorCode = this.text(error.errorCode);\n }\n if (error instanceof ApiBadRequestError) {\n payload.field = this.text(error.field);\n payload.callerMessage = this.text(error.callerMessage);\n }\n if (error instanceof ApiDependencyBackoffError) {\n payload.retryAfterSeconds = this.wait(error.retryAfterSeconds);\n }\n if (depth < 3) {\n // `Error.cause` is `unknown` by construction, so the recursion narrows it with `toError`\n // exactly as a catch block would — and then deals only in `Error` like everything else.\n const cause = Object.getOwnPropertyDescriptor(error, 'cause')?.value;\n if (cause !== undefined && cause !== null) {\n payload.cause = this.encodeDepth(toError(cause), depth + 1);\n }\n }\n return payload;\n }\n\n /** Decode a value received from another process. Implementation failures are marked remote. */\n // webpieces-disable no-function-outside-class -- stateless public transport codec; webpieces-disable no-any-unknown -- wire values require validation\n static decode(value: unknown): ApiError {\n return this.decodeDepth(value, 0);\n }\n\n /** Whether a body carries a recognized Webpieces semantic discriminator. */\n // webpieces-disable no-function-outside-class -- stateless wire discriminator check; webpieces-disable no-any-unknown -- wire values require validation\n static isPayload(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) return false;\n const kind = Object.getOwnPropertyDescriptor(value, 'kind')?.value;\n return typeof kind === 'string' && this.kinds.has(kind as ApiErrorKind);\n }\n\n // webpieces-disable no-function-outside-class -- recursive bounded transport codec; webpieces-disable no-any-unknown -- remote causes require validation\n private static decodeDepth(value: unknown, depth: number): ApiError {\n const error = this.decodeOne(value);\n if (typeof value === 'object' && value !== null && depth < 3) {\n const cause = Object.getOwnPropertyDescriptor(value, 'cause')?.value;\n if (typeof cause === 'object' && cause !== null) {\n // webpieces-disable no-anonymous-object-literals -- standard descriptor preserves Error.cause semantics\n Object.defineProperty(error, 'cause', {\n value: this.decodeDepth(cause, depth + 1),\n configurable: true,\n writable: true,\n });\n }\n }\n return error;\n }\n\n // webpieces-disable no-function-outside-class -- allowlisted wire codec; webpieces-disable no-any-unknown -- wire fields require validation\n private static decodeOne(value: unknown): ApiError {\n if (typeof value !== 'object' || value === null) return this.remoteImplementationError();\n // webpieces-disable no-any-unknown -- own wire fields remain untrusted until narrowed\n const field = (key: string): unknown => Object.getOwnPropertyDescriptor(value, key)?.value;\n const kind = field('kind');\n const message = this.text(field('message')) ?? this.message(kind);\n switch (kind) {\n case 'end-user':\n return new ApiEndUserError(\n message,\n this.text(field('errorCode')),\n this.edgeStatus(field('edgeHttpStatus')),\n );\n case 'bad-request':\n return new ApiBadRequestError(\n message,\n this.text(field('field')),\n this.text(field('callerMessage')),\n );\n case 'unauthorized':\n return new ApiUnauthorizedError(message, this.text(field('subType')));\n case 'forbidden':\n return new ApiForbiddenError(message);\n case 'not-found':\n return new ApiNotFoundError(message);\n case 'endpoint-not-found':\n return new ApiEndpointNotFoundError(message);\n case 'request-timeout':\n return new ApiRequestTimeoutError(message);\n case 'rate-limited':\n return new ApiRateLimitedError(message);\n case 'conflict':\n return new ApiConflictError(message);\n case 'unprocessable':\n return new ApiUnprocessableError(message);\n case 'precondition-failed':\n return new ApiPreconditionFailedError(message);\n case 'unsupported-media-type':\n return new ApiUnsupportedMediaTypeError(message);\n case 'not-implemented':\n return new ApiNotImplementedError(message);\n case 'coded':\n return this.coded(message, field('statusCode'), field('errorCode'));\n case 'dependency':\n return new ApiDependencyError(message);\n case 'unavailable':\n return new ApiUnavailableError(message);\n case 'dependency-timeout':\n return new ApiDependencyTimeoutError(message);\n case 'dependency-backoff':\n return new ApiDependencyBackoffError(\n message,\n this.wait(field('retryAfterSeconds')),\n );\n case 'connection':\n return new ApiConnectionError(message);\n case 'implementation':\n return new ApiImplementationError(message, undefined, true);\n default:\n return this.remoteImplementationError();\n }\n }\n\n // webpieces-disable no-function-outside-class -- coded wire decode; webpieces-disable no-any-unknown -- wire fields require validation\n private static coded(message: string, statusCode: unknown, errorCode: unknown): ApiError {\n if (!ApiCodedError.isStatusCode(statusCode)) return this.remoteImplementationError();\n return new ApiCodedError(message, statusCode, this.text(errorCode));\n }\n\n // webpieces-disable no-function-outside-class -- concrete normalization for malformed remote errors\n private static remoteImplementationError(): ApiImplementationError {\n return new ApiImplementationError('Internal Error', undefined, true);\n }\n\n // webpieces-disable no-function-outside-class -- caller-safe message selection\n private static publicMessage(error: Error, kind: ApiErrorKind): string {\n if (error instanceof ApiEndUserError) return error.message.slice(0, 4096);\n return this.message(kind);\n }\n\n // webpieces-disable no-function-outside-class -- wire string bound; webpieces-disable no-any-unknown -- wire values require validation\n private static text(value: unknown): string | undefined {\n return typeof value === 'string' ? value.slice(0, 4096) : undefined;\n }\n\n /** Tolerant: a peer that sends nothing, or a status outside {@link EdgeHttpStatus}, decodes as undefined. */\n // webpieces-disable no-function-outside-class -- wire status bound; webpieces-disable no-any-unknown -- wire values require validation\n private static edgeStatus(value: unknown): EdgeHttpStatus | undefined {\n return ApiEndUserError.isEdgeHttpStatus(value) ? value : undefined;\n }\n\n // webpieces-disable no-function-outside-class -- wire number bound; webpieces-disable no-any-unknown -- wire values require validation\n private static wait(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0\n ? Math.min(value, 86400)\n : undefined;\n }\n\n // webpieces-disable no-function-outside-class -- allowlisted generic messages; webpieces-disable no-any-unknown -- discriminator is validated by switch\n private static message(kind: unknown): string {\n switch (kind) {\n case 'end-user':\n return 'End User Error';\n case 'bad-request':\n return 'Bad Request';\n case 'unauthorized':\n return 'Unauthorized';\n case 'forbidden':\n return 'Forbidden';\n case 'not-found':\n return 'Not Found';\n case 'endpoint-not-found':\n return 'Endpoint Not Found';\n case 'request-timeout':\n return 'Request Timeout';\n case 'rate-limited':\n return 'Rate Limited';\n case 'conflict':\n return 'Conflict';\n case 'unprocessable':\n return 'Unprocessable Content';\n case 'precondition-failed':\n return 'Precondition Failed';\n case 'unsupported-media-type':\n return 'Unsupported Media Type';\n case 'not-implemented':\n return 'Not Implemented';\n case 'coded':\n return 'Request Failed';\n case 'dependency':\n return 'Dependency Error';\n case 'unavailable':\n return 'Service Unavailable';\n case 'dependency-timeout':\n return 'Dependency Timeout';\n case 'dependency-backoff':\n return 'Dependency Unavailable';\n case 'connection':\n return 'Connection Error';\n default:\n return 'Internal Error';\n }\n }\n\n private static readonly kinds: ReadonlySet<ApiErrorKind> = new Set<ApiErrorKind>([\n 'end-user',\n 'bad-request',\n 'unauthorized',\n 'forbidden',\n 'not-found',\n 'endpoint-not-found',\n 'request-timeout',\n 'rate-limited',\n 'conflict',\n 'unprocessable',\n 'precondition-failed',\n 'unsupported-media-type',\n 'not-implemented',\n 'coded',\n 'implementation',\n 'dependency',\n 'unavailable',\n 'dependency-timeout',\n 'dependency-backoff',\n 'connection',\n ]);\n}\n"]}
|
|
@@ -1,6 +1,27 @@
|
|
|
1
|
-
import { ApiError } from '../errors/ApiError';
|
|
1
|
+
import { ApiError, ApiErrorKind } from '../errors/ApiError';
|
|
2
|
+
/**
|
|
3
|
+
* Every kind that HAS an HTTP status. `'connection'` is excluded on purpose: a caller-local
|
|
4
|
+
* connection failure is never published as itself — `ApiErrorBoundary.encode` publishes it as
|
|
5
|
+
* `implementation` — so asking this table for its status is a bug the COMPILER now rejects rather
|
|
6
|
+
* than a `throw` at the bottom of a switch.
|
|
7
|
+
*/
|
|
8
|
+
export type PublishedKind = Exclude<ApiErrorKind, 'connection'>;
|
|
9
|
+
/** An {@link ApiError} that {@link ApiErrorHttpStatus.hasCode} has proved carries an HTTP status. */
|
|
10
|
+
export type PublishedApiError = ApiError & {
|
|
11
|
+
readonly kind: PublishedKind;
|
|
12
|
+
};
|
|
2
13
|
/** HTTP-only adapter: semantic errors themselves have no protocol status property. */
|
|
3
14
|
export declare class ApiErrorHttpStatus {
|
|
4
|
-
|
|
5
|
-
|
|
15
|
+
/**
|
|
16
|
+
* CLIENT direction. A client that just DECODED a payload holds an `ApiError`, and needs to know
|
|
17
|
+
* whether the status it received agrees with the kind the body claims. This narrows the error to
|
|
18
|
+
* the kinds {@link codeFor} accepts, so the pair reads as one guarded call with no cast.
|
|
19
|
+
*/
|
|
20
|
+
static hasCode(error: ApiError): error is PublishedApiError;
|
|
21
|
+
/**
|
|
22
|
+
* The ONE status table, keyed by the published kind. `statusCode` is the `'coded'` kind's own
|
|
23
|
+
* status — it travels on {@link ApiErrorPayload.statusCode} and on `ApiCodedError.statusCode`,
|
|
24
|
+
* so both directions can supply it without this table reaching back into the error object.
|
|
25
|
+
*/
|
|
26
|
+
static codeFor(kind: PublishedKind, statusCode?: number): number;
|
|
6
27
|
}
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ApiErrorHttpStatus = void 0;
|
|
4
|
-
const ApiError_1 = require("../errors/ApiError");
|
|
5
4
|
/** HTTP-only adapter: semantic errors themselves have no protocol status property. */
|
|
6
5
|
class ApiErrorHttpStatus {
|
|
6
|
+
/**
|
|
7
|
+
* CLIENT direction. A client that just DECODED a payload holds an `ApiError`, and needs to know
|
|
8
|
+
* whether the status it received agrees with the kind the body claims. This narrows the error to
|
|
9
|
+
* the kinds {@link codeFor} accepts, so the pair reads as one guarded call with no cast.
|
|
10
|
+
*/
|
|
7
11
|
// webpieces-disable no-function-outside-class -- stateless mapping predicate
|
|
8
12
|
static hasCode(error) {
|
|
9
13
|
return error.kind !== 'connection';
|
|
10
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* The ONE status table, keyed by the published kind. `statusCode` is the `'coded'` kind's own
|
|
17
|
+
* status — it travels on {@link ApiErrorPayload.statusCode} and on `ApiCodedError.statusCode`,
|
|
18
|
+
* so both directions can supply it without this table reaching back into the error object.
|
|
19
|
+
*/
|
|
11
20
|
// webpieces-disable no-function-outside-class -- stateless mapping shared by HTTP adapters
|
|
12
|
-
static
|
|
13
|
-
switch (
|
|
21
|
+
static codeFor(kind, statusCode) {
|
|
22
|
+
switch (kind) {
|
|
14
23
|
case 'end-user':
|
|
15
24
|
return 266;
|
|
16
25
|
case 'bad-request':
|
|
@@ -39,9 +48,9 @@ class ApiErrorHttpStatus {
|
|
|
39
48
|
case 'not-implemented':
|
|
40
49
|
return 501;
|
|
41
50
|
case 'coded':
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
// A 'coded' failure whose status did not survive the wire is an internal failure to
|
|
52
|
+
// this adapter, not a status it may invent.
|
|
53
|
+
return statusCode ?? 500;
|
|
45
54
|
case 'dependency':
|
|
46
55
|
return 502;
|
|
47
56
|
case 'unavailable':
|
|
@@ -49,8 +58,6 @@ class ApiErrorHttpStatus {
|
|
|
49
58
|
return 503;
|
|
50
59
|
case 'dependency-timeout':
|
|
51
60
|
return 504;
|
|
52
|
-
case 'connection':
|
|
53
|
-
throw new Error('ApiConnectionError has no HTTP status; normalize it at the server boundary');
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiErrorHttpStatus.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ApiErrorHttpStatus.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"ApiErrorHttpStatus.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ApiErrorHttpStatus.ts"],"names":[],"mappings":";;;AAaA,sFAAsF;AACtF,MAAa,kBAAkB;IAC3B;;;;OAIG;IACH,6EAA6E;IAC7E,MAAM,CAAC,OAAO,CAAC,KAAe;QAC1B,OAAO,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC;IACvC,CAAC;IAED;;;;OAIG;IACH,2FAA2F;IAC3F,MAAM,CAAC,OAAO,CAAC,IAAmB,EAAE,UAAmB;QACnD,QAAQ,IAAI,EAAE,CAAC;YACX,KAAK,UAAU;gBACX,OAAO,GAAG,CAAC;YACf,KAAK,aAAa;gBACd,OAAO,GAAG,CAAC;YACf,KAAK,cAAc;gBACf,OAAO,GAAG,CAAC;YACf,KAAK,WAAW;gBACZ,OAAO,GAAG,CAAC;YACf,KAAK,WAAW,CAAC;YACjB,KAAK,oBAAoB;gBACrB,OAAO,GAAG,CAAC;YACf,KAAK,iBAAiB;gBAClB,OAAO,GAAG,CAAC;YACf,KAAK,UAAU;gBACX,OAAO,GAAG,CAAC;YACf,KAAK,qBAAqB;gBACtB,OAAO,GAAG,CAAC;YACf,KAAK,wBAAwB;gBACzB,OAAO,GAAG,CAAC;YACf,KAAK,eAAe;gBAChB,OAAO,GAAG,CAAC;YACf,KAAK,cAAc;gBACf,OAAO,GAAG,CAAC;YACf,KAAK,gBAAgB;gBACjB,OAAO,GAAG,CAAC;YACf,KAAK,iBAAiB;gBAClB,OAAO,GAAG,CAAC;YACf,KAAK,OAAO;gBACR,oFAAoF;gBACpF,4CAA4C;gBAC5C,OAAO,UAAU,IAAI,GAAG,CAAC;YAC7B,KAAK,YAAY;gBACb,OAAO,GAAG,CAAC;YACf,KAAK,aAAa,CAAC;YACnB,KAAK,oBAAoB;gBACrB,OAAO,GAAG,CAAC;YACf,KAAK,oBAAoB;gBACrB,OAAO,GAAG,CAAC;QACnB,CAAC;IACL,CAAC;CACJ;AA3DD,gDA2DC","sourcesContent":["import { ApiError, ApiErrorKind } from '../errors/ApiError';\n\n/**\n * Every kind that HAS an HTTP status. `'connection'` is excluded on purpose: a caller-local\n * connection failure is never published as itself — `ApiErrorBoundary.encode` publishes it as\n * `implementation` — so asking this table for its status is a bug the COMPILER now rejects rather\n * than a `throw` at the bottom of a switch.\n */\nexport type PublishedKind = Exclude<ApiErrorKind, 'connection'>;\n\n/** An {@link ApiError} that {@link ApiErrorHttpStatus.hasCode} has proved carries an HTTP status. */\nexport type PublishedApiError = ApiError & { readonly kind: PublishedKind };\n\n/** HTTP-only adapter: semantic errors themselves have no protocol status property. */\nexport class ApiErrorHttpStatus {\n /**\n * CLIENT direction. A client that just DECODED a payload holds an `ApiError`, and needs to know\n * whether the status it received agrees with the kind the body claims. This narrows the error to\n * the kinds {@link codeFor} accepts, so the pair reads as one guarded call with no cast.\n */\n // webpieces-disable no-function-outside-class -- stateless mapping predicate\n static hasCode(error: ApiError): error is PublishedApiError {\n return error.kind !== 'connection';\n }\n\n /**\n * The ONE status table, keyed by the published kind. `statusCode` is the `'coded'` kind's own\n * status — it travels on {@link ApiErrorPayload.statusCode} and on `ApiCodedError.statusCode`,\n * so both directions can supply it without this table reaching back into the error object.\n */\n // webpieces-disable no-function-outside-class -- stateless mapping shared by HTTP adapters\n static codeFor(kind: PublishedKind, statusCode?: number): number {\n switch (kind) {\n case 'end-user':\n return 266;\n case 'bad-request':\n return 400;\n case 'unauthorized':\n return 401;\n case 'forbidden':\n return 403;\n case 'not-found':\n case 'endpoint-not-found':\n return 404;\n case 'request-timeout':\n return 408;\n case 'conflict':\n return 409;\n case 'precondition-failed':\n return 412;\n case 'unsupported-media-type':\n return 415;\n case 'unprocessable':\n return 422;\n case 'rate-limited':\n return 429;\n case 'implementation':\n return 500;\n case 'not-implemented':\n return 501;\n case 'coded':\n // A 'coded' failure whose status did not survive the wire is an internal failure to\n // this adapter, not a status it may invent.\n return statusCode ?? 500;\n case 'dependency':\n return 502;\n case 'unavailable':\n case 'dependency-backoff':\n return 503;\n case 'dependency-timeout':\n return 504;\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMPILE-time assertions for {@link ApiErrorHttpStatus}. In a COMPILED file, never a `.spec.ts`:
|
|
3
|
+
* `tsconfig.lib.json` excludes specs and vitest strips types with esbuild, so a `@ts-expect-error`
|
|
4
|
+
* in a spec is inert and the suite passes either way (`.claude/rules/no-backwards-compat.md`).
|
|
5
|
+
*
|
|
6
|
+
* What is pinned: `'connection'` is UNREPRESENTABLE as an argument. The table used to end in
|
|
7
|
+
* `throw new Error('ApiConnectionError has no HTTP status; normalize it at the server boundary')` —
|
|
8
|
+
* a runtime throw standing in for a type that could not express the bad state, which is shim shape
|
|
9
|
+
* #4. `PublishedKind = Exclude<ApiErrorKind, 'connection'>` deletes the throw by making the call a
|
|
10
|
+
* COMPILE error, and tsc fails the build with TS2578 if either line below ever starts compiling.
|
|
11
|
+
*/
|
|
12
|
+
export declare class ApiErrorHttpStatusCompileAssertions {
|
|
13
|
+
connectionKindIsNotAStatus(): void;
|
|
14
|
+
aConnectionErrorsKindIsNotAStatusEither(): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ApiErrorHttpStatusCompileAssertions = void 0;
|
|
4
|
+
const ApiError_1 = require("../errors/ApiError");
|
|
5
|
+
const ApiErrorHttpStatus_1 = require("./ApiErrorHttpStatus");
|
|
6
|
+
/**
|
|
7
|
+
* COMPILE-time assertions for {@link ApiErrorHttpStatus}. In a COMPILED file, never a `.spec.ts`:
|
|
8
|
+
* `tsconfig.lib.json` excludes specs and vitest strips types with esbuild, so a `@ts-expect-error`
|
|
9
|
+
* in a spec is inert and the suite passes either way (`.claude/rules/no-backwards-compat.md`).
|
|
10
|
+
*
|
|
11
|
+
* What is pinned: `'connection'` is UNREPRESENTABLE as an argument. The table used to end in
|
|
12
|
+
* `throw new Error('ApiConnectionError has no HTTP status; normalize it at the server boundary')` —
|
|
13
|
+
* a runtime throw standing in for a type that could not express the bad state, which is shim shape
|
|
14
|
+
* #4. `PublishedKind = Exclude<ApiErrorKind, 'connection'>` deletes the throw by making the call a
|
|
15
|
+
* COMPILE error, and tsc fails the build with TS2578 if either line below ever starts compiling.
|
|
16
|
+
*/
|
|
17
|
+
class ApiErrorHttpStatusCompileAssertions {
|
|
18
|
+
connectionKindIsNotAStatus() {
|
|
19
|
+
// @ts-expect-error - 'connection' is not a PublishedKind: the boundary publishes it as 'implementation'
|
|
20
|
+
ApiErrorHttpStatus_1.ApiErrorHttpStatus.codeFor('connection');
|
|
21
|
+
}
|
|
22
|
+
aConnectionErrorsKindIsNotAStatusEither() {
|
|
23
|
+
const error = new ApiError_1.ApiConnectionError('ECONNREFUSED');
|
|
24
|
+
// @ts-expect-error - the same, reached through the error object rather than the literal
|
|
25
|
+
const kind = error.kind;
|
|
26
|
+
void kind;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.ApiErrorHttpStatusCompileAssertions = ApiErrorHttpStatusCompileAssertions;
|
|
30
|
+
//# sourceMappingURL=ApiErrorHttpStatusCompileAssertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiErrorHttpStatusCompileAssertions.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ApiErrorHttpStatusCompileAssertions.ts"],"names":[],"mappings":";;;AAAA,iDAAwD;AACxD,6DAAyE;AAEzE;;;;;;;;;;GAUG;AACH,MAAa,mCAAmC;IAC5C,0BAA0B;QACtB,wGAAwG;QACxG,uCAAkB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7C,CAAC;IAED,uCAAuC;QACnC,MAAM,KAAK,GAAG,IAAI,6BAAkB,CAAC,cAAc,CAAC,CAAC;QACrD,wFAAwF;QACxF,MAAM,IAAI,GAAkB,KAAK,CAAC,IAAI,CAAC;QACvC,KAAK,IAAI,CAAC;IACd,CAAC;CACJ;AAZD,kFAYC","sourcesContent":["import { ApiConnectionError } from '../errors/ApiError';\nimport { ApiErrorHttpStatus, PublishedKind } from './ApiErrorHttpStatus';\n\n/**\n * COMPILE-time assertions for {@link ApiErrorHttpStatus}. In a COMPILED file, never a `.spec.ts`:\n * `tsconfig.lib.json` excludes specs and vitest strips types with esbuild, so a `@ts-expect-error`\n * in a spec is inert and the suite passes either way (`.claude/rules/no-backwards-compat.md`).\n *\n * What is pinned: `'connection'` is UNREPRESENTABLE as an argument. The table used to end in\n * `throw new Error('ApiConnectionError has no HTTP status; normalize it at the server boundary')` —\n * a runtime throw standing in for a type that could not express the bad state, which is shim shape\n * #4. `PublishedKind = Exclude<ApiErrorKind, 'connection'>` deletes the throw by making the call a\n * COMPILE error, and tsc fails the build with TS2578 if either line below ever starts compiling.\n */\nexport class ApiErrorHttpStatusCompileAssertions {\n connectionKindIsNotAStatus(): void {\n // @ts-expect-error - 'connection' is not a PublishedKind: the boundary publishes it as 'implementation'\n ApiErrorHttpStatus.codeFor('connection');\n }\n\n aConnectionErrorsKindIsNotAStatusEither(): void {\n const error = new ApiConnectionError('ECONNREFUSED');\n // @ts-expect-error - the same, reached through the error object rather than the literal\n const kind: PublishedKind = error.kind;\n void kind;\n }\n}\n"]}
|