diff --git a/@types/index.d.ts b/@types/index.d.ts index cb2ac41..832d9ed 100644 --- a/@types/index.d.ts +++ b/@types/index.d.ts @@ -26,8 +26,13 @@ export interface IProviderCustomErrorOptions extends IErrorOptions { message: string, } +interface SerializeErrorOptions { + fallbackError?: object, + shouldSerializeStack?: boolean, +} + export interface ISerializeError { - (error: any, fallbackError?: DefaultError): IEthereumRpcError + (error: any, options?: SerializeErrorOptions): IEthereumRpcError } export interface IGetMessageFromCode { diff --git a/src/utils.js b/src/utils.js index e4d080e..23f2b16 100644 --- a/src/utils.js +++ b/src/utils.js @@ -70,14 +70,19 @@ function isValidCode (code) { * Merely copies the given error's values if it is already compatible. * If the given error is not fully compatible, it will be preserved on the * returned object's data.originalError property. - * Adds a 'stack' property if it exists on the given error. * * @param {any} error - The error to serialize. - * @param {object} fallbackError - The custom fallback error values if the - * given error is invalid. - * @return {object} A standardized error object. + * @param {Object} [options] - An options object. + * @param {Object} [options.fallbackError] - The custom fallback error values if + * the given error is invalid. + * @param {boolean} [options.shouldIncludeStack] - Whether the 'stack' property + * of the given error should be included on the serialized error, if present. + * @return {Object} A standardized, plain error object. */ -function serializeError (error, fallbackError = FALLBACK_ERROR) { +function serializeError ( + error, + { fallbackError = FALLBACK_ERROR, shouldIncludeStack = false } = {}, +) { if ( !fallbackError || @@ -85,7 +90,7 @@ function serializeError (error, fallbackError = FALLBACK_ERROR) { typeof fallbackError.message !== 'string' ) { throw new Error( - 'fallbackError must contain integer number code and string message.', + 'Must provide fallback error with integer number code and string message.', ) } @@ -119,7 +124,7 @@ function serializeError (error, fallbackError = FALLBACK_ERROR) { serialized.data = { originalError: assignOriginalError(error) } } - if (error && error.stack) { + if (shouldIncludeStack && error && typeof error.stack === 'string') { serialized.stack = error.stack } return serialized diff --git a/test/serializeError.js b/test/serializeError.js index 6953ff8..aebb331 100644 --- a/test/serializeError.js +++ b/test/serializeError.js @@ -22,11 +22,14 @@ const invalidError7 = { code: 34, message: dummyMessage, data: { ...dummyData } const validError0 = { code: 4001, message: dummyMessage } const validError1 = { code: 4001, message: dummyMessage, data: { ...dummyData } } const validError2 = ethErrors.rpc.parse() +delete validError2.stack const validError3 = ethErrors.rpc.parse(dummyMessage) +delete validError3.stack const validError4 = ethErrors.rpc.parse({ message: dummyMessage, data: { ...dummyData }, }) +delete validError4.stack test('invalid error: non-object', (t) => { const result = serializeError(invalidError0) @@ -156,6 +159,25 @@ test('invalid error: invalid code with string message', (t) => { t.end() }) +test('invalid error: invalid code, no message, custom fallback', (t) => { + const result = serializeError( + invalidError2, + { fallbackError: { code: rpcCodes.methodNotFound, message: 'foo' } }, + ) + t.ok( + dequal( + result, + { + code: rpcCodes.methodNotFound, + message: 'foo', + data: { originalError: { ...invalidError2 } }, + }, + ), + 'serialized error matches expected result', + ) + t.end() +}) + test('valid error: code and message only', (t) => { const result = serializeError(validError0) t.ok( @@ -195,7 +217,6 @@ test('valid error: instantiated error', (t) => { { code: rpcCodes.parse, message: getMessageFromCode(rpcCodes.parse), - stack: validError2.stack, }, ), 'serialized error matches expected result', @@ -211,7 +232,6 @@ test('valid error: instantiated error', (t) => { { code: rpcCodes.parse, message: dummyMessage, - stack: validError3.stack, }, ), 'serialized error matches expected result', @@ -228,7 +248,80 @@ test('valid error: instantiated error with custom message and data', (t) => { code: rpcCodes.parse, message: validError4.message, data: { ...validError4.data }, - stack: validError4.stack, + }, + ), + 'serialized error matches expected result', + ) + t.end() +}) + +test('valid error: message, data, and stack', (t) => { + const result = serializeError({ ...validError1, stack: 'foo' }) + t.ok( + dequal( + result, + { + code: 4001, + message: validError1.message, + data: { ...validError1.data }, + }, + ), + 'serialized error matches expected result', + ) + t.end() +}) + +test('include stack: no stack present', (t) => { + const result = serializeError( + validError1, + { shouldIncludeStack: true }, + ) + t.ok( + dequal( + result, + { + code: 4001, + message: validError1.message, + data: { ...validError1.data }, + }, + ), + 'serialized error matches expected result', + ) + t.end() +}) + +test('include stack: string stack present', (t) => { + const result = serializeError( + { ...validError1, stack: 'foo' }, + { shouldIncludeStack: true }, + ) + t.ok( + dequal( + result, + { + code: 4001, + message: validError1.message, + data: { ...validError1.data }, + stack: 'foo', + }, + ), + 'serialized error matches expected result', + ) + t.end() +}) + +test('include stack: non-string stack present', (t) => { + const result = serializeError( + { ...validError1, stack: 2 }, + { shouldIncludeStack: true }, + ) + t.ok( + dequal( + result, + { + code: 4001, + message: validError1.message, + data: { ...validError1.data }, }, ), 'serialized error matches expected result',