init commit

This commit is contained in:
rxliuli
2025-11-04 05:03:50 +08:00
commit bce557cc2d
1396 changed files with 172991 additions and 0 deletions

View File

@@ -0,0 +1,320 @@
import { getCurrentHub } from '@sentry/core';
import { addInstrumentationHandler, getEventDescription, severityLevelFromString, safeJoin, SENTRY_XHR_DATA_KEY, parseUrl, logger, htmlTreeAsString } from '@sentry/utils';
import { WINDOW } from '../helpers.js';
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/** maxStringLength gets capped to prevent 100 breadcrumbs exceeding 1MB event payload size */
const MAX_ALLOWED_STRING_LENGTH = 1024;
const BREADCRUMB_INTEGRATION_ID = 'Breadcrumbs';
/**
* Default Breadcrumbs instrumentations
* TODO: Deprecated - with v6, this will be renamed to `Instrument`
*/
class Breadcrumbs {
/**
* @inheritDoc
*/
static __initStatic() {this.id = BREADCRUMB_INTEGRATION_ID;}
/**
* @inheritDoc
*/
__init() {this.name = Breadcrumbs.id;}
/**
* Options of the breadcrumbs integration.
*/
// This field is public, because we use it in the browser client to check if the `sentry` option is enabled.
/**
* @inheritDoc
*/
constructor(options) {Breadcrumbs.prototype.__init.call(this);
this.options = {
console: true,
dom: true,
fetch: true,
history: true,
sentry: true,
xhr: true,
...options,
};
}
/**
* Instrument browser built-ins w/ breadcrumb capturing
* - Console API
* - DOM API (click/typing)
* - XMLHttpRequest API
* - Fetch API
* - History API
*/
setupOnce() {
if (this.options.console) {
addInstrumentationHandler('console', _consoleBreadcrumb);
}
if (this.options.dom) {
addInstrumentationHandler('dom', _domBreadcrumb(this.options.dom));
}
if (this.options.xhr) {
addInstrumentationHandler('xhr', _xhrBreadcrumb);
}
if (this.options.fetch) {
addInstrumentationHandler('fetch', _fetchBreadcrumb);
}
if (this.options.history) {
addInstrumentationHandler('history', _historyBreadcrumb);
}
}
/**
* Adds a breadcrumb for Sentry events or transactions if this option is enabled.
*/
addSentryBreadcrumb(event) {
if (this.options.sentry) {
getCurrentHub().addBreadcrumb(
{
category: `sentry.${event.type === 'transaction' ? 'transaction' : 'event'}`,
event_id: event.event_id,
level: event.level,
message: getEventDescription(event),
},
{
event,
},
);
}
}
} Breadcrumbs.__initStatic();
/**
* A HOC that creaes a function that creates breadcrumbs from DOM API calls.
* This is a HOC so that we get access to dom options in the closure.
*/
function _domBreadcrumb(dom) {
function _innerDomBreadcrumb(handlerData) {
let target;
let keyAttrs = typeof dom === 'object' ? dom.serializeAttribute : undefined;
let maxStringLength =
typeof dom === 'object' && typeof dom.maxStringLength === 'number' ? dom.maxStringLength : undefined;
if (maxStringLength && maxStringLength > MAX_ALLOWED_STRING_LENGTH) {
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) &&
logger.warn(
`\`dom.maxStringLength\` cannot exceed ${MAX_ALLOWED_STRING_LENGTH}, but a value of ${maxStringLength} was configured. Sentry will use ${MAX_ALLOWED_STRING_LENGTH} instead.`,
);
maxStringLength = MAX_ALLOWED_STRING_LENGTH;
}
if (typeof keyAttrs === 'string') {
keyAttrs = [keyAttrs];
}
// Accessing event.target can throw (see getsentry/raven-js#838, #768)
try {
const event = handlerData.event ;
target = _isEvent(event)
? htmlTreeAsString(event.target, { keyAttrs, maxStringLength })
: htmlTreeAsString(event, { keyAttrs, maxStringLength });
} catch (e) {
target = '<unknown>';
}
if (target.length === 0) {
return;
}
getCurrentHub().addBreadcrumb(
{
category: `ui.${handlerData.name}`,
message: target,
},
{
event: handlerData.event,
name: handlerData.name,
global: handlerData.global,
},
);
}
return _innerDomBreadcrumb;
}
/**
* Creates breadcrumbs from console API calls
*/
function _consoleBreadcrumb(handlerData) {
// This is a hack to fix a Vue3-specific bug that causes an infinite loop of
// console warnings. This happens when a Vue template is rendered with
// an undeclared variable, which we try to stringify, ultimately causing
// Vue to issue another warning which repeats indefinitely.
// see: https://github.com/getsentry/sentry-javascript/pull/6010
// see: https://github.com/getsentry/sentry-javascript/issues/5916
for (let i = 0; i < handlerData.args.length; i++) {
if (handlerData.args[i] === 'ref=Ref<') {
handlerData.args[i + 1] = 'viewRef';
break;
}
}
const breadcrumb = {
category: 'console',
data: {
arguments: handlerData.args,
logger: 'console',
},
level: severityLevelFromString(handlerData.level),
message: safeJoin(handlerData.args, ' '),
};
if (handlerData.level === 'assert') {
if (handlerData.args[0] === false) {
breadcrumb.message = `Assertion failed: ${safeJoin(handlerData.args.slice(1), ' ') || 'console.assert'}`;
breadcrumb.data.arguments = handlerData.args.slice(1);
} else {
// Don't capture a breadcrumb for passed assertions
return;
}
}
getCurrentHub().addBreadcrumb(breadcrumb, {
input: handlerData.args,
level: handlerData.level,
});
}
/**
* Creates breadcrumbs from XHR API calls
*/
function _xhrBreadcrumb(handlerData) {
const { startTimestamp, endTimestamp } = handlerData;
const sentryXhrData = handlerData.xhr[SENTRY_XHR_DATA_KEY];
// We only capture complete, non-sentry requests
if (!startTimestamp || !endTimestamp || !sentryXhrData) {
return;
}
const { method, url, status_code, body } = sentryXhrData;
const data = {
method,
url,
status_code,
};
const hint = {
xhr: handlerData.xhr,
input: body,
startTimestamp,
endTimestamp,
};
getCurrentHub().addBreadcrumb(
{
category: 'xhr',
data,
type: 'http',
},
hint,
);
}
/**
* Creates breadcrumbs from fetch API calls
*/
function _fetchBreadcrumb(handlerData) {
const { startTimestamp, endTimestamp } = handlerData;
// We only capture complete fetch requests
if (!endTimestamp) {
return;
}
if (handlerData.fetchData.url.match(/sentry_key/) && handlerData.fetchData.method === 'POST') {
// We will not create breadcrumbs for fetch requests that contain `sentry_key` (internal sentry requests)
return;
}
if (handlerData.error) {
const data = handlerData.fetchData;
const hint = {
data: handlerData.error,
input: handlerData.args,
startTimestamp,
endTimestamp,
};
getCurrentHub().addBreadcrumb(
{
category: 'fetch',
data,
level: 'error',
type: 'http',
},
hint,
);
} else {
const data = {
...handlerData.fetchData,
status_code: handlerData.response && handlerData.response.status,
};
const hint = {
input: handlerData.args,
response: handlerData.response,
startTimestamp,
endTimestamp,
};
getCurrentHub().addBreadcrumb(
{
category: 'fetch',
data,
type: 'http',
},
hint,
);
}
}
/**
* Creates breadcrumbs from history API calls
*/
function _historyBreadcrumb(handlerData) {
let from = handlerData.from;
let to = handlerData.to;
const parsedLoc = parseUrl(WINDOW.location.href);
let parsedFrom = parseUrl(from);
const parsedTo = parseUrl(to);
// Initial pushState doesn't provide `from` information
if (!parsedFrom.path) {
parsedFrom = parsedLoc;
}
// Use only the path component of the URL if the URL matches the current
// document (almost all the time when using pushState)
if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host) {
to = parsedTo.relative;
}
if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host) {
from = parsedFrom.relative;
}
getCurrentHub().addBreadcrumb({
category: 'navigation',
data: {
from,
to,
},
});
}
function _isEvent(event) {
return !!event && !!(event ).target;
}
export { BREADCRUMB_INTEGRATION_ID, Breadcrumbs };
//# sourceMappingURL=breadcrumbs.js.map

