UNPKG

@sentry/browser

Version:
164 lines (161 loc) 5.57 kB
import { defineIntegration, fill, getFunctionName, getOriginalFunction } from '@sentry/core/browser'; import { WINDOW, wrap } from '../helpers.js'; const DEFAULT_EVENT_TARGET = "EventTarget,Window,Node,ApplicationCache,AudioTrackList,BroadcastChannel,ChannelMergerNode,CryptoOperation,EventSource,FileReader,HTMLUnknownElement,IDBDatabase,IDBRequest,IDBTransaction,KeyOperation,MediaController,MessagePort,ModalWindow,Notification,SVGElementInstance,Screen,SharedWorker,TextTrack,TextTrackCue,TextTrackList,WebSocket,WebSocketWorker,Worker,XMLHttpRequest,XMLHttpRequestEventTarget,XMLHttpRequestUpload".split( "," ); const INTEGRATION_NAME = "BrowserApiErrors"; const _browserApiErrorsIntegration = ((options = {}) => { const _options = { XMLHttpRequest: true, eventTarget: true, requestAnimationFrame: true, setInterval: true, setTimeout: true, unregisterOriginalCallbacks: false, ...options }; return { name: INTEGRATION_NAME, // TODO: This currently only works for the first client this is setup // We may want to adjust this to check for client etc. setupOnce() { if (_options.setTimeout) { fill(WINDOW, "setTimeout", _wrapTimeFunction); } if (_options.setInterval) { fill(WINDOW, "setInterval", _wrapTimeFunction); } if (_options.requestAnimationFrame) { fill(WINDOW, "requestAnimationFrame", _wrapRAF); } if (_options.XMLHttpRequest && "XMLHttpRequest" in WINDOW) { fill(XMLHttpRequest.prototype, "send", _wrapXHR); } const eventTargetOption = _options.eventTarget; if (eventTargetOption) { const eventTarget = Array.isArray(eventTargetOption) ? eventTargetOption : DEFAULT_EVENT_TARGET; eventTarget.forEach((target) => _wrapEventTarget(target, _options)); } } }; }); const browserApiErrorsIntegration = defineIntegration(_browserApiErrorsIntegration); function _wrapTimeFunction(original) { return function(...args) { const originalCallback = args[0]; args[0] = wrap(originalCallback, { mechanism: { handled: false, type: `auto.browser.browserapierrors.${getFunctionName(original)}` } }); return original.apply(this, args); }; } function _wrapRAF(original) { return function(callback) { return original.apply(this, [ wrap(callback, { mechanism: { data: { handler: getFunctionName(original) }, handled: false, type: "auto.browser.browserapierrors.requestAnimationFrame" } }) ]); }; } function _wrapXHR(originalSend) { return function(...args) { const xhr = this; const xmlHttpRequestProps = ["onload", "onerror", "onprogress", "onreadystatechange"]; xmlHttpRequestProps.forEach((prop) => { if (prop in xhr && typeof xhr[prop] === "function") { fill(xhr, prop, function(original) { const wrapOptions = { mechanism: { data: { handler: getFunctionName(original) }, handled: false, type: `auto.browser.browserapierrors.xhr.${prop}` } }; const originalFunction = getOriginalFunction(original); if (originalFunction) { wrapOptions.mechanism.data.handler = getFunctionName(originalFunction); } return wrap(original, wrapOptions); }); } }); return originalSend.apply(this, args); }; } function _wrapEventTarget(target, integrationOptions) { const globalObject = WINDOW; const proto = globalObject[target]?.prototype; if (!proto?.hasOwnProperty?.("addEventListener")) { return; } fill(proto, "addEventListener", function(original) { return function(eventName, fn, options) { try { if (isEventListenerObject(fn)) { fn.handleEvent = wrap(fn.handleEvent, { mechanism: { data: { handler: getFunctionName(fn), target }, handled: false, type: "auto.browser.browserapierrors.handleEvent" } }); } } catch { } if (integrationOptions.unregisterOriginalCallbacks) { unregisterOriginalCallback(this, eventName, fn); } return original.apply(this, [ eventName, wrap(fn, { mechanism: { data: { handler: getFunctionName(fn), target }, handled: false, type: "auto.browser.browserapierrors.addEventListener" } }), options ]); }; }); fill(proto, "removeEventListener", function(originalRemoveEventListener) { return function(eventName, fn, options) { try { const originalEventHandler = fn.__sentry_wrapped__; if (originalEventHandler) { originalRemoveEventListener.call(this, eventName, originalEventHandler, options); } } catch { } return originalRemoveEventListener.call(this, eventName, fn, options); }; }); } function isEventListenerObject(obj) { return typeof obj.handleEvent === "function"; } function unregisterOriginalCallback(target, eventName, fn) { if (target && typeof target === "object" && "removeEventListener" in target && typeof target.removeEventListener === "function") { target.removeEventListener(eventName, fn); } } export { browserApiErrorsIntegration }; //# sourceMappingURL=browserapierrors.js.map