@marianmeres/http-utils 2.0.1 → 2.1.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/AGENTS.md +197 -0
- package/API.md +459 -0
- package/README.md +42 -159
- package/dist/api.d.ts +57 -17
- package/dist/api.js +60 -29
- package/dist/error.d.ts +58 -3
- package/dist/error.js +76 -22
- package/dist/mod.d.ts +24 -1
- package/dist/mod.js +23 -0
- package/dist/status.d.ts +29 -1
- package/dist/status.js +31 -2
- package/package.json +8 -1
package/AGENTS.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# AGENTS.md - Machine-Readable Package Context
|
|
2
|
+
|
|
3
|
+
## Package Identity
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
name: "@marianmeres/http-utils"
|
|
7
|
+
version: "2.0.2"
|
|
8
|
+
license: MIT
|
|
9
|
+
runtime: deno, node
|
|
10
|
+
type: library
|
|
11
|
+
category: http-client
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Purpose
|
|
15
|
+
|
|
16
|
+
Lightweight, opinionated HTTP client wrapper for the native `fetch` API. Provides type-safe HTTP errors mapped to specific error classes (e.g., 404 → NotFound), convenient defaults (auto JSON parsing, Bearer token support, base URLs), and flexible three-tier error message extraction.
|
|
17
|
+
|
|
18
|
+
## Architecture
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
src/
|
|
22
|
+
├── mod.ts # Public exports (main entry point)
|
|
23
|
+
├── api.ts # HttpApi class, createHttpApi factory
|
|
24
|
+
├── error.ts # HttpError classes, HTTP_ERROR namespace
|
|
25
|
+
└── status.ts # HTTP_STATUS codes and lookup
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Public API
|
|
29
|
+
|
|
30
|
+
### Primary Export: createHttpApi
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
function createHttpApi(
|
|
34
|
+
base?: string | null,
|
|
35
|
+
defaults?: Partial<FetchParams> | (() => Promise<Partial<FetchParams>>),
|
|
36
|
+
factoryErrorMessageExtractor?: ErrorMessageExtractor | null
|
|
37
|
+
): HttpApi
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### HttpApi Methods
|
|
41
|
+
|
|
42
|
+
| Method | Signature | Description |
|
|
43
|
+
|--------|-----------|-------------|
|
|
44
|
+
| `get` | `get(path, options?: GetOptions): Promise<unknown>` | GET request |
|
|
45
|
+
| `post` | `post(path, options?: DataOptions): Promise<unknown>` | POST request |
|
|
46
|
+
| `put` | `put(path, options?: DataOptions): Promise<unknown>` | PUT request |
|
|
47
|
+
| `patch` | `patch(path, options?: DataOptions): Promise<unknown>` | PATCH request |
|
|
48
|
+
| `del` | `del(path, options?: DataOptions): Promise<unknown>` | DELETE request |
|
|
49
|
+
| `url` | `url(path: string): string` | Build full URL |
|
|
50
|
+
| `base` | `get/set base: string \| null` | Base URL property |
|
|
51
|
+
|
|
52
|
+
### Exported Types
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
type RequestData = Record<string, unknown> | FormData | string | null;
|
|
56
|
+
|
|
57
|
+
interface FetchParams {
|
|
58
|
+
data?: RequestData;
|
|
59
|
+
token?: string | null;
|
|
60
|
+
headers?: Record<string, string> | null;
|
|
61
|
+
signal?: AbortSignal;
|
|
62
|
+
credentials?: 'omit' | 'same-origin' | 'include' | null;
|
|
63
|
+
raw?: boolean | null;
|
|
64
|
+
assert?: boolean | null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface GetOptions {
|
|
68
|
+
params?: FetchParams;
|
|
69
|
+
respHeaders?: ResponseHeaders | null;
|
|
70
|
+
errorExtractor?: ErrorMessageExtractor | null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface DataOptions {
|
|
74
|
+
data?: RequestData;
|
|
75
|
+
params?: FetchParams;
|
|
76
|
+
respHeaders?: ResponseHeaders | null;
|
|
77
|
+
errorExtractor?: ErrorMessageExtractor | null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type ErrorMessageExtractor = (body: unknown, response: Response) => string;
|
|
81
|
+
type ResponseHeaders = Record<string, string | number>;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Error Classes (HTTP_ERROR namespace)
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
HTTP_ERROR.HttpError // Base class (default 500)
|
|
88
|
+
HTTP_ERROR.BadRequest // 400
|
|
89
|
+
HTTP_ERROR.Unauthorized // 401
|
|
90
|
+
HTTP_ERROR.Forbidden // 403
|
|
91
|
+
HTTP_ERROR.NotFound // 404
|
|
92
|
+
HTTP_ERROR.MethodNotAllowed // 405
|
|
93
|
+
HTTP_ERROR.RequestTimeout // 408
|
|
94
|
+
HTTP_ERROR.Conflict // 409
|
|
95
|
+
HTTP_ERROR.Gone // 410
|
|
96
|
+
HTTP_ERROR.LengthRequired // 411
|
|
97
|
+
HTTP_ERROR.ImATeapot // 418
|
|
98
|
+
HTTP_ERROR.UnprocessableContent // 422
|
|
99
|
+
HTTP_ERROR.TooManyRequests // 429
|
|
100
|
+
HTTP_ERROR.InternalServerError // 500
|
|
101
|
+
HTTP_ERROR.NotImplemented // 501
|
|
102
|
+
HTTP_ERROR.BadGateway // 502
|
|
103
|
+
HTTP_ERROR.ServiceUnavailable // 503
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Utility Functions
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
function createHttpError(code: number | string, message?: string | null, body?: unknown, cause?: unknown): HttpError
|
|
110
|
+
function getErrorMessage(e: unknown, stripErrorPrefix?: boolean): string
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### HTTP Status Codes
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
class HTTP_STATUS {
|
|
117
|
+
static readonly INFO: {...} // 1xx
|
|
118
|
+
static readonly SUCCESS: {...} // 2xx
|
|
119
|
+
static readonly REDIRECT: {...} // 3xx
|
|
120
|
+
static readonly ERROR_CLIENT: {...} // 4xx
|
|
121
|
+
static readonly ERROR_SERVER: {...} // 5xx
|
|
122
|
+
|
|
123
|
+
// Direct shortcuts
|
|
124
|
+
static readonly OK: 200
|
|
125
|
+
static readonly NOT_FOUND: 404
|
|
126
|
+
// ... etc
|
|
127
|
+
|
|
128
|
+
static findByCode(code: number | string): { CODE, TEXT, _TYPE, _KEY } | null
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Key Behaviors
|
|
133
|
+
|
|
134
|
+
1. **Auto JSON parsing**: Response bodies are automatically parsed as JSON if possible
|
|
135
|
+
2. **Bearer token**: `token` param auto-adds `Authorization: Bearer {token}` header
|
|
136
|
+
3. **Error throwing**: By default, non-OK responses throw HttpError (disable with `assert: false`)
|
|
137
|
+
4. **Response headers**: Pass `respHeaders: {}` to capture response headers (mutated in place)
|
|
138
|
+
5. **Raw response**: Use `raw: true` to get raw Response object instead of parsed body
|
|
139
|
+
6. **Error priority**: per-request extractor → per-instance → global → built-in fallback
|
|
140
|
+
|
|
141
|
+
## Development Commands
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
deno task test # Run tests
|
|
145
|
+
deno task test:watch # Run tests in watch mode
|
|
146
|
+
deno task npm:build # Build for NPM
|
|
147
|
+
deno task publish # Publish to JSR and NPM
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Dependencies
|
|
151
|
+
|
|
152
|
+
- **Runtime**: None (zero dependencies)
|
|
153
|
+
- **Dev**: @std/assert (testing)
|
|
154
|
+
|
|
155
|
+
## File Locations
|
|
156
|
+
|
|
157
|
+
| Purpose | Path |
|
|
158
|
+
|---------|------|
|
|
159
|
+
| Entry point | `src/mod.ts` |
|
|
160
|
+
| HttpApi implementation | `src/api.ts` |
|
|
161
|
+
| Error classes | `src/error.ts` |
|
|
162
|
+
| Status codes | `src/status.ts` |
|
|
163
|
+
| Tests | `tests/*.test.ts` |
|
|
164
|
+
| NPM output | `.npm-dist/` |
|
|
165
|
+
|
|
166
|
+
## Common Patterns
|
|
167
|
+
|
|
168
|
+
### Basic Usage
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const api = createHttpApi("https://api.example.com", {
|
|
172
|
+
headers: { "Authorization": "Bearer token" }
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const data = await api.get("/users");
|
|
176
|
+
await api.post("/users", { data: { name: "John" } });
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Error Handling
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
try {
|
|
183
|
+
await api.get("/resource");
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (error instanceof HTTP_ERROR.NotFound) {
|
|
186
|
+
// Handle 404
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Dynamic Token
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
const api = createHttpApi("https://api.example.com", async () => ({
|
|
195
|
+
headers: { "Authorization": `Bearer ${await getToken()}` }
|
|
196
|
+
}));
|
|
197
|
+
```
|
package/API.md
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
# API Documentation
|
|
2
|
+
|
|
3
|
+
Complete API reference for `@marianmeres/http-utils`.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [createHttpApi](#createhttpapi)
|
|
8
|
+
- [HttpApi Class](#httpapi-class)
|
|
9
|
+
- [Types](#types)
|
|
10
|
+
- [HTTP Errors](#http-errors)
|
|
11
|
+
- [HTTP Status Codes](#http-status-codes)
|
|
12
|
+
- [Utilities](#utilities)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## createHttpApi
|
|
17
|
+
|
|
18
|
+
Creates an HTTP API client with convenient defaults and error handling.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
function createHttpApi(
|
|
22
|
+
base?: string | null,
|
|
23
|
+
defaults?: Partial<FetchParams> | (() => Promise<Partial<FetchParams>>),
|
|
24
|
+
factoryErrorMessageExtractor?: ErrorMessageExtractor | null
|
|
25
|
+
): HttpApi
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Parameters
|
|
29
|
+
|
|
30
|
+
| Parameter | Type | Description |
|
|
31
|
+
|-----------|------|-------------|
|
|
32
|
+
| `base` | `string \| null` | Optional base URL to prepend to all requests. |
|
|
33
|
+
| `defaults` | `object \| function` | Optional default parameters or async function returning defaults. |
|
|
34
|
+
| `factoryErrorMessageExtractor` | `ErrorMessageExtractor \| null` | Optional function to extract error messages from failed responses. |
|
|
35
|
+
|
|
36
|
+
### Returns
|
|
37
|
+
|
|
38
|
+
An `HttpApi` instance with methods: `get`, `post`, `put`, `patch`, `del`, `url`, and `base` property.
|
|
39
|
+
|
|
40
|
+
### Example
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { createHttpApi } from "@marianmeres/http-utils";
|
|
44
|
+
|
|
45
|
+
// Basic usage
|
|
46
|
+
const api = createHttpApi("https://api.example.com");
|
|
47
|
+
|
|
48
|
+
// With default headers
|
|
49
|
+
const api = createHttpApi("https://api.example.com", {
|
|
50
|
+
headers: { "Authorization": "Bearer token" }
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// With dynamic defaults (e.g., for token refresh)
|
|
54
|
+
const api = createHttpApi("https://api.example.com", async () => {
|
|
55
|
+
const token = await getToken();
|
|
56
|
+
return { headers: { "Authorization": `Bearer ${token}` } };
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// With custom error extractor
|
|
60
|
+
const api = createHttpApi("https://api.example.com", null, (body) => {
|
|
61
|
+
return body?.error?.message || "Unknown error";
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Static Properties
|
|
66
|
+
|
|
67
|
+
#### `createHttpApi.defaultErrorMessageExtractor`
|
|
68
|
+
|
|
69
|
+
Global default error message extractor. Applied to all requests unless overridden.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
createHttpApi.defaultErrorMessageExtractor = (body, response) => {
|
|
73
|
+
return body?.error?.message || response.statusText;
|
|
74
|
+
};
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Priority order: per-request > per-instance > global > built-in fallback.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## HttpApi Class
|
|
82
|
+
|
|
83
|
+
HTTP API client class. Usually created via `createHttpApi()`.
|
|
84
|
+
|
|
85
|
+
### Methods
|
|
86
|
+
|
|
87
|
+
#### `get(path, options?)`
|
|
88
|
+
|
|
89
|
+
Performs a GET request.
|
|
90
|
+
|
|
91
|
+
**New Options API (recommended):**
|
|
92
|
+
```ts
|
|
93
|
+
async get(path: string, options?: GetOptions): Promise<unknown>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Legacy API:**
|
|
97
|
+
```ts
|
|
98
|
+
async get(
|
|
99
|
+
path: string,
|
|
100
|
+
params?: FetchParams,
|
|
101
|
+
respHeaders?: ResponseHeaders | null,
|
|
102
|
+
errorMessageExtractor?: ErrorMessageExtractor | null
|
|
103
|
+
): Promise<unknown>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Example:**
|
|
107
|
+
```ts
|
|
108
|
+
// New API
|
|
109
|
+
const data = await api.get("/users", {
|
|
110
|
+
params: { headers: { "X-Custom": "value" } },
|
|
111
|
+
respHeaders: {}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Legacy API
|
|
115
|
+
const data = await api.get("/users", { headers: { "X-Custom": "value" } });
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### `post(path, options?)`
|
|
119
|
+
|
|
120
|
+
Performs a POST request.
|
|
121
|
+
|
|
122
|
+
**New Options API (recommended):**
|
|
123
|
+
```ts
|
|
124
|
+
async post(path: string, options?: DataOptions): Promise<unknown>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Legacy API:**
|
|
128
|
+
```ts
|
|
129
|
+
async post(
|
|
130
|
+
path: string,
|
|
131
|
+
data?: RequestData,
|
|
132
|
+
params?: FetchParams,
|
|
133
|
+
respHeaders?: ResponseHeaders | null,
|
|
134
|
+
errorMessageExtractor?: ErrorMessageExtractor | null
|
|
135
|
+
): Promise<unknown>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Example:**
|
|
139
|
+
```ts
|
|
140
|
+
// New API
|
|
141
|
+
const result = await api.post("/users", {
|
|
142
|
+
data: { name: "John" },
|
|
143
|
+
params: { headers: { "X-Custom": "value" } }
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Legacy API
|
|
147
|
+
const result = await api.post("/users", { name: "John" });
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### `put(path, options?)`
|
|
151
|
+
|
|
152
|
+
Performs a PUT request. Same signature as `post()`.
|
|
153
|
+
|
|
154
|
+
#### `patch(path, options?)`
|
|
155
|
+
|
|
156
|
+
Performs a PATCH request. Same signature as `post()`.
|
|
157
|
+
|
|
158
|
+
#### `del(path, options?)`
|
|
159
|
+
|
|
160
|
+
Performs a DELETE request. Same signature as `post()`.
|
|
161
|
+
|
|
162
|
+
#### `url(path)`
|
|
163
|
+
|
|
164
|
+
Builds the full URL from a path.
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
url(path: string): string
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Example:**
|
|
171
|
+
```ts
|
|
172
|
+
const api = createHttpApi("https://api.example.com");
|
|
173
|
+
api.url("/users"); // "https://api.example.com/users"
|
|
174
|
+
api.url("https://other.com/path"); // "https://other.com/path" (absolute URLs returned as-is)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Properties
|
|
178
|
+
|
|
179
|
+
#### `base`
|
|
180
|
+
|
|
181
|
+
Get or set the base URL.
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
get base(): string | null | undefined
|
|
185
|
+
set base(v: string | null | undefined)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Types
|
|
191
|
+
|
|
192
|
+
### RequestData
|
|
193
|
+
|
|
194
|
+
Request body data type.
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
type RequestData = Record<string, unknown> | FormData | string | null;
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### FetchParams
|
|
201
|
+
|
|
202
|
+
Parameters for fetch requests.
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
interface FetchParams {
|
|
206
|
+
/** Request body data (automatically JSON stringified unless FormData). */
|
|
207
|
+
data?: RequestData;
|
|
208
|
+
/** Bearer token (auto-adds `Authorization: Bearer {token}` header). */
|
|
209
|
+
token?: string | null;
|
|
210
|
+
/** Custom request headers. */
|
|
211
|
+
headers?: Record<string, string> | null;
|
|
212
|
+
/** AbortSignal for request cancellation. */
|
|
213
|
+
signal?: AbortSignal;
|
|
214
|
+
/** Credentials mode for the request. */
|
|
215
|
+
credentials?: 'omit' | 'same-origin' | 'include' | null;
|
|
216
|
+
/** If true, returns the raw Response object instead of parsed body. */
|
|
217
|
+
raw?: boolean | null;
|
|
218
|
+
/** If false, does not throw on HTTP errors (default: true). */
|
|
219
|
+
assert?: boolean | null;
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### GetOptions
|
|
224
|
+
|
|
225
|
+
Options for HTTP GET requests (new API).
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
interface GetOptions {
|
|
229
|
+
/** Fetch parameters (headers, token, signal, credentials, raw, assert). */
|
|
230
|
+
params?: FetchParams;
|
|
231
|
+
/** Object to receive response headers (will be mutated). */
|
|
232
|
+
respHeaders?: ResponseHeaders | null;
|
|
233
|
+
/** Custom error message extractor for this request. */
|
|
234
|
+
errorExtractor?: ErrorMessageExtractor | null;
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### DataOptions
|
|
239
|
+
|
|
240
|
+
Options for HTTP POST/PUT/PATCH/DELETE requests (new API).
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
interface DataOptions {
|
|
244
|
+
/** Request body data. */
|
|
245
|
+
data?: RequestData;
|
|
246
|
+
/** Fetch parameters (headers, token, signal, credentials, raw, assert). */
|
|
247
|
+
params?: FetchParams;
|
|
248
|
+
/** Object to receive response headers (will be mutated). */
|
|
249
|
+
respHeaders?: ResponseHeaders | null;
|
|
250
|
+
/** Custom error message extractor for this request. */
|
|
251
|
+
errorExtractor?: ErrorMessageExtractor | null;
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### ResponseHeaders
|
|
256
|
+
|
|
257
|
+
Object to receive response headers after a request completes.
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
type ResponseHeaders = Record<string, string | number>;
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Special keys added after request:
|
|
264
|
+
- `__http_status_code__`: The HTTP status code
|
|
265
|
+
- `__http_status_text__`: The HTTP status text
|
|
266
|
+
|
|
267
|
+
### ErrorMessageExtractor
|
|
268
|
+
|
|
269
|
+
Function to extract error messages from failed HTTP responses.
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
type ErrorMessageExtractor = (body: unknown, response: Response) => string;
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## HTTP Errors
|
|
278
|
+
|
|
279
|
+
All errors extend `HttpError` base class.
|
|
280
|
+
|
|
281
|
+
### HttpError (Base Class)
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
class HttpError extends Error {
|
|
285
|
+
status: number; // HTTP status code
|
|
286
|
+
statusText: string; // HTTP status text
|
|
287
|
+
body: unknown; // Response body (auto-parsed as JSON)
|
|
288
|
+
cause: unknown; // Error cause/details
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Client Errors (4xx)
|
|
293
|
+
|
|
294
|
+
| Class | Status | Description |
|
|
295
|
+
|-------|--------|-------------|
|
|
296
|
+
| `BadRequest` | 400 | Bad Request |
|
|
297
|
+
| `Unauthorized` | 401 | Unauthorized |
|
|
298
|
+
| `Forbidden` | 403 | Forbidden |
|
|
299
|
+
| `NotFound` | 404 | Not Found |
|
|
300
|
+
| `MethodNotAllowed` | 405 | Method Not Allowed |
|
|
301
|
+
| `RequestTimeout` | 408 | Request Timeout |
|
|
302
|
+
| `Conflict` | 409 | Conflict |
|
|
303
|
+
| `Gone` | 410 | Gone |
|
|
304
|
+
| `LengthRequired` | 411 | Length Required |
|
|
305
|
+
| `ImATeapot` | 418 | I'm a Teapot |
|
|
306
|
+
| `UnprocessableContent` | 422 | Unprocessable Content |
|
|
307
|
+
| `TooManyRequests` | 429 | Too Many Requests |
|
|
308
|
+
|
|
309
|
+
### Server Errors (5xx)
|
|
310
|
+
|
|
311
|
+
| Class | Status | Description |
|
|
312
|
+
|-------|--------|-------------|
|
|
313
|
+
| `InternalServerError` | 500 | Internal Server Error |
|
|
314
|
+
| `NotImplemented` | 501 | Not Implemented |
|
|
315
|
+
| `BadGateway` | 502 | Bad Gateway |
|
|
316
|
+
| `ServiceUnavailable` | 503 | Service Unavailable |
|
|
317
|
+
|
|
318
|
+
### HTTP_ERROR Namespace
|
|
319
|
+
|
|
320
|
+
All error classes are available via the `HTTP_ERROR` namespace:
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
import { HTTP_ERROR } from "@marianmeres/http-utils";
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
await api.get("/resource");
|
|
327
|
+
} catch (error) {
|
|
328
|
+
if (error instanceof HTTP_ERROR.NotFound) {
|
|
329
|
+
console.log("Resource not found");
|
|
330
|
+
}
|
|
331
|
+
if (error instanceof HTTP_ERROR.HttpError) {
|
|
332
|
+
console.log("HTTP error:", error.status);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## HTTP Status Codes
|
|
340
|
+
|
|
341
|
+
### HTTP_STATUS Class
|
|
342
|
+
|
|
343
|
+
Access status codes by category or via direct shortcuts.
|
|
344
|
+
|
|
345
|
+
#### Categories
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
HTTP_STATUS.INFO // 1xx Informational
|
|
349
|
+
HTTP_STATUS.SUCCESS // 2xx Success
|
|
350
|
+
HTTP_STATUS.REDIRECT // 3xx Redirection
|
|
351
|
+
HTTP_STATUS.ERROR_CLIENT // 4xx Client Error
|
|
352
|
+
HTTP_STATUS.ERROR_SERVER // 5xx Server Error
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
#### Category Access
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
HTTP_STATUS.SUCCESS.OK.CODE // 200
|
|
359
|
+
HTTP_STATUS.SUCCESS.OK.TEXT // "OK"
|
|
360
|
+
HTTP_STATUS.ERROR_CLIENT.NOT_FOUND.CODE // 404
|
|
361
|
+
HTTP_STATUS.ERROR_CLIENT.NOT_FOUND.TEXT // "Not Found"
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
#### Direct Shortcuts
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
HTTP_STATUS.OK // 200
|
|
368
|
+
HTTP_STATUS.CREATED // 201
|
|
369
|
+
HTTP_STATUS.ACCEPTED // 202
|
|
370
|
+
HTTP_STATUS.NO_CONTENT // 204
|
|
371
|
+
HTTP_STATUS.MOVED_PERMANENTLY // 301
|
|
372
|
+
HTTP_STATUS.FOUND // 302
|
|
373
|
+
HTTP_STATUS.NOT_MODIFIED // 304
|
|
374
|
+
HTTP_STATUS.BAD_REQUEST // 400
|
|
375
|
+
HTTP_STATUS.UNAUTHORIZED // 401
|
|
376
|
+
HTTP_STATUS.FORBIDDEN // 403
|
|
377
|
+
HTTP_STATUS.NOT_FOUND // 404
|
|
378
|
+
HTTP_STATUS.METHOD_NOT_ALLOWED // 405
|
|
379
|
+
HTTP_STATUS.CONFLICT // 409
|
|
380
|
+
HTTP_STATUS.GONE // 410
|
|
381
|
+
HTTP_STATUS.UNPROCESSABLE_CONTENT // 422
|
|
382
|
+
HTTP_STATUS.TOO_MANY_REQUESTS // 429
|
|
383
|
+
HTTP_STATUS.INTERNAL_SERVER_ERROR // 500
|
|
384
|
+
HTTP_STATUS.NOT_IMPLEMENTED // 501
|
|
385
|
+
HTTP_STATUS.SERVICE_UNAVAILABLE // 503
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### findByCode(code)
|
|
389
|
+
|
|
390
|
+
Lookup status code by numeric value.
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
static findByCode(code: number | string): {
|
|
394
|
+
CODE: number;
|
|
395
|
+
TEXT: string;
|
|
396
|
+
_TYPE: string;
|
|
397
|
+
_KEY: string;
|
|
398
|
+
} | null
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**Example:**
|
|
402
|
+
```ts
|
|
403
|
+
const info = HTTP_STATUS.findByCode(404);
|
|
404
|
+
// { CODE: 404, TEXT: "Not Found", _TYPE: "ERROR_CLIENT", _KEY: "NOT_FOUND" }
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Utilities
|
|
410
|
+
|
|
411
|
+
### createHttpError
|
|
412
|
+
|
|
413
|
+
Creates an HTTP error from a status code and optional details.
|
|
414
|
+
|
|
415
|
+
```ts
|
|
416
|
+
function createHttpError(
|
|
417
|
+
code: number | string,
|
|
418
|
+
message?: string | null,
|
|
419
|
+
body?: unknown,
|
|
420
|
+
cause?: unknown
|
|
421
|
+
): HttpError
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Returns a specific error class for well-known status codes.
|
|
425
|
+
|
|
426
|
+
**Example:**
|
|
427
|
+
```ts
|
|
428
|
+
const error = createHttpError(404, "User not found", { userId: 123 });
|
|
429
|
+
console.log(error instanceof NotFound); // true
|
|
430
|
+
console.log(error.status); // 404
|
|
431
|
+
console.log(error.body); // { userId: 123 }
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### getErrorMessage
|
|
435
|
+
|
|
436
|
+
Extracts a human-readable error message from various error formats.
|
|
437
|
+
|
|
438
|
+
```ts
|
|
439
|
+
function getErrorMessage(e: unknown, stripErrorPrefix?: boolean): string
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Priority order:**
|
|
443
|
+
1. `e.cause.message` / `e.cause.code` / `e.cause` (if string)
|
|
444
|
+
2. `e.body.error.message` / `e.body.message` / `e.body.error` / `e.body` (if string)
|
|
445
|
+
3. `e.message`
|
|
446
|
+
4. `e.name`
|
|
447
|
+
5. `e.toString()`
|
|
448
|
+
6. `"Unknown Error"`
|
|
449
|
+
|
|
450
|
+
**Example:**
|
|
451
|
+
```ts
|
|
452
|
+
import { getErrorMessage } from "@marianmeres/http-utils";
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
await api.get("/fail");
|
|
456
|
+
} catch (error) {
|
|
457
|
+
console.log(getErrorMessage(error)); // "Not Found"
|
|
458
|
+
}
|
|
459
|
+
```
|