View File

@@ -0,0 +1,211 @@
import { logger } from '@sentry/utils';
/** Deduplication filter */
class Dedupe {constructor() { Dedupe.prototype.__init.call(this); }
/**
* @inheritDoc
*/
static __initStatic() {this.id = 'Dedupe';}
/**
* @inheritDoc
*/
__init() {this.name = Dedupe.id;}
/**
* @inheritDoc
*/
/**
* @inheritDoc
*/
setupOnce(addGlobalEventProcessor, getCurrentHub) {
const eventProcessor = currentEvent => {
// We want to ignore any non-error type events, e.g. transactions or replays
// These should never be deduped, and also not be compared against as _previousEvent.
if (currentEvent.type) {
return currentEvent;
}
const self = getCurrentHub().getIntegration(Dedupe);
if (self) {
// Juuust in case something goes wrong
try {
if (_shouldDropEvent(currentEvent, self._previousEvent)) {
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('Event dropped due to being a duplicate of previously captured event.');
return null;
}
} catch (_oO) {
return (self._previousEvent = currentEvent);
}
return (self._previousEvent = currentEvent);
}
return currentEvent;
};
eventProcessor.id = this.name;
addGlobalEventProcessor(eventProcessor);
}
} Dedupe.__initStatic();
/** JSDoc */
function _shouldDropEvent(currentEvent, previousEvent) {
if (!previousEvent) {
return false;
}
if (_isSameMessageEvent(currentEvent, previousEvent)) {
return true;
}
if (_isSameExceptionEvent(currentEvent, previousEvent)) {
return true;
}
return false;
}
/** JSDoc */
function _isSameMessageEvent(currentEvent, previousEvent) {
const currentMessage = currentEvent.message;
const previousMessage = previousEvent.message;
// If neither event has a message property, they were both exceptions, so bail out
if (!currentMessage && !previousMessage) {
return false;
}
// If only one event has a stacktrace, but not the other one, they are not the same
if ((currentMessage && !previousMessage) || (!currentMessage && previousMessage)) {
return false;
}
if (currentMessage !== previousMessage) {
return false;
}
if (!_isSameFingerprint(currentEvent, previousEvent)) {
return false;
}
if (!_isSameStacktrace(currentEvent, previousEvent)) {
return false;
}
return true;
}
/** JSDoc */
function _isSameExceptionEvent(currentEvent, previousEvent) {
const previousException = _getExceptionFromEvent(previousEvent);
const currentException = _getExceptionFromEvent(currentEvent);
if (!previousException || !currentException) {
return false;
}
if (previousException.type !== currentException.type || previousException.value !== currentException.value) {
return false;
}
if (!_isSameFingerprint(currentEvent, previousEvent)) {
return false;
}
if (!_isSameStacktrace(currentEvent, previousEvent)) {
return false;
}
return true;
}
/** JSDoc */
function _isSameStacktrace(currentEvent, previousEvent) {
let currentFrames = _getFramesFromEvent(currentEvent);
let previousFrames = _getFramesFromEvent(previousEvent);
// If neither event has a stacktrace, they are assumed to be the same
if (!currentFrames && !previousFrames) {
return true;
}
// If only one event has a stacktrace, but not the other one, they are not the same
if ((currentFrames && !previousFrames) || (!currentFrames && previousFrames)) {
return false;
}
currentFrames = currentFrames ;
previousFrames = previousFrames ;
// If number of frames differ, they are not the same
if (previousFrames.length !== currentFrames.length) {
return false;
}
// Otherwise, compare the two
for (let i = 0; i < previousFrames.length; i++) {
const frameA = previousFrames[i];
const frameB = currentFrames[i];
if (
frameA.filename !== frameB.filename ||
frameA.lineno !== frameB.lineno ||
frameA.colno !== frameB.colno ||
frameA.function !== frameB.function
) {
return false;
}
}
return true;
}
/** JSDoc */
function _isSameFingerprint(currentEvent, previousEvent) {
let currentFingerprint = currentEvent.fingerprint;
let previousFingerprint = previousEvent.fingerprint;
// If neither event has a fingerprint, they are assumed to be the same
if (!currentFingerprint && !previousFingerprint) {
return true;
}
// If only one event has a fingerprint, but not the other one, they are not the same
if ((currentFingerprint && !previousFingerprint) || (!currentFingerprint && previousFingerprint)) {
return false;
}
currentFingerprint = currentFingerprint ;
previousFingerprint = previousFingerprint ;
// Otherwise, compare the two
try {
return !!(currentFingerprint.join('') === previousFingerprint.join(''));
} catch (_oO) {
return false;
}
}
/** JSDoc */
function _getExceptionFromEvent(event) {
return event.exception && event.exception.values && event.exception.values[0];
}
/** JSDoc */
function _getFramesFromEvent(event) {
const exception = event.exception;
if (exception) {
try {
// @ts-ignore Object could be undefined
return exception.values[0].stacktrace.frames;
} catch (_oO) {
return undefined;
}
}
return undefined;
}
export { Dedupe };
//# sourceMappingURL=dedupe.js.map

