errors.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. // Avoid circular dependency on EventEmitter by implementing a subset of the interface.
  6. export class ErrorHandler {
  7. constructor() {
  8. this.listeners = [];
  9. this.unexpectedErrorHandler = function (e) {
  10. setTimeout(() => {
  11. if (e.stack) {
  12. if (ErrorNoTelemetry.isErrorNoTelemetry(e)) {
  13. throw new ErrorNoTelemetry(e.message + '\n\n' + e.stack);
  14. }
  15. throw new Error(e.message + '\n\n' + e.stack);
  16. }
  17. throw e;
  18. }, 0);
  19. };
  20. }
  21. emit(e) {
  22. this.listeners.forEach((listener) => {
  23. listener(e);
  24. });
  25. }
  26. onUnexpectedError(e) {
  27. this.unexpectedErrorHandler(e);
  28. this.emit(e);
  29. }
  30. // For external errors, we don't want the listeners to be called
  31. onUnexpectedExternalError(e) {
  32. this.unexpectedErrorHandler(e);
  33. }
  34. }
  35. export const errorHandler = new ErrorHandler();
  36. export function onUnexpectedError(e) {
  37. // ignore errors from cancelled promises
  38. if (!isCancellationError(e)) {
  39. errorHandler.onUnexpectedError(e);
  40. }
  41. return undefined;
  42. }
  43. export function onUnexpectedExternalError(e) {
  44. // ignore errors from cancelled promises
  45. if (!isCancellationError(e)) {
  46. errorHandler.onUnexpectedExternalError(e);
  47. }
  48. return undefined;
  49. }
  50. export function transformErrorForSerialization(error) {
  51. if (error instanceof Error) {
  52. const { name, message } = error;
  53. const stack = error.stacktrace || error.stack;
  54. return {
  55. $isError: true,
  56. name,
  57. message,
  58. stack,
  59. noTelemetry: ErrorNoTelemetry.isErrorNoTelemetry(error)
  60. };
  61. }
  62. // return as is
  63. return error;
  64. }
  65. const canceledName = 'Canceled';
  66. /**
  67. * Checks if the given error is a promise in canceled state
  68. */
  69. export function isCancellationError(error) {
  70. if (error instanceof CancellationError) {
  71. return true;
  72. }
  73. return error instanceof Error && error.name === canceledName && error.message === canceledName;
  74. }
  75. // !!!IMPORTANT!!!
  76. // Do NOT change this class because it is also used as an API-type.
  77. export class CancellationError extends Error {
  78. constructor() {
  79. super(canceledName);
  80. this.name = this.message;
  81. }
  82. }
  83. /**
  84. * @deprecated use {@link CancellationError `new CancellationError()`} instead
  85. */
  86. export function canceled() {
  87. const error = new Error(canceledName);
  88. error.name = error.message;
  89. return error;
  90. }
  91. export function illegalArgument(name) {
  92. if (name) {
  93. return new Error(`Illegal argument: ${name}`);
  94. }
  95. else {
  96. return new Error('Illegal argument');
  97. }
  98. }
  99. export function illegalState(name) {
  100. if (name) {
  101. return new Error(`Illegal state: ${name}`);
  102. }
  103. else {
  104. return new Error('Illegal state');
  105. }
  106. }
  107. export class NotSupportedError extends Error {
  108. constructor(message) {
  109. super('NotSupported');
  110. if (message) {
  111. this.message = message;
  112. }
  113. }
  114. }
  115. /**
  116. * Error that when thrown won't be logged in telemetry as an unhandled error.
  117. */
  118. export class ErrorNoTelemetry extends Error {
  119. constructor(msg) {
  120. super(msg);
  121. this.name = 'CodeExpectedError';
  122. }
  123. static fromError(err) {
  124. if (err instanceof ErrorNoTelemetry) {
  125. return err;
  126. }
  127. const result = new ErrorNoTelemetry();
  128. result.message = err.message;
  129. result.stack = err.stack;
  130. return result;
  131. }
  132. static isErrorNoTelemetry(err) {
  133. return err.name === 'CodeExpectedError';
  134. }
  135. }
  136. /**
  137. * This error indicates a bug.
  138. * Do not throw this for invalid user input.
  139. * Only catch this error to recover gracefully from bugs.
  140. */
  141. export class BugIndicatingError extends Error {
  142. constructor(message) {
  143. super(message || 'An unexpected bug occurred.');
  144. Object.setPrototypeOf(this, BugIndicatingError.prototype);
  145. // Because we know for sure only buggy code throws this,
  146. // we definitely want to break here and fix the bug.
  147. // eslint-disable-next-line no-debugger
  148. debugger;
  149. }
  150. }