@metamask/snaps-sdk
Version:
A library containing the core functionality for building MetaMask Snaps
182 lines • 5.36 kB
JavaScript
import { isJsonRpcError, isObject } from "@metamask/utils";
import { getErrorCause, getErrorCode, getErrorData, getErrorMessage, getErrorName, getErrorStack, SNAP_ERROR_CODE, SNAP_ERROR_MESSAGE } from "./internals/index.mjs";
/**
* A generic error which can be thrown by a Snap, without it causing the Snap to
* crash.
*/
export class SnapError extends Error {
#code;
#message;
#data;
#stack;
/**
* Create a new `SnapError`.
*
* @param error - The error to create the `SnapError` from. If this is a
* `string`, it will be used as the error message. If this is an `Error`, its
* `message` property will be used as the error message. If this is a
* `JsonRpcError`, its `message` property will be used as the error message
* and its `code` property will be used as the error code. Otherwise, the
* error will be converted to a string and used as the error message.
* @param data - Additional data to include in the error. This will be merged
* with the error data, if any.
*/
constructor(error, data = {}) {
const message = getErrorMessage(error);
super(message);
this.#message = message;
this.#code = getErrorCode(error);
const mergedData = { ...getErrorData(error), ...data };
if (Object.keys(mergedData).length > 0) {
this.#data = mergedData;
}
this.#stack = super.stack;
}
/**
* The error name.
*
* @returns The error name.
*/
get name() {
return 'SnapError';
}
/**
* The error code.
*
* @returns The error code.
*/
get code() {
return this.#code;
}
/**
* The error message.
*
* @returns The error message.
*/
// This line is covered, but Jest doesn't pick it up for some reason.
/* istanbul ignore next */
get message() {
return this.#message;
}
/**
* Additional data for the error.
*
* @returns Additional data for the error.
*/
get data() {
return this.#data;
}
/**
* The error stack.
*
* @returns The error stack.
*/
// This line is covered, but Jest doesn't pick it up for some reason.
/* istanbul ignore next */
get stack() {
return this.#stack;
}
/**
* Convert the error to a JSON object.
*
* @returns The JSON object.
*/
toJSON() {
return {
code: SNAP_ERROR_CODE,
message: SNAP_ERROR_MESSAGE,
data: {
cause: {
code: this.code,
message: this.message,
stack: this.stack,
...(this.data ? { data: this.data } : {}),
},
},
};
}
/**
* Serialize the error to a JSON object. This is called by
* `@metamask/rpc-errors` when serializing the error.
*
* @returns The JSON object.
*/
serialize() {
return this.toJSON();
}
}
/**
* Get a serialised JSON error from a given error. This is intended to be used
* with `snap_trackError` to convert an error to a JSON object that can be
* tracked by the Sentry instance in the client.
*
* @param error - The error to convert to a JSON error. This can be a string, an
* `Error`, a `JsonRpcError`, or any other type. If it is not a string or an
* `Error`, it will be converted to a string using its `toString()` method.
* @returns A JSON object containing the error message and stack trace, if
* available.
* @example
* try {
* // Some code that may throw an error
* } catch (error) {
* await snap.request({
* method: 'snap_trackError',
* params: {
* error: getJsonError(error),
* },
* });
* }
*/
export function getJsonError(
// TypeScript will narrow this to `unknown`, but we specify all the types for
// clarity.
error) {
if (typeof error === 'string') {
return {
name: 'Error',
message: error,
stack: null,
cause: null,
};
}
if (isJsonRpcError(error)) {
return {
name: 'JsonRpcError',
message: getErrorMessage(error),
stack: getErrorStack(error) ?? getErrorStack(error.data) ?? null,
cause: null,
};
}
const cause = getErrorCause(error);
return {
name: getErrorName(error),
message: getErrorMessage(error),
stack: getErrorStack(error) ?? null,
cause: cause === null ? null : getJsonError(cause),
};
}
/**
* Check if a JSON-RPC error is a `SnapError`.
*
* @param error - The object to check.
* @returns Whether the object is a `SnapError`.
*/
export function isSerializedSnapError(error) {
return error.code === SNAP_ERROR_CODE && error.message === SNAP_ERROR_MESSAGE;
}
/**
* Check if an object is a `SnapError`.
*
* @param error - The object to check.
* @returns Whether the object is a `SnapError`.
*/
export function isSnapError(error) {
if (isObject(error) &&
'serialize' in error &&
typeof error.serialize === 'function') {
const serialized = error.serialize();
return isJsonRpcError(serialized) && isSerializedSnapError(serialized);
}
return false;
}
//# sourceMappingURL=errors.mjs.map