UNPKG

@metamask/rpc-errors

Version:
172 lines 6.07 kB
import { hasProperty, isValidJson, isObject, isJsonRpcError } from "@metamask/utils"; import { errorCodes, errorValues } from "./error-constants.mjs"; const FALLBACK_ERROR_CODE = errorCodes.rpc.internal; const FALLBACK_MESSAGE = 'Unspecified error message. This is a bug, please report it.'; const FALLBACK_ERROR = { code: FALLBACK_ERROR_CODE, message: getMessageFromCode(FALLBACK_ERROR_CODE), }; export const JSON_RPC_SERVER_ERROR_MESSAGE = 'Unspecified server error.'; /** * Gets the message for a given code, or a fallback message if the code has * no corresponding message. * * @param code - The error code. * @param fallbackMessage - The fallback message to use if the code has no * corresponding message. * @returns The message for the given code, or the fallback message if the code * has no corresponding message. */ export function getMessageFromCode(code, fallbackMessage = FALLBACK_MESSAGE) { if (isValidCode(code)) { const codeString = code.toString(); if (hasProperty(errorValues, codeString)) { return errorValues[codeString].message; } if (isJsonRpcServerError(code)) { return JSON_RPC_SERVER_ERROR_MESSAGE; } } return fallbackMessage; } /** * Returns whether the given code is valid. * A code is valid if it is an integer. * * @param code - The error code. * @returns Whether the given code is valid. */ export function isValidCode(code) { return Number.isInteger(code); } /** * Serializes the given error to an Ethereum JSON RPC-compatible error object. * If the given error is not fully compatible, it will be preserved on the * returned object's data.cause property. * * @param error - The error to serialize. * @param options - Options bag. * @param options.fallbackError - The error to return if the given error is * not compatible. Should be a JSON-serializable value. * @param options.shouldIncludeStack - Whether to include the error's stack * on the returned object. * @param options.shouldPreserveMessage - Whether to preserve the error's * message if the fallback error is used. * @returns The serialized error. */ export function serializeError(error, { fallbackError = FALLBACK_ERROR, shouldIncludeStack = true, shouldPreserveMessage = true, } = {}) { if (!isJsonRpcError(fallbackError)) { throw new Error('Must provide fallback error with integer number code and string message.'); } const serialized = buildError(error, fallbackError, shouldPreserveMessage); if (!shouldIncludeStack) { delete serialized.stack; } return serialized; } /** * Construct a JSON-serializable object given an error and a JSON-serializable `fallbackError` * * @param error - The error in question. * @param fallbackError - A JSON-serializable fallback error. * @param shouldPreserveMessage - Whether to preserve the error's message if the fallback * error is used. * @returns A JSON-serializable error object. */ function buildError(error, fallbackError, shouldPreserveMessage) { // If an error specifies a `serialize` function, we call it and return the result. if (error && typeof error === 'object' && 'serialize' in error && typeof error.serialize === 'function') { return error.serialize(); } if (isJsonRpcError(error)) { return error; } const originalMessage = getOriginalMessage(error); // If the error does not match the JsonRpcError type, use the fallback error, but try to include the original error as `cause`. const cause = serializeCause(error); const fallbackWithCause = { ...fallbackError, ...(shouldPreserveMessage && originalMessage && { message: originalMessage }), data: { cause }, }; return fallbackWithCause; } /** * Attempts to extract the original `message` property from an error value of uncertain shape. * * @param error - The error in question. * @returns The original message, if it exists and is a non-empty string. */ function getOriginalMessage(error) { if (isObject(error) && hasProperty(error, 'message') && typeof error.message === 'string' && error.message.length > 0) { return error.message; } return undefined; } /** * Check if the given code is a valid JSON-RPC server error code. * * @param code - The error code. * @returns Whether the given code is a valid JSON-RPC server error code. */ function isJsonRpcServerError(code) { return code >= -32099 && code <= -32000; } /** * Serializes an unknown error to be used as the `cause` in a fallback error. * * @param error - The unknown error. * @returns A JSON-serializable object containing as much information about the original error as possible. */ export function serializeCause(error) { if (Array.isArray(error)) { return error.map((entry) => { if (isValidJson(entry)) { return entry; } else if (isObject(entry)) { return serializeObject(entry); } return null; }); } else if (isObject(error)) { return serializeObject(error); } if (isValidJson(error)) { return error; } return null; } /** * Extracts all JSON-serializable properties from an object. * * @param object - The object in question. * @returns An object containing all the JSON-serializable properties. */ function serializeObject(object) { return Object.getOwnPropertyNames(object).reduce((acc, key) => { const value = object[key]; if (isValidJson(value)) { acc[key] = value; } return acc; }, {}); } /** * Returns true if supplied error data has a usable `cause` property; false otherwise. * * @param data - Optional data to validate. * @returns Whether cause property is present and an object. */ export function dataHasCause(data) { return isObject(data) && hasProperty(data, 'cause') && isObject(data.cause); } //# sourceMappingURL=utils.mjs.map