View File

@@ -0,0 +1,248 @@
import { getCurrentHub } from '@sentry/core';
import { addInstrumentationHandler, isString, isPrimitive, isErrorEvent, getLocationHref, logger, addExceptionMechanism } from '@sentry/utils';
import { eventFromUnknownInput } from '../eventbuilder.js';
import { shouldIgnoreOnError } from '../helpers.js';
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/** Global handlers */
class GlobalHandlers {
/**
* @inheritDoc
*/
static __initStatic() {this.id = 'GlobalHandlers';}
/**
* @inheritDoc
*/
__init() {this.name = GlobalHandlers.id;}
/** JSDoc */
/**
* Stores references functions to installing handlers. Will set to undefined
* after they have been run so that they are not used twice.
*/
__init2() {this._installFunc = {
onerror: _installGlobalOnErrorHandler,
onunhandledrejection: _installGlobalOnUnhandledRejectionHandler,
};}
/** JSDoc */
constructor(options) {GlobalHandlers.prototype.__init.call(this);GlobalHandlers.prototype.__init2.call(this);
this._options = {
onerror: true,
onunhandledrejection: true,
...options,
};
}
/**
* @inheritDoc
*/
setupOnce() {
Error.stackTraceLimit = 50;
const options = this._options;
// We can disable guard-for-in as we construct the options object above + do checks against
// `this._installFunc` for the property.
// eslint-disable-next-line guard-for-in
for (const key in options) {
const installFunc = this._installFunc[key ];
if (installFunc && options[key ]) {
globalHandlerLog(key);
installFunc();
this._installFunc[key ] = undefined;
}
}
}
} GlobalHandlers.__initStatic();
/** JSDoc */
function _installGlobalOnErrorHandler() {
addInstrumentationHandler(
'error',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(data) => {
const [hub, stackParser, attachStacktrace] = getHubAndOptions();
if (!hub.getIntegration(GlobalHandlers)) {
return;
}
const { msg, url, line, column, error } = data;
if (shouldIgnoreOnError() || (error && error.__sentry_own_request__)) {
return;
}
const event =
error === undefined && isString(msg)
? _eventFromIncompleteOnError(msg, url, line, column)
: _enhanceEventWithInitialFrame(
eventFromUnknownInput(stackParser, error || msg, undefined, attachStacktrace, false),
url,
line,
column,
);
event.level = 'error';
addMechanismAndCapture(hub, error, event, 'onerror');
},
);
}
/** JSDoc */
function _installGlobalOnUnhandledRejectionHandler() {
addInstrumentationHandler(
'unhandledrejection',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(e) => {
const [hub, stackParser, attachStacktrace] = getHubAndOptions();
if (!hub.getIntegration(GlobalHandlers)) {
return;
}
let error = e;
// dig the object of the rejection out of known event types
try {
// PromiseRejectionEvents store the object of the rejection under 'reason'
// see https://developer.mozilla.org/en-US/docs/Web/API/PromiseRejectionEvent
if ('reason' in e) {
error = e.reason;
}
// something, somewhere, (likely a browser extension) effectively casts PromiseRejectionEvents
// to CustomEvents, moving the `promise` and `reason` attributes of the PRE into
// the CustomEvent's `detail` attribute, since they're not part of CustomEvent's spec
// see https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent and
// https://github.com/getsentry/sentry-javascript/issues/2380
else if ('detail' in e && 'reason' in e.detail) {
error = e.detail.reason;
}
} catch (_oO) {
// no-empty
}
if (shouldIgnoreOnError() || (error && error.__sentry_own_request__)) {
return true;
}
const event = isPrimitive(error)
? _eventFromRejectionWithPrimitive(error)
: eventFromUnknownInput(stackParser, error, undefined, attachStacktrace, true);
event.level = 'error';
addMechanismAndCapture(hub, error, event, 'onunhandledrejection');
return;
},
);
}
/**
* Create an event from a promise rejection where the `reason` is a primitive.
*
* @param reason: The `reason` property of the promise rejection
* @returns An Event object with an appropriate `exception` value
*/
function _eventFromRejectionWithPrimitive(reason) {
return {
exception: {
values: [
{
type: 'UnhandledRejection',
// String() is needed because the Primitive type includes symbols (which can't be automatically stringified)
value: `Non-Error promise rejection captured with value: ${String(reason)}`,
},
],
},
};
}
/**
* This function creates a stack from an old, error-less onerror handler.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function _eventFromIncompleteOnError(msg, url, line, column) {
const ERROR_TYPES_RE =
/^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/i;
// If 'message' is ErrorEvent, get real message from inside
let message = isErrorEvent(msg) ? msg.message : msg;
let name = 'Error';
const groups = message.match(ERROR_TYPES_RE);
if (groups) {
name = groups[1];
message = groups[2];
}
const event = {
exception: {
values: [
{
type: name,
value: message,
},
],
},
};
return _enhanceEventWithInitialFrame(event, url, line, column);
}
/** JSDoc */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function _enhanceEventWithInitialFrame(event, url, line, column) {
// event.exception
const e = (event.exception = event.exception || {});
// event.exception.values
const ev = (e.values = e.values || []);
// event.exception.values[0]
const ev0 = (ev[0] = ev[0] || {});
// event.exception.values[0].stacktrace
const ev0s = (ev0.stacktrace = ev0.stacktrace || {});
// event.exception.values[0].stacktrace.frames
const ev0sf = (ev0s.frames = ev0s.frames || []);
const colno = isNaN(parseInt(column, 10)) ? undefined : column;
const lineno = isNaN(parseInt(line, 10)) ? undefined : line;
const filename = isString(url) && url.length > 0 ? url : getLocationHref();
// event.exception.values[0].stacktrace.frames
if (ev0sf.length === 0) {
ev0sf.push({
colno,
filename,
function: '?',
in_app: true,
lineno,
});
}
return event;
}
function globalHandlerLog(type) {
(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.log(`Global Handler attached: ${type}`);
}
function addMechanismAndCapture(hub, error, event, type) {
addExceptionMechanism(event, {
handled: false,
type,
});
hub.captureEvent(event, {
originalException: error,
});
}
function getHubAndOptions() {
const hub = getCurrentHub();
const client = hub.getClient();
const options = (client && client.getOptions()) || {
stackParser: () => [],
attachStacktrace: false,
};
return [hub, options.stackParser, options.attachStacktrace];
}
export { GlobalHandlers };
//# sourceMappingURL=globalhandlers.js.map

View File

@@ -0,0 +1,47 @@
import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core';
import { WINDOW } from '../helpers.js';
/** HttpContext integration collects information about HTTP request headers */
class HttpContext {constructor() { HttpContext.prototype.__init.call(this); }
/**
* @inheritDoc
*/
static __initStatic() {this.id = 'HttpContext';}
/**
* @inheritDoc
*/
__init() {this.name = HttpContext.id;}
/**
* @inheritDoc
*/
setupOnce() {
addGlobalEventProcessor((event) => {
if (getCurrentHub().getIntegration(HttpContext)) {
// if none of the information we want exists, don't bother
if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) {
return event;
}
// grab as much info as exists and add it to the event
const url = (event.request && event.request.url) || (WINDOW.location && WINDOW.location.href);
const { referrer } = WINDOW.document || {};
const { userAgent } = WINDOW.navigator || {};
const headers = {
...(event.request && event.request.headers),
...(referrer && { Referer: referrer }),
...(userAgent && { 'User-Agent': userAgent }),
};
const request = { ...event.request, ...(url && { url }), headers };
return { ...event, request };
}
return event;
});
}
} HttpContext.__initStatic();
export { HttpContext };
//# sourceMappingURL=httpcontext.js.map

