243 lines
7.1 KiB
JavaScript
243 lines
7.1 KiB
JavaScript
/*---------------------------------------------------------------------------------------------
|
|
* 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 {
|
|
unexpectedErrorHandler;
|
|
listeners;
|
|
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);
|
|
};
|
|
}
|
|
addListener(listener) {
|
|
this.listeners.push(listener);
|
|
return () => {
|
|
this._removeListener(listener);
|
|
};
|
|
}
|
|
emit(e) {
|
|
this.listeners.forEach((listener) => {
|
|
listener(e);
|
|
});
|
|
}
|
|
_removeListener(listener) {
|
|
this.listeners.splice(this.listeners.indexOf(listener), 1);
|
|
}
|
|
setUnexpectedErrorHandler(newUnexpectedErrorHandler) {
|
|
this.unexpectedErrorHandler = newUnexpectedErrorHandler;
|
|
}
|
|
getUnexpectedErrorHandler() {
|
|
return this.unexpectedErrorHandler;
|
|
}
|
|
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();
|
|
/** @skipMangle */
|
|
export function setUnexpectedErrorHandler(newUnexpectedErrorHandler) {
|
|
errorHandler.setUnexpectedErrorHandler(newUnexpectedErrorHandler);
|
|
}
|
|
/**
|
|
* Returns if the error is a SIGPIPE error. SIGPIPE errors should generally be
|
|
* logged at most once, to avoid a loop.
|
|
*
|
|
* @see https://github.com/microsoft/vscode-remote-release/issues/6481
|
|
*/
|
|
export function isSigPipeError(e) {
|
|
if (!e || typeof e !== 'object') {
|
|
return false;
|
|
}
|
|
const cast = e;
|
|
return cast.code === 'EPIPE' && cast.syscall?.toUpperCase() === 'WRITE';
|
|
}
|
|
/**
|
|
* This function should only be called with errors that indicate a bug in the product.
|
|
* E.g. buggy extensions/invalid user-input/network issues should not be able to trigger this code path.
|
|
* If they are, this indicates there is also a bug in the product.
|
|
*/
|
|
export function onBugIndicatingError(e) {
|
|
errorHandler.onUnexpectedError(e);
|
|
return undefined;
|
|
}
|
|
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, cause } = error;
|
|
const stack = error.stacktrace || error.stack;
|
|
return {
|
|
$isError: true,
|
|
name,
|
|
message,
|
|
stack,
|
|
noTelemetry: ErrorNoTelemetry.isErrorNoTelemetry(error),
|
|
cause: cause ? transformErrorForSerialization(cause) : undefined,
|
|
code: error.code
|
|
};
|
|
}
|
|
// return as is
|
|
return error;
|
|
}
|
|
export function transformErrorFromSerialization(data) {
|
|
let error;
|
|
if (data.noTelemetry) {
|
|
error = new ErrorNoTelemetry();
|
|
}
|
|
else {
|
|
error = new Error();
|
|
error.name = data.name;
|
|
}
|
|
error.message = data.message;
|
|
error.stack = data.stack;
|
|
if (data.code) {
|
|
error.code = data.code;
|
|
}
|
|
if (data.cause) {
|
|
error.cause = transformErrorFromSerialization(data.cause);
|
|
}
|
|
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 ReadonlyError extends TypeError {
|
|
constructor(name) {
|
|
super(name ? `${name} is read-only and cannot be changed` : 'Cannot change read-only property');
|
|
}
|
|
}
|
|
export function getErrorMessage(err) {
|
|
if (!err) {
|
|
return 'Error';
|
|
}
|
|
if (err.message) {
|
|
return err.message;
|
|
}
|
|
if (err.stack) {
|
|
return err.stack.split('\n')[0];
|
|
}
|
|
return String(err);
|
|
}
|
|
export class NotImplementedError extends Error {
|
|
constructor(message) {
|
|
super('NotImplemented');
|
|
if (message) {
|
|
this.message = message;
|
|
}
|
|
}
|
|
}
|
|
export class NotSupportedError extends Error {
|
|
constructor(message) {
|
|
super('NotSupported');
|
|
if (message) {
|
|
this.message = message;
|
|
}
|
|
}
|
|
}
|
|
export class ExpectedError extends Error {
|
|
isExpected = true;
|
|
}
|
|
/**
|
|
* Error that when thrown won't be logged in telemetry as an unhandled error.
|
|
*/
|
|
export class ErrorNoTelemetry extends Error {
|
|
name;
|
|
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.
|
|
// debugger;
|
|
}
|
|
}
|
|
//# sourceMappingURL=errors.js.map
|