diff --git a/packages/angular/src/tracing.ts b/packages/angular/src/tracing.ts index 797aa10b16b7..057734d5f6e5 100644 --- a/packages/angular/src/tracing.ts +++ b/packages/angular/src/tracing.ts @@ -3,7 +3,7 @@ import { AfterViewInit, Directive, Injectable, Input, NgModule, OnDestroy, OnIni import { ActivatedRouteSnapshot, Event, NavigationEnd, NavigationStart, ResolveEnd, Router } from '@angular/router'; import { getCurrentHub } from '@sentry/browser'; import { Span, Transaction, TransactionContext } from '@sentry/types'; -import { getGlobalObject, logger, stripUrlQueryAndFragment, timestampWithMs } from '@sentry/utils'; +import { logger, stripUrlQueryAndFragment, timestampWithMs, WINDOW } from '@sentry/utils'; import { Observable, Subscription } from 'rxjs'; import { filter, tap } from 'rxjs/operators'; @@ -15,8 +15,6 @@ let instrumentationInitialized: boolean; let stashedStartTransaction: (context: TransactionContext) => Transaction | undefined; let stashedStartTransactionOnLocationChange: boolean; -const global = getGlobalObject(); - /** * Creates routing instrumentation for Angular Router. */ @@ -29,9 +27,9 @@ export function routingInstrumentation( stashedStartTransaction = customStartTransaction; stashedStartTransactionOnLocationChange = startTransactionOnLocationChange; - if (startTransactionOnPageLoad && global && global.location) { + if (startTransactionOnPageLoad && WINDOW && WINDOW.location) { customStartTransaction({ - name: global.location.pathname, + name: WINDOW.location.pathname, op: 'pageload', metadata: { source: 'url' }, }); diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 04c5b580aa83..dc419e680dbb 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -4,9 +4,9 @@ import { createClientReportEnvelope, dsnToString, getEventDescription, - getGlobalObject, logger, serializeEnvelope, + WINDOW, } from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; @@ -14,8 +14,6 @@ import { Breadcrumbs } from './integrations'; import { BREADCRUMB_INTEGRATION_ID } from './integrations/breadcrumbs'; import { BrowserTransportOptions } from './transports/types'; -const globalObject = getGlobalObject(); - export interface BaseBrowserOptions { /** * A pattern for error URLs which should exclusively be sent to Sentry. @@ -71,9 +69,9 @@ export class BrowserClient extends BaseClient { super(options); - if (options.sendClientReports && globalObject.document) { - globalObject.document.addEventListener('visibilitychange', () => { - if (globalObject.document.visibilityState === 'hidden') { + if (options.sendClientReports && WINDOW.document) { + WINDOW.document.addEventListener('visibilitychange', () => { + if (WINDOW.document.visibilityState === 'hidden') { this._flushOutcomes(); } }); @@ -164,13 +162,12 @@ export class BrowserClient extends BaseClient { const envelope = createClientReportEnvelope(outcomes, this._options.tunnel && dsnToString(this._dsn)); try { - const global = getGlobalObject(); - const isRealNavigator = Object.prototype.toString.call(global && global.navigator) === '[object Navigator]'; - const hasSendBeacon = isRealNavigator && typeof global.navigator.sendBeacon === 'function'; + const isRealNavigator = Object.prototype.toString.call(WINDOW && WINDOW.navigator) === '[object Navigator]'; + const hasSendBeacon = isRealNavigator && typeof WINDOW.navigator.sendBeacon === 'function'; // Make sure beacon is not used if user configures custom transport options if (hasSendBeacon && !this._options.transportOptions) { // Prevent illegal invocations - https://xgwang.me/posts/you-may-not-know-beacon/#it-may-throw-error%2C-be-sure-to-catch - const sendBeacon = global.navigator.sendBeacon.bind(global.navigator); + const sendBeacon = WINDOW.navigator.sendBeacon.bind(WINDOW.navigator); sendBeacon(url, serializeEnvelope(envelope)); } else { // If beacon is not supported or if they are using the tunnel option diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index ab754f6a192f..625df5c37d8d 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -1,16 +1,15 @@ export * from './exports'; import { Integrations as CoreIntegrations } from '@sentry/core'; -import { getGlobalObject } from '@sentry/utils'; +import { WINDOW } from '@sentry/utils'; import * as BrowserIntegrations from './integrations'; 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 (WINDOW.Sentry && WINDOW.Sentry.Integrations) { + windowIntegrations = WINDOW.Sentry.Integrations; } const INTEGRATIONS = { diff --git a/packages/browser/src/integrations/breadcrumbs.ts b/packages/browser/src/integrations/breadcrumbs.ts index c633bf5d139c..6f89732e9767 100644 --- a/packages/browser/src/integrations/breadcrumbs.ts +++ b/packages/browser/src/integrations/breadcrumbs.ts @@ -4,11 +4,11 @@ import { getCurrentHub } from '@sentry/core'; import { Integration } from '@sentry/types'; import { addInstrumentationHandler, - getGlobalObject, htmlTreeAsString, parseUrl, safeJoin, severityLevelFromString, + WINDOW, } from '@sentry/utils'; /** JSDoc */ @@ -245,10 +245,9 @@ function _fetchBreadcrumb(handlerData: { [key: string]: any }): void { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any function _historyBreadcrumb(handlerData: { [key: string]: any }): void { - const global = getGlobalObject(); let from = handlerData.from; let to = handlerData.to; - const parsedLoc = parseUrl(global.location.href); + const parsedLoc = parseUrl(WINDOW.location.href); let parsedFrom = parseUrl(from); const parsedTo = parseUrl(to); diff --git a/packages/browser/src/integrations/httpcontext.ts b/packages/browser/src/integrations/httpcontext.ts index 2c7d5aa4e841..1d11845510f6 100644 --- a/packages/browser/src/integrations/httpcontext.ts +++ b/packages/browser/src/integrations/httpcontext.ts @@ -1,8 +1,6 @@ import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; import { Event, Integration } from '@sentry/types'; -import { getGlobalObject } from '@sentry/utils'; - -const global = getGlobalObject(); +import { WINDOW } from '@sentry/utils'; /** HttpContext integration collects information about HTTP request headers */ export class HttpContext implements Integration { @@ -23,14 +21,14 @@ export class HttpContext implements Integration { addGlobalEventProcessor((event: Event) => { if (getCurrentHub().getIntegration(HttpContext)) { // if none of the information we want exists, don't bother - if (!global.navigator && !global.location && !global.document) { + 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) || (global.location && global.location.href); - const { referrer } = global.document || {}; - const { userAgent } = global.navigator || {}; + 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), diff --git a/packages/browser/src/integrations/trycatch.ts b/packages/browser/src/integrations/trycatch.ts index b6b293d70c37..af06c54d6290 100644 --- a/packages/browser/src/integrations/trycatch.ts +++ b/packages/browser/src/integrations/trycatch.ts @@ -1,5 +1,5 @@ import { Integration, WrappedFunction } from '@sentry/types'; -import { fill, getFunctionName, getGlobalObject, getOriginalFunction } from '@sentry/utils'; +import { fill, getFunctionName, getOriginalFunction, WINDOW } from '@sentry/utils'; import { wrap } from '../helpers'; @@ -80,21 +80,19 @@ export class TryCatch implements Integration { * and provide better metadata. */ public setupOnce(): void { - const global = getGlobalObject(); - if (this._options.setTimeout) { - fill(global, 'setTimeout', _wrapTimeFunction); + fill(WINDOW, 'setTimeout', _wrapTimeFunction); } if (this._options.setInterval) { - fill(global, 'setInterval', _wrapTimeFunction); + fill(WINDOW, 'setInterval', _wrapTimeFunction); } if (this._options.requestAnimationFrame) { - fill(global, 'requestAnimationFrame', _wrapRAF); + fill(WINDOW, 'requestAnimationFrame', _wrapRAF); } - if (this._options.XMLHttpRequest && 'XMLHttpRequest' in global) { + if (this._options.XMLHttpRequest && 'XMLHttpRequest' in WINDOW) { fill(XMLHttpRequest.prototype, 'send', _wrapXHR); } @@ -185,7 +183,7 @@ function _wrapXHR(originalSend: () => void): () => void { /** JSDoc */ function _wrapEventTarget(target: string): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any - const global = getGlobalObject() as { [key: string]: any }; + const global = WINDOW as { [key: string]: any }; // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access const proto = global[target] && global[target].prototype; diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index e4cb2cfa279c..2c5c9fd63235 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -8,11 +8,11 @@ import { } from '@sentry/core'; import { addInstrumentationHandler, - getGlobalObject, logger, resolvedSyncPromise, stackParserFromStackParserOptions, supportsFetch, + WINDOW, } from '@sentry/utils'; import { BrowserClient, BrowserClientOptions, BrowserOptions } from './client'; @@ -94,10 +94,9 @@ export function init(options: BrowserOptions = {}): void { options.defaultIntegrations = defaultIntegrations; } if (options.release === undefined) { - const window = getGlobalObject(); // This supports the variable that sentry-webpack-plugin injects - if (window.SENTRY_RELEASE && window.SENTRY_RELEASE.id) { - options.release = window.SENTRY_RELEASE.id; + if (WINDOW.SENTRY_RELEASE && WINDOW.SENTRY_RELEASE.id) { + options.release = WINDOW.SENTRY_RELEASE.id; } } if (options.autoSessionTracking === undefined) { @@ -128,8 +127,7 @@ export function init(options: BrowserOptions = {}): void { */ export function showReportDialog(options: ReportDialogOptions = {}, hub: Hub = getCurrentHub()): void { // doesn't work without a document (React Native) - const global = getGlobalObject(); - if (!global.document) { + if (!WINDOW.document) { __DEBUG_BUILD__ && logger.error('Global document not defined in showReportDialog call'); return; } @@ -152,7 +150,7 @@ export function showReportDialog(options: ReportDialogOptions = {}, hub: Hub = g options.eventId = hub.lastEventId(); } - const script = global.document.createElement('script'); + const script = WINDOW.document.createElement('script'); script.async = true; script.src = getReportDialogEndpoint(dsn, options); @@ -161,7 +159,7 @@ export function showReportDialog(options: ReportDialogOptions = {}, hub: Hub = g script.onload = options.onLoad; } - const injectionPoint = global.document.head || global.document.body; + const injectionPoint = WINDOW.document.head || WINDOW.document.body; if (injectionPoint) { injectionPoint.appendChild(script); } else { @@ -249,10 +247,7 @@ function startSessionOnHub(hub: Hub): void { * Enable automatic Session Tracking for the initial page load. */ function startSessionTracking(): void { - const window = getGlobalObject(); - const document = window.document; - - if (typeof document === 'undefined') { + if (typeof WINDOW.document === 'undefined') { __DEBUG_BUILD__ && logger.warn('Session tracking in non-browser environment with @sentry/browser is not supported.'); return; diff --git a/packages/browser/src/transports/utils.ts b/packages/browser/src/transports/utils.ts index 405f35e400ff..137cbc88487e 100644 --- a/packages/browser/src/transports/utils.ts +++ b/packages/browser/src/transports/utils.ts @@ -1,6 +1,5 @@ -import { getGlobalObject, isNativeFetch, logger } from '@sentry/utils'; +import { isNativeFetch, logger, WINDOW } from '@sentry/utils'; -const global = getGlobalObject(); let cachedFetchImpl: FetchImpl; export type FetchImpl = typeof fetch; @@ -51,12 +50,12 @@ export function getNativeFetchImplementation(): FetchImpl { /* eslint-disable @typescript-eslint/unbound-method */ // Fast path to avoid DOM I/O - if (isNativeFetch(global.fetch)) { - return (cachedFetchImpl = global.fetch.bind(global)); + if (isNativeFetch(WINDOW.fetch)) { + return (cachedFetchImpl = WINDOW.fetch.bind(WINDOW)); } - const document = global.document; - let fetchImpl = global.fetch; + const document = WINDOW.document; + let fetchImpl = WINDOW.fetch; // eslint-disable-next-line deprecation/deprecation if (document && typeof document.createElement === 'function') { try { @@ -74,6 +73,6 @@ export function getNativeFetchImplementation(): FetchImpl { } } - return (cachedFetchImpl = fetchImpl.bind(global)); + return (cachedFetchImpl = fetchImpl.bind(WINDOW)); /* eslint-enable @typescript-eslint/unbound-method */ } diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 4d0bf265af46..1e3ea2ad9565 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -22,20 +22,18 @@ jest.mock('@sentry/utils', () => { uuid4(): string { return '42'; }, - getGlobalObject(): any { - return { - console: { - log(): void { - // no-empty - }, - warn(): void { - // no-empty - }, - error(): void { - // no-empty - }, + GLOBAL_OBJ: { + console: { + log(): void { + // no-empty }, - }; + warn(): void { + // no-empty + }, + error(): void { + // no-empty + }, + }, }, consoleSandbox(cb: () => any): any { return cb(); diff --git a/packages/core/test/lib/hint.test.ts b/packages/core/test/lib/hint.test.ts index 03b755a63c71..a503614d133d 100644 --- a/packages/core/test/lib/hint.test.ts +++ b/packages/core/test/lib/hint.test.ts @@ -1,5 +1,5 @@ import { captureEvent, configureScope } from '@sentry/hub'; -import { getGlobalObject } from '@sentry/utils'; +import { GLOBAL_OBJ } from '@sentry/utils'; import { initAndBind } from '../../src/sdk'; import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; @@ -16,7 +16,7 @@ describe('Hint', () => { afterEach(() => { jest.clearAllMocks(); - delete getGlobalObject().__SENTRY__; + delete GLOBAL_OBJ.__SENTRY__; }); describe('attachments', () => { diff --git a/packages/ember/addon/index.ts b/packages/ember/addon/index.ts index da359e705dd4..c69d5c57674c 100644 --- a/packages/ember/addon/index.ts +++ b/packages/ember/addon/index.ts @@ -4,12 +4,11 @@ import { macroCondition, isDevelopingApp, getOwnConfig } from '@embroider/macros import { next } from '@ember/runloop'; import { assert, warn } from '@ember/debug'; import Ember from 'ember'; -import { timestampWithMs } from '@sentry/utils'; +import { timestampWithMs, GLOBAL_OBJ } from '@sentry/utils'; import { GlobalConfig, OwnConfig } from './types'; -import { getGlobalObject } from '@sentry/utils'; function _getSentryInitConfig() { - const _global = getGlobalObject(); + const _global = GLOBAL_OBJ as typeof GLOBAL_OBJ & GlobalConfig; _global.__sentryEmberConfig = _global.__sentryEmberConfig ?? {}; return _global.__sentryEmberConfig; } diff --git a/packages/ember/addon/instance-initializers/sentry-performance.ts b/packages/ember/addon/instance-initializers/sentry-performance.ts index 8f992f8716cc..e45a26295348 100644 --- a/packages/ember/addon/instance-initializers/sentry-performance.ts +++ b/packages/ember/addon/instance-initializers/sentry-performance.ts @@ -6,12 +6,12 @@ import { ExtendedBackburner } from '@sentry/ember/runloop'; import { Span, Transaction, Integration } from '@sentry/types'; import { EmberRunQueues } from '@ember/runloop/-private/types'; import { getActiveTransaction } from '..'; -import { browserPerformanceTimeOrigin, getGlobalObject, timestampWithMs } from '@sentry/utils'; +import { browserPerformanceTimeOrigin, GLOBAL_OBJ, timestampWithMs } from '@sentry/utils'; import { macroCondition, isTesting, getOwnConfig } from '@embroider/macros'; import { EmberSentryConfig, GlobalConfig, OwnConfig } from '../types'; function getSentryConfig() { - const _global = getGlobalObject(); + const _global = GLOBAL_OBJ as typeof GLOBAL_OBJ & GlobalConfig; _global.__sentryEmberConfig = _global.__sentryEmberConfig ?? {}; const environmentConfig = getOwnConfig().sentryConfig; if (!environmentConfig.sentry) { diff --git a/packages/hub/src/hub.ts b/packages/hub/src/hub.ts index dc5e6ccefd21..5036158e08eb 100644 --- a/packages/hub/src/hub.ts +++ b/packages/hub/src/hub.ts @@ -23,8 +23,8 @@ import { import { consoleSandbox, dateTimestampInSeconds, - getGlobalObject, getGlobalSingleton, + GLOBAL_OBJ, isNodeEnv, logger, uuid4, @@ -413,8 +413,7 @@ export class Hub implements HubInterface { const { release, environment } = (client && client.getOptions()) || {}; // Will fetch userAgent if called from browser sdk - const global = getGlobalObject<{ navigator?: { userAgent?: string } }>(); - const { userAgent } = global.navigator || {}; + const { userAgent } = GLOBAL_OBJ.navigator || {}; const session = makeSession({ release, @@ -500,12 +499,11 @@ export class Hub implements HubInterface { * at the call-site. We always access the carrier through this function, so we can guarantee that `__SENTRY__` is there. **/ export function getMainCarrier(): Carrier { - const carrier = getGlobalObject(); - carrier.__SENTRY__ = carrier.__SENTRY__ || { + GLOBAL_OBJ.__SENTRY__ = GLOBAL_OBJ.__SENTRY__ || { extensions: {}, hub: undefined, }; - return carrier; + return GLOBAL_OBJ; } /** diff --git a/packages/hub/test/global.test.ts b/packages/hub/test/global.test.ts index 8dc32acb7fc7..c388e870413d 100644 --- a/packages/hub/test/global.test.ts +++ b/packages/hub/test/global.test.ts @@ -1,13 +1,11 @@ -import { getGlobalObject } from '@sentry/utils'; +import { GLOBAL_OBJ } from '@sentry/utils'; import { getCurrentHub, getHubFromCarrier, Hub } from '../src'; -const global = getGlobalObject(); - describe('global', () => { test('getGlobalHub', () => { expect(getCurrentHub()).toBeTruthy(); - expect((global as any).__SENTRY__.hub).toBeTruthy(); + expect(GLOBAL_OBJ.__SENTRY__.hub).toBeTruthy(); }); test('getHubFromCarrier', () => { @@ -20,22 +18,21 @@ describe('global', () => { test('getGlobalHub', () => { const newestHub = new Hub(undefined, undefined, 999999); - (global as any).__SENTRY__.hub = newestHub; + GLOBAL_OBJ.__SENTRY__.hub = newestHub; expect(getCurrentHub()).toBe(newestHub); }); test('hub extension methods receive correct hub instance', () => { const newestHub = new Hub(undefined, undefined, 999999); - (global as any).__SENTRY__.hub = newestHub; + GLOBAL_OBJ.__SENTRY__.hub = newestHub; const fn = jest.fn().mockImplementation(function (...args: []) { // @ts-ignore typescript complains that this can be `any` expect(this).toBe(newestHub); expect(args).toEqual([1, 2, 3]); }); - (global as any).__SENTRY__.extensions = {}; - (global as any).__SENTRY__.extensions.testy = fn; + GLOBAL_OBJ.__SENTRY__.extensions = {}; + GLOBAL_OBJ.__SENTRY__.extensions.testy = fn; (getCurrentHub() as any)._callExtensionMethod('testy', 1, 2, 3); expect(fn).toBeCalled(); }); - // (global as any).__SENTRY__ }); diff --git a/packages/hub/test/scope.test.ts b/packages/hub/test/scope.test.ts index 1bad52db6d9f..d7f5934d1b26 100644 --- a/packages/hub/test/scope.test.ts +++ b/packages/hub/test/scope.test.ts @@ -1,5 +1,5 @@ import { Event, EventHint, RequestSessionStatus } from '@sentry/types'; -import { getGlobalObject } from '@sentry/utils'; +import { GLOBAL_OBJ } from '@sentry/utils'; import { addGlobalEventProcessor, Scope } from '../src'; @@ -7,7 +7,7 @@ describe('Scope', () => { afterEach(() => { jest.resetAllMocks(); jest.useRealTimers(); - getGlobalObject().__SENTRY__.globalEventProcessors = undefined; + GLOBAL_OBJ.__SENTRY__.globalEventProcessors = undefined; }); describe('attributes modification', () => { diff --git a/packages/integrations/src/captureconsole.ts b/packages/integrations/src/captureconsole.ts index 7243520c0661..ea9f2df40837 100644 --- a/packages/integrations/src/captureconsole.ts +++ b/packages/integrations/src/captureconsole.ts @@ -1,7 +1,5 @@ import { EventProcessor, Hub, Integration } from '@sentry/types'; -import { CONSOLE_LEVELS, fill, getGlobalObject, safeJoin, severityLevelFromString } from '@sentry/utils'; - -const global = getGlobalObject(); +import { CONSOLE_LEVELS, fill, GLOBAL_OBJ, safeJoin, severityLevelFromString } from '@sentry/utils'; /** Send Console API calls as Sentry Events */ export class CaptureConsole implements Integration { @@ -33,17 +31,18 @@ export class CaptureConsole implements Integration { * @inheritDoc */ public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { - if (!('console' in global)) { + if (!('console' in GLOBAL_OBJ)) { return; } this._levels.forEach((level: string) => { - if (!(level in global.console)) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any + if (!(level in (GLOBAL_OBJ as any).console)) { return; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - fill(global.console, level, (originalConsoleMethod: () => any) => (...args: any[]): void => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + fill((GLOBAL_OBJ as any).console, level, (originalConsoleMethod: () => any) => (...args: any[]): void => { const hub = getCurrentHub(); if (hub.getIntegration(CaptureConsole)) { @@ -72,7 +71,7 @@ export class CaptureConsole implements Integration { // this fails for some browsers. :( if (originalConsoleMethod) { - originalConsoleMethod.apply(global.console, args); + originalConsoleMethod.apply(GLOBAL_OBJ.console, args); } }); }); diff --git a/packages/integrations/src/offline.ts b/packages/integrations/src/offline.ts index 1da4f77b3a3a..84d4c2531ee7 100644 --- a/packages/integrations/src/offline.ts +++ b/packages/integrations/src/offline.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { Event, EventProcessor, Hub, Integration } from '@sentry/types'; -import { getGlobalObject, logger, normalize, uuid4 } from '@sentry/utils'; +import { logger, normalize, uuid4, WINDOW } from '@sentry/utils'; import localForage from 'localforage'; type LocalForage = { @@ -30,12 +30,6 @@ export class Offline implements Integration { */ public readonly name: string = Offline.id; - /** - * the global instance - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public global: any; - /** * the current hub instance */ @@ -55,8 +49,6 @@ export class Offline implements Integration { * @inheritDoc */ public constructor(options: { maxStoredEvents?: number } = {}) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this.global = getGlobalObject(); this.maxStoredEvents = options.maxStoredEvents || 30; // set a reasonable default // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access this.offlineEventStore = localForage.createInstance({ @@ -70,8 +62,8 @@ export class Offline implements Integration { public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { this.hub = getCurrentHub(); - if ('addEventListener' in this.global) { - this.global.addEventListener('online', () => { + if ('addEventListener' in WINDOW) { + WINDOW.addEventListener('online', () => { void this._sendEvents().catch(() => { __DEBUG_BUILD__ && logger.warn('could not send cached events'); }); @@ -81,7 +73,7 @@ export class Offline implements Integration { const eventProcessor: EventProcessor = event => { if (this.hub && this.hub.getIntegration(Offline)) { // cache if we are positively offline - if ('navigator' in this.global && 'onLine' in this.global.navigator && !this.global.navigator.onLine) { + if ('navigator' in WINDOW && 'onLine' in WINDOW.navigator && !WINDOW.navigator.onLine) { __DEBUG_BUILD__ && logger.log('Event dropped due to being a offline - caching instead'); void this._cacheEvent(event) @@ -102,7 +94,7 @@ export class Offline implements Integration { addGlobalEventProcessor(eventProcessor); // if online now, send any events stored in a previous offline session - if ('navigator' in this.global && 'onLine' in this.global.navigator && this.global.navigator.onLine) { + if ('navigator' in WINDOW && 'onLine' in WINDOW.navigator && WINDOW.navigator.onLine) { void this._sendEvents().catch(() => { __DEBUG_BUILD__ && logger.warn('could not send cached events'); }); diff --git a/packages/integrations/src/reportingobserver.ts b/packages/integrations/src/reportingobserver.ts index 41e5247c2376..567e5627c2a6 100644 --- a/packages/integrations/src/reportingobserver.ts +++ b/packages/integrations/src/reportingobserver.ts @@ -1,5 +1,5 @@ import { EventProcessor, Hub, Integration } from '@sentry/types'; -import { getGlobalObject, supportsReportingObserver } from '@sentry/utils'; +import { supportsReportingObserver, WINDOW } from '@sentry/utils'; interface Report { [key: string]: unknown; @@ -76,7 +76,7 @@ export class ReportingObserver implements Integration { this._getCurrentHub = getCurrentHub; // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any - const observer = new (getGlobalObject().ReportingObserver)(this.handler.bind(this), { + const observer = new (WINDOW as any).ReportingObserver(this.handler.bind(this), { buffered: true, types: this._options.types, }); diff --git a/packages/integrations/test/offline.test.ts b/packages/integrations/test/offline.test.ts index 7bd77c047182..7cf5ca00825d 100644 --- a/packages/integrations/test/offline.test.ts +++ b/packages/integrations/test/offline.test.ts @@ -1,5 +1,5 @@ import { Event, EventProcessor, Hub, Integration, IntegrationClass } from '@sentry/types'; -import * as utils from '@sentry/utils'; +import { WINDOW } from '@sentry/utils'; import { Item, Offline } from '../src/offline'; @@ -34,12 +34,30 @@ jest.mock('localforage', () => ({ })); let integration: Offline; -let online: boolean; + +// We need to mock the WINDOW object so we can modify 'navigator.online' which is readonly +jest.mock('@sentry/utils', () => { + const originalModule = jest.requireActual('@sentry/utils'); + + return { + ...originalModule, + get WINDOW() { + return { + addEventListener: (_windowEvent: any, callback: any) => { + eventListeners.push(callback); + }, + navigator: { + onLine: false, + }, + }; + }, + }; +}); describe('Offline', () => { describe('when app is online', () => { beforeEach(() => { - online = true; + (WINDOW.navigator as any).onLine = true; initIntegration(); }); @@ -71,7 +89,7 @@ describe('Offline', () => { describe('when app is offline', () => { beforeEach(() => { - online = false; + (WINDOW.navigator as any).onLine = false; }); it('stores events in offline store', async () => { @@ -166,18 +184,6 @@ function initIntegration(options: { maxStoredEvents?: number } = {}): void { eventProcessors = []; events = []; - jest.spyOn(utils, 'getGlobalObject').mockImplementation( - () => - ({ - addEventListener: (_windowEvent: any, callback: any) => { - eventListeners.push(callback); - }, - navigator: { - onLine: online, - }, - } as any), - ); - integration = new Offline(options); } diff --git a/packages/nextjs/src/performance/client.ts b/packages/nextjs/src/performance/client.ts index fd7d7dadbf2a..20c1225f0a1e 100644 --- a/packages/nextjs/src/performance/client.ts +++ b/packages/nextjs/src/performance/client.ts @@ -3,21 +3,19 @@ import { Primitive, TraceparentData, Transaction, TransactionContext, Transactio import { baggageHeaderToDynamicSamplingContext, extractTraceparentData, - getGlobalObject, logger, stripUrlQueryAndFragment, + WINDOW, } from '@sentry/utils'; import type { NEXT_DATA as NextData } from 'next/dist/next-server/lib/utils'; import { default as Router } from 'next/router'; import type { ParsedUrlQuery } from 'querystring'; -const global = getGlobalObject< - Window & { - __BUILD_MANIFEST?: { - sortedPages?: string[]; - }; - } ->(); +const global = WINDOW as typeof WINDOW & { + __BUILD_MANIFEST?: { + sortedPages?: string[]; + }; +}; type StartTransactionCb = (context: TransactionContext) => Transaction | undefined; diff --git a/packages/nextjs/test/index.client.test.ts b/packages/nextjs/test/index.client.test.ts index 89ca6fdb6c93..607bf36117dc 100644 --- a/packages/nextjs/test/index.client.test.ts +++ b/packages/nextjs/test/index.client.test.ts @@ -3,7 +3,7 @@ import { getCurrentHub } from '@sentry/hub'; import * as SentryReact from '@sentry/react'; import { Integrations as TracingIntegrations } from '@sentry/tracing'; import { Integration } from '@sentry/types'; -import { getGlobalObject, logger } from '@sentry/utils'; +import { logger, WINDOW } from '@sentry/utils'; import { JSDOM } from 'jsdom'; import { init, Integrations, nextRouterInstrumentation } from '../src/index.client'; @@ -11,8 +11,6 @@ import { UserIntegrationsFunction } from '../src/utils/userIntegrations'; const { BrowserTracing } = TracingIntegrations; -const global = getGlobalObject(); - const reactInit = jest.spyOn(SentryReact, 'init'); const captureEvent = jest.spyOn(BaseClient.prototype, 'captureEvent'); const loggerLogSpy = jest.spyOn(logger, 'log'); @@ -24,12 +22,12 @@ const dom = new JSDOM(undefined, { url: 'https://example.com/' }); Object.defineProperty(global, 'document', { value: dom.window.document, writable: true }); Object.defineProperty(global, 'location', { value: dom.window.document.location, writable: true }); -const originalGlobalDocument = getGlobalObject().document; -const originalGlobalLocation = getGlobalObject().location; +const originalGlobalDocument = WINDOW.document; +const originalGlobalLocation = WINDOW.location; afterAll(() => { // Clean up JSDom - Object.defineProperty(global, 'document', { value: originalGlobalDocument }); - Object.defineProperty(global, 'location', { value: originalGlobalLocation }); + Object.defineProperty(WINDOW, 'document', { value: originalGlobalDocument }); + Object.defineProperty(WINDOW, 'location', { value: originalGlobalLocation }); }); function findIntegrationByName(integrations: Integration[] = [], name: string): Integration | undefined { @@ -39,7 +37,7 @@ function findIntegrationByName(integrations: Integration[] = [], name: string): describe('Client init()', () => { afterEach(() => { jest.clearAllMocks(); - global.__SENTRY__.hub = undefined; + WINDOW.__SENTRY__.hub = undefined; }); it('inits the React SDK', () => { diff --git a/packages/nextjs/test/index.server.test.ts b/packages/nextjs/test/index.server.test.ts index 3f62158392db..827a4be6d73c 100644 --- a/packages/nextjs/test/index.server.test.ts +++ b/packages/nextjs/test/index.server.test.ts @@ -1,17 +1,15 @@ import * as SentryNode from '@sentry/node'; import { getCurrentHub, NodeClient } from '@sentry/node'; import { Integration } from '@sentry/types'; -import { getGlobalObject, logger } from '@sentry/utils'; +import { GLOBAL_OBJ, logger } from '@sentry/utils'; import * as domain from 'domain'; import { init } from '../src/index.server'; const { Integrations } = SentryNode; -const global = getGlobalObject(); - // normally this is set as part of the build process, so mock it here -(global as typeof global & { __rewriteFramesDistDir__: string }).__rewriteFramesDistDir__ = '.next'; +(GLOBAL_OBJ as typeof GLOBAL_OBJ & { __rewriteFramesDistDir__: string }).__rewriteFramesDistDir__ = '.next'; const nodeInit = jest.spyOn(SentryNode, 'init'); const loggerLogSpy = jest.spyOn(logger, 'log'); @@ -23,7 +21,7 @@ function findIntegrationByName(integrations: Integration[] = [], name: string): describe('Server init()', () => { afterEach(() => { jest.clearAllMocks(); - global.__SENTRY__.hub = undefined; + GLOBAL_OBJ.__SENTRY__.hub = undefined; delete process.env.VERCEL; }); diff --git a/packages/nextjs/test/performance/client.test.ts b/packages/nextjs/test/performance/client.test.ts index 2e152b6decfd..7ce9161b4afb 100644 --- a/packages/nextjs/test/performance/client.test.ts +++ b/packages/nextjs/test/performance/client.test.ts @@ -1,18 +1,16 @@ import { Transaction } from '@sentry/types'; -import { getGlobalObject } from '@sentry/utils'; +import { WINDOW } from '@sentry/utils'; import { JSDOM } from 'jsdom'; import { NEXT_DATA as NextData } from 'next/dist/next-server/lib/utils'; import { default as Router } from 'next/router'; import { nextRouterInstrumentation } from '../../src/performance/client'; -const globalObject = getGlobalObject< - Window & { - __BUILD_MANIFEST?: { - sortedPages?: string[]; - }; - } ->(); +const globalObject = WINDOW as typeof WINDOW & { + __BUILD_MANIFEST?: { + sortedPages?: string[]; + }; +}; const originalBuildManifest = globalObject.__BUILD_MANIFEST; const originalBuildManifestRoutes = globalObject.__BUILD_MANIFEST?.sortedPages; @@ -60,8 +58,8 @@ function createMockStartTransaction() { } describe('nextRouterInstrumentation', () => { - const originalGlobalDocument = getGlobalObject().document; - const originalGlobalLocation = getGlobalObject().location; + const originalGlobalDocument = WINDOW.document; + const originalGlobalLocation = WINDOW.location; function setUpNextPage(pageProperties: { url: string; @@ -93,25 +91,25 @@ describe('nextRouterInstrumentation', () => { // The Next.js routing instrumentations requires a few things to be present on pageload: // 1. Access to window.document API for `window.document.getElementById` // 2. Access to window.location API for `window.location.pathname` - Object.defineProperty(global, 'document', { value: dom.window.document, writable: true }); - Object.defineProperty(global, 'location', { value: dom.window.document.location, writable: true }); + Object.defineProperty(WINDOW, 'document', { value: dom.window.document, writable: true }); + Object.defineProperty(WINDOW, 'location', { value: dom.window.document.location, writable: true }); // Define Next.js clientside build manifest with navigatable routes - (global as any).__BUILD_MANIFEST = { - ...(global as any).__BUILD_MANIFEST, - sortedPages: pageProperties.navigatableRoutes, + globalObject.__BUILD_MANIFEST = { + ...globalObject.__BUILD_MANIFEST, + sortedPages: pageProperties.navigatableRoutes as string[], }; } afterEach(() => { // Clean up JSDom - Object.defineProperty(global, 'document', { value: originalGlobalDocument }); - Object.defineProperty(global, 'location', { value: originalGlobalLocation }); + Object.defineProperty(WINDOW, 'document', { value: originalGlobalDocument }); + Object.defineProperty(WINDOW, 'location', { value: originalGlobalLocation }); // Reset Next.js' __BUILD_MANIFEST - (global as any).__BUILD_MANIFEST = originalBuildManifest; - if ((global as any).__BUILD_MANIFEST) { - (global as any).__BUILD_MANIFEST.sortedPages = originalBuildManifestRoutes; + globalObject.__BUILD_MANIFEST = originalBuildManifest; + if (globalObject.__BUILD_MANIFEST) { + globalObject.__BUILD_MANIFEST.sortedPages = originalBuildManifestRoutes as string[]; } // Clear all event handlers diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 9f4a7eb3ee3c..2733a47f9ff9 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -4,7 +4,7 @@ import { getMainCarrier, setHubOnCarrier } from '@sentry/hub'; import { SessionStatus, StackParser } from '@sentry/types'; import { createStackParser, - getGlobalObject, + GLOBAL_OBJ, logger, nodeStackLineParser, stackParserFromStackParserOptions, @@ -227,9 +227,8 @@ export function getSentryRelease(fallback?: string): string | undefined { } // This supports the variable that sentry-webpack-plugin injects - const global = getGlobalObject(); - if (global.SENTRY_RELEASE && global.SENTRY_RELEASE.id) { - return global.SENTRY_RELEASE.id; + if (GLOBAL_OBJ.SENTRY_RELEASE && GLOBAL_OBJ.SENTRY_RELEASE.id) { + return GLOBAL_OBJ.SENTRY_RELEASE.id; } return ( diff --git a/packages/react/src/reactrouter.tsx b/packages/react/src/reactrouter.tsx index 07fdd710b8ff..8928853fad76 100644 --- a/packages/react/src/reactrouter.tsx +++ b/packages/react/src/reactrouter.tsx @@ -1,5 +1,5 @@ import { Transaction, TransactionSource } from '@sentry/types'; -import { getGlobalObject } from '@sentry/utils'; +import { WINDOW } from '@sentry/utils'; import hoistNonReactStatics from 'hoist-non-react-statics'; import * as React from 'react'; @@ -26,8 +26,6 @@ export type RouteConfig = { type MatchPath = (pathname: string, props: string | string[] | any, parent?: Match | null) => Match | null; /* eslint-enable @typescript-eslint/no-explicit-any */ -const global = getGlobalObject(); - let activeTransaction: Transaction | undefined; export function reactRouterV4Instrumentation( @@ -57,8 +55,8 @@ function createReactRouterInstrumentation( return history.location.pathname; } - if (global && global.location) { - return global.location.pathname; + if (WINDOW && WINDOW.location) { + return WINDOW.location.pathname; } return undefined; diff --git a/packages/react/src/reactrouterv3.ts b/packages/react/src/reactrouterv3.ts index 494d16c2bbca..e51ef135fbd6 100644 --- a/packages/react/src/reactrouterv3.ts +++ b/packages/react/src/reactrouterv3.ts @@ -1,5 +1,5 @@ import { Primitive, Transaction, TransactionContext, TransactionSource } from '@sentry/types'; -import { getGlobalObject } from '@sentry/utils'; +import { WINDOW } from '@sentry/utils'; import { Location, ReactRouterInstrumentation } from './types'; @@ -21,8 +21,6 @@ export type Match = ( type ReactRouterV3TransactionSource = Extract; -const global = getGlobalObject(); - /** * Creates routing instrumentation for React Router v3 * Works for React Router >= 3.2.0 and < 4.0.0 @@ -44,11 +42,11 @@ export function reactRouterV3Instrumentation( let activeTransaction: Transaction | undefined; let prevName: string | undefined; - // Have to use global.location because history.location might not be defined. - if (startTransactionOnPageLoad && global && global.location) { + // Have to use window.location because history.location might not be defined. + if (startTransactionOnPageLoad && WINDOW && WINDOW.location) { normalizeTransactionName( routes, - global.location as unknown as Location, + WINDOW.location as unknown as Location, match, (localName: string, source: ReactRouterV3TransactionSource = 'url') => { prevName = localName; diff --git a/packages/react/src/reactrouterv6.tsx b/packages/react/src/reactrouterv6.tsx index 15c823daca40..6d0d27b9ecf2 100644 --- a/packages/react/src/reactrouterv6.tsx +++ b/packages/react/src/reactrouterv6.tsx @@ -2,7 +2,7 @@ // https://gist.github.com/wontondon/e8c4bdf2888875e4c755712e99279536 import { Transaction, TransactionContext, TransactionSource } from '@sentry/types'; -import { getGlobalObject, getNumberOfUrlSegments, logger } from '@sentry/utils'; +import { getNumberOfUrlSegments, logger, WINDOW } from '@sentry/utils'; import hoistNonReactStatics from 'hoist-non-react-statics'; import React from 'react'; @@ -58,8 +58,6 @@ let _matchRoutes: MatchRoutes; let _customStartTransaction: (context: TransactionContext) => Transaction | undefined; let _startTransactionOnLocationChange: boolean; -const global = getGlobalObject(); - const SENTRY_TAGS = { 'routing.instrumentation': 'react-router-v6', }; @@ -76,7 +74,7 @@ export function reactRouterV6Instrumentation( startTransactionOnPageLoad = true, startTransactionOnLocationChange = true, ): void => { - const initPathName = global && global.location && global.location.pathname; + const initPathName = WINDOW && WINDOW.location && WINDOW.location.pathname; if (startTransactionOnPageLoad && initPathName) { activeTransaction = customStartTransaction({ name: initPathName, diff --git a/packages/remix/src/performance/client.tsx b/packages/remix/src/performance/client.tsx index 6a6a986bbf5c..cc4bb3ff6ecc 100644 --- a/packages/remix/src/performance/client.tsx +++ b/packages/remix/src/performance/client.tsx @@ -1,7 +1,7 @@ import type { ErrorBoundaryProps } from '@sentry/react'; import { withErrorBoundary } from '@sentry/react'; import { Transaction, TransactionContext } from '@sentry/types'; -import { getGlobalObject, logger } from '@sentry/utils'; +import { logger, WINDOW } from '@sentry/utils'; import * as React from 'react'; const DEFAULT_TAGS = { @@ -38,11 +38,9 @@ let _useMatches: UseMatches; let _customStartTransaction: (context: TransactionContext) => Transaction | undefined; let _startTransactionOnLocationChange: boolean; -const global = getGlobalObject(); - function getInitPathName(): string | undefined { - if (global && global.location) { - return global.location.pathname; + if (WINDOW && WINDOW.location) { + return WINDOW.location.pathname; } return undefined; diff --git a/packages/remix/test/index.client.test.ts b/packages/remix/test/index.client.test.ts index eaca0b58c0f5..a3895d561ffc 100644 --- a/packages/remix/test/index.client.test.ts +++ b/packages/remix/test/index.client.test.ts @@ -1,17 +1,15 @@ import { getCurrentHub } from '@sentry/hub'; import * as SentryReact from '@sentry/react'; -import { getGlobalObject } from '@sentry/utils'; +import { GLOBAL_OBJ } from '@sentry/utils'; import { init } from '../src/index.client'; -const global = getGlobalObject(); - const reactInit = jest.spyOn(SentryReact, 'init'); describe('Client init()', () => { afterEach(() => { jest.clearAllMocks(); - global.__SENTRY__.hub = undefined; + GLOBAL_OBJ.__SENTRY__.hub = undefined; }); it('inits the React SDK', () => { diff --git a/packages/remix/test/index.server.test.ts b/packages/remix/test/index.server.test.ts index 2bfe118fcc5f..f4d4f9c95fe0 100644 --- a/packages/remix/test/index.server.test.ts +++ b/packages/remix/test/index.server.test.ts @@ -1,17 +1,15 @@ import * as SentryNode from '@sentry/node'; import { getCurrentHub } from '@sentry/node'; -import { getGlobalObject } from '@sentry/utils'; +import { GLOBAL_OBJ } from '@sentry/utils'; import { init } from '../src/index.server'; -const global = getGlobalObject(); - const nodeInit = jest.spyOn(SentryNode, 'init'); describe('Server init()', () => { afterEach(() => { jest.clearAllMocks(); - global.__SENTRY__.hub = undefined; + GLOBAL_OBJ.__SENTRY__.hub = undefined; }); it('inits the Node SDK', () => { 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]; }); } 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..031075d9572e 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)) || @@ -64,15 +72,6 @@ const GLOBAL = })() || {}; -/** - * Safely get global scope object - * - * @returns Global scope object - */ -export function getGlobalObject(): T & SentryGlobal { - return GLOBAL as T & SentryGlobal; -} - /** * Returns a global singleton contained in the global `__SENTRY__` object. * @@ -81,12 +80,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..bb6e6b68eeb2 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('WINDOW', () => { 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; }); diff --git a/packages/vue/src/index.bundle.ts b/packages/vue/src/index.bundle.ts index 57059e55e80c..27bbfa24191f 100644 --- a/packages/vue/src/index.bundle.ts +++ b/packages/vue/src/index.bundle.ts @@ -47,7 +47,7 @@ export { } from '@sentry/browser'; import { Integrations as BrowserIntegrations } from '@sentry/browser'; -import { getGlobalObject } from '@sentry/utils'; +import { WINDOW } from '@sentry/utils'; export { init } from './sdk'; export { vueRouterInstrumentation } from './router'; @@ -57,9 +57,8 @@ export { createTracingMixins } from './tracing'; 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 (WINDOW.Sentry && WINDOW.Sentry.Integrations) { + windowIntegrations = WINDOW.Sentry.Integrations; } const INTEGRATIONS = { diff --git a/packages/vue/src/sdk.ts b/packages/vue/src/sdk.ts index 35490f925a8d..2d9030674309 100644 --- a/packages/vue/src/sdk.ts +++ b/packages/vue/src/sdk.ts @@ -1,13 +1,15 @@ import { init as browserInit, SDK_VERSION } from '@sentry/browser'; -import { arrayify, getGlobalObject, logger } from '@sentry/utils'; +import { arrayify, GLOBAL_OBJ, logger } from '@sentry/utils'; import { DEFAULT_HOOKS } from './constants'; import { attachErrorHandler } from './errorhandler'; import { createTracingMixins } from './tracing'; import { Options, TracingOptions, Vue } from './types'; +const globalWithVue = GLOBAL_OBJ as typeof GLOBAL_OBJ & { Vue: Vue }; + const DEFAULT_CONFIG: Options = { - Vue: getGlobalObject<{ Vue: Vue }>().Vue, + Vue: globalWithVue.Vue, attachProps: true, logErrors: false, hooks: DEFAULT_HOOKS,