helpers.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import '@sentry-internal/tracing';
  2. import { withScope, captureException } from '@sentry/core';
  3. import { GLOBAL_OBJ, getOriginalFunction, markFunctionWrapped, addNonEnumerableProperty, addExceptionTypeValue, addExceptionMechanism } from '@sentry/utils';
  4. const WINDOW = GLOBAL_OBJ ;
  5. let ignoreOnError = 0;
  6. /**
  7. * @hidden
  8. */
  9. function shouldIgnoreOnError() {
  10. return ignoreOnError > 0;
  11. }
  12. /**
  13. * @hidden
  14. */
  15. function ignoreNextOnError() {
  16. // onerror should trigger before setTimeout
  17. ignoreOnError++;
  18. setTimeout(() => {
  19. ignoreOnError--;
  20. });
  21. }
  22. /**
  23. * Instruments the given function and sends an event to Sentry every time the
  24. * function throws an exception.
  25. *
  26. * @param fn A function to wrap. It is generally safe to pass an unbound function, because the returned wrapper always
  27. * has a correct `this` context.
  28. * @returns The wrapped function.
  29. * @hidden
  30. */
  31. function wrap(
  32. fn,
  33. options
  34. = {},
  35. before,
  36. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  37. ) {
  38. // for future readers what this does is wrap a function and then create
  39. // a bi-directional wrapping between them.
  40. //
  41. // example: wrapped = wrap(original);
  42. // original.__sentry_wrapped__ -> wrapped
  43. // wrapped.__sentry_original__ -> original
  44. if (typeof fn !== 'function') {
  45. return fn;
  46. }
  47. try {
  48. // if we're dealing with a function that was previously wrapped, return
  49. // the original wrapper.
  50. const wrapper = fn.__sentry_wrapped__;
  51. if (wrapper) {
  52. return wrapper;
  53. }
  54. // We don't wanna wrap it twice
  55. if (getOriginalFunction(fn)) {
  56. return fn;
  57. }
  58. } catch (e) {
  59. // Just accessing custom props in some Selenium environments
  60. // can cause a "Permission denied" exception (see raven-js#495).
  61. // Bail on wrapping and return the function as-is (defers to window.onerror).
  62. return fn;
  63. }
  64. /* eslint-disable prefer-rest-params */
  65. // It is important that `sentryWrapped` is not an arrow function to preserve the context of `this`
  66. const sentryWrapped = function () {
  67. const args = Array.prototype.slice.call(arguments);
  68. try {
  69. if (before && typeof before === 'function') {
  70. before.apply(this, arguments);
  71. }
  72. // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
  73. const wrappedArguments = args.map((arg) => wrap(arg, options));
  74. // Attempt to invoke user-land function
  75. // NOTE: If you are a Sentry user, and you are seeing this stack frame, it
  76. // means the sentry.javascript SDK caught an error invoking your application code. This
  77. // is expected behavior and NOT indicative of a bug with sentry.javascript.
  78. return fn.apply(this, wrappedArguments);
  79. } catch (ex) {
  80. ignoreNextOnError();
  81. withScope(scope => {
  82. scope.addEventProcessor(event => {
  83. if (options.mechanism) {
  84. addExceptionTypeValue(event, undefined, undefined);
  85. addExceptionMechanism(event, options.mechanism);
  86. }
  87. event.extra = {
  88. ...event.extra,
  89. arguments: args,
  90. };
  91. return event;
  92. });
  93. captureException(ex);
  94. });
  95. throw ex;
  96. }
  97. };
  98. /* eslint-enable prefer-rest-params */
  99. // Accessing some objects may throw
  100. // ref: https://github.com/getsentry/sentry-javascript/issues/1168
  101. try {
  102. for (const property in fn) {
  103. if (Object.prototype.hasOwnProperty.call(fn, property)) {
  104. sentryWrapped[property] = fn[property];
  105. }
  106. }
  107. } catch (_oO) {} // eslint-disable-line no-empty
  108. // Signal that this function has been wrapped/filled already
  109. // for both debugging and to prevent it to being wrapped/filled twice
  110. markFunctionWrapped(sentryWrapped, fn);
  111. addNonEnumerableProperty(fn, '__sentry_wrapped__', sentryWrapped);
  112. // Restore original function name (not all browsers allow that)
  113. try {
  114. const descriptor = Object.getOwnPropertyDescriptor(sentryWrapped, 'name') ;
  115. if (descriptor.configurable) {
  116. Object.defineProperty(sentryWrapped, 'name', {
  117. get() {
  118. return fn.name;
  119. },
  120. });
  121. }
  122. // eslint-disable-next-line no-empty
  123. } catch (_oO) {}
  124. return sentryWrapped;
  125. }
  126. export { WINDOW, ignoreNextOnError, shouldIgnoreOnError, wrap };
  127. //# sourceMappingURL=helpers.js.map