View File

@@ -0,0 +1,87 @@
import { getCurrentHub, addGlobalEventProcessor } from '@sentry/core';
import { isInstanceOf } from '@sentry/utils';
import { exceptionFromError } from '../eventbuilder.js';
const DEFAULT_KEY = 'cause';
const DEFAULT_LIMIT = 5;
/** Adds SDK info to an event. */
class LinkedErrors {
/**
* @inheritDoc
*/
static __initStatic() {this.id = 'LinkedErrors';}
/**
* @inheritDoc
*/
__init() {this.name = LinkedErrors.id;}
/**
* @inheritDoc
*/
/**
* @inheritDoc
*/
/**
* @inheritDoc
*/
constructor(options = {}) {LinkedErrors.prototype.__init.call(this);
this._key = options.key || DEFAULT_KEY;
this._limit = options.limit || DEFAULT_LIMIT;
}
/**
* @inheritDoc
*/
setupOnce() {
const client = getCurrentHub().getClient();
if (!client) {
return;
}
addGlobalEventProcessor((event, hint) => {
const self = getCurrentHub().getIntegration(LinkedErrors);
return self ? _handler(client.getOptions().stackParser, self._key, self._limit, event, hint) : event;
});
}
} LinkedErrors.__initStatic();
/**
* @inheritDoc
*/
function _handler(
parser,
key,
limit,
event,
hint,
) {
if (!event.exception || !event.exception.values || !hint || !isInstanceOf(hint.originalException, Error)) {
return event;
}
const linkedErrors = _walkErrorTree(parser, limit, hint.originalException , key);
event.exception.values = [...linkedErrors, ...event.exception.values];
return event;
}
/**
* JSDOC
*/
function _walkErrorTree(
parser,
limit,
error,
key,
stack = [],
) {
if (!isInstanceOf(error[key], Error) || stack.length + 1 >= limit) {
return stack;
}
const exception = exceptionFromError(parser, error[key]);
return _walkErrorTree(parser, limit, error[key], key, [exception, ...stack]);
}
export { LinkedErrors, _handler, _walkErrorTree };
//# sourceMappingURL=linkederrors.js.map

