Skip to content

Commit c600ac8

Browse files
committed
add basic implementation for populating and retrieving versioned carrier
1 parent c0c2136 commit c600ac8

File tree

6 files changed

+140
-17
lines changed

6 files changed

+140
-17
lines changed

packages/core/src/carrier.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import type { Integration } from '@sentry/types';
1+
import type { Integration, VersionString } from '@sentry/types';
22
import { GLOBAL_OBJ } from '@sentry/utils';
3+
import type { AsyncContextStack } from './asyncContext/stackStrategy';
34
import type { AsyncContextStrategy } from './asyncContext/types';
45
import { SDK_VERSION } from './version';
56

@@ -11,10 +12,10 @@ export interface Carrier {
1112
__SENTRY__?: VersionedCarrier;
1213
}
1314

14-
interface VersionedCarrier {
15-
version: typeof SDK_VERSION;
16-
[SDK_VERSION]: SentryCarrier;
17-
}
15+
type VersionedCarrier = {
16+
[key: VersionString]: SentryCarrier;
17+
version?: VersionString;
18+
};
1819

1920
interface SentryCarrier {
2021
acs?: AsyncContextStrategy;
@@ -27,6 +28,7 @@ interface SentryCarrier {
2728
// eslint-disable-next-line @typescript-eslint/ban-types
2829
[key: string]: Function;
2930
};
31+
stack?: AsyncContextStack;
3032
}
3133

