elysia 2.0.0-beta.10 → 2.0.0-beta.11
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/dist/adapter/web-standard/handler.js +5 -5
- package/dist/adapter/web-standard/handler.mjs +5 -5
- package/dist/compile/handler/jit.js +1 -1
- package/dist/compile/handler/jit.mjs +1 -1
- package/dist/cookie/error.d.ts +1 -2
- package/dist/cookie/error.js +6 -3
- package/dist/cookie/error.mjs +6 -3
- package/dist/error.d.ts +76 -20
- package/dist/error.js +67 -26
- package/dist/error.mjs +67 -27
- package/dist/handler/error.js +8 -6
- package/dist/handler/error.mjs +8 -6
- package/dist/handler/utils.js +3 -2
- package/dist/handler/utils.mjs +3 -2
- package/dist/package.js +1 -1
- package/dist/package.mjs +1 -1
- package/dist/types.d.ts +14 -4
- package/dist/ws/route.js +2 -6
- package/dist/ws/route.mjs +3 -7
- package/package.json +1 -4
|
@@ -46,7 +46,7 @@ function mapResponseWithSet(response, set, request, owned) {
|
|
|
46
46
|
case "File":
|
|
47
47
|
case "Blob": return require_adapter_utils.handleFile(response, set, request);
|
|
48
48
|
case "ElysiaStatus":
|
|
49
|
-
set.status = response.
|
|
49
|
+
set.status = response.status;
|
|
50
50
|
if (response.headers) Object.assign(require_adapter_utils.materializeSetHeaders(set), response.headers);
|
|
51
51
|
return mapResponseWithSet(response.response, set, request, owned);
|
|
52
52
|
case void 0: return response ? Response.json(response, set) : new Response(null, set);
|
|
@@ -62,7 +62,7 @@ function mapResponse(response, set, request, owned) {
|
|
|
62
62
|
const headers = set.headers;
|
|
63
63
|
if (set.status !== void 0 || set.cookie || headers["\0"] === headers || require_utils.isNotEmpty(headers)) return mapResponseWithSet(response, set, request, owned);
|
|
64
64
|
if (response instanceof require_error.ElysiaStatus) {
|
|
65
|
-
set.status = response.
|
|
65
|
+
set.status = response.status;
|
|
66
66
|
if (response.headers) Object.assign(set.headers, response.headers);
|
|
67
67
|
return mapResponse(response.response, set, request, owned);
|
|
68
68
|
}
|
|
@@ -84,7 +84,7 @@ function mapCompactResponse(response, request, owned) {
|
|
|
84
84
|
case "File":
|
|
85
85
|
case "Blob": return require_adapter_utils.handleFile(response, void 0, request);
|
|
86
86
|
case "ElysiaStatus": return mapResponse(response.response, {
|
|
87
|
-
status: response.
|
|
87
|
+
status: response.status,
|
|
88
88
|
headers: response.headers ? Object.assign(require_utils.nullObject(), response.headers) : require_utils.nullObject()
|
|
89
89
|
}, request, owned);
|
|
90
90
|
case void 0: return response ? Response.json(response) : new Response("");
|
|
@@ -121,14 +121,14 @@ function mapFallback(response, set, request, owned = false) {
|
|
|
121
121
|
if (response instanceof Promise) return response.then((x) => set ? mapResponse(x, set, request, owned) : mapCompactResponse(x, request, owned));
|
|
122
122
|
if (response instanceof Error) return errorToResponse(response, set, request, owned);
|
|
123
123
|
if (response instanceof require_error.ElysiaStatus) if (set) {
|
|
124
|
-
set.status = response.
|
|
124
|
+
set.status = response.status;
|
|
125
125
|
if (response.headers) set.headers = {
|
|
126
126
|
...set.headers,
|
|
127
127
|
...response.headers
|
|
128
128
|
};
|
|
129
129
|
return mapResponse(response.response, set, request, owned);
|
|
130
130
|
} else return mapResponse(response.response, {
|
|
131
|
-
status: response.
|
|
131
|
+
status: response.status,
|
|
132
132
|
headers: response.headers ? { ...response.headers } : {}
|
|
133
133
|
}, request, owned);
|
|
134
134
|
if (response instanceof require_universal_file.ElysiaFile) return handleElysiaFile(response, void 0, request);
|
|
@@ -45,7 +45,7 @@ function mapResponseWithSet(response, set, request, owned) {
|
|
|
45
45
|
case "File":
|
|
46
46
|
case "Blob": return handleFile(response, set, request);
|
|
47
47
|
case "ElysiaStatus":
|
|
48
|
-
set.status = response.
|
|
48
|
+
set.status = response.status;
|
|
49
49
|
if (response.headers) Object.assign(materializeSetHeaders(set), response.headers);
|
|
50
50
|
return mapResponseWithSet(response.response, set, request, owned);
|
|
51
51
|
case void 0: return response ? Response.json(response, set) : new Response(null, set);
|
|
@@ -61,7 +61,7 @@ function mapResponse(response, set, request, owned) {
|
|
|
61
61
|
const headers = set.headers;
|
|
62
62
|
if (set.status !== void 0 || set.cookie || headers["\0"] === headers || isNotEmpty(headers)) return mapResponseWithSet(response, set, request, owned);
|
|
63
63
|
if (response instanceof ElysiaStatus) {
|
|
64
|
-
set.status = response.
|
|
64
|
+
set.status = response.status;
|
|
65
65
|
if (response.headers) Object.assign(set.headers, response.headers);
|
|
66
66
|
return mapResponse(response.response, set, request, owned);
|
|
67
67
|
}
|
|
@@ -83,7 +83,7 @@ function mapCompactResponse(response, request, owned) {
|
|
|
83
83
|
case "File":
|
|
84
84
|
case "Blob": return handleFile(response, void 0, request);
|
|
85
85
|
case "ElysiaStatus": return mapResponse(response.response, {
|
|
86
|
-
status: response.
|
|
86
|
+
status: response.status,
|
|
87
87
|
headers: response.headers ? Object.assign(nullObject(), response.headers) : nullObject()
|
|
88
88
|
}, request, owned);
|
|
89
89
|
case void 0: return response ? Response.json(response) : new Response("");
|
|
@@ -120,14 +120,14 @@ function mapFallback(response, set, request, owned = false) {
|
|
|
120
120
|
if (response instanceof Promise) return response.then((x) => set ? mapResponse(x, set, request, owned) : mapCompactResponse(x, request, owned));
|
|
121
121
|
if (response instanceof Error) return errorToResponse(response, set, request, owned);
|
|
122
122
|
if (response instanceof ElysiaStatus) if (set) {
|
|
123
|
-
set.status = response.
|
|
123
|
+
set.status = response.status;
|
|
124
124
|
if (response.headers) set.headers = {
|
|
125
125
|
...set.headers,
|
|
126
126
|
...response.headers
|
|
127
127
|
};
|
|
128
128
|
return mapResponse(response.response, set, request, owned);
|
|
129
129
|
} else return mapResponse(response.response, {
|
|
130
|
-
status: response.
|
|
130
|
+
status: response.status,
|
|
131
131
|
headers: response.headers ? { ...response.headers } : {}
|
|
132
132
|
}, request, owned);
|
|
133
133
|
if (response instanceof ElysiaFile) return handleElysiaFile(response, void 0, request);
|
|
@@ -458,7 +458,7 @@ const _s=tee(_r,${teeCount})\nreturn _fin2(c,_s[0],_s[1])\n}\nreturn _fin2(c,_r,
|
|
|
458
458
|
link(require_error.ElysiaStatus, "es");
|
|
459
459
|
const awaitStr = responseValiAsync ? "await " : "";
|
|
460
460
|
code += `if(_r instanceof es){
|
|
461
|
-
const _vr=va.response[_r.
|
|
461
|
+
const _vr=va.response[_r.status]
|
|
462
462
|
if(_vr)_r.response=${awaitStr}${responseValiAsync ? `(_vr.mayReturnPromise?_vr.From(_r.response,'response',true):_vr.EncodeFrom(_r.response,'response'))` : `_vr.EncodeFrom(_r.response,'response')`}\n}else if(!(_r instanceof Response)&&!(_r instanceof ReadableStream)&&typeof _r?.next!=='function'){\nconst _vr=va.response[c.set.status??200]\nif(_vr)_r=${awaitStr}${responseValiAsync ? `(_vr.mayReturnPromise?_vr.From(_r,'response',true):_vr.EncodeFrom(_r,'response'))` : `_vr.EncodeFrom(_r,'response')`}\n}\n`;
|
|
463
463
|
code += abortCheck();
|
|
464
464
|
}
|
|
@@ -457,7 +457,7 @@ const _s=tee(_r,${teeCount})\nreturn _fin2(c,_s[0],_s[1])\n}\nreturn _fin2(c,_r,
|
|
|
457
457
|
link(ElysiaStatus, "es");
|
|
458
458
|
const awaitStr = responseValiAsync ? "await " : "";
|
|
459
459
|
code += `if(_r instanceof es){
|
|
460
|
-
const _vr=va.response[_r.
|
|
460
|
+
const _vr=va.response[_r.status]
|
|
461
461
|
if(_vr)_r.response=${awaitStr}${responseValiAsync ? `(_vr.mayReturnPromise?_vr.From(_r.response,'response',true):_vr.EncodeFrom(_r.response,'response'))` : `_vr.EncodeFrom(_r.response,'response')`}\n}else if(!(_r instanceof Response)&&!(_r instanceof ReadableStream)&&typeof _r?.next!=='function'){\nconst _vr=va.response[c.set.status??200]\nif(_vr)_r=${awaitStr}${responseValiAsync ? `(_vr.mayReturnPromise?_vr.From(_r,'response',true):_vr.EncodeFrom(_r,'response'))` : `_vr.EncodeFrom(_r,'response')`}\n}\n`;
|
|
462
462
|
code += abortCheck();
|
|
463
463
|
}
|
package/dist/cookie/error.d.ts
CHANGED
|
@@ -5,8 +5,7 @@ declare class InvalidCookie extends ElysiaError {
|
|
|
5
5
|
key?: string | undefined;
|
|
6
6
|
response: string;
|
|
7
7
|
status: 400;
|
|
8
|
-
|
|
9
|
-
problemTitle: string;
|
|
8
|
+
readonly code = "invalid-cookie";
|
|
10
9
|
constructor(key?: string | undefined, response?: string, status?: number);
|
|
11
10
|
static signature(key: string): InvalidCookie;
|
|
12
11
|
static secret(key?: string): Omit<InvalidCookie, 'status'> & {
|
package/dist/cookie/error.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_universal_is_production = require('../universal/is-production.js');
|
|
2
3
|
const require_error = require('../error.js');
|
|
3
4
|
|
|
4
5
|
//#region src/cookie/error.ts
|
|
@@ -8,15 +9,17 @@ var InvalidCookie = class InvalidCookie extends require_error.ElysiaError {
|
|
|
8
9
|
this.key = key;
|
|
9
10
|
this.response = response;
|
|
10
11
|
this.status = 400;
|
|
11
|
-
this.
|
|
12
|
-
this.problemTitle = "Invalid Cookie";
|
|
12
|
+
this.code = "invalid-cookie";
|
|
13
13
|
if (status) this.status = status;
|
|
14
14
|
}
|
|
15
15
|
static signature(key) {
|
|
16
16
|
return new InvalidCookie(key, `"${key}" has invalid cookie signature`);
|
|
17
17
|
}
|
|
18
18
|
static secret(key) {
|
|
19
|
-
|
|
19
|
+
const advice = `${key ? `Cookie "${key}"` : "A cookie"} is signed but \`cookie.secrets\` is missing or empty`;
|
|
20
|
+
const error = new InvalidCookie(key, require_universal_is_production.isProduction() ? "Internal Server Error" : advice, 500);
|
|
21
|
+
error.message = advice;
|
|
22
|
+
return error;
|
|
20
23
|
}
|
|
21
24
|
};
|
|
22
25
|
|
package/dist/cookie/error.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isProduction } from "../universal/is-production.mjs";
|
|
1
2
|
import { ElysiaError } from "../error.mjs";
|
|
2
3
|
|
|
3
4
|
//#region src/cookie/error.ts
|
|
@@ -7,15 +8,17 @@ var InvalidCookie = class InvalidCookie extends ElysiaError {
|
|
|
7
8
|
this.key = key;
|
|
8
9
|
this.response = response;
|
|
9
10
|
this.status = 400;
|
|
10
|
-
this.
|
|
11
|
-
this.problemTitle = "Invalid Cookie";
|
|
11
|
+
this.code = "invalid-cookie";
|
|
12
12
|
if (status) this.status = status;
|
|
13
13
|
}
|
|
14
14
|
static signature(key) {
|
|
15
15
|
return new InvalidCookie(key, `"${key}" has invalid cookie signature`);
|
|
16
16
|
}
|
|
17
17
|
static secret(key) {
|
|
18
|
-
|
|
18
|
+
const advice = `${key ? `Cookie "${key}"` : "A cookie"} is signed but \`cookie.secrets\` is missing or empty`;
|
|
19
|
+
const error = new InvalidCookie(key, isProduction() ? "Internal Server Error" : advice, 500);
|
|
20
|
+
error.message = advice;
|
|
21
|
+
return error;
|
|
19
22
|
}
|
|
20
23
|
};
|
|
21
24
|
|
package/dist/error.d.ts
CHANGED
|
@@ -6,13 +6,26 @@ import { MaybePromise } from "./types.js";
|
|
|
6
6
|
declare class ElysiaError<Status extends number = number, Response extends string = string> extends Error {
|
|
7
7
|
status?: Status;
|
|
8
8
|
response?: Response;
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Stable machine-readable token served in the `code` member, the same
|
|
11
|
+
* contract a {@link HTTPError.id} class speaks (set per subclass)
|
|
12
|
+
*/
|
|
13
|
+
readonly code?: string;
|
|
14
|
+
/**
|
|
15
|
+
* RFC 9457 problem type served in the `type` member. Mirrors `code`, or
|
|
16
|
+
* resolves to `<typeBase>/<code>` once {@link HTTPError.typeBase} is set.
|
|
17
|
+
* Defined on the prototype below
|
|
18
|
+
*/
|
|
19
|
+
type?: string;
|
|
13
20
|
constructor(message: string, cause?: Error);
|
|
14
21
|
toResponse(headers?: Record<string, any>): globalThis.Response;
|
|
15
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Problem members an `ElysiaError` serves. `title` is left to `problemBody`,
|
|
25
|
+
* which derives it from the status — so `response` is only worth serving as
|
|
26
|
+
* `detail` when it says something the title doesn't
|
|
27
|
+
*/
|
|
28
|
+
declare function elysiaErrorProblem(error: ElysiaError): Problem;
|
|
16
29
|
/**
|
|
17
30
|
* Wrap a string into a TypeBox `error` callback that overrides the default
|
|
18
31
|
* validation message. Use as `t.Number({ error: validationDetail('x must be a number') })`.
|
|
@@ -21,22 +34,19 @@ declare const validationDetail: <T>(message: T) => (error: any) => any;
|
|
|
21
34
|
declare class InternalServerError extends ElysiaError {
|
|
22
35
|
response: string;
|
|
23
36
|
status: 500;
|
|
24
|
-
|
|
25
|
-
problemTitle: string;
|
|
37
|
+
readonly code = "internal-server-error";
|
|
26
38
|
constructor(response?: string, cause?: Error);
|
|
27
39
|
}
|
|
28
40
|
declare class NotFound extends ElysiaError {
|
|
29
41
|
response: string;
|
|
30
42
|
status: 404;
|
|
31
|
-
|
|
32
|
-
problemTitle: string;
|
|
43
|
+
readonly code = "not-found";
|
|
33
44
|
constructor(response?: string);
|
|
34
45
|
}
|
|
35
46
|
declare class ParseError extends ElysiaError {
|
|
36
47
|
status: 400;
|
|
37
48
|
response: string;
|
|
38
|
-
|
|
39
|
-
problemTitle: string;
|
|
49
|
+
readonly code = "parse";
|
|
40
50
|
constructor(cause?: Error);
|
|
41
51
|
toResponse(headers?: Record<string, any>): Response;
|
|
42
52
|
}
|
|
@@ -109,11 +119,18 @@ declare class ValidationError extends ElysiaError {
|
|
|
109
119
|
toResponse(headers?: Record<string, any>): Response;
|
|
110
120
|
}
|
|
111
121
|
declare class ElysiaStatus<const in out Code extends number | keyof StatusMap, T = (Code extends keyof StatusMapBack ? StatusMapBack[Code] : Code), const in out Status extends (Code extends keyof StatusMap ? StatusMap[Code] : Code) = (Code extends keyof StatusMap ? StatusMap[Code] : Code)> {
|
|
112
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Type-only brand, erased at runtime.
|
|
124
|
+
*
|
|
125
|
+
* `status` + `response` is a shape a handler may write by hand, so without
|
|
126
|
+
* a nominal marker a plain `{ status, response }` literal would be
|
|
127
|
+
* absorbed as a status box by response inference
|
|
128
|
+
*/
|
|
129
|
+
readonly ['~status']: true;
|
|
130
|
+
status: Status;
|
|
113
131
|
response: T;
|
|
114
132
|
headers?: Record<string, string>;
|
|
115
133
|
constructor(code: Code, res: T, headers?: Record<string, string>);
|
|
116
|
-
get status(): number;
|
|
117
134
|
}
|
|
118
135
|
declare const status: <const Code extends number | keyof StatusMap, const T = (Code extends keyof StatusMapBack ? StatusMapBack[Code] : Code)>(code: Code, response?: T) => ElysiaStatus<Code, T, Code extends "Continue" | "Switching Protocols" | "Processing" | "Early Hints" | "OK" | "Created" | "Accepted" | "Non-Authoritative Information" | "No Content" | "Reset Content" | "Partial Content" | "Multi-Status" | "Already Reported" | "Multiple Choices" | "Moved Permanently" | "Found" | "See Other" | "Not Modified" | "Temporary Redirect" | "Permanent Redirect" | "Bad Request" | "Unauthorized" | "Payment Required" | "Forbidden" | "Not Found" | "Method Not Allowed" | "Not Acceptable" | "Proxy Authentication Required" | "Request Timeout" | "Conflict" | "Gone" | "Length Required" | "Precondition Failed" | "Payload Too Large" | "URI Too Long" | "Unsupported Media Type" | "Range Not Satisfiable" | "Expectation Failed" | "I'm a teapot" | "Enhance Your Calm" | "Misdirected Request" | "Unprocessable Content" | "Locked" | "Failed Dependency" | "Too Early" | "Upgrade Required" | "Precondition Required" | "Too Many Requests" | "Request Header Fields Too Large" | "Unavailable For Legal Reasons" | "Internal Server Error" | "Not Implemented" | "Bad Gateway" | "Service Unavailable" | "Gateway Timeout" | "HTTP Version Not Supported" | "Variant Also Negotiates" | "Insufficient Storage" | "Loop Detected" | "Not Extended" | "Network Authentication Required" ? {
|
|
119
136
|
readonly Continue: 100;
|
|
@@ -185,7 +202,14 @@ declare const PROBLEM_JSON = "application/problem+json";
|
|
|
185
202
|
* @see https://www.rfc-editor.org/info/rfc9457/
|
|
186
203
|
*/
|
|
187
204
|
type Problem<Code extends number | keyof StatusMap = number | keyof StatusMap, Extension extends Record<string, unknown> = {}> = {
|
|
188
|
-
/** URI (or slug) identifying the problem type. @default 'about:blank' */type?: string;
|
|
205
|
+
/** URI (or slug) identifying the problem type. @default 'about:blank' */type?: string;
|
|
206
|
+
/**
|
|
207
|
+
* Extension member carrying a stable machine-readable token.
|
|
208
|
+
*
|
|
209
|
+
* `type` may be widened to a URI by {@link HTTPError.typeBase}, `code`
|
|
210
|
+
* never is
|
|
211
|
+
*/
|
|
212
|
+
code?: string; /** Short, human-readable summary of the problem type */
|
|
189
213
|
title?: string; /** HTTP status. A number or a `StatusMap` name (e.g. `'Conflict'`) */
|
|
190
214
|
status?: Code; /** Human-readable explanation specific to this occurrence */
|
|
191
215
|
detail?: string; /** URI identifying the specific occurrence of the problem */
|
|
@@ -242,6 +266,7 @@ type SelectiveStatus<in out Res> = <const Code extends keyof Res | StatusMapBack
|
|
|
242
266
|
type TaggedHTTPError<Type extends string, Annotation = {}> = {
|
|
243
267
|
new (message?: string, options?: ErrorOptions): HTTPError<Type> & {
|
|
244
268
|
type: Type;
|
|
269
|
+
readonly code: Type;
|
|
245
270
|
} & Annotation;
|
|
246
271
|
prototype: HTTPError<Type>;
|
|
247
272
|
};
|
|
@@ -306,8 +331,38 @@ declare abstract class HTTPError<const in out T extends string = string> extends
|
|
|
306
331
|
/**
|
|
307
332
|
* RFC 9457 problem type served in the `type` member, a URI or a slug.
|
|
308
333
|
* Doubles as the tag that discriminates one error class from another
|
|
334
|
+
*
|
|
335
|
+
* On a {@link HTTPError.id} class this mirrors `code`, or resolves to
|
|
336
|
+
* `<typeBase>/<code>` once {@link HTTPError.typeBase} is set
|
|
309
337
|
*/
|
|
310
338
|
abstract type: T;
|
|
339
|
+
/**
|
|
340
|
+
* Stable machine-readable token served in the `code` member, set by
|
|
341
|
+
* {@link HTTPError.id} to its first argument
|
|
342
|
+
*
|
|
343
|
+
* Unlike `type` it is never widened to a URI, so it stays the value to
|
|
344
|
+
* dispatch on
|
|
345
|
+
*/
|
|
346
|
+
readonly code?: string;
|
|
347
|
+
/**
|
|
348
|
+
* Absolute base for the RFC 9457 `type` URI, applied to every class made
|
|
349
|
+
* by {@link HTTPError.id} and to every built-in `ElysiaError`
|
|
350
|
+
* (`NotFound`, `ParseError`, …) — both derive `type` from the same slug
|
|
351
|
+
*
|
|
352
|
+
* Unset, `type` mirrors `code` verbatim. Set, `type` resolves to
|
|
353
|
+
* `<typeBase>/<code>` with the code carried through as written — no
|
|
354
|
+
* casing or separator rewrite — while `code` keeps serving the bare token
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```ts
|
|
358
|
+
* HTTPError.typeBase = 'https://example.com/errors'
|
|
359
|
+
*
|
|
360
|
+
* class OutOfCredit extends HTTPError.id('OUT_OF_CREDIT', 402) {}
|
|
361
|
+
* // → { type: 'https://example.com/errors/OUT_OF_CREDIT',
|
|
362
|
+
* // code: 'OUT_OF_CREDIT', … }
|
|
363
|
+
* ```
|
|
364
|
+
*/
|
|
365
|
+
static typeBase?: string;
|
|
311
366
|
/**
|
|
312
367
|
* HTTP status served when the error reaches the error pipeline,
|
|
313
368
|
* as a number or a status name (`'Payment Required'`)
|
|
@@ -329,9 +384,10 @@ declare abstract class HTTPError<const in out T extends string = string> extends
|
|
|
329
384
|
*/
|
|
330
385
|
detail?(): MaybePromise<unknown>;
|
|
331
386
|
/**
|
|
332
|
-
* Create a concrete `HTTPError` subclass carrying `
|
|
387
|
+
* Create a concrete `HTTPError` subclass carrying `code`, mirrored into
|
|
388
|
+
* `type` unless {@link HTTPError.typeBase} is set
|
|
333
389
|
*
|
|
334
|
-
* `
|
|
390
|
+
* `code` carries no status annotation, annotate `status` in the subclass
|
|
335
391
|
* body as a number or a status name
|
|
336
392
|
*
|
|
337
393
|
* @example
|
|
@@ -343,9 +399,9 @@ declare abstract class HTTPError<const in out T extends string = string> extends
|
|
|
343
399
|
* }
|
|
344
400
|
* ```
|
|
345
401
|
*/
|
|
346
|
-
static id<const Type extends string>(
|
|
402
|
+
static id<const Type extends string>(code: Type): TaggedHTTPError<Type>;
|
|
347
403
|
/**
|
|
348
|
-
* Create a concrete `HTTPError` subclass carrying `
|
|
404
|
+
* Create a concrete `HTTPError` subclass carrying `code`, annotated with
|
|
349
405
|
* `status` as a number or a status name
|
|
350
406
|
*
|
|
351
407
|
* Annotate what is served with `detail()` (problem `detail` member) or
|
|
@@ -354,16 +410,16 @@ declare abstract class HTTPError<const in out T extends string = string> extends
|
|
|
354
410
|
* @example
|
|
355
411
|
* ```ts
|
|
356
412
|
* class OutOfCredit extends HTTPError.id('OUT_OF_CREDIT', 402) {}
|
|
357
|
-
* class Denied extends HTTPError.id('
|
|
413
|
+
* class Denied extends HTTPError.id('DENIED', 'Payment Required') {
|
|
358
414
|
* async detail() {
|
|
359
415
|
* return await describeBalance()
|
|
360
416
|
* }
|
|
361
417
|
* }
|
|
362
418
|
* ```
|
|
363
419
|
*/
|
|
364
|
-
static id<const Type extends string, const S extends number | keyof StatusMap>(
|
|
420
|
+
static id<const Type extends string, const S extends number | keyof StatusMap>(code: Type, status: S): TaggedHTTPError<Type, {
|
|
365
421
|
readonly status: S extends keyof StatusMap ? StatusMap[S] : S;
|
|
366
422
|
}>;
|
|
367
423
|
}
|
|
368
424
|
//#endregion
|
|
369
|
-
export { ElysiaError, ElysiaStatus, HTTPError, InternalServerError, MAX_ERRORS, NotFound, PROBLEM_JSON, ParseError, Problem, ProblemResponseBody, SelectiveStatus, TaggedHTTPError, ValidationError, internalServerErrorBody, internalServerErrorBodyString, internalServerErrorResponse, isProduction, problem, problemBody, problemResponse, status, subValueAt, validationDetail };
|
|
425
|
+
export { ElysiaError, ElysiaStatus, HTTPError, InternalServerError, MAX_ERRORS, NotFound, PROBLEM_JSON, ParseError, Problem, ProblemResponseBody, SelectiveStatus, TaggedHTTPError, ValidationError, elysiaErrorProblem, internalServerErrorBody, internalServerErrorBodyString, internalServerErrorResponse, isProduction, problem, problemBody, problemResponse, status, subValueAt, validationDetail };
|
package/dist/error.js
CHANGED
|
@@ -29,20 +29,63 @@ function isCacheableExpected(value, visited = /* @__PURE__ */ new Set()) {
|
|
|
29
29
|
var ElysiaError = class extends Error {
|
|
30
30
|
constructor(message, cause) {
|
|
31
31
|
super(message);
|
|
32
|
-
this.problemType = "about:blank";
|
|
33
32
|
this.name = this.constructor.name;
|
|
34
33
|
if (cause) this.cause = cause;
|
|
35
34
|
}
|
|
36
35
|
toResponse(headers) {
|
|
37
|
-
return problemResponse(
|
|
38
|
-
type: this.problemType,
|
|
39
|
-
title: this.problemTitle,
|
|
40
|
-
status: this.status ?? 500,
|
|
41
|
-
detail: this.response !== void 0 && this.response !== this.problemTitle ? this.response : void 0
|
|
42
|
-
}, headers);
|
|
36
|
+
return problemResponse(elysiaErrorProblem(this), headers);
|
|
43
37
|
}
|
|
44
38
|
};
|
|
45
39
|
/**
|
|
40
|
+
* `type` an error slug resolves to: the slug verbatim, or `<typeBase>/<slug>`
|
|
41
|
+
* once {@link HTTPError.typeBase} is set. Shared by `ElysiaError` and every
|
|
42
|
+
* class {@link HTTPError.id} makes, so both lanes derive `type` identically
|
|
43
|
+
*/
|
|
44
|
+
function problemTypeOf(code) {
|
|
45
|
+
if (code === void 0) return;
|
|
46
|
+
const base = HTTPError.typeBase;
|
|
47
|
+
if (!base) return code;
|
|
48
|
+
return (base.endsWith("/") ? base : base + "/") + code;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Problem members an `ElysiaError` serves. `title` is left to `problemBody`,
|
|
52
|
+
* which derives it from the status — so `response` is only worth serving as
|
|
53
|
+
* `detail` when it says something the title doesn't
|
|
54
|
+
*/
|
|
55
|
+
function elysiaErrorProblem(error) {
|
|
56
|
+
const status = error.status ?? 500;
|
|
57
|
+
return {
|
|
58
|
+
type: error.type ?? "about:blank",
|
|
59
|
+
code: error.code,
|
|
60
|
+
status,
|
|
61
|
+
detail: error.response !== void 0 && error.response !== require_constants.StatusMapBack[status] ? error.response : void 0
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Install the `type` mirror on an error prototype. Reads `code` off the
|
|
66
|
+
* instance so a subclass that renames its own `code` retags with it, and keeps
|
|
67
|
+
* `type` assignable — a value written onto an instance lands as an own
|
|
68
|
+
* property instead of throwing against a getter-only slot
|
|
69
|
+
*/
|
|
70
|
+
function defineProblemType(prototype) {
|
|
71
|
+
Object.defineProperty(prototype, "type", {
|
|
72
|
+
get() {
|
|
73
|
+
return problemTypeOf(this.code);
|
|
74
|
+
},
|
|
75
|
+
set(value) {
|
|
76
|
+
Object.defineProperty(this, "type", {
|
|
77
|
+
value,
|
|
78
|
+
writable: true,
|
|
79
|
+
enumerable: true,
|
|
80
|
+
configurable: true
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
enumerable: true,
|
|
84
|
+
configurable: true
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
defineProblemType(ElysiaError.prototype);
|
|
88
|
+
/**
|
|
46
89
|
* Wrap a string into a TypeBox `error` callback that overrides the default
|
|
47
90
|
* validation message. Use as `t.Number({ error: validationDetail('x must be a number') })`.
|
|
48
91
|
*/
|
|
@@ -55,8 +98,7 @@ var InternalServerError = class extends ElysiaError {
|
|
|
55
98
|
super(response, cause);
|
|
56
99
|
this.response = response;
|
|
57
100
|
this.status = 500;
|
|
58
|
-
this.
|
|
59
|
-
this.problemTitle = "Internal Server Error";
|
|
101
|
+
this.code = "internal-server-error";
|
|
60
102
|
}
|
|
61
103
|
};
|
|
62
104
|
var NotFound = class extends ElysiaError {
|
|
@@ -64,8 +106,7 @@ var NotFound = class extends ElysiaError {
|
|
|
64
106
|
super(response);
|
|
65
107
|
this.response = response;
|
|
66
108
|
this.status = 404;
|
|
67
|
-
this.
|
|
68
|
-
this.problemTitle = "Not Found";
|
|
109
|
+
this.code = "not-found";
|
|
69
110
|
}
|
|
70
111
|
};
|
|
71
112
|
var ParseError = class extends ElysiaError {
|
|
@@ -73,15 +114,12 @@ var ParseError = class extends ElysiaError {
|
|
|
73
114
|
super("Bad Request", cause);
|
|
74
115
|
this.status = 400;
|
|
75
116
|
this.response = "Bad Request";
|
|
76
|
-
this.
|
|
77
|
-
this.problemTitle = "Bad Request";
|
|
117
|
+
this.code = "parse";
|
|
78
118
|
}
|
|
79
119
|
toResponse(headers) {
|
|
80
120
|
const cause = this.cause;
|
|
81
121
|
return problemResponse({
|
|
82
|
-
|
|
83
|
-
title: this.problemTitle,
|
|
84
|
-
status: 400,
|
|
122
|
+
...elysiaErrorProblem(this),
|
|
85
123
|
detail: !require_universal_is_production.isProduction() && cause?.message != null ? String(cause.message) : void 0
|
|
86
124
|
}, headers);
|
|
87
125
|
}
|
|
@@ -487,13 +525,10 @@ const emptyHttpStatus = new Set([
|
|
|
487
525
|
var ElysiaStatus = class {
|
|
488
526
|
constructor(code, res, headers) {
|
|
489
527
|
const response = res ?? (code in require_constants.StatusMapBack ? require_constants.StatusMapBack[code] : code);
|
|
490
|
-
this.
|
|
491
|
-
if (!emptyHttpStatus.has(this.
|
|
528
|
+
this.status = require_constants.StatusMap[code] ?? code;
|
|
529
|
+
if (!emptyHttpStatus.has(this.status)) this.response = response;
|
|
492
530
|
this.headers = headers;
|
|
493
531
|
}
|
|
494
|
-
get status() {
|
|
495
|
-
return this.code;
|
|
496
|
-
}
|
|
497
532
|
};
|
|
498
533
|
Object.defineProperty(ElysiaStatus, "name", { value: "ElysiaStatus" });
|
|
499
534
|
const status = (code, response) => new ElysiaStatus(code, response);
|
|
@@ -624,12 +659,17 @@ function problem(statusOrDetail, detail) {
|
|
|
624
659
|
* @since 2.0.0
|
|
625
660
|
*/
|
|
626
661
|
var HTTPError = class HTTPError extends Error {
|
|
627
|
-
static id(
|
|
628
|
-
const Tagged = class extends HTTPError {
|
|
629
|
-
|
|
630
|
-
|
|
662
|
+
static id(code, status) {
|
|
663
|
+
const Tagged = class extends HTTPError {
|
|
664
|
+
constructor(..._args) {
|
|
665
|
+
super(..._args);
|
|
666
|
+
this.code = code;
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
defineProblemType(Tagged.prototype);
|
|
670
|
+
Tagged.prototype.name = code;
|
|
631
671
|
Object.defineProperty(Tagged, "name", {
|
|
632
|
-
value:
|
|
672
|
+
value: code,
|
|
633
673
|
configurable: true
|
|
634
674
|
});
|
|
635
675
|
const resolved = typeof status === "string" ? require_constants.StatusMap[status] : status;
|
|
@@ -648,6 +688,7 @@ exports.NotFound = NotFound;
|
|
|
648
688
|
exports.PROBLEM_JSON = PROBLEM_JSON;
|
|
649
689
|
exports.ParseError = ParseError;
|
|
650
690
|
exports.ValidationError = ValidationError;
|
|
691
|
+
exports.elysiaErrorProblem = elysiaErrorProblem;
|
|
651
692
|
exports.internalServerErrorBody = internalServerErrorBody;
|
|
652
693
|
exports.internalServerErrorBodyString = internalServerErrorBodyString;
|
|
653
694
|
exports.internalServerErrorResponse = internalServerErrorResponse;
|
package/dist/error.mjs
CHANGED
|
@@ -28,20 +28,63 @@ function isCacheableExpected(value, visited = /* @__PURE__ */ new Set()) {
|
|
|
28
28
|
var ElysiaError = class extends Error {
|
|
29
29
|
constructor(message, cause) {
|
|
30
30
|
super(message);
|
|
31
|
-
this.problemType = "about:blank";
|
|
32
31
|
this.name = this.constructor.name;
|
|
33
32
|
if (cause) this.cause = cause;
|
|
34
33
|
}
|
|
35
34
|
toResponse(headers) {
|
|
36
|
-
return problemResponse(
|
|
37
|
-
type: this.problemType,
|
|
38
|
-
title: this.problemTitle,
|
|
39
|
-
status: this.status ?? 500,
|
|
40
|
-
detail: this.response !== void 0 && this.response !== this.problemTitle ? this.response : void 0
|
|
41
|
-
}, headers);
|
|
35
|
+
return problemResponse(elysiaErrorProblem(this), headers);
|
|
42
36
|
}
|
|
43
37
|
};
|
|
44
38
|
/**
|
|
39
|
+
* `type` an error slug resolves to: the slug verbatim, or `<typeBase>/<slug>`
|
|
40
|
+
* once {@link HTTPError.typeBase} is set. Shared by `ElysiaError` and every
|
|
41
|
+
* class {@link HTTPError.id} makes, so both lanes derive `type` identically
|
|
42
|
+
*/
|
|
43
|
+
function problemTypeOf(code) {
|
|
44
|
+
if (code === void 0) return;
|
|
45
|
+
const base = HTTPError.typeBase;
|
|
46
|
+
if (!base) return code;
|
|
47
|
+
return (base.endsWith("/") ? base : base + "/") + code;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Problem members an `ElysiaError` serves. `title` is left to `problemBody`,
|
|
51
|
+
* which derives it from the status — so `response` is only worth serving as
|
|
52
|
+
* `detail` when it says something the title doesn't
|
|
53
|
+
*/
|
|
54
|
+
function elysiaErrorProblem(error) {
|
|
55
|
+
const status = error.status ?? 500;
|
|
56
|
+
return {
|
|
57
|
+
type: error.type ?? "about:blank",
|
|
58
|
+
code: error.code,
|
|
59
|
+
status,
|
|
60
|
+
detail: error.response !== void 0 && error.response !== StatusMapBack[status] ? error.response : void 0
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Install the `type` mirror on an error prototype. Reads `code` off the
|
|
65
|
+
* instance so a subclass that renames its own `code` retags with it, and keeps
|
|
66
|
+
* `type` assignable — a value written onto an instance lands as an own
|
|
67
|
+
* property instead of throwing against a getter-only slot
|
|
68
|
+
*/
|
|
69
|
+
function defineProblemType(prototype) {
|
|
70
|
+
Object.defineProperty(prototype, "type", {
|
|
71
|
+
get() {
|
|
72
|
+
return problemTypeOf(this.code);
|
|
73
|
+
},
|
|
74
|
+
set(value) {
|
|
75
|
+
Object.defineProperty(this, "type", {
|
|
76
|
+
value,
|
|
77
|
+
writable: true,
|
|
78
|
+
enumerable: true,
|
|
79
|
+
configurable: true
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
enumerable: true,
|
|
83
|
+
configurable: true
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
defineProblemType(ElysiaError.prototype);
|
|
87
|
+
/**
|
|
45
88
|
* Wrap a string into a TypeBox `error` callback that overrides the default
|
|
46
89
|
* validation message. Use as `t.Number({ error: validationDetail('x must be a number') })`.
|
|
47
90
|
*/
|
|
@@ -54,8 +97,7 @@ var InternalServerError = class extends ElysiaError {
|
|
|
54
97
|
super(response, cause);
|
|
55
98
|
this.response = response;
|
|
56
99
|
this.status = 500;
|
|
57
|
-
this.
|
|
58
|
-
this.problemTitle = "Internal Server Error";
|
|
100
|
+
this.code = "internal-server-error";
|
|
59
101
|
}
|
|
60
102
|
};
|
|
61
103
|
var NotFound = class extends ElysiaError {
|
|
@@ -63,8 +105,7 @@ var NotFound = class extends ElysiaError {
|
|
|
63
105
|
super(response);
|
|
64
106
|
this.response = response;
|
|
65
107
|
this.status = 404;
|
|
66
|
-
this.
|
|
67
|
-
this.problemTitle = "Not Found";
|
|
108
|
+
this.code = "not-found";
|
|
68
109
|
}
|
|
69
110
|
};
|
|
70
111
|
var ParseError = class extends ElysiaError {
|
|
@@ -72,15 +113,12 @@ var ParseError = class extends ElysiaError {
|
|
|
72
113
|
super("Bad Request", cause);
|
|
73
114
|
this.status = 400;
|
|
74
115
|
this.response = "Bad Request";
|
|
75
|
-
this.
|
|
76
|
-
this.problemTitle = "Bad Request";
|
|
116
|
+
this.code = "parse";
|
|
77
117
|
}
|
|
78
118
|
toResponse(headers) {
|
|
79
119
|
const cause = this.cause;
|
|
80
120
|
return problemResponse({
|
|
81
|
-
|
|
82
|
-
title: this.problemTitle,
|
|
83
|
-
status: 400,
|
|
121
|
+
...elysiaErrorProblem(this),
|
|
84
122
|
detail: !isProduction() && cause?.message != null ? String(cause.message) : void 0
|
|
85
123
|
}, headers);
|
|
86
124
|
}
|
|
@@ -486,13 +524,10 @@ const emptyHttpStatus = new Set([
|
|
|
486
524
|
var ElysiaStatus = class {
|
|
487
525
|
constructor(code, res, headers) {
|
|
488
526
|
const response = res ?? (code in StatusMapBack ? StatusMapBack[code] : code);
|
|
489
|
-
this.
|
|
490
|
-
if (!emptyHttpStatus.has(this.
|
|
527
|
+
this.status = StatusMap[code] ?? code;
|
|
528
|
+
if (!emptyHttpStatus.has(this.status)) this.response = response;
|
|
491
529
|
this.headers = headers;
|
|
492
530
|
}
|
|
493
|
-
get status() {
|
|
494
|
-
return this.code;
|
|
495
|
-
}
|
|
496
531
|
};
|
|
497
532
|
Object.defineProperty(ElysiaStatus, "name", { value: "ElysiaStatus" });
|
|
498
533
|
const status = (code, response) => new ElysiaStatus(code, response);
|
|
@@ -623,12 +658,17 @@ function problem(statusOrDetail, detail) {
|
|
|
623
658
|
* @since 2.0.0
|
|
624
659
|
*/
|
|
625
660
|
var HTTPError = class HTTPError extends Error {
|
|
626
|
-
static id(
|
|
627
|
-
const Tagged = class extends HTTPError {
|
|
628
|
-
|
|
629
|
-
|
|
661
|
+
static id(code, status) {
|
|
662
|
+
const Tagged = class extends HTTPError {
|
|
663
|
+
constructor(..._args) {
|
|
664
|
+
super(..._args);
|
|
665
|
+
this.code = code;
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
defineProblemType(Tagged.prototype);
|
|
669
|
+
Tagged.prototype.name = code;
|
|
630
670
|
Object.defineProperty(Tagged, "name", {
|
|
631
|
-
value:
|
|
671
|
+
value: code,
|
|
632
672
|
configurable: true
|
|
633
673
|
});
|
|
634
674
|
const resolved = typeof status === "string" ? StatusMap[status] : status;
|
|
@@ -638,4 +678,4 @@ var HTTPError = class HTTPError extends Error {
|
|
|
638
678
|
};
|
|
639
679
|
|
|
640
680
|
//#endregion
|
|
641
|
-
export { ElysiaError, ElysiaStatus, HTTPError, InternalServerError, MAX_ERRORS, NotFound, PROBLEM_JSON, ParseError, ValidationError, internalServerErrorBody, internalServerErrorBodyString, internalServerErrorResponse, isProduction, problem, problemBody, problemResponse, status, subValueAt, validationDetail };
|
|
681
|
+
export { ElysiaError, ElysiaStatus, HTTPError, InternalServerError, MAX_ERRORS, NotFound, PROBLEM_JSON, ParseError, ValidationError, elysiaErrorProblem, internalServerErrorBody, internalServerErrorBodyString, internalServerErrorResponse, isProduction, problem, problemBody, problemResponse, status, subValueAt, validationDetail };
|
package/dist/handler/error.js
CHANGED
|
@@ -39,9 +39,10 @@ const claimsProblemType = (error) => typeof error?.type === "string" ? !(error i
|
|
|
39
39
|
function adoptErrorType(result, error) {
|
|
40
40
|
const body = result?.response;
|
|
41
41
|
if (typeof error?.type !== "string" || !claimsProblemType(error) || body?.type !== "about:blank" || result.headers?.["content-type"] !== "application/problem+json") return result;
|
|
42
|
-
return new require_error.ElysiaStatus(result.
|
|
42
|
+
return new require_error.ElysiaStatus(result.status, {
|
|
43
43
|
...body,
|
|
44
|
-
type: error.type
|
|
44
|
+
type: error.type,
|
|
45
|
+
...typeof error.code === "string" ? { code: error.code } : {}
|
|
45
46
|
}, result.headers);
|
|
46
47
|
}
|
|
47
48
|
/**
|
|
@@ -76,8 +77,9 @@ function statusFallbackBody(error, status) {
|
|
|
76
77
|
/**
|
|
77
78
|
* RFC 9457 problem document carrying `detail` verbatim, mirroring `problem()`.
|
|
78
79
|
*/
|
|
79
|
-
const problemOf = (self, detail, status) => new require_error.ElysiaStatus(status, require_error.problemBody({
|
|
80
|
+
const problemOf = (self, detail, status, claimsProblem) => new require_error.ElysiaStatus(status, require_error.problemBody({
|
|
80
81
|
type: self.type ?? "about:blank",
|
|
82
|
+
...claimsProblem && typeof self.code === "string" ? { code: self.code } : {},
|
|
81
83
|
detail,
|
|
82
84
|
status
|
|
83
85
|
}), { "content-type": require_error.PROBLEM_JSON });
|
|
@@ -119,7 +121,7 @@ function fallbackErrorResponse(context, error, mapResponse, defaultError) {
|
|
|
119
121
|
const serveMessage = () => {
|
|
120
122
|
if (!claimsProblem) return legacy();
|
|
121
123
|
mergeHeaders();
|
|
122
|
-
return mapResponse(problemOf(self, statusFallbackBody(error, served), served), context.set, context);
|
|
124
|
+
return mapResponse(problemOf(self, statusFallbackBody(error, served), served, claimsProblem), context.set, context);
|
|
123
125
|
};
|
|
124
126
|
const serveDetail = () => {
|
|
125
127
|
let detail;
|
|
@@ -132,10 +134,10 @@ function fallbackErrorResponse(context, error, mapResponse, defaultError) {
|
|
|
132
134
|
if (detail instanceof Promise) return detail.then((resolved) => {
|
|
133
135
|
if (resolved === void 0) return serveMessage();
|
|
134
136
|
mergeHeaders();
|
|
135
|
-
return mapResponse(problemOf(self, resolved, served), context.set, context);
|
|
137
|
+
return mapResponse(problemOf(self, resolved, served, claimsProblem), context.set, context);
|
|
136
138
|
}, failed);
|
|
137
139
|
mergeHeaders();
|
|
138
|
-
return mapResponse(problemOf(self, detail, served), context.set, context);
|
|
140
|
+
return mapResponse(problemOf(self, detail, served, claimsProblem), context.set, context);
|
|
139
141
|
};
|
|
140
142
|
if (error instanceof require_error.ValidationError) return mapResponse(new require_error.ElysiaStatus(422, require_error.problemBody({
|
|
141
143
|
type: "validation",
|
package/dist/handler/error.mjs
CHANGED
|
@@ -38,9 +38,10 @@ const claimsProblemType = (error) => typeof error?.type === "string" ? !(error i
|
|
|
38
38
|
function adoptErrorType(result, error) {
|
|
39
39
|
const body = result?.response;
|
|
40
40
|
if (typeof error?.type !== "string" || !claimsProblemType(error) || body?.type !== "about:blank" || result.headers?.["content-type"] !== "application/problem+json") return result;
|
|
41
|
-
return new ElysiaStatus(result.
|
|
41
|
+
return new ElysiaStatus(result.status, {
|
|
42
42
|
...body,
|
|
43
|
-
type: error.type
|
|
43
|
+
type: error.type,
|
|
44
|
+
...typeof error.code === "string" ? { code: error.code } : {}
|
|
44
45
|
}, result.headers);
|
|
45
46
|
}
|
|
46
47
|
/**
|
|
@@ -75,8 +76,9 @@ function statusFallbackBody(error, status) {
|
|
|
75
76
|
/**
|
|
76
77
|
* RFC 9457 problem document carrying `detail` verbatim, mirroring `problem()`.
|
|
77
78
|
*/
|
|
78
|
-
const problemOf = (self, detail, status) => new ElysiaStatus(status, problemBody({
|
|
79
|
+
const problemOf = (self, detail, status, claimsProblem) => new ElysiaStatus(status, problemBody({
|
|
79
80
|
type: self.type ?? "about:blank",
|
|
81
|
+
...claimsProblem && typeof self.code === "string" ? { code: self.code } : {},
|
|
80
82
|
detail,
|
|
81
83
|
status
|
|
82
84
|
}), { "content-type": PROBLEM_JSON });
|
|
@@ -118,7 +120,7 @@ function fallbackErrorResponse(context, error, mapResponse, defaultError) {
|
|
|
118
120
|
const serveMessage = () => {
|
|
119
121
|
if (!claimsProblem) return legacy();
|
|
120
122
|
mergeHeaders();
|
|
121
|
-
return mapResponse(problemOf(self, statusFallbackBody(error, served), served), context.set, context);
|
|
123
|
+
return mapResponse(problemOf(self, statusFallbackBody(error, served), served, claimsProblem), context.set, context);
|
|
122
124
|
};
|
|
123
125
|
const serveDetail = () => {
|
|
124
126
|
let detail;
|
|
@@ -131,10 +133,10 @@ function fallbackErrorResponse(context, error, mapResponse, defaultError) {
|
|
|
131
133
|
if (detail instanceof Promise) return detail.then((resolved) => {
|
|
132
134
|
if (resolved === void 0) return serveMessage();
|
|
133
135
|
mergeHeaders();
|
|
134
|
-
return mapResponse(problemOf(self, resolved, served), context.set, context);
|
|
136
|
+
return mapResponse(problemOf(self, resolved, served, claimsProblem), context.set, context);
|
|
135
137
|
}, failed);
|
|
136
138
|
mergeHeaders();
|
|
137
|
-
return mapResponse(problemOf(self, detail, served), context.set, context);
|
|
139
|
+
return mapResponse(problemOf(self, detail, served, claimsProblem), context.set, context);
|
|
138
140
|
};
|
|
139
141
|
if (error instanceof ValidationError) return mapResponse(new ElysiaStatus(422, problemBody({
|
|
140
142
|
type: "validation",
|
package/dist/handler/utils.js
CHANGED
|
@@ -19,8 +19,9 @@ function cachedResponse(body, status, headers) {
|
|
|
19
19
|
}
|
|
20
20
|
const NOT_FOUND_BODY = JSON.stringify({
|
|
21
21
|
type: "not-found",
|
|
22
|
-
|
|
23
|
-
status: 404
|
|
22
|
+
code: "not-found",
|
|
23
|
+
status: 404,
|
|
24
|
+
title: "Not Found"
|
|
24
25
|
});
|
|
25
26
|
const getNotFound = cachedResponse(NOT_FOUND_BODY, 404, { "content-type": require_error.PROBLEM_JSON });
|
|
26
27
|
function forwardError(value) {
|
package/dist/handler/utils.mjs
CHANGED
|
@@ -18,8 +18,9 @@ function cachedResponse(body, status, headers) {
|
|
|
18
18
|
}
|
|
19
19
|
const NOT_FOUND_BODY = JSON.stringify({
|
|
20
20
|
type: "not-found",
|
|
21
|
-
|
|
22
|
-
status: 404
|
|
21
|
+
code: "not-found",
|
|
22
|
+
status: 404,
|
|
23
|
+
title: "Not Found"
|
|
23
24
|
});
|
|
24
25
|
const getNotFound = cachedResponse(NOT_FOUND_BODY, 404, { "content-type": PROBLEM_JSON });
|
|
25
26
|
function forwardError(value) {
|
package/dist/package.js
CHANGED
package/dist/package.mjs
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -685,8 +685,8 @@ type ExtractResolveFromMacro<A> = IsNever<A> extends true ? {} : A extends AnyEl
|
|
|
685
685
|
type ExtractOnlyResponseFromMacro<A> = IsNever<A> extends true ? {} : Extract<A, AnyElysiaStatus> extends infer A ? IsNever<A> extends true ? {} : {
|
|
686
686
|
return: MergeResponseStatus<A>;
|
|
687
687
|
} : {};
|
|
688
|
-
type MergeResponseStatus<A> = { [status in keyof UnionToIntersect<A extends ElysiaStatus<any, any, infer Status> ? { [A in Status]: 1 } : never>]: Extract<A, {
|
|
689
|
-
|
|
688
|
+
type MergeResponseStatus<A> = { [status in keyof UnionToIntersect<A extends ElysiaStatus<any, any, infer Status> ? { [A in Status]: 1 } : never>]: Extract<A, AnyElysiaStatus & {
|
|
689
|
+
status: status;
|
|
690
690
|
}>['response'] extends infer Value ? IsAny<Value> extends true ? StatusMapBack[status] : Value : never };
|
|
691
691
|
type ExtractAllResponseFromMacro<A> = IsNever<A> extends true ? {} : {
|
|
692
692
|
return: MergeResponseStatus<A> & (Exclude<A, AnyElysiaStatus> extends infer A ? IsAny<A> extends true ? {} : IsNever<A> extends true ? {} : NonNullable<void> extends A ? {} : undefined extends A ? {} : {
|
|
@@ -808,6 +808,16 @@ type OwnedError<E> = E extends {
|
|
|
808
808
|
type ErrorProblemType<E> = OwnedError<E> extends true ? E extends {
|
|
809
809
|
type: infer T extends string;
|
|
810
810
|
} ? T : 'about:blank' : 'about:blank';
|
|
811
|
+
/**
|
|
812
|
+
* `code` extension member an error contributes a class made by
|
|
813
|
+
* `HTTPError.id`, or a built-in `ElysiaError`. `type` may be widened to a URI
|
|
814
|
+
* by `HTTPError.typeBase`, `code` never is
|
|
815
|
+
*/
|
|
816
|
+
type ErrorProblemCode<E> = E extends {
|
|
817
|
+
code: infer C extends string;
|
|
818
|
+
} ? string extends C ? {} : {
|
|
819
|
+
code: C;
|
|
820
|
+
} : {};
|
|
811
821
|
/**
|
|
812
822
|
* Problem document served for a `detail` annotation. `detail` is carried
|
|
813
823
|
* verbatim, objects included — it is never spread into the envelope
|
|
@@ -815,7 +825,7 @@ type ErrorProblemType<E> = OwnedError<E> extends true ? E extends {
|
|
|
815
825
|
type ProblemOf<E, Detail> = ProblemResponseBody<ErrorFallbackStatus<E>, {
|
|
816
826
|
type: ErrorProblemType<E>;
|
|
817
827
|
detail: Detail;
|
|
818
|
-
}
|
|
828
|
+
} & ErrorProblemCode<E>>;
|
|
819
829
|
/**
|
|
820
830
|
* Tier 3 — nothing annotated. An error that claimed a problem serves its
|
|
821
831
|
* message as `detail`, anything else keeps the legacy raw lane
|
|
@@ -902,7 +912,7 @@ type ComposeElysiaResponse<Schema extends RouteSchema, Handle, Possibility exten
|
|
|
902
912
|
expected?: string;
|
|
903
913
|
};
|
|
904
914
|
})>>;
|
|
905
|
-
type ExtractErrorFromHandle<in out Handle> = { [ErrorResponse in Extract<Handle, AnyElysiaStatus> as ErrorResponse extends AnyElysiaStatus ? ErrorResponse['
|
|
915
|
+
type ExtractErrorFromHandle<in out Handle> = { [ErrorResponse in Extract<Handle, AnyElysiaStatus> as ErrorResponse extends AnyElysiaStatus ? ErrorResponse['status'] : never]: Prettify<ErrorResponse['response']> };
|
|
906
916
|
/**
|
|
907
917
|
* Status used when an error handler returns a plain value (or falls through):
|
|
908
918
|
* the error's declared literal `status`, otherwise 500
|
package/dist/ws/route.js
CHANGED
|
@@ -170,12 +170,7 @@ function wsErrorFrame(error) {
|
|
|
170
170
|
} catch {}
|
|
171
171
|
if (typeof error?.toResponse === "function") {
|
|
172
172
|
if (error instanceof require_error.ElysiaError && error.toResponse === require_error.ElysiaError.prototype.toResponse) try {
|
|
173
|
-
return JSON.stringify(require_error.problemBody(
|
|
174
|
-
type: error.problemType,
|
|
175
|
-
title: error.problemTitle,
|
|
176
|
-
status: error.status ?? 500,
|
|
177
|
-
detail: error.response !== void 0 && error.response !== error.problemTitle ? error.response : void 0
|
|
178
|
-
}));
|
|
173
|
+
return JSON.stringify(require_error.problemBody(require_error.elysiaErrorProblem(error)));
|
|
179
174
|
} catch {}
|
|
180
175
|
try {
|
|
181
176
|
const r = error.toResponse();
|
|
@@ -198,6 +193,7 @@ function wsProblemFrame(error) {
|
|
|
198
193
|
const stringify = (value) => typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
199
194
|
const problemFrame = (detail) => JSON.stringify(require_error.problemBody({
|
|
200
195
|
type: error.type ?? "about:blank",
|
|
196
|
+
...typeof error.code === "string" ? { code: error.code } : {},
|
|
201
197
|
detail,
|
|
202
198
|
status: served
|
|
203
199
|
}));
|
package/dist/ws/route.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isBun } from "../universal/constants.mjs";
|
|
2
2
|
import { assignOwn, deriveEntryFn, isNotEmpty, isSocketQuiet, nullObject } from "../utils.mjs";
|
|
3
3
|
import { isBridgeLive } from "../type/bridge.mjs";
|
|
4
|
-
import { ElysiaError, ElysiaStatus, ValidationError, internalServerErrorBodyString, problemBody, problemResponse } from "../error.mjs";
|
|
4
|
+
import { ElysiaError, ElysiaStatus, ValidationError, elysiaErrorProblem, internalServerErrorBodyString, problemBody, problemResponse } from "../error.mjs";
|
|
5
5
|
import { hasSyncHmac } from "../cookie/crypto.mjs";
|
|
6
6
|
import { mapResponse } from "../adapter/web-standard/handler.mjs";
|
|
7
7
|
import { parseQueryFromURL } from "../parse-query.mjs";
|
|
@@ -169,12 +169,7 @@ function wsErrorFrame(error) {
|
|
|
169
169
|
} catch {}
|
|
170
170
|
if (typeof error?.toResponse === "function") {
|
|
171
171
|
if (error instanceof ElysiaError && error.toResponse === ElysiaError.prototype.toResponse) try {
|
|
172
|
-
return JSON.stringify(problemBody(
|
|
173
|
-
type: error.problemType,
|
|
174
|
-
title: error.problemTitle,
|
|
175
|
-
status: error.status ?? 500,
|
|
176
|
-
detail: error.response !== void 0 && error.response !== error.problemTitle ? error.response : void 0
|
|
177
|
-
}));
|
|
172
|
+
return JSON.stringify(problemBody(elysiaErrorProblem(error)));
|
|
178
173
|
} catch {}
|
|
179
174
|
try {
|
|
180
175
|
const r = error.toResponse();
|
|
@@ -197,6 +192,7 @@ function wsProblemFrame(error) {
|
|
|
197
192
|
const stringify = (value) => typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
198
193
|
const problemFrame = (detail) => JSON.stringify(problemBody({
|
|
199
194
|
type: error.type ?? "about:blank",
|
|
195
|
+
...typeof error.code === "string" ? { code: error.code } : {},
|
|
200
196
|
detail,
|
|
201
197
|
status: served
|
|
202
198
|
}));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elysia",
|
|
3
3
|
"description": "Ergonomic Framework for Human",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.11",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "saltyAom",
|
|
7
7
|
"url": "https://github.com/SaltyAom",
|
|
@@ -287,9 +287,6 @@
|
|
|
287
287
|
"@types/bun": {
|
|
288
288
|
"optional": true
|
|
289
289
|
},
|
|
290
|
-
"openapi-types": {
|
|
291
|
-
"optional": true
|
|
292
|
-
},
|
|
293
290
|
"typescript": {
|
|
294
291
|
"optional": true
|
|
295
292
|
}
|