summaryrefslogtreecommitdiff
path: root/shared/logger/node_modules/@sentry/browser/esm/transports/utils.js
diff options
context:
space:
mode:
authorrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
committerrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
commitbce557cc2dc767628bed6aac87301a1be7c5431b (patch)
treeb51a051228d01fe3306cd7626d4a96768aadb944 /shared/logger/node_modules/@sentry/browser/esm/transports/utils.js
init commit
Diffstat (limited to 'shared/logger/node_modules/@sentry/browser/esm/transports/utils.js')
-rw-r--r--shared/logger/node_modules/@sentry/browser/esm/transports/utils.js85
1 files changed, 85 insertions, 0 deletions
diff --git a/shared/logger/node_modules/@sentry/browser/esm/transports/utils.js b/shared/logger/node_modules/@sentry/browser/esm/transports/utils.js
new file mode 100644
index 0000000..bc50f0c
--- /dev/null
+++ b/shared/logger/node_modules/@sentry/browser/esm/transports/utils.js
@@ -0,0 +1,85 @@
+import { isNativeFetch, logger } from '@sentry/utils';
+import { WINDOW } from '../helpers.js';
+
+let cachedFetchImpl = undefined;
+
+/**
+ * A special usecase for incorrectly wrapped Fetch APIs in conjunction with ad-blockers.
+ * Whenever someone wraps the Fetch API and returns the wrong promise chain,
+ * this chain becomes orphaned and there is no possible way to capture it's rejections
+ * other than allowing it bubble up to this very handler. eg.
+ *
+ * const f = window.fetch;
+ * window.fetch = function () {
+ * const p = f.apply(this, arguments);
+ *
+ * p.then(function() {
+ * console.log('hi.');
+ * });
+ *
+ * return p;
+ * }
+ *
+ * `p.then(function () { ... })` is producing a completely separate promise chain,
+ * however, what's returned is `p` - the result of original `fetch` call.
+ *
+ * This mean, that whenever we use the Fetch API to send our own requests, _and_
+ * some ad-blocker blocks it, this orphaned chain will _always_ reject,
+ * effectively causing another event to be captured.
+ * This makes a whole process become an infinite loop, which we need to somehow
+ * deal with, and break it in one way or another.
+ *
+ * To deal with this issue, we are making sure that we _always_ use the real
+ * browser Fetch API, instead of relying on what `window.fetch` exposes.
+ * The only downside to this would be missing our own requests as breadcrumbs,
+ * but because we are already not doing this, it should be just fine.
+ *
+ * Possible failed fetch error messages per-browser:
+ *
+ * Chrome: Failed to fetch
+ * Edge: Failed to Fetch
+ * Firefox: NetworkError when attempting to fetch resource
+ * Safari: resource blocked by content blocker
+ */
+function getNativeFetchImplementation() {
+ if (cachedFetchImpl) {
+ return cachedFetchImpl;
+ }
+
+ /* eslint-disable @typescript-eslint/unbound-method */
+
+ // Fast path to avoid DOM I/O
+ if (isNativeFetch(WINDOW.fetch)) {
+ return (cachedFetchImpl = WINDOW.fetch.bind(WINDOW));
+ }
+
+ const document = WINDOW.document;
+ let fetchImpl = WINDOW.fetch;
+ // eslint-disable-next-line deprecation/deprecation
+ if (document && typeof document.createElement === 'function') {
+ try {
+ const sandbox = document.createElement('iframe');
+ sandbox.hidden = true;
+ document.head.appendChild(sandbox);
+ const contentWindow = sandbox.contentWindow;
+ if (contentWindow && contentWindow.fetch) {
+ fetchImpl = contentWindow.fetch;
+ }
+ document.head.removeChild(sandbox);
+ } catch (e) {
+ (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) &&
+ logger.warn('Could not create sandbox iframe for pure fetch check, bailing to window.fetch: ', e);
+ }
+ }
+
+ return (cachedFetchImpl = fetchImpl.bind(WINDOW));
+ /* eslint-enable @typescript-eslint/unbound-method */
+}
+
+/** Clears cached fetch impl */
+function clearCachedFetchImplementation() {
+ cachedFetchImpl = undefined;
+}
+
+export { clearCachedFetchImplementation, getNativeFetchImplementation };
+//# sourceMappingURL=utils.js.map