@aklinker1/zeta 1.0.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/LICENSE +21 -0
- package/README.md +655 -0
- package/package.json +60 -0
- package/src/adapters/zod-schema-adapter.ts +46 -0
- package/src/app.ts +453 -0
- package/src/client.ts +183 -0
- package/src/custom-responses.ts +166 -0
- package/src/errors.ts +543 -0
- package/src/index.ts +5 -0
- package/src/internal/call-handler.ts +132 -0
- package/src/internal/serialization.ts +72 -0
- package/src/internal/utils.ts +131 -0
- package/src/open-api.ts +234 -0
- package/src/status.ts +143 -0
- package/src/testing.ts +62 -0
- package/src/types.ts +1111 -0
package/src/errors.ts
ADDED
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import { HttpStatus } from "./status";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Thrown when validation fails. Refer to `.issues` for details on what went wrong.
|
|
6
|
+
*/
|
|
7
|
+
export class ValidationGlobalError extends Error {
|
|
8
|
+
constructor(
|
|
9
|
+
readonly input: any,
|
|
10
|
+
readonly issues: ReadonlyArray<StandardSchemaV1.Issue>,
|
|
11
|
+
) {
|
|
12
|
+
super("Validation error");
|
|
13
|
+
this.name = "ValidationGlobalError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Base class of all HTTP errors. You can throw this error or throw any of the
|
|
19
|
+
* subclasses. Zeta will automatically detect and handle these errors, setting
|
|
20
|
+
* the appropriate status code and message.
|
|
21
|
+
*
|
|
22
|
+
* Error responses will include the stacktrace during development. To hide the
|
|
23
|
+
* stacktrace in production, set the `NODE_ENV` environment variable to
|
|
24
|
+
* `production`.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* throw new HttpError(HttpStatus.BadRequest, "Bad request")
|
|
29
|
+
* // OR
|
|
30
|
+
* throw new BadRequestError()
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class HttpError extends Error {
|
|
34
|
+
constructor(
|
|
35
|
+
readonly status: HttpStatus,
|
|
36
|
+
message: string,
|
|
37
|
+
readonly additionalInfo?: Record<string, any>,
|
|
38
|
+
options?: ErrorOptions,
|
|
39
|
+
) {
|
|
40
|
+
super(message, options);
|
|
41
|
+
this.name = "HttpError";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Shorthand for `new HttpError(HttpStatus.BadRequest, "Bad Request", ...)` */
|
|
46
|
+
export class BadRequestHttpError extends HttpError {
|
|
47
|
+
constructor(
|
|
48
|
+
message: string = "Bad Request",
|
|
49
|
+
additionalInfo?: Record<string, any>,
|
|
50
|
+
options?: ErrorOptions,
|
|
51
|
+
) {
|
|
52
|
+
super(HttpStatus.BadRequest, message, additionalInfo, options);
|
|
53
|
+
this.name = "BadRequestError";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Shorthand for `new HttpError(HttpStatus.Unauthorized, "Unauthorized", ...)` */
|
|
58
|
+
export class UnauthorizedHttpError extends HttpError {
|
|
59
|
+
constructor(
|
|
60
|
+
message: string = "Unauthorized",
|
|
61
|
+
additionalInfo?: Record<string, any>,
|
|
62
|
+
options?: ErrorOptions,
|
|
63
|
+
) {
|
|
64
|
+
super(HttpStatus.Unauthorized, message, additionalInfo, options);
|
|
65
|
+
this.name = "UnauthorizedError";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Shorthand for `new HttpError(HttpStatus.PaymentRequired, "Payment Required", ...)` */
|
|
70
|
+
export class PaymentRequiredHttpError extends HttpError {
|
|
71
|
+
constructor(
|
|
72
|
+
message: string = "Payment Required",
|
|
73
|
+
additionalInfo?: Record<string, any>,
|
|
74
|
+
options?: ErrorOptions,
|
|
75
|
+
) {
|
|
76
|
+
super(HttpStatus.PaymentRequired, message, additionalInfo, options);
|
|
77
|
+
this.name = "PaymentRequiredError";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Shorthand for `new HttpError(HttpStatus.Forbidden, "Forbidden", ...)` */
|
|
82
|
+
export class ForbiddenHttpError extends HttpError {
|
|
83
|
+
constructor(
|
|
84
|
+
message: string = "Forbidden",
|
|
85
|
+
additionalInfo?: Record<string, any>,
|
|
86
|
+
options?: ErrorOptions,
|
|
87
|
+
) {
|
|
88
|
+
super(HttpStatus.Forbidden, message, additionalInfo, options);
|
|
89
|
+
this.name = "ForbiddenError";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Shorthand for `new HttpError(HttpStatus.NotFound, "Not Found", ...)` */
|
|
94
|
+
export class NotFoundHttpError extends HttpError {
|
|
95
|
+
constructor(
|
|
96
|
+
message: string = "Not Found",
|
|
97
|
+
additionalInfo?: Record<string, any>,
|
|
98
|
+
options?: ErrorOptions,
|
|
99
|
+
) {
|
|
100
|
+
super(HttpStatus.NotFound, message, additionalInfo, options);
|
|
101
|
+
this.name = "NotFoundHttpError";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Shorthand for `new HttpError(HttpStatus.MethodNotAllowed, "Method Not Allowed", ...)` */
|
|
106
|
+
export class MethodNotAllowedHttpError extends HttpError {
|
|
107
|
+
constructor(
|
|
108
|
+
message: string = "Method Not Allowed",
|
|
109
|
+
additionalInfo?: Record<string, any>,
|
|
110
|
+
options?: ErrorOptions,
|
|
111
|
+
) {
|
|
112
|
+
super(HttpStatus.MethodNotAllowed, message, additionalInfo, options);
|
|
113
|
+
this.name = "MethodNotAllowedError";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Shorthand for `new HttpError(HttpStatus.NotAcceptable, "Not Acceptable", ...)` */
|
|
118
|
+
export class NotAcceptableHttpError extends HttpError {
|
|
119
|
+
constructor(
|
|
120
|
+
message: string = "Not Acceptable",
|
|
121
|
+
additionalInfo?: Record<string, any>,
|
|
122
|
+
options?: ErrorOptions,
|
|
123
|
+
) {
|
|
124
|
+
super(HttpStatus.NotAcceptable, message, additionalInfo, options);
|
|
125
|
+
this.name = "NotAcceptableError";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Shorthand for `new HttpError(HttpStatus.ProxyAuthenticationRequired, "Proxy Authentication Required", ...)` */
|
|
130
|
+
export class ProxyAuthenticationRequiredHttpError extends HttpError {
|
|
131
|
+
constructor(
|
|
132
|
+
message: string = "Proxy Authentication Required",
|
|
133
|
+
additionalInfo?: Record<string, any>,
|
|
134
|
+
options?: ErrorOptions,
|
|
135
|
+
) {
|
|
136
|
+
super(
|
|
137
|
+
HttpStatus.ProxyAuthenticationRequired,
|
|
138
|
+
message,
|
|
139
|
+
additionalInfo,
|
|
140
|
+
options,
|
|
141
|
+
);
|
|
142
|
+
this.name = "ProxyAuthenticationRequiredError";
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Shorthand for `new HttpError(HttpStatus.RequestTimeout, "Request Timeout", ...)` */
|
|
147
|
+
export class RequestTimeoutHttpError extends HttpError {
|
|
148
|
+
constructor(
|
|
149
|
+
message: string = "Request Timeout",
|
|
150
|
+
additionalInfo?: Record<string, any>,
|
|
151
|
+
options?: ErrorOptions,
|
|
152
|
+
) {
|
|
153
|
+
super(HttpStatus.RequestTimeout, message, additionalInfo, options);
|
|
154
|
+
this.name = "RequestTimeoutError";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Shorthand for `new HttpError(HttpStatus.Conflict, "Conflict", ...)` */
|
|
159
|
+
export class ConflictHttpError extends HttpError {
|
|
160
|
+
constructor(
|
|
161
|
+
message: string = "Conflict",
|
|
162
|
+
additionalInfo?: Record<string, any>,
|
|
163
|
+
options?: ErrorOptions,
|
|
164
|
+
) {
|
|
165
|
+
super(HttpStatus.Conflict, message, additionalInfo, options);
|
|
166
|
+
this.name = "ConflictError";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Shorthand for `new HttpError(HttpStatus.Gone, "Gone", ...)` */
|
|
171
|
+
export class GoneHttpError extends HttpError {
|
|
172
|
+
constructor(
|
|
173
|
+
message: string = "Gone",
|
|
174
|
+
additionalInfo?: Record<string, any>,
|
|
175
|
+
options?: ErrorOptions,
|
|
176
|
+
) {
|
|
177
|
+
super(HttpStatus.Gone, message, additionalInfo, options);
|
|
178
|
+
this.name = "GoneError";
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Shorthand for `new HttpError(HttpStatus.LengthRequired, "Length Required", ...)` */
|
|
183
|
+
export class LengthRequiredHttpError extends HttpError {
|
|
184
|
+
constructor(
|
|
185
|
+
message: string = "Length Required",
|
|
186
|
+
additionalInfo?: Record<string, any>,
|
|
187
|
+
options?: ErrorOptions,
|
|
188
|
+
) {
|
|
189
|
+
super(HttpStatus.LengthRequired, message, additionalInfo, options);
|
|
190
|
+
this.name = "LengthRequiredError";
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Shorthand for `new HttpError(HttpStatus.PreconditionFailed, "Precondition Failed", ...)` */
|
|
195
|
+
export class PreconditionFailedHttpError extends HttpError {
|
|
196
|
+
constructor(
|
|
197
|
+
message: string = "Precondition Failed",
|
|
198
|
+
additionalInfo?: Record<string, any>,
|
|
199
|
+
options?: ErrorOptions,
|
|
200
|
+
) {
|
|
201
|
+
super(HttpStatus.PreconditionFailed, message, additionalInfo, options);
|
|
202
|
+
this.name = "PreconditionFailedError";
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Shorthand for `new HttpError(HttpStatus.ContentTooLarge, "Content Too Large", ...)` */
|
|
207
|
+
export class ContentTooLargeHttpError extends HttpError {
|
|
208
|
+
constructor(
|
|
209
|
+
message: string = "Content Too Large",
|
|
210
|
+
additionalInfo?: Record<string, any>,
|
|
211
|
+
options?: ErrorOptions,
|
|
212
|
+
) {
|
|
213
|
+
super(HttpStatus.ContentTooLarge, message, additionalInfo, options);
|
|
214
|
+
this.name = "ContentTooLargeError";
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Shorthand for `new HttpError(HttpStatus.UriTooLong, "URI Too Long", ...)` */
|
|
219
|
+
export class UriTooLongHttpError extends HttpError {
|
|
220
|
+
constructor(
|
|
221
|
+
message: string = "URI Too Long",
|
|
222
|
+
additionalInfo?: Record<string, any>,
|
|
223
|
+
options?: ErrorOptions,
|
|
224
|
+
) {
|
|
225
|
+
super(HttpStatus.UriTooLong, message, additionalInfo, options);
|
|
226
|
+
this.name = "UriTooLongError";
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Shorthand for `new HttpError(HttpStatus.UnsupportedMediaType, "Unsupported Media Type", ...)` */
|
|
231
|
+
export class UnsupportedMediaTypeHttpError extends HttpError {
|
|
232
|
+
constructor(
|
|
233
|
+
message: string = "Unsupported Media Type",
|
|
234
|
+
additionalInfo?: Record<string, any>,
|
|
235
|
+
options?: ErrorOptions,
|
|
236
|
+
) {
|
|
237
|
+
super(HttpStatus.UnsupportedMediaType, message, additionalInfo, options);
|
|
238
|
+
this.name = "UnsupportedMediaTypeError";
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Shorthand for `new HttpError(HttpStatus.RangeNotSatisfiable, "Range Not Satisfiable", ...)` */
|
|
243
|
+
export class RangeNotSatisfiableHttpError extends HttpError {
|
|
244
|
+
constructor(
|
|
245
|
+
message: string = "Range Not Satisfiable",
|
|
246
|
+
additionalInfo?: Record<string, any>,
|
|
247
|
+
options?: ErrorOptions,
|
|
248
|
+
) {
|
|
249
|
+
super(HttpStatus.RangeNotSatisfiable, message, additionalInfo, options);
|
|
250
|
+
this.name = "RangeNotSatisfiableError";
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Shorthand for `new HttpError(HttpStatus.ExpectationFailed, "Expectation Failed", ...)` */
|
|
255
|
+
export class ExpectationFailedHttpError extends HttpError {
|
|
256
|
+
constructor(
|
|
257
|
+
message: string = "Expectation Failed",
|
|
258
|
+
additionalInfo?: Record<string, any>,
|
|
259
|
+
options?: ErrorOptions,
|
|
260
|
+
) {
|
|
261
|
+
super(HttpStatus.ExpectationFailed, message, additionalInfo, options);
|
|
262
|
+
this.name = "ExpectationFailedError";
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** Shorthand for `new HttpError(HttpStatus.ImATeapot, "I'm a Teapot", ...)` */
|
|
267
|
+
export class ImATeapotHttpError extends HttpError {
|
|
268
|
+
constructor(
|
|
269
|
+
message: string = "I'm a Teapot",
|
|
270
|
+
additionalInfo?: Record<string, any>,
|
|
271
|
+
options?: ErrorOptions,
|
|
272
|
+
) {
|
|
273
|
+
super(HttpStatus.ImATeapot, message, additionalInfo, options);
|
|
274
|
+
this.name = "ImATeapotError";
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** Shorthand for `new HttpError(HttpStatus.MisdirectedRequest, "Misdirected Request", ...)` */
|
|
279
|
+
export class MisdirectedRequestHttpError extends HttpError {
|
|
280
|
+
constructor(
|
|
281
|
+
message: string = "Misdirected Request",
|
|
282
|
+
additionalInfo?: Record<string, any>,
|
|
283
|
+
options?: ErrorOptions,
|
|
284
|
+
) {
|
|
285
|
+
super(HttpStatus.MisdirectedRequest, message, additionalInfo, options);
|
|
286
|
+
this.name = "MisdirectedRequestError";
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Shorthand for `new HttpError(HttpStatus.UnprocessableEntity, "Unprocessable Entity", ...)` */
|
|
291
|
+
export class UnprocessableEntityHttpError extends HttpError {
|
|
292
|
+
constructor(
|
|
293
|
+
message: string = "Unprocessable Entity",
|
|
294
|
+
additionalInfo?: Record<string, any>,
|
|
295
|
+
options?: ErrorOptions,
|
|
296
|
+
) {
|
|
297
|
+
super(HttpStatus.UnprocessableEntity, message, additionalInfo, options);
|
|
298
|
+
this.name = "UnprocessableEntityError";
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** Shorthand for `new HttpError(HttpStatus.Locked, "Locked", ...)` */
|
|
303
|
+
export class LockedHttpError extends HttpError {
|
|
304
|
+
constructor(
|
|
305
|
+
message: string = "Locked",
|
|
306
|
+
additionalInfo?: Record<string, any>,
|
|
307
|
+
options?: ErrorOptions,
|
|
308
|
+
) {
|
|
309
|
+
super(HttpStatus.Locked, message, additionalInfo, options);
|
|
310
|
+
this.name = "LockedError";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Shorthand for `new HttpError(HttpStatus.FailedDependency, "Failed Dependency", ...)` */
|
|
315
|
+
export class FailedDependencyHttpError extends HttpError {
|
|
316
|
+
constructor(
|
|
317
|
+
message: string = "Failed Dependency",
|
|
318
|
+
additionalInfo?: Record<string, any>,
|
|
319
|
+
options?: ErrorOptions,
|
|
320
|
+
) {
|
|
321
|
+
super(HttpStatus.FailedDependency, message, additionalInfo, options);
|
|
322
|
+
this.name = "FailedDependencyError";
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Shorthand for `new HttpError(HttpStatus.TooEarly, "Too Early", ...)` */
|
|
327
|
+
export class TooEarlyHttpError extends HttpError {
|
|
328
|
+
constructor(
|
|
329
|
+
message: string = "Too Early",
|
|
330
|
+
additionalInfo?: Record<string, any>,
|
|
331
|
+
options?: ErrorOptions,
|
|
332
|
+
) {
|
|
333
|
+
super(HttpStatus.TooEarly, message, additionalInfo, options);
|
|
334
|
+
this.name = "TooEarlyError";
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Shorthand for `new HttpError(HttpStatus.UpgradeRequired, "Upgrade Required", ...)` */
|
|
339
|
+
export class UpgradeRequiredHttpError extends HttpError {
|
|
340
|
+
constructor(
|
|
341
|
+
message: string = "Upgrade Required",
|
|
342
|
+
additionalInfo?: Record<string, any>,
|
|
343
|
+
options?: ErrorOptions,
|
|
344
|
+
) {
|
|
345
|
+
super(HttpStatus.UpgradeRequired, message, additionalInfo, options);
|
|
346
|
+
this.name = "UpgradeRequiredError";
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** Shorthand for `new HttpError(HttpStatus.PreconditionRequired, "Precondition Required", ...)` */
|
|
351
|
+
export class PreconditionRequiredHttpError extends HttpError {
|
|
352
|
+
constructor(
|
|
353
|
+
message: string = "Precondition Required",
|
|
354
|
+
additionalInfo?: Record<string, any>,
|
|
355
|
+
options?: ErrorOptions,
|
|
356
|
+
) {
|
|
357
|
+
super(HttpStatus.PreconditionRequired, message, additionalInfo, options);
|
|
358
|
+
this.name = "PreconditionRequiredError";
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Shorthand for `new HttpError(HttpStatus.TooManyRequests, "Too Many Requests", ...)` */
|
|
363
|
+
export class TooManyRequestsHttpError extends HttpError {
|
|
364
|
+
constructor(
|
|
365
|
+
message: string = "Too Many Requests",
|
|
366
|
+
additionalInfo?: Record<string, any>,
|
|
367
|
+
options?: ErrorOptions,
|
|
368
|
+
) {
|
|
369
|
+
super(HttpStatus.TooManyRequests, message, additionalInfo, options);
|
|
370
|
+
this.name = "TooManyRequestsError";
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/** Shorthand for `new HttpError(HttpStatus.RequestHeaderFieldsTooLarge, "Request Header Fields Too Large", ...)` */
|
|
375
|
+
export class RequestHeaderFieldsTooLargeHttpError extends HttpError {
|
|
376
|
+
constructor(
|
|
377
|
+
message: string = "Request Header Fields Too Large",
|
|
378
|
+
additionalInfo?: Record<string, any>,
|
|
379
|
+
options?: ErrorOptions,
|
|
380
|
+
) {
|
|
381
|
+
super(
|
|
382
|
+
HttpStatus.RequestHeaderFieldsTooLarge,
|
|
383
|
+
message,
|
|
384
|
+
additionalInfo,
|
|
385
|
+
options,
|
|
386
|
+
);
|
|
387
|
+
this.name = "RequestHeaderFieldsTooLargeError";
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** Shorthand for `new HttpError(HttpStatus.UnavailableForLegalReasons, "Unavailable For Legal Reasons", ...)` */
|
|
392
|
+
export class UnavailableForLegalReasonsHttpError extends HttpError {
|
|
393
|
+
constructor(
|
|
394
|
+
message: string = "Unavailable For Legal Reasons",
|
|
395
|
+
additionalInfo?: Record<string, any>,
|
|
396
|
+
options?: ErrorOptions,
|
|
397
|
+
) {
|
|
398
|
+
super(
|
|
399
|
+
HttpStatus.UnavailableForLegalReasons,
|
|
400
|
+
message,
|
|
401
|
+
additionalInfo,
|
|
402
|
+
options,
|
|
403
|
+
);
|
|
404
|
+
this.name = "UnavailableForLegalReasonsError";
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/** Shorthand for `new HttpError(HttpStatus.InternalServerError, "Internal Server Error", ...)` */
|
|
409
|
+
export class InternalServerErrorHttpError extends HttpError {
|
|
410
|
+
constructor(
|
|
411
|
+
message: string = "Internal Server Error",
|
|
412
|
+
additionalInfo?: Record<string, any>,
|
|
413
|
+
options?: ErrorOptions,
|
|
414
|
+
) {
|
|
415
|
+
super(HttpStatus.InternalServerError, message, additionalInfo, options);
|
|
416
|
+
this.name = "InternalServerErrorError";
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/** Shorthand for `new HttpError(HttpStatus.NotImplemented, "Not Implemented", ...)` */
|
|
421
|
+
export class NotImplementedHttpError extends HttpError {
|
|
422
|
+
constructor(
|
|
423
|
+
message: string = "Not Implemented",
|
|
424
|
+
additionalInfo?: Record<string, any>,
|
|
425
|
+
options?: ErrorOptions,
|
|
426
|
+
) {
|
|
427
|
+
super(HttpStatus.NotImplemented, message, additionalInfo, options);
|
|
428
|
+
this.name = "NotImplementedError";
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** Shorthand for `new HttpError(HttpStatus.BadGateway, "Bad Gateway", ...)` */
|
|
433
|
+
export class BadGatewayHttpError extends HttpError {
|
|
434
|
+
constructor(
|
|
435
|
+
message: string = "Bad Gateway",
|
|
436
|
+
additionalInfo?: Record<string, any>,
|
|
437
|
+
options?: ErrorOptions,
|
|
438
|
+
) {
|
|
439
|
+
super(HttpStatus.BadGateway, message, additionalInfo, options);
|
|
440
|
+
this.name = "BadGatewayError";
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/** Shorthand for `new HttpError(HttpStatus.ServiceUnavailable, "Service Unavailable", ...)` */
|
|
445
|
+
export class ServiceUnavailableHttpError extends HttpError {
|
|
446
|
+
constructor(
|
|
447
|
+
message: string = "Service Unavailable",
|
|
448
|
+
additionalInfo?: Record<string, any>,
|
|
449
|
+
options?: ErrorOptions,
|
|
450
|
+
) {
|
|
451
|
+
super(HttpStatus.ServiceUnavailable, message, additionalInfo, options);
|
|
452
|
+
this.name = "ServiceUnavailableError";
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** Shorthand for `new HttpError(HttpStatus.GatewayTimeout, "Gateway Timeout", ...)` */
|
|
457
|
+
export class GatewayTimeoutHttpError extends HttpError {
|
|
458
|
+
constructor(
|
|
459
|
+
message: string = "Gateway Timeout",
|
|
460
|
+
additionalInfo?: Record<string, any>,
|
|
461
|
+
options?: ErrorOptions,
|
|
462
|
+
) {
|
|
463
|
+
super(HttpStatus.GatewayTimeout, message, additionalInfo, options);
|
|
464
|
+
this.name = "GatewayTimeoutError";
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/** Shorthand for `new HttpError(HttpStatus.HttpVersionNotSupported, "HTTP Version Not Supported", ...)` */
|
|
469
|
+
export class HttpVersionNotSupportedHttpError extends HttpError {
|
|
470
|
+
constructor(
|
|
471
|
+
message: string = "HTTP Version Not Supported",
|
|
472
|
+
additionalInfo?: Record<string, any>,
|
|
473
|
+
options?: ErrorOptions,
|
|
474
|
+
) {
|
|
475
|
+
super(HttpStatus.HttpVersionNotSupported, message, additionalInfo, options);
|
|
476
|
+
this.name = "HttpVersionNotSupportedError";
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** Shorthand for `new HttpError(HttpStatus.VariantAlsoNegotiates, "Variant Also Negotiates", ...)` */
|
|
481
|
+
export class VariantAlsoNegotiatesHttpError extends HttpError {
|
|
482
|
+
constructor(
|
|
483
|
+
message: string = "Variant Also Negotiates",
|
|
484
|
+
additionalInfo?: Record<string, any>,
|
|
485
|
+
options?: ErrorOptions,
|
|
486
|
+
) {
|
|
487
|
+
super(HttpStatus.VariantAlsoNegotiates, message, additionalInfo, options);
|
|
488
|
+
this.name = "VariantAlsoNegotiatesError";
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/** Shorthand for `new HttpError(HttpStatus.InsufficientStorage, "Insufficient Storage", ...)` */
|
|
493
|
+
export class InsufficientStorageHttpError extends HttpError {
|
|
494
|
+
constructor(
|
|
495
|
+
message: string = "Insufficient Storage",
|
|
496
|
+
additionalInfo?: Record<string, any>,
|
|
497
|
+
options?: ErrorOptions,
|
|
498
|
+
) {
|
|
499
|
+
super(HttpStatus.InsufficientStorage, message, additionalInfo, options);
|
|
500
|
+
this.name = "InsufficientStorageError";
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/** Shorthand for `new HttpError(HttpStatus.LoopDetected, "Loop Detected", ...)` */
|
|
505
|
+
export class LoopDetectedHttpError extends HttpError {
|
|
506
|
+
constructor(
|
|
507
|
+
message: string = "Loop Detected",
|
|
508
|
+
additionalInfo?: Record<string, any>,
|
|
509
|
+
options?: ErrorOptions,
|
|
510
|
+
) {
|
|
511
|
+
super(HttpStatus.LoopDetected, message, additionalInfo, options);
|
|
512
|
+
this.name = "LoopDetectedError";
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/** Shorthand for `new HttpError(HttpStatus.NotExtended, "Not Extended", ...)` */
|
|
517
|
+
export class NotExtendedHttpError extends HttpError {
|
|
518
|
+
constructor(
|
|
519
|
+
message: string = "Not Extended",
|
|
520
|
+
additionalInfo?: Record<string, any>,
|
|
521
|
+
options?: ErrorOptions,
|
|
522
|
+
) {
|
|
523
|
+
super(HttpStatus.NotExtended, message, additionalInfo, options);
|
|
524
|
+
this.name = "NotExtendedError";
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/** Shorthand for `new HttpError(HttpStatus.NetworkAuthenticationRequired, "Network Authentication Required", ...)` */
|
|
529
|
+
export class NetworkAuthenticationRequiredHttpError extends HttpError {
|
|
530
|
+
constructor(
|
|
531
|
+
message: string = "Network Authentication Required",
|
|
532
|
+
additionalInfo?: Record<string, any>,
|
|
533
|
+
options?: ErrorOptions,
|
|
534
|
+
) {
|
|
535
|
+
super(
|
|
536
|
+
HttpStatus.NetworkAuthenticationRequired,
|
|
537
|
+
message,
|
|
538
|
+
additionalInfo,
|
|
539
|
+
options,
|
|
540
|
+
);
|
|
541
|
+
this.name = "NetworkAuthenticationRequiredError";
|
|
542
|
+
}
|
|
543
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { MatchedRoute } from "rou3";
|
|
2
|
+
import type { RouterData } from "../types";
|
|
3
|
+
import { NotFoundHttpError } from "../errors";
|
|
4
|
+
import {
|
|
5
|
+
callCtxModifierHooks,
|
|
6
|
+
getRawParams,
|
|
7
|
+
getRawQuery,
|
|
8
|
+
isStatusResult,
|
|
9
|
+
IsStatusResult,
|
|
10
|
+
validateInputSchema,
|
|
11
|
+
validateOutputSchema,
|
|
12
|
+
} from "./utils";
|
|
13
|
+
import { smartDeserialize, smartSerialize } from "./serialization";
|
|
14
|
+
|
|
15
|
+
export async function callHandler(
|
|
16
|
+
ctx: any,
|
|
17
|
+
getRoute: (
|
|
18
|
+
method: string,
|
|
19
|
+
path: string,
|
|
20
|
+
) => MatchedRoute<RouterData> | undefined,
|
|
21
|
+
): Promise<Response> {
|
|
22
|
+
const route = getRoute(ctx.method, ctx.path);
|
|
23
|
+
if (route == null) {
|
|
24
|
+
throw new NotFoundHttpError(undefined, {
|
|
25
|
+
method: ctx.method,
|
|
26
|
+
path: ctx.path,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if ("fetch" in route.data) {
|
|
31
|
+
const res = route.data.fetch(ctx.request);
|
|
32
|
+
return res instanceof Promise ? await res : res;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const rawBody = await smartDeserialize(ctx.request);
|
|
36
|
+
const rawQuery = getRawQuery(ctx.url);
|
|
37
|
+
const rawParams = getRawParams(route);
|
|
38
|
+
ctx.route = route.data.route;
|
|
39
|
+
ctx.params = rawParams;
|
|
40
|
+
ctx.query = rawQuery;
|
|
41
|
+
ctx.body = rawBody;
|
|
42
|
+
|
|
43
|
+
if (route.data.hooks.onTransform.length > 0) {
|
|
44
|
+
const onTransformResponse = await callCtxModifierHooks(
|
|
45
|
+
ctx,
|
|
46
|
+
route.data.hooks.onTransform,
|
|
47
|
+
);
|
|
48
|
+
if (onTransformResponse) return onTransformResponse;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (route.data.def?.body)
|
|
52
|
+
ctx.body = validateInputSchema(route.data.def?.body, rawBody);
|
|
53
|
+
if (route.data.def?.query)
|
|
54
|
+
ctx.query = validateInputSchema(route.data.def?.query, rawQuery);
|
|
55
|
+
if (route.data.def?.params)
|
|
56
|
+
ctx.params = validateInputSchema(route.data.def?.params, rawParams);
|
|
57
|
+
|
|
58
|
+
if (route.data.hooks.onBeforeHandle.length > 0) {
|
|
59
|
+
const res = await callCtxModifierHooks(
|
|
60
|
+
ctx,
|
|
61
|
+
route.data.hooks.onBeforeHandle,
|
|
62
|
+
);
|
|
63
|
+
if (res) return res;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
ctx.status = (status: number, body: any) => ({
|
|
67
|
+
[IsStatusResult]: true,
|
|
68
|
+
status,
|
|
69
|
+
body,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
let response: any = route.data.handler(ctx);
|
|
73
|
+
if (response instanceof Promise) response = await response;
|
|
74
|
+
|
|
75
|
+
ctx.response = response;
|
|
76
|
+
|
|
77
|
+
for (const hook of route.data.hooks.onAfterHandle) {
|
|
78
|
+
let res = hook.callback(ctx);
|
|
79
|
+
res = res instanceof Promise ? await res : res;
|
|
80
|
+
if (res instanceof Response) return res;
|
|
81
|
+
ctx.response = res;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!(ctx.response instanceof Response)) {
|
|
85
|
+
if (route.data.def?.responses) {
|
|
86
|
+
if ("~standard" in route.data.def.responses) {
|
|
87
|
+
ctx.response = validateOutputSchema(
|
|
88
|
+
route.data.def.responses,
|
|
89
|
+
ctx.response,
|
|
90
|
+
);
|
|
91
|
+
} else {
|
|
92
|
+
if (!ctx.response || !isStatusResult(ctx.response)) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
"When `responses` is a record of schemas, you must return a value from `ctx.status()`.",
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const { status, body } = ctx.response;
|
|
98
|
+
const schema = route.data.def.responses[status];
|
|
99
|
+
if (!schema) {
|
|
100
|
+
// This should be caught by the `status` function's type definition, but it's here as a safeguard.
|
|
101
|
+
throw new Error(`No response schema found for status ${status}.`);
|
|
102
|
+
}
|
|
103
|
+
ctx.set.status = status;
|
|
104
|
+
ctx.response = validateOutputSchema(schema, body);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (response instanceof Response) return response;
|
|
110
|
+
|
|
111
|
+
for (const hook of route.data.hooks.onMapResponse) {
|
|
112
|
+
let res = hook.callback(ctx);
|
|
113
|
+
res = res instanceof Promise ? await res : res;
|
|
114
|
+
if (res instanceof Response) return res;
|
|
115
|
+
ctx.response = res;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const resBody = smartSerialize(ctx.response);
|
|
119
|
+
if (!resBody)
|
|
120
|
+
return new Response(undefined, {
|
|
121
|
+
status: ctx.set.status,
|
|
122
|
+
headers: ctx.set.headers,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return new Response(resBody.serialized, {
|
|
126
|
+
status: ctx.set.status,
|
|
127
|
+
headers: {
|
|
128
|
+
"Content-Type": resBody.contentType,
|
|
129
|
+
...ctx.set.headers,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
}
|