Skip to content

Commit 8a2dce8

Browse files
committed
ref(core): Use versioned carrier on global object
1 parent 2c41300 commit 8a2dce8

File tree

11 files changed

+95
-49
lines changed

11 files changed

+95
-49
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
/**
4+
* This simulates an old, pre-v8 SDK setting itself up on the global __SENTRY__ carrier.
5+
*/
6+
window.__SENTRY__ = {
7+
hub: {
8+
isOlderThan: version => {
9+
return version < 7;
10+
},
11+
},
12+
};
13+
14+
window.Sentry = Sentry;
15+
16+
Sentry.init({
17+
dsn: 'https://[email protected]/1337',
18+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const sentryCarrier = window && window.__SENTRY__;
2+
3+
/**
4+
* Simulate an old pre v8 SDK obtaining the hub from the global sentry carrier
5+
* and checking for the hub version.
6+
*/
7+
const res = sentryCarrier.hub && sentryCarrier.hub.isOlderThan(7);
8+
9+
// Write back result into the document
10+
document.getElementById('olderThan').innerText = res;
11+
12+
console.log(sentryCarrier);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
</head>
6+
<body>
7+
<p id="olderThan"></p>
8+
</body>
9+
</html>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { expect } from '@playwright/test';
2+
3+
import { sentryTest } from '../../../utils/fixtures';
4+
5+
sentryTest(
6+
"doesn't crash if older SDKs access `hub.isOlderThan` on the global object",
7+
async ({ getLocalTestUrl, page }) => {
8+
const url = await getLocalTestUrl({ testDir: __dirname });
9+
await page.goto(url);
10+
11+
await expect(page.locator('#olderThan')).toHaveText('false');
12+
},
13+
);

packages/core/src/asyncContext/stackStrategy.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -132,29 +132,19 @@ export class AsyncContextStack {
132132
*/
133133
function getAsyncContextStack(): AsyncContextStack {
134134
const registry = getMainCarrier();
135+
const sentry = getSentryCarrier(registry);
135136

136-
// For now we continue to keep this as `hub` on the ACS,
137-
// as e.g. the Loader Script relies on this.
138-
// Eventually we may change this if/when we update the loader to not require this field anymore
139-
// Related, we also write to `hub` in {@link ./../sdk.ts registerClientOnGlobalHub}
140-
const sentry = getSentryCarrier(registry) as { hub?: AsyncContextStack };
141-
142-
if (sentry.hub) {
143-
return sentry.hub;
144-
}
145-
146-
sentry.hub = new AsyncContextStack(getDefaultCurrentScope(), getDefaultIsolationScope());
147-
return sentry.hub;
137+
return (sentry.stack = sentry.stack || new AsyncContextStack(getDefaultCurrentScope(), getDefaultIsolationScope()));
148138
}
149139

150140
function withScope<T>(callback: (scope: ScopeInterface) => T): T {
151141
return getAsyncContextStack().withScope(callback);
152142
}
153143

154144
function withSetScope<T>(scope: ScopeInterface, callback: (scope: ScopeInterface) => T): T {
155-
const hub = getAsyncContextStack() as AsyncContextStack;
156-
return hub.withScope(() => {
157-
hub.getStackTop().scope = scope;
145+
const stack = getAsyncContextStack() as AsyncContextStack;
146+
return stack.withScope(() => {
147+
stack.getStackTop().scope = scope;
158148
return callback(scope);
159149
});
160150
}

packages/core/src/carrier.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { AsyncContextStrategy } from './asyncContext/types';
55
import { SDK_VERSION } from './version';
66

77
/**
8-
* An object that contains a hub and maintains a scope stack.
8+
* An object that contains globally accessible properties and maintains a scope stack.
99
* @hidden
1010
*/
1111
export interface Carrier {

packages/core/src/integration.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export function setupIntegration(client: Client, integration: Integration, integ
146146
DEBUG_BUILD && logger.log(`Integration installed: ${integration.name}`);
147147
}
148148

149-
/** Add an integration to the current hub's client. */
149+
/** Add an integration to the current scope's client. */
150150
export function addIntegration(integration: Integration): void {
151151
const client = getClient();
152152

packages/core/src/sdk.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function setCurrentClient(client: Client): void {
4747
}
4848

4949
/**
50-
* Unfortunately, we still have to manually bind the client to the "hub" property set on the global
50+
* Unfortunately, we still have to manually bind the client to the "stack" property set on the global
5151
* Sentry carrier object. This is because certain scripts (e.g. our loader script) obtain
5252
* the client via `window.__SENTRY__[version].stack.getClient()`.
5353
*

packages/core/src/utils/prepareEvent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export function prepareEvent(
7676
const clientEventProcessors = client ? client.getEventProcessors() : [];
7777

7878
// This should be the last thing called, since we want that
79-
// {@link Hub.addEventProcessor} gets the finished prepared event.
79+
// {@link Scope.addEventProcessor} gets the finished prepared event.
8080
// Merge scope data together
8181
const data = getGlobalScope().getScopeData();
8282

packages/utils/src/version.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const SDK_VERSION = '8.4.0';

packages/utils/src/worldwide.ts

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,33 @@
1515
import type { Client, MetricsAggregator, Scope, VersionString } from '@sentry/types';
1616

1717
import type { SdkSource } from './env';
18+
import { SDK_VERSION } from './version';
19+
20+
type BackwardsCompatibleSentryCarrier = {
21+
// v8 scope stack (replaces .hub)
22+
stack?: any;
23+
acs?: any;
24+
// pre-v7 hub (replaced by .stack)
25+
hub: any;
26+
integrations?: any[];
27+
logger: any;
28+
extensions?: {
29+
/** Extension methods for the hub, which are bound to the current Hub instance */
30+
// eslint-disable-next-line @typescript-eslint/ban-types
31+
[key: string]: Function;
32+
};
33+
globalScope: Scope | undefined;
34+
defaultCurrentScope: Scope | undefined;
35+
defaultIsolationScope: Scope | undefined;
36+
globalMetricsAggregators: WeakMap<Client, MetricsAggregator> | undefined;
37+
/** Overwrites TextEncoder used in `@sentry/utils`, need for `[email protected]` and older */
38+
encodePolyfill?: (input: string) => Uint8Array;
39+
/** Overwrites TextDecoder used in `@sentry/utils`, need for `[email protected]` and older */
40+
decodePolyfill?: (input: Uint8Array) => string;
41+
};
1842

1943
/** Internal global with common properties and Sentry extensions */
20-
export interface InternalGlobal {
44+
export type InternalGlobal = {
2145
navigator?: { userAgent?: string };
2246
console: Console;
2347
Sentry?: any;
@@ -44,45 +68,23 @@ export interface InternalGlobal {
4468
*/
4569
_sentryDebugIds?: Record<string, string>;
4670
__SENTRY__: {
47-
[key: VersionString]: {
48-
acs?: any;
49-
integrations?: any[];
50-
extensions?: {
51-
/** Extra Hub properties injected by various SDKs */
52-
// eslint-disable-next-line @typescript-eslint/ban-types
53-
[key: string]: Function;
54-
};
55-
stack?: any;
56-
};
71+
[key: VersionString]: BackwardsCompatibleSentryCarrier;
5772
version: VersionString;
58-
hub: any;
59-
logger: any;
60-
extensions?: {
61-
/** Extension methods for the hub, which are bound to the current Hub instance */
62-
// eslint-disable-next-line @typescript-eslint/ban-types
63-
[key: string]: Function;
64-
};
65-
globalScope: Scope | undefined;
66-
defaultCurrentScope: Scope | undefined;
67-
defaultIsolationScope: Scope | undefined;
68-
globalMetricsAggregators: WeakMap<Client, MetricsAggregator> | undefined;
69-
/** Overwrites TextEncoder used in `@sentry/utils`, need for `[email protected]` and older */
70-
encodePolyfill?: (input: string) => Uint8Array;
71-
/** Overwrites TextDecoder used in `@sentry/utils`, need for `[email protected]` and older */
72-
decodePolyfill?: (input: Uint8Array) => string;
73-
};
73+
} & BackwardsCompatibleSentryCarrier;
7474
/**
7575
* Raw module metadata that is injected by bundler plugins.
7676
*
7777
* Keys are `error.stack` strings, values are the metadata.
7878
*/
7979
_sentryModuleMetadata?: Record<string, any>;
8080
_sentryEsmLoaderHookRegistered?: boolean;
81-
}
81+
};
8282

8383
/** Get's the global object for the current JavaScript runtime */
8484
export const GLOBAL_OBJ = globalThis as unknown as InternalGlobal;
8585

86+
type SingletonKeys = Exclude<keyof InternalGlobal['__SENTRY__'], 'version' | VersionString>;
87+
8688
/**
8789
* Returns a global singleton contained in the global `__SENTRY__` object.
8890
*
@@ -94,9 +96,10 @@ export const GLOBAL_OBJ = globalThis as unknown as InternalGlobal;
9496
* @param obj (Optional) The global object on which to look for `__SENTRY__`, if not `GLOBAL_OBJ`'s return value
9597
* @returns the singleton
9698
*/
97-
export function getGlobalSingleton<T>(name: keyof InternalGlobal['__SENTRY__'], creator: () => T, obj?: unknown): T {
99+
export function getGlobalSingleton<T>(name: SingletonKeys, creator: () => T, obj?: unknown): T {
98100
const gbl = (obj || GLOBAL_OBJ) as InternalGlobal;
99101
const __SENTRY__ = (gbl.__SENTRY__ = gbl.__SENTRY__ || {});
100-
const singleton = __SENTRY__[name] || (__SENTRY__[name] = creator());
102+
const versionedCarrier = (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {});
103+
const singleton = versionedCarrier[name] || (versionedCarrier[name] = creator());
101104
return singleton;
102105
}

0 commit comments

Comments
 (0)