import deepCopy from "@stdlib/utils-copy"; import { GhostError } from "./GhostError"; import * as errors from "./errors"; const errorsWithBase = { ...errors, GhostError }; const _private = { serialize(err) { try { return { id: err.id, status: err.statusCode, code: err.code || err.errorType, title: err.name, detail: err.message, meta: { context: err.context, help: err.help, errorDetails: err.errorDetails, level: err.level, errorType: err.errorType } }; } catch (error) { return { detail: "Something went wrong." }; } }, deserialize(obj) { return { id: obj.id, message: obj.detail || obj.error_description || obj.message, statusCode: obj.status, code: obj.code || obj.error, level: obj.meta && obj.meta.level, help: obj.meta && obj.meta.help, context: obj.meta && obj.meta.context }; }, /** * @description Serialize error instance into oauth format. * * @see https://tools.ietf.org/html/rfc6749#page-45 * * To not loose any error data when sending errors between internal services, we use the suggested OAuth properties and add ours as well. */ OAuthSerialize(err) { const matchTable = { [errors.NoPermissionError.name]: "access_denied", [errors.MaintenanceError.name]: "temporarily_unavailable", [errors.BadRequestError.name]: "invalid_request", [errors.ValidationError.name]: "invalid_request", default: "server_error" }; const { detail, code, ...properties } = _private.serialize(err); return { error: err.code || matchTable[err.name] || "server_error", error_description: err.message, ...properties }; }, /** * @description Deserialize oauth error format into GhostError instance. * @constructor */ OAuthDeserialize(errorFormat) { try { return new errorsWithBase[errorFormat.title || errorFormat.name || errors.InternalServerError.name](_private.deserialize(errorFormat)); } catch (err) { return new errors.InternalServerError({ errorType: errorFormat.title || errorFormat.name, ..._private.deserialize(errorFormat) }); } }, /** * @description Serialize GhostError instance into jsonapi.org format. * @param err * @return {Object} */ JSONAPISerialize(err) { const errorFormat = { errors: [_private.serialize(err)] }; errorFormat.errors[0].source = {}; if (err.property) { errorFormat.errors[0].source.pointer = "/data/attributes/" + err.property; } return errorFormat; }, /** * @description Deserialize JSON api format into GhostError instance. */ JSONAPIDeserialize(errorFormat) { errorFormat = errorFormat.errors && errorFormat.errors[0] || {}; let internalError; try { internalError = new errorsWithBase[errorFormat.title || errorFormat.name || errors.InternalServerError.name](_private.deserialize(errorFormat)); } catch (err) { internalError = new errors.InternalServerError({ errorType: errorFormat.title || errorFormat.name, ..._private.deserialize(errorFormat) }); } if (errorFormat.source && errorFormat.source.pointer) { internalError.property = errorFormat.source.pointer.split("/")[3]; } return internalError; } }; function serialize(err, options) { options = options || { format: "jsonapi" }; let errorFormat = {}; try { if (options.format === "jsonapi") { errorFormat = _private.JSONAPISerialize(err); } else { errorFormat = _private.OAuthSerialize(err); } } catch (error) { errorFormat.message = "Something went wrong."; } return errorFormat; } ; function deserialize(errorFormat) { let internalError = {}; if (errorFormat.errors) { internalError = _private.JSONAPIDeserialize(errorFormat); } else { internalError = _private.OAuthDeserialize(errorFormat); } return internalError; } ; function prepareStackForUser(error) { const stackbits = error.stack?.split(/\n/) || []; const hideStack = "hideStack" in error && error.hideStack; if (hideStack) { stackbits.splice(1, stackbits.length - 1); } else { stackbits.splice(1, 0, `Stack Trace:`); } if ("help" in error && error.help) { stackbits.splice(1, 0, `${error.help}`); } if ("context" in error && error.context) { stackbits.splice(1, 0, `${error.context}`); } const errorClone = deepCopy(error); errorClone.stack = stackbits.join("\n"); return errorClone; } ; function isGhostError(err) { const errorName = GhostError.name; const legacyErrorName = "IgnitionError"; const recursiveIsGhostError = function recursiveIsGhostError2(obj) { if (!obj || !obj.name) { return false; } if (obj.name === errorName || obj.name === legacyErrorName) { return true; } return recursiveIsGhostError2(Object.getPrototypeOf(obj)); }; return recursiveIsGhostError(err.constructor); } ; export { deserialize, isGhostError, prepareStackForUser, serialize };