From e373820a4c0e2baf643e6ce137019ae001740ebb Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 23 May 2024 10:34:09 +0200 Subject: [PATCH 01/13] wip --- packages/core/src/carrier.ts | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/core/src/carrier.ts b/packages/core/src/carrier.ts index 215ed9673148..5d87f2f8271c 100644 --- a/packages/core/src/carrier.ts +++ b/packages/core/src/carrier.ts @@ -1,25 +1,19 @@ import type { Integration } from '@sentry/types'; import { GLOBAL_OBJ } from '@sentry/utils'; import type { AsyncContextStrategy } from './asyncContext/types'; +import { SDK_VERSION } from './version'; /** * An object that contains a hub and maintains a scope stack. * @hidden */ export interface Carrier { - __SENTRY__?: SentryCarrier; + __SENTRY__?: VersionedCarrier; } -interface SentryCarrier { - acs?: AsyncContextStrategy; -} - -/** - * An object that contains a hub and maintains a scope stack. - * @hidden - */ -export interface Carrier { - __SENTRY__?: SentryCarrier; +interface VersionedCarrier { + version: typeof SDK_VERSION; + [SDK_VERSION]: SentryCarrier; } interface SentryCarrier { @@ -52,8 +46,11 @@ export function getMainCarrier(): Carrier { export function getSentryCarrier(carrier: Carrier): SentryCarrier { if (!carrier.__SENTRY__) { carrier.__SENTRY__ = { - extensions: {}, + version: SDK_VERSION, + [SDK_VERSION]: { + extensions: {}, + }, }; } - return carrier.__SENTRY__; + return carrier.__SENTRY__[SDK_VERSION]; } From b07a98f1f2112c402e8579b5177923557be1dbc2 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 23 May 2024 18:14:22 +0200 Subject: [PATCH 02/13] add basic implementation for populating and retrieving versioned carrier --- packages/core/src/carrier.ts | 30 +++++--- packages/core/src/sdk.ts | 9 ++- packages/core/test/lib/carrier.test.ts | 98 ++++++++++++++++++++++++++ packages/types/src/index.ts | 2 +- packages/types/src/misc.ts | 5 ++ packages/utils/src/worldwide.ts | 13 +++- 6 files changed, 140 insertions(+), 17 deletions(-) create mode 100644 packages/core/test/lib/carrier.test.ts diff --git a/packages/core/src/carrier.ts b/packages/core/src/carrier.ts index 5d87f2f8271c..63d0a42c90b7 100644 --- a/packages/core/src/carrier.ts +++ b/packages/core/src/carrier.ts @@ -1,5 +1,6 @@ -import type { Integration } from '@sentry/types'; +import type { Integration, VersionString } from '@sentry/types'; import { GLOBAL_OBJ } from '@sentry/utils'; +import type { AsyncContextStack } from './asyncContext/stackStrategy'; import type { AsyncContextStrategy } from './asyncContext/types'; import { SDK_VERSION } from './version'; @@ -11,10 +12,10 @@ export interface Carrier { __SENTRY__?: VersionedCarrier; } -interface VersionedCarrier { - version: typeof SDK_VERSION; - [SDK_VERSION]: SentryCarrier; -} +type VersionedCarrier = { + [key: VersionString]: SentryCarrier; + version?: VersionString; +}; interface SentryCarrier { acs?: AsyncContextStrategy; @@ -27,6 +28,7 @@ interface SentryCarrier { // eslint-disable-next-line @typescript-eslint/ban-types [key: string]: Function; }; + stack?: AsyncContextStack; } /** @@ -45,11 +47,19 @@ export function getMainCarrier(): Carrier { /** Will either get the existing sentry carrier, or create a new one. */ export function getSentryCarrier(carrier: Carrier): SentryCarrier { if (!carrier.__SENTRY__) { - carrier.__SENTRY__ = { - version: SDK_VERSION, - [SDK_VERSION]: { - extensions: {}, - }, + carrier.__SENTRY__ = {}; + } + + // For now: First SDK that sets the .version property wins + if (!carrier.__SENTRY__.version) { + carrier.__SENTRY__.version = SDK_VERSION; + } + + // Intentionally populating and returning the version of "this" SDK instance + // rather than what's set in .version so that "this" SDK always gets its carrier + if (!carrier.__SENTRY__[SDK_VERSION]) { + carrier.__SENTRY__[SDK_VERSION] = { + extensions: {}, }; } return carrier.__SENTRY__[SDK_VERSION]; diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index 70bf19779c9b..c50a2dbe52ac 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -2,7 +2,6 @@ import type { Client, ClientOptions } from '@sentry/types'; import { consoleSandbox, logger } from '@sentry/utils'; import { getCurrentScope } from './currentScopes'; -import type { AsyncContextStack } from './asyncContext/stackStrategy'; import { getMainCarrier, getSentryCarrier } from './carrier'; import { DEBUG_BUILD } from './debug-build'; @@ -50,13 +49,13 @@ export function setCurrentClient(client: Client): void { /** * Unfortunately, we still have to manually bind the client to the "hub" property set on the global * Sentry carrier object. This is because certain scripts (e.g. our loader script) obtain - * the client via `window.__SENTRY__.hub.getClient()`. + * the client via `window.__SENTRY__[version].stack.getClient()`. * * @see {@link ./asyncContext/stackStrategy.ts getAsyncContextStack} */ function registerClientOnGlobalHub(client: Client): void { - const sentryGlobal = getSentryCarrier(getMainCarrier()) as { hub?: AsyncContextStack }; - if (sentryGlobal.hub && typeof sentryGlobal.hub.getStackTop === 'function') { - sentryGlobal.hub.getStackTop().client = client; + const sentryGlobal = getSentryCarrier(getMainCarrier()); + if (sentryGlobal.stack && typeof sentryGlobal.stack.getStackTop === 'function') { + sentryGlobal.stack.getStackTop().client = client; } } diff --git a/packages/core/test/lib/carrier.test.ts b/packages/core/test/lib/carrier.test.ts new file mode 100644 index 000000000000..c371fb0b0339 --- /dev/null +++ b/packages/core/test/lib/carrier.test.ts @@ -0,0 +1,98 @@ +import { getSentryCarrier } from '../../src/carrier'; +import { SDK_VERSION } from '../../src/version'; + +describe('getSentryCarrier', () => { + describe('base case (one SDK)', () => { + it('populates the default sentry carrier object if it does not exist', () => { + const globalObject = {}; + const sentryCarrier = getSentryCarrier(globalObject); + + expect(sentryCarrier).toEqual({ + extensions: {}, + }); + + expect(globalObject).toEqual({ + __SENTRY__: { + version: SDK_VERSION, + [SDK_VERSION]: { + extensions: {}, + }, + }, + }); + }); + + it('returns the existing sentry carrier object if it already exists', () => { + const originalGlobalObject = { + __SENTRY__: { + version: SDK_VERSION, + [SDK_VERSION]: { + integrations: [() => {}], + }, + }, + }; + + const globalObject = { ...originalGlobalObject }; + // @ts-expect-error - TS complains because the object spread makes the version key become type string + const sentryCarrier = getSentryCarrier(globalObject); + + expect(sentryCarrier).toEqual({ + integrations: [expect.any(Function)], + }); + + expect(globalObject).toStrictEqual(originalGlobalObject); + }); + }); + + describe('multiple (older) SDKs', () => { + it("returns the version of the sentry carrier object of the SDK's version rather than the one set in .version", () => { + const sentryCarrier = getSentryCarrier({ + __SENTRY__: { + version: '8.0.0' as const, // another SDK set this + '8.0.0': { + // and this object + extensions: {}, + }, + [SDK_VERSION]: { + integrations: [() => {}], + }, + // @ts-expect-error - this is just a test object, no need to pass a hub + hub: {}, + }, + }); + + expect(sentryCarrier).toEqual({ + integrations: [expect.any(Function)], + }); + }); + + it("doesn't overwrite the .version property if it's already set and creates a new global sentry carrier for the SDK version if not set yet", () => { + const globalObject = { + __SENTRY__: { + version: '8.0.0' as const, + '8.0.0': { + // and this object + integrations: [() => {}], + }, + }, + }; + + const sentryCarrier = getSentryCarrier(globalObject); + + expect(sentryCarrier).toEqual({ + extensions: {}, + }); + + expect(globalObject).toEqual({ + __SENTRY__: { + version: '8.0.0', + '8.0.0': { + integrations: [expect.any(Function)], + }, + [SDK_VERSION]: { + extensions: {}, + }, + }, + }); + }); + }); +}); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index c90b7841f9ff..f9a67a9657d6 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -57,7 +57,7 @@ export type { Extra, Extras } from './extra'; export type { Hub } from './hub'; export type { Integration, IntegrationClass, IntegrationFn } from './integration'; export type { Mechanism } from './mechanism'; -export type { ExtractedNodeRequestData, HttpHeaderValue, Primitive, WorkerLocation } from './misc'; +export type { ExtractedNodeRequestData, HttpHeaderValue, Primitive, WorkerLocation, VersionString } from './misc'; export type { ClientOptions, Options } from './options'; export type { Package } from './package'; export type { PolymorphicEvent, PolymorphicRequest } from './polymorphics'; diff --git a/packages/types/src/misc.ts b/packages/types/src/misc.ts index af9ed8fc6bd7..0ac8f4ddc62c 100644 --- a/packages/types/src/misc.ts +++ b/packages/types/src/misc.ts @@ -65,3 +65,8 @@ export interface WorkerLocation { export type Primitive = number | string | boolean | bigint | symbol | null | undefined; export type HttpHeaderValue = string | string[] | number | null; + +/** + * Type representing a semver version string + */ +export type VersionString = `${number}.${number}.${number}${`-${string}${string}` | ''}`; diff --git a/packages/utils/src/worldwide.ts b/packages/utils/src/worldwide.ts index cc5241f00e0f..a142c609b921 100644 --- a/packages/utils/src/worldwide.ts +++ b/packages/utils/src/worldwide.ts @@ -12,7 +12,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { Client, MetricsAggregator, Scope } from '@sentry/types'; +import type { Client, MetricsAggregator, Scope, VersionString } from '@sentry/types'; import type { SdkSource } from './env'; @@ -44,6 +44,17 @@ export interface InternalGlobal { */ _sentryDebugIds?: Record; __SENTRY__: { + [key: VersionString]: { + acs?: any; + integrations?: any[]; + extensions?: { + /** Extra Hub properties injected by various SDKs */ + // eslint-disable-next-line @typescript-eslint/ban-types + [key: string]: Function; + }; + stack?: any; + }; + version: VersionString; hub: any; logger: any; extensions?: { From 73458c313252149fb9cec56bf39692a085b8a914 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 24 May 2024 12:39:07 +0200 Subject: [PATCH 03/13] ref(core): Use versioned carrier on global object --- .../suites/old-sdk-interop/hub/init.js | 18 ++++++ .../suites/old-sdk-interop/hub/subject.js | 12 ++++ .../suites/old-sdk-interop/hub/template.html | 9 +++ .../suites/old-sdk-interop/hub/test.ts | 13 ++++ .../core/src/asyncContext/stackStrategy.ts | 20 ++---- packages/core/src/carrier.ts | 8 ++- packages/core/src/integration.ts | 2 +- packages/core/src/sdk.ts | 2 +- packages/core/src/utils/prepareEvent.ts | 2 +- packages/core/test/lib/clear-global-scope.ts | 5 +- packages/utils/package.json | 1 + packages/utils/src/version.ts | 1 + packages/utils/src/worldwide.ts | 63 ++++++++++--------- 13 files changed, 104 insertions(+), 52 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/init.js create mode 100644 dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/template.html create mode 100644 dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/test.ts create mode 100644 packages/utils/src/version.ts diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/init.js b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/init.js new file mode 100644 index 000000000000..ef1b38e11402 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/init.js @@ -0,0 +1,18 @@ +import * as Sentry from '@sentry/browser'; + +/** + * This simulates an old, pre-v8 SDK setting itself up on the global __SENTRY__ carrier. + */ +window.__SENTRY__ = { + hub: { + isOlderThan: version => { + return version < 7; + }, + }, +}; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', +}); diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/subject.js b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/subject.js new file mode 100644 index 000000000000..1f713df8364c --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/subject.js @@ -0,0 +1,12 @@ +const sentryCarrier = window && window.__SENTRY__; + +/** + * Simulate an old pre v8 SDK obtaining the hub from the global sentry carrier + * and checking for the hub version. + */ +const res = sentryCarrier.hub && sentryCarrier.hub.isOlderThan(7); + +// Write back result into the document +document.getElementById('olderThan').innerText = res; + +console.log(sentryCarrier); diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/template.html b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/template.html new file mode 100644 index 000000000000..a363fad46013 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/template.html @@ -0,0 +1,9 @@ + + + + + + +

+ + diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/test.ts b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/test.ts new file mode 100644 index 000000000000..00b94eef4bd5 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/test.ts @@ -0,0 +1,13 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../utils/fixtures'; + +sentryTest( + "doesn't crash if older SDKs access `hub.isOlderThan` on the global object", + async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + await page.goto(url); + + await expect(page.locator('#olderThan')).toHaveText('false'); + }, +); diff --git a/packages/core/src/asyncContext/stackStrategy.ts b/packages/core/src/asyncContext/stackStrategy.ts index aa075b644e85..7da6960e7801 100644 --- a/packages/core/src/asyncContext/stackStrategy.ts +++ b/packages/core/src/asyncContext/stackStrategy.ts @@ -132,19 +132,9 @@ export class AsyncContextStack { */ function getAsyncContextStack(): AsyncContextStack { const registry = getMainCarrier(); + const sentry = getSentryCarrier(registry); - // For now we continue to keep this as `hub` on the ACS, - // as e.g. the Loader Script relies on this. - // Eventually we may change this if/when we update the loader to not require this field anymore - // Related, we also write to `hub` in {@link ./../sdk.ts registerClientOnGlobalHub} - const sentry = getSentryCarrier(registry) as { hub?: AsyncContextStack }; - - if (sentry.hub) { - return sentry.hub; - } - - sentry.hub = new AsyncContextStack(getDefaultCurrentScope(), getDefaultIsolationScope()); - return sentry.hub; + return (sentry.stack = sentry.stack || new AsyncContextStack(getDefaultCurrentScope(), getDefaultIsolationScope())); } function withScope(callback: (scope: ScopeInterface) => T): T { @@ -152,9 +142,9 @@ function withScope(callback: (scope: ScopeInterface) => T): T { } function withSetScope(scope: ScopeInterface, callback: (scope: ScopeInterface) => T): T { - const hub = getAsyncContextStack() as AsyncContextStack; - return hub.withScope(() => { - hub.getStackTop().scope = scope; + const stack = getAsyncContextStack() as AsyncContextStack; + return stack.withScope(() => { + stack.getStackTop().scope = scope; return callback(scope); }); } diff --git a/packages/core/src/carrier.ts b/packages/core/src/carrier.ts index 63d0a42c90b7..3da0f72e5254 100644 --- a/packages/core/src/carrier.ts +++ b/packages/core/src/carrier.ts @@ -1,11 +1,11 @@ -import type { Integration, VersionString } from '@sentry/types'; +import type { Integration, Scope, VersionString } from '@sentry/types'; import { GLOBAL_OBJ } from '@sentry/utils'; import type { AsyncContextStack } from './asyncContext/stackStrategy'; import type { AsyncContextStrategy } from './asyncContext/types'; import { SDK_VERSION } from './version'; /** - * An object that contains a hub and maintains a scope stack. + * An object that contains globally accessible properties and maintains a scope stack. * @hidden */ export interface Carrier { @@ -29,6 +29,10 @@ interface SentryCarrier { [key: string]: Function; }; stack?: AsyncContextStack; + + globalScope?: Scope; + defaultIsolationScope?: Scope; + defaultCurrentScope?: Scope; } /** diff --git a/packages/core/src/integration.ts b/packages/core/src/integration.ts index c5f9499f342e..f39e09dae3ff 100644 --- a/packages/core/src/integration.ts +++ b/packages/core/src/integration.ts @@ -146,7 +146,7 @@ export function setupIntegration(client: Client, integration: Integration, integ DEBUG_BUILD && logger.log(`Integration installed: ${integration.name}`); } -/** Add an integration to the current hub's client. */ +/** Add an integration to the current scope's client. */ export function addIntegration(integration: Integration): void { const client = getClient(); diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index c50a2dbe52ac..d5a3c9399b0b 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -47,7 +47,7 @@ export function setCurrentClient(client: Client): void { } /** - * Unfortunately, we still have to manually bind the client to the "hub" property set on the global + * Unfortunately, we still have to manually bind the client to the "stack" property set on the global * Sentry carrier object. This is because certain scripts (e.g. our loader script) obtain * the client via `window.__SENTRY__[version].stack.getClient()`. * diff --git a/packages/core/src/utils/prepareEvent.ts b/packages/core/src/utils/prepareEvent.ts index 6bc21cadbffa..c60c6d21331b 100644 --- a/packages/core/src/utils/prepareEvent.ts +++ b/packages/core/src/utils/prepareEvent.ts @@ -76,7 +76,7 @@ export function prepareEvent( const clientEventProcessors = client ? client.getEventProcessors() : []; // This should be the last thing called, since we want that - // {@link Hub.addEventProcessor} gets the finished prepared event. + // {@link Scope.addEventProcessor} gets the finished prepared event. // Merge scope data together const data = getGlobalScope().getScopeData(); diff --git a/packages/core/test/lib/clear-global-scope.ts b/packages/core/test/lib/clear-global-scope.ts index 5290a610e961..0ebe6884429a 100644 --- a/packages/core/test/lib/clear-global-scope.ts +++ b/packages/core/test/lib/clear-global-scope.ts @@ -1,6 +1,7 @@ import { GLOBAL_OBJ } from '@sentry/utils'; +import { getSentryCarrier } from '../../src/carrier'; export function clearGlobalScope() { - const __SENTRY__ = (GLOBAL_OBJ.__SENTRY__ = GLOBAL_OBJ.__SENTRY__ || {}); - __SENTRY__.globalScope = undefined; + const carrier = getSentryCarrier(GLOBAL_OBJ); + carrier.globalScope = undefined; } diff --git a/packages/utils/package.json b/packages/utils/package.json index 3c4186b1ccc9..31b93ea62719 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -67,6 +67,7 @@ "test": "jest", "test:watch": "jest --watch", "test:package": "node test/types/index.js", + "version": "node ../../scripts/versionbump.js src/version.ts", "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push --sig" }, "volta": { diff --git a/packages/utils/src/version.ts b/packages/utils/src/version.ts new file mode 100644 index 000000000000..8fc48c61dd0b --- /dev/null +++ b/packages/utils/src/version.ts @@ -0,0 +1 @@ +export const SDK_VERSION = '8.4.0'; diff --git a/packages/utils/src/worldwide.ts b/packages/utils/src/worldwide.ts index a142c609b921..64c3ae4561d6 100644 --- a/packages/utils/src/worldwide.ts +++ b/packages/utils/src/worldwide.ts @@ -15,9 +15,33 @@ import type { Client, MetricsAggregator, Scope, VersionString } from '@sentry/types'; import type { SdkSource } from './env'; +import { SDK_VERSION } from './version'; + +type BackwardsCompatibleSentryCarrier = { + // v8 scope stack (replaces .hub) + stack?: any; + acs?: any; + // pre-v7 hub (replaced by .stack) + hub: any; + integrations?: 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; + }; + globalScope: Scope | undefined; + defaultCurrentScope: Scope | undefined; + defaultIsolationScope: Scope | undefined; + globalMetricsAggregators: WeakMap | undefined; + /** Overwrites TextEncoder used in `@sentry/utils`, need for `react-native@0.73` and older */ + encodePolyfill?: (input: string) => Uint8Array; + /** Overwrites TextDecoder used in `@sentry/utils`, need for `react-native@0.73` and older */ + decodePolyfill?: (input: Uint8Array) => string; +}; /** Internal global with common properties and Sentry extensions */ -export interface InternalGlobal { +export type InternalGlobal = { navigator?: { userAgent?: string }; console: Console; Sentry?: any; @@ -44,33 +68,9 @@ export interface InternalGlobal { */ _sentryDebugIds?: Record; __SENTRY__: { - [key: VersionString]: { - acs?: any; - integrations?: any[]; - extensions?: { - /** Extra Hub properties injected by various SDKs */ - // eslint-disable-next-line @typescript-eslint/ban-types - [key: string]: Function; - }; - stack?: any; - }; + [key: VersionString]: BackwardsCompatibleSentryCarrier; version: VersionString; - 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; - }; - globalScope: Scope | undefined; - defaultCurrentScope: Scope | undefined; - defaultIsolationScope: Scope | undefined; - globalMetricsAggregators: WeakMap | undefined; - /** Overwrites TextEncoder used in `@sentry/utils`, need for `react-native@0.73` and older */ - encodePolyfill?: (input: string) => Uint8Array; - /** Overwrites TextDecoder used in `@sentry/utils`, need for `react-native@0.73` and older */ - decodePolyfill?: (input: Uint8Array) => string; - }; + } & BackwardsCompatibleSentryCarrier; /** * Raw module metadata that is injected by bundler plugins. * @@ -78,11 +78,13 @@ export interface InternalGlobal { */ _sentryModuleMetadata?: Record; _sentryEsmLoaderHookRegistered?: boolean; -} +}; /** Get's the global object for the current JavaScript runtime */ export const GLOBAL_OBJ = globalThis as unknown as InternalGlobal; +type SingletonKeys = Exclude; + /** * Returns a global singleton contained in the global `__SENTRY__` object. * @@ -94,9 +96,10 @@ export const GLOBAL_OBJ = globalThis as unknown as InternalGlobal; * @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 InternalGlobal['__SENTRY__'], creator: () => T, obj?: unknown): T { +export function getGlobalSingleton(name: SingletonKeys, 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()); + const versionedCarrier = (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {}); + const singleton = versionedCarrier[name] || (versionedCarrier[name] = creator()); return singleton; } From ee49730bfef1c3903580b6047a70d08327d5ed8e Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 24 May 2024 15:43:25 +0200 Subject: [PATCH 04/13] tmp-fix loader tests --- .../fixtures/loader.js | 141 +++++++++++++++++- .../loader/onLoad/customInit/init.js | 8 +- 2 files changed, 145 insertions(+), 4 deletions(-) diff --git a/dev-packages/browser-integration-tests/fixtures/loader.js b/dev-packages/browser-integration-tests/fixtures/loader.js index 2f4705810b2f..39ad8439a86f 100644 --- a/dev-packages/browser-integration-tests/fixtures/loader.js +++ b/dev-packages/browser-integration-tests/fixtures/loader.js @@ -1,5 +1,140 @@ -!function(n,e,r,t,i,o,a,c,s){for(var u=s,f=0;f-1){u&&"no"===document.scripts[f].getAttribute("data-lazy")&&(u=!1);break}var p=[];function l(n){return"e"in n}function d(n){return"p"in n}function _(n){return"f"in n}var v=[];function y(n){u&&(l(n)||d(n)||_(n)&&n.f.indexOf("capture")>-1||_(n)&&n.f.indexOf("showReportDialog")>-1)&&m(),v.push(n)}function g(){y({e:[].slice.call(arguments)})}function h(n){y({p:n})}function E(){try{n.SENTRY_SDK_SOURCE="loader";var e=n[i],o=e.init;e.init=function(i){n.removeEventListener(r,g),n.removeEventListener(t,h);var a=c;for(var s in i)Object.prototype.hasOwnProperty.call(i,s)&&(a[s]=i[s]);!function(n,e){var r=n.integrations||[];if(!Array.isArray(r))return;var t=r.map((function(n){return n.name}));n.tracesSampleRate&&-1===t.indexOf("BrowserTracing")&&(e.BrowserTracing?r.push(new e.BrowserTracing):e.browserTracingIntegration&&r.push(e.browserTracingIntegration()));(n.replaysSessionSampleRate||n.replaysOnErrorSampleRate)&&-1===t.indexOf("Replay")&&(e.Replay?r.push(new e.Replay):e.replayIntegration&&r.push(e.replayIntegration()));n.integrations=r}(a,e),o(a)},setTimeout((function(){return function(e){try{"function"==typeof n.sentryOnLoad&&(n.sentryOnLoad(),n.sentryOnLoad=void 0);for(var r=0;r -1) { + u && 'no' === document.scripts[f].getAttribute('data-lazy') && (u = !1); + break; + } + var p = []; + function l(n) { + return 'e' in n; + } + function d(n) { + return 'p' in n; + } + function _(n) { + return 'f' in n; + } + var v = []; + function y(n) { + u && + (l(n) || d(n) || (_(n) && n.f.indexOf('capture') > -1) || (_(n) && n.f.indexOf('showReportDialog') > -1)) && + m(), + v.push(n); + } + function g() { + y({ e: [].slice.call(arguments) }); + } + function h(n) { + y({ p: n }); + } + function E() { + try { + n.SENTRY_SDK_SOURCE = 'loader'; + var e = n[i], + o = e.init; + (e.init = function (i) { + n.removeEventListener(r, g), n.removeEventListener(t, h); + var a = c; + for (var s in i) Object.prototype.hasOwnProperty.call(i, s) && (a[s] = i[s]); + !(function (n, e) { + var r = n.integrations || []; + if (!Array.isArray(r)) return; + var t = r.map(function (n) { + return n.name; + }); + n.tracesSampleRate && + -1 === t.indexOf('BrowserTracing') && + (e.BrowserTracing + ? r.push(new e.BrowserTracing()) + : e.browserTracingIntegration && r.push(e.browserTracingIntegration())); + (n.replaysSessionSampleRate || n.replaysOnErrorSampleRate) && + -1 === t.indexOf('Replay') && + (e.Replay ? r.push(new e.Replay()) : e.replayIntegration && r.push(e.replayIntegration())); + n.integrations = r; + })(a, e), + o(a); + }), + setTimeout(function () { + return (function (e) { + try { + 'function' == typeof n.sentryOnLoad && (n.sentryOnLoad(), (n.sentryOnLoad = void 0)); + for (var r = 0; r < p.length; r++) 'function' == typeof p[r] && p[r](); + p.splice(0); + for (r = 0; r < v.length; r++) { + _((o = v[r])) && 'init' === o.f && e.init.apply(e, o.a); + } + L() || e.init(); + var t = n.onerror, + i = n.onunhandledrejection; + for (r = 0; r < v.length; r++) { + var o; + if (_((o = v[r]))) { + if ('init' === o.f) continue; + e[o.f].apply(e, o.a); + } else l(o) && t ? t.apply(n, o.e) : d(o) && i && i.apply(n, [o.p]); + } + } catch (n) { + console.error(n); + } + })(e); + }); + } catch (n) { + console.error(n); + } + } + var O = !1; + function m() { + if (!O) { + O = !0; + var n = e.scripts[0], + r = e.createElement('script'); + (r.src = a), + (r.crossOrigin = 'anonymous'), + r.addEventListener('load', E, { once: !0, passive: !0 }), + n.parentNode.insertBefore(r, n); + } + } + function L() { + var e = n.__SENTRY__; + + // TODO: This is a temporary hack to make the loader script compatible with the versioned + // carrier. This needs still needs to be added to the actual loader script before we + // release the loader for v8! + var v = e && e.version && e[e.version]; + var c = v && v.stack && v.stack.getClient(); + + return !(void 0 === e || !e.hub || !e.hub.getClient()) || !!c; + } + (n[i] = n[i] || {}), + (n[i].onLoad = function (n) { + L() ? n() : p.push(n); + }), + (n[i].forceLoad = function () { + setTimeout(function () { + m(); + }); + }), + [ + 'init', + 'addBreadcrumb', + 'captureMessage', + 'captureException', + 'captureEvent', + 'configureScope', + 'withScope', + 'showReportDialog', + ].forEach(function (e) { + n[i][e] = function () { + y({ f: e, a: arguments }); + }; + }), + n.addEventListener(r, g), + n.addEventListener(t, h), + u || + setTimeout(function () { + m(); + }); +})( window, document, 'error', @@ -8,5 +143,5 @@ 'loader.js', __LOADER_BUNDLE__, __LOADER_OPTIONS__, - __LOADER_LAZY__ + __LOADER_LAZY__, ); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/customInit/init.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/customInit/init.js index 4a9e000fd1c2..1a2131b84daa 100644 --- a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/customInit/init.js +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/customInit/init.js @@ -16,5 +16,11 @@ window.sentryIsLoaded = () => { const __sentry = window.__SENTRY__; // If there is a global __SENTRY__ that means that in any of the callbacks init() was already invoked - return !!(!(typeof __sentry === 'undefined') && __sentry.hub && __sentry.hub.getClient()); + return !!( + !(typeof __sentry === 'undefined') && + __sentry.version && + __sentry[__sentry.version] && + __sentry[__sentry.version].stack && + __sentry[__sentry.version].stack.getClient() + ); }; From c696ab87248887b478b7a184afd5f0aefac9345b Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 24 May 2024 15:52:42 +0200 Subject: [PATCH 05/13] add another sdk interop test --- .../acs/getCurrentScope/init.js | 19 +++++++++++++++++++ .../acs/getCurrentScope/subject.js | 10 ++++++++++ .../acs/getCurrentScope/template.html | 9 +++++++++ .../acs/getCurrentScope/test.ts | 13 +++++++++++++ .../hub/{ => isOlderThan}/init.js | 1 + .../hub/{ => isOlderThan}/subject.js | 2 -- .../hub/{ => isOlderThan}/template.html | 0 .../hub/{ => isOlderThan}/test.ts | 2 +- 8 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/init.js create mode 100644 dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/template.html create mode 100644 dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/test.ts rename dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/{ => isOlderThan}/init.js (83%) rename dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/{ => isOlderThan}/subject.js (92%) rename dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/{ => isOlderThan}/template.html (100%) rename dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/{ => isOlderThan}/test.ts (85%) diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/init.js b/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/init.js new file mode 100644 index 000000000000..c21a085e735e --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/init.js @@ -0,0 +1,19 @@ +import * as Sentry from '@sentry/browser'; + +/** + * This simulates an relatively new v7 SDK setting acs on the __SENTRY__ carrier. + * see: https://github.com/getsentry/sentry-javascript/issues/12054 + */ +window.__SENTRY__ = { + acs: { + getCurrentScope: () => { + return 'scope'; + }, + }, +}; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', +}); diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/subject.js b/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/subject.js new file mode 100644 index 000000000000..6b195f6d2b20 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/subject.js @@ -0,0 +1,10 @@ +const sentryCarrier = window && window.__SENTRY__; + +/** + * Simulate an old pre v8 SDK obtaining the hub from the global sentry carrier + * and checking for the hub version. + */ +const res = sentryCarrier.acs && sentryCarrier.acs.getCurrentScope(); + +// Write back result into the document +document.getElementById('currentScope').innerText = res && 'scope'; diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/template.html b/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/template.html new file mode 100644 index 000000000000..b0a083eab503 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/template.html @@ -0,0 +1,9 @@ + + + + + + +

+ + diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/test.ts b/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/test.ts new file mode 100644 index 000000000000..18e26c659a3b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/old-sdk-interop/acs/getCurrentScope/test.ts @@ -0,0 +1,13 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../utils/fixtures'; + +sentryTest( + "doesn't crash if older SDKs access `acs.getCurrentScope` on the global object", + async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + await page.goto(url); + + await expect(page.locator('#currentScope')).toHaveText('scope'); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/init.js b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/isOlderThan/init.js similarity index 83% rename from dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/init.js rename to dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/isOlderThan/init.js index ef1b38e11402..0bb1961ce1d9 100644 --- a/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/init.js +++ b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/isOlderThan/init.js @@ -2,6 +2,7 @@ import * as Sentry from '@sentry/browser'; /** * This simulates an old, pre-v8 SDK setting itself up on the global __SENTRY__ carrier. + * see: https://github.com/getsentry/sentry-javascript/issues/12155 */ window.__SENTRY__ = { hub: { diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/subject.js b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/isOlderThan/subject.js similarity index 92% rename from dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/subject.js rename to dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/isOlderThan/subject.js index 1f713df8364c..3de7e795e416 100644 --- a/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/subject.js +++ b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/isOlderThan/subject.js @@ -8,5 +8,3 @@ const res = sentryCarrier.hub && sentryCarrier.hub.isOlderThan(7); // Write back result into the document document.getElementById('olderThan').innerText = res; - -console.log(sentryCarrier); diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/template.html b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/isOlderThan/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/template.html rename to dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/isOlderThan/template.html diff --git a/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/test.ts b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/isOlderThan/test.ts similarity index 85% rename from dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/test.ts rename to dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/isOlderThan/test.ts index 00b94eef4bd5..5cdc3e833f8a 100644 --- a/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/test.ts +++ b/dev-packages/browser-integration-tests/suites/old-sdk-interop/hub/isOlderThan/test.ts @@ -1,6 +1,6 @@ import { expect } from '@playwright/test'; -import { sentryTest } from '../../../utils/fixtures'; +import { sentryTest } from '../../../../utils/fixtures'; sentryTest( "doesn't crash if older SDKs access `hub.isOlderThan` on the global object", From 9d14585dd806ac6f612127c12bc97fe0ee51979f Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 May 2024 13:16:08 +0200 Subject: [PATCH 06/13] make version.ts in utils the one source of truth for sdk version --- packages/core/package.json | 1 - packages/core/src/carrier.ts | 2 +- packages/core/src/index.ts | 3 ++- packages/core/src/utils/sdkMetadata.ts | 2 +- packages/core/src/version.ts | 1 - packages/utils/src/index.ts | 1 + packages/utils/src/version.ts | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 packages/core/src/version.ts diff --git a/packages/core/package.json b/packages/core/package.json index 4f6088735125..b4958d4db216 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -63,7 +63,6 @@ "lint": "eslint . --format stylish", "test": "jest", "test:watch": "jest --watch", - "version": "node ../../scripts/versionbump.js src/version.ts", "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push --sig" }, "volta": { diff --git a/packages/core/src/carrier.ts b/packages/core/src/carrier.ts index 3da0f72e5254..792efe50417a 100644 --- a/packages/core/src/carrier.ts +++ b/packages/core/src/carrier.ts @@ -1,8 +1,8 @@ import type { Integration, Scope, VersionString } from '@sentry/types'; import { GLOBAL_OBJ } from '@sentry/utils'; +import { SDK_VERSION } from '@sentry/utils'; import type { AsyncContextStack } from './asyncContext/stackStrategy'; import type { AsyncContextStrategy } from './asyncContext/types'; -import { SDK_VERSION } from './version'; /** * An object that contains globally accessible properties and maintains a scope stack. diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 03dfa8e63aa3..7ab3c4008dd1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -56,7 +56,6 @@ export { initAndBind, setCurrentClient } from './sdk'; export { createTransport } from './transports/base'; export { makeOfflineTransport } from './transports/offline'; export { makeMultiplexedTransport } from './transports/multiplexed'; -export { SDK_VERSION } from './version'; export { getIntegrationsToSetup, addIntegration, @@ -107,3 +106,5 @@ export { captureFeedback } from './feedback'; // eslint-disable-next-line deprecation/deprecation export { getCurrentHubShim, getCurrentHub } from './getCurrentHubShim'; + +export { SDK_VERSION } from '@sentry/utils'; diff --git a/packages/core/src/utils/sdkMetadata.ts b/packages/core/src/utils/sdkMetadata.ts index e865861f4f55..9ad46bc5b375 100644 --- a/packages/core/src/utils/sdkMetadata.ts +++ b/packages/core/src/utils/sdkMetadata.ts @@ -1,5 +1,5 @@ import type { Options } from '@sentry/types'; -import { SDK_VERSION } from '../version'; +import { SDK_VERSION } from '@sentry/utils'; /** * A builder for the SDK metadata in the options for the SDK initialization. diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts deleted file mode 100644 index c34fa88d1e69..000000000000 --- a/packages/core/src/version.ts +++ /dev/null @@ -1 +0,0 @@ -export const SDK_VERSION = '8.5.0'; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 2fb6f420ab58..433e29a68d79 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -36,3 +36,4 @@ export * from './anr'; export * from './lru'; export * from './buildPolyfills'; export * from './propagationContext'; +export * from './version'; diff --git a/packages/utils/src/version.ts b/packages/utils/src/version.ts index 8fc48c61dd0b..c34fa88d1e69 100644 --- a/packages/utils/src/version.ts +++ b/packages/utils/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '8.4.0'; +export const SDK_VERSION = '8.5.0'; From 4575321ba5b37745f5e02c559a3d5b59a0a25f70 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 May 2024 13:21:25 +0200 Subject: [PATCH 07/13] Update packages/core/src/sdk.ts Co-authored-by: Francesco Novy --- packages/core/src/sdk.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index d5a3c9399b0b..f9fad8564feb 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -55,7 +55,9 @@ export function setCurrentClient(client: Client): void { */ function registerClientOnGlobalHub(client: Client): void { const sentryGlobal = getSentryCarrier(getMainCarrier()); - if (sentryGlobal.stack && typeof sentryGlobal.stack.getStackTop === 'function') { + try { sentryGlobal.stack.getStackTop().client = client; - } + } catch { + // ignore errors here + } } From 71fe56af8b0f4d0aa4a486d1ad459189626ea4ef Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 May 2024 13:23:16 +0200 Subject: [PATCH 08/13] try/catch instead of definedness guarding --- packages/core/src/sdk.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index f9fad8564feb..81b1ed891024 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -54,10 +54,10 @@ export function setCurrentClient(client: Client): void { * @see {@link ./asyncContext/stackStrategy.ts getAsyncContextStack} */ function registerClientOnGlobalHub(client: Client): void { - const sentryGlobal = getSentryCarrier(getMainCarrier()); try { - sentryGlobal.stack.getStackTop().client = client; - } catch { - // ignore errors here - } + // @ts-expect-error - purposefully not guarding the call here but try/catching for bundle size efficiency + getSentryCarrier(getMainCarrier()).stack.getStackTop().client = client; + } catch { + // ignore errors here + } } From efe3229268447c4a0e26d1cdefdb1b3f965ca71a Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 May 2024 14:50:34 +0200 Subject: [PATCH 09/13] cleanup types, reduce bundle size a bit --- packages/core/src/carrier.ts | 34 +++++++++---------------- packages/core/test/lib/carrier.test.ts | 23 ++++++----------- packages/utils/src/worldwide.ts | 35 ++++++++++++++------------ 3 files changed, 39 insertions(+), 53 deletions(-) diff --git a/packages/core/src/carrier.ts b/packages/core/src/carrier.ts index 792efe50417a..22480bbac26a 100644 --- a/packages/core/src/carrier.ts +++ b/packages/core/src/carrier.ts @@ -1,4 +1,4 @@ -import type { Integration, Scope, VersionString } from '@sentry/types'; +import type { Client, Integration, MetricsAggregator, Scope, VersionString } from '@sentry/types'; import { GLOBAL_OBJ } from '@sentry/utils'; import { SDK_VERSION } from '@sentry/utils'; import type { AsyncContextStack } from './asyncContext/stackStrategy'; @@ -19,20 +19,19 @@ type VersionedCarrier = { interface SentryCarrier { acs?: AsyncContextStrategy; - /** - * Extra Hub properties injected by various SDKs - */ - integrations?: Integration[]; - 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; - }; stack?: AsyncContextStack; globalScope?: Scope; defaultIsolationScope?: Scope; defaultCurrentScope?: Scope; + globalMetricsAggregators?: WeakMap | undefined; + + // TODO(v9): Remove these properties - they are no longer used and were left over in v8 + integrations?: Integration[]; + extensions?: { + // eslint-disable-next-line @typescript-eslint/ban-types + [key: string]: Function; + }; } /** @@ -50,21 +49,12 @@ export function getMainCarrier(): Carrier { /** Will either get the existing sentry carrier, or create a new one. */ export function getSentryCarrier(carrier: Carrier): SentryCarrier { - if (!carrier.__SENTRY__) { - carrier.__SENTRY__ = {}; - } + const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {}); // For now: First SDK that sets the .version property wins - if (!carrier.__SENTRY__.version) { - carrier.__SENTRY__.version = SDK_VERSION; - } + __SENTRY__.version = __SENTRY__.version || SDK_VERSION; // Intentionally populating and returning the version of "this" SDK instance // rather than what's set in .version so that "this" SDK always gets its carrier - if (!carrier.__SENTRY__[SDK_VERSION]) { - carrier.__SENTRY__[SDK_VERSION] = { - extensions: {}, - }; - } - return carrier.__SENTRY__[SDK_VERSION]; + return (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {}); } diff --git a/packages/core/test/lib/carrier.test.ts b/packages/core/test/lib/carrier.test.ts index c371fb0b0339..e44dde0ab827 100644 --- a/packages/core/test/lib/carrier.test.ts +++ b/packages/core/test/lib/carrier.test.ts @@ -1,5 +1,5 @@ +import { SDK_VERSION } from '@sentry/utils'; import { getSentryCarrier } from '../../src/carrier'; -import { SDK_VERSION } from '../../src/version'; describe('getSentryCarrier', () => { describe('base case (one SDK)', () => { @@ -7,16 +7,12 @@ describe('getSentryCarrier', () => { const globalObject = {}; const sentryCarrier = getSentryCarrier(globalObject); - expect(sentryCarrier).toEqual({ - extensions: {}, - }); + expect(sentryCarrier).toEqual({}); expect(globalObject).toEqual({ __SENTRY__: { version: SDK_VERSION, - [SDK_VERSION]: { - extensions: {}, - }, + [SDK_VERSION]: {}, }, }); }); @@ -71,26 +67,23 @@ describe('getSentryCarrier', () => { version: '8.0.0' as const, '8.0.0': { // and this object - integrations: [() => {}], + acs: {}, }, }, }; + // @ts-expect-error - this is just a test object, no need to pass a hub const sentryCarrier = getSentryCarrier(globalObject); - expect(sentryCarrier).toEqual({ - extensions: {}, - }); + expect(sentryCarrier).toEqual({}); expect(globalObject).toEqual({ __SENTRY__: { version: '8.0.0', '8.0.0': { - integrations: [expect.any(Function)], - }, - [SDK_VERSION]: { - extensions: {}, + acs: {}, }, + [SDK_VERSION]: {}, }, }); }); diff --git a/packages/utils/src/worldwide.ts b/packages/utils/src/worldwide.ts index 64c3ae4561d6..664aea9794bf 100644 --- a/packages/utils/src/worldwide.ts +++ b/packages/utils/src/worldwide.ts @@ -17,10 +17,23 @@ import type { Client, MetricsAggregator, Scope, VersionString } from '@sentry/ty import type { SdkSource } from './env'; import { SDK_VERSION } from './version'; -type BackwardsCompatibleSentryCarrier = { - // v8 scope stack (replaces .hub) - stack?: any; +interface SentryCarrier { acs?: any; + stack?: any; + + globalScope?: Scope; + defaultIsolationScope?: Scope; + defaultCurrentScope?: Scope; + globalMetricsAggregators: WeakMap | undefined; + + /** Overwrites TextEncoder used in `@sentry/utils`, need for `react-native@0.73` and older */ + encodePolyfill?: (input: string) => Uint8Array; + /** Overwrites TextDecoder used in `@sentry/utils`, need for `react-native@0.73` and older */ + decodePolyfill?: (input: Uint8Array) => string; +} + +// TODO(v9): Clean up or remove this type +type BackwardsCompatibleSentryCarrier = SentryCarrier & { // pre-v7 hub (replaced by .stack) hub: any; integrations?: any[]; @@ -30,14 +43,6 @@ type BackwardsCompatibleSentryCarrier = { // eslint-disable-next-line @typescript-eslint/ban-types [key: string]: Function; }; - globalScope: Scope | undefined; - defaultCurrentScope: Scope | undefined; - defaultIsolationScope: Scope | undefined; - globalMetricsAggregators: WeakMap | undefined; - /** Overwrites TextEncoder used in `@sentry/utils`, need for `react-native@0.73` and older */ - encodePolyfill?: (input: string) => Uint8Array; - /** Overwrites TextDecoder used in `@sentry/utils`, need for `react-native@0.73` and older */ - decodePolyfill?: (input: Uint8Array) => string; }; /** Internal global with common properties and Sentry extensions */ @@ -68,7 +73,7 @@ export type InternalGlobal = { */ _sentryDebugIds?: Record; __SENTRY__: { - [key: VersionString]: BackwardsCompatibleSentryCarrier; + [key: VersionString]: SentryCarrier; version: VersionString; } & BackwardsCompatibleSentryCarrier; /** @@ -83,10 +88,8 @@ export type InternalGlobal = { /** Get's the global object for the current JavaScript runtime */ export const GLOBAL_OBJ = globalThis as unknown as InternalGlobal; -type SingletonKeys = Exclude; - /** - * Returns a global singleton contained in the global `__SENTRY__` object. + * Returns a global singleton contained in the global `__SENTRY__[]` object. * * If the singleton doesn't already exist in `__SENTRY__`, it will be created using the given factory * function and added to the `__SENTRY__` object. @@ -96,7 +99,7 @@ type SingletonKeys = Exclude(name: SingletonKeys, creator: () => T, obj?: unknown): T { +export function getGlobalSingleton(name: keyof SentryCarrier, creator: () => T, obj?: unknown): T { const gbl = (obj || GLOBAL_OBJ) as InternalGlobal; const __SENTRY__ = (gbl.__SENTRY__ = gbl.__SENTRY__ || {}); const versionedCarrier = (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {}); From b74ee3f54ab00eb432f970ca2af9405c8d50ee05 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 May 2024 14:52:21 +0200 Subject: [PATCH 10/13] InternalGlobal .version optional --- packages/utils/src/worldwide.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/worldwide.ts b/packages/utils/src/worldwide.ts index 664aea9794bf..cd5078711a1c 100644 --- a/packages/utils/src/worldwide.ts +++ b/packages/utils/src/worldwide.ts @@ -74,7 +74,7 @@ export type InternalGlobal = { _sentryDebugIds?: Record; __SENTRY__: { [key: VersionString]: SentryCarrier; - version: VersionString; + version?: VersionString; } & BackwardsCompatibleSentryCarrier; /** * Raw module metadata that is injected by bundler plugins. From 0c0841872c00be748914dcfb59b1d1b302fe3146 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 May 2024 15:24:59 +0200 Subject: [PATCH 11/13] rm `registerClientOnGlobalHub` --- .../fixtures/loader.js | 3 +-- packages/core/src/carrier.ts | 3 +-- packages/core/src/sdk.ts | 18 ------------------ packages/utils/src/worldwide.ts | 3 +-- 4 files changed, 3 insertions(+), 24 deletions(-) diff --git a/dev-packages/browser-integration-tests/fixtures/loader.js b/dev-packages/browser-integration-tests/fixtures/loader.js index 39ad8439a86f..17a9a1d652a8 100644 --- a/dev-packages/browser-integration-tests/fixtures/loader.js +++ b/dev-packages/browser-integration-tests/fixtures/loader.js @@ -101,9 +101,8 @@ // carrier. This needs still needs to be added to the actual loader script before we // release the loader for v8! var v = e && e.version && e[e.version]; - var c = v && v.stack && v.stack.getClient(); - return !(void 0 === e || !e.hub || !e.hub.getClient()) || !!c; + return !(void 0 === e || !e.hub || !e.hub.getClient()) || !!v; } (n[i] = n[i] || {}), (n[i].onLoad = function (n) { diff --git a/packages/core/src/carrier.ts b/packages/core/src/carrier.ts index 22480bbac26a..1de9778bf7df 100644 --- a/packages/core/src/carrier.ts +++ b/packages/core/src/carrier.ts @@ -1,6 +1,5 @@ import type { Client, Integration, MetricsAggregator, Scope, VersionString } from '@sentry/types'; -import { GLOBAL_OBJ } from '@sentry/utils'; -import { SDK_VERSION } from '@sentry/utils'; +import { GLOBAL_OBJ, SDK_VERSION } from '@sentry/utils'; import type { AsyncContextStack } from './asyncContext/stackStrategy'; import type { AsyncContextStrategy } from './asyncContext/types'; diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index 81b1ed891024..d4c974242e1b 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -2,7 +2,6 @@ import type { Client, ClientOptions } from '@sentry/types'; import { consoleSandbox, logger } from '@sentry/utils'; import { getCurrentScope } from './currentScopes'; -import { getMainCarrier, getSentryCarrier } from './carrier'; import { DEBUG_BUILD } from './debug-build'; /** A class object that can instantiate Client objects. */ @@ -43,21 +42,4 @@ export function initAndBind( */ export function setCurrentClient(client: Client): void { getCurrentScope().setClient(client); - registerClientOnGlobalHub(client); -} - -/** - * Unfortunately, we still have to manually bind the client to the "stack" property set on the global - * Sentry carrier object. This is because certain scripts (e.g. our loader script) obtain - * the client via `window.__SENTRY__[version].stack.getClient()`. - * - * @see {@link ./asyncContext/stackStrategy.ts getAsyncContextStack} - */ -function registerClientOnGlobalHub(client: Client): void { - try { - // @ts-expect-error - purposefully not guarding the call here but try/catching for bundle size efficiency - getSentryCarrier(getMainCarrier()).stack.getStackTop().client = client; - } catch { - // ignore errors here - } } diff --git a/packages/utils/src/worldwide.ts b/packages/utils/src/worldwide.ts index cd5078711a1c..1622193fef27 100644 --- a/packages/utils/src/worldwide.ts +++ b/packages/utils/src/worldwide.ts @@ -103,6 +103,5 @@ export function getGlobalSingleton(name: keyof SentryCarrier, creator: () => const gbl = (obj || GLOBAL_OBJ) as InternalGlobal; const __SENTRY__ = (gbl.__SENTRY__ = gbl.__SENTRY__ || {}); const versionedCarrier = (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {}); - const singleton = versionedCarrier[name] || (versionedCarrier[name] = creator()); - return singleton; + return versionedCarrier[name] || (versionedCarrier[name] = creator()); } From 26f7d020dac0fb99b07ce7579564bdaa98057cae Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 May 2024 15:37:05 +0200 Subject: [PATCH 12/13] remove VersionString type -> fix TS3.8 --- packages/core/src/carrier.ts | 7 +++---- packages/types/src/index.ts | 2 +- packages/types/src/misc.ts | 5 ----- packages/utils/src/worldwide.ts | 7 +++---- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/core/src/carrier.ts b/packages/core/src/carrier.ts index 1de9778bf7df..7442244223ca 100644 --- a/packages/core/src/carrier.ts +++ b/packages/core/src/carrier.ts @@ -1,4 +1,4 @@ -import type { Client, Integration, MetricsAggregator, Scope, VersionString } from '@sentry/types'; +import type { Client, Integration, MetricsAggregator, Scope } from '@sentry/types'; import { GLOBAL_OBJ, SDK_VERSION } from '@sentry/utils'; import type { AsyncContextStack } from './asyncContext/stackStrategy'; import type { AsyncContextStrategy } from './asyncContext/types'; @@ -12,9 +12,8 @@ export interface Carrier { } type VersionedCarrier = { - [key: VersionString]: SentryCarrier; - version?: VersionString; -}; + version?: string; +} & Record, SentryCarrier>; interface SentryCarrier { acs?: AsyncContextStrategy; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index f9a67a9657d6..c90b7841f9ff 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -57,7 +57,7 @@ export type { Extra, Extras } from './extra'; export type { Hub } from './hub'; export type { Integration, IntegrationClass, IntegrationFn } from './integration'; export type { Mechanism } from './mechanism'; -export type { ExtractedNodeRequestData, HttpHeaderValue, Primitive, WorkerLocation, VersionString } from './misc'; +export type { ExtractedNodeRequestData, HttpHeaderValue, Primitive, WorkerLocation } from './misc'; export type { ClientOptions, Options } from './options'; export type { Package } from './package'; export type { PolymorphicEvent, PolymorphicRequest } from './polymorphics'; diff --git a/packages/types/src/misc.ts b/packages/types/src/misc.ts index 0ac8f4ddc62c..af9ed8fc6bd7 100644 --- a/packages/types/src/misc.ts +++ b/packages/types/src/misc.ts @@ -65,8 +65,3 @@ export interface WorkerLocation { export type Primitive = number | string | boolean | bigint | symbol | null | undefined; export type HttpHeaderValue = string | string[] | number | null; - -/** - * Type representing a semver version string - */ -export type VersionString = `${number}.${number}.${number}${`-${string}${string}` | ''}`; diff --git a/packages/utils/src/worldwide.ts b/packages/utils/src/worldwide.ts index 1622193fef27..0263fc5ec719 100644 --- a/packages/utils/src/worldwide.ts +++ b/packages/utils/src/worldwide.ts @@ -12,7 +12,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { Client, MetricsAggregator, Scope, VersionString } from '@sentry/types'; +import type { Client, MetricsAggregator, Scope } from '@sentry/types'; import type { SdkSource } from './env'; import { SDK_VERSION } from './version'; @@ -72,9 +72,8 @@ export type InternalGlobal = { * file. */ _sentryDebugIds?: Record; - __SENTRY__: { - [key: VersionString]: SentryCarrier; - version?: VersionString; + __SENTRY__: Record, SentryCarrier> & { + version?: string; } & BackwardsCompatibleSentryCarrier; /** * Raw module metadata that is injected by bundler plugins. From 58791ab4dc60d7bcf1f9f2b39e3fd69338c31003 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 28 May 2024 15:59:52 +0200 Subject: [PATCH 13/13] fix tests --- .../loader/onLoad/customInit/init.js | 4 +--- packages/core/test/lib/carrier.test.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/customInit/init.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/customInit/init.js index 1a2131b84daa..0ed922379b56 100644 --- a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/customInit/init.js +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/customInit/init.js @@ -19,8 +19,6 @@ window.sentryIsLoaded = () => { return !!( !(typeof __sentry === 'undefined') && __sentry.version && - __sentry[__sentry.version] && - __sentry[__sentry.version].stack && - __sentry[__sentry.version].stack.getClient() + !!__sentry[__sentry.version] ); }; diff --git a/packages/core/test/lib/carrier.test.ts b/packages/core/test/lib/carrier.test.ts index e44dde0ab827..3c94c96b98c1 100644 --- a/packages/core/test/lib/carrier.test.ts +++ b/packages/core/test/lib/carrier.test.ts @@ -22,17 +22,17 @@ describe('getSentryCarrier', () => { __SENTRY__: { version: SDK_VERSION, [SDK_VERSION]: { - integrations: [() => {}], + acs: {}, }, }, }; const globalObject = { ...originalGlobalObject }; - // @ts-expect-error - TS complains because the object spread makes the version key become type string + // @ts-expect-error - this is just a test object, not passing a full ACS const sentryCarrier = getSentryCarrier(globalObject); expect(sentryCarrier).toEqual({ - integrations: [expect.any(Function)], + acs: {}, }); expect(globalObject).toStrictEqual(originalGlobalObject); @@ -45,19 +45,19 @@ describe('getSentryCarrier', () => { __SENTRY__: { version: '8.0.0' as const, // another SDK set this '8.0.0': { - // and this object - extensions: {}, + // @ts-expect-error - this is just a test object, not passing a full stack + stack: {}, }, [SDK_VERSION]: { - integrations: [() => {}], + // @ts-expect-error - this is just a test object, not passing a full ACS + acs: {}, }, - // @ts-expect-error - this is just a test object, no need to pass a hub hub: {}, }, }); expect(sentryCarrier).toEqual({ - integrations: [expect.any(Function)], + acs: {}, }); });