UNPKG

koatty_exception

Version:
1,116 lines (1,110 loc) 44.1 kB
/*! * @Author: richen * @Date: 2025-06-02 18:27:33 * @License: BSD (3-Clause) * @Copyright (c) - <richenlin(at)gmail.com> * @HomePage: https://koatty.org/ */ import { IOCContainer } from 'koatty_container'; import * as Helper from 'koatty_lib'; import { Helper as Helper$1 } from 'koatty_lib'; import { DefaultLogger } from 'koatty_logger'; import { SpanStatusCode } from '@opentelemetry/api'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; /* * @Description: * @Usage: * @Author: richen * @Date: 2021-11-19 00:53:19 * @LastEditTime: 2023-01-13 10:02:11 */ /** * HttpStatusCode * * @export * @enum {number} */ var HttpStatusCode; (function (HttpStatusCode) { /** * The server has received the request headers and the client should proceed to send the request body * (in the case of a request for which a body needs to be sent; for example, a POST request). * Sending a large request body to a server after a request has been rejected for inappropriate headers would be inefficient. * To have a server check the request's headers, a client must send Expect: 100-continue as a header in its initial request * and receive a 100 Continue status code in response before sending the body. The response 417 Expectation Failed indicates the request should not be continued. */ HttpStatusCode[HttpStatusCode["CONTINUE"] = 100] = "CONTINUE"; /** * The requester has asked the server to switch protocols and the server has agreed to do so. */ HttpStatusCode[HttpStatusCode["SWITCHING_PROTOCOLS"] = 101] = "SWITCHING_PROTOCOLS"; /** * A WebDAV request may contain many sub-requests involving file operations, requiring a long time to complete the request. * This code indicates that the server has received and is processing the request, but no response is available yet. * This prevents the client from timing out and assuming the request was lost. */ HttpStatusCode[HttpStatusCode["PROCESSING"] = 102] = "PROCESSING"; /** * Standard response for successful HTTP requests. * The actual response will depend on the request method used. * In a GET request, the response will contain an entity corresponding to the requested resource. * In a POST request, the response will contain an entity describing or containing the result of the action. */ HttpStatusCode[HttpStatusCode["OK"] = 200] = "OK"; /** * The request has been fulfilled, resulting in the creation of a new resource. */ HttpStatusCode[HttpStatusCode["CREATED"] = 201] = "CREATED"; /** * The request has been accepted for processing, but the processing has not been completed. * The request might or might not be eventually acted upon, and may be disallowed when processing occurs. */ HttpStatusCode[HttpStatusCode["ACCEPTED"] = 202] = "ACCEPTED"; /** * SINCE HTTP/1.1 * The server is a transforming proxy that received a 200 OK from its origin, * but is returning a modified version of the origin's response. */ HttpStatusCode[HttpStatusCode["NON_AUTHORITATIVE_INFORMATION"] = 203] = "NON_AUTHORITATIVE_INFORMATION"; /** * The server successfully processed the request and is not returning any content. */ HttpStatusCode[HttpStatusCode["NO_CONTENT"] = 204] = "NO_CONTENT"; /** * The server successfully processed the request, but is not returning any content. * Unlike a 204 response, this response requires that the requester reset the document view. */ HttpStatusCode[HttpStatusCode["RESET_CONTENT"] = 205] = "RESET_CONTENT"; /** * The server is delivering only part of the resource (byte serving) due to a range header sent by the client. * The range header is used by HTTP clients to enable resuming of interrupted downloads, * or split a download into multiple simultaneous streams. */ HttpStatusCode[HttpStatusCode["PARTIAL_CONTENT"] = 206] = "PARTIAL_CONTENT"; /** * The message body that follows is an XML message and can contain a number of separate response codes, * depending on how many sub-requests were made. */ HttpStatusCode[HttpStatusCode["MULTI_STATUS"] = 207] = "MULTI_STATUS"; /** * The members of a DAV binding have already been enumerated in a preceding part of the (multistatus) response, * and are not being included again. */ HttpStatusCode[HttpStatusCode["ALREADY_REPORTED"] = 208] = "ALREADY_REPORTED"; /** * The server has fulfilled a request for the resource, * and the response is a representation of the result of one or more instance-manipulations applied to the current instance. */ HttpStatusCode[HttpStatusCode["IM_USED"] = 226] = "IM_USED"; /** * Indicates multiple options for the resource from which the client may choose (via agent-driven content negotiation). * For example, this code could be used to present multiple video format options, * to list files with different filename extensions, or to suggest word-sense disambiguation. */ HttpStatusCode[HttpStatusCode["MULTIPLE_CHOICES"] = 300] = "MULTIPLE_CHOICES"; /** * This and all future requests should be directed to the given URI. */ HttpStatusCode[HttpStatusCode["MOVED_PERMANENTLY"] = 301] = "MOVED_PERMANENTLY"; /** * This is an example of industry practice contradicting the standard. * The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect * (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302 * with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 * to distinguish between the two behaviours. However, some Web applications and frameworks * use the 302 status code as if it were the 303. */ HttpStatusCode[HttpStatusCode["FOUND"] = 302] = "FOUND"; /** * SINCE HTTP/1.1 * The response to the request can be found under another URI using a GET method. * When received in response to a POST (or PUT/DELETE), the client should presume that * the server has received the data and should issue a redirect with a separate GET message. */ HttpStatusCode[HttpStatusCode["SEE_OTHER"] = 303] = "SEE_OTHER"; /** * Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-None-Match. * In such case, there is no need to retransmit the resource since the client still has a previously-downloaded copy. */ HttpStatusCode[HttpStatusCode["NOT_MODIFIED"] = 304] = "NOT_MODIFIED"; /** * SINCE HTTP/1.1 * The requested resource is available only through a proxy, the address for which is provided in the response. * Many HTTP clients (such as Mozilla and Internet Explorer) do not correctly handle responses with this status code, primarily for security reasons. */ HttpStatusCode[HttpStatusCode["USE_PROXY"] = 305] = "USE_PROXY"; /** * No longer used. Originally meant "Subsequent requests should use the specified proxy." */ HttpStatusCode[HttpStatusCode["SWITCH_PROXY"] = 306] = "SWITCH_PROXY"; /** * SINCE HTTP/1.1 * In this case, the request should be repeated with another URI; however, future requests should still use the original URI. * In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the original request. * For example, a POST request should be repeated using another POST request. */ HttpStatusCode[HttpStatusCode["TEMPORARY_REDIRECT"] = 307] = "TEMPORARY_REDIRECT"; /** * The request and all future requests should be repeated using another URI. * 307 and 308 parallel the behaviors of 302 and 301, but do not allow the HTTP method to change. * So, for example, submitting a form to a permanently redirected resource may continue smoothly. */ HttpStatusCode[HttpStatusCode["PERMANENT_REDIRECT"] = 308] = "PERMANENT_REDIRECT"; /** * The server cannot or will not process the request due to an apparent client error * (e.g., malformed request syntax, too large size, invalid request message framing, or deceptive request routing). */ HttpStatusCode[HttpStatusCode["BAD_REQUEST"] = 400] = "BAD_REQUEST"; /** * Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet * been provided. The response must include a WWW-Authenticate header field containing a challenge applicable to the * requested resource. See Basic access authentication and Digest access authentication. 401 semantically means * "unauthenticated",i.e. the user does not have the necessary credentials. */ HttpStatusCode[HttpStatusCode["UNAUTHORIZED"] = 401] = "UNAUTHORIZED"; /** * Reserved for future use. The original intention was that this code might be used as part of some form of digital * cash or micro payment scheme, but that has not happened, and this code is not usually used. * Google Developers API uses this status if a particular developer has exceeded the daily limit on requests. */ HttpStatusCode[HttpStatusCode["PAYMENT_REQUIRED"] = 402] = "PAYMENT_REQUIRED"; /** * The request was valid, but the server is refusing action. * The user might not have the necessary permissions for a resource. */ HttpStatusCode[HttpStatusCode["FORBIDDEN"] = 403] = "FORBIDDEN"; /** * The requested resource could not be found but may be available in the future. * Subsequent requests by the client are permissible. */ HttpStatusCode[HttpStatusCode["NOT_FOUND"] = 404] = "NOT_FOUND"; /** * A request method is not supported for the requested resource; * for example, a GET request on a form that requires data to be presented via POST, or a PUT request on a read-only resource. */ HttpStatusCode[HttpStatusCode["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED"; /** * The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request. */ HttpStatusCode[HttpStatusCode["NOT_ACCEPTABLE"] = 406] = "NOT_ACCEPTABLE"; /** * The client must first authenticate itself with the proxy. */ HttpStatusCode[HttpStatusCode["PROXY_AUTHENTICATION_REQUIRED"] = 407] = "PROXY_AUTHENTICATION_REQUIRED"; /** * The server timed out waiting for the request. * According to HTTP specifications: * "The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time." */ HttpStatusCode[HttpStatusCode["REQUEST_TIMEOUT"] = 408] = "REQUEST_TIMEOUT"; /** * Indicates that the request could not be processed because of conflict in the request, * such as an edit conflict between multiple simultaneous updates. */ HttpStatusCode[HttpStatusCode["CONFLICT"] = 409] = "CONFLICT"; /** * Indicates that the resource requested is no longer available and will not be available again. * This should be used when a resource has been intentionally removed and the resource should be purged. * Upon receiving a 410 status code, the client should not request the resource in the future. * Clients such as search engines should remove the resource from their indices. * Most use cases do not require clients and search engines to purge the resource, and a "404 Not Found" may be used instead. */ HttpStatusCode[HttpStatusCode["GONE"] = 410] = "GONE"; /** * The request did not specify the length of its content, which is required by the requested resource. */ HttpStatusCode[HttpStatusCode["LENGTH_REQUIRED"] = 411] = "LENGTH_REQUIRED"; /** * The server does not meet one of the preconditions that the requester put on the request. */ HttpStatusCode[HttpStatusCode["PRECONDITION_FAILED"] = 412] = "PRECONDITION_FAILED"; /** * The request is larger than the server is willing or able to process. Previously called "Request Entity Too Large". */ HttpStatusCode[HttpStatusCode["PAYLOAD_TOO_LARGE"] = 413] = "PAYLOAD_TOO_LARGE"; /** * The URI provided was too long for the server to process. Often the result of too much data being encoded as a query-string of a GET request, * in which case it should be converted to a POST request. * Called "Request-URI Too Long" previously. */ HttpStatusCode[HttpStatusCode["URI_TOO_LONG"] = 414] = "URI_TOO_LONG"; /** * The request entity has a media type which the server or resource does not support. * For example, the client uploads an image as image/svg+xml, but the server requires that images use a different format. */ HttpStatusCode[HttpStatusCode["UNSUPPORTED_MEDIA_TYPE"] = 415] = "UNSUPPORTED_MEDIA_TYPE"; /** * The client has asked for a portion of the file (byte serving), but the server cannot supply that portion. * For example, if the client asked for a part of the file that lies beyond the end of the file. * Called "Requested Range Not Satisfiable" previously. */ HttpStatusCode[HttpStatusCode["RANGE_NOT_SATISFIABLE"] = 416] = "RANGE_NOT_SATISFIABLE"; /** * The server cannot meet the requirements of the Expect request-header field. */ HttpStatusCode[HttpStatusCode["EXPECTATION_FAILED"] = 417] = "EXPECTATION_FAILED"; /** * This code was defined in 1998 as one of the traditional IETF April Fools' jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol, * and is not expected to be implemented by actual HTTP servers. The RFC specifies this code should be returned by * teapots requested to brew coffee. This HTTP status is used as an Easter egg in some websites, including Google.com. */ HttpStatusCode[HttpStatusCode["I_AM_A_TEAPOT"] = 418] = "I_AM_A_TEAPOT"; /** * The request was directed at a server that is not able to produce a response (for example because a connection reuse). */ HttpStatusCode[HttpStatusCode["MISDIRECTED_REQUEST"] = 421] = "MISDIRECTED_REQUEST"; /** * The request was well-formed but was unable to be followed due to semantic errors. */ HttpStatusCode[HttpStatusCode["UNPROCESSABLE_ENTITY"] = 422] = "UNPROCESSABLE_ENTITY"; /** * The resource that is being accessed is locked. */ HttpStatusCode[HttpStatusCode["LOCKED"] = 423] = "LOCKED"; /** * The request failed due to failure of a previous request (e.g., a PROPPATCH). */ HttpStatusCode[HttpStatusCode["FAILED_DEPENDENCY"] = 424] = "FAILED_DEPENDENCY"; /** * The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field. */ HttpStatusCode[HttpStatusCode["UPGRADE_REQUIRED"] = 426] = "UPGRADE_REQUIRED"; /** * The origin server requires the request to be conditional. * Intended to prevent "the 'lost update' problem, where a client * GETs a resource's state, modifies it, and PUTs it back to the server, * when meanwhile a third party has modified the state on the server, leading to a conflict." */ HttpStatusCode[HttpStatusCode["PRECONDITION_REQUIRED"] = 428] = "PRECONDITION_REQUIRED"; /** * The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes. */ HttpStatusCode[HttpStatusCode["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS"; /** * The server is unwilling to process the request because either an individual header field, * or all the header fields collectively, are too large. */ HttpStatusCode[HttpStatusCode["REQUEST_HEADER_FIELDS_TOO_LARGE"] = 431] = "REQUEST_HEADER_FIELDS_TOO_LARGE"; /** * A server operator has received a legal demand to deny access to a resource or to a set of resources * that includes the requested resource. The code 451 was chosen as a reference to the novel Fahrenheit 451. */ HttpStatusCode[HttpStatusCode["UNAVAILABLE_FOR_LEGAL_REASONS"] = 451] = "UNAVAILABLE_FOR_LEGAL_REASONS"; /** * A generic error message, given when an unexpected condition was encountered and no more specific message is suitable. */ HttpStatusCode[HttpStatusCode["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR"; /** * The server either does not recognize the request method, or it lacks the ability to fulfill the request. * Usually this implies future availability (e.g., a new feature of a web-service API). */ HttpStatusCode[HttpStatusCode["NOT_IMPLEMENTED"] = 501] = "NOT_IMPLEMENTED"; /** * The server was acting as a gateway or proxy and received an invalid response from the upstream server. */ HttpStatusCode[HttpStatusCode["BAD_GATEWAY"] = 502] = "BAD_GATEWAY"; /** * The server is currently unavailable (because it is overloaded or down for maintenance). * Generally, this is a temporary state. */ HttpStatusCode[HttpStatusCode["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE"; /** * The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. */ HttpStatusCode[HttpStatusCode["GATEWAY_TIMEOUT"] = 504] = "GATEWAY_TIMEOUT"; /** * The server does not support the HTTP protocol version used in the request */ HttpStatusCode[HttpStatusCode["HTTP_VERSION_NOT_SUPPORTED"] = 505] = "HTTP_VERSION_NOT_SUPPORTED"; /** * Transparent content negotiation for the request results in a circular reference. */ HttpStatusCode[HttpStatusCode["VARIANT_ALSO_NEGOTIATES"] = 506] = "VARIANT_ALSO_NEGOTIATES"; /** * The server is unable to store the representation needed to complete the request. */ HttpStatusCode[HttpStatusCode["INSUFFICIENT_STORAGE"] = 507] = "INSUFFICIENT_STORAGE"; /** * The server detected an infinite loop while processing the request. */ HttpStatusCode[HttpStatusCode["LOOP_DETECTED"] = 508] = "LOOP_DETECTED"; /** * Further extensions to the request are required for the server to fulfill it. */ HttpStatusCode[HttpStatusCode["NOT_EXTENDED"] = 510] = "NOT_EXTENDED"; /** * The client needs to authenticate to gain network access. * Intended for use by intercepting proxies used to control access to the network (e.g., "captive portals" used * to require agreement to Terms of Service before granting full Internet access via a Wi-Fi hotspot). */ HttpStatusCode[HttpStatusCode["NETWORK_AUTHENTICATION_REQUIRED"] = 511] = "NETWORK_AUTHENTICATION_REQUIRED"; })(HttpStatusCode || (HttpStatusCode = {})); /** * HttpStatusCodeMap */ const HttpStatusCodeMap = new Map([ [100, "CONTINUE"], [101, "SWITCHING_PROTOCOLS"], [102, "PROCESSING"], [200, "OK"], [201, "CREATED"], [202, "ACCEPTED"], [203, "NON_AUTHORITATIVE_INFORMATION"], [204, "NO_CONTENT"], [205, "RESET_CONTENT"], [206, "PARTIAL_CONTENT"], [207, "MULTI_STATUS"], [208, "ALREADY_REPORTED"], [226, "IM_USED"], [300, "MULTIPLE_CHOICES"], [301, "MOVED_PERMANENTLY"], [302, "FOUND"], [303, "SEE_OTHER"], [304, "NOT_MODIFIED"], [305, "USE_PROXY"], [306, "SWITCH_PROXY"], [307, "TEMPORARY_REDIRECT"], [308, "PERMANENT_REDIRECT"], [400, "BAD_REQUEST"], [401, "UNAUTHORIZED"], [402, "PAYMENT_REQUIRED"], [403, "FORBIDDEN"], [404, "NOT_FOUND"], [405, "METHOD_NOT_ALLOWED"], [406, "NOT_ACCEPTABLE"], [407, "PROXY_AUTHENTICATION_REQUIRED"], [408, "REQUEST_TIMEOUT"], [409, "CONFLICT"], [410, "GONE"], [411, "LENGTH_REQUIRED"], [412, "PRECONDITION_FAILED"], [413, "PAYLOAD_TOO_LARGE"], [414, "URI_TOO_LONG"], [415, "UNSUPPORTED_MEDIA_TYPE"], [416, "RANGE_NOT_SATISFIABLE"], [417, "EXPECTATION_FAILED"], [418, "I_AM_A_TEAPOT"], [421, "MISDIRECTED_REQUEST"], [422, "UNPROCESSABLE_ENTITY"], [423, "LOCKED"], [424, "FAILED_DEPENDENCY"], [426, "UPGRADE_REQUIRED"], [428, "PRECONDITION_REQUIRED"], [429, "TOO_MANY_REQUESTS"], [431, "REQUEST_HEADER_FIELDS_TOO_LARGE"], [451, "UNAVAILABLE_FOR_LEGAL_REASONS"], [500, "INTERNAL_SERVER_ERROR"], [501, "NOT_IMPLEMENTED"], [502, "BAD_GATEWAY"], [503, "SERVICE_UNAVAILABLE"], [504, "GATEWAY_TIMEOUT"], [505, "HTTP_VERSION_NOT_SUPPORTED"], [506, "VARIANT_ALSO_NEGOTIATES"], [507, "INSUFFICIENT_STORAGE"], [508, "LOOP_DETECTED"], [510, "NOT_EXTENDED"], [511, "NETWORK_AUTHENTICATION_REQUIRED"], ]); /** * GrpcStatusCode * * @export * @enum {number} */ var GrpcStatusCode; (function (GrpcStatusCode) { GrpcStatusCode[GrpcStatusCode["OK"] = 0] = "OK"; GrpcStatusCode[GrpcStatusCode["CANCELLED"] = 1] = "CANCELLED"; GrpcStatusCode[GrpcStatusCode["UNKNOWN"] = 2] = "UNKNOWN"; GrpcStatusCode[GrpcStatusCode["INVALID_ARGUMENT"] = 3] = "INVALID_ARGUMENT"; GrpcStatusCode[GrpcStatusCode["DEADLINE_EXCEEDED"] = 4] = "DEADLINE_EXCEEDED"; GrpcStatusCode[GrpcStatusCode["NOT_FOUND"] = 5] = "NOT_FOUND"; GrpcStatusCode[GrpcStatusCode["ALREADY_EXISTS"] = 6] = "ALREADY_EXISTS"; GrpcStatusCode[GrpcStatusCode["PERMISSION_DENIED"] = 7] = "PERMISSION_DENIED"; GrpcStatusCode[GrpcStatusCode["RESOURCE_EXHAUSTED"] = 8] = "RESOURCE_EXHAUSTED"; GrpcStatusCode[GrpcStatusCode["FAILED_PRECONDITION"] = 9] = "FAILED_PRECONDITION"; GrpcStatusCode[GrpcStatusCode["ABORTED"] = 10] = "ABORTED"; GrpcStatusCode[GrpcStatusCode["OUT_OF_RANGE"] = 11] = "OUT_OF_RANGE"; GrpcStatusCode[GrpcStatusCode["UNIMPLEMENTED"] = 12] = "UNIMPLEMENTED"; GrpcStatusCode[GrpcStatusCode["INTERNAL"] = 13] = "INTERNAL"; GrpcStatusCode[GrpcStatusCode["UNAVAILABLE"] = 14] = "UNAVAILABLE"; GrpcStatusCode[GrpcStatusCode["DATA_LOSS"] = 15] = "DATA_LOSS"; GrpcStatusCode[GrpcStatusCode["UNAUTHENTICATED"] = 16] = "UNAUTHENTICATED"; })(GrpcStatusCode || (GrpcStatusCode = {})); /** * GrpcStatusCodeMap */ const GrpcStatusCodeMap = new Map([ [0, "OK"], [1, "CANCELLED"], [2, "UNKNOWN"], [3, "INVALID_ARGUMENT"], [4, "DEADLINE_EXCEEDED"], [5, "NOT_FOUND"], [6, "ALREADY_EXISTS"], [7, "PERMISSION_DENIED"], [8, "RESOURCE_EXHAUSTED"], [9, "FAILED_PRECONDITION"], [10, "ABORTED"], [11, "OUT_OF_RANGE"], [12, "UNIMPLEMENTED"], [13, "INTERNAL"], [14, "UNAVAILABLE"], [15, "DATA_LOSS"], [14, "UNAUTHENTICATED"], ]); /** * Convert HttpStatusCode to GrpcStatusCode * https://chromium.googlesource.com/external/github.com/grpc/grpc/+/refs/heads/chromium-deps/2016-10-25/doc/http-grpc-status-mapping.md * @export * @param {HttpStatusCode} code * @returns {*} {GrpcStatusCode} */ function StatusCodeConvert(code) { switch (code) { case 200: //HttpStatusCode.OK: return 0; //GrpcStatusCode.OK; case 304: //HttpStatusCode.NOT_MODIFIED: return 1; //GrpcStatusCode.CANCELLED; case 400: //HttpStatusCode.BAD_REQUEST: return 3; //GrpcStatusCode.INVALID_ARGUMENT; case 408: //HttpStatusCode.REQUEST_TIMEOUT: return 4; //GrpcStatusCode.DEADLINE_EXCEEDED; case 404: //HttpStatusCode.NOT_FOUND: return 5; //GrpcStatusCode.NOT_FOUND; case 409: //HttpStatusCode.CONFLICT: return 6; //GrpcStatusCode.ALREADY_EXISTS; case 403: //HttpStatusCode.FORBIDDEN: return 7; //GrpcStatusCode.PERMISSION_DENIED; case 429: //HttpStatusCode.TOO_MANY_REQUESTS: return 8; //GrpcStatusCode.RESOURCE_EXHAUSTED; case 412: //HttpStatusCode.PRECONDITION_FAILED: return 9; //GrpcStatusCode.FAILED_PRECONDITION; case 421: //HttpStatusCode.MISDIRECTED_REQUEST: return 10; //GrpcStatusCode.ABORTED; case 416: //HttpStatusCode.RANGE_NOT_SATISFIABLE: return 11; //GrpcStatusCode.OUT_OF_RANGE; case 501: //HttpStatusCode.NOT_IMPLEMENTED: return 12; //GrpcStatusCode.UNIMPLEMENTED; case 500: //HttpStatusCode.INTERNAL_SERVER_ERROR: return 13; //GrpcStatusCode.INTERNAL; case 503: //HttpStatusCode.SERVICE_UNAVAILABLE: return 14; //GrpcStatusCode.UNAVAILABLE; case 410: //HttpStatusCode.GONE: return 15; //GrpcStatusCode.DATA_LOSS; case 401: //HttpStatusCode.UNAUTHORIZED: return 16; //GrpcStatusCode.UNAUTHENTICATED; default: return 2; //GrpcStatusCode.UNKNOWN; } } /** * Common error codes enum */ var CommonErrorCode; (function (CommonErrorCode) { CommonErrorCode[CommonErrorCode["SUCCESS"] = 0] = "SUCCESS"; CommonErrorCode[CommonErrorCode["GENERAL_ERROR"] = 1] = "GENERAL_ERROR"; CommonErrorCode[CommonErrorCode["VALIDATION_ERROR"] = 1001] = "VALIDATION_ERROR"; CommonErrorCode[CommonErrorCode["AUTHENTICATION_ERROR"] = 1002] = "AUTHENTICATION_ERROR"; CommonErrorCode[CommonErrorCode["AUTHORIZATION_ERROR"] = 1003] = "AUTHORIZATION_ERROR"; CommonErrorCode[CommonErrorCode["RESOURCE_NOT_FOUND"] = 1004] = "RESOURCE_NOT_FOUND"; CommonErrorCode[CommonErrorCode["RESOURCE_CONFLICT"] = 1005] = "RESOURCE_CONFLICT"; CommonErrorCode[CommonErrorCode["RATE_LIMIT_EXCEEDED"] = 1006] = "RATE_LIMIT_EXCEEDED"; CommonErrorCode[CommonErrorCode["INTERNAL_SERVER_ERROR"] = 1007] = "INTERNAL_SERVER_ERROR"; CommonErrorCode[CommonErrorCode["SERVICE_UNAVAILABLE"] = 1008] = "SERVICE_UNAVAILABLE"; CommonErrorCode[CommonErrorCode["TIMEOUT_ERROR"] = 1009] = "TIMEOUT_ERROR"; CommonErrorCode[CommonErrorCode["NETWORK_ERROR"] = 1010] = "NETWORK_ERROR"; })(CommonErrorCode || (CommonErrorCode = {})); /* * @Description: output data * @Usage: * @Author: richen * @Date: 2024-01-03 22:03:34 * @LastEditTime: 2024-03-15 06:16:52 * @License: BSD (3-Clause) * @Copyright (c): <richenlin(at)gmail.com> */ /** * Type guard for JsonResult * * @param {unknown} obj - Object to check * @returns {obj is JsonResult} Type guard result */ function isJsonResult(obj) { return !!(obj && typeof obj === 'object' && 'code' in obj && 'message' in obj && 'data' in obj && typeof obj.code === 'number' && typeof obj.message === 'string'); } /** * Type guard for CodeError * * @param {unknown} obj - Object to check * @returns {obj is CodeError} Type guard result */ function isCodeError(obj) { return !!(obj && typeof obj === 'object' && ('code' in obj || 'message' in obj || 'data' in obj)); } /** * Safe string conversion * * @param {unknown} value - Value to convert * @returns {string} String representation */ function safeStringify(value) { if (typeof value === 'string') { return value; } if (value === null || value === undefined) { return ''; } if (typeof value === 'object') { try { return JSON.stringify(value); } catch { return '[Object object]'; } } return String(value); } class Output { /** * Response to normalize json format content for success * * @template T * @param {string | JsonResult<T>} msg - Message or JsonResult object * @param {T} [data] - Response data * @param {number} [code=0] - Success code, default 0 * @returns {JsonResult<T>} Normalized success response */ static ok(msg, data, code = 0) { // Validate inputs if (typeof code !== 'number' || code < 0) { code = 0; } // Handle JsonResult input if (isJsonResult(msg)) { return { code: typeof msg.code === 'number' ? msg.code : code, message: safeStringify(msg.message), data: msg.data }; } return { code: code, message: safeStringify(msg), data: data }; } /** * Response to normalize json format content for fail * * @template T * @param {Error | CodeError | string | unknown} err - Error object, message, or unknown error * @param {T} [data] - Additional error data * @param {number} [code=1] - Error code, default 1 * @returns {JsonResult<T>} Normalized error response */ static fail(err, data, code = 1) { // Validate code input if (typeof code !== 'number' || code < 1) { code = 1; } // Handle Error objects if (Helper$1.isError(err)) { return { code: code, message: err.message || 'Unknown error', data: data }; } // Handle CodeError objects if (isCodeError(err)) { return { code: typeof err.code === 'number' && err.code > 0 ? err.code : code, message: safeStringify(err.message) || 'Unknown error', data: err.data || data }; } // Handle string messages if (typeof err === 'string') { return { code: code, message: err.trim() || 'Unknown error', data: data }; } // Handle unknown errors return { code: code, message: safeStringify(err) || 'Unknown error occurred', data: data }; } /** * Create a paginated response * * @template T * @param {T[]} items - Array of items * @param {number} total - Total count * @param {number} page - Current page (1-based) * @param {number} pageSize - Items per page * @param {string} [message='Success'] - Response message * @returns {JsonResult} Paginated response */ static paginate(items, total, page, pageSize, message = 'Success') { // Validate inputs const safeTotal = Math.max(0, Math.floor(total)); const safePage = Math.max(1, Math.floor(page)); const safePageSize = Math.max(1, Math.floor(pageSize)); const totalPages = Math.ceil(safeTotal / safePageSize); return this.ok(message, { items: Array.isArray(items) ? items : [], pagination: { total: safeTotal, page: safePage, pageSize: safePageSize, totalPages, hasNext: safePage < totalPages, hasPrev: safePage > 1 } }); } /** * Create a response with metadata * * @template T * @param {string} message - Response message * @param {T} data - Response data * @param {Record<string, unknown>} [meta] - Additional metadata * @param {number} [code=0] - Response code * @returns {JsonResult} Response with metadata */ static withMeta(message, data, meta, code = 0) { return this.ok(message, { data, ...(meta && Object.keys(meta).length > 0 && { meta }) }, code); } } /** * @ author: richen * @ copyright: Copyright (c) - <richenlin(at)gmail.com> * @ license: BSD (3-Clause) * @ version: 2020-12-15 11:49:15 */ // 全局配置 let globalConfig = { enableStackTrace: process.env.NODE_ENV !== 'production', logFormat: 'json', maxStackLength: 1000 }; /** * Set global exception configuration * * @param {Partial<ExceptionConfig>} config - Configuration options */ function setExceptionConfig(config) { globalConfig = { ...globalConfig, ...config }; } /** * Get current exception configuration * * @returns {ExceptionConfig} Current configuration */ function getExceptionConfig() { return { ...globalConfig }; } /** * Indicates that an decorated class is a "ExceptionHandler". * @ExceptionHandler() * export class BusinessException extends Exception { * constructor(message: string, code: number, status: number) { ... } * handler(ctx: KoattyContext) { * * ...//Handling business exceptions * * } * } * * @export * @param {string} [identifier] class name * @returns {ClassDecorator} */ function ExceptionHandler() { return (target) => { const identifier = IOCContainer.getIdentifier(target); // if (identifier === "Exception") { // throw new Error("class name cannot be `Exception`"); // } // if (!identifier.endsWith("Exception")) { // throw Error("class name must end with 'Exception'"); // } // if (!target.prototype.type) { // throw new Error("class's property 'type' must be set"); // } if (!(target.prototype instanceof Exception)) { throw new Error(`class ${identifier} does not inherit from class 'Exception'`); } IOCContainer.saveClass("COMPONENT", target, "ExceptionHandler"); }; } /** * Predefined runtime exception * * @export * @class Exception * @extends {Error} */ class Exception extends Error { /** * @description: Creates an instance of Exception. * @param {string} message err message * @param {number} code err code * @param {number} status http status * @param {string} stack err stack * @param {Span} span opentracing span * @return {Exception} */ constructor(message, code, status, stack, span) { super(message); this.type = 'Exception'; this.status = 500; this.code = 1; this.timestamp = Date.now(); this.setCode(code); this.setStatus(status); this.setStack(stack); this.span = span; // 确保错误名称正确 this.name = this.constructor.name; } /** * Set error code with validation * * @param {number} code - Error code * @returns {Exception} This instance for chaining */ setCode(code) { if (code !== undefined && Helper$1.isNumber(code) && code >= 0) { this.code = Math.floor(code); // 确保是整数 } return this; } /** * Set HTTP status with validation * * @param {number} status - HTTP status code * @returns {Exception} This instance for chaining */ setStatus(status) { if (status !== undefined && Helper$1.isNumber(status) && status >= 100 && status < 600) { this.status = Math.floor(status); // 确保是整数 } return this; } /** * Set error message with validation * * @param {string} message - Error message * @returns {Exception} This instance for chaining */ setMessage(message) { if (message && typeof message === 'string') { this.message = message.trim(); } return this; } /** * Set stack trace with validation * * @param {string} stack - Stack trace * @returns {Exception} This instance for chaining */ setStack(stack) { if (stack && typeof stack === 'string') { const config = getExceptionConfig(); this.stack = config.maxStackLength && stack.length > config.maxStackLength ? stack.substring(0, config.maxStackLength) + '...[truncated]' : stack; } return this; } /** * Set OpenTelemetry span * * @param {Span} span - OpenTelemetry span * @returns {Exception} This instance for chaining */ setSpan(span) { if (span) { this.span = span; } return this; } /** * Set error context * * @param {Partial<ErrorContext>} context - Error context * @returns {Exception} This instance for chaining */ setContext(context) { this.context = { ...this.context, ...context }; return this; } /** * @description: Default exception handler * @param {KoattyContext} ctx * @return {Promise<any>} */ async handler(ctx) { try { // 设置响应状态 ctx.status = this.status || ctx.status; // 设置错误上下文 this.setContext({ requestId: ctx.requestId, path: ctx.originalPath || '/', method: ctx.method, userAgent: ctx.get('User-Agent'), startTime: ctx.startTime, endTime: Date.now(), duration: Date.now() - ctx.startTime }); // 记录日志 this.log(ctx); // 设置响应类型 ctx.type = ctx.encoding !== false ? `application/json; charset=${ctx.encoding}` : 'application/json'; return this.output(ctx); } catch (error) { DefaultLogger.Error('Exception handler failed:', error); // 降级处理 - 返回基本错误响应 return this.fallbackOutput(ctx, error); } } /** * @description: 降级输出处理 * @param {KoattyContext} ctx * @param {unknown} _error * @return {any} */ fallbackOutput(ctx, _error) { const isGrpc = ctx.protocol === 'grpc'; const isWebSocket = ctx.protocol === 'ws' || ctx.protocol === 'wss'; const fallbackResponse = { code: 500, message: "Internal Server Error", data: null }; if (isGrpc) { return ctx.rpc.callback({ code: 13, // INTERNAL details: JSON.stringify(fallbackResponse) }, null); } const body = JSON.stringify(fallbackResponse); ctx.length = Buffer.byteLength(body); if (isWebSocket) { return ctx.websocket.send(body); } return ctx.res.end(body); } /** * @description: 结构化日志记录 * @param {KoattyContext} ctx * @return {void} */ log(ctx) { const config = getExceptionConfig(); const logData = { timestamp: new Date().toISOString(), level: 'ERROR', type: this.type, message: this.message, code: this.code, status: this.status, context: this.context || { requestId: ctx.requestId, path: ctx.originalPath || '/', method: ctx.method, startTime: ctx.startTime, duration: Date.now() - ctx.startTime, endTime: Date.now() }, ...(config.enableStackTrace && this.stack && { stack: this.stack }) }; // 根据配置决定日志格式 const logMessage = config.logFormat === 'json' ? JSON.stringify(logData, null, 2) : this.formatTextLog(logData); DefaultLogger.Error(logMessage); // 更新链路追踪信息 this.updateSpan(ctx); } /** * Format log as text * * @param {any} logData - Log data object * @returns {string} Formatted text log */ formatTextLog(logData) { const { timestamp, level, type, message, code, status, context, stack } = logData; let log = `[${timestamp}] ${level}: ${type} - ${message} (code: ${code}, status: ${status})`; if (context) { log += `\n Context: ${JSON.stringify(context)}`; } if (stack) { log += `\n Stack: ${stack}`; } return log; } /** * Update OpenTelemetry span with error information * * @param {KoattyContext} ctx - Koatty context */ updateSpan(ctx) { if (this.span) { this.span.setStatus({ code: SpanStatusCode.ERROR, message: this.message }); this.span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, ctx.status); this.span.setAttribute(SemanticAttributes.HTTP_METHOD, ctx.method); this.span.setAttribute(SemanticAttributes.HTTP_URL, ctx.url); this.span.setAttribute('error.code', this.code); this.span.setAttribute('error.type', this.type); } } /** * @description: 输出处理 * @param {KoattyContext} ctx * @return {any} */ output(ctx) { const config = getExceptionConfig(); const isGrpc = ctx.protocol === 'grpc'; const isWebSocket = ctx.protocol === 'ws' || ctx.protocol === 'wss'; // 使用自定义格式或默认格式 const responseBody = config.customErrorFormat ? config.customErrorFormat(this) : this.message || ""; if (isGrpc) { if (this.code < 2) { this.code = StatusCodeConvert(ctx.status); } return ctx.rpc.callback({ code: this.code, details: JSON.stringify(ctx.body || responseBody) }, null); } const body = JSON.stringify(Output.fail(responseBody, ctx.body || '', this.code)); ctx.length = Buffer.byteLength(body); if (isWebSocket) { return ctx.websocket.send(body); } return ctx.res.end(body); } /** * Convert exception to plain object * * @returns {object} Plain object representation */ toJSON() { const config = getExceptionConfig(); return { type: this.type, name: this.name, message: this.message, code: this.code, status: this.status, timestamp: this.timestamp, context: this.context, ...(config.enableStackTrace && this.stack && { stack: this.stack }) }; } } /* * @Description: * @Usage: * @Author: richen * @Date: 2022-02-10 17:40:55 * @LastEditTime: 2024-02-01 10:48:46 */ const PREVENT_NEXT_PROCESS = 'PREVENT_NEXT_PROCESS'; /** * Prevent next process * * @returns {Promise<never>} */ function prevent() { return Promise.reject(new Error(PREVENT_NEXT_PROCESS)); } /** * Check if the error is a predefined exception * * @param {unknown} err - The error to check * @returns {err is Exception} Type guard for Exception */ const isException = (err) => { if (err instanceof Exception) { return true; } // 检查是否具有 Exception 的特征 return !!(err && typeof err === 'object' && 'type' in err && err.type === "Exception" && 'message' in err && 'code' in err && 'status' in err); }; /** * Create a safe error object from unknown input * * @param {unknown} err - The error input * @returns {Error} Safe error object */ function toSafeError(err) { if (Helper.isError(err)) { return err; } if (isException(err)) { return err; } if (typeof err === 'string') { return new Error(err); } if (typeof err === 'number') { return new Error(String(err)); } if (typeof err === 'boolean') { return new Error(String(err)); } if (err && typeof err === 'object' && 'message' in err) { return new Error(String(err.message)); } return new Error('Unknown error occurred'); } /** * Check if an error is a network error * * @param {unknown} err - The error to check * @returns {boolean} True if it's a network error */ function isNetworkError(err) { if (!Helper.isError(err)) { return false; } const networkErrorCodes = ['ECONNREFUSED', 'ENOTFOUND', 'ETIMEDOUT', 'ECONNRESET']; return networkErrorCodes.includes(err.code); } export { CommonErrorCode, Exception, ExceptionHandler, GrpcStatusCode, GrpcStatusCodeMap, HttpStatusCode, HttpStatusCodeMap, Output, StatusCodeConvert, getExceptionConfig, isException, isNetworkError, prevent, setExceptionConfig, toSafeError };