View File

@@ -0,0 +1,281 @@
import { fill, getFunctionName, getOriginalFunction } from '@sentry/utils';
import { WINDOW, wrap } from '../helpers.js';
const DEFAULT_EVENT_TARGET = [
'EventTarget',
'Window',
'Node',
'ApplicationCache',
'AudioTrackList',
'ChannelMergerNode',
'CryptoOperation',
'EventSource',
'FileReader',
'HTMLUnknownElement',
'IDBDatabase',
'IDBRequest',
'IDBTransaction',
'KeyOperation',
'MediaController',
'MessagePort',
'ModalWindow',
'Notification',
'SVGElementInstance',
'Screen',
'TextTrack',
'TextTrackCue',
'TextTrackList',
'WebSocket',
'WebSocketWorker',
'Worker',
'XMLHttpRequest',
'XMLHttpRequestEventTarget',
'XMLHttpRequestUpload',
];
/** Wrap timer functions and event targets to catch errors and provide better meta data */
class TryCatch {
/**
* @inheritDoc
*/
static __initStatic() {this.id = 'TryCatch';}
/**
* @inheritDoc
*/
__init() {this.name = TryCatch.id;}
/** JSDoc */
/**
* @inheritDoc
*/
constructor(options) {TryCatch.prototype.__init.call(this);
this._options = {
XMLHttpRequest: true,
eventTarget: true,
requestAnimationFrame: true,
setInterval: true,
setTimeout: true,
...options,
};
}
/**
* Wrap timer functions and event targets to catch errors
* and provide better metadata.
*/
setupOnce() {
if (this._options.setTimeout) {
fill(WINDOW, 'setTimeout', _wrapTimeFunction);
}
if (this._options.setInterval) {
fill(WINDOW, 'setInterval', _wrapTimeFunction);
}
if (this._options.requestAnimationFrame) {
fill(WINDOW, 'requestAnimationFrame', _wrapRAF);
}
if (this._options.XMLHttpRequest && 'XMLHttpRequest' in WINDOW) {
fill(XMLHttpRequest.prototype, 'send', _wrapXHR);
}
const eventTargetOption = this._options.eventTarget;
if (eventTargetOption) {
const eventTarget = Array.isArray(eventTargetOption) ? eventTargetOption : DEFAULT_EVENT_TARGET;
eventTarget.forEach(_wrapEventTarget);
}
}
} TryCatch.__initStatic();
/** JSDoc */
function _wrapTimeFunction(original) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return function ( ...args) {
const originalCallback = args[0];
args[0] = wrap(originalCallback, {
mechanism: {
data: { function: getFunctionName(original) },
handled: true,
type: 'instrument',
},
});
return original.apply(this, args);
};
}
/** JSDoc */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function _wrapRAF(original) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return function ( callback) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return original.apply(this, [
wrap(callback, {
mechanism: {
data: {
function: 'requestAnimationFrame',
handler: getFunctionName(original),
},
handled: true,
type: 'instrument',
},
}),
]);
};
}
/** JSDoc */
function _wrapXHR(originalSend) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return function ( ...args) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const xhr = this;
const xmlHttpRequestProps = ['onload', 'onerror', 'onprogress', 'onreadystatechange'];
xmlHttpRequestProps.forEach(prop => {
if (prop in xhr && typeof xhr[prop] === 'function') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fill(xhr, prop, function (original) {
const wrapOptions = {
mechanism: {
data: {
function: prop,
handler: getFunctionName(original),
},
handled: true,
type: 'instrument',
},
};
// If Instrument integration has been called before TryCatch, get the name of original function
const originalFunction = getOriginalFunction(original);
if (originalFunction) {
wrapOptions.mechanism.data.handler = getFunctionName(originalFunction);
}
// Otherwise wrap directly
return wrap(original, wrapOptions);
});
}
});
return originalSend.apply(this, args);
};
}
/** JSDoc */
function _wrapEventTarget(target) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const globalObject = WINDOW ;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const proto = globalObject[target] && globalObject[target].prototype;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, no-prototype-builtins
if (!proto || !proto.hasOwnProperty || !proto.hasOwnProperty('addEventListener')) {
return;
}
fill(proto, 'addEventListener', function (original)
{
return function (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
eventName,
fn,
options,
) {
try {
if (typeof fn.handleEvent === 'function') {
// ESlint disable explanation:
// First, it is generally safe to call `wrap` with an unbound function. Furthermore, using `.bind()` would
// introduce a bug here, because bind returns a new function that doesn't have our
// flags(like __sentry_original__) attached. `wrap` checks for those flags to avoid unnecessary wrapping.
// Without those flags, every call to addEventListener wraps the function again, causing a memory leak.
// eslint-disable-next-line @typescript-eslint/unbound-method
fn.handleEvent = wrap(fn.handleEvent, {
mechanism: {
data: {
function: 'handleEvent',
handler: getFunctionName(fn),
target,
},
handled: true,
type: 'instrument',
},
});
}
} catch (err) {
// can sometimes get 'Permission denied to access property "handle Event'
}
return original.apply(this, [
eventName,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
wrap(fn , {
mechanism: {
data: {
function: 'addEventListener',
handler: getFunctionName(fn),
target,
},
handled: true,
type: 'instrument',
},
}),
options,
]);
};
});
fill(
proto,
'removeEventListener',
function (
originalRemoveEventListener,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) {
return function (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
eventName,
fn,
options,
) {
/**
* There are 2 possible scenarios here:
*
* 1. Someone passes a callback, which was attached prior to Sentry initialization, or by using unmodified
* method, eg. `document.addEventListener.call(el, name, handler). In this case, we treat this function
* as a pass-through, and call original `removeEventListener` with it.
*
* 2. Someone passes a callback, which was attached after Sentry was initialized, which means that it was using
* our wrapped version of `addEventListener`, which internally calls `wrap` helper.
* This helper "wraps" whole callback inside a try/catch statement, and attached appropriate metadata to it,
* in order for us to make a distinction between wrapped/non-wrapped functions possible.
* If a function was wrapped, it has additional property of `__sentry_wrapped__`, holding the handler.
*
* When someone adds a handler prior to initialization, and then do it again, but after,
* then we have to detach both of them. Otherwise, if we'd detach only wrapped one, it'd be impossible
* to get rid of the initial handler and it'd stick there forever.
*/
const wrappedEventHandler = fn ;
try {
const originalEventHandler = wrappedEventHandler && wrappedEventHandler.__sentry_wrapped__;
if (originalEventHandler) {
originalRemoveEventListener.call(this, eventName, originalEventHandler, options);
}
} catch (e) {
// ignore, accessing __sentry_wrapped__ will throw in some Selenium environments
}
return originalRemoveEventListener.call(this, eventName, wrappedEventHandler, options);
};
},
);
}
export { TryCatch };
//# sourceMappingURL=trycatch.js.map