UNPKG

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