Skip to content

Commit 005f40d

Browse files
Lms24mydea
andauthored
ref(core): Use versioned carrier on global object (#12206)
This PR implements a versioned Sentry carrier as described in #12188. The idea is that SDKs can from now on access their global Sentry instance and thereby no longer overwrite or interfere with potentially other SDKs (e.g. 3rd party libraries, scripts, etc). Internally, SDKs can access their carrier via the `window.__SENTRY__[SDK_VERSION]`. Externally (spotlight, loader script) via `window.__SENTRY__[window.__SENTRY__.version]`. --------- Co-authored-by: Francesco Novy <francesco.novy@sentry.io>
1 parent fb1ed25 commit 005f40d

File tree

24 files changed

+411
-90
lines changed

24 files changed

+411
-90
lines changed
Lines changed: 137 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,139 @@
1-
!function(n,e,r,t,i,o,a,c,s){for(var u=s,f=0;f<document.scripts.length;f++)if(document.scripts[f].src.indexOf(o)>-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__;return!(void 0===e||!e.hub||!e.hub.getClient())}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()}))}
2-
(
1+
!(function (n, e, r, t, i, o, a, c, s) {
2+
for (var u = s, f = 0; f < document.scripts.length; f++)
3+
if (document.scripts[f].src.indexOf(o) > -1) {
4+
u && 'no' === document.scripts[f].getAttribute('data-lazy') && (u = !1);
5+
break;
6+
}
7+
var p = [];
8+
function l(n) {
9+
return 'e' in n;
10+
}
11+
function d(n) {
12+
return 'p' in n;
13+
}
14+
function _(n) {
15+
return 'f' in n;
16+
}
17+
var v = [];
18+
function y(n) {
19+
u &&
20+
(l(n) || d(n) || (_(n) && n.f.indexOf('capture') > -1) || (_(n) && n.f.indexOf('showReportDialog') > -1)) &&
21+
m(),
22+
v.push(n);
23+
}
24+
function g() {
25+
y({ e: [].slice.call(arguments) });
26+
}
27+
function h(n) {
28+
y({ p: n });
29+
}
30+
function E() {
31+
try {
32+
n.SENTRY_SDK_SOURCE = 'loader';
33+
var e = n[i],
34+
o = e.init;
35+
(e.init = function (i) {
36+
n.removeEventListener(r, g), n.removeEventListener(t, h);
37+
var a = c;
38+
for (var s in i) Object.prototype.hasOwnProperty.call(i, s) && (a[s] = i[s]);
39+
!(function (n, e) {
40+
var r = n.integrations || [];
41+
if (!Array.isArray(r)) return;
42+
var t = r.map(function (n) {
43+
return n.name;
44+
});
45+
n.tracesSampleRate &&
46+
-1 === t.indexOf('BrowserTracing') &&
47+
(e.BrowserTracing
48+
? r.push(new e.BrowserTracing())
49+
: e.browserTracingIntegration && r.push(e.browserTracingIntegration()));
50+
(n.replaysSessionSampleRate || n.replaysOnErrorSampleRate) &&
51+
-1 === t.indexOf('Replay') &&
52+
(e.Replay ? r.push(new e.Replay()) : e.replayIntegration && r.push(e.replayIntegration()));
53+
n.integrations = r;
54+
})(a, e),
55+
o(a);
56+
}),
57+
setTimeout(function () {
58+
return (function (e) {
59+
try {
60+
'function' == typeof n.sentryOnLoad && (n.sentryOnLoad(), (n.sentryOnLoad = void 0));
61+
for (var r = 0; r < p.length; r++) 'function' == typeof p[r] && p[r]();
62+
p.splice(0);
63+
for (r = 0; r < v.length; r++) {
64+
_((o = v[r])) && 'init' === o.f && e.init.apply(e, o.a);
65+
}
66+
L() || e.init();
67+
var t = n.onerror,
68+
i = n.onunhandledrejection;
69+
for (r = 0; r < v.length; r++) {
70+
var o;
71+
if (_((o = v[r]))) {
72+
if ('init' === o.f) continue;
73+
e[o.f].apply(e, o.a);
74+
} else l(o) && t ? t.apply(n, o.e) : d(o) && i && i.apply(n, [o.p]);
75+
}
76+
} catch (n) {
77+
console.error(n);
78+
}
79+
})(e);
80+
});
81+
} catch (n) {
82+
console.error(n);
83+
}
84+
}
85+
var O = !1;
86+
function m() {
87+
if (!O) {
88+
O = !0;
89+
var n = e.scripts[0],
90+
r = e.createElement('script');
91+
(r.src = a),
92+
(r.crossOrigin = 'anonymous'),
93+
r.addEventListener('load', E, { once: !0, passive: !0 }),
94+
n.parentNode.insertBefore(r, n);
95+
}
96+
}
97+
function L() {
98+
var e = n.__SENTRY__;
99+
100+
// TODO: This is a temporary hack to make the loader script compatible with the versioned
101+
// carrier. This needs still needs to be added to the actual loader script before we
102+
// release the loader for v8!
103+
var v = e && e.version && e[e.version];
104+
105+
return !(void 0 === e || !e.hub || !e.hub.getClient()) || !!v;
106+
}
107+
(n[i] = n[i] || {}),
108+
(n[i].onLoad = function (n) {
109+
L() ? n() : p.push(n);
110+
}),
111+
(n[i].forceLoad = function () {
112+
setTimeout(function () {
113+
m();
114+
});
115+
}),
116+
[
117+
'init',
118+
'addBreadcrumb',
119+
'captureMessage',
120+
'captureException',
121+
'captureEvent',
122+
'configureScope',
123+
'withScope',
124+
'showReportDialog',
125+
].forEach(function (e) {
126+
n[i][e] = function () {
127+
y({ f: e, a: arguments });
128+
};
129+
}),
130+
n.addEventListener(r, g),
131+
n.addEventListener(t, h),
132+
u ||
133+
setTimeout(function () {
134+
m();
135+
});
136+
})(
3137
window,
4138
document,
5139
'error',
@@ -8,5 +142,5 @@
8142
'loader.js',
9143
__LOADER_BUNDLE__,
10144
__LOADER_OPTIONS__,
11-
__LOADER_LAZY__
145+
__LOADER_LAZY__,
12146
);

dev-packages/browser-integration-tests/loader-suites/loader/onLoad/customInit/init.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,9 @@ window.sentryIsLoaded = () => {
1616
const __sentry = window.__SENTRY__;
1717

1818
// If there is a global __SENTRY__ that means that in any of the callbacks init() was already invoked
19-
return !!(!(typeof __sentry === 'undefined') && __sentry.hub && __sentry.hub.getClient());
19+
return !!(
20+
!(typeof __sentry === 'undefined') &&
21+
__sentry.version &&
22+
!!__sentry[__sentry.version]
23+
);
2024
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
/**
4+
* This simulates an relatively new v7 SDK setting acs on the __SENTRY__ carrier.
5+
* see: https://github.com/getsentry/sentry-javascript/issues/12054
6+
*/
7+
window.__SENTRY__ = {
8+
acs: {
9+
getCurrentScope: () => {
10+
return 'scope';
11+
},
12+
},
13+
};
14+
15+
window.Sentry = Sentry;
16+
17+
Sentry.init({
18+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
19+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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.acs && sentryCarrier.acs.getCurrentScope();
8+
9+
// Write back result into the document
10+
document.getElementById('currentScope').innerText = res && 'scope';
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="currentScope"></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 `acs.getCurrentScope` 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('#currentScope')).toHaveText('scope');
12+
},
13+
);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
* see: https://github.com/getsentry/sentry-javascript/issues/12155
6+
*/
7+
window.__SENTRY__ = {
8+
hub: {
9+
isOlderThan: version => {
10+
return version < 7;
11+
},
12+
},
13+
};
14+
15+
window.Sentry = Sentry;
16+
17+
Sentry.init({
18+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
19+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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;
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+
);

0 commit comments

Comments
 (0)