@web-ts-toolkit/http-errors 0.31.5 → 0.33.0

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/README.md CHANGED
@@ -15,10 +15,23 @@ pnpm add @web-ts-toolkit/http-errors
15
15
  - machine-readable error metadata
16
16
  - helpers for AIP-193 and RFC 9457 payloads
17
17
 
18
+ ## Constructor Status Contract
19
+
20
+ `HttpError` accepts only finite integer HTTP error status codes from `400` through `599`.
21
+ `ClientError` narrows that to `400` through `499`, and `ServerError` narrows it to `500` through `599`.
22
+ Invalid or category-mismatched statuses throw synchronously before an error instance is created.
23
+
18
24
  ## Quick Start
19
25
 
20
26
  ```ts
21
- import { HttpError, UnauthorizedError, ServiceUnavailableError } from '@web-ts-toolkit/http-errors';
27
+ import {
28
+ HttpError,
29
+ ServiceUnavailableError,
30
+ UnauthorizedError,
31
+ toAip193ErrorPayload,
32
+ toRfc9457ErrorPayload,
33
+ toRfc9457ValidationErrorPayload,
34
+ } from '@web-ts-toolkit/http-errors';
22
35
 
23
36
  throw new UnauthorizedError();
24
37
  throw new UnauthorizedError('missing bearer token');
@@ -27,15 +40,100 @@ throw new HttpError(503);
27
40
  throw new HttpError(503, 'please try again later');
28
41
 
29
42
  throw new ServiceUnavailableError();
43
+
44
+ const error = new HttpError(400, 'Email must be a valid address.', {
45
+ reason: 'INVALID_EMAIL',
46
+ domain: 'api.example.com',
47
+ type: 'https://api.example.com/problems/invalid-email',
48
+ title: 'Invalid email address',
49
+ instance: '/problems/invalid-email/123',
50
+ metadata: { field: 'email' },
51
+ errors: [{ detail: 'must be a valid email address', pointer: '#/email' }],
52
+ });
53
+
54
+ const aip193 = toAip193ErrorPayload(error);
55
+ const rfc9457 = toRfc9457ErrorPayload(error);
56
+ const validation = toRfc9457ValidationErrorPayload(error);
57
+ void [aip193, rfc9457, validation];
30
58
  ```
31
59
 
32
60
  ## Main Exports
33
61
 
34
62
  - `HttpError`
63
+ - `ClientError` and `ServerError`
35
64
  - specific error classes such as `BadRequestError`, `ForbiddenError`, `NotFoundError`
36
65
  - `toAip193ErrorPayload(...)`
37
66
  - `toRfc9457ErrorPayload(...)`
38
67
  - `toRfc9457ValidationErrorPayload(...)`
68
+ - types including `HttpErrorOptions`, `HttpErrorShape`, `HttpErrorProblemFields`, `HttpErrorMetadataValue`, `Aip193ErrorInfoDetail`, `Aip193ErrorPayload`, `Rfc9457ErrorPayload`, and `Rfc9457ValidationError`
69
+
70
+ ## AIP-193 Serializer Contract
71
+
72
+ `toAip193ErrorPayload(...)` returns a Google-style `{ error }` envelope. It emits `code`, `status`, `message`, and `details` on every payload. The first detail is always an `error_info` entry whose `reason` defaults to the error `reason` or canonical status, whose `domain` defaults to the error `domain` or the serializer fallback domain, and whose `metadata` is copied when present.
73
+
74
+ ```ts
75
+ import { BadRequestError, toAip193ErrorPayload } from '@web-ts-toolkit/http-errors';
76
+
77
+ const payload = toAip193ErrorPayload(
78
+ new BadRequestError('Email must be a valid address.', {
79
+ reason: 'INVALID_EMAIL',
80
+ domain: 'api.example.com',
81
+ metadata: { field: 'email' },
82
+ }),
83
+ );
84
+
85
+ // payload.error.details[0] is:
86
+ // { type: 'error_info', reason: 'INVALID_EMAIL', domain: 'api.example.com', metadata: { field: 'email' } }
87
+ void payload;
88
+ ```
89
+
90
+ ## RFC 9457 Serializer Contract
91
+
92
+ `toRfc9457ErrorPayload(...)` emits required RFC 9457 problem members (`type`, `title`, `status`, and `detail`) on every payload. If the input shape has `errors` typed as an array, the returned payload preserves that entry type for custom extension errors.
93
+
94
+ When `type` is missing it falls back to `about:blank`. When `title` is missing it falls back to the canonical HTTP status title, or `Unknown` for unmapped status codes. `instance` is emitted only when present.
95
+
96
+ ```ts
97
+ import { BadRequestError, toRfc9457ErrorPayload } from '@web-ts-toolkit/http-errors';
98
+
99
+ const payload = toRfc9457ErrorPayload(
100
+ new BadRequestError('Email must be a valid address.', {
101
+ type: 'https://api.example.com/problems/invalid-email',
102
+ title: 'Invalid email address',
103
+ instance: '/problems/invalid-email/123',
104
+ errors: [{ detail: 'must be a valid email address', pointer: '#/email' }],
105
+ }),
106
+ );
107
+
108
+ // payload is:
109
+ // { type, title, status: 400, detail: 'Email must be a valid address.', instance, errors }
110
+ void payload;
111
+ ```
112
+
113
+ `toRfc9457ValidationErrorPayload(...)` is the validation-specific helper. It accepts JavaScript input defensively and only emits `errors` entries that have an own string `detail` plus optional own string `pointer`, `parameter`, and `header` fields. Non-arrays, empty arrays, inherited validation fields, and invalid entries are omitted from the returned `errors` array; if no valid entries remain, `errors` is omitted.
114
+
115
+ ```ts
116
+ import { BadRequestError, toRfc9457ValidationErrorPayload } from '@web-ts-toolkit/http-errors';
117
+
118
+ const payload = toRfc9457ValidationErrorPayload(
119
+ new BadRequestError('Email must be a valid address.', {
120
+ errors: [{ detail: 'must be a valid email address', pointer: '#/email', debug: 'omitted' }, { pointer: '#/name' }],
121
+ }),
122
+ );
123
+
124
+ // payload.errors is [{ detail: 'must be a valid email address', pointer: '#/email' }]
125
+ void payload;
126
+ ```
127
+
128
+ ## External Payload Disclosure
129
+
130
+ Serializers emit public payload fields verbatim. Treat `message`, `details`, `errors`, `metadata`, `type`, `title`, and `instance` as externally visible API response data. Do not put secrets, stack traces, raw upstream errors, tokens, credentials, or internal diagnostics in these fields unless you intentionally want clients to receive them.
131
+
132
+ ## Structured Value Ownership
133
+
134
+ `HttpError` snapshots supported top-level collections at construction. `metadata` is normalized into an error-owned frozen string record, and array-valued `details` and `errors` are copied into error-owned frozen arrays. Mutating the source objects after construction cannot add, remove, reorder, or rename error-owned top-level entries.
135
+
136
+ Serializers also return fresh top-level arrays and metadata records, so mutating one payload does not change the source error or later payloads. This is a shallow boundary: nested detail and error entry objects remain shared, and `ErrorOptions.cause` identity is preserved.
39
137
 
