@b9g/http-errors 0.1.0 → 0.1.3
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 +314 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# @b9g/http-errors
|
|
2
|
+
|
|
3
|
+
Standard HTTP error responses with proper status codes and web platform Response objects.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Standard HTTP Errors**: Pre-defined error classes for common HTTP status codes
|
|
8
|
+
- **Web Platform Response**: Returns proper Response objects, not thrown exceptions
|
|
9
|
+
- **Consistent API**: Uniform interface across all error types
|
|
10
|
+
- **TypeScript Support**: Full type definitions for all error classes
|
|
11
|
+
- **Lightweight**: Minimal dependencies, works everywhere
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @b9g/http-errors
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
import {
|
|
23
|
+
NotFound,
|
|
24
|
+
BadRequest,
|
|
25
|
+
Unauthorized,
|
|
26
|
+
InternalServerError
|
|
27
|
+
} from '@b9g/http-errors';
|
|
28
|
+
|
|
29
|
+
// Create error responses
|
|
30
|
+
const notFound = NotFound('Page not found');
|
|
31
|
+
const badRequest = BadRequest('Invalid input data');
|
|
32
|
+
const unauthorized = Unauthorized('Authentication required');
|
|
33
|
+
const serverError = InternalServerError('Database connection failed');
|
|
34
|
+
|
|
35
|
+
// All return Response objects
|
|
36
|
+
console.log(notFound instanceof Response); // true
|
|
37
|
+
console.log(notFound.status); // 404
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Available Error Classes
|
|
41
|
+
|
|
42
|
+
### Client Errors (4xx)
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
import {
|
|
46
|
+
BadRequest, // 400
|
|
47
|
+
Unauthorized, // 401
|
|
48
|
+
PaymentRequired, // 402
|
|
49
|
+
Forbidden, // 403
|
|
50
|
+
NotFound, // 404
|
|
51
|
+
MethodNotAllowed, // 405
|
|
52
|
+
NotAcceptable, // 406
|
|
53
|
+
RequestTimeout, // 408
|
|
54
|
+
Conflict, // 409
|
|
55
|
+
Gone, // 410
|
|
56
|
+
LengthRequired, // 411
|
|
57
|
+
PreconditionFailed, // 412
|
|
58
|
+
PayloadTooLarge, // 413
|
|
59
|
+
URITooLong, // 414
|
|
60
|
+
UnsupportedMediaType, // 415
|
|
61
|
+
RangeNotSatisfiable, // 416
|
|
62
|
+
ExpectationFailed, // 417
|
|
63
|
+
ImATeapot, // 418
|
|
64
|
+
UnprocessableEntity, // 422
|
|
65
|
+
TooManyRequests, // 429
|
|
66
|
+
} from '@b9g/http-errors';
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Server Errors (5xx)
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
import {
|
|
73
|
+
InternalServerError, // 500
|
|
74
|
+
NotImplemented, // 501
|
|
75
|
+
BadGateway, // 502
|
|
76
|
+
ServiceUnavailable, // 503
|
|
77
|
+
GatewayTimeout, // 504
|
|
78
|
+
HTTPVersionNotSupported, // 505
|
|
79
|
+
} from '@b9g/http-errors';
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Usage Examples
|
|
83
|
+
|
|
84
|
+
### Basic Error Responses
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
import { NotFound, BadRequest } from '@b9g/http-errors';
|
|
88
|
+
|
|
89
|
+
// Simple message
|
|
90
|
+
const error1 = NotFound('User not found');
|
|
91
|
+
|
|
92
|
+
// With additional details
|
|
93
|
+
const error2 = BadRequest('Invalid email format', {
|
|
94
|
+
field: 'email',
|
|
95
|
+
code: 'INVALID_FORMAT'
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Router Integration
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
import { Router } from '@b9g/router';
|
|
103
|
+
import { NotFound, BadRequest, Unauthorized } from '@b9g/http-errors';
|
|
104
|
+
|
|
105
|
+
const router = new Router();
|
|
106
|
+
|
|
107
|
+
router.get('/users/:id', async (request, context) => {
|
|
108
|
+
const { id } = context.params;
|
|
109
|
+
|
|
110
|
+
// Validate input
|
|
111
|
+
if (!id || isNaN(Number(id))) {
|
|
112
|
+
return BadRequest('Invalid user ID');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check authentication
|
|
116
|
+
if (!request.headers.get('authorization')) {
|
|
117
|
+
return Unauthorized('Authentication required');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Find user
|
|
121
|
+
const user = await db.users.find(id);
|
|
122
|
+
if (!user) {
|
|
123
|
+
return NotFound('User not found');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return Response.json(user);
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Middleware Error Handling
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
router.use(async (request, context, next) => {
|
|
134
|
+
try {
|
|
135
|
+
return await next();
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error('Request failed:', error);
|
|
138
|
+
return InternalServerError('Something went wrong');
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Custom Error Details
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
import { BadRequest, Conflict } from '@b9g/http-errors';
|
|
147
|
+
|
|
148
|
+
// With error code
|
|
149
|
+
const validationError = BadRequest('Validation failed', {
|
|
150
|
+
code: 'VALIDATION_ERROR',
|
|
151
|
+
fields: ['email', 'password']
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// With retry information
|
|
155
|
+
const rateLimitError = TooManyRequests('Rate limit exceeded', {
|
|
156
|
+
retryAfter: 60,
|
|
157
|
+
limit: 100,
|
|
158
|
+
window: 3600
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// With conflict details
|
|
162
|
+
const duplicateError = Conflict('Email already exists', {
|
|
163
|
+
field: 'email',
|
|
164
|
+
value: 'user@example.com'
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## API Reference
|
|
169
|
+
|
|
170
|
+
### Error Functions
|
|
171
|
+
|
|
172
|
+
All error functions follow the same signature:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
function ErrorName(message?: string, details?: any): Response
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### Parameters
|
|
179
|
+
|
|
180
|
+
- `message` (optional): Human-readable error message
|
|
181
|
+
- `details` (optional): Additional error details (serialized as JSON)
|
|
182
|
+
|
|
183
|
+
#### Returns
|
|
184
|
+
|
|
185
|
+
Returns a `Response` object with:
|
|
186
|
+
- Appropriate HTTP status code
|
|
187
|
+
- `Content-Type: application/json`
|
|
188
|
+
- JSON body containing error information
|
|
189
|
+
|
|
190
|
+
### Response Format
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
{
|
|
194
|
+
"error": {
|
|
195
|
+
"type": "NotFound",
|
|
196
|
+
"message": "User not found",
|
|
197
|
+
"status": 404,
|
|
198
|
+
"details": {
|
|
199
|
+
// Any additional details provided
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## TypeScript Support
|
|
206
|
+
|
|
207
|
+
Full TypeScript definitions included:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
import type { ErrorResponse } from '@b9g/http-errors';
|
|
211
|
+
|
|
212
|
+
function handleError(): ErrorResponse {
|
|
213
|
+
return NotFound('Resource not found');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ErrorResponse extends Response
|
|
217
|
+
const response: Response = handleError();
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Integration Examples
|
|
221
|
+
|
|
222
|
+
### API Error Handling
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
import {
|
|
226
|
+
BadRequest,
|
|
227
|
+
NotFound,
|
|
228
|
+
Conflict,
|
|
229
|
+
InternalServerError
|
|
230
|
+
} from '@b9g/http-errors';
|
|
231
|
+
|
|
232
|
+
router.post('/api/users', async (request) => {
|
|
233
|
+
try {
|
|
234
|
+
const data = await request.json();
|
|
235
|
+
|
|
236
|
+
// Validation
|
|
237
|
+
if (!data.email) {
|
|
238
|
+
return BadRequest('Email is required');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Check for existing user
|
|
242
|
+
const existing = await db.users.findByEmail(data.email);
|
|
243
|
+
if (existing) {
|
|
244
|
+
return Conflict('Email already registered');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Create user
|
|
248
|
+
const user = await db.users.create(data);
|
|
249
|
+
return Response.json(user, { status: 201 });
|
|
250
|
+
|
|
251
|
+
} catch (error) {
|
|
252
|
+
return InternalServerError('Failed to create user');
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Auth Middleware
|
|
258
|
+
|
|
259
|
+
```javascript
|
|
260
|
+
import { Unauthorized, Forbidden } from '@b9g/http-errors';
|
|
261
|
+
|
|
262
|
+
const authMiddleware = async (request, context, next) => {
|
|
263
|
+
const token = request.headers.get('authorization');
|
|
264
|
+
|
|
265
|
+
if (!token) {
|
|
266
|
+
return Unauthorized('Authentication required');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
const user = await verifyToken(token);
|
|
271
|
+
context.user = user;
|
|
272
|
+
return await next();
|
|
273
|
+
} catch (error) {
|
|
274
|
+
return Forbidden('Invalid or expired token');
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
router.use('/api/admin/*', authMiddleware);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Input Validation
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
import { BadRequest } from '@b9g/http-errors';
|
|
285
|
+
|
|
286
|
+
function validateUser(data) {
|
|
287
|
+
const errors = [];
|
|
288
|
+
|
|
289
|
+
if (!data.email) errors.push('email is required');
|
|
290
|
+
if (!data.password) errors.push('password is required');
|
|
291
|
+
if (data.password && data.password.length < 8) {
|
|
292
|
+
errors.push('password must be at least 8 characters');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (errors.length > 0) {
|
|
296
|
+
return BadRequest('Validation failed', { errors });
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return null; // Valid
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
router.post('/register', async (request) => {
|
|
303
|
+
const data = await request.json();
|
|
304
|
+
|
|
305
|
+
const validationError = validateUser(data);
|
|
306
|
+
if (validationError) return validationError;
|
|
307
|
+
|
|
308
|
+
// Process valid data...
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## License
|
|
313
|
+
|
|
314
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/http-errors",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "HTTP error classes for web applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"http",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
],
|
|
11
11
|
"dependencies": {},
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"@b9g/libuild": "^0.1.
|
|
13
|
+
"@b9g/libuild": "^0.1.11",
|
|
14
14
|
"bun-types": "latest"
|
|
15
15
|
},
|
|
16
16
|
"type": "module",
|