From ce7fd0501f0f514f7873e14548f541b0a959866d Mon Sep 17 00:00:00 2001 From: mshanemc Date: Mon, 22 Jul 2024 11:40:23 -0500 Subject: [PATCH 1/3] fix: wrap error-like objects --- src/sfError.ts | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/sfError.ts b/src/sfError.ts index f8fd430db..afbee899b 100644 --- a/src/sfError.ts +++ b/src/sfError.ts @@ -136,17 +136,12 @@ export class SfError extend } const sfError = - err instanceof Error - ? // a basic error with message and name. We make it the cause to preserve any other properties - SfError.create({ - message: err.message, - name: err.name, - cause: err, - }) - : // ok, something was throws that wasn't error or string. Convert it to an Error that preserves the information as the cause and wrap that. - SfError.wrap( - new Error(`SfError.wrap received type ${typeof err} but expects type Error or string`, { cause: err }) - ); + fromBasicError(err) ?? + fromErrorLikeObject(err) ?? + // something was thrown that wasn't error, error-like object or string. Convert it to an Error that preserves the information as the cause and wrap that. + SfError.wrap( + new Error(`SfError.wrap received type ${typeof err} but expects type Error or string`, { cause: err }) + ); // If the original error has a code, use that instead of name. if (hasString(err, 'code')) { @@ -190,3 +185,15 @@ export class SfError extend }; } } + +const fromBasicError = (err: unknown): SfError | undefined => + err instanceof Error ? SfError.create({ message: err.message, name: err.name, cause: err }) : undefined; + +/* an object that is the result of spreading an Error or SfError */ +const fromErrorLikeObject = (err: unknown): SfError | undefined => { + try { + return SfError.create(err as Error); + } catch { + return undefined; + } +}; From 9ff92c45ad42430fc7c44eddf28703558594eebf Mon Sep 17 00:00:00 2001 From: mshanemc Date: Mon, 22 Jul 2024 12:00:10 -0500 Subject: [PATCH 2/3] fix: handle error-like objects --- src/sfError.ts | 6 +++++ test/unit/sfErrorTest.ts | 54 +++++++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/sfError.ts b/src/sfError.ts index afbee899b..6c9728ca3 100644 --- a/src/sfError.ts +++ b/src/sfError.ts @@ -191,6 +191,12 @@ const fromBasicError = (err: unknown): SfError /* an object that is the result of spreading an Error or SfError */ const fromErrorLikeObject = (err: unknown): SfError | undefined => { + if (!err || typeof err !== 'object') { + return undefined; + } + if (!('message' in err)) { + return undefined; + } try { return SfError.create(err as Error); } catch { diff --git a/test/unit/sfErrorTest.ts b/test/unit/sfErrorTest.ts index 755b2088c..eb7a39e2c 100644 --- a/test/unit/sfErrorTest.ts +++ b/test/unit/sfErrorTest.ts @@ -162,21 +162,7 @@ describe('SfError', () => { assert(mySfError.cause instanceof Error); expect(mySfError.cause.cause).to.equal(wrapMe); }); - it('an object', () => { - const wrapMe = { a: 2 }; - const mySfError = SfError.wrap(wrapMe); - expect(mySfError).to.be.an.instanceOf(SfError); - assert(mySfError.cause instanceof Error); - expect(mySfError.cause.cause).to.equal(wrapMe); - }); - it('an object that has a code', () => { - const wrapMe = { a: 2, code: 'foo' }; - const mySfError = SfError.wrap(wrapMe); - expect(mySfError).to.be.an.instanceOf(SfError); - assert(mySfError.cause instanceof Error); - expect(mySfError.cause.cause).to.equal(wrapMe); - expect(mySfError.code).to.equal('foo'); - }); + it('an array', () => { const wrapMe = [1, 5, 6]; const mySfError = SfError.wrap(wrapMe); @@ -191,6 +177,44 @@ describe('SfError', () => { assert(mySfError.cause instanceof Error); expect(mySfError.cause.cause).to.equal(wrapMe); }); + describe('objects', () => { + describe('error-like', () => { + it('an object with only a message', () => { + const wrapMe = { message: 'foo' }; + const mySfError = SfError.wrap(wrapMe); + expect(mySfError).to.be.an.instanceOf(SfError); + expect(mySfError.message).to.equal('foo'); + expect(mySfError.exitCode).to.equal(1); + expect(mySfError.name).to.equal('SfError'); + }); + it('an object with several props and code', () => { + const wrapMe = { message: 'foo', name: 'bar', code: 'baz', exitCode: 100 }; + const mySfError = SfError.wrap(wrapMe); + expect(mySfError).to.be.an.instanceOf(SfError); + expect(mySfError.message).to.equal('foo'); + expect(mySfError.exitCode).to.equal(100); + expect(mySfError.name).to.equal('bar'); + expect(mySfError.code).to.equal('baz'); + }); + }); + describe('not error-like', () => { + it('an object', () => { + const wrapMe = { a: 2 }; + const mySfError = SfError.wrap(wrapMe); + expect(mySfError).to.be.an.instanceOf(SfError); + assert(mySfError.cause instanceof Error); + expect(mySfError.cause.cause).to.equal(wrapMe); + }); + it('an object that has a code', () => { + const wrapMe = { a: 2, code: 'foo' }; + const mySfError = SfError.wrap(wrapMe); + expect(mySfError).to.be.an.instanceOf(SfError); + assert(mySfError.cause instanceof Error); + expect(mySfError.cause.cause).to.equal(wrapMe); + expect(mySfError.code).to.equal('foo'); + }); + }); + }); }); }); From 748342cb881fd17e63dfaabe71363dc7b4c1f5f0 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Mon, 22 Jul 2024 12:21:43 -0500 Subject: [PATCH 3/3] test: empty object for wrap --- test/unit/sfErrorTest.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/unit/sfErrorTest.ts b/test/unit/sfErrorTest.ts index eb7a39e2c..ae0576d5e 100644 --- a/test/unit/sfErrorTest.ts +++ b/test/unit/sfErrorTest.ts @@ -205,6 +205,13 @@ describe('SfError', () => { assert(mySfError.cause instanceof Error); expect(mySfError.cause.cause).to.equal(wrapMe); }); + it('empty object', () => { + const wrapMe = {}; + const mySfError = SfError.wrap(wrapMe); + expect(mySfError).to.be.an.instanceOf(SfError); + assert(mySfError.cause instanceof Error); + expect(mySfError.cause.cause).to.equal(wrapMe); + }); it('an object that has a code', () => { const wrapMe = { a: 2, code: 'foo' }; const mySfError = SfError.wrap(wrapMe);