From 7fc28988cfb2e3b74de4e04d8709efd93dc02ef0 Mon Sep 17 00:00:00 2001 From: fregante Date: Sat, 4 Jan 2025 12:43:57 +0100 Subject: [PATCH] Tighten Error determination (#101) --- index.js | 9 +++++---- test.js | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 376711a..d32d91a 100644 --- a/index.js +++ b/index.js @@ -196,15 +196,16 @@ export function deserializeError(value, options = {}) { export function isErrorLike(value) { return Boolean(value) && typeof value === 'object' - && 'name' in value - && 'message' in value - && 'stack' in value; + && typeof value.name === 'string' + && typeof value.message === 'string' + && typeof value.stack === 'string'; } +// Used as a weak check for immediately-passed objects, whereas `isErrorLike` is used for nested values to avoid bad detection function isMinimumViableSerializedError(value) { return Boolean(value) && typeof value === 'object' - && 'message' in value + && typeof value.message === 'string' && !Array.isArray(value); } diff --git a/test.js b/test.js index 2da916f..5f31174 100644 --- a/test.js +++ b/test.js @@ -2,7 +2,12 @@ import {Buffer} from 'node:buffer'; import Stream from 'node:stream'; import test from 'ava'; import errorConstructors from './error-constructors.js'; -import {serializeError, deserializeError, isErrorLike} from './index.js'; +import { + serializeError, + deserializeError, + isErrorLike, + NonError, +} from './index.js'; function deserializeNonError(t, value) { const deserialized = deserializeError(value); @@ -351,6 +356,44 @@ test('should deserialize properties up to `Options.maxDepth` levels deep', t => t.deepEqual(levelThree, error); }); +test('should ignore invalid error-like objects', t => { + const errorLike = { + name: 'Error', + message: 'Some error message', + }; + + const nonErrorLike = { + name: 'Error', + message: (new class Message {}('Bottle')), + }; + + t.true(deserializeError(errorLike) instanceof Error); + t.true(deserializeError(nonErrorLike) instanceof NonError); +}); + +test('should ignore nested invalid error-like objects', t => { + const errorLike = { + message: 'Base', + nested: { + name: 'Error', + message: 'Some error message', + stack: 'at :1:13', + }, + }; + + const nonErrorLike = { + message: 'Base', + nested: { + name: 'Error', + message: (new class Message {}('Bottle')), + stack: 'at :1:13', + }, + }; + + t.true(deserializeError(errorLike).nested instanceof Error); + t.false(deserializeError(nonErrorLike).nested instanceof Error); +}); + test('should serialize Date as ISO string', t => { const date = {date: new Date(0)}; const serialized = serializeError(date);