From 532bd050c3b31937ca215e8f594a606524bd197f Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Fri, 21 Jun 2024 18:48:42 +0200 Subject: [PATCH 01/12] chore(deps): Update to sentry-javascript 8.11.0 --- package.json | 16 +-- src/js/integrations/debugsymbolicator.ts | 2 +- src/js/tracing/reactnativetracing.ts | 14 +- src/js/tracing/timetodisplay.tsx | 10 +- src/js/vendor/react-native/index.ts | 2 +- tsconfig.build.json | 3 +- yarn.lock | 162 +++++++++++------------ 7 files changed, 105 insertions(+), 104 deletions(-) diff --git a/package.json b/package.json index d2482963da..fdbe3bd950 100644 --- a/package.json +++ b/package.json @@ -67,20 +67,20 @@ "react-native": ">=0.65.0" }, "dependencies": { - "@sentry/browser": "8.4.0", + "@sentry/browser": "8.11.0", "@sentry/cli": "2.31.2", - "@sentry/core": "8.4.0", - "@sentry/react": "8.4.0", - "@sentry/types": "8.4.0", - "@sentry/utils": "8.4.0" + "@sentry/core": "8.11.0", + "@sentry/react": "8.11.0", + "@sentry/types": "8.11.0", + "@sentry/utils": "8.11.0" }, "devDependencies": { "@babel/core": "^7.23.5", "@expo/metro-config": "0.17.5", "@mswjs/interceptors": "^0.25.15", - "@sentry-internal/eslint-config-sdk": "8.4.0", - "@sentry-internal/eslint-plugin-sdk": "8.4.0", - "@sentry-internal/typescript": "8.4.0", + "@sentry-internal/eslint-config-sdk": "8.11.0", + "@sentry-internal/eslint-plugin-sdk": "8.11.0", + "@sentry-internal/typescript": "8.11.0", "@sentry/wizard": "3.16.3", "@types/jest": "^29.5.3", "@types/node": "^20.9.3", diff --git a/src/js/integrations/debugsymbolicator.ts b/src/js/integrations/debugsymbolicator.ts index aa2ccc1101..f0d5e86141 100644 --- a/src/js/integrations/debugsymbolicator.ts +++ b/src/js/integrations/debugsymbolicator.ts @@ -72,7 +72,7 @@ async function symbolicate(rawStack: string, skipFirstFrames: number = 0): Promi } // This has been changed in an react-native version so stack is contained in here - const newStack = prettyStack.stack || prettyStack; + const newStack = 'stack' in prettyStack ? prettyStack.stack : prettyStack; // https://github.com/getsentry/sentry-javascript/blob/739d904342aaf9327312f409952f14ceff4ae1ab/packages/utils/src/stacktrace.ts#L23 // Match SentryParser which counts lines of stack (-1 for first line with the Error message) diff --git a/src/js/tracing/reactnativetracing.ts b/src/js/tracing/reactnativetracing.ts index f3947fb876..6a6cbce694 100644 --- a/src/js/tracing/reactnativetracing.ts +++ b/src/js/tracing/reactnativetracing.ts @@ -287,27 +287,27 @@ export class ReactNativeTracing implements Integration { public startUserInteractionSpan(userInteractionId: { elementId: string | undefined; op: string }): Span | undefined { const client = this._client; if (!client) { - return; + return undefined; } const { elementId, op } = userInteractionId; if (!this.options.enableUserInteractionTracing) { logger.log('[ReactNativeTracing] User Interaction Tracing is disabled.'); - return; + return undefined; } if (!this.options.routingInstrumentation) { logger.error( '[ReactNativeTracing] User Interaction Tracing is not working because no routing instrumentation is set.', ); - return; + return undefined; } if (!elementId) { logger.log('[ReactNativeTracing] User Interaction Tracing can not create transaction with undefined elementId.'); - return; + return undefined; } if (!this._currentRoute) { logger.log('[ReactNativeTracing] User Interaction Tracing can not create transaction without a current route.'); - return; + return undefined; } const activeTransaction = getActiveSpan(); @@ -321,7 +321,7 @@ export class ReactNativeTracing implements Integration { spanToJSON(activeTransaction).description } exists on the scope.`, ); - return; + return undefined; } const name = `${this._currentRoute}.${elementId}`; @@ -335,7 +335,7 @@ export class ReactNativeTracing implements Integration { spanToJSON(this._inflightInteractionTransaction).description } already exists on the scope.`, ); - return; + return undefined; } if (this._inflightInteractionTransaction) { diff --git a/src/js/tracing/timetodisplay.tsx b/src/js/tracing/timetodisplay.tsx index 8a8c6e4236..6976f2025f 100644 --- a/src/js/tracing/timetodisplay.tsx +++ b/src/js/tracing/timetodisplay.tsx @@ -94,7 +94,7 @@ export function startTimeToInitialDisplaySpan( const activeSpan = getActiveSpan(); if (!activeSpan) { logger.warn(`[TimeToDisplay] No active span found to attach ui.load.initial_display to.`); - return; + return undefined; } const existingSpan = getSpanDescendants(activeSpan).find((span) => spanToJSON(span).op === 'ui.load.initial_display'); @@ -111,7 +111,7 @@ export function startTimeToInitialDisplaySpan( }); if (!initialDisplaySpan) { - return; + return undefined; } if (!options?.isAutoInstrumented) { @@ -133,7 +133,7 @@ export function startTimeToFullDisplaySpan( const activeSpan = getActiveSpan(); if (!activeSpan) { logger.warn(`[TimeToDisplay] No active span found to attach ui.load.full_display to.`); - return; + return undefined; } const descendantSpans = getSpanDescendants(activeSpan); @@ -141,7 +141,7 @@ export function startTimeToFullDisplaySpan( const initialDisplaySpan = descendantSpans.find((span) => spanToJSON(span).op === 'ui.load.initial_display'); if (!initialDisplaySpan) { logger.warn(`[TimeToDisplay] No initial display span found to attach ui.load.full_display to.`); - return; + return undefined; } const existingSpan = descendantSpans.find((span) => spanToJSON(span).op === 'ui.load.full_display'); @@ -157,7 +157,7 @@ export function startTimeToFullDisplaySpan( ...options, }); if (!fullDisplaySpan) { - return; + return undefined; } const timeout = setTimeout(() => { diff --git a/src/js/vendor/react-native/index.ts b/src/js/vendor/react-native/index.ts index de54382381..d5f8f2db1a 100644 --- a/src/js/vendor/react-native/index.ts +++ b/src/js/vendor/react-native/index.ts @@ -44,7 +44,7 @@ export type CodeFrame = Readonly<{ export type SymbolicatedStackTrace = Readonly<{ stack: Array; codeFrame?: CodeFrame; -}>; +}> | Array; // Adapted from https://github.com/facebook/react-native/blob/d09c02f9e2d468e4d0bde51890e312ae7003a3e6/packages/react-native/Libraries/Core/Devtools/getDevServer.js#L17 export type DevServerInfo = { diff --git a/tsconfig.build.json b/tsconfig.build.json index a246661478..84c1ce9e7c 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -24,6 +24,7 @@ "module": "es6", "skipLibCheck": true, "allowSyntheticDefaultImports": true, - "strictBindCallApply": true + "strictBindCallApply": true, + "strictNullChecks": false } } diff --git a/yarn.lock b/yarn.lock index 2c75e976fb..669ab7615e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3739,22 +3739,22 @@ component-type "^1.2.1" join-component "^1.1.0" -"@sentry-internal/browser-utils@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.4.0.tgz#5b108878e93713757d75e7e8ae7780297d36ad17" - integrity sha512-Mfm3TK3KUlghhuKM3rjTeD4D5kAiB7iVNFoaDJIJBVKa67M9BvlNTnNJMDi7+9rV4RuLQYxXn0p5HEZJFYp3Zw== - dependencies: - "@sentry/core" "8.4.0" - "@sentry/types" "8.4.0" - "@sentry/utils" "8.4.0" - -"@sentry-internal/eslint-config-sdk@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/eslint-config-sdk/-/eslint-config-sdk-8.4.0.tgz#432d56a8969c4b1c699cfaa0d5f2be4eb7835dbc" - integrity sha512-9jC2PBUw0Gn2ZlCeKeDfSMKsUPJczyQYoG7x2gHce/zur7jpKIc5DTt5NM74Sjyai7wMyUY2rLP8GnFdYPQosg== - dependencies: - "@sentry-internal/eslint-plugin-sdk" "8.4.0" - "@sentry-internal/typescript" "8.4.0" +"@sentry-internal/browser-utils@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.11.0.tgz#ee73594bde569608b0dd0c8aa499f8497dbb0f53" + integrity sha512-PCnmzeLm7eTdMleVWa1jbdNcB6M5R17CSX8oQF6A/5a2w9qW6HbjEwK6X4yc9MzsFXFaTNekvPQLMRhIE1MgpA== + dependencies: + "@sentry/core" "8.11.0" + "@sentry/types" "8.11.0" + "@sentry/utils" "8.11.0" + +"@sentry-internal/eslint-config-sdk@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/eslint-config-sdk/-/eslint-config-sdk-8.11.0.tgz#73009825229e8f1fd8053c42aca8409c94695610" + integrity sha512-Qhwnhx+rEuD6Y1Ar5u74nn51f+rOfqtkyYkVrfJRCH+ob+hTD92TjA4yWer9nzYaVpPJmh09vktyAVAlFDXoaQ== + dependencies: + "@sentry-internal/eslint-plugin-sdk" "8.11.0" + "@sentry-internal/typescript" "8.11.0" "@typescript-eslint/eslint-plugin" "^5.48.0" "@typescript-eslint/parser" "^5.48.0" eslint-config-prettier "^6.11.0" @@ -3764,39 +3764,39 @@ eslint-plugin-jsdoc "^30.0.3" eslint-plugin-simple-import-sort "^5.0.3" -"@sentry-internal/eslint-plugin-sdk@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/eslint-plugin-sdk/-/eslint-plugin-sdk-8.4.0.tgz#eca0a66d2b4af76139881d4c252db83ca301afbf" - integrity sha512-w1YbQR+c6w0mu3WUvX2tD/E6o3hCQmgutBlQu0qElVmEPwgUgMS6b5036MnYZt7AHCIfuBqEA4DHaQt+3zT/Lw== +"@sentry-internal/eslint-plugin-sdk@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/eslint-plugin-sdk/-/eslint-plugin-sdk-8.11.0.tgz#f79bdb65fc4b6b1e2bbe1400809cb890e783928f" + integrity sha512-1JVY3u2/4IRFuKzXjW6EJ4tbGVoQocWSi2f6xjUndN55cHJe94it/IrYFUnK1wjWOX4JIrJrmuow3yULgTD/Ag== -"@sentry-internal/feedback@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.4.0.tgz#81067dadda249b354b72f5adba20374dea43fdf4" - integrity sha512-1/WshI2X9seZAQXrOiv6/LU08fbSSvJU0b1ZWMhn+onb/FWPomsL/UN0WufCYA65S5JZGdaWC8fUcJxWC8PATQ== +"@sentry-internal/feedback@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.11.0.tgz#72bfa11678dfa19857a3cf6943a05f0ddb72f12d" + integrity sha512-cMiFAuHP4jXCqWD7/UA5cvl0ee3hN5klAWTDVCZutnZ30pbUurg+nIggYBcaxdtLlqW6BCwyVs2nb/OB75CCSQ== dependencies: - "@sentry/core" "8.4.0" - "@sentry/types" "8.4.0" - "@sentry/utils" "8.4.0" + "@sentry/core" "8.11.0" + "@sentry/types" "8.11.0" + "@sentry/utils" "8.11.0" -"@sentry-internal/replay-canvas@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.4.0.tgz#cf5e903d8935ba6b60a5027d0055902987353920" - integrity sha512-g+U4IPQdODCg7fQQVNvH6ix05Tl1mOQXXRexgtp+tXdys4sHQSBUYraJYZy+mY3OGnLRgKFqELM0fnffJSpuyQ== +"@sentry-internal/replay-canvas@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.11.0.tgz#c01c1385c426a39189bec151a74bc173b6068a4b" + integrity sha512-SrBFI0vwyeyUjibCbYfxzCNMd07QMDNoi+0SYzhBagp6ALbU8r/pa02JRsnr//qhZt2NOM6S2kks9e2VHr6hYg== dependencies: - "@sentry-internal/replay" "8.4.0" - "@sentry/core" "8.4.0" - "@sentry/types" "8.4.0" - "@sentry/utils" "8.4.0" + "@sentry-internal/replay" "8.11.0" + "@sentry/core" "8.11.0" + "@sentry/types" "8.11.0" + "@sentry/utils" "8.11.0" -"@sentry-internal/replay@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.4.0.tgz#8fc4a6bf1d5f480fcde2d56cd75042953e44efda" - integrity sha512-RSzQwCF/QTi5/5XAuj0VJImAhu4MheeHYvAbr/PuMSF4o1j89gBA7e3boA4u8633IqUeu5w3S5sb6jVrKaVifg== +"@sentry-internal/replay@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.11.0.tgz#58c923fbae32204ad32e1e99c01c4040c1e06b2f" + integrity sha512-NyuHW1Ds2GGW6PjN7nnRl/XoM31Y/BUnOhhLbNmbxWj5mgWuUP/7tOlz2PhP0YqZxVteZ99QIssfSRgtYOeQlg== dependencies: - "@sentry-internal/browser-utils" "8.4.0" - "@sentry/core" "8.4.0" - "@sentry/types" "8.4.0" - "@sentry/utils" "8.4.0" + "@sentry-internal/browser-utils" "8.11.0" + "@sentry/core" "8.11.0" + "@sentry/types" "8.11.0" + "@sentry/utils" "8.11.0" "@sentry-internal/tracing@7.76.0": version "7.76.0" @@ -3807,23 +3807,23 @@ "@sentry/types" "7.76.0" "@sentry/utils" "7.76.0" -"@sentry-internal/typescript@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/typescript/-/typescript-8.4.0.tgz#6ebc97285e516ad2c2ef09e368821d9ecb39c57e" - integrity sha512-EgkYnSAi1Ryvb5t2xmMA7mc63ohpFh/CRaDdQUBQdNOK+TVH2wul7h525V3hUkxDJRHZxnNb7a6TWvfCZR7bYA== +"@sentry-internal/typescript@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/typescript/-/typescript-8.11.0.tgz#a4552bf3918671d6723b6ec1fd448180a94217b6" + integrity sha512-AsyiBomA0MCJulIV15+JWqabgfNj/2Wjsu3epAvGtjcNST7tCq0tfEwjXUv4JKGMORmkUqf1Js15wnu3HEXBUA== -"@sentry/browser@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.4.0.tgz#f4aa381eab212432d71366884693a36c2e3a1675" - integrity sha512-hmXeIZBdN0A6yCuoMTcigGxLl42nbeb205fXtouwE7Maa0qM2HM+Ijq0sHzbhxR3zU0JXDtcJh1k6wtJOREJ3g== +"@sentry/browser@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.11.0.tgz#9e0282a9136d138c8b6c001f0ac9face9a9ce18b" + integrity sha512-++5IrBpzkaAptNjAYnGTnQ2lCjmU6nlu/ABFjUTgi7Vu+ZNiY8qYKEUw65mSxD3EoFLt8IwtjvfAwSMVTB2q8w== dependencies: - "@sentry-internal/browser-utils" "8.4.0" - "@sentry-internal/feedback" "8.4.0" - "@sentry-internal/replay" "8.4.0" - "@sentry-internal/replay-canvas" "8.4.0" - "@sentry/core" "8.4.0" - "@sentry/types" "8.4.0" - "@sentry/utils" "8.4.0" + "@sentry-internal/browser-utils" "8.11.0" + "@sentry-internal/feedback" "8.11.0" + "@sentry-internal/replay" "8.11.0" + "@sentry-internal/replay-canvas" "8.11.0" + "@sentry/core" "8.11.0" + "@sentry/types" "8.11.0" + "@sentry/utils" "8.11.0" "@sentry/cli-darwin@2.31.2": version "2.31.2" @@ -3900,13 +3900,13 @@ "@sentry/types" "7.76.0" "@sentry/utils" "7.76.0" -"@sentry/core@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.4.0.tgz#ab3f7202f3cae82daf4c3c408f50d2c6fb913620" - integrity sha512-0eACPlJvKloFIlcT1c/vjGnvqxLxpGyGuSsU7uonrkmBqIRwLYXWtR4PoHapysKtjPVoHAn9au50ut6ymC2V8Q== +"@sentry/core@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.11.0.tgz#bd78d9b598b6658ad18c8e0457fdffcd5f95b51e" + integrity sha512-rZaM55j5Fw0IGb8lNXOTVoq7WR6JmUzm9x5cURGsjL9gzAurGl817oK3iyOvYQ3JZnfijjh0QF0SQr4NZHKbIg== dependencies: - "@sentry/types" "8.4.0" - "@sentry/utils" "8.4.0" + "@sentry/types" "8.11.0" + "@sentry/utils" "8.11.0" "@sentry/node@^7.69.0": version "7.76.0" @@ -3919,15 +3919,15 @@ "@sentry/utils" "7.76.0" https-proxy-agent "^5.0.0" -"@sentry/react@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry/react/-/react-8.4.0.tgz#95f4fed03709b231770a4f32d3c960c544b0dc3c" - integrity sha512-YnDN+szKFm1fQ9311nAulsRbboeMbqNmosMLA6PweBDEwD0HEJsovQT+ZJxXiOL220qsgWVJzk+aTPtf+oY4wA== +"@sentry/react@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-8.11.0.tgz#324353df35f6e9a28448fabd7501577be2fbcd7f" + integrity sha512-EyPOxDyRwOMPHRCc1/+dlWygXb6+92d0AbVTo4C8ZPT67aMWiczMzZC9qVUN6OqDVrpKwHMYzRyCdsu5OIIWHw== dependencies: - "@sentry/browser" "8.4.0" - "@sentry/core" "8.4.0" - "@sentry/types" "8.4.0" - "@sentry/utils" "8.4.0" + "@sentry/browser" "8.11.0" + "@sentry/core" "8.11.0" + "@sentry/types" "8.11.0" + "@sentry/utils" "8.11.0" hoist-non-react-statics "^3.3.2" "@sentry/types@7.76.0": @@ -3935,10 +3935,10 @@ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.76.0.tgz#628c9899bfa82ea762708314c50fd82f2138587d" integrity sha512-vj6z+EAbVrKAXmJPxSv/clpwS9QjPqzkraMFk2hIdE/kii8s8kwnkBwTSpIrNc8GnzV3qYC4r3qD+BXDxAGPaw== -"@sentry/types@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.4.0.tgz#42500005a198ff8c247490434ed55e0a9f975ad1" - integrity sha512-mHUaaYEQCNukzYsTLp4rP2NNO17vUf+oSGS6qmhrsGqmGNICKw2CIwJlPPGeAkq9Y4tiUOye2m5OT1xsOtxLIw== +"@sentry/types@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.11.0.tgz#613da07f91d58ad5b460b95550e64aa6159ee97f" + integrity sha512-kz9/d2uw7wEXcK8DnCrCuMI75hZnpVAjYr8mq1uatltOx+2JOYPNdaK6ispxXlhb5KXOnVWNgfVDbGlLp0w+Gg== "@sentry/utils@7.76.0": version "7.76.0" @@ -3947,12 +3947,12 @@ dependencies: "@sentry/types" "7.76.0" -"@sentry/utils@8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.4.0.tgz#1b816e65d8dbf055c5e1554361aaf9a8a8a94102" - integrity sha512-oDF0RVWW0AyEnsP1x4McHUvQSAxJgx3G6wM9Sb4wc1F8rwsHnCtGHc+WRZ5Gd2AXC5EGkfbg5919+1ku/L4Dww== +"@sentry/utils@8.11.0": + version "8.11.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.11.0.tgz#b8856db2c1cfc1835df8854cba20cea82f6c6e08" + integrity sha512-iDt5YVMYNgT151bPYVGo8XlpM0MHWy8DH+czmAiAlFTV7ns7lAeHGF6tsFYo7wOZOPDHxtF6F2CM7AvuYnOZGw== dependencies: - "@sentry/types" "8.4.0" + "@sentry/types" "8.11.0" "@sentry/wizard@3.16.3": version "3.16.3" From 247a710eae98e071ac01c959939470d1fc17864c Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Fri, 21 Jun 2024 18:54:12 +0200 Subject: [PATCH 02/12] fix lint --- src/js/vendor/react-native/index.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/js/vendor/react-native/index.ts b/src/js/vendor/react-native/index.ts index d5f8f2db1a..31ef54444a 100644 --- a/src/js/vendor/react-native/index.ts +++ b/src/js/vendor/react-native/index.ts @@ -41,10 +41,12 @@ export type CodeFrame = Readonly<{ }>; // Adapted from https://github.com/facebook/react-native/blob/d09c02f9e2d468e4d0bde51890e312ae7003a3e6/packages/react-native/Libraries/Core/Devtools/symbolicateStackTrace.js#L27 -export type SymbolicatedStackTrace = Readonly<{ - stack: Array; - codeFrame?: CodeFrame; -}> | Array; +export type SymbolicatedStackTrace = + | Readonly<{ + stack: Array; + codeFrame?: CodeFrame; + }> + | Array; // Adapted from https://github.com/facebook/react-native/blob/d09c02f9e2d468e4d0bde51890e312ae7003a3e6/packages/react-native/Libraries/Core/Devtools/getDevServer.js#L17 export type DevServerInfo = { From cf513f5b80bd962cc4ea25792bb708333446f0c1 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Fri, 21 Jun 2024 18:57:17 +0200 Subject: [PATCH 03/12] fix(scope): Use React Native Scope as isolation and global scope to fix native sync --- src/js/mobileScopes.ts | 49 +++++++++++++++++++++++++++++++++++++++ src/js/sdk.tsx | 3 +++ src/js/utils/worldwide.ts | 10 ++++++++ 3 files changed, 62 insertions(+) create mode 100644 src/js/mobileScopes.ts diff --git a/src/js/mobileScopes.ts b/src/js/mobileScopes.ts new file mode 100644 index 0000000000..f883fd4773 --- /dev/null +++ b/src/js/mobileScopes.ts @@ -0,0 +1,49 @@ +import type { Carrier } from '@sentry/core'; +import { getIsolationScope, SDK_VERSION } from '@sentry/core'; + +import { ReactNativeScope } from './scope'; +import type { SentryCarrier } from './utils/worldwide'; +import { RN_GLOBAL_OBJ } from './utils/worldwide'; + +/** + * You should never call this directly. It will be called by the SDK. + * This is need as the native SDKs still have Hubs and Scopes. + * To be able to ensure all expected data are applied to processed events + * global an isolation scope are set to the same scope instance. + * + * @internal + */ +export function setIsolationScopeAsGlobal(): void { + const carrier = getSentryCarrier(RN_GLOBAL_OBJ); + carrier.globalScope = getIsolationScope(); +} + +/** + * You should never call this directly. It will be called by the SDK. + * React Native Scope implementation synchronizes with the native scope. + * + * @internal + */ +export function setReactNativeDefaultIsolationScope(): void { + const carrier = getSentryCarrier(RN_GLOBAL_OBJ); + carrier.defaultIsolationScope = new ReactNativeScope(); +} + +/** + * Will either get the existing sentry carrier, or create a new one. + * + * @tmp This should be exported from @sentry/core + * @internal + */ +export function getSentryCarrier(carrier: Carrier): SentryCarrier { + const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {}); + + // For now: First SDK that sets the .version property wins + // This has to use the version of the JS Core SDK otherwise we are reading + // unused version of the carrier (the carrier is set by the JS Core SDK) + __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 + return (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {}); +} diff --git a/src/js/sdk.tsx b/src/js/sdk.tsx index 07576d1f9e..ce8253afb1 100644 --- a/src/js/sdk.tsx +++ b/src/js/sdk.tsx @@ -10,6 +10,7 @@ import * as React from 'react'; import { ReactNativeClient } from './client'; import { getDefaultIntegrations } from './integrations/default'; +import { setIsolationScopeAsGlobal, setReactNativeDefaultIsolationScope } from './mobileScopes'; import type { ReactNativeClientOptions, ReactNativeOptions, ReactNativeWrapperOptions } from './options'; import { shouldEnableNativeNagger } from './options'; import { TouchEventBoundary } from './touchevents'; @@ -44,6 +45,8 @@ export function init(passedOptions: ReactNativeOptions): void { } useEncodePolyfill(); + setReactNativeDefaultIsolationScope(); + setIsolationScopeAsGlobal(); const maxQueueSize = passedOptions.maxQueueSize // eslint-disable-next-line deprecation/deprecation diff --git a/src/js/utils/worldwide.ts b/src/js/utils/worldwide.ts index 4f1cfc4c7b..da9f9b85e9 100644 --- a/src/js/utils/worldwide.ts +++ b/src/js/utils/worldwide.ts @@ -1,3 +1,4 @@ +import type { Scope } from '@sentry/types'; import type { InternalGlobal } from '@sentry/utils'; import { GLOBAL_OBJ } from '@sentry/utils'; import type { ErrorUtils } from 'react-native/types'; @@ -26,5 +27,14 @@ export interface ReactNativeInternalGlobal extends InternalGlobal { nativePerformanceNow?: () => number; } +/** + * Minimum required Sentry Carrier from JS Core SDK + * https://github.com/getsentry/sentry-javascript/blob/874df8e5b28e2f0f530b0e651067cdd2aa3e5cf7/packages/core/src/carrier.ts#L18 + */ +export type SentryCarrier = { + defaultIsolationScope?: Scope; + globalScope?: Scope; +}; + /** Get's the global object for the current JavaScript runtime */ export const RN_GLOBAL_OBJ = GLOBAL_OBJ as ReactNativeInternalGlobal; From 57a26af707dc0c1b09f5c143dd6754d7de25733c Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Fri, 21 Jun 2024 19:11:13 +0200 Subject: [PATCH 04/12] use getglobalsingleton --- src/js/mobileScopes.ts | 31 ++++--------------------------- src/js/utils/worldwide.ts | 10 ---------- 2 files changed, 4 insertions(+), 37 deletions(-) diff --git a/src/js/mobileScopes.ts b/src/js/mobileScopes.ts index f883fd4773..b5c0fe5ede 100644 --- a/src/js/mobileScopes.ts +++ b/src/js/mobileScopes.ts @@ -1,9 +1,7 @@ -import type { Carrier } from '@sentry/core'; -import { getIsolationScope, SDK_VERSION } from '@sentry/core'; +import { getIsolationScope } from '@sentry/core'; +import { getGlobalSingleton } from '@sentry/utils'; import { ReactNativeScope } from './scope'; -import type { SentryCarrier } from './utils/worldwide'; -import { RN_GLOBAL_OBJ } from './utils/worldwide'; /** * You should never call this directly. It will be called by the SDK. @@ -14,8 +12,7 @@ import { RN_GLOBAL_OBJ } from './utils/worldwide'; * @internal */ export function setIsolationScopeAsGlobal(): void { - const carrier = getSentryCarrier(RN_GLOBAL_OBJ); - carrier.globalScope = getIsolationScope(); + getGlobalSingleton('globalScope', () => getIsolationScope()); } /** @@ -25,25 +22,5 @@ export function setIsolationScopeAsGlobal(): void { * @internal */ export function setReactNativeDefaultIsolationScope(): void { - const carrier = getSentryCarrier(RN_GLOBAL_OBJ); - carrier.defaultIsolationScope = new ReactNativeScope(); -} - -/** - * Will either get the existing sentry carrier, or create a new one. - * - * @tmp This should be exported from @sentry/core - * @internal - */ -export function getSentryCarrier(carrier: Carrier): SentryCarrier { - const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {}); - - // For now: First SDK that sets the .version property wins - // This has to use the version of the JS Core SDK otherwise we are reading - // unused version of the carrier (the carrier is set by the JS Core SDK) - __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 - return (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {}); + getGlobalSingleton('defaultIsolationScope', () => new ReactNativeScope()); } diff --git a/src/js/utils/worldwide.ts b/src/js/utils/worldwide.ts index da9f9b85e9..4f1cfc4c7b 100644 --- a/src/js/utils/worldwide.ts +++ b/src/js/utils/worldwide.ts @@ -1,4 +1,3 @@ -import type { Scope } from '@sentry/types'; import type { InternalGlobal } from '@sentry/utils'; import { GLOBAL_OBJ } from '@sentry/utils'; import type { ErrorUtils } from 'react-native/types'; @@ -27,14 +26,5 @@ export interface ReactNativeInternalGlobal extends InternalGlobal { nativePerformanceNow?: () => number; } -/** - * Minimum required Sentry Carrier from JS Core SDK - * https://github.com/getsentry/sentry-javascript/blob/874df8e5b28e2f0f530b0e651067cdd2aa3e5cf7/packages/core/src/carrier.ts#L18 - */ -export type SentryCarrier = { - defaultIsolationScope?: Scope; - globalScope?: Scope; -}; - /** Get's the global object for the current JavaScript runtime */ export const RN_GLOBAL_OBJ = GLOBAL_OBJ as ReactNativeInternalGlobal; From 8c2f209d568aae4e2999cdacde5a8375ad376374 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Fri, 21 Jun 2024 18:57:17 +0200 Subject: [PATCH 05/12] fix(scope): Use React Native Scope as isolation and global scope to fix native sync --- src/js/mobileScopes.ts | 49 +++++++++++++++++++++++++++++++++++++++ src/js/sdk.tsx | 3 +++ src/js/utils/worldwide.ts | 10 ++++++++ 3 files changed, 62 insertions(+) create mode 100644 src/js/mobileScopes.ts diff --git a/src/js/mobileScopes.ts b/src/js/mobileScopes.ts new file mode 100644 index 0000000000..f883fd4773 --- /dev/null +++ b/src/js/mobileScopes.ts @@ -0,0 +1,49 @@ +import type { Carrier } from '@sentry/core'; +import { getIsolationScope, SDK_VERSION } from '@sentry/core'; + +import { ReactNativeScope } from './scope'; +import type { SentryCarrier } from './utils/worldwide'; +import { RN_GLOBAL_OBJ } from './utils/worldwide'; + +/** + * You should never call this directly. It will be called by the SDK. + * This is need as the native SDKs still have Hubs and Scopes. + * To be able to ensure all expected data are applied to processed events + * global an isolation scope are set to the same scope instance. + * + * @internal + */ +export function setIsolationScopeAsGlobal(): void { + const carrier = getSentryCarrier(RN_GLOBAL_OBJ); + carrier.globalScope = getIsolationScope(); +} + +/** + * You should never call this directly. It will be called by the SDK. + * React Native Scope implementation synchronizes with the native scope. + * + * @internal + */ +export function setReactNativeDefaultIsolationScope(): void { + const carrier = getSentryCarrier(RN_GLOBAL_OBJ); + carrier.defaultIsolationScope = new ReactNativeScope(); +} + +/** + * Will either get the existing sentry carrier, or create a new one. + * + * @tmp This should be exported from @sentry/core + * @internal + */ +export function getSentryCarrier(carrier: Carrier): SentryCarrier { + const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {}); + + // For now: First SDK that sets the .version property wins + // This has to use the version of the JS Core SDK otherwise we are reading + // unused version of the carrier (the carrier is set by the JS Core SDK) + __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 + return (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {}); +} diff --git a/src/js/sdk.tsx b/src/js/sdk.tsx index 07576d1f9e..ce8253afb1 100644 --- a/src/js/sdk.tsx +++ b/src/js/sdk.tsx @@ -10,6 +10,7 @@ import * as React from 'react'; import { ReactNativeClient } from './client'; import { getDefaultIntegrations } from './integrations/default'; +import { setIsolationScopeAsGlobal, setReactNativeDefaultIsolationScope } from './mobileScopes'; import type { ReactNativeClientOptions, ReactNativeOptions, ReactNativeWrapperOptions } from './options'; import { shouldEnableNativeNagger } from './options'; import { TouchEventBoundary } from './touchevents'; @@ -44,6 +45,8 @@ export function init(passedOptions: ReactNativeOptions): void { } useEncodePolyfill(); + setReactNativeDefaultIsolationScope(); + setIsolationScopeAsGlobal(); const maxQueueSize = passedOptions.maxQueueSize // eslint-disable-next-line deprecation/deprecation diff --git a/src/js/utils/worldwide.ts b/src/js/utils/worldwide.ts index 4f1cfc4c7b..da9f9b85e9 100644 --- a/src/js/utils/worldwide.ts +++ b/src/js/utils/worldwide.ts @@ -1,3 +1,4 @@ +import type { Scope } from '@sentry/types'; import type { InternalGlobal } from '@sentry/utils'; import { GLOBAL_OBJ } from '@sentry/utils'; import type { ErrorUtils } from 'react-native/types'; @@ -26,5 +27,14 @@ export interface ReactNativeInternalGlobal extends InternalGlobal { nativePerformanceNow?: () => number; } +/** + * Minimum required Sentry Carrier from JS Core SDK + * https://github.com/getsentry/sentry-javascript/blob/874df8e5b28e2f0f530b0e651067cdd2aa3e5cf7/packages/core/src/carrier.ts#L18 + */ +export type SentryCarrier = { + defaultIsolationScope?: Scope; + globalScope?: Scope; +}; + /** Get's the global object for the current JavaScript runtime */ export const RN_GLOBAL_OBJ = GLOBAL_OBJ as ReactNativeInternalGlobal; From c095f91d5dd09a9c772c932477a24cccf2afa17b Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Fri, 21 Jun 2024 19:11:13 +0200 Subject: [PATCH 06/12] use getglobalsingleton --- src/js/mobileScopes.ts | 31 ++++--------------------------- src/js/utils/worldwide.ts | 10 ---------- 2 files changed, 4 insertions(+), 37 deletions(-) diff --git a/src/js/mobileScopes.ts b/src/js/mobileScopes.ts index f883fd4773..b5c0fe5ede 100644 --- a/src/js/mobileScopes.ts +++ b/src/js/mobileScopes.ts @@ -1,9 +1,7 @@ -import type { Carrier } from '@sentry/core'; -import { getIsolationScope, SDK_VERSION } from '@sentry/core'; +import { getIsolationScope } from '@sentry/core'; +import { getGlobalSingleton } from '@sentry/utils'; import { ReactNativeScope } from './scope'; -import type { SentryCarrier } from './utils/worldwide'; -import { RN_GLOBAL_OBJ } from './utils/worldwide'; /** * You should never call this directly. It will be called by the SDK. @@ -14,8 +12,7 @@ import { RN_GLOBAL_OBJ } from './utils/worldwide'; * @internal */ export function setIsolationScopeAsGlobal(): void { - const carrier = getSentryCarrier(RN_GLOBAL_OBJ); - carrier.globalScope = getIsolationScope(); + getGlobalSingleton('globalScope', () => getIsolationScope()); } /** @@ -25,25 +22,5 @@ export function setIsolationScopeAsGlobal(): void { * @internal */ export function setReactNativeDefaultIsolationScope(): void { - const carrier = getSentryCarrier(RN_GLOBAL_OBJ); - carrier.defaultIsolationScope = new ReactNativeScope(); -} - -/** - * Will either get the existing sentry carrier, or create a new one. - * - * @tmp This should be exported from @sentry/core - * @internal - */ -export function getSentryCarrier(carrier: Carrier): SentryCarrier { - const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {}); - - // For now: First SDK that sets the .version property wins - // This has to use the version of the JS Core SDK otherwise we are reading - // unused version of the carrier (the carrier is set by the JS Core SDK) - __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 - return (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {}); + getGlobalSingleton('defaultIsolationScope', () => new ReactNativeScope()); } diff --git a/src/js/utils/worldwide.ts b/src/js/utils/worldwide.ts index da9f9b85e9..4f1cfc4c7b 100644 --- a/src/js/utils/worldwide.ts +++ b/src/js/utils/worldwide.ts @@ -1,4 +1,3 @@ -import type { Scope } from '@sentry/types'; import type { InternalGlobal } from '@sentry/utils'; import { GLOBAL_OBJ } from '@sentry/utils'; import type { ErrorUtils } from 'react-native/types'; @@ -27,14 +26,5 @@ export interface ReactNativeInternalGlobal extends InternalGlobal { nativePerformanceNow?: () => number; } -/** - * Minimum required Sentry Carrier from JS Core SDK - * https://github.com/getsentry/sentry-javascript/blob/874df8e5b28e2f0f530b0e651067cdd2aa3e5cf7/packages/core/src/carrier.ts#L18 - */ -export type SentryCarrier = { - defaultIsolationScope?: Scope; - globalScope?: Scope; -}; - /** Get's the global object for the current JavaScript runtime */ export const RN_GLOBAL_OBJ = GLOBAL_OBJ as ReactNativeInternalGlobal; From ac3633367b3279188531949fb1a8cc960a3963b6 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Fri, 21 Jun 2024 19:54:39 +0200 Subject: [PATCH 07/12] Revert "use getglobalsingleton" This reverts commit c095f91d5dd09a9c772c932477a24cccf2afa17b. --- src/js/mobileScopes.ts | 31 +++++++++++++++++++++++++++---- src/js/utils/worldwide.ts | 10 ++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/js/mobileScopes.ts b/src/js/mobileScopes.ts index b5c0fe5ede..f883fd4773 100644 --- a/src/js/mobileScopes.ts +++ b/src/js/mobileScopes.ts @@ -1,7 +1,9 @@ -import { getIsolationScope } from '@sentry/core'; -import { getGlobalSingleton } from '@sentry/utils'; +import type { Carrier } from '@sentry/core'; +import { getIsolationScope, SDK_VERSION } from '@sentry/core'; import { ReactNativeScope } from './scope'; +import type { SentryCarrier } from './utils/worldwide'; +import { RN_GLOBAL_OBJ } from './utils/worldwide'; /** * You should never call this directly. It will be called by the SDK. @@ -12,7 +14,8 @@ import { ReactNativeScope } from './scope'; * @internal */ export function setIsolationScopeAsGlobal(): void { - getGlobalSingleton('globalScope', () => getIsolationScope()); + const carrier = getSentryCarrier(RN_GLOBAL_OBJ); + carrier.globalScope = getIsolationScope(); } /** @@ -22,5 +25,25 @@ export function setIsolationScopeAsGlobal(): void { * @internal */ export function setReactNativeDefaultIsolationScope(): void { - getGlobalSingleton('defaultIsolationScope', () => new ReactNativeScope()); + const carrier = getSentryCarrier(RN_GLOBAL_OBJ); + carrier.defaultIsolationScope = new ReactNativeScope(); +} + +/** + * Will either get the existing sentry carrier, or create a new one. + * + * @tmp This should be exported from @sentry/core + * @internal + */ +export function getSentryCarrier(carrier: Carrier): SentryCarrier { + const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {}); + + // For now: First SDK that sets the .version property wins + // This has to use the version of the JS Core SDK otherwise we are reading + // unused version of the carrier (the carrier is set by the JS Core SDK) + __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 + return (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {}); } diff --git a/src/js/utils/worldwide.ts b/src/js/utils/worldwide.ts index 4f1cfc4c7b..da9f9b85e9 100644 --- a/src/js/utils/worldwide.ts +++ b/src/js/utils/worldwide.ts @@ -1,3 +1,4 @@ +import type { Scope } from '@sentry/types'; import type { InternalGlobal } from '@sentry/utils'; import { GLOBAL_OBJ } from '@sentry/utils'; import type { ErrorUtils } from 'react-native/types'; @@ -26,5 +27,14 @@ export interface ReactNativeInternalGlobal extends InternalGlobal { nativePerformanceNow?: () => number; } +/** + * Minimum required Sentry Carrier from JS Core SDK + * https://github.com/getsentry/sentry-javascript/blob/874df8e5b28e2f0f530b0e651067cdd2aa3e5cf7/packages/core/src/carrier.ts#L18 + */ +export type SentryCarrier = { + defaultIsolationScope?: Scope; + globalScope?: Scope; +}; + /** Get's the global object for the current JavaScript runtime */ export const RN_GLOBAL_OBJ = GLOBAL_OBJ as ReactNativeInternalGlobal; From a4324195e667bfb1acd5c1c3b866a0f854fa4319 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Mon, 24 Jun 2024 11:08:17 +0200 Subject: [PATCH 08/12] create scope sync --- src/js/mobileScopes.ts | 49 ---------- src/js/scope.ts | 106 ---------------------- src/js/scopeSync.ts | 78 ++++++++++++++++ src/js/sdk.tsx | 10 +- src/js/utils/fill.ts | 13 +++ test/{scope.test.ts => scopeSync.test.ts} | 21 ++--- 6 files changed, 105 insertions(+), 172 deletions(-) delete mode 100644 src/js/mobileScopes.ts delete mode 100644 src/js/scope.ts create mode 100644 src/js/scopeSync.ts create mode 100644 src/js/utils/fill.ts rename test/{scope.test.ts => scopeSync.test.ts} (83%) diff --git a/src/js/mobileScopes.ts b/src/js/mobileScopes.ts deleted file mode 100644 index f883fd4773..0000000000 --- a/src/js/mobileScopes.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { Carrier } from '@sentry/core'; -import { getIsolationScope, SDK_VERSION } from '@sentry/core'; - -import { ReactNativeScope } from './scope'; -import type { SentryCarrier } from './utils/worldwide'; -import { RN_GLOBAL_OBJ } from './utils/worldwide'; - -/** - * You should never call this directly. It will be called by the SDK. - * This is need as the native SDKs still have Hubs and Scopes. - * To be able to ensure all expected data are applied to processed events - * global an isolation scope are set to the same scope instance. - * - * @internal - */ -export function setIsolationScopeAsGlobal(): void { - const carrier = getSentryCarrier(RN_GLOBAL_OBJ); - carrier.globalScope = getIsolationScope(); -} - -/** - * You should never call this directly. It will be called by the SDK. - * React Native Scope implementation synchronizes with the native scope. - * - * @internal - */ -export function setReactNativeDefaultIsolationScope(): void { - const carrier = getSentryCarrier(RN_GLOBAL_OBJ); - carrier.defaultIsolationScope = new ReactNativeScope(); -} - -/** - * Will either get the existing sentry carrier, or create a new one. - * - * @tmp This should be exported from @sentry/core - * @internal - */ -export function getSentryCarrier(carrier: Carrier): SentryCarrier { - const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {}); - - // For now: First SDK that sets the .version property wins - // This has to use the version of the JS Core SDK otherwise we are reading - // unused version of the carrier (the carrier is set by the JS Core SDK) - __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 - return (__SENTRY__[SDK_VERSION] = __SENTRY__[SDK_VERSION] || {}); -} diff --git a/src/js/scope.ts b/src/js/scope.ts deleted file mode 100644 index bf8454b52f..0000000000 --- a/src/js/scope.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { Scope } from '@sentry/core'; -import type { Attachment, Breadcrumb, User } from '@sentry/types'; - -import { DEFAULT_BREADCRUMB_LEVEL } from './breadcrumb'; -import { convertToNormalizedObject } from './utils/normalize'; -import { NATIVE } from './wrapper'; - -/** - * Extends the scope methods to set scope on the Native SDKs - */ -export class ReactNativeScope extends Scope { - /** - * @inheritDoc - */ - public setUser(user: User | null): this { - NATIVE.setUser(user); - return super.setUser(user); - } - - /** - * @inheritDoc - */ - public setTag(key: string, value: string): this { - NATIVE.setTag(key, value); - return super.setTag(key, value); - } - - /** - * @inheritDoc - */ - public setTags(tags: { [key: string]: string }): this { - // As native only has setTag, we just loop through each tag key. - Object.keys(tags).forEach(key => { - NATIVE.setTag(key, tags[key]); - }); - return super.setTags(tags); - } - - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public setExtras(extras: { [key: string]: any }): this { - Object.keys(extras).forEach(key => { - NATIVE.setExtra(key, extras[key]); - }); - return super.setExtras(extras); - } - - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types,@typescript-eslint/no-explicit-any - public setExtra(key: string, extra: any): this { - NATIVE.setExtra(key, extra); - return super.setExtra(key, extra); - } - - /** - * @inheritDoc - */ - public addBreadcrumb(breadcrumb: Breadcrumb, maxBreadcrumbs?: number): this { - const mergedBreadcrumb: Breadcrumb = { - ...breadcrumb, - level: breadcrumb.level || DEFAULT_BREADCRUMB_LEVEL, - data: breadcrumb.data ? convertToNormalizedObject(breadcrumb.data) : undefined, - }; - - super.addBreadcrumb(mergedBreadcrumb, maxBreadcrumbs); - - const finalBreadcrumb = this._breadcrumbs[this._breadcrumbs.length - 1]; - NATIVE.addBreadcrumb(finalBreadcrumb); - return this; - } - - /** - * @inheritDoc - */ - public clearBreadcrumbs(): this { - NATIVE.clearBreadcrumbs(); - return super.clearBreadcrumbs(); - } - - /** - * @inheritDoc - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public setContext(key: string, context: { [key: string]: any } | null): this { - NATIVE.setContext(key, context); - return super.setContext(key, context); - } - - /** - * @inheritDoc - */ - public addAttachment(attachment: Attachment): this { - return super.addAttachment(attachment); - } - - /** - * @inheritDoc - */ - public clearAttachments(): this { - return super.clearAttachments(); - } -} diff --git a/src/js/scopeSync.ts b/src/js/scopeSync.ts new file mode 100644 index 0000000000..770d7c9d43 --- /dev/null +++ b/src/js/scopeSync.ts @@ -0,0 +1,78 @@ +import type { Breadcrumb, Scope } from '@sentry/types'; + +import { DEFAULT_BREADCRUMB_LEVEL } from './breadcrumb'; +import { fillTyped } from './utils/fill'; +import { convertToNormalizedObject } from './utils/normalize'; +import { NATIVE } from './wrapper'; + +/** + * This WeakMap is used to keep track of which scopes have been synced to the native SDKs. + * This ensures that we don't double sync the same scope. + */ +const syncedToNativeMap = new WeakMap(); + +/** + * Hooks into the scope set methods and sync new data added to the given scope with the native SDKs. + */ +export function enableSyncToNative(scope: Scope): void { + if (syncedToNativeMap.has(scope)) { + return; + } + syncedToNativeMap.set(scope, true); + + fillTyped(scope, 'setUser', original => (user): Scope => { + NATIVE.setUser(user); + return original.call(scope, user); + }); + + fillTyped(scope, 'setTag', original => (key, value): Scope => { + NATIVE.setTag(key, value as string); + return original.call(scope, key, value); + }); + + fillTyped(scope, 'setTags', original => (tags): Scope => { + // As native only has setTag, we just loop through each tag key. + Object.keys(tags).forEach(key => { + NATIVE.setTag(key, tags[key] as string); + }); + return original.call(scope, tags); + }); + + fillTyped(scope, 'setExtras', original => (extras): Scope => { + Object.keys(extras).forEach(key => { + NATIVE.setExtra(key, extras[key]); + }); + return original.call(scope, extras); + }); + + fillTyped(scope, 'setExtra', original => (key, value): Scope => { + NATIVE.setExtra(key, value); + return original.call(scope, key, value); + }); + + fillTyped(scope, 'addBreadcrumb', original => (breadcrumb, maxBreadcrumbs): Scope => { + const mergedBreadcrumb: Breadcrumb = { + ...breadcrumb, + level: breadcrumb.level || DEFAULT_BREADCRUMB_LEVEL, + data: breadcrumb.data ? convertToNormalizedObject(breadcrumb.data) : undefined, + }; + + original.call(scope, mergedBreadcrumb, maxBreadcrumbs); + + const finalBreadcrumb = scope.getLastBreadcrumb(); + NATIVE.addBreadcrumb(finalBreadcrumb); + + return scope; + }); + + fillTyped(scope, 'clearBreadcrumbs', original => (): Scope => { + NATIVE.clearBreadcrumbs(); + return original.call(scope); + }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fillTyped(scope, 'setContext', original => (key: string, context: { [key: string]: any } | null): Scope => { + NATIVE.setContext(key, context); + return original.call(scope, key, context); + }); +} diff --git a/src/js/sdk.tsx b/src/js/sdk.tsx index ce8253afb1..ca6600984a 100644 --- a/src/js/sdk.tsx +++ b/src/js/sdk.tsx @@ -1,18 +1,18 @@ /* eslint-disable complexity */ -import { getClient, getIntegrationsToSetup, initAndBind, withScope as coreWithScope } from '@sentry/core'; +import { getClient, getGlobalScope,getIntegrationsToSetup, getIsolationScope,initAndBind, withScope as coreWithScope } from '@sentry/core'; import { defaultStackParser, makeFetchTransport, } from '@sentry/react'; -import type { Integration, Scope, UserFeedback } from '@sentry/types'; +import type { Integration, Scope,UserFeedback } from '@sentry/types'; import { logger, stackParserFromStackParserOptions } from '@sentry/utils'; import * as React from 'react'; import { ReactNativeClient } from './client'; import { getDefaultIntegrations } from './integrations/default'; -import { setIsolationScopeAsGlobal, setReactNativeDefaultIsolationScope } from './mobileScopes'; import type { ReactNativeClientOptions, ReactNativeOptions, ReactNativeWrapperOptions } from './options'; import { shouldEnableNativeNagger } from './options'; +import { enableSyncToNative } from './scopeSync'; import { TouchEventBoundary } from './touchevents'; import type { ReactNativeTracing } from './tracing'; import { ReactNativeProfiler } from './tracing'; @@ -45,8 +45,8 @@ export function init(passedOptions: ReactNativeOptions): void { } useEncodePolyfill(); - setReactNativeDefaultIsolationScope(); - setIsolationScopeAsGlobal(); + enableSyncToNative(getGlobalScope()); + enableSyncToNative(getIsolationScope()); const maxQueueSize = passedOptions.maxQueueSize // eslint-disable-next-line deprecation/deprecation diff --git a/src/js/utils/fill.ts b/src/js/utils/fill.ts new file mode 100644 index 0000000000..25e034ba9e --- /dev/null +++ b/src/js/utils/fill.ts @@ -0,0 +1,13 @@ +import { fill } from '@sentry/utils'; + +/** + * The same as `import { fill } from '@sentry/utils';` but with explicit types. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function fillTyped( + source: Source, + name: Name, + replacement: (original: Source[Name]) => Source[Name], +): void { + fill(source, name, replacement); +} diff --git a/test/scope.test.ts b/test/scopeSync.test.ts similarity index 83% rename from test/scope.test.ts rename to test/scopeSync.test.ts index cccf772076..2861ade57d 100644 --- a/test/scope.test.ts +++ b/test/scopeSync.test.ts @@ -1,22 +1,19 @@ +import { Scope } from '@sentry/core'; import type { Breadcrumb } from '@sentry/types'; -import { ReactNativeScope } from '../src/js/scope'; +import { enableSyncToNative } from '../src/js/scopeSync'; import { NATIVE } from '../src/js/wrapper'; jest.mock('../src/js/wrapper'); -type TestScope = ReactNativeScope & { _breadcrumbs: Breadcrumb[] }; -const createScope = (): TestScope => { - return new ReactNativeScope() as TestScope; -}; - -describe('Scope', () => { +describe('ScopeSync', () => { describe('addBreadcrumb', () => { - let scope: TestScope; + let scope: Scope; let nativeAddBreadcrumbMock: jest.Mock; beforeEach(() => { - scope = createScope(); + scope = new Scope(); + enableSyncToNative(scope); nativeAddBreadcrumbMock = (NATIVE.addBreadcrumb as jest.Mock).mockImplementationOnce(() => { return; }); @@ -32,13 +29,13 @@ describe('Scope', () => { timestamp: 1234, }; scope.addBreadcrumb(breadcrumb); - expect(scope._breadcrumbs).toEqual([ + expect(scope.getLastBreadcrumb()).toEqual( { message: 'test', timestamp: 1234, level: 'info', }, - ]); + ); }); it('adds timestamp to breadcrumb without timestamp', () => { @@ -46,7 +43,7 @@ describe('Scope', () => { message: 'test', }; scope.addBreadcrumb(breadcrumb); - expect(scope._breadcrumbs).toEqual([expect.objectContaining({ timestamp: expect.any(Number) })]); + expect(scope.getLastBreadcrumb()).toEqual(expect.objectContaining({ timestamp: expect.any(Number) })); }); it('passes breadcrumb with timestamp to native', () => { From 1afd265a5abc1b4de210299dd428ca7bb0838256 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Mon, 24 Jun 2024 11:34:26 +0200 Subject: [PATCH 09/12] add more tests --- test/scopeSync.test.ts | 187 ++++++++++++++++++++++++++--------------- 1 file changed, 120 insertions(+), 67 deletions(-) diff --git a/test/scopeSync.test.ts b/test/scopeSync.test.ts index 2861ade57d..390b68bca7 100644 --- a/test/scopeSync.test.ts +++ b/test/scopeSync.test.ts @@ -1,99 +1,152 @@ +jest.mock('../src/js/wrapper', () => jest.requireActual('./mockWrapper')); +import * as SentryCore from '@sentry/core'; import { Scope } from '@sentry/core'; import type { Breadcrumb } from '@sentry/types'; import { enableSyncToNative } from '../src/js/scopeSync'; -import { NATIVE } from '../src/js/wrapper'; +import { getDefaultTestClientOptions, TestClient } from './mocks/client'; +import { NATIVE } from './mockWrapper'; jest.mock('../src/js/wrapper'); describe('ScopeSync', () => { - describe('addBreadcrumb', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('scope apis', () => { let scope: Scope; - let nativeAddBreadcrumbMock: jest.Mock; beforeEach(() => { scope = new Scope(); enableSyncToNative(scope); - nativeAddBreadcrumbMock = (NATIVE.addBreadcrumb as jest.Mock).mockImplementationOnce(() => { - return; - }); }); - afterEach(() => { - jest.resetAllMocks(); - }); + describe('addBreadcrumb', () => { + it('it only syncs once per scope', () => { + enableSyncToNative(scope); + enableSyncToNative(scope); - it('adds default level if no level specified', () => { - const breadcrumb = { - message: 'test', - timestamp: 1234, - }; - scope.addBreadcrumb(breadcrumb); - expect(scope.getLastBreadcrumb()).toEqual( - { + scope.addBreadcrumb({ message: 'test' }); + + expect(NATIVE.addBreadcrumb).toBeCalledTimes(1); + }); + + it('adds default level if no level specified', () => { + const breadcrumb = { + message: 'test', + timestamp: 1234, + }; + scope.addBreadcrumb(breadcrumb); + expect(scope.getLastBreadcrumb()).toEqual({ message: 'test', timestamp: 1234, level: 'info', - }, - ); + }); + }); + + it('adds timestamp to breadcrumb without timestamp', () => { + const breadcrumb = { + message: 'test', + }; + scope.addBreadcrumb(breadcrumb); + expect(scope.getLastBreadcrumb()).toEqual( + expect.objectContaining({ timestamp: expect.any(Number) }), + ); + }); + + it('passes breadcrumb with timestamp to native', () => { + const breadcrumb = { + message: 'test', + }; + scope.addBreadcrumb(breadcrumb); + expect(NATIVE.addBreadcrumb).toBeCalledWith( + expect.objectContaining({ + timestamp: expect.any(Number), + }), + ); + }); + + test('undefined breadcrumb data is not normalized when passing to the native layer', () => { + const breadcrumb: Breadcrumb = { + data: undefined, + }; + scope.addBreadcrumb(breadcrumb); + expect(NATIVE.addBreadcrumb).toBeCalledWith( + expect.objectContaining({ + data: undefined, + }), + ); + }); + + test('object is normalized when passing to the native layer', () => { + const breadcrumb: Breadcrumb = { + data: { + foo: NaN, + }, + }; + scope.addBreadcrumb(breadcrumb); + expect(NATIVE.addBreadcrumb).toBeCalledWith( + expect.objectContaining({ + data: { foo: '[NaN]' }, + }), + ); + }); + + test('not object data is converted to object', () => { + const breadcrumb: Breadcrumb = { + data: 'foo' as unknown as object, + }; + scope.addBreadcrumb(breadcrumb); + expect(NATIVE.addBreadcrumb).toBeCalledWith( + expect.objectContaining({ + data: { value: 'foo' }, + }), + ); + }); + }); + }); + + describe('static apis', () => { + beforeEach(() => { + SentryCore.setCurrentClient(new TestClient(getDefaultTestClientOptions())); + enableSyncToNative(SentryCore.getIsolationScope()); }); - it('adds timestamp to breadcrumb without timestamp', () => { - const breadcrumb = { - message: 'test', - }; - scope.addBreadcrumb(breadcrumb); - expect(scope.getLastBreadcrumb()).toEqual(expect.objectContaining({ timestamp: expect.any(Number) })); + it('setUser', () => { + const user = { id: '123' }; + SentryCore.setUser(user); + expect(NATIVE.setUser).toHaveBeenCalledExactlyOnceWith({ id: '123' }); }); - it('passes breadcrumb with timestamp to native', () => { - const breadcrumb = { - message: 'test', - }; - scope.addBreadcrumb(breadcrumb); - expect(nativeAddBreadcrumbMock).toBeCalledWith( - expect.objectContaining({ - timestamp: expect.any(Number), - }), - ); + it('setTag', () => { + SentryCore.setTag('key', 'value'); + expect(NATIVE.setTag).toHaveBeenCalledExactlyOnceWith('key', 'value'); }); - test('undefined breadcrumb data is not normalized when passing to the native layer', () => { - const breadcrumb: Breadcrumb = { - data: undefined, - }; - scope.addBreadcrumb(breadcrumb); - expect(nativeAddBreadcrumbMock).toBeCalledWith( - expect.objectContaining({ - data: undefined, - }), - ); + it('setTags', () => { + SentryCore.setTags({ key: 'value' }); + expect(NATIVE.setTag).toHaveBeenCalledExactlyOnceWith('key', 'value'); + }); + + it('setExtra', () => { + SentryCore.setExtra('key', 'value'); + expect(NATIVE.setExtra).toHaveBeenCalledExactlyOnceWith('key', 'value'); + }); + + it('setExtras', () => { + SentryCore.setExtras({ key: 'value' }); + expect(NATIVE.setExtra).toHaveBeenCalledExactlyOnceWith('key', 'value'); }); - test('object is normalized when passing to the native layer', () => { - const breadcrumb: Breadcrumb = { - data: { - foo: NaN, - }, - }; - scope.addBreadcrumb(breadcrumb); - expect(nativeAddBreadcrumbMock).toBeCalledWith( - expect.objectContaining({ - data: { foo: '[NaN]' }, - }), - ); + it('addBreadcrumb', () => { + SentryCore.addBreadcrumb({ message: 'test' }); + expect(NATIVE.addBreadcrumb).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({ message: 'test' })); }); - test('not object data is converted to object', () => { - const breadcrumb: Breadcrumb = { - data: 'foo' as unknown as object, - }; - scope.addBreadcrumb(breadcrumb); - expect(nativeAddBreadcrumbMock).toBeCalledWith( - expect.objectContaining({ - data: { value: 'foo' }, - }), - ); + it('setContext', () => { + SentryCore.setContext('key', { key: 'value' }); + expect(NATIVE.setContext).toHaveBeenCalledExactlyOnceWith('key', { key: 'value' }); }); }); }); From 1e81d08ac5d730022b3e81f7aecd40704c765b4c Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Mon, 24 Jun 2024 11:34:50 +0200 Subject: [PATCH 10/12] fix metro serializer build --- src/js/tools/sentryMetroSerializer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/tools/sentryMetroSerializer.ts b/src/js/tools/sentryMetroSerializer.ts index 05ab40d1ac..62c47be247 100644 --- a/src/js/tools/sentryMetroSerializer.ts +++ b/src/js/tools/sentryMetroSerializer.ts @@ -130,7 +130,7 @@ function addDebugIdModule( modifiedPreModules[0].path === PRELUDE_MODULE_PATH ) { // prelude module must be first as it measures the bundle startup time - modifiedPreModules.unshift(preModules[0]); + modifiedPreModules.unshift(preModules[0] as Module); modifiedPreModules[1] = debugIdModule; } else { modifiedPreModules.unshift(debugIdModule); From 6dcef1b3546a0e06a1cccc9b4060e32ffbd5a4b5 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Mon, 24 Jun 2024 18:33:10 +0200 Subject: [PATCH 11/12] add original check to the tests --- src/js/sdk.tsx | 11 ++++++--- test/scopeSync.test.ts | 55 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/js/sdk.tsx b/src/js/sdk.tsx index ca6600984a..776fe00bff 100644 --- a/src/js/sdk.tsx +++ b/src/js/sdk.tsx @@ -44,10 +44,6 @@ export function init(passedOptions: ReactNativeOptions): void { return; } - useEncodePolyfill(); - enableSyncToNative(getGlobalScope()); - enableSyncToNative(getIsolationScope()); - const maxQueueSize = passedOptions.maxQueueSize // eslint-disable-next-line deprecation/deprecation ?? passedOptions.transportOptions?.bufferSize @@ -56,6 +52,13 @@ export function init(passedOptions: ReactNativeOptions): void { const enableNative = passedOptions.enableNative === undefined || passedOptions.enableNative ? NATIVE.isNativeAvailable() : false; + + useEncodePolyfill(); + if (enableNative) { + enableSyncToNative(getGlobalScope()); + enableSyncToNative(getIsolationScope()); + } + const options: ReactNativeClientOptions = { ...DEFAULT_OPTIONS, ...passedOptions, diff --git a/test/scopeSync.test.ts b/test/scopeSync.test.ts index 390b68bca7..e6f7e161fe 100644 --- a/test/scopeSync.test.ts +++ b/test/scopeSync.test.ts @@ -108,45 +108,92 @@ describe('ScopeSync', () => { }); describe('static apis', () => { + let setUserScopeSpy: jest.SpyInstance; + let setTagScopeSpy: jest.SpyInstance; + let setTagsScopeSpy: jest.SpyInstance; + let setExtraScopeSpy: jest.SpyInstance; + let setExtrasScopeSpy: jest.SpyInstance; + let addBreadcrumbScopeSpy: jest.SpyInstance; + let setContextScopeSpy: jest.SpyInstance; + + beforeAll(() => { + const testScope = SentryCore.getIsolationScope(); + setUserScopeSpy = jest.spyOn(testScope, 'setUser'); + setTagScopeSpy = jest.spyOn(testScope, 'setTag'); + setTagsScopeSpy = jest.spyOn(testScope, 'setTags'); + setExtraScopeSpy = jest.spyOn(testScope, 'setExtra'); + setExtrasScopeSpy = jest.spyOn(testScope, 'setExtras'); + addBreadcrumbScopeSpy = jest.spyOn(testScope, 'addBreadcrumb'); + setContextScopeSpy = jest.spyOn(testScope, 'setContext'); + }); + beforeEach(() => { SentryCore.setCurrentClient(new TestClient(getDefaultTestClientOptions())); enableSyncToNative(SentryCore.getIsolationScope()); }); it('setUser', () => { + expect(SentryCore.getIsolationScope().setUser).not.toBe(setUserScopeSpy); + const user = { id: '123' }; SentryCore.setUser(user); expect(NATIVE.setUser).toHaveBeenCalledExactlyOnceWith({ id: '123' }); + expect(setUserScopeSpy).toHaveBeenCalledExactlyOnceWith({ id: '123' }); }); it('setTag', () => { + expect(SentryCore.getIsolationScope().setTag).not.toBe(setTagScopeSpy); + SentryCore.setTag('key', 'value'); expect(NATIVE.setTag).toHaveBeenCalledExactlyOnceWith('key', 'value'); + expect(setTagScopeSpy).toHaveBeenCalledExactlyOnceWith('key', 'value'); }); it('setTags', () => { - SentryCore.setTags({ key: 'value' }); - expect(NATIVE.setTag).toHaveBeenCalledExactlyOnceWith('key', 'value'); + expect(SentryCore.getIsolationScope().setTags).not.toBe(setTagsScopeSpy); + + SentryCore.setTags({ key: 'value', second: 'bar' }); + expect(NATIVE.setTag).toBeCalledTimes(2); + expect(NATIVE.setTag).toHaveBeenCalledWith('key', 'value'); + expect(NATIVE.setTag).toHaveBeenCalledWith('second', 'bar'); + expect(setTagsScopeSpy).toHaveBeenCalledExactlyOnceWith({ key: 'value', second: 'bar' }); }); it('setExtra', () => { + expect(SentryCore.getIsolationScope().setExtra).not.toBe(setExtraScopeSpy); + SentryCore.setExtra('key', 'value'); expect(NATIVE.setExtra).toHaveBeenCalledExactlyOnceWith('key', 'value'); + expect(setExtraScopeSpy).toHaveBeenCalledExactlyOnceWith('key', 'value'); }); it('setExtras', () => { - SentryCore.setExtras({ key: 'value' }); - expect(NATIVE.setExtra).toHaveBeenCalledExactlyOnceWith('key', 'value'); + expect(SentryCore.getIsolationScope().setExtras).not.toBe(setExtrasScopeSpy); + + SentryCore.setExtras({ key: 'value', second: 'bar' }); + expect(NATIVE.setExtra).toBeCalledTimes(2); + expect(NATIVE.setExtra).toHaveBeenCalledWith('key', 'value'); + expect(NATIVE.setExtra).toHaveBeenCalledWith('second', 'bar'); + expect(setExtrasScopeSpy).toHaveBeenCalledExactlyOnceWith({ key: 'value', second: 'bar' }); }); it('addBreadcrumb', () => { + expect(SentryCore.getIsolationScope().addBreadcrumb).not.toBe(addBreadcrumbScopeSpy); + SentryCore.addBreadcrumb({ message: 'test' }); expect(NATIVE.addBreadcrumb).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({ message: 'test' })); + expect(addBreadcrumbScopeSpy).toHaveBeenCalledExactlyOnceWith( + expect.objectContaining({ message: 'test' }), + expect.any(Number), + ); }); it('setContext', () => { + expect(SentryCore.getIsolationScope().setContext).not.toBe(setContextScopeSpy); + SentryCore.setContext('key', { key: 'value' }); expect(NATIVE.setContext).toHaveBeenCalledExactlyOnceWith('key', { key: 'value' }); + expect(setContextScopeSpy).toHaveBeenCalledExactlyOnceWith('key', { key: 'value' }); }); }); }); From d5af7373a4dd4d8b5f81cfb8440a3b76152787cb Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Mon, 24 Jun 2024 18:34:01 +0200 Subject: [PATCH 12/12] remove carrier --- src/js/utils/worldwide.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/js/utils/worldwide.ts b/src/js/utils/worldwide.ts index da9f9b85e9..4f1cfc4c7b 100644 --- a/src/js/utils/worldwide.ts +++ b/src/js/utils/worldwide.ts @@ -1,4 +1,3 @@ -import type { Scope } from '@sentry/types'; import type { InternalGlobal } from '@sentry/utils'; import { GLOBAL_OBJ } from '@sentry/utils'; import type { ErrorUtils } from 'react-native/types'; @@ -27,14 +26,5 @@ export interface ReactNativeInternalGlobal extends InternalGlobal { nativePerformanceNow?: () => number; } -/** - * Minimum required Sentry Carrier from JS Core SDK - * https://github.com/getsentry/sentry-javascript/blob/874df8e5b28e2f0f530b0e651067cdd2aa3e5cf7/packages/core/src/carrier.ts#L18 - */ -export type SentryCarrier = { - defaultIsolationScope?: Scope; - globalScope?: Scope; -}; - /** Get's the global object for the current JavaScript runtime */ export const RN_GLOBAL_OBJ = GLOBAL_OBJ as ReactNativeInternalGlobal;