40
138
  ## Documentation
41
139
 
package/index.d.mts CHANGED
@@ -1,57 +1,108 @@
1
+ /** Scalar values accepted in `HttpErrorOptions.metadata` before string normalization. */
1
2
  type HttpErrorMetadataValue = string | number | boolean | bigint | null | undefined;
3
+ /** RFC 9457 problem fields stored on an HTTP error and emitted by RFC serializers. */
2
4
  type HttpErrorProblemFields = {
5
+ /** Problem type URI. Falls back to `about:blank` when serialized if absent. */
3
6
  type?: string;
7
+ /** Public problem title. Falls back to the status title when serialized if absent. */
4
8
  title?: string;
9
+ /** Problem instance URI emitted only when present. */
5
10
  instance?: string;
6
11
  };
7
- type HttpErrorOptions = ErrorOptions & HttpErrorProblemFields & {
12
+ /**
13
+ * Constructor options for `HttpError`, `ClientError`, `ServerError`, and specific subclasses.
14
+ *
15
+ * `message`, `details`, `errors`, `metadata`, `type`, `title`, and `instance` can
16
+ * be emitted verbatim by serializers. Do not place secrets or log-only diagnostics
17
+ * in these fields. Top-level `metadata`, `details`, and array-valued `errors` are
18
+ * shallow-snapshotted by `HttpError` construction.
19
+ */
20
+ type HttpErrorOptions<TErrors = unknown> = ErrorOptions & HttpErrorProblemFields & {
21
+ /** Override canonical status text for serializers such as AIP-193. */
8
22
  status?: string;
23
+ /** Machine-readable application reason, such as `INVALID_EMAIL`. */
9
24
  reason?: string;
25
+ /** Logical application domain, such as `api.example.com`. */
10
26
  domain?: string;
11
- metadata?: Record<string, HttpErrorMetadataValue>;
12
- details?: unknown[];
13
- errors?: unknown;
27
+ /** Key-value metadata normalized to strings and copied into serialized payloads. */
28
+ metadata?: Readonly<Record<string, HttpErrorMetadataValue>>;
29
+ /** Additional structured details emitted by AIP-193 payloads. */
30
+ details?: readonly unknown[];
31
+ /** Extension or validation errors emitted by RFC 9457 helpers when array-valued. */
32
+ errors?: TErrors;
14
33
  };
15
- type HttpErrorShape = HttpErrorProblemFields & {
34
+ /**
35
+ * Minimal error-like input accepted by serializers.
36
+ *
37
+ * This shape allows using real `HttpError` instances or compatible plain objects.
38
+ * Serializers treat `message` and structured fields as public response data.
39
+ */
40
+ type HttpErrorShape<TErrors = unknown> = HttpErrorProblemFields & {
41
+ /** HTTP error status code used as the response code/status. */
16
42
  statusCode: number;
43
+ /** Canonical or custom status string used by AIP-193 payloads. */
17
44
  status?: string;
45
+ /** Public error detail/message emitted verbatim. */
18
46
  message: string;
47
+ /** Machine-readable application reason used by AIP-193 `error_info`. */
19
48
  reason?: string;
49
+ /** Domain used by AIP-193 `error_info`. */
20
50
  domain?: string;
21
- metadata?: Record<string, string>;
22
- details?: unknown[];
23
- errors?: unknown;
51
+ /** String metadata copied into AIP-193 `error_info`. */
52
+ metadata?: Readonly<Record<string, string>>;
53
+ /** Additional AIP-193 detail entries. */
54
+ details?: readonly unknown[];
55
+ /** RFC 9457 extension or validation errors. Array-valued entries are shallow-copied. */
56
+ errors?: TErrors;
24
57
  };
58
+ /** AIP-193 `error_info` detail entry emitted as the first AIP-193 detail. */
25
59
  type Aip193ErrorInfoDetail = {
26
60
  type: 'error_info';
27
61
  reason: string;
28
62
  domain: string;
29
- metadata?: Record<string, string>;
63
+ metadata?: Readonly<Record<string, string>>;
30
64
  };
65
+ /** Google/AIP-193-style error response envelope. */
31
66
  type Aip193ErrorPayload = {
32
67
  error: {
68
+ /** HTTP status code. */
33
69
  code: number;
70
+ /** Canonical or custom status string. */
34
71
  status: string;
72
+ /** Public error message emitted verbatim from the input error. */
35
73
  message: string;
36
- details?: unknown[];
74
+ /** Structured details, beginning with an `error_info` entry. */
75
+ details?: readonly unknown[];
37
76
  };
38
77
  };
78
+ /** RFC 9457 problem-details payload with optional typed `errors` extension entries. */
39
79
  type Rfc9457ErrorPayload<TError = unknown> = {
40
- type?: string;
41
- title?: string;
42
- status?: number;
43
- detail?: string;
80
+ /** Problem type URI, defaulting to `about:blank` when absent on the input. */
81
+ type: string;
82
+ /** Public problem title, defaulting to the status title or `Unknown`. */
83
+ title: string;
84
+ /** HTTP status code. */
85
+ status: number;
86
+ /** Public problem detail emitted verbatim from the input error message. */
87
+ detail: string;
88
+ /** Problem instance URI, emitted only when present on the input. */
44
89
  instance?: string;
45
- errors?: TError[];
90
+ /** Optional extension errors. Validation helpers emit only validated entries. */
91
+ errors?: readonly TError[];
46
92
  };
93
+ /** RFC 9457 validation error entry accepted by `toRfc9457ValidationErrorPayload`. */
47
94
  type Rfc9457ValidationError = {
95
+ /** Human-readable validation detail. Required and must be an own string field. */
48
96
  detail: string;
97
+ /** JSON Pointer to the invalid field, when available. */
49
98
  pointer?: string;
99
+ /** Query/path/header parameter name, when available. */
50
100
  parameter?: string;
101
+ /** Header name, when available. */
51
102
  header?: string;
52
103
  };
53
104
 
54
- declare const canonicalStatusByHttpStatus: {
105
+ declare const canonicalStatusLookup: Readonly<{
55
106
  readonly 400: "INVALID_ARGUMENT";
56
107
  readonly 401: "UNAUTHENTICATED";
57
108
  readonly 403: "PERMISSION_DENIED";
@@ -63,18 +114,88 @@ declare const canonicalStatusByHttpStatus: {
63
114
  readonly 501: "UNIMPLEMENTED";
64
115
  readonly 503: "UNAVAILABLE";
65
116
  readonly 504: "DEADLINE_EXCEEDED";
66
- };
117
+ }>;
118
+ declare const canonicalStatusByHttpStatus: Readonly<typeof canonicalStatusLookup>;
67
119
  declare const getCanonicalStatus: (statusCode: number) => string;
68
120
  declare const getStatusTitle: (statusCode: number) => string;
121
+ declare const isHttpErrorStatusCode: (statusCode: unknown) => statusCode is number;
122
+ declare const validateHttpErrorStatusCode: (statusCode: unknown, source?: string) => number;
123
+ declare const validateClientErrorStatusCode: (statusCode: unknown, source?: string) => number;
124
+ declare const validateServerErrorStatusCode: (statusCode: unknown, source?: string) => number;
69
125
 
70
- declare const createAip193ErrorInfoDetail: (reason: string, domain: string, metadata?: Record<string, string>) => Aip193ErrorInfoDetail;
126
+ /**
127
+ * Creates an AIP-193 `error_info` detail entry.
128
+ *
129
+ * The returned `metadata` record is a shallow copy so payload mutation does not
130
+ * mutate the source error metadata.
131
+ *
132
+ * @param reason Machine-readable reason, usually an application code or canonical status.
133
+ * @param domain Logical error domain, such as `api.example.com`.
134
+ * @param metadata Optional string metadata copied into the returned detail.
135
+ * @returns A serializable AIP-193 `error_info` detail.
136
+ */
137
+ declare const createAip193ErrorInfoDetail: (reason: string, domain: string, metadata?: Readonly<Record<string, string>>) => Aip193ErrorInfoDetail;
138
+ /**
139
+ * Converts an HTTP error shape into an AIP-193-style `{ error }` envelope.
140
+ *
141
+ * Emits `error.message` verbatim as the public payload message. The first detail
142
+ * is always an `error_info` entry. Additional `errors` and `details` entries are
143
+ * shallow-copied at the top level only.
144
+ *
145
+ * @param error Error-like shape with status, message, and optional structured fields.
146
+ * @param fallbackDomain Domain used for `error_info` when `error.domain` is absent.
147
+ * @returns A payload containing `code`, `status`, `message`, and `details`.
148
+ */
71
149
  declare const toAip193ErrorPayload: (error: HttpErrorShape, fallbackDomain?: string) => Aip193ErrorPayload;
72
- declare const toRfc9457ErrorPayload: <TError = unknown>(error: HttpErrorShape) => Rfc9457ErrorPayload<TError>;
150
+ /**
151
+ * Converts an HTTP error shape with typed extension errors into an RFC 9457 problem payload.
152
+ *
153
+ * Emits `message` as `detail` and passes `type`, `title`, `instance`, and array
154
+ * `errors` through as public response data. Array-valued `errors` are shallow-copied.
155
+ *
156
+ * @param error Error-like shape whose `errors` field is a typed array.
157
+ * @returns RFC 9457 problem details preserving the typed extension error entries.
158
+ */
159
+ declare function toRfc9457ErrorPayload<TError>(error: HttpErrorShape<readonly TError[]>): Rfc9457ErrorPayload<TError>;
160
+ /**
161
+ * Converts an HTTP error shape into an RFC 9457 problem payload.
162
+ *
163
+ * Missing `type` falls back to `about:blank`; missing `title` falls back to the
164
+ * canonical status title, or `Unknown` for unmapped status codes. `message` is
165
+ * emitted verbatim as `detail`; `instance` and array-valued `errors` are emitted
166
+ * when present.
167
+ *
168
+ * @param error Error-like shape with status, message, and optional problem fields.
169
+ * @returns RFC 9457 problem details.
170
+ */
171
+ declare function toRfc9457ErrorPayload(error: HttpErrorShape): Rfc9457ErrorPayload;
172
+ /**
173
+ * Converts an HTTP error shape into an RFC 9457 validation problem payload.
174
+ *
175
+ * Runtime-narrows `errors` before typing the result as validation entries. Keeps
176
+ * only own string `detail` plus optional own string `pointer`, `parameter`, and
177
+ * `header`; extra fields are omitted. If no valid entries remain, `errors` is omitted.
178
+ *
179
+ * @param error Error-like shape with optional validation-style `errors` entries.
180
+ * @returns RFC 9457 problem details with validated `errors` entries when available.
181
+ */
73
182
  declare const toRfc9457ValidationErrorPayload: (error: HttpErrorShape) => Rfc9457ErrorPayload<Rfc9457ValidationError>;
74
183
 
75
184
  /**
76
185
  * Base typed HTTP error with a numeric `statusCode` and machine-readable metadata.
77
186
  *
187
+ * Accepts only finite integer status codes from 400 through 599. Structured options
188
+ * are intended for external API payloads: serializers emit `message`, `details`,
189
+ * `errors`, `metadata`, `type`, `title`, and `instance` verbatim, so do not store
190
+ * secrets or log-only diagnostics in those fields.
191
+ *
192
+ * Top-level `metadata`, `details`, and array-valued `errors` are shallow snapshots:
193
+ * the error owns frozen top-level records/arrays, but nested objects remain shared.
194
+ *
195
+ * @param statusCode HTTP error status code from 400 through 599. Defaults to 500.
196
+ * @param message Public error message. Defaults to the package's status message.
197
+ * @param options Optional cause and structured fields used by payload serializers.
198
+ *
78
199
  * @example
79
200
  * throw new HttpError(503, 'please try again later');
80
201
  */
@@ -84,17 +205,37 @@ declare class HttpError extends Error {
84
205
  readonly date: Date;
85
206
  readonly reason?: string;
86
207
  readonly domain?: string;
87
- readonly metadata?: Record<string, string>;
88
- readonly details?: unknown[];
208
+ readonly metadata?: Readonly<Record<string, string>>;
209
+ readonly details?: readonly unknown[];
89
210
  readonly errors?: unknown;
90
211
  readonly type?: string;
91
212
  readonly title?: string;
92
213
  readonly instance?: string;
93
214
  constructor(statusCode?: number, message?: string, options?: HttpErrorOptions);
94
215
  }
216
+ /**
217
+ * Base class for 4xx client errors.
218
+ *
219
+ * Accepts only finite integer status codes from 400 through 499 and otherwise has
220
+ * the same payload, disclosure, and shallow ownership semantics as `HttpError`.
221
+ *
222
+ * @param statusCode HTTP client error status code from 400 through 499. Defaults to 400.
223
+ * @param message Public error message. Defaults to the package's status message.
224
+ * @param options Optional cause and structured fields used by payload serializers.
225
+ */
95
226
  declare class ClientError extends HttpError {
96
227
  constructor(statusCode?: number, message?: string, options?: HttpErrorOptions);
97
228
  }
229
+ /**
230
+ * Base class for 5xx server errors.
231
+ *
232
+ * Accepts only finite integer status codes from 500 through 599 and otherwise has
233
+ * the same payload, disclosure, and shallow ownership semantics as `HttpError`.
234
+ *
235
+ * @param statusCode HTTP server error status code from 500 through 599. Defaults to 500.
236
+ * @param message Public error message. Defaults to the package's status message.
237
+ * @param options Optional cause and structured fields used by payload serializers.
238
+ */
98
239
  declare class ServerError extends HttpError {
99
240
  constructor(statusCode?: number, message?: string, options?: HttpErrorOptions);
100
241
  }
@@ -215,4 +356,4 @@ declare class NetworkAuthenticationRequiredError extends ServerError {
215
356
  constructor(message?: string, options?: HttpErrorOptions);
216
357
  }
217
358
 
218
- export { type Aip193ErrorInfoDetail, type Aip193ErrorPayload, BadGatewayError, BadRequestError, ClientError, ConflictError, ExpectationFailedError, FailedDependencyError, ForbiddenError, GatewayTimeoutError, GoneError, HttpError, type HttpErrorMetadataValue, type HttpErrorOptions, type HttpErrorProblemFields, type HttpErrorShape, HttpVersionNotSupportedError, InsufficientStorageError, InternalServerError, LengthRequiredError, LockedError, LoopDetectedError, MethodNotAllowedError, MisdirectedRequestError, NetworkAuthenticationRequiredError, NotAcceptableError, NotExtendedError, NotFoundError, NotImplementedError, PayloadTooLargeError, PreconditionFailedError, PreconditionRequiredError, ProxyAuthRequiredError, RequestHeaderFieldsTooLargeError, RequestTimeoutError, RequestedRangeNotSatisfiableError, type Rfc9457ErrorPayload, type Rfc9457ValidationError, ServerError, ServiceUnavailableError, TeapotError, TooManyRequestsError, UnauthorizedError, UnavailableForLegalReasonsError, UnprocessableEntityError, UnsupportedMediaTypeError, UpgradeRequiredError, UriTooLongError, VariantAlsoNegotiatesError, canonicalStatusByHttpStatus, createAip193ErrorInfoDetail, getCanonicalStatus, getStatusTitle, toAip193ErrorPayload, toRfc9457ErrorPayload, toRfc9457ValidationErrorPayload };
359
+ export { type Aip193ErrorInfoDetail, type Aip193ErrorPayload, BadGatewayError, BadRequestError, ClientError, ConflictError, ExpectationFailedError, FailedDependencyError, ForbiddenError, GatewayTimeoutError, GoneError, HttpError, type HttpErrorMetadataValue, type HttpErrorOptions, type HttpErrorProblemFields, type HttpErrorShape, HttpVersionNotSupportedError, InsufficientStorageError, InternalServerError, LengthRequiredError, LockedError, LoopDetectedError, MethodNotAllowedError, MisdirectedRequestError, NetworkAuthenticationRequiredError, NotAcceptableError, NotExtendedError, NotFoundError, NotImplementedError, PayloadTooLargeError, PreconditionFailedError, PreconditionRequiredError, ProxyAuthRequiredError, RequestHeaderFieldsTooLargeError, RequestTimeoutError, RequestedRangeNotSatisfiableError, type Rfc9457ErrorPayload, type Rfc9457ValidationError, ServerError, ServiceUnavailableError, TeapotError, TooManyRequestsError, UnauthorizedError, UnavailableForLegalReasonsError, UnprocessableEntityError, UnsupportedMediaTypeError, UpgradeRequiredError, UriTooLongError, VariantAlsoNegotiatesError, canonicalStatusByHttpStatus, createAip193ErrorInfoDetail, getCanonicalStatus, getStatusTitle, isHttpErrorStatusCode, toAip193ErrorPayload, toRfc9457ErrorPayload, toRfc9457ValidationErrorPayload, validateClientErrorStatusCode, validateHttpErrorStatusCode, validateServerErrorStatusCode };
package/index.d.ts CHANGED
@@ -1,57 +1,108 @@
1
+ /** Scalar values accepted in `HttpErrorOptions.metadata` before string normalization. */
1
2
  type HttpErrorMetadataValue = string | number | boolean | bigint | null | undefined;
3
+ /** RFC 9457 problem fields stored on an HTTP error and emitted by RFC serializers. */
2
4
  type HttpErrorProblemFields = {
5
+ /** Problem type URI. Falls back to `about:blank` when serialized if absent. */
3
6
  type?: string;
7
+ /** Public problem title. Falls back to the status title when serialized if absent. */
4
8
  title?: string;
9
+ /** Problem instance URI emitted only when present. */
5
10
  instance?: string;
6
11
  };
7
- type HttpErrorOptions = ErrorOptions & HttpErrorProblemFields & {
12
+ /**
13
+ * Constructor options for `HttpError`, `ClientError`, `ServerError`, and specific subclasses.
14
+ *
15
+ * `message`, `details`, `errors`, `metadata`, `type`, `title`, and `instance` can
16
+ * be emitted verbatim by serializers. Do not place secrets or log-only diagnostics
17
+ * in these fields. Top-level `metadata`, `details`, and array-valued `errors` are
18
+ * shallow-snapshotted by `HttpError` construction.
19
+ */
20
+ type HttpErrorOptions<TErrors = unknown> = ErrorOptions & HttpErrorProblemFields & {
21
+ /** Override canonical status text for serializers such as AIP-193. */
8
22
  status?: string;
23
+ /** Machine-readable application reason, such as `INVALID_EMAIL`. */
9
24
  reason?: string;
25
+ /** Logical application domain, such as `api.example.com`. */
10
26
  domain?: string;
11
- metadata?: Record<string, HttpErrorMetadataValue>;
12
- details?: unknown[];
13
- errors?: unknown;
27
+ /** Key-value metadata normalized to strings and copied into serialized payloads. */
28
+ metadata?: Readonly<Record<string, HttpErrorMetadataValue>>;
29
+ /** Additional structured details emitted by AIP-193 payloads. */
30
+ details?: readonly unknown[];
31
+ /** Extension or validation errors emitted by RFC 9457 helpers when array-valued. */
32
+ errors?: TErrors;
14
33
  };
15
- type HttpErrorShape = HttpErrorProblemFields & {
34
+ /**
35
+ * Minimal error-like input accepted by serializers.
36
+ *
37
+ * This shape allows using real `HttpError` instances or compatible plain objects.
38
+ * Serializers treat `message` and structured fields as public response data.
39
+ */
40
+ type HttpErrorShape<TErrors = unknown> = HttpErrorProblemFields & {
41
+ /** HTTP error status code used as the response code/status. */
16
42
  statusCode: number;
43
+ /** Canonical or custom status string used by AIP-193 payloads. */
17
44
  status?: string;
45
+ /** Public error detail/message emitted verbatim. */
18
46
  message: string;
47
+ /** Machine-readable application reason used by AIP-193 `error_info`. */
19
48
  reason?: string;
49
+ /** Domain used by AIP-193 `error_info`. */
20
50
  domain?: string;
21
- metadata?: Record<string, string>;
22
- details?: unknown[];
23
- errors?: unknown;
51
+ /** String metadata copied into AIP-193 `error_info`. */
52
+ metadata?: Readonly<Record<string, string>>;
53
+ /** Additional AIP-193 detail entries. */
54
+ details?: readonly unknown[];
55
+ /** RFC 9457 extension or validation errors. Array-valued entries are shallow-copied. */
56
+ errors?: TErrors;
24
57
  };
58
+ /** AIP-193 `error_info` detail entry emitted as the first AIP-193 detail. */
25
59
  type Aip193ErrorInfoDetail = {
26
60
  type: 'error_info';
27
61
  reason: string;
28
62
  domain: string;
29
- metadata?: Record<string, string>;
63
+ metadata?: Readonly<Record<string, string>>;
30
64
  };
65
+ /** Google/AIP-193-style error response envelope. */
31
66
  type Aip193ErrorPayload = {
32
67
  error: {
68
+ /** HTTP status code. */
33
69
  code: number;
70
+ /** Canonical or custom status string. */
34
71
  status: string;
72
+ /** Public error message emitted verbatim from the input error. */
35
73
  message: string;
36
- details?: unknown[];
74
+ /** Structured details, beginning with an `error_info` entry. */
75
+ details?: readonly unknown[];
37
76
  };
38
77
  };
78
+ /** RFC 9457 problem-details payload with optional typed `errors` extension entries. */
39
79
  type Rfc9457ErrorPayload<TError = unknown> = {
40
- type?: string;
41
- title?: string;
42
- status?: number;
43
- detail?: string;
80
+ /** Problem type URI, defaulting to `about:blank` when absent on the input. */
81
+ type: string;
82
+ /** Public problem title, defaulting to the status title or `Unknown`. */
83
+ title: string;
84
+ /** HTTP status code. */
85
+ status: number;
86
+ /** Public problem detail emitted verbatim from the input error message. */
87
+ detail: string;
88
+ /** Problem instance URI, emitted only when present on the input. */
44
89
  instance?: string;
45
- errors?: TError[];
90
+ /** Optional extension errors. Validation helpers emit only validated entries. */
91
+ errors?: readonly TError[];
46
92
  };
93
+ /** RFC 9457 validation error entry accepted by `toRfc9457ValidationErrorPayload`. */
47
94
  type Rfc9457ValidationError = {
95
+ /** Human-readable validation detail. Required and must be an own string field. */
48
96
  detail: string;
97
+ /** JSON Pointer to the invalid field, when available. */
49
98
  pointer?: string;
99
+ /** Query/path/header parameter name, when available. */
50
100
  parameter?: string;
101
+ /** Header name, when available. */
51
102
  header?: string;
52
103
  };
53
104
 
54
- declare const canonicalStatusByHttpStatus: {
105
+ declare const canonicalStatusLookup: Readonly<{
55
106
  readonly 400: "INVALID_ARGUMENT";
56
107
  readonly 401: "UNAUTHENTICATED";
57
108
  readonly 403: "PERMISSION_DENIED";
@@ -63,18 +114,88 @@ declare const canonicalStatusByHttpStatus: {
63
114
  readonly 501: "UNIMPLEMENTED";
64
115
  readonly 503: "UNAVAILABLE";
65
116
  readonly 504: "DEADLINE_EXCEEDED";
66
- };
117
+ }>;
118
+ declare const canonicalStatusByHttpStatus: Readonly<typeof canonicalStatusLookup>;
67
119
  declare const getCanonicalStatus: (statusCode: number) => string;
68
120
  declare const getStatusTitle: (statusCode: number) => string;
121
+ declare const isHttpErrorStatusCode: (statusCode: unknown) => statusCode is number;
122
+ declare const validateHttpErrorStatusCode: (statusCode: unknown, source?: string) => number;
123
+ declare const validateClientErrorStatusCode: (statusCode: unknown, source?: string) => number;
124
+ declare const validateServerErrorStatusCode: (statusCode: unknown, source?: string) => number;
69
125
 
70
- declare const createAip193ErrorInfoDetail: (reason: string, domain: string, metadata?: Record<string, string>) => Aip193ErrorInfoDetail;
126
+ /**
127
+ * Creates an AIP-193 `error_info` detail entry.
128
+ *
129
+ * The returned `metadata` record is a shallow copy so payload mutation does not
130
+ * mutate the source error metadata.
131
+ *
132
+ * @param reason Machine-readable reason, usually an application code or canonical status.
133
+ * @param domain Logical error domain, such as `api.example.com`.
134
+ * @param metadata Optional string metadata copied into the returned detail.
135
+ * @returns A serializable AIP-193 `error_info` detail.
136
+ */
137
+ declare const createAip193ErrorInfoDetail: (reason: string, domain: string, metadata?: Readonly<Record<string, string>>) => Aip193ErrorInfoDetail;
138
+ /**
139
+ * Converts an HTTP error shape into an AIP-193-style `{ error }` envelope.
140
+ *
141
+ * Emits `error.message` verbatim as the public payload message. The first detail
142
+ * is always an `error_info` entry. Additional `errors` and `details` entries are
143
+ * shallow-copied at the top level only.
144
+ *
145
+ * @param error Error-like shape with status, message, and optional structured fields.
146
+ * @param fallbackDomain Domain used for `error_info` when `error.domain` is absent.
147
+ * @returns A payload containing `code`, `status`, `message`, and `details`.
148
+ */
71
149
  declare const toAip193ErrorPayload: (error: HttpErrorShape, fallbackDomain?: string) => Aip193ErrorPayload;
72
- declare const toRfc9457ErrorPayload: <TError = unknown>(error: HttpErrorShape) => Rfc9457ErrorPayload<TError>;
150
+ /**
151
+ * Converts an HTTP error shape with typed extension errors into an RFC 9457 problem payload.
152
+ *
153
+ * Emits `message` as `detail` and passes `type`, `title`, `instance`, and array
154
+ * `errors` through as public response data. Array-valued `errors` are shallow-copied.
155
+ *
156
+ * @param error Error-like shape whose `errors` field is a typed array.
157
+ * @returns RFC 9457 problem details preserving the typed extension error entries.
158
+ */
159
+ declare function toRfc9457ErrorPayload<TError>(error: HttpErrorShape<readonly TError[]>): Rfc9457ErrorPayload<TError>;
160
+ /**
161
+ * Converts an HTTP error shape into an RFC 9457 problem payload.
162
+ *
163
+ * Missing `type` falls back to `about:blank`; missing `title` falls back to the
164
+ * canonical status title, or `Unknown` for unmapped status codes. `message` is
165
+ * emitted verbatim as `detail`; `instance` and array-valued `errors` are emitted
166
+ * when present.
167
+ *
168
+ * @param error Error-like shape with status, message, and optional problem fields.
169
+ * @returns RFC 9457 problem details.
170
+ */
171
+ declare function toRfc9457ErrorPayload(error: HttpErrorShape): Rfc9457ErrorPayload;
172
+ /**
173
+ * Converts an HTTP error shape into an RFC 9457 validation problem payload.
174
+ *
175
+ * Runtime-narrows `errors` before typing the result as validation entries. Keeps
176
+ * only own string `detail` plus optional own string `pointer`, `parameter`, and
177
+ * `header`; extra fields are omitted. If no valid entries remain, `errors` is omitted.
178
+ *
179
+ * @param error Error-like shape with optional validation-style `errors` entries.
180
+ * @returns RFC 9457 problem details with validated `errors` entries when available.
181
+ */
73
182
  declare const toRfc9457ValidationErrorPayload: (error: HttpErrorShape) => Rfc9457ErrorPayload<Rfc9457ValidationError>;
74
183
 
75
184
  /**
76
185
  * Base typed HTTP error with a numeric `statusCode` and machine-readable metadata.
77
186
  *
187
+ * Accepts only finite integer status codes from 400 through 599. Structured options
188
+ * are intended for external API payloads: serializers emit `message`, `details`,
189
+ * `errors`, `metadata`, `type`, `title`, and `instance` verbatim, so do not store
190
+ * secrets or log-only diagnostics in those fields.
191
+ *
192
+ * Top-level `metadata`, `details`, and array-valued `errors` are shallow snapshots:
193
+ * the error owns frozen top-level records/arrays, but nested objects remain shared.
194
+ *
195
+ * @param statusCode HTTP error status code from 400 through 599. Defaults to 500.
196
+ * @param message Public error message. Defaults to the package's status message.
197
+ * @param options Optional cause and structured fields used by payload serializers.
198
+ *
78
199
  * @example
79
200
  * throw new HttpError(503, 'please try again later');
80
201
  */
@@ -84,17 +205,37 @@ declare class HttpError extends Error {
84
205
  readonly date: Date;
85
206
  readonly reason?: string;
86
207
  readonly domain?: string;
87
- readonly metadata?: Record<string, string>;
88
- readonly details?: unknown[];
208
+ readonly metadata?: Readonly<Record<string, string>>;
209
+ readonly details?: readonly unknown[];
89
210
  readonly errors?: unknown;
90
211
  readonly type?: string;
91
212
  readonly title?: string;
92
213
  readonly instance?: string;
93
214
  constructor(statusCode?: number, message?: string, options?: HttpErrorOptions);
94
215
  }
216
+ /**
217
+ * Base class for 4xx client errors.
218
+ *
219
+ * Accepts only finite integer status codes from 400 through 499 and otherwise has
220
+ * the same payload, disclosure, and shallow ownership semantics as `HttpError`.
221
+ *
222
+ * @param statusCode HTTP client error status code from 400 through 499. Defaults to 400.
223
+ * @param message Public error message. Defaults to the package's status message.
224
+ * @param options Optional cause and structured fields used by payload serializers.
225
+ */
95
226
  declare class ClientError extends HttpError {
96
227
  constructor(statusCode?: number, message?: string, options?: HttpErrorOptions);
97
228
  }
229
+ /**
230
+ * Base class for 5xx server errors.
231
+ *
232
+ * Accepts only finite integer status codes from 500 through 599 and otherwise has
233
+ * the same payload, disclosure, and shallow ownership semantics as `HttpError`.
234
+ *
235
+ * @param statusCode HTTP server error status code from 500 through 599. Defaults to 500.
236
+ * @param message Public error message. Defaults to the package's status message.
237
+ * @param options Optional cause and structured fields used by payload serializers.
238
+ */
98
239
  declare class ServerError extends HttpError {
99
240
  constructor(statusCode?: number, message?: string, options?: HttpErrorOptions);
100
241
  }
@@ -215,4 +356,4 @@ declare class NetworkAuthenticationRequiredError extends ServerError {
215
356
  constructor(message?: string, options?: HttpErrorOptions);
216
357
  }
217
358
 
218
- export { type Aip193ErrorInfoDetail, type Aip193ErrorPayload, BadGatewayError, BadRequestError, ClientError, ConflictError, ExpectationFailedError, FailedDependencyError, ForbiddenError, GatewayTimeoutError, GoneError, HttpError, type HttpErrorMetadataValue, type HttpErrorOptions, type HttpErrorProblemFields, type HttpErrorShape, HttpVersionNotSupportedError, InsufficientStorageError, InternalServerError, LengthRequiredError, LockedError, LoopDetectedError, MethodNotAllowedError, MisdirectedRequestError, NetworkAuthenticationRequiredError, NotAcceptableError, NotExtendedError, NotFoundError, NotImplementedError, PayloadTooLargeError, PreconditionFailedError, PreconditionRequiredError, ProxyAuthRequiredError, RequestHeaderFieldsTooLargeError, RequestTimeoutError, RequestedRangeNotSatisfiableError, type Rfc9457ErrorPayload, type Rfc9457ValidationError, ServerError, ServiceUnavailableError, TeapotError, TooManyRequestsError, UnauthorizedError, UnavailableForLegalReasonsError, UnprocessableEntityError, UnsupportedMediaTypeError, UpgradeRequiredError, UriTooLongError, VariantAlsoNegotiatesError, canonicalStatusByHttpStatus, createAip193ErrorInfoDetail, getCanonicalStatus, getStatusTitle, toAip193ErrorPayload, toRfc9457ErrorPayload, toRfc9457ValidationErrorPayload };
359
+ export { type Aip193ErrorInfoDetail, type Aip193ErrorPayload, BadGatewayError, BadRequestError, ClientError, ConflictError, ExpectationFailedError, FailedDependencyError, ForbiddenError, GatewayTimeoutError, GoneError, HttpError, type HttpErrorMetadataValue, type HttpErrorOptions, type HttpErrorProblemFields, type HttpErrorShape, HttpVersionNotSupportedError, InsufficientStorageError, InternalServerError, LengthRequiredError, LockedError, LoopDetectedError, MethodNotAllowedError, MisdirectedRequestError, NetworkAuthenticationRequiredError, NotAcceptableError, NotExtendedError, NotFoundError, NotImplementedError, PayloadTooLargeError, PreconditionFailedError, PreconditionRequiredError, ProxyAuthRequiredError, RequestHeaderFieldsTooLargeError, RequestTimeoutError, RequestedRangeNotSatisfiableError, type Rfc9457ErrorPayload, type Rfc9457ValidationError, ServerError, ServiceUnavailableError, TeapotError, TooManyRequestsError, UnauthorizedError, UnavailableForLegalReasonsError, UnprocessableEntityError, UnsupportedMediaTypeError, UpgradeRequiredError, UriTooLongError, VariantAlsoNegotiatesError, canonicalStatusByHttpStatus, createAip193ErrorInfoDetail, getCanonicalStatus, getStatusTitle, isHttpErrorStatusCode, toAip193ErrorPayload, toRfc9457ErrorPayload, toRfc9457ValidationErrorPayload, validateClientErrorStatusCode, validateHttpErrorStatusCode, validateServerErrorStatusCode };