UNPKG

elysia

Version:

Ergonomic Framework for Human

326 lines (323 loc) 9.45 kB
// src/error.ts import { Value } from "@sinclair/typebox/value"; // src/utils.ts var hasHeaderShorthand = "toJSON" in new Headers(); var primitiveHooks = [ "start", "request", "parse", "transform", "resolve", "beforeHandle", "afterHandle", "mapResponse", "afterResponse", "trace", "error", "stop", "body", "headers", "params", "query", "response", "type", "detail" ], primitiveHookMap = primitiveHooks.reduce( (acc, x) => (acc[x] = !0, acc), {} ); var isBun2 = typeof Bun < "u", hasBunHash = isBun2 && typeof Bun.hash == "function"; var StatusMap = { Continue: 100, "Switching Protocols": 101, Processing: 102, "Early Hints": 103, OK: 200, Created: 201, Accepted: 202, "Non-Authoritative Information": 203, "No Content": 204, "Reset Content": 205, "Partial Content": 206, "Multi-Status": 207, "Already Reported": 208, "Multiple Choices": 300, "Moved Permanently": 301, Found: 302, "See Other": 303, "Not Modified": 304, "Temporary Redirect": 307, "Permanent Redirect": 308, "Bad Request": 400, Unauthorized: 401, "Payment Required": 402, Forbidden: 403, "Not Found": 404, "Method Not Allowed": 405, "Not Acceptable": 406, "Proxy Authentication Required": 407, "Request Timeout": 408, Conflict: 409, Gone: 410, "Length Required": 411, "Precondition Failed": 412, "Payload Too Large": 413, "URI Too Long": 414, "Unsupported Media Type": 415, "Range Not Satisfiable": 416, "Expectation Failed": 417, "I'm a teapot": 418, "Misdirected Request": 421, "Unprocessable Content": 422, Locked: 423, "Failed Dependency": 424, "Too Early": 425, "Upgrade Required": 426, "Precondition Required": 428, "Too Many Requests": 429, "Request Header Fields Too Large": 431, "Unavailable For Legal Reasons": 451, "Internal Server Error": 500, "Not Implemented": 501, "Bad Gateway": 502, "Service Unavailable": 503, "Gateway Timeout": 504, "HTTP Version Not Supported": 505, "Variant Also Negotiates": 506, "Insufficient Storage": 507, "Loop Detected": 508, "Not Extended": 510, "Network Authentication Required": 511 }, InvertedStatusMap = Object.fromEntries( Object.entries(StatusMap).map(([k, v]) => [v, k]) ); var encoder = new TextEncoder(); var ELYSIA_FORM_DATA = Symbol("ElysiaFormData"), ELYSIA_REQUEST_ID = Symbol("ElysiaRequestId"); var supportPerMethodInlineHandler = (() => { if (typeof Bun > "u") return !0; let semver = Bun.version.split("."); return !(+semver[0] < 1 || +semver[1] < 2 || +semver[2] < 14); })(); // src/error.ts var env = typeof Bun < "u" ? Bun.env : typeof process < "u" ? process?.env : void 0, ERROR_CODE = Symbol("ElysiaErrorCode"), isProduction = (env?.NODE_ENV ?? env?.ENV) === "production", emptyHttpStatus = { 101: void 0, 204: void 0, 205: void 0, 304: void 0, 307: void 0, 308: void 0 }, ElysiaCustomStatusResponse = class { constructor(code, response) { let res = response ?? (code in InvertedStatusMap ? ( // @ts-expect-error Always correct InvertedStatusMap[code] ) : code); this.code = StatusMap[code] ?? code, code in emptyHttpStatus ? this.response = void 0 : this.response = res; } }, status = (code, response) => new ElysiaCustomStatusResponse(code, response), error = status, InternalServerError = class extends Error { constructor(message) { super(message ?? "INTERNAL_SERVER_ERROR"); this.code = "INTERNAL_SERVER_ERROR"; this.status = 500; } }, NotFoundError = class extends Error { constructor(message) { super(message ?? "NOT_FOUND"); this.code = "NOT_FOUND"; this.status = 404; } }, ParseError = class extends Error { constructor(cause) { super("Bad Request", { cause }); this.code = "PARSE"; this.status = 400; } }, InvalidCookieSignature = class extends Error { constructor(key, message) { super(message ?? `"${key}" has invalid cookie signature`); this.key = key; this.code = "INVALID_COOKIE_SIGNATURE"; this.status = 400; } }, mapValueError = (error2) => { if (!error2) return { summary: void 0 }; let { message, path, value, type } = error2, property = path.slice(1).replaceAll("/", "."), isRoot = path === ""; switch (type) { case 42: return { ...error2, summary: isRoot ? "Value should not be provided" : `Property '${property}' should not be provided` }; case 45: return { ...error2, summary: isRoot ? "Value is missing" : `Property '${property}' is missing` }; case 50: let quoteIndex = message.indexOf("'"), format = message.slice( quoteIndex + 1, message.indexOf("'", quoteIndex + 1) ); return { ...error2, summary: isRoot ? "Value should be an email" : `Property '${property}' should be ${format}` }; case 54: return { ...error2, summary: `${message.slice(0, 9).trim()} property '${property}' to be ${message.slice(8).trim()} but found: ${value}` }; case 62: let union = error2.schema.anyOf.map((x) => `'${x?.format ?? x.type}'`).join(", "); return { ...error2, summary: isRoot ? `Value should be one of ${union}` : `Property '${property}' should be one of: ${union}` }; default: return { summary: message, ...error2 }; } }, InvalidFileType = class _InvalidFileType extends Error { constructor(property, expected, message = `"${property}" has invalid file type`) { super(message); this.property = property; this.expected = expected; this.message = message; this.code = "INVALID_FILE_TYPE"; this.status = 422; Object.setPrototypeOf(this, _InvalidFileType.prototype); } toResponse(headers) { return isProduction ? new Response( JSON.stringify({ type: "validation", on: "body" }), { status: 422, headers: { ...headers, "content-type": "application/json" } } ) : new Response( JSON.stringify({ type: "validation", on: "body", summary: "Invalid file type", message: this.message, property: this.property, expected: this.expected }), { status: 422, headers: { ...headers, "content-type": "application/json" } } ); } }, ValidationError = class _ValidationError extends Error { constructor(type, validator, value, errors) { value && typeof value == "object" && value instanceof ElysiaCustomStatusResponse && (value = value.response); let error2 = errors?.First() || (isProduction ? void 0 : "Errors" in validator ? validator.Errors(value).First() : Value.Errors(validator, value).First()), customError = error2?.schema?.message || error2?.schema?.error !== void 0 ? typeof error2.schema.error == "function" ? error2.schema.error({ type, validator, value, get errors() { return [...validator.Errors(value)].map( mapValueError ); } }) : error2.schema.error : void 0, accessor = error2?.path || "root", message = ""; if (customError !== void 0) message = typeof customError == "object" ? JSON.stringify(customError) : customError + ""; else if (isProduction) message = JSON.stringify({ type: "validation", on: type, summary: mapValueError(error2).summary, message: error2?.message, found: value }); else { let schema = validator?.schema ?? validator, errors2 = "Errors" in validator ? [...validator.Errors(value)].map(mapValueError) : [...Value.Errors(validator, value)].map(mapValueError), expected; try { expected = Value.Create(schema); } catch (error3) { expected = { type: "Could not create expected value", // @ts-expect-error message: error3?.message, error: error3 }; } message = JSON.stringify( { type: "validation", on: type, summary: mapValueError(error2).summary, property: accessor, message: error2?.message, expected, found: value, errors: errors2 }, null, 2 ); } super(message); this.type = type; this.validator = validator; this.value = value; this.code = "VALIDATION"; this.status = 422; Object.setPrototypeOf(this, _ValidationError.prototype); } get all() { return "Errors" in this.validator ? [...this.validator.Errors(this.value)].map(mapValueError) : ( // @ts-ignore [...Value.Errors(this.validator, this.value)].map(mapValueError) ); } static simplifyModel(validator) { let model = "schema" in validator ? validator.schema : validator; try { return Value.Create(model); } catch { return model; } } get model() { return _ValidationError.simplifyModel(this.validator); } toResponse(headers) { return new Response(this.message, { status: 400, headers: { ...headers, "content-type": "application/json" } }); } }; export { ERROR_CODE, ElysiaCustomStatusResponse, InternalServerError, InvalidCookieSignature, InvalidFileType, NotFoundError, ParseError, ValidationError, error, isProduction, mapValueError, status };