@b9g/http-errors 0.1.5 → 0.2.0-beta.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 +112 -286
- package/package.json +3 -4
- package/src/index.d.ts +4 -22
- package/src/index.js +12 -27
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# @b9g/http-errors
|
|
2
|
-
|
|
3
|
-
**Standard HTTP error responses for ServiceWorker applications. Returns proper Response objects with status codes, not thrown exceptions.**
|
|
2
|
+
**Standard HTTP error classes with native cause support and automatic serialization**
|
|
4
3
|
|
|
5
4
|
## Features
|
|
6
5
|
|
|
7
|
-
- **ServiceWorker Compatible**: Returns Response objects perfect for `event.respondWith()`
|
|
8
|
-
- **Standard HTTP Status Codes**: Pre-defined functions for all common HTTP errors
|
|
9
|
-
- **No Exceptions**: Functional approach - return errors, don't throw them
|
|
10
|
-
- **TypeScript Support**: Full type definitions for all error classes
|
|
11
6
|
- **Universal**: Works in browsers, Node.js, Bun, and edge platforms
|
|
7
|
+
- **Response Protocol**: Implements `toResponse()` for automatic HTTP response conversion
|
|
8
|
+
- **Structured Logging**: Built-in `toJSON()` for clean error serialization
|
|
9
|
+
- **Standard HTTP Status Codes**: Pre-defined classes for all common HTTP errors
|
|
10
|
+
- **TypeScript Support**: Full type definitions for all error classes
|
|
11
|
+
- **Error Chaining**: Native `cause` support for error context
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
@@ -18,336 +18,162 @@ npm install @b9g/http-errors
|
|
|
18
18
|
|
|
19
19
|
## Quick Start
|
|
20
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
21
|
```javascript
|
|
45
22
|
import {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
23
|
+
NotFound,
|
|
24
|
+
BadRequest,
|
|
25
|
+
Unauthorized,
|
|
26
|
+
InternalServerError
|
|
66
27
|
} 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
28
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
29
|
+
// Throw as exceptions
|
|
30
|
+
throw new NotFound('Page not found');
|
|
31
|
+
throw new BadRequest('Invalid input data');
|
|
104
32
|
|
|
105
|
-
|
|
33
|
+
// Or convert to Response objects
|
|
34
|
+
const error = new NotFound('Page not found');
|
|
35
|
+
return error.toResponse(); // Response with status 404
|
|
106
36
|
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
});
|
|
37
|
+
// In development, get detailed error pages
|
|
38
|
+
return error.toResponse(true); // HTML page with stack trace
|
|
128
39
|
```
|
|
129
40
|
|
|
130
|
-
|
|
41
|
+
## API
|
|
131
42
|
|
|
132
|
-
|
|
133
|
-
router.use(async function* (request, context) {
|
|
134
|
-
try {
|
|
135
|
-
return yield request;
|
|
136
|
-
} catch (error) {
|
|
137
|
-
console.error('Request failed:', error);
|
|
138
|
-
return InternalServerError('Something went wrong');
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
```
|
|
43
|
+
### HTTPError Class
|
|
142
44
|
|
|
143
|
-
|
|
45
|
+
Base class for all HTTP errors. Extends `Error`.
|
|
144
46
|
|
|
145
47
|
```javascript
|
|
146
|
-
import {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// With retry information
|
|
155
|
-
const rateLimitError = TooManyRequests('Rate limit exceeded', {
|
|
156
|
-
retryAfter: 60,
|
|
157
|
-
limit: 100,
|
|
158
|
-
window: 3600
|
|
48
|
+
import { HTTPError } from '@b9g/http-errors';
|
|
49
|
+
|
|
50
|
+
const error = new HTTPError(404, 'Resource not found', {
|
|
51
|
+
cause: originalError, // Error that caused this
|
|
52
|
+
headers: { // Custom headers for response
|
|
53
|
+
'Cache-Control': 'no-store'
|
|
54
|
+
},
|
|
55
|
+
expose: true // Whether to expose message to client
|
|
159
56
|
});
|
|
160
57
|
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
});
|
|
58
|
+
error.status; // 404
|
|
59
|
+
error.message; // 'Resource not found'
|
|
60
|
+
error.expose; // true (client errors default to true, server errors to false)
|
|
61
|
+
error.headers; // { 'Cache-Control': 'no-store' }
|
|
166
62
|
```
|
|
167
63
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
### Classes
|
|
171
|
-
|
|
172
|
-
- `HTTPError` - Base HTTP error class (extends Error)
|
|
173
|
-
- `NotHandled` - Special error for unhandled requests
|
|
174
|
-
|
|
175
|
-
### Client Error Classes (4xx)
|
|
176
|
-
|
|
177
|
-
- `BadRequest` (400)
|
|
178
|
-
- `Unauthorized` (401)
|
|
179
|
-
- `Forbidden` (403)
|
|
180
|
-
- `NotFound` (404)
|
|
181
|
-
- `MethodNotAllowed` (405)
|
|
182
|
-
- `Conflict` (409)
|
|
183
|
-
- `UnprocessableEntity` (422)
|
|
184
|
-
- `TooManyRequests` (429)
|
|
185
|
-
|
|
186
|
-
### Server Error Classes (5xx)
|
|
187
|
-
|
|
188
|
-
- `InternalServerError` (500)
|
|
189
|
-
- `NotImplemented` (501)
|
|
190
|
-
- `BadGateway` (502)
|
|
191
|
-
- `ServiceUnavailable` (503)
|
|
192
|
-
- `GatewayTimeout` (504)
|
|
193
|
-
|
|
194
|
-
### Functions
|
|
195
|
-
|
|
196
|
-
- `createHTTPError(status, message?, options?)` - Create an HTTPError with a specific status code
|
|
197
|
-
- `isHTTPError(value)` - Type guard to check if a value is an HTTPError
|
|
198
|
-
|
|
199
|
-
### Types
|
|
200
|
-
|
|
201
|
-
- `HTTPErrorOptions` - Options for HTTPError constructor
|
|
64
|
+
### Methods
|
|
202
65
|
|
|
203
|
-
|
|
66
|
+
#### `toResponse(isDev?: boolean): Response`
|
|
204
67
|
|
|
205
|
-
|
|
68
|
+
Converts the error to an HTTP Response object.
|
|
206
69
|
|
|
207
|
-
|
|
70
|
+
- In development mode (`isDev = true`): Returns HTML page with stack trace
|
|
71
|
+
- In production mode: Returns plain text with minimal information
|
|
208
72
|
|
|
209
|
-
|
|
73
|
+
```javascript
|
|
74
|
+
const error = new NotFound('Page not found');
|
|
210
75
|
|
|
211
|
-
|
|
76
|
+
// Production response
|
|
77
|
+
error.toResponse(); // Response { status: 404, body: 'Page not found' }
|
|
212
78
|
|
|
213
|
-
|
|
214
|
-
|
|
79
|
+
// Development response with stack trace
|
|
80
|
+
error.toResponse(true); // Response { status: 404, body: '<html>...</html>' }
|
|
215
81
|
```
|
|
216
82
|
|
|
217
|
-
####
|
|
83
|
+
#### `toJSON(): object`
|
|
218
84
|
|
|
219
|
-
|
|
220
|
-
- `details` (optional): Additional error details (serialized as JSON)
|
|
221
|
-
|
|
222
|
-
#### Returns
|
|
223
|
-
|
|
224
|
-
Returns a `Response` object with:
|
|
225
|
-
- Appropriate HTTP status code
|
|
226
|
-
- `Content-Type: application/json`
|
|
227
|
-
- JSON body containing error information
|
|
228
|
-
|
|
229
|
-
### Response Format
|
|
85
|
+
Converts the error to a plain object for logging and serialization.
|
|
230
86
|
|
|
231
87
|
```javascript
|
|
232
|
-
{
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
"message": "User not found",
|
|
236
|
-
"status": 404,
|
|
237
|
-
"details": {
|
|
238
|
-
// Any additional details provided
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
```
|
|
88
|
+
const error = new BadRequest('Invalid email', {
|
|
89
|
+
headers: { 'X-Custom': 'value' }
|
|
90
|
+
});
|
|
243
91
|
|
|
244
|
-
|
|
92
|
+
JSON.stringify(error);
|
|
93
|
+
// {
|
|
94
|
+
// "name": "BadRequest",
|
|
95
|
+
// "message": "Invalid email",
|
|
96
|
+
// "status": 400,
|
|
97
|
+
// "statusCode": 400,
|
|
98
|
+
// "expose": true,
|
|
99
|
+
// "headers": { "X-Custom": "value" }
|
|
100
|
+
// }
|
|
245
101
|
|
|
246
|
-
|
|
102
|
+
### Client Error Classes (4xx)
|
|
247
103
|
|
|
248
|
-
```
|
|
249
|
-
import
|
|
104
|
+
```javascript
|
|
105
|
+
import {
|
|
106
|
+
BadRequest, // 400
|
|
107
|
+
Unauthorized, // 401
|
|
108
|
+
Forbidden, // 403
|
|
109
|
+
NotFound, // 404
|
|
110
|
+
MethodNotAllowed, // 405
|
|
111
|
+
Conflict, // 409
|
|
112
|
+
UnprocessableEntity, // 422
|
|
113
|
+
TooManyRequests // 429
|
|
114
|
+
} from '@b9g/http-errors';
|
|
250
115
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
116
|
+
// All accept message and options
|
|
117
|
+
throw new Unauthorized('Invalid credentials', {
|
|
118
|
+
headers: { 'WWW-Authenticate': 'Bearer realm="api"' }
|
|
119
|
+
});
|
|
254
120
|
|
|
255
|
-
|
|
256
|
-
|
|
121
|
+
throw new TooManyRequests('Rate limit exceeded', {
|
|
122
|
+
headers: { 'Retry-After': '60' }
|
|
123
|
+
});
|
|
257
124
|
```
|
|
258
125
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
### API Error Handling
|
|
126
|
+
### Server Error Classes (5xx)
|
|
262
127
|
|
|
263
128
|
```javascript
|
|
264
|
-
import {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
129
|
+
import {
|
|
130
|
+
InternalServerError, // 500
|
|
131
|
+
NotImplemented, // 501
|
|
132
|
+
BadGateway, // 502
|
|
133
|
+
ServiceUnavailable, // 503
|
|
134
|
+
GatewayTimeout // 504
|
|
269
135
|
} from '@b9g/http-errors';
|
|
270
136
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// Validation
|
|
276
|
-
if (!data.email) {
|
|
277
|
-
return BadRequest('Email is required');
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Check for existing user
|
|
281
|
-
const existing = await db.users.findByEmail(data.email);
|
|
282
|
-
if (existing) {
|
|
283
|
-
return Conflict('Email already registered');
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Create user
|
|
287
|
-
const user = await db.users.create(data);
|
|
288
|
-
return Response.json(user, { status: 201 });
|
|
289
|
-
|
|
290
|
-
} catch (error) {
|
|
291
|
-
return InternalServerError('Failed to create user');
|
|
292
|
-
}
|
|
137
|
+
// Server errors default to expose: false
|
|
138
|
+
throw new InternalServerError('Database connection failed', {
|
|
139
|
+
cause: dbError // Chain the original error
|
|
293
140
|
});
|
|
294
141
|
```
|
|
295
142
|
|
|
296
|
-
###
|
|
143
|
+
### Functions
|
|
144
|
+
|
|
145
|
+
#### `isHTTPError(value): value is HTTPError`
|
|
146
|
+
|
|
147
|
+
Type guard to check if a value is an HTTPError.
|
|
297
148
|
|
|
298
149
|
```javascript
|
|
299
|
-
import {
|
|
150
|
+
import {isHTTPError} from '@b9g/http-errors';
|
|
300
151
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (
|
|
305
|
-
return
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
try {
|
|
309
|
-
const user = await verifyToken(token);
|
|
310
|
-
context.user = user;
|
|
311
|
-
return yield request;
|
|
312
|
-
} catch (error) {
|
|
313
|
-
return Forbidden('Invalid or expired token');
|
|
152
|
+
try {
|
|
153
|
+
// ...
|
|
154
|
+
} catch (err) {
|
|
155
|
+
if (isHTTPError(err)) {
|
|
156
|
+
return err.toResponse();
|
|
314
157
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
router.use('/api/admin/*', authMiddleware);
|
|
158
|
+
throw err;
|
|
159
|
+
}
|
|
318
160
|
```
|
|
319
161
|
|
|
320
|
-
###
|
|
162
|
+
### Types
|
|
321
163
|
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (errors.length > 0) {
|
|
335
|
-
return BadRequest('Validation failed', { errors });
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return null; // Valid
|
|
164
|
+
```typescript
|
|
165
|
+
interface HTTPErrorOptions {
|
|
166
|
+
/** Original error that caused this HTTP error */
|
|
167
|
+
cause?: Error;
|
|
168
|
+
/** Custom headers to include in the error response */
|
|
169
|
+
headers?: Record<string, string>;
|
|
170
|
+
/** Whether error details should be exposed to clients (defaults based on status) */
|
|
171
|
+
expose?: boolean;
|
|
172
|
+
/** Additional properties to attach to the error */
|
|
173
|
+
[key: string]: any;
|
|
339
174
|
}
|
|
340
|
-
|
|
341
|
-
router.post('/register', async (request) => {
|
|
342
|
-
const data = await request.json();
|
|
343
|
-
|
|
344
|
-
const validationError = validateUser(data);
|
|
345
|
-
if (validationError) return validationError;
|
|
346
|
-
|
|
347
|
-
// Process valid data...
|
|
348
|
-
});
|
|
349
175
|
```
|
|
350
176
|
|
|
351
177
|
## License
|
|
352
178
|
|
|
353
|
-
MIT
|
|
179
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b9g/http-errors",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Standard HTTP error
|
|
3
|
+
"version": "0.2.0-beta.0",
|
|
4
|
+
"description": "Standard HTTP error classes with native cause support and automatic serialization",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"http",
|
|
7
7
|
"errors",
|
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@b9g/libuild": "^0.1.
|
|
18
|
-
"bun-types": "latest"
|
|
17
|
+
"@b9g/libuild": "^0.1.18"
|
|
19
18
|
},
|
|
20
19
|
"type": "module",
|
|
21
20
|
"types": "src/index.d.ts",
|
package/src/index.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Lightweight alternative to http-errors with native Error cause support
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Options for creating HTTP errors
|
|
2
|
+
* Standard HTTP error classes with native cause support and automatic serialization
|
|
7
3
|
*/
|
|
4
|
+
/** Options for creating HTTP errors */
|
|
8
5
|
export interface HTTPErrorOptions {
|
|
9
6
|
/** Original error that caused this HTTP error */
|
|
10
7
|
cause?: Error;
|
|
@@ -12,18 +9,14 @@ export interface HTTPErrorOptions {
|
|
|
12
9
|
headers?: Record<string, string>;
|
|
13
10
|
/** Whether the error details should be exposed to clients (defaults based on status) */
|
|
14
11
|
expose?: boolean;
|
|
15
|
-
/** Additional properties to attach to the error */
|
|
16
|
-
[key: string]: any;
|
|
17
12
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Base HTTP error class
|
|
20
|
-
*/
|
|
13
|
+
/** Base HTTP error class */
|
|
21
14
|
export declare class HTTPError extends Error {
|
|
22
15
|
readonly status: number;
|
|
23
|
-
readonly statusCode: number;
|
|
24
16
|
readonly expose: boolean;
|
|
25
17
|
readonly headers?: Record<string, string>;
|
|
26
18
|
constructor(status: number, message?: string, options?: HTTPErrorOptions);
|
|
19
|
+
get statusCode(): number;
|
|
27
20
|
/**
|
|
28
21
|
* Convert error to a plain object for serialization
|
|
29
22
|
*/
|
|
@@ -42,20 +35,10 @@ export declare class HTTPError extends Error {
|
|
|
42
35
|
*/
|
|
43
36
|
toResponse(isDev?: boolean): Response;
|
|
44
37
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Special error for middleware fallthrough (not an HTTP error)
|
|
47
|
-
*/
|
|
48
|
-
export declare class NotHandled extends Error {
|
|
49
|
-
constructor(message?: string);
|
|
50
|
-
}
|
|
51
38
|
/**
|
|
52
39
|
* Check if a value is an HTTP error
|
|
53
40
|
*/
|
|
54
41
|
export declare function isHTTPError(value: any): value is HTTPError;
|
|
55
|
-
/**
|
|
56
|
-
* Create an HTTP error with the given status code
|
|
57
|
-
*/
|
|
58
|
-
export declare function createHTTPError(status: number, message?: string, options?: HTTPErrorOptions): HTTPError;
|
|
59
42
|
export declare class BadRequest extends HTTPError {
|
|
60
43
|
constructor(message?: string, options?: HTTPErrorOptions);
|
|
61
44
|
}
|
|
@@ -95,4 +78,3 @@ export declare class ServiceUnavailable extends HTTPError {
|
|
|
95
78
|
export declare class GatewayTimeout extends HTTPError {
|
|
96
79
|
constructor(message?: string, options?: HTTPErrorOptions);
|
|
97
80
|
}
|
|
98
|
-
export default createHTTPError;
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="./index.d.ts" />
|
|
2
2
|
// src/index.ts
|
|
3
|
-
var
|
|
3
|
+
var STATUS_CODE_DEFAULTS = {
|
|
4
4
|
// 4xx Client Errors
|
|
5
5
|
400: "Bad Request",
|
|
6
6
|
401: "Unauthorized",
|
|
@@ -44,20 +44,23 @@ var STATUS_CODES = {
|
|
|
44
44
|
510: "Not Extended",
|
|
45
45
|
511: "Network Authentication Required"
|
|
46
46
|
};
|
|
47
|
+
var HTTP_ERROR = /* @__PURE__ */ Symbol.for("shovel.http-error");
|
|
47
48
|
var HTTPError = class extends Error {
|
|
48
49
|
status;
|
|
49
|
-
statusCode;
|
|
50
50
|
expose;
|
|
51
51
|
headers;
|
|
52
52
|
constructor(status, message, options = {}) {
|
|
53
|
-
const defaultMessage =
|
|
53
|
+
const defaultMessage = STATUS_CODE_DEFAULTS[status] || "Unknown Error";
|
|
54
54
|
super(message || defaultMessage, { cause: options.cause });
|
|
55
55
|
this.name = this.constructor.name;
|
|
56
|
-
this.status =
|
|
56
|
+
this.status = status;
|
|
57
57
|
this.expose = options.expose ?? status < 500;
|
|
58
58
|
this.headers = options.headers;
|
|
59
59
|
Object.assign(this, options);
|
|
60
60
|
}
|
|
61
|
+
get statusCode() {
|
|
62
|
+
return this.status;
|
|
63
|
+
}
|
|
61
64
|
/**
|
|
62
65
|
* Convert error to a plain object for serialization
|
|
63
66
|
*/
|
|
@@ -80,7 +83,7 @@ var HTTPError = class extends Error {
|
|
|
80
83
|
const headers = new Headers(this.headers);
|
|
81
84
|
if (isDev && this.expose) {
|
|
82
85
|
headers.set("Content-Type", "text/html; charset=utf-8");
|
|
83
|
-
const statusText =
|
|
86
|
+
const statusText = STATUS_CODE_DEFAULTS[this.status] || "Unknown Error";
|
|
84
87
|
const html = `<!DOCTYPE html>
|
|
85
88
|
<html>
|
|
86
89
|
<head>
|
|
@@ -105,34 +108,20 @@ var HTTPError = class extends Error {
|
|
|
105
108
|
});
|
|
106
109
|
}
|
|
107
110
|
headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
108
|
-
const body = this.expose ? this.message :
|
|
111
|
+
const body = this.expose ? this.message : STATUS_CODE_DEFAULTS[this.status] || "Unknown Error";
|
|
109
112
|
return new Response(body, {
|
|
110
113
|
status: this.status,
|
|
111
|
-
statusText:
|
|
114
|
+
statusText: STATUS_CODE_DEFAULTS[this.status],
|
|
112
115
|
headers
|
|
113
116
|
});
|
|
114
117
|
}
|
|
115
118
|
};
|
|
119
|
+
HTTPError.prototype[HTTP_ERROR] = true;
|
|
116
120
|
function escapeHTML(str) {
|
|
117
121
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
118
122
|
}
|
|
119
|
-
var NotHandled = class extends Error {
|
|
120
|
-
constructor(message = "Request not handled by middleware") {
|
|
121
|
-
super(message);
|
|
122
|
-
this.name = "NotHandled";
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
123
|
function isHTTPError(value) {
|
|
126
|
-
|
|
127
|
-
return true;
|
|
128
|
-
if (!(value instanceof Error))
|
|
129
|
-
return false;
|
|
130
|
-
const hasStatus = "status" in value && typeof value.status === "number";
|
|
131
|
-
const hasStatusCode = "statusCode" in value && typeof value.statusCode === "number";
|
|
132
|
-
return hasStatus && hasStatusCode && value.status === value.statusCode;
|
|
133
|
-
}
|
|
134
|
-
function createHTTPError(status, message, options) {
|
|
135
|
-
return new HTTPError(status, message, options);
|
|
124
|
+
return !!(value && value[HTTP_ERROR]);
|
|
136
125
|
}
|
|
137
126
|
var BadRequest = class extends HTTPError {
|
|
138
127
|
constructor(message, options) {
|
|
@@ -199,7 +188,6 @@ var GatewayTimeout = class extends HTTPError {
|
|
|
199
188
|
super(504, message, options);
|
|
200
189
|
}
|
|
201
190
|
};
|
|
202
|
-
var src_default = createHTTPError;
|
|
203
191
|
export {
|
|
204
192
|
BadGateway,
|
|
205
193
|
BadRequest,
|
|
@@ -210,13 +198,10 @@ export {
|
|
|
210
198
|
InternalServerError,
|
|
211
199
|
MethodNotAllowed,
|
|
212
200
|
NotFound,
|
|
213
|
-
NotHandled,
|
|
214
201
|
NotImplemented,
|
|
215
202
|
ServiceUnavailable,
|
|
216
203
|
TooManyRequests,
|
|
217
204
|
Unauthorized,
|
|
218
205
|
UnprocessableEntity,
|
|
219
|
-
createHTTPError,
|
|
220
|
-
src_default as default,
|
|
221
206
|
isHTTPError
|
|
222
207
|
};
|