diff --git a/src/sfError.ts b/src/sfError.ts index f8fd430db..6c9728ca3 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,21 @@ 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 => { + if (!err || typeof err !== 'object') { + return undefined; + } + if (!('message' in err)) { + return undefined; + } + try { + return SfError.create(err as Error); + } catch { + return undefined; + } +}; diff --git a/test/unit/sfErrorTest.ts b/test/unit/sfErrorTest.ts index 755b2088c..ae0576d5e 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,51 @@ 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('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); + 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'); + }); + }); + }); }); });