{"version":3,"file":"xhr.js","sources":["../../../src/instrument/xhr.ts"],"sourcesContent":["import type { HandlerDataXhr, SentryWrappedXMLHttpRequest } from '@sentry/core';\nimport { addHandler, isString, maybeInstrument, timestampInSeconds, triggerHandlers } from '@sentry/core';\nimport { WINDOW } from '../types';\n\nexport const SENTRY_XHR_DATA_KEY = '__sentry_xhr_v3__';\n\ntype WindowWithXhr = Window & { XMLHttpRequest?: typeof XMLHttpRequest };\n\n/**\n * Add an instrumentation handler for when an XHR request happens.\n * The handler function is called once when the request starts and once when it ends,\n * which can be identified by checking if it has an `endTimestamp`.\n *\n * Use at your own risk, this might break without changelog notice, only used internally.\n * @hidden\n */\nexport function addXhrInstrumentationHandler(handler: (data: HandlerDataXhr) => void): void {\n const type = 'xhr';\n addHandler(type, handler);\n maybeInstrument(type, instrumentXHR);\n}\n\n/** Exported only for tests. */\nexport function instrumentXHR(): void {\n if (!(WINDOW as WindowWithXhr).XMLHttpRequest) {\n return;\n }\n\n const xhrproto = XMLHttpRequest.prototype;\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n xhrproto.open = new Proxy(xhrproto.open, {\n apply(\n originalOpen,\n xhrOpenThisArg: XMLHttpRequest & SentryWrappedXMLHttpRequest,\n xhrOpenArgArray:\n | [method: string, url: string | URL]\n | [method: string, url: string | URL, async: boolean, username?: string | null, password?: string | null],\n ) {\n // NOTE: If you are a Sentry user, and you are seeing this stack frame,\n // it means the error, that was caused by your XHR call did not\n // have a stack trace. If you are using HttpClient integration,\n // this is the expected behavior, as we are using this virtual error to capture\n // the location of your XHR call, and group your HttpClient events accordingly.\n const virtualError = new Error();\n\n const startTimestamp = timestampInSeconds() * 1000;\n\n // open() should always be called with two or more arguments\n // But to be on the safe side, we actually validate this and bail out if we don't have a method & url\n const method = isString(xhrOpenArgArray[0]) ? xhrOpenArgArray[0].toUpperCase() : undefined;\n const url = parseXhrUrlArg(xhrOpenArgArray[1]);\n\n if (!method || !url) {\n return originalOpen.apply(xhrOpenThisArg, xhrOpenArgArray);\n }\n\n xhrOpenThisArg[SENTRY_XHR_DATA_KEY] = {\n method,\n url,\n request_headers: {},\n };\n\n // if Sentry key appears in URL, don't capture it as a request\n if (method === 'POST' && url.match(/sentry_key/)) {\n xhrOpenThisArg.__sentry_own_request__ = true;\n }\n\n const onreadystatechangeHandler: () => void = () => {\n // For whatever reason, this is not the same instance here as from the outer method\n const xhrInfo = xhrOpenThisArg[SENTRY_XHR_DATA_KEY];\n\n if (!xhrInfo) {\n return;\n }\n\n if (xhrOpenThisArg.readyState === 4) {\n try {\n // touching statusCode in some platforms throws\n // an exception\n xhrInfo.status_code = xhrOpenThisArg.status;\n } catch {\n /* do nothing */\n }\n\n const handlerData: HandlerDataXhr = {\n endTimestamp: timestampInSeconds() * 1000,\n startTimestamp,\n xhr: xhrOpenThisArg,\n virtualError,\n };\n triggerHandlers('xhr', handlerData);\n }\n };\n\n if ('onreadystatechange' in xhrOpenThisArg && typeof xhrOpenThisArg.onreadystatechange === 'function') {\n xhrOpenThisArg.onreadystatechange = new Proxy(xhrOpenThisArg.onreadystatechange, {\n apply(originalOnreadystatechange, onreadystatechangeThisArg, onreadystatechangeArgArray: unknown[]) {\n onreadystatechangeHandler();\n return originalOnreadystatechange.apply(onreadystatechangeThisArg, onreadystatechangeArgArray);\n },\n });\n } else {\n xhrOpenThisArg.addEventListener('readystatechange', onreadystatechangeHandler);\n }\n\n // Intercepting `setRequestHeader` to access the request headers of XHR instance.\n // This will only work for user/library defined headers, not for the default/browser-assigned headers.\n // Request cookies are also unavailable for XHR, as `Cookie` header can't be defined by `setRequestHeader`.\n xhrOpenThisArg.setRequestHeader = new Proxy(xhrOpenThisArg.setRequestHeader, {\n apply(\n originalSetRequestHeader,\n setRequestHeaderThisArg: SentryWrappedXMLHttpRequest,\n setRequestHeaderArgArray: unknown[],\n ) {\n const [header, value] = setRequestHeaderArgArray;\n\n const xhrInfo = setRequestHeaderThisArg[SENTRY_XHR_DATA_KEY];\n\n if (xhrInfo && isString(header) && isString(value)) {\n xhrInfo.request_headers[header.toLowerCase()] = value;\n }\n\n return originalSetRequestHeader.apply(setRequestHeaderThisArg, setRequestHeaderArgArray);\n },\n });\n\n return originalOpen.apply(xhrOpenThisArg, xhrOpenArgArray);\n },\n });\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n xhrproto.send = new Proxy(xhrproto.send, {\n apply(originalSend, sendThisArg: XMLHttpRequest & SentryWrappedXMLHttpRequest, sendArgArray: unknown[]) {\n const sentryXhrData = sendThisArg[SENTRY_XHR_DATA_KEY];\n\n if (!sentryXhrData) {\n return originalSend.apply(sendThisArg, sendArgArray);\n }\n\n if (sendArgArray[0] !== undefined) {\n sentryXhrData.body = sendArgArray[0];\n }\n\n const handlerData: HandlerDataXhr = {\n startTimestamp: timestampInSeconds() * 1000,\n xhr: sendThisArg,\n };\n triggerHandlers('xhr', handlerData);\n\n return originalSend.apply(sendThisArg, sendArgArray);\n },\n });\n}\n\n/**\n * Parses the URL argument of a XHR method to a string.\n *\n * See: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/open#url\n * url: A string or any other object with a stringifier — including a URL object — that provides the URL of the resource to send the request to.\n *\n * @param url - The URL argument of an XHR method\n * @returns The parsed URL string or undefined if the URL is invalid\n */\nfunction parseXhrUrlArg(url: unknown): string | undefined {\n if (isString(url)) {\n return url;\n }\n\n try {\n // If the passed in argument is not a string, it should have a `toString` method as a stringifier.\n // If that fails, we just return undefined (like in IE11 where URL is not available)\n return (url as URL).toString();\n } catch {} // eslint-disable-line no-empty\n\n return undefined;\n}\n"],"names":["addHandler","maybeInstrument","WINDOW","timestampInSeconds","isString","triggerHandlers"],"mappings":"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"}