From 99a068775f06049f8e4b29e38645a926905a6680 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Tue, 27 Sep 2022 12:35:47 +0100 Subject: [PATCH 1/2] Remove `getGlobalObject` usage from utils --- packages/utils/src/browser.ts | 15 +++++---- packages/utils/src/global.ts | 28 +++++++++++------ packages/utils/src/instrument.ts | 50 ++++++++++++++---------------- packages/utils/src/logger.ts | 15 +++------ packages/utils/src/misc.ts | 21 ++++++------- packages/utils/src/supports.ts | 17 +++++----- packages/utils/src/time.ts | 6 ++-- packages/utils/test/global.test.ts | 8 ++--- 8 files changed, 80 insertions(+), 80 deletions(-) diff --git a/packages/utils/src/browser.ts b/packages/utils/src/browser.ts index 62d38077054b..30cd546c6ad8 100644 --- a/packages/utils/src/browser.ts +++ b/packages/utils/src/browser.ts @@ -1,6 +1,11 @@ -import { getGlobalObject } from './global'; +import { GLOBAL_OBJ } from './global'; import { isString } from './is'; +/** + * TODO: Move me to @sentry/browser when @sentry/utils no longer contains any browser code + */ +export const WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window; + /** * Given a child DOM element, returns a query-selector statement describing that * and its ancestors @@ -115,9 +120,8 @@ function _htmlElementAsString(el: unknown, keyAttrs?: string[]): string { * A safe form of location.href */ export function getLocationHref(): string { - const global = getGlobalObject(); try { - return global.document.location.href; + return WINDOW.document.location.href; } catch (oO) { return ''; } @@ -141,9 +145,8 @@ export function getLocationHref(): string { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export function getDomElement(selector: string): E | null { - const global = getGlobalObject(); - if (global.document && global.document.querySelector) { - return global.document.querySelector(selector) as unknown as E; + if (WINDOW.document && WINDOW.document.querySelector) { + return WINDOW.document.querySelector(selector) as unknown as E; } return null; } diff --git a/packages/utils/src/global.ts b/packages/utils/src/global.ts index 8515e2af82eb..d32860fc75be 100644 --- a/packages/utils/src/global.ts +++ b/packages/utils/src/global.ts @@ -7,8 +7,10 @@ import { Integration } from '@sentry/types'; -/** Internal */ -interface SentryGlobal { +/** Internal global with common properties and Sentry extensions */ +export interface InternalGlobal { + navigator?: { userAgent?: string }; + console: Console; Sentry?: { Integrations?: Integration[]; }; @@ -21,10 +23,15 @@ interface SentryGlobal { globalEventProcessors: any; hub: any; logger: any; + extensions?: { + /** Extension methods for the hub, which are bound to the current Hub instance */ + // eslint-disable-next-line @typescript-eslint/ban-types + [key: string]: Function; + }; }; } -// The code below for 'isGlobalObj' and 'GLOBAL' was copied from core-js before modification +// The code below for 'isGlobalObj' and 'GLOBAL_OBJ' was copied from core-js before modification // https://github.com/zloirock/core-js/blob/1b944df55282cdc99c90db5f49eb0b6eda2cc0a3/packages/core-js/internals/global.js // core-js has the following licence: // @@ -53,7 +60,8 @@ function isGlobalObj(obj: { Math?: Math }): any | undefined { return obj && obj.Math == Math ? obj : undefined; } -const GLOBAL = +/** Get's the global object for the current JavaScript runtime */ +export const GLOBAL_OBJ: InternalGlobal = (typeof globalThis == 'object' && isGlobalObj(globalThis)) || // eslint-disable-next-line no-restricted-globals (typeof window == 'object' && isGlobalObj(window)) || @@ -69,8 +77,8 @@ const GLOBAL = * * @returns Global scope object */ -export function getGlobalObject(): T & SentryGlobal { - return GLOBAL as T & SentryGlobal; +export function getGlobalObject(): T & InternalGlobal { + return GLOBAL_OBJ as T & InternalGlobal; } /** @@ -81,12 +89,12 @@ export function getGlobalObject(): T & SentryGlobal { * * @param name name of the global singleton on __SENTRY__ * @param creator creator Factory function to create the singleton if it doesn't already exist on `__SENTRY__` - * @param obj (Optional) The global object on which to look for `__SENTRY__`, if not `getGlobalObject`'s return value + * @param obj (Optional) The global object on which to look for `__SENTRY__`, if not `GLOBAL_OBJ`'s return value * @returns the singleton */ -export function getGlobalSingleton(name: keyof SentryGlobal['__SENTRY__'], creator: () => T, obj?: unknown): T { - const global = (obj || GLOBAL) as SentryGlobal; - const __SENTRY__ = (global.__SENTRY__ = global.__SENTRY__ || {}); +export function getGlobalSingleton(name: keyof InternalGlobal['__SENTRY__'], creator: () => T, obj?: unknown): T { + const gbl = (obj || GLOBAL_OBJ) as InternalGlobal; + const __SENTRY__ = (gbl.__SENTRY__ = gbl.__SENTRY__ || {}); const singleton = __SENTRY__[name] || (__SENTRY__[name] = creator()); return singleton; } diff --git a/packages/utils/src/instrument.ts b/packages/utils/src/instrument.ts index 05a2a0abdf3c..12dcfae9ac4e 100644 --- a/packages/utils/src/instrument.ts +++ b/packages/utils/src/instrument.ts @@ -3,15 +3,13 @@ /* eslint-disable @typescript-eslint/ban-types */ import { WrappedFunction } from '@sentry/types'; -import { getGlobalObject } from './global'; +import { WINDOW } from './browser'; import { isInstanceOf, isString } from './is'; import { CONSOLE_LEVELS, logger } from './logger'; import { fill } from './object'; import { getFunctionName } from './stacktrace'; import { supportsHistory, supportsNativeFetch } from './supports'; -const global = getGlobalObject(); - export type InstrumentHandlerType = | 'console' | 'dom' @@ -105,22 +103,22 @@ function triggerHandlers(type: InstrumentHandlerType, data: any): void { /** JSDoc */ function instrumentConsole(): void { - if (!('console' in global)) { + if (!('console' in WINDOW)) { return; } CONSOLE_LEVELS.forEach(function (level: string): void { - if (!(level in global.console)) { + if (!(level in WINDOW.console)) { return; } - fill(global.console, level, function (originalConsoleMethod: () => any): Function { + fill(WINDOW.console, level, function (originalConsoleMethod: () => any): Function { return function (...args: any[]): void { triggerHandlers('console', { args, level }); // this fails for some browsers. :( if (originalConsoleMethod) { - originalConsoleMethod.apply(global.console, args); + originalConsoleMethod.apply(WINDOW.console, args); } }; }); @@ -133,7 +131,7 @@ function instrumentFetch(): void { return; } - fill(global, 'fetch', function (originalFetch: () => void): () => void { + fill(WINDOW, 'fetch', function (originalFetch: () => void): () => void { return function (...args: any[]): void { const handlerData = { args, @@ -149,7 +147,7 @@ function instrumentFetch(): void { }); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return originalFetch.apply(global, args).then( + return originalFetch.apply(WINDOW, args).then( (response: Response) => { triggerHandlers('fetch', { ...handlerData, @@ -190,7 +188,7 @@ interface SentryWrappedXMLHttpRequest extends XMLHttpRequest { /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /** Extract `method` from fetch call arguments */ function getFetchMethod(fetchArgs: any[] = []): string { - if ('Request' in global && isInstanceOf(fetchArgs[0], Request) && fetchArgs[0].method) { + if ('Request' in WINDOW && isInstanceOf(fetchArgs[0], Request) && fetchArgs[0].method) { return String(fetchArgs[0].method).toUpperCase(); } if (fetchArgs[1] && fetchArgs[1].method) { @@ -204,7 +202,7 @@ function getFetchUrl(fetchArgs: any[] = []): string { if (typeof fetchArgs[0] === 'string') { return fetchArgs[0]; } - if ('Request' in global && isInstanceOf(fetchArgs[0], Request)) { + if ('Request' in WINDOW && isInstanceOf(fetchArgs[0], Request)) { return fetchArgs[0].url; } return String(fetchArgs[0]); @@ -213,7 +211,7 @@ function getFetchUrl(fetchArgs: any[] = []): string { /** JSDoc */ function instrumentXHR(): void { - if (!('XMLHttpRequest' in global)) { + if (!('XMLHttpRequest' in WINDOW)) { return; } @@ -295,9 +293,9 @@ function instrumentHistory(): void { return; } - const oldOnPopState = global.onpopstate; - global.onpopstate = function (this: WindowEventHandlers, ...args: any[]): any { - const to = global.location.href; + const oldOnPopState = WINDOW.onpopstate; + WINDOW.onpopstate = function (this: WindowEventHandlers, ...args: any[]): any { + const to = WINDOW.location.href; // keep track of the current URL state, as we always receive only the updated state const from = lastHref; lastHref = to; @@ -336,8 +334,8 @@ function instrumentHistory(): void { }; } - fill(global.history, 'pushState', historyReplacementFunction); - fill(global.history, 'replaceState', historyReplacementFunction); + fill(WINDOW.history, 'pushState', historyReplacementFunction); + fill(WINDOW.history, 'replaceState', historyReplacementFunction); } const debounceDuration = 1000; @@ -452,7 +450,7 @@ function makeDOMEventHandler(handler: Function, globalListener: boolean = false) // Start a new debounce timer that will prevent us from capturing multiple events that should be grouped together. clearTimeout(debounceTimerID); - debounceTimerID = global.setTimeout(() => { + debounceTimerID = WINDOW.setTimeout(() => { debounceTimerID = undefined; }, debounceDuration); }; @@ -481,7 +479,7 @@ type InstrumentedElement = Element & { /** JSDoc */ function instrumentDOM(): void { - if (!('document' in global)) { + if (!('document' in WINDOW)) { return; } @@ -490,8 +488,8 @@ function instrumentDOM(): void { // we instrument `addEventListener` so that we don't end up attaching this handler twice. const triggerDOMHandler = triggerHandlers.bind(null, 'dom'); const globalDOMEventHandler = makeDOMEventHandler(triggerDOMHandler, true); - global.document.addEventListener('click', globalDOMEventHandler, false); - global.document.addEventListener('keypress', globalDOMEventHandler, false); + WINDOW.document.addEventListener('click', globalDOMEventHandler, false); + WINDOW.document.addEventListener('keypress', globalDOMEventHandler, false); // After hooking into click and keypress events bubbled up to `document`, we also hook into user-handled // clicks & keypresses, by adding an event listener of our own to any element to which they add a listener. That @@ -500,7 +498,7 @@ function instrumentDOM(): void { // guaranteed to fire at least once.) ['EventTarget', 'Node'].forEach((target: string) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - const proto = (global as any)[target] && (global as any)[target].prototype; + const proto = (WINDOW as any)[target] && (WINDOW as any)[target].prototype; // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, no-prototype-builtins if (!proto || !proto.hasOwnProperty || !proto.hasOwnProperty('addEventListener')) { return; @@ -582,9 +580,9 @@ function instrumentDOM(): void { let _oldOnErrorHandler: OnErrorEventHandler = null; /** JSDoc */ function instrumentError(): void { - _oldOnErrorHandler = global.onerror; + _oldOnErrorHandler = WINDOW.onerror; - global.onerror = function (msg: any, url: any, line: any, column: any, error: any): boolean { + WINDOW.onerror = function (msg: any, url: any, line: any, column: any, error: any): boolean { triggerHandlers('error', { column, error, @@ -605,9 +603,9 @@ function instrumentError(): void { let _oldOnUnhandledRejectionHandler: ((e: any) => void) | null = null; /** JSDoc */ function instrumentUnhandledRejection(): void { - _oldOnUnhandledRejectionHandler = global.onunhandledrejection; + _oldOnUnhandledRejectionHandler = WINDOW.onunhandledrejection; - global.onunhandledrejection = function (e: any): boolean { + WINDOW.onunhandledrejection = function (e: any): boolean { triggerHandlers('unhandledrejection', e); if (_oldOnUnhandledRejectionHandler) { diff --git a/packages/utils/src/logger.ts b/packages/utils/src/logger.ts index b7111a216871..031c969a930d 100644 --- a/packages/utils/src/logger.ts +++ b/packages/utils/src/logger.ts @@ -1,9 +1,6 @@ import { WrappedFunction } from '@sentry/types'; -import { getGlobalObject, getGlobalSingleton } from './global'; - -// TODO: Implement different loggers for different environments -const global = getGlobalObject(); +import { getGlobalSingleton, GLOBAL_OBJ } from './global'; /** Prefix for logging strings */ const PREFIX = 'Sentry Logger '; @@ -27,13 +24,11 @@ interface Logger extends LoggerConsoleMethods { * @returns The results of the callback */ export function consoleSandbox(callback: () => T): T { - const global = getGlobalObject(); - - if (!('console' in global)) { + if (!('console' in GLOBAL_OBJ)) { return callback(); } - const originalConsole = global.console as Console & Record; + const originalConsole = GLOBAL_OBJ.console as Console & Record; const wrappedLevels: Partial = {}; // Restore all wrapped console methods @@ -41,7 +36,7 @@ export function consoleSandbox(callback: () => T): T { // TODO(v7): Remove this check as it's only needed for Node 6 const originalWrappedFunc = originalConsole[level] && (originalConsole[level] as WrappedFunction).__sentry_original__; - if (level in global.console && originalWrappedFunc) { + if (level in originalConsole && originalWrappedFunc) { wrappedLevels[level] = originalConsole[level] as LoggerConsoleMethods[typeof level]; originalConsole[level] = originalWrappedFunc as Console[typeof level]; } @@ -74,7 +69,7 @@ function makeLogger(): Logger { logger[name] = (...args: any[]) => { if (enabled) { consoleSandbox(() => { - global.console[name](`${PREFIX}[${name}]:`, ...args); + GLOBAL_OBJ.console[name](`${PREFIX}[${name}]:`, ...args); }); } }; diff --git a/packages/utils/src/misc.ts b/packages/utils/src/misc.ts index 964bc12c13bc..8c24439a47d4 100644 --- a/packages/utils/src/misc.ts +++ b/packages/utils/src/misc.ts @@ -1,20 +1,19 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Event, Exception, Mechanism, StackFrame } from '@sentry/types'; -import { getGlobalObject } from './global'; +import { GLOBAL_OBJ } from './global'; import { addNonEnumerableProperty } from './object'; import { snipLine } from './string'; -/** - * Extended Window interface that allows for Crypto API usage in IE browsers - */ -interface MsCryptoWindow extends Window { - msCrypto?: Crypto; +interface CryptoInternal { + getRandomValues(array: Uint8Array): Uint8Array; + randomUUID?(): string; } -/** Many browser now support native uuid v4 generation */ -interface CryptoWithRandomUUID extends Crypto { - randomUUID?(): string; +/** An interface for common properties on global */ +interface CryptoGlobal { + msCrypto?: CryptoInternal; + crypto?: CryptoInternal; } /** @@ -23,8 +22,8 @@ interface CryptoWithRandomUUID extends Crypto { * @returns string Generated UUID4. */ export function uuid4(): string { - const global = getGlobalObject() as MsCryptoWindow; - const crypto = (global.crypto || global.msCrypto) as CryptoWithRandomUUID; + const gbl = GLOBAL_OBJ as typeof GLOBAL_OBJ & CryptoGlobal; + const crypto = gbl.crypto || gbl.msCrypto; if (crypto && crypto.randomUUID) { return crypto.randomUUID().replace(/-/g, ''); diff --git a/packages/utils/src/supports.ts b/packages/utils/src/supports.ts index 4e3e10834bd2..c370bbb30621 100644 --- a/packages/utils/src/supports.ts +++ b/packages/utils/src/supports.ts @@ -1,4 +1,4 @@ -import { getGlobalObject } from './global'; +import { WINDOW } from './browser'; import { logger } from './logger'; /** @@ -56,7 +56,7 @@ export function supportsDOMException(): boolean { * @returns Answer to the given question. */ export function supportsFetch(): boolean { - if (!('fetch' in getGlobalObject())) { + if (!('fetch' in WINDOW)) { return false; } @@ -88,18 +88,16 @@ export function supportsNativeFetch(): boolean { return false; } - const global = getGlobalObject(); - // Fast path to avoid DOM I/O // eslint-disable-next-line @typescript-eslint/unbound-method - if (isNativeFetch(global.fetch)) { + if (isNativeFetch(WINDOW.fetch)) { return true; } // window.fetch is implemented, but is polyfilled or already wrapped (e.g: by a chrome extension) // so create a "pure" iframe to see if that has native fetch let result = false; - const doc = global.document; + const doc = WINDOW.document; // eslint-disable-next-line deprecation/deprecation if (doc && typeof (doc.createElement as unknown) === 'function') { try { @@ -127,7 +125,7 @@ export function supportsNativeFetch(): boolean { * @returns Answer to the given question. */ export function supportsReportingObserver(): boolean { - return 'ReportingObserver' in getGlobalObject(); + return 'ReportingObserver' in WINDOW; } /** @@ -166,13 +164,12 @@ export function supportsHistory(): boolean { // NOTE: in Chrome App environment, touching history.pushState, *even inside // a try/catch block*, will cause Chrome to output an error to console.error // borrowed from: https://github.com/angular/angular.js/pull/13945/files - const global = getGlobalObject(); /* eslint-disable @typescript-eslint/no-unsafe-member-access */ // eslint-disable-next-line @typescript-eslint/no-explicit-any - const chrome = (global as any).chrome; + const chrome = (WINDOW as any).chrome; const isChromePackagedApp = chrome && chrome.app && chrome.app.runtime; /* eslint-enable @typescript-eslint/no-unsafe-member-access */ - const hasHistoryApi = 'history' in global && !!global.history.pushState && !!global.history.replaceState; + const hasHistoryApi = 'history' in WINDOW && !!WINDOW.history.pushState && !!WINDOW.history.replaceState; return !isChromePackagedApp && hasHistoryApi; } diff --git a/packages/utils/src/time.ts b/packages/utils/src/time.ts index 135bd2b5b5f4..b2d9628db5d9 100644 --- a/packages/utils/src/time.ts +++ b/packages/utils/src/time.ts @@ -1,4 +1,4 @@ -import { getGlobalObject } from './global'; +import { WINDOW } from './browser'; import { dynamicRequire, isNodeEnv } from './node'; /** @@ -41,7 +41,7 @@ interface Performance { * Wrapping the native API works around differences in behavior from different browsers. */ function getBrowserPerformance(): Performance | undefined { - const { performance } = getGlobalObject(); + const { performance } = WINDOW; if (!performance || !performance.now) { return undefined; } @@ -140,7 +140,7 @@ export const browserPerformanceTimeOrigin = ((): number | undefined => { // performance.timing.navigationStart, which results in poor results in performance data. We only treat time origin // data as reliable if they are within a reasonable threshold of the current time. - const { performance } = getGlobalObject(); + const { performance } = WINDOW; if (!performance || !performance.now) { _browserPerformanceTimeOriginMode = 'none'; return undefined; diff --git a/packages/utils/test/global.test.ts b/packages/utils/test/global.test.ts index 908cc59a9772..722abf678bb0 100644 --- a/packages/utils/test/global.test.ts +++ b/packages/utils/test/global.test.ts @@ -1,11 +1,11 @@ -import { getGlobalObject } from '../src/global'; +import { GLOBAL_OBJ } from '../src/global'; -describe('getGlobalObject()', () => { +describe('GLOBAL_OBJ', () => { test('should return the same object', () => { const backup = global.process; delete global.process; - const first = getGlobalObject(); - const second = getGlobalObject(); + const first = GLOBAL_OBJ; + const second = GLOBAL_OBJ; expect(first).toEqual(second); global.process = backup; }); From b2fce7dbd28cba1d5daef787a2f820e6c6f75c9a Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Wed, 5 Oct 2022 11:15:08 +0200 Subject: [PATCH 2/2] Use `GLOBAL_OBJ` and `WINDOW` rather than `getGlobalObject` --- packages/tracing/src/browser/backgroundtab.ts | 10 ++++------ packages/tracing/src/browser/browsertracing.ts | 4 ++-- packages/tracing/src/browser/metrics/index.ts | 14 ++++++-------- packages/tracing/src/browser/router.ts | 12 +++++------- .../browser/web-vitals/lib/getVisibilityWatcher.ts | 4 ++-- .../tracing/src/browser/web-vitals/lib/onHidden.ts | 4 ++-- packages/tracing/src/index.bundle.ts | 7 +++---- .../tracing/test/browser/browsertracing.test.ts | 12 ++++++------ packages/tracing/test/hub.test.ts | 2 -- packages/tracing/test/testutils.ts | 9 ++++----- 10 files changed, 34 insertions(+), 44 deletions(-) diff --git a/packages/tracing/src/browser/backgroundtab.ts b/packages/tracing/src/browser/backgroundtab.ts index 137779e16168..1aba2e9e5389 100644 --- a/packages/tracing/src/browser/backgroundtab.ts +++ b/packages/tracing/src/browser/backgroundtab.ts @@ -1,20 +1,18 @@ -import { getGlobalObject, logger } from '@sentry/utils'; +import { logger, WINDOW } from '@sentry/utils'; import { IdleTransaction } from '../idletransaction'; import { SpanStatusType } from '../span'; import { getActiveTransaction } from '../utils'; -const global = getGlobalObject(); - /** * Add a listener that cancels and finishes a transaction when the global * document is hidden. */ export function registerBackgroundTabDetection(): void { - if (global && global.document) { - global.document.addEventListener('visibilitychange', () => { + if (WINDOW && WINDOW.document) { + WINDOW.document.addEventListener('visibilitychange', () => { const activeTransaction = getActiveTransaction() as IdleTransaction; - if (global.document.hidden && activeTransaction) { + if (WINDOW.document.hidden && activeTransaction) { const statusType: SpanStatusType = 'cancelled'; __DEBUG_BUILD__ && diff --git a/packages/tracing/src/browser/browsertracing.ts b/packages/tracing/src/browser/browsertracing.ts index 8a06f029d53f..69cfa2a155e9 100644 --- a/packages/tracing/src/browser/browsertracing.ts +++ b/packages/tracing/src/browser/browsertracing.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ import { Hub } from '@sentry/hub'; import { EventProcessor, Integration, Transaction, TransactionContext } from '@sentry/types'; -import { baggageHeaderToDynamicSamplingContext, getDomElement, getGlobalObject, logger } from '@sentry/utils'; +import { baggageHeaderToDynamicSamplingContext, getDomElement, logger, WINDOW } from '@sentry/utils'; import { startIdleTransaction } from '../hubextensions'; import { DEFAULT_FINAL_TIMEOUT, DEFAULT_IDLE_TIMEOUT } from '../idletransaction'; @@ -255,7 +255,7 @@ export class BrowserTracing implements Integration { __DEBUG_BUILD__ && logger.log(`[Tracing] Starting ${finalContext.op} transaction on scope`); const hub = this._getCurrentHub(); - const { location } = getGlobalObject() as WindowOrWorkerGlobalScope & { location: Location }; + const { location } = WINDOW; const idleTransaction = startIdleTransaction( hub, diff --git a/packages/tracing/src/browser/metrics/index.ts b/packages/tracing/src/browser/metrics/index.ts index d77b05fed837..c61f69a331d6 100644 --- a/packages/tracing/src/browser/metrics/index.ts +++ b/packages/tracing/src/browser/metrics/index.ts @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ import { Measurements } from '@sentry/types'; -import { browserPerformanceTimeOrigin, getGlobalObject, htmlTreeAsString, logger } from '@sentry/utils'; +import { browserPerformanceTimeOrigin, htmlTreeAsString, logger, WINDOW } from '@sentry/utils'; import { IdleTransaction } from '../../idletransaction'; import { Transaction } from '../../transaction'; @@ -13,10 +13,8 @@ import { observe, PerformanceEntryHandler } from '../web-vitals/lib/observe'; import { NavigatorDeviceMemory, NavigatorNetworkInformation } from '../web-vitals/types'; import { _startChild, isMeasurementValue } from './utils'; -const global = getGlobalObject(); - function getBrowserPerformanceAPI(): Performance | undefined { - return global && global.addEventListener && global.performance; + return WINDOW && WINDOW.addEventListener && WINDOW.performance; } let _performanceCursor: number = 0; @@ -32,7 +30,7 @@ export function startTrackingWebVitals(reportAllChanges: boolean = false): void const performance = getBrowserPerformanceAPI(); if (performance && browserPerformanceTimeOrigin) { if (performance.mark) { - global.performance.mark('sentry-tracing-init'); + WINDOW.performance.mark('sentry-tracing-init'); } _trackCLS(); _trackLCP(reportAllChanges); @@ -112,7 +110,7 @@ function _trackFID(): void { /** Add performance related spans to a transaction */ export function addPerformanceEntries(transaction: Transaction): void { const performance = getBrowserPerformanceAPI(); - if (!performance || !global.performance.getEntries || !browserPerformanceTimeOrigin) { + if (!performance || !WINDOW.performance.getEntries || !browserPerformanceTimeOrigin) { // Gatekeeper if performance API not available return; } @@ -162,7 +160,7 @@ export function addPerformanceEntries(transaction: Transaction): void { break; } case 'resource': { - const resourceName = (entry.name as string).replace(global.location.origin, ''); + const resourceName = (entry.name as string).replace(WINDOW.location.origin, ''); _addResourceSpans(transaction, entry, resourceName, startTime, duration, timeOrigin); break; } @@ -376,7 +374,7 @@ export function _addResourceSpans( * Capture the information of the user agent. */ function _trackNavigator(transaction: Transaction): void { - const navigator = global.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory); + const navigator = WINDOW.navigator as null | (Navigator & NavigatorNetworkInformation & NavigatorDeviceMemory); if (!navigator) { return; } diff --git a/packages/tracing/src/browser/router.ts b/packages/tracing/src/browser/router.ts index b66037f38512..871f74383815 100644 --- a/packages/tracing/src/browser/router.ts +++ b/packages/tracing/src/browser/router.ts @@ -1,7 +1,5 @@ import { Transaction, TransactionContext } from '@sentry/types'; -import { addInstrumentationHandler, getGlobalObject, logger } from '@sentry/utils'; - -const global = getGlobalObject(); +import { addInstrumentationHandler, logger, WINDOW } from '@sentry/utils'; /** * Default function implementing pageload and navigation transactions @@ -11,17 +9,17 @@ export function instrumentRoutingWithDefaults( startTransactionOnPageLoad: boolean = true, startTransactionOnLocationChange: boolean = true, ): void { - if (!global || !global.location) { + if (!WINDOW || !WINDOW.location) { __DEBUG_BUILD__ && logger.warn('Could not initialize routing instrumentation due to invalid location'); return; } - let startingUrl: string | undefined = global.location.href; + let startingUrl: string | undefined = WINDOW.location.href; let activeTransaction: T | undefined; if (startTransactionOnPageLoad) { activeTransaction = customStartTransaction({ - name: global.location.pathname, + name: WINDOW.location.pathname, op: 'pageload', metadata: { source: 'url' }, }); @@ -51,7 +49,7 @@ export function instrumentRoutingWithDefaults( activeTransaction.finish(); } activeTransaction = customStartTransaction({ - name: global.location.pathname, + name: WINDOW.location.pathname, op: 'navigation', metadata: { source: 'url' }, }); diff --git a/packages/tracing/src/browser/web-vitals/lib/getVisibilityWatcher.ts b/packages/tracing/src/browser/web-vitals/lib/getVisibilityWatcher.ts index 8769b6d80935..371133ff6df2 100644 --- a/packages/tracing/src/browser/web-vitals/lib/getVisibilityWatcher.ts +++ b/packages/tracing/src/browser/web-vitals/lib/getVisibilityWatcher.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -import { getGlobalObject } from '@sentry/utils'; +import { WINDOW } from '@sentry/utils'; import { onHidden } from './onHidden'; let firstHiddenTime = -1; const initHiddenTime = (): number => { - return getGlobalObject().document.visibilityState === 'hidden' ? 0 : Infinity; + return WINDOW.document.visibilityState === 'hidden' ? 0 : Infinity; }; const trackChanges = (): void => { diff --git a/packages/tracing/src/browser/web-vitals/lib/onHidden.ts b/packages/tracing/src/browser/web-vitals/lib/onHidden.ts index c1a0d4df231f..2b0c7329f3a5 100644 --- a/packages/tracing/src/browser/web-vitals/lib/onHidden.ts +++ b/packages/tracing/src/browser/web-vitals/lib/onHidden.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { getGlobalObject } from '@sentry/utils'; +import { WINDOW } from '@sentry/utils'; export interface OnHiddenCallback { (event: Event): void; @@ -22,7 +22,7 @@ export interface OnHiddenCallback { export const onHidden = (cb: OnHiddenCallback, once?: boolean): void => { const onHiddenOrPageHide = (event: Event): void => { - if (event.type === 'pagehide' || getGlobalObject().document.visibilityState === 'hidden') { + if (event.type === 'pagehide' || WINDOW.document.visibilityState === 'hidden') { cb(event); if (once) { removeEventListener('visibilitychange', onHiddenOrPageHide, true); diff --git a/packages/tracing/src/index.bundle.ts b/packages/tracing/src/index.bundle.ts index b765cb0cca0a..13d2bb38c2e2 100644 --- a/packages/tracing/src/index.bundle.ts +++ b/packages/tracing/src/index.bundle.ts @@ -53,7 +53,7 @@ export { export { SDK_VERSION } from '@sentry/browser'; import { Integrations as BrowserIntegrations } from '@sentry/browser'; -import { getGlobalObject } from '@sentry/utils'; +import { GLOBAL_OBJ } from '@sentry/utils'; import { BrowserTracing } from './browser'; import { addExtensionMethods } from './hubextensions'; @@ -63,9 +63,8 @@ export { Span } from './span'; let windowIntegrations = {}; // This block is needed to add compatibility with the integrations packages when used with a CDN -const _window = getGlobalObject(); -if (_window.Sentry && _window.Sentry.Integrations) { - windowIntegrations = _window.Sentry.Integrations; +if (GLOBAL_OBJ.Sentry && GLOBAL_OBJ.Sentry.Integrations) { + windowIntegrations = GLOBAL_OBJ.Sentry.Integrations; } const INTEGRATIONS = { diff --git a/packages/tracing/test/browser/browsertracing.test.ts b/packages/tracing/test/browser/browsertracing.test.ts index 2fc24fa1d670..fbcd9132bff3 100644 --- a/packages/tracing/test/browser/browsertracing.test.ts +++ b/packages/tracing/test/browser/browsertracing.test.ts @@ -1,7 +1,7 @@ import { BrowserClient } from '@sentry/browser'; import { Hub, makeMain } from '@sentry/hub'; import type { BaseTransportOptions, ClientOptions, DsnComponents } from '@sentry/types'; -import { getGlobalObject, InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; +import { InstrumentHandlerCallback, InstrumentHandlerType, WINDOW } from '@sentry/utils'; import { JSDOM } from 'jsdom'; import { BrowserTracing, BrowserTracingOptions, getMetaContent } from '../../src/browser/browsertracing'; @@ -35,11 +35,11 @@ const warnSpy = jest.spyOn(logger, 'warn'); beforeAll(() => { const dom = new JSDOM(); // @ts-ignore need to override global document - global.document = dom.window.document; + WINDOW.document = dom.window.document; // @ts-ignore need to override global document - global.window = dom.window; + WINDOW.window = dom.window; // @ts-ignore need to override global document - global.location = dom.window.location; + WINDOW.location = dom.window.location; }); describe('BrowserTracing', () => { @@ -484,7 +484,7 @@ describe('BrowserTracing', () => { }; it('extracts window.location/self.location for sampling context in pageload transactions', () => { - getGlobalObject().location = dogParkLocation as any; + WINDOW.location = dogParkLocation as any; const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); @@ -501,7 +501,7 @@ describe('BrowserTracing', () => { }); it('extracts window.location/self.location for sampling context in navigation transactions', () => { - getGlobalObject().location = dogParkLocation as any; + WINDOW.location = dogParkLocation as any; const tracesSampler = jest.fn(); const options = getDefaultBrowserClientOptions({ tracesSampler }); diff --git a/packages/tracing/test/hub.test.ts b/packages/tracing/test/hub.test.ts index e859a302878e..823f190ecb43 100644 --- a/packages/tracing/test/hub.test.ts +++ b/packages/tracing/test/hub.test.ts @@ -23,8 +23,6 @@ jest.spyOn(logger, 'warn'); jest.spyOn(logger, 'log'); jest.spyOn(utilsModule, 'isNodeEnv'); -// we have to add things into the real global object (rather than mocking the return value of getGlobalObject) because -// there are modules which call getGlobalObject as they load, which is seemingly too early for jest to intervene addDOMPropertiesToGlobal(['XMLHttpRequest', 'Event', 'location', 'document']); describe('Hub', () => { diff --git a/packages/tracing/test/testutils.ts b/packages/tracing/test/testutils.ts index 5b4594c64913..474eb57846f4 100644 --- a/packages/tracing/test/testutils.ts +++ b/packages/tracing/test/testutils.ts @@ -1,6 +1,6 @@ import { createTransport } from '@sentry/browser'; import { ClientOptions } from '@sentry/types'; -import { getGlobalObject, resolvedSyncPromise } from '@sentry/utils'; +import { GLOBAL_OBJ, resolvedSyncPromise } from '@sentry/utils'; import { JSDOM } from 'jsdom'; /** @@ -13,14 +13,13 @@ import { JSDOM } from 'jsdom'; * @param properties The names of the properties to add */ export function addDOMPropertiesToGlobal(properties: string[]): void { - // we have to add things into the real global object (rather than mocking the return value of getGlobalObject) - // because there are modules which call getGlobalObject as they load, which is too early for jest to intervene + // we have to add things into the real global object + // because there are modules which call GLOBAL_OBJ as they load, which is too early for jest to intervene const { window } = new JSDOM('', { url: 'http://dogs.are.great/' }); - const global = getGlobalObject(); properties.forEach(prop => { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - (global as any)[prop] = window[prop]; + (GLOBAL_OBJ as any)[prop] = window[prop]; }); }