UNPKG

@lbu/stdlib

Version:

All kinds of utility functions

172 lines (157 loc) 4 kB
import { inspect } from "util"; import { isNil } from "./lodash.js"; export class AppError extends Error { /** * @param {string} key * @param {number} status * @param {object} [info={}] * @param {Error} [originalError] */ constructor(key, status, info, originalError) { super(); this.key = key; this.status = status; this.info = info || {}; this.originalError = originalError; Object.setPrototypeOf(this, AppError.prototype); if ( isNil(key) || isNil(status) || typeof status !== "number" || typeof key !== "string" ) { return AppError.serverError( { appErrorConstructParams: { key, status, }, }, this, ); } } /** * @param {*} value * @returns {boolean} */ static instanceOf(value) { return ( value && typeof value.key === "string" && typeof value.status === "number" && !!value.info ); } /** * @param {object} [info={}] * @param {Error} [error] * @returns {AppError} */ static notFound(info = {}, error = undefined) { return new AppError("error.server.notFound", 404, info, error); } /** * @param {object} [info={}] * @param {Error} [error] * @returns {AppError} */ static notImplemented(info = {}, error = undefined) { return new AppError("error.server.notImplemented", 405, info, error); } /** * @param {object} [info={}] * @param {Error} [error] * @returns {AppError} */ static serverError(info = {}, error = undefined) { return new AppError("error.server.internal", 500, info, error); } /** * @param {string} key * @param {object} [info={}] * @param {Error} [error] * @returns {AppError} */ static validationError(key, info = {}, error = undefined) { return new AppError(key, 400, info, error); } /** * Format any error skipping the stack automatically for nested errors * * @param {AppError|Error} e * @param {boolean} [skipStack=false] * @returns {object} */ static format(e, skipStack = false) { let stack; if (skipStack) { stack = []; } else { stack = (e?.stack ?? "").split("\n").map((it) => it.trim()); // Remove first element as this is the Error name stack.shift(); } if (AppError.instanceOf(e)) { return { key: e.key, status: e.status, info: e.info, stack, originalError: e.originalError ? AppError.format(e.originalError, true) : undefined, }; } else if (e.name === "PostgresError") { return { name: e.name, message: e.message, postgres: { severity: e?.severity, code: e?.code, position: e?.position, routine: e?.routine, }, stack, }; } else if (e.isAxiosError) { return { name: e.name, message: e.message, axios: { requestPath: e.request?.path, requestMethod: e.request?.method, responseStatus: e.response?.status, responseHeaders: e.response?.headers, responseBody: e.response?.data, }, stack, }; } else if (typeof e.toJSON === "function") { const result = e.toJSON(); result.stack = stack; return result; } // Any unhandled case return { name: e.name, message: e.message, stack, }; } /** * Use AppError#format when AppError is passed to console.log / console.error. * This works because it uses `util.inspect` under the hood. * Util#inspect checks if the Symbol `util.inspect.custom` is available. */ [inspect.custom]() { return AppError.format(this); } /** * Use AppError#format when AppError is passed to JSON.stringify(). * This is used in the lbu insight logger in production mode. */ toJSON() { return AppError.format(this); } }