Merge pull request #25606 from overleaf/em-o-error-types

OError: handle unknown error types
GitOrigin-RevId: a52dd92a4f95cd738556612599805f41f24de69f
This commit is contained in:
Eric Mc Sween
2025-05-15 07:10:30 -04:00
committed by Copybot
parent 966cea3d8b
commit 2dd054d602
3 changed files with 40 additions and 10 deletions

View File

@@ -1,20 +1,34 @@
// @ts-check
/**
* Light-weight helpers for handling JavaScript Errors in node.js and the
* browser.
*/
class OError extends Error {
/**
* The error that is the underlying cause of this error
*
* @type {unknown}
*/
cause
/**
* List of errors encountered as the callback chain is unwound
*
* @type {TaggedError[] | undefined}
*/
_oErrorTags
/**
* @param {string} message as for built-in Error
* @param {Object} [info] extra data to attach to the error
* @param {Error} [cause] the internal error that caused this error
* @param {unknown} [cause] the internal error that caused this error
*/
constructor(message, info, cause) {
super(message)
this.name = this.constructor.name
if (info) this.info = info
if (cause) this.cause = cause
/** @private @type {Array<TaggedError> | undefined} */
this._oErrorTags // eslint-disable-line
}
/**
@@ -31,7 +45,7 @@ class OError extends Error {
/**
* Wrap the given error, which caused this error.
*
* @param {Error} cause the internal error that caused this error
* @param {unknown} cause the internal error that caused this error
* @return {this}
*/
withCause(cause) {
@@ -65,13 +79,16 @@ class OError extends Error {
* }
* }
*
* @param {Error} error the error to tag
* @template {unknown} E
* @param {E} error the error to tag
* @param {string} [message] message with which to tag `error`
* @param {Object} [info] extra data with wich to tag `error`
* @return {Error} the modified `error` argument
* @return {E} the modified `error` argument
*/
static tag(error, message, info) {
const oError = /** @type{OError} */ (error)
const oError = /** @type {{ _oErrorTags: TaggedError[] | undefined }} */ (
error
)
if (!oError._oErrorTags) oError._oErrorTags = []
@@ -102,7 +119,7 @@ class OError extends Error {
*
* If an info property is repeated, the last one wins.
*
* @param {Error | null | undefined} error any error (may or may not be an `OError`)
* @param {unknown} error any error (may or may not be an `OError`)
* @return {Object}
*/
static getFullInfo(error) {
@@ -129,7 +146,7 @@ class OError extends Error {
* Return the `stack` property from `error`, including the `stack`s for any
* tagged errors added with `OError.tag` and for any `cause`s.
*
* @param {Error | null | undefined} error any error (may or may not be an `OError`)
* @param {unknown} error any error (may or may not be an `OError`)
* @return {string}
*/
static getFullStack(error) {
@@ -143,7 +160,7 @@ class OError extends Error {
stack += `\n${oError._oErrorTags.map(tag => tag.stack).join('\n')}`
}
const causeStack = oError.cause && OError.getFullStack(oError.cause)
const causeStack = OError.getFullStack(oError.cause)
if (causeStack) {
stack += '\ncaused by:\n' + indent(causeStack)
}

View File

@@ -268,6 +268,11 @@ describe('utils', function () {
expect(OError.getFullInfo(null)).to.deep.equal({})
})
it('works when given a string', function () {
const err = 'not an error instance'
expect(OError.getFullInfo(err)).to.deep.equal({})
})
it('works on a normal error', function () {
const err = new Error('foo')
expect(OError.getFullInfo(err)).to.deep.equal({})

View File

@@ -35,6 +35,14 @@ describe('OError', function () {
expect(err2.cause.message).to.equal('cause 2')
})
it('accepts non-Error causes', function () {
const err1 = new OError('foo', {}, 'not-an-error')
expect(err1.cause).to.equal('not-an-error')
const err2 = new OError('foo').withCause('not-an-error')
expect(err2.cause).to.equal('not-an-error')
})
it('handles a custom error type with a cause', function () {
function doSomethingBadInternally() {
throw new Error('internal error')