export interface ErrorListenerCallback { (error: any): void; } // Avoid circular dependency on EventEmitter by implementing a subset of the interface. export class ErrorHandler { private unexpectedErrorHandler: (e: any) => void; private listeners: ErrorListenerCallback[]; constructor() { this.listeners = []; this.unexpectedErrorHandler = function (e: any) { setTimeout(() => { if (e.stack) { throw new Error(e.message + '\n\n' + e.stack); } throw e; }, 0); }; } private emit(e: any): void { this.listeners.forEach((listener) => { listener(e); }); } onUnexpectedError(e: any): void { this.unexpectedErrorHandler(e); this.emit(e); } // For external errors, we don't want the listeners to be called onUnexpectedExternalError(e: any): void { this.unexpectedErrorHandler(e); } } export const errorHandler = new ErrorHandler(); export function onUnexpectedError(e: any): undefined { // ignore errors from cancelled promises if (!isPromiseCanceledError(e)) { errorHandler.onUnexpectedError(e); } return undefined; } export function onUnexpectedExternalError(e: any): undefined { // ignore errors from cancelled promises if (!isPromiseCanceledError(e)) { errorHandler.onUnexpectedExternalError(e); } return undefined; } export interface SerializedError {} export function transformErrorForSerialization(error: Error): SerializedError; export function transformErrorForSerialization(error: any): any; export function transformErrorForSerialization(error: any): any { if (error instanceof Error) { let { name, message } = error; const stack: string = (error).stacktrace || (error).stack; return { $isError: true, name, message, stack, }; } // return as is return error; } const canceledName = 'Canceled'; /** * Checks if the given error is a promise in canceled state */ export function isPromiseCanceledError(error: any): boolean { return ( error instanceof Error && error.name === canceledName && error.message === canceledName ); } /** * Returns an error that signals cancellation. */ export function canceled(): Error { const error = new Error(canceledName); error.name = error.message; return error; } export function illegalArgument(name?: string): Error { if (name) { return new Error(`Illegal argument: ${name}`); } else { return new Error('Illegal argument'); } } export function illegalState(name?: string): Error { if (name) { return new Error(`Illegal state: ${name}`); } else { return new Error('Illegal state'); } }