/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ // Avoid circular dependency on EventEmitter by implementing a subset of the interface. export class ErrorHandler { constructor() { this.listeners = []; this.unexpectedErrorHandler = function (e) { setTimeout(() => { if (e.stack) { if (ErrorNoTelemetry.isErrorNoTelemetry(e)) { throw new ErrorNoTelemetry(e.message + '\n\n' + e.stack); } throw new Error(e.message + '\n\n' + e.stack); } throw e; }, 0); }; } emit(e) { this.listeners.forEach((listener) => { listener(e); }); } onUnexpectedError(e) { this.unexpectedErrorHandler(e); this.emit(e); } // For external errors, we don't want the listeners to be called onUnexpectedExternalError(e) { this.unexpectedErrorHandler(e); } } export const errorHandler = new ErrorHandler(); export function onUnexpectedError(e) { // ignore errors from cancelled promises if (!isCancellationError(e)) { errorHandler.onUnexpectedError(e); } return undefined; } export function onUnexpectedExternalError(e) { // ignore errors from cancelled promises if (!isCancellationError(e)) { errorHandler.onUnexpectedExternalError(e); } return undefined; } export function transformErrorForSerialization(error) { if (error instanceof Error) { const { name, message } = error; const stack = error.stacktrace || error.stack; return { $isError: true, name, message, stack, noTelemetry: ErrorNoTelemetry.isErrorNoTelemetry(error) }; } // return as is return error; } const canceledName = 'Canceled'; /** * Checks if the given error is a promise in canceled state */ export function isCancellationError(error) { if (error instanceof CancellationError) { return true; } return error instanceof Error && error.name === canceledName && error.message === canceledName; } // !!!IMPORTANT!!! // Do NOT change this class because it is also used as an API-type. export class CancellationError extends Error { constructor() { super(canceledName); this.name = this.message; } } /** * @deprecated use {@link CancellationError `new CancellationError()`} instead */ export function canceled() { const error = new Error(canceledName); error.name = error.message; return error; } export function illegalArgument(name) { if (name) { return new Error(`Illegal argument: ${name}`); } else { return new Error('Illegal argument'); } } export function illegalState(name) { if (name) { return new Error(`Illegal state: ${name}`); } else { return new Error('Illegal state'); } } export class NotSupportedError extends Error { constructor(message) { super('NotSupported'); if (message) { this.message = message; } } } /** * Error that when thrown won't be logged in telemetry as an unhandled error. */ export class ErrorNoTelemetry extends Error { constructor(msg) { super(msg); this.name = 'CodeExpectedError'; } static fromError(err) { if (err instanceof ErrorNoTelemetry) { return err; } const result = new ErrorNoTelemetry(); result.message = err.message; result.stack = err.stack; return result; } static isErrorNoTelemetry(err) { return err.name === 'CodeExpectedError'; } } /** * This error indicates a bug. * Do not throw this for invalid user input. * Only catch this error to recover gracefully from bugs. */ export class BugIndicatingError extends Error { constructor(message) { super(message || 'An unexpected bug occurred.'); Object.setPrototypeOf(this, BugIndicatingError.prototype); // Because we know for sure only buggy code throws this, // we definitely want to break here and fix the bug. // eslint-disable-next-line no-debugger // debugger; } }