// @flow /* eslint-disable no-continue */ const errorClasses = {} export const createCustomErrorClass = (name: string): Class => { const C = function CustomError(message?: string, fields?: Object) { this.name = name this.message = message || name this.stack = new Error().stack Object.assign(this, fields) } // $FlowFixMe C.prototype = new Error() errorClasses[name] = C // $FlowFixMe we can't easily type a subset of Error for now... return C } // inspired from https://github.com/programble/errio/blob/master/index.js export const deserializeError = (object: mixed): Error => { if (typeof object === 'object' && object) { const constructor = (typeof object.name === 'string' && errorClasses[object.name]) || Error const error = Object.create(constructor.prototype) for (const prop in object) { if (object.hasOwnProperty(prop)) { error[prop] = object[prop] } } if (!error.stack && Error.captureStackTrace) { Error.captureStackTrace(error, deserializeError) } return error } return new Error(String(object)) } // inspired from https://github.com/sindresorhus/serialize-error/blob/master/index.js export const serializeError = (value: mixed) => { if (!value) return value if (typeof value === 'object') { return destroyCircular(value, []) } if (typeof value === 'function') { return `[Function: ${value.name || 'anonymous'}]` } return value } // https://www.npmjs.com/package/destroy-circular function destroyCircular(from: Object, seen) { const to = {} seen.push(from) for (const key of Object.keys(from)) { const value = from[key] if (typeof value === 'function') { continue } if (!value || typeof value !== 'object') { to[key] = value continue } if (seen.indexOf(from[key]) === -1) { to[key] = destroyCircular(from[key], seen.slice(0)) continue } to[key] = '[Circular]' } if (typeof from.name === 'string') { to.name = from.name } if (typeof from.message === 'string') { to.message = from.message } if (typeof from.stack === 'string') { to.stack = from.stack } return to }