@metamask/rpc-errors
Version:
Ethereum RPC and Provider errors
1 lines • 10.6 kB
Source Map (JSON)
{"version":3,"file":"utils.mjs","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n Json,\n JsonRpcError as SerializedJsonRpcError,\n RuntimeObject,\n} from '@metamask/utils';\nimport {\n hasProperty,\n isValidJson,\n isObject,\n isJsonRpcError,\n} from '@metamask/utils';\n\nimport { errorCodes, errorValues } from './error-constants';\n\n/**\n * A data object, that must be either:\n *\n * - A JSON-serializable object.\n * - An object with a `cause` property that is an error-like value, and any\n * other properties that are JSON-serializable.\n */\nexport type DataWithOptionalCause =\n | Json\n | {\n // Unfortunately we can't use just `Json` here, because all properties of\n // an object with an index signature must be assignable to the index\n // signature's type. So we have to use `Json | unknown` instead.\n [key: string]: Json | unknown;\n cause?: unknown;\n };\n\n/**\n * A data object, that must be either:\n *\n * - A valid DataWithOptionalCause value.\n * - undefined.\n */\nexport type OptionalDataWithOptionalCause = undefined | DataWithOptionalCause;\n\nconst FALLBACK_ERROR_CODE = errorCodes.rpc.internal;\nconst FALLBACK_MESSAGE =\n 'Unspecified error message. This is a bug, please report it.';\nconst FALLBACK_ERROR: SerializedJsonRpcError = {\n code: FALLBACK_ERROR_CODE,\n message: getMessageFromCode(FALLBACK_ERROR_CODE),\n};\n\nexport const JSON_RPC_SERVER_ERROR_MESSAGE = 'Unspecified server error.';\n\ntype ErrorValueKey = keyof typeof errorValues;\n\n/**\n * Gets the message for a given code, or a fallback message if the code has\n * no corresponding message.\n *\n * @param code - The error code.\n * @param fallbackMessage - The fallback message to use if the code has no\n * corresponding message.\n * @returns The message for the given code, or the fallback message if the code\n * has no corresponding message.\n */\nexport function getMessageFromCode(\n code: unknown,\n fallbackMessage: string = FALLBACK_MESSAGE,\n): string {\n if (isValidCode(code)) {\n const codeString = code.toString();\n\n if (hasProperty(errorValues, codeString)) {\n return errorValues[codeString as ErrorValueKey].message;\n }\n\n if (isJsonRpcServerError(code)) {\n return JSON_RPC_SERVER_ERROR_MESSAGE;\n }\n }\n return fallbackMessage;\n}\n\n/**\n * Returns whether the given code is valid.\n * A code is valid if it is an integer.\n *\n * @param code - The error code.\n * @returns Whether the given code is valid.\n */\nexport function isValidCode(code: unknown): code is number {\n return Number.isInteger(code);\n}\n\n/**\n * Serializes the given error to an Ethereum JSON RPC-compatible error object.\n * If the given error is not fully compatible, it will be preserved on the\n * returned object's data.cause property.\n *\n * @param error - The error to serialize.\n * @param options - Options bag.\n * @param options.fallbackError - The error to return if the given error is\n * not compatible. Should be a JSON-serializable value.\n * @param options.shouldIncludeStack - Whether to include the error's stack\n * on the returned object.\n * @param options.shouldPreserveMessage - Whether to preserve the error's\n * message if the fallback error is used.\n * @returns The serialized error.\n */\nexport function serializeError(\n error: unknown,\n {\n fallbackError = FALLBACK_ERROR,\n shouldIncludeStack = true,\n shouldPreserveMessage = true,\n } = {},\n): SerializedJsonRpcError {\n if (!isJsonRpcError(fallbackError)) {\n throw new Error(\n 'Must provide fallback error with integer number code and string message.',\n );\n }\n\n const serialized = buildError(error, fallbackError, shouldPreserveMessage);\n\n if (!shouldIncludeStack) {\n delete serialized.stack;\n }\n\n return serialized;\n}\n\n/**\n * Construct a JSON-serializable object given an error and a JSON-serializable `fallbackError`\n *\n * @param error - The error in question.\n * @param fallbackError - A JSON-serializable fallback error.\n * @param shouldPreserveMessage - Whether to preserve the error's message if the fallback\n * error is used.\n * @returns A JSON-serializable error object.\n */\nfunction buildError(\n error: unknown,\n fallbackError: SerializedJsonRpcError,\n shouldPreserveMessage: boolean,\n): SerializedJsonRpcError {\n // If an error specifies a `serialize` function, we call it and return the result.\n if (\n error &&\n typeof error === 'object' &&\n 'serialize' in error &&\n typeof error.serialize === 'function'\n ) {\n return error.serialize();\n }\n\n if (isJsonRpcError(error)) {\n return error;\n }\n\n const originalMessage = getOriginalMessage(error);\n\n // If the error does not match the JsonRpcError type, use the fallback error, but try to include the original error as `cause`.\n const cause = serializeCause(error);\n const fallbackWithCause = {\n ...fallbackError,\n ...(shouldPreserveMessage &&\n originalMessage && { message: originalMessage }),\n data: { cause },\n };\n\n return fallbackWithCause;\n}\n\n/**\n * Attempts to extract the original `message` property from an error value of uncertain shape.\n *\n * @param error - The error in question.\n * @returns The original message, if it exists and is a non-empty string.\n */\nfunction getOriginalMessage(error: unknown): string | undefined {\n if (\n isObject(error) &&\n hasProperty(error, 'message') &&\n typeof error.message === 'string' &&\n error.message.length > 0\n ) {\n return error.message;\n }\n return undefined;\n}\n\n/**\n * Check if the given code is a valid JSON-RPC server error code.\n *\n * @param code - The error code.\n * @returns Whether the given code is a valid JSON-RPC server error code.\n */\nfunction isJsonRpcServerError(code: number): boolean {\n return code >= -32099 && code <= -32000;\n}\n\n/**\n * Serializes an unknown error to be used as the `cause` in a fallback error.\n *\n * @param error - The unknown error.\n * @returns A JSON-serializable object containing as much information about the original error as possible.\n */\nexport function serializeCause(error: unknown): Json {\n if (Array.isArray(error)) {\n return error.map((entry) => {\n if (isValidJson(entry)) {\n return entry;\n } else if (isObject(entry)) {\n return serializeObject(entry);\n }\n return null;\n });\n } else if (isObject(error)) {\n return serializeObject(error);\n }\n\n if (isValidJson(error)) {\n return error;\n }\n\n return null;\n}\n\n/**\n * Extracts all JSON-serializable properties from an object.\n *\n * @param object - The object in question.\n * @returns An object containing all the JSON-serializable properties.\n */\nfunction serializeObject(object: RuntimeObject): Json {\n return Object.getOwnPropertyNames(object).reduce<Record<string, Json>>(\n (acc, key) => {\n const value = object[key];\n if (isValidJson(value)) {\n acc[key] = value;\n }\n\n return acc;\n },\n {},\n );\n}\n\n/**\n * Returns true if supplied error data has a usable `cause` property; false otherwise.\n *\n * @param data - Optional data to validate.\n * @returns Whether cause property is present and an object.\n */\nexport function dataHasCause(data: unknown): data is {\n [key: string]: Json | unknown;\n cause: object;\n} {\n return isObject(data) && hasProperty(data, 'cause') && isObject(data.cause);\n}\n"]}