@webpieces/core-util 0.4.787 → 0.4.789

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.787",
3
+ "version": "0.4.789",
4
4
  "description": "Utility functions for WebPieces - works in browser and Node.js",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -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 thrown value before
6
- * rendering it, plus the one operator log line per failure. Adapters differ only in the wire shape
7
- * they render; classification and log levels live here so they cannot drift apart.
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 and the operator log still
11
- * names it. Classification is a VALUE computed from the thrown error, never a wrapper around it.
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
- * Logs one operator line at the per-kind level: expected caller outcomes at info, everything else
36
- * at error. `correlation` is appended verbatim (for example `requestId=... tool=...`).
34
+ * The wire payload THIS service publishes for `error`.
37
35
  *
38
- * The line names the class that was ACTUALLY thrown, which is the single fact an operator needs
39
- * and the one a wrapper would have destroyed.
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
- logOperatorDetail(thrown: unknown, correlation?: string): void;
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 thrown value before
9
- * rendering it, plus the one operator log line per failure. Adapters differ only in the wire shape
10
- * they render; classification and log levels live here so they cannot drift apart.
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 and the operator log still
14
- * names it. Classification is a VALUE computed from the thrown error, never a wrapper around it.
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 `thrown`. Only an {@link ApiEndUserError} ever
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
- * The line names the class that was ACTUALLY thrown, which is the single fact an operator needs
55
- * and the one a wrapper would have destroyed.
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
- // webpieces-disable no-any-unknown -- thrown values are unknown until classified at this boundary
58
- logOperatorDetail(thrown, correlation) {
59
- const suffix = correlation ? ` ${correlation}` : '';
60
- const detail = `${this.operatorDetail(thrown)}${suffix}`;
61
- if (thrown instanceof ApiError_1.ApiEndUserError)
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":"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","sourcesContent":["import { Logger } from '../logging/Logger';\nimport { toError } from '../lib/errorUtils';\nimport { ApiErrorCodec, ApiErrorPayload } from './ApiErrorCodec';\nimport {\n ApiBadRequestError,\n ApiCodedError,\n ApiConflictError,\n ApiConnectionError,\n ApiEndUserError,\n ApiError,\n ApiForbiddenError,\n ApiNotFoundError,\n ApiPreconditionFailedError,\n ApiRequestTimeoutError,\n ApiUnauthorizedError,\n ApiUnprocessableError,\n ApiUnsupportedMediaTypeError,\n} from './ApiError';\n\n/**\n * The ONE rule every outbound protocol boundary (HTTP server, MCP) applies to a thrown value before\n * rendering it, plus the one operator log line per failure. Adapters differ only in the wire shape\n * they render; classification and log levels live here so they cannot drift apart.\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 and the operator log still\n * names it. Classification is a VALUE computed from the thrown error, never a wrapper around it.\n */\nexport class ApiErrorBoundary {\n constructor(private readonly log: Logger) {}\n\n /**\n * The classified API outcome `thrown` publishes as, or `undefined` when it is a bug or broken\n * state of THIS service — a non-`ApiError`, a non-`Error` throw, or a caller-local\n * `ApiConnectionError`.\n *\n * A connection failure is OUR outbound call failing, so from our caller's seat this service is\n * the thing that is broken: it publishes as `implementation`, never as `connection`. That rule\n * is deliberately NOT in `ApiErrorCodec`, which encodes a value faithfully; it belongs to the\n * boundary that owns the failure.\n *\n * The returned error IS the thrown error — never a replacement.\n */\n // webpieces-disable no-any-unknown -- thrown values are unknown until classified at this boundary\n apiOutcome(thrown: unknown): ApiError | undefined {\n if (thrown instanceof ApiError && !(thrown instanceof ApiConnectionError)) return thrown;\n return undefined;\n }\n\n /**\n * The wire payload THIS service publishes for `thrown`. Only an {@link ApiEndUserError} ever\n * publishes its own message; everything else gets allowlisted generic text by kind.\n */\n // webpieces-disable no-any-unknown -- thrown values are unknown until classified at this boundary\n encode(thrown: unknown): ApiErrorPayload {\n const outcome = this.apiOutcome(thrown);\n if (!outcome) return new ApiErrorPayload('implementation', 'Internal Error');\n return ApiErrorCodec.encode(outcome);\n }\n\n /**\n * Logs one operator line at the per-kind level: expected caller outcomes at info, everything else\n * at error. `correlation` is appended verbatim (for example `requestId=... tool=...`).\n *\n * The line names the class that was ACTUALLY thrown, which is the single fact an operator needs\n * and the one a wrapper would have destroyed.\n */\n // webpieces-disable no-any-unknown -- thrown values are unknown until classified at this boundary\n logOperatorDetail(thrown: unknown, correlation?: string): void {\n const suffix = correlation ? ` ${correlation}` : '';\n const detail = `${this.operatorDetail(thrown)}${suffix}`;\n if (thrown instanceof ApiEndUserError) this.log.info(`End User Error: ${detail}`);\n else if (thrown instanceof ApiBadRequestError) this.log.info(`Bad Request: ${detail}`);\n else if (thrown instanceof ApiNotFoundError) this.log.info(`Not Found: ${detail}`);\n else if (thrown instanceof ApiUnauthorizedError) this.log.info(`Unauthorized: ${detail}`);\n else if (thrown instanceof ApiForbiddenError) this.log.info(`Forbidden: ${detail}`);\n else if (thrown instanceof ApiConflictError) this.log.info(`Conflict: ${detail}`);\n else if (thrown instanceof ApiUnprocessableError) this.log.info(`Unprocessable: ${detail}`);\n else if (thrown instanceof ApiPreconditionFailedError)\n this.log.info(`Precondition Failed: ${detail}`);\n else if (thrown instanceof ApiUnsupportedMediaTypeError)\n this.log.info(`Unsupported Media Type: ${detail}`);\n else if (thrown instanceof ApiCodedError && thrown.isCallerError())\n this.log.info(`Coded ${thrown.statusCode}: ${detail}`);\n else if (thrown instanceof ApiRequestTimeoutError)\n this.log.error(`Request Timeout: ${detail}`);\n else this.log.error(`API failure: ${detail}`);\n }\n\n // webpieces-disable no-any-unknown -- thrown values are unknown until classified at this boundary\n private operatorDetail(thrown: unknown): string {\n const error = toError(thrown);\n const kind = this.apiOutcome(thrown)?.kind ?? 'implementation';\n const subType = thrown instanceof ApiError ? thrown.subType : undefined;\n const cause = error.cause instanceof Error ? ` cause=${error.cause.message}` : '';\n return `[name=${error.name} kind=${kind} subType=${subType ?? 'none'}] ${error.message}${cause}`;\n }\n}\n"]}
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
- static encode(error: unknown): ApiErrorPayload;
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
- // webpieces-disable no-function-outside-class -- stateless public transport codec; webpieces-disable no-any-unknown -- thrown values are untrusted until narrowed
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; webpieces-disable no-any-unknown -- thrown causes require narrowing
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 (error instanceof Error && depth < 3) {
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 instanceof Error)
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; webpieces-disable no-any-unknown -- thrown values require narrowing
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":"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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';\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
- static hasCode(error: ApiError): boolean;
5
- static code(error: ApiError): number;
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 code(error) {
13
- switch (error.kind) {
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
- if (error instanceof ApiError_1.ApiCodedError)
43
- return error.statusCode;
44
- throw new Error(`kind 'coded' must be an ApiCodedError, got ${error.name}`);
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":";;;AAAA,iDAA6D;AAE7D,sFAAsF;AACtF,MAAa,kBAAkB;IAC3B,6EAA6E;IAC7E,MAAM,CAAC,OAAO,CAAC,KAAe;QAC1B,OAAO,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC;IACvC,CAAC;IAED,2FAA2F;IAC3F,MAAM,CAAC,IAAI,CAAC,KAAe;QACvB,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACjB,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,IAAI,KAAK,YAAY,wBAAa;oBAAE,OAAO,KAAK,CAAC,UAAU,CAAC;gBAC5D,MAAM,IAAI,KAAK,CAAC,8CAA8C,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAChF,KAAK,YAAY;gBACb,OAAO,GAAG,CAAC;YACf,KAAK,aAAa,CAAC;YACnB,KAAK,oBAAoB;gBACrB,OAAO,GAAG,CAAC;YACf,KAAK,oBAAoB;gBACrB,OAAO,GAAG,CAAC;YACf,KAAK,YAAY;gBACb,MAAM,IAAI,KAAK,CACX,4EAA4E,CAC/E,CAAC;QACV,CAAC;IACL,CAAC;CACJ;AApDD,gDAoDC","sourcesContent":["import { ApiCodedError, ApiError } from '../errors/ApiError';\n\n/** HTTP-only adapter: semantic errors themselves have no protocol status property. */\nexport class ApiErrorHttpStatus {\n // webpieces-disable no-function-outside-class -- stateless mapping predicate\n static hasCode(error: ApiError): boolean {\n return error.kind !== 'connection';\n }\n\n // webpieces-disable no-function-outside-class -- stateless mapping shared by HTTP adapters\n static code(error: ApiError): number {\n switch (error.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 if (error instanceof ApiCodedError) return error.statusCode;\n throw new Error(`kind 'coded' must be an ApiCodedError, got ${error.name}`);\n case 'dependency':\n return 502;\n case 'unavailable':\n case 'dependency-backoff':\n return 503;\n case 'dependency-timeout':\n return 504;\n case 'connection':\n throw new Error(\n 'ApiConnectionError has no HTTP status; normalize it at the server boundary',\n );\n }\n }\n}\n"]}
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"]}