3234
/**
@@ -45,11 +47,19 @@ export function getMainCarrier(): Carrier {
4547
/** Will either get the existing sentry carrier, or create a new one. */
4648
export function getSentryCarrier(carrier: Carrier): SentryCarrier {
4749
if (!carrier.__SENTRY__) {
48-
carrier.__SENTRY__ = {
49-
version: SDK_VERSION,
50-
[SDK_VERSION]: {
51-
extensions: {},
52-
},
50+
carrier.__SENTRY__ = {};
51+
}
52+
53+
// For now: First SDK that sets the .version property wins
54+
if (!carrier.__SENTRY__.version) {
55+
carrier.__SENTRY__.version = SDK_VERSION;
56+
}
57+
58+
// Intentionally populating and returning the version of "this" SDK instance
59+
// rather than what's set in .version so that "this" SDK always gets its carrier
60+
if (!carrier.__SENTRY__[SDK_VERSION]) {
61+
carrier.__SENTRY__[SDK_VERSION] = {
62+
extensions: {},
5363
};
5464
}
5565
return carrier.__SENTRY__[SDK_VERSION];

packages/core/src/sdk.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { Client, ClientOptions } from '@sentry/types';
22
import { consoleSandbox, logger } from '@sentry/utils';
33
import { getCurrentScope } from './currentScopes';
44

5-
import type { AsyncContextStack } from './asyncContext/stackStrategy';
65
import { getMainCarrier, getSentryCarrier } from './carrier';
76
import { DEBUG_BUILD } from './debug-build';
87

@@ -50,13 +49,13 @@ export function setCurrentClient(client: Client): void {
5049
/**
5150
* Unfortunately, we still have to manually bind the client to the "hub" property set on the global
5251
* Sentry carrier object. This is because certain scripts (e.g. our loader script) obtain
53-
* the client via `window.__SENTRY__.hub.getClient()`.
52+
* the client via `window.__SENTRY__[version].stack.getClient()`.
5453
*
5554
* @see {@link ./asyncContext/stackStrategy.ts getAsyncContextStack}
5655
*/
5756
function registerClientOnGlobalHub(client: Client): void {
58-
const sentryGlobal = getSentryCarrier(getMainCarrier()) as { hub?: AsyncContextStack };
59-
if (sentryGlobal.hub && typeof sentryGlobal.hub.getStackTop === 'function') {
60-
sentryGlobal.hub.getStackTop().client = client;
57+
const sentryGlobal = getSentryCarrier(getMainCarrier());
58+
if (sentryGlobal.stack && typeof sentryGlobal.stack.getStackTop === 'function') {
59+
sentryGlobal.stack.getStackTop().client = client;
6160
}
6261
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { getSentryCarrier } from '../../src/carrier';
2+
import { SDK_VERSION } from '../../src/version';
3+
4+
describe('getSentryCarrier', () => {
5+
describe('base case (one SDK)', () => {
6+
it('populates the default sentry carrier object if it does not exist', () => {
7+
const globalObject = {};
8+
const sentryCarrier = getSentryCarrier(globalObject);
9+
10+
expect(sentryCarrier).toEqual({
11+
extensions: {},
12+
});
13+
14+
expect(globalObject).toEqual({
15+
__SENTRY__: {
16+
version: SDK_VERSION,
17+
[SDK_VERSION]: {
18+
extensions: {},
19+
},
20+
},
21+
});
22+
});
23+
24+
it('returns the existing sentry carrier object if it already exists', () => {
25+
const originalGlobalObject = {
26+
__SENTRY__: {
27+
version: SDK_VERSION,
28+
[SDK_VERSION]: {
29+
integrations: [() => {}],
30+
},
31+
},
32+
};
33+
34+
const globalObject = { ...originalGlobalObject };
35+
// @ts-expect-error - TS complains because the object spread makes the version key become type string
36+
const sentryCarrier = getSentryCarrier(globalObject);
37+
38+
expect(sentryCarrier).toEqual({
39+
integrations: [expect.any(Function)],
40+
});
41+
42+
expect(globalObject).toStrictEqual(originalGlobalObject);
43+
});
44+
});
45+
46+
describe('multiple (older) SDKs', () => {
47+
it("returns the version of the sentry carrier object of the SDK's version rather than the one set in .version", () => {
48+
const sentryCarrier = getSentryCarrier({
49+
__SENTRY__: {
50+
version: '8.0.0' as const, // another SDK set this
51+
'8.0.0': {
52+
// and this object
53+
extensions: {},
54+
},
55+
[SDK_VERSION]: {
56+
integrations: [() => {}],
57+
},
58+
// @ts-expect-error - this is just a test object, no need to pass a hub
59+
hub: {},
60+
},
61+
});
62+
63+
expect(sentryCarrier).toEqual({
64+
integrations: [expect.any(Function)],
65+
});
66+
});
67+
68+
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", () => {
69+
const globalObject = {
70+
__SENTRY__: {
71+
version: '8.0.0' as const,
72+
'8.0.0': {
73+
// and this object
74+
integrations: [() => {}],
75+
},
76+
},
77+
};
78+
79+
const sentryCarrier = getSentryCarrier(globalObject);
80+
81+
expect(sentryCarrier).toEqual({
82+
extensions: {},
83+
});
84+
85+
expect(globalObject).toEqual({
86+
__SENTRY__: {
87+
version: '8.0.0',
88+
'8.0.0': {
89+
integrations: [expect.any(Function)],
90+
},
91+
[SDK_VERSION]: {
92+
extensions: {},
93+
},
94+
},
95+
});
96+
});
97+
});
98+
});

packages/types/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export type { Extra, Extras } from './extra';
5757
export type { Hub } from './hub';
5858
export type { Integration, IntegrationClass, IntegrationFn } from './integration';
5959
export type { Mechanism } from './mechanism';
60-
export type { ExtractedNodeRequestData, HttpHeaderValue, Primitive, WorkerLocation } from './misc';
60+
export type { ExtractedNodeRequestData, HttpHeaderValue, Primitive, WorkerLocation, VersionString } from './misc';
6161
export type { ClientOptions, Options } from './options';
6262
export type { Package } from './package';
6363
export type { PolymorphicEvent, PolymorphicRequest } from './polymorphics';

packages/types/src/misc.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,8 @@ export interface WorkerLocation {
6565
export type Primitive = number | string | boolean | bigint | symbol | null | undefined;
6666

6767
export type HttpHeaderValue = string | string[] | number | null;
68+
69+
/**
70+
* Type representing a semver version string
71+
*/
72+
export type VersionString = `${number}.${number}.${number}${`-${string}${string}` | ''}`;

packages/utils/src/worldwide.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
/* eslint-disable @typescript-eslint/no-explicit-any */
1414

15-
import type { Client, MetricsAggregator, Scope } from '@sentry/types';
15+
import type { Client, MetricsAggregator, Scope, VersionString } from '@sentry/types';
1616

1717
import type { SdkSource } from './env';
1818

@@ -44,6 +44,17 @@ export interface InternalGlobal {
4444
*/
4545
_sentryDebugIds?: Record<string, string>;
4646
__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+
};
57+
version: VersionString;
4758
hub: any;
4859
logger: any;
4960
extensions?: {

0 commit comments

Comments
 (0)