@sentry/react-native 5.19.2 → 5.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +63 -1
  2. package/README.md +1 -1
  3. package/RNSentry.podspec +2 -2
  4. package/android/build.gradle +1 -1
  5. package/android/src/main/java/io/sentry/react/{MapConverter.java → RNSentryMapConverter.java} +1 -1
  6. package/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +55 -10
  7. package/android/src/main/java/io/sentry/react/RNSentryOnDrawReporterManager.java +139 -0
  8. package/android/src/main/java/io/sentry/react/RNSentryPackage.java +13 -0
  9. package/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java +99 -0
  10. package/android/src/newarch/java/io/sentry/react/RNSentryModule.java +15 -1
  11. package/android/src/oldarch/java/io/sentry/react/RNSentryModule.java +15 -0
  12. package/dist/js/NativeRNSentry.d.ts +3 -0
  13. package/dist/js/NativeRNSentry.d.ts.map +1 -1
  14. package/dist/js/NativeRNSentry.js.map +1 -1
  15. package/dist/js/index.d.ts +2 -2
  16. package/dist/js/index.d.ts.map +1 -1
  17. package/dist/js/index.js +1 -1
  18. package/dist/js/index.js.map +1 -1
  19. package/dist/js/tools/utils.js +1 -1
  20. package/dist/js/tools/utils.js.map +1 -1
  21. package/dist/js/tracing/index.d.ts +1 -0
  22. package/dist/js/tracing/index.d.ts.map +1 -1
  23. package/dist/js/tracing/index.js +1 -0
  24. package/dist/js/tracing/index.js.map +1 -1
  25. package/dist/js/tracing/reactnativeprofiler.d.ts +4 -0
  26. package/dist/js/tracing/reactnativeprofiler.d.ts.map +1 -1
  27. package/dist/js/tracing/reactnativeprofiler.js +12 -0
  28. package/dist/js/tracing/reactnativeprofiler.js.map +1 -1
  29. package/dist/js/tracing/reactnativetracing.d.ts.map +1 -1
  30. package/dist/js/tracing/reactnativetracing.js +15 -2
  31. package/dist/js/tracing/reactnativetracing.js.map +1 -1
  32. package/dist/js/tracing/reactnavigation.d.ts +10 -1
  33. package/dist/js/tracing/reactnavigation.d.ts.map +1 -1
  34. package/dist/js/tracing/reactnavigation.js +69 -4
  35. package/dist/js/tracing/reactnavigation.js.map +1 -1
  36. package/dist/js/tracing/timetodisplay.d.ts +46 -0
  37. package/dist/js/tracing/timetodisplay.d.ts.map +1 -0
  38. package/dist/js/tracing/timetodisplay.js +198 -0
  39. package/dist/js/tracing/timetodisplay.js.map +1 -0
  40. package/dist/js/tracing/timetodisplaynative.d.ts +17 -0
  41. package/dist/js/tracing/timetodisplaynative.d.ts.map +1 -0
  42. package/dist/js/tracing/timetodisplaynative.js +27 -0
  43. package/dist/js/tracing/timetodisplaynative.js.map +1 -0
  44. package/dist/js/tracing/timetodisplaynative.types.d.ts +14 -0
  45. package/dist/js/tracing/timetodisplaynative.types.d.ts.map +1 -0
  46. package/dist/js/tracing/timetodisplaynative.types.js +2 -0
  47. package/dist/js/tracing/timetodisplaynative.types.js.map +1 -0
  48. package/dist/js/tracing/utils.d.ts +8 -3
  49. package/dist/js/tracing/utils.d.ts.map +1 -1
  50. package/dist/js/tracing/utils.js +19 -0
  51. package/dist/js/tracing/utils.js.map +1 -1
  52. package/dist/js/utils/sentryeventemitter.d.ts +24 -0
  53. package/dist/js/utils/sentryeventemitter.d.ts.map +1 -0
  54. package/dist/js/utils/sentryeventemitter.js +82 -0
  55. package/dist/js/utils/sentryeventemitter.js.map +1 -0
  56. package/dist/js/version.d.ts +1 -1
  57. package/dist/js/version.js +1 -1
  58. package/dist/js/version.js.map +1 -1
  59. package/dist/js/wrapper.d.ts +5 -0
  60. package/dist/js/wrapper.d.ts.map +1 -1
  61. package/dist/js/wrapper.js +20 -3
  62. package/dist/js/wrapper.js.map +1 -1
  63. package/ios/RNSentry.h +2 -1
  64. package/ios/RNSentry.mm +44 -0
  65. package/ios/RNSentryDependencyContainer.h +15 -0
  66. package/ios/RNSentryDependencyContainer.m +32 -0
  67. package/ios/RNSentryEvents.h +3 -0
  68. package/ios/RNSentryEvents.m +3 -0
  69. package/ios/RNSentryFramesTrackerListener.h +17 -0
  70. package/ios/RNSentryFramesTrackerListener.m +30 -0
  71. package/ios/RNSentryOnDrawReporter.m +70 -0
  72. package/ios/RNSentryRNSScreen.h +7 -0
  73. package/ios/RNSentryRNSScreen.m +26 -0
  74. package/package.json +9 -6
  75. package/scripts/expo-upload-sourcemaps.js +15 -1
  76. package/src/js/NativeRNSentry.ts +3 -0
  77. package/ts3.8/dist/js/NativeRNSentry.d.ts +3 -0
  78. package/ts3.8/dist/js/index.d.ts +2 -2
  79. package/ts3.8/dist/js/tracing/index.d.ts +1 -0
  80. package/ts3.8/dist/js/tracing/reactnativeprofiler.d.ts +4 -0
  81. package/ts3.8/dist/js/tracing/reactnavigation.d.ts +10 -1
  82. package/ts3.8/dist/js/tracing/timetodisplay.d.ts +46 -0
  83. package/ts3.8/dist/js/tracing/timetodisplaynative.d.ts +17 -0
  84. package/ts3.8/dist/js/tracing/timetodisplaynative.types.d.ts +14 -0
  85. package/ts3.8/dist/js/tracing/utils.d.ts +8 -3
  86. package/ts3.8/dist/js/utils/sentryeventemitter.d.ts +24 -0
  87. package/ts3.8/dist/js/version.d.ts +1 -1
  88. package/ts3.8/dist/js/wrapper.d.ts +5 -0
@@ -1 +1 @@
1
- {"version":3,"file":"reactnavigation.js","sourceRoot":"","sources":["../../../src/js/tracing/reactnavigation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAE1E,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AAyBxG,MAAM,cAAc,GAA2B;IAC7C,oBAAoB,EAAE,IAAI;CAC3B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,OAAO,8BAA+B,SAAQ,8BAA8B;IAiBhF,YAAmB,UAA2C,EAAE;QAC9D,KAAK,EAAE,CAAC;QAfM,SAAI,GAAW,8BAA8B,CAAC,mBAAmB,CAAC;QAE1E,yBAAoB,GAA+B,IAAI,CAAC;QAE/C,uBAAkB,GAAW,GAAG,CAAC;QAI1C,yBAAoB,GAAY,KAAK,CAAC;QAEtC,qBAAgB,GAAa,EAAE,CAAC;QAsNxC,sGAAsG;QAC9F,wBAAmB,GAAG,CAAC,GAAW,EAAQ,EAAE;YAClD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAEhC,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE;gBAC1D,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC;aAC7G;QACH,CAAC,CAAC;QAtNA,IAAI,CAAC,QAAQ,mCACR,cAAc,GACd,OAAO,CACX,CAAC;IACJ,CAAC;IAED;;OAEG;IACI,8BAA8B,CACnC,QAA4B,EAC5B,cAA8B,EAC9B,cAA8B;QAE9B,KAAK,CAAC,8BAA8B,CAAC,QAAQ,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;QAE/E,sGAAsG;QACtG,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE;YAC9B,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,IAAI,CAAC,oBAAoB,EAAE;gBAC7B,0EAA0E;gBAC1E,IAAI,CAAC,cAAc,EAAE,CAAC;gBAEtB,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;aAClC;SACF;IACH,CAAC;IAED;;;OAGG;IACH,iHAAiH;IAC1G,2BAA2B,CAAC,sBAA2B;QAC5D;;;;WAIG;QACH,IAAI,CAAC,aAAa,CAAC,yBAAyB,EAAE;YAC5C,IAAI,SAAS,IAAI,sBAAsB,EAAE;gBACvC,sEAAsE;gBACtE,IAAI,CAAC,oBAAoB,GAAG,sBAAsB,CAAC,OAAO,CAAC;aAC5D;iBAAM;gBACL,IAAI,CAAC,oBAAoB,GAAG,sBAAsB,CAAC;aACpD;YAED,IAAI,IAAI,CAAC,oBAAoB,EAAE;gBAC7B,IAAI,CAAC,oBAAoB,CAAC,WAAW,CACnC,mBAAmB,EAAE,2CAA2C;gBAChE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAC5B,CAAC;gBACF,IAAI,CAAC,oBAAoB,CAAC,WAAW,CACnC,OAAO,EAAE,+CAA+C;gBACxD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAC/B,CAAC;gBAEF,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE;oBAC9B,IAAI,IAAI,CAAC,kBAAkB,EAAE;wBAC3B,yFAAyF;wBACzF,IAAI,CAAC,cAAc,EAAE,CAAC;wBAEtB,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;qBAClC;yBAAM;wBACL,MAAM,CAAC,GAAG,CACR,2GAA2G,CAC5G,CAAC;qBACH;iBACF;gBAED,aAAa,CAAC,yBAAyB,GAAG,IAAI,CAAC;aAChD;iBAAM;gBACL,MAAM,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;aAC5F;SACF;aAAM;YACL,MAAM,CAAC,GAAG,CACR,qHAAqH,CACtH,CAAC;SACH;IACH,CAAC;IAED;;;;OAIG;IACK,WAAW;QACjB,IAAI,IAAI,CAAC,kBAAkB,EAAE;YAC3B,MAAM,CAAC,GAAG,CACR,uGAAuG,CACxG,CAAC;YACF,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACjC,IAAI,CAAC,wBAAwB,EAAE,CAAC;SACjC;QAED,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,CAC9C,0BAA0B,CAAC,8BAA8B,CAAC,mBAAmB,CAAC,CAC/E,CAAC;QAEF,IAAI,CAAC,mBAAmB,GAAG,UAAU,CACnC,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,EACzC,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CACnC,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,cAAc;;QACpB,iDAAiD;QACjD,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC;QAExC,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE;YAC9B,MAAM,CAAC,IAAI,CACT,yGAAyG,CAC1G,CAAC;YAEF,OAAO;SACR;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,oBAAoB,CAAC,eAAe,EAAE,CAAC;QAE1D,IAAI,KAAK,EAAE;YACT,IAAI,IAAI,CAAC,kBAAkB,EAAE;gBAC3B,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,EAAE;oBACrD,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,CAAC,SAAS,EAAsC,CAAC;oBAChG,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAEnE,MAAM,IAAI,mCACL,eAAe,CAAC,IAAI,KACvB,KAAK,EAAE;4BACL,IAAI,EAAE,KAAK,CAAC,IAAI;4BAChB,GAAG,EAAE,KAAK,CAAC,GAAG;4BACd,uDAAuD;4BACvD,MAAM,EAAE,EAAE;4BACV,WAAW,EAAE,gBAAgB;yBAC9B,EACD,aAAa,EAAE,aAAa;4BAC1B,CAAC,CAAC;gCACE,IAAI,EAAE,aAAa,CAAC,IAAI;gCACxB,GAAG,EAAE,aAAa,CAAC,GAAG;gCACtB,uDAAuD;gCACvD,MAAM,EAAE,EAAE;6BACX;4BACH,CAAC,CAAC,IAAI,GACT,CAAC;oBAEF,MAAM,cAAc,mCACf,eAAe,KAClB,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,IAAI,kCACC,eAAe,CAAC,IAAI,KACvB,oBAAoB,EAAE,KAAK,CAAC,IAAI,KAElC,IAAI,GACL,CAAC;oBAEF,MAAM,YAAY,GAAG,IAAI,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC;oBAC/D,IAAI,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC;oBAExD,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,KAAK,YAAY,CAAC,IAAI,CAAC;oBAC/D,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAC7B,YAAY,CAAC,IAAI,EACjB,YAAY,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,wBAAwB,CAClE,CAAC;oBAEF,MAAA,IAAI,CAAC,eAAe,qDAAG,YAAY,CAAC,CAAC;iBACtC;gBAED,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACpC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;gBAE1B,uDAAuD;gBACvD,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;aACrC;SACF;IACH,CAAC;IAED,4DAA4D;IACpD,oBAAoB,CAAC,cAAkC;;QAC7D,IAAI,YAAY,GAAG,MAAA,IAAI,CAAC,eAAe,uEAAQ,cAAc,EAAG,CAAC;QAEjE,mEAAmE;QACnE,IAAI,CAAC,YAAY,EAAE;YACjB,MAAM,CAAC,KAAK,CACV,4DAA4D,YAAY,2DAA2D,CACpI,CAAC;YAEF,YAAY,mCACP,cAAc,KACjB,OAAO,EAAE,KAAK,GACf,CAAC;SACH;QAED,8GAA8G;QAC9G,IAAI,YAAY,CAAC,OAAO,KAAK,KAAK,EAAE;YAClC,MAAM,CAAC,GAAG,CACR,+DAA+D,YAAY,CAAC,IAAI,0BAA0B,CAC3G,CAAC;SACH;aAAM;YACL,+DAA+D;YAC/D,IAAI,CAAC,wBAAwB,EAAE,CAAC;SACjC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;IAWD,wEAAwE;IAChE,yBAAyB;QAC/B,IAAI,IAAI,CAAC,kBAAkB,EAAE;YAC3B,IAAI,CAAC,kBAAkB,CAAC,OAAO,GAAG,KAAK,CAAC;YACxC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC;YACjC,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;SACrC;IACH,CAAC;IAED;;OAEG;IACK,wBAAwB;QAC9B,IAAI,OAAO,IAAI,CAAC,mBAAmB,KAAK,WAAW,EAAE;YACnD,YAAY,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YACvC,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;SACtC;IACH,CAAC;;AA5Pa,kDAAmB,GAAW,qBAAqB,CAAC;AA+PpE;;;GAGG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,8BAA8B,CAAC;AAE/E,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,IAAI,EAAE,cAAc;IACpB,EAAE,EAAE,YAAY;IAChB,IAAI,EAAE;QACJ,yBAAyB,EAAE,8BAA8B,CAAC,mBAAmB;KAC9E;IACD,IAAI,EAAE,EAAE;CACT,CAAC","sourcesContent":["/* eslint-disable max-lines */\nimport type { Transaction as TransactionType, TransactionContext } from '@sentry/types';\nimport { logger } from '@sentry/utils';\n\nimport { RN_GLOBAL_OBJ } from '../utils/worldwide';\nimport type { OnConfirmRoute, TransactionCreator } from './routingInstrumentation';\nimport { InternalRoutingInstrumentation } from './routingInstrumentation';\nimport type { BeforeNavigate, ReactNavigationTransactionContext, RouteChangeContextData } from './types';\nimport { customTransactionSource, defaultTransactionSource, getBlankTransactionContext } from './utils';\n\nexport interface NavigationRoute {\n name: string;\n key: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n params?: Record<string, any>;\n}\n\ninterface NavigationContainer {\n addListener: (type: string, listener: () => void) => void;\n getCurrentRoute: () => NavigationRoute;\n}\n\ninterface ReactNavigationOptions {\n /**\n * How long the instrumentation will wait for the route to mount after a change has been initiated,\n * before the transaction is discarded.\n * Time is in ms.\n *\n * Default: 1000\n */\n routeChangeTimeoutMs: number;\n}\n\nconst defaultOptions: ReactNavigationOptions = {\n routeChangeTimeoutMs: 1000,\n};\n\n/**\n * Instrumentation for React-Navigation V5 and above. See docs or sample app for usage.\n *\n * How this works:\n * - `_onDispatch` is called every time a dispatch happens and sets an IdleTransaction on the scope without any route context.\n * - `_onStateChange` is then called AFTER the state change happens due to a dispatch and sets the route context onto the active transaction.\n * - If `_onStateChange` isn't called within `STATE_CHANGE_TIMEOUT_DURATION` of the dispatch, then the transaction is not sampled and finished.\n */\nexport class ReactNavigationInstrumentation extends InternalRoutingInstrumentation {\n public static instrumentationName: string = 'react-navigation-v5';\n\n public readonly name: string = ReactNavigationInstrumentation.instrumentationName;\n\n private _navigationContainer: NavigationContainer | null = null;\n\n private readonly _maxRecentRouteLen: number = 200;\n\n private _latestRoute?: NavigationRoute;\n private _latestTransaction?: TransactionType;\n private _initialStateHandled: boolean = false;\n private _stateChangeTimeout?: number | undefined;\n private _recentRouteKeys: string[] = [];\n\n private _options: ReactNavigationOptions;\n\n public constructor(options: Partial<ReactNavigationOptions> = {}) {\n super();\n\n this._options = {\n ...defaultOptions,\n ...options,\n };\n }\n\n /**\n * Extends by calling _handleInitialState at the end.\n */\n public registerRoutingInstrumentation(\n listener: TransactionCreator,\n beforeNavigate: BeforeNavigate,\n onConfirmRoute: OnConfirmRoute,\n ): void {\n super.registerRoutingInstrumentation(listener, beforeNavigate, onConfirmRoute);\n\n // We create an initial state here to ensure a transaction gets created before the first route mounts.\n if (!this._initialStateHandled) {\n this._onDispatch();\n if (this._navigationContainer) {\n // Navigation container already registered, just populate with route state\n this._onStateChange();\n\n this._initialStateHandled = true;\n }\n }\n }\n\n /**\n * Pass the ref to the navigation container to register it to the instrumentation\n * @param navigationContainerRef Ref to a `NavigationContainer`\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types\n public registerNavigationContainer(navigationContainerRef: any): void {\n /* We prevent duplicate routing instrumentation to be initialized on fast refreshes\n\n Explanation: If the user triggers a fast refresh on the file that the instrumentation is\n initialized in, it will initialize a new instance and will cause undefined behavior.\n */\n if (!RN_GLOBAL_OBJ.__sentry_rn_v5_registered) {\n if ('current' in navigationContainerRef) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n this._navigationContainer = navigationContainerRef.current;\n } else {\n this._navigationContainer = navigationContainerRef;\n }\n\n if (this._navigationContainer) {\n this._navigationContainer.addListener(\n '__unsafe_action__', // This action is emitted on every dispatch\n this._onDispatch.bind(this),\n );\n this._navigationContainer.addListener(\n 'state', // This action is emitted on every state change\n this._onStateChange.bind(this),\n );\n\n if (!this._initialStateHandled) {\n if (this._latestTransaction) {\n // If registerRoutingInstrumentation was called first _onDispatch has already been called\n this._onStateChange();\n\n this._initialStateHandled = true;\n } else {\n logger.log(\n '[ReactNavigationInstrumentation] Navigation container registered, but integration has not been setup yet.',\n );\n }\n }\n\n RN_GLOBAL_OBJ.__sentry_rn_v5_registered = true;\n } else {\n logger.warn('[ReactNavigationInstrumentation] Received invalid navigation container ref!');\n }\n } else {\n logger.log(\n '[ReactNavigationInstrumentation] Instrumentation already exists, but register has been called again, doing nothing.',\n );\n }\n }\n\n /**\n * To be called on every React-Navigation action dispatch.\n * It does not name the transaction or populate it with route information. Instead, it waits for the state to fully change\n * and gets the route information from there, @see _onStateChange\n */\n private _onDispatch(): void {\n if (this._latestTransaction) {\n logger.log(\n '[ReactNavigationInstrumentation] A transaction was detected that turned out to be a noop, discarding.',\n );\n this._discardLatestTransaction();\n this._clearStateChangeTimeout();\n }\n\n this._latestTransaction = this.onRouteWillChange(\n getBlankTransactionContext(ReactNavigationInstrumentation.instrumentationName),\n );\n\n this._stateChangeTimeout = setTimeout(\n this._discardLatestTransaction.bind(this),\n this._options.routeChangeTimeoutMs,\n );\n }\n\n /**\n * To be called AFTER the state has been changed to populate the transaction with the current route.\n */\n private _onStateChange(): void {\n // Use the getCurrentRoute method to be accurate.\n const previousRoute = this._latestRoute;\n\n if (!this._navigationContainer) {\n logger.warn(\n '[ReactNavigationInstrumentation] Missing navigation container ref. Route transactions will not be sent.',\n );\n\n return;\n }\n\n const route = this._navigationContainer.getCurrentRoute();\n\n if (route) {\n if (this._latestTransaction) {\n if (!previousRoute || previousRoute.key !== route.key) {\n const originalContext = this._latestTransaction.toContext() as typeof BLANK_TRANSACTION_CONTEXT;\n const routeHasBeenSeen = this._recentRouteKeys.includes(route.key);\n\n const data: RouteChangeContextData = {\n ...originalContext.data,\n route: {\n name: route.name,\n key: route.key,\n // TODO: filter PII params instead of dropping them all\n params: {},\n hasBeenSeen: routeHasBeenSeen,\n },\n previousRoute: previousRoute\n ? {\n name: previousRoute.name,\n key: previousRoute.key,\n // TODO: filter PII params instead of dropping them all\n params: {},\n }\n : null,\n };\n\n const updatedContext: ReactNavigationTransactionContext = {\n ...originalContext,\n name: route.name,\n tags: {\n ...originalContext.tags,\n 'routing.route.name': route.name,\n },\n data,\n };\n\n const finalContext = this._prepareFinalContext(updatedContext);\n this._latestTransaction.updateWithContext(finalContext);\n\n const isCustomName = updatedContext.name !== finalContext.name;\n this._latestTransaction.setName(\n finalContext.name,\n isCustomName ? customTransactionSource : defaultTransactionSource,\n );\n\n this._onConfirmRoute?.(finalContext);\n }\n\n this._pushRecentRouteKey(route.key);\n this._latestRoute = route;\n\n // Clear the latest transaction as it has been handled.\n this._latestTransaction = undefined;\n }\n }\n }\n\n /** Creates final transaction context before confirmation */\n private _prepareFinalContext(updatedContext: TransactionContext): TransactionContext {\n let finalContext = this._beforeNavigate?.({ ...updatedContext });\n\n // This block is to catch users not returning a transaction context\n if (!finalContext) {\n logger.error(\n `[ReactNavigationInstrumentation] beforeNavigate returned ${finalContext}, return context.sampled = false to not send transaction.`,\n );\n\n finalContext = {\n ...updatedContext,\n sampled: false,\n };\n }\n\n // Note: finalContext.sampled will be false at this point only if the user sets it to be so in beforeNavigate.\n if (finalContext.sampled === false) {\n logger.log(\n `[ReactNavigationInstrumentation] Will not send transaction \"${finalContext.name}\" due to beforeNavigate.`,\n );\n } else {\n // Clear the timeout so the transaction does not get cancelled.\n this._clearStateChangeTimeout();\n }\n\n return finalContext;\n }\n\n /** Pushes a recent route key, and removes earlier routes when there is greater than the max length */\n private _pushRecentRouteKey = (key: string): void => {\n this._recentRouteKeys.push(key);\n\n if (this._recentRouteKeys.length > this._maxRecentRouteLen) {\n this._recentRouteKeys = this._recentRouteKeys.slice(this._recentRouteKeys.length - this._maxRecentRouteLen);\n }\n };\n\n /** Cancels the latest transaction so it does not get sent to Sentry. */\n private _discardLatestTransaction(): void {\n if (this._latestTransaction) {\n this._latestTransaction.sampled = false;\n this._latestTransaction.finish();\n this._latestTransaction = undefined;\n }\n }\n\n /**\n *\n */\n private _clearStateChangeTimeout(): void {\n if (typeof this._stateChangeTimeout !== 'undefined') {\n clearTimeout(this._stateChangeTimeout);\n this._stateChangeTimeout = undefined;\n }\n }\n}\n\n/**\n * Backwards compatibility alias for ReactNavigationInstrumentation\n * @deprecated Use ReactNavigationInstrumentation\n */\nexport const ReactNavigationV5Instrumentation = ReactNavigationInstrumentation;\n\nexport const BLANK_TRANSACTION_CONTEXT = {\n name: 'Route Change',\n op: 'navigation',\n tags: {\n 'routing.instrumentation': ReactNavigationInstrumentation.instrumentationName,\n },\n data: {},\n};\n"]}
1
+ {"version":3,"file":"reactnavigation.js","sourceRoot":"","sources":["../../../src/js/tracing/reactnavigation.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAE5F,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAG3D,OAAO,EAA2B,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACnH,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAC1E,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,iBAAiB,CAAC;AAE3F,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AAiCxG,MAAM,cAAc,GAA2B;IAC7C,oBAAoB,EAAE,IAAI;IAC1B,0BAA0B,EAAE,KAAK;CAClC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,OAAO,8BAA+B,SAAQ,8BAA8B;IAoBhF,YAAmB,UAA2C,EAAE;QAC9D,KAAK,EAAE,CAAC;QAlBM,SAAI,GAAW,8BAA8B,CAAC,mBAAmB,CAAC;QAE1E,yBAAoB,GAA+B,IAAI,CAAC;QACxD,gCAA2B,GAA8B,IAAI,CAAC;QAErD,uBAAkB,GAAW,GAAG,CAAC;QAM1C,yBAAoB,GAAY,KAAK,CAAC;QAEtC,qBAAgB,GAAa,EAAE,CAAC;QAiSxC,sGAAsG;QAC9F,wBAAmB,GAAG,CAAC,GAAW,EAAQ,EAAE;YAClD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAEhC,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE;gBAC1D,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC;aAC7G;QACH,CAAC,CAAC;QAjSA,IAAI,CAAC,QAAQ,mCACR,cAAc,GACd,OAAO,CACX,CAAC;QAEF,IAAI,IAAI,CAAC,QAAQ,CAAC,0BAA0B,EAAE;YAC5C,IAAI,CAAC,2BAA2B,GAAG,wBAAwB,EAAE,CAAC;YAC9D,IAAI,CAAC,2BAA2B,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;YAC9D,MAAM,CAAC,yCAAyC,EAAE,CAAC,KAAK,CAAC,CAAC,MAAe,EAAE,EAAE;gBAC3E,MAAM,CAAC,KAAK,CAAC,oFAAoF,MAAM,EAAE,CAAC,CAAC;YAC7G,CAAC,CAAC,CAAC;SACJ;IACH,CAAC;IAED;;OAEG;IACI,8BAA8B,CACnC,QAA4B,EAC5B,cAA8B,EAC9B,cAA8B;QAE9B,KAAK,CAAC,8BAA8B,CAAC,QAAQ,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;QAE/E,sGAAsG;QACtG,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE;YAC9B,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,IAAI,CAAC,oBAAoB,EAAE;gBAC7B,0EAA0E;gBAC1E,IAAI,CAAC,cAAc,EAAE,CAAC;gBAEtB,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;aAClC;SACF;IACH,CAAC;IAED;;;OAGG;IACH,iHAAiH;IAC1G,2BAA2B,CAAC,sBAA2B;QAC5D;;;;WAIG;QACH,IAAI,CAAC,aAAa,CAAC,yBAAyB,EAAE;YAC5C,IAAI,SAAS,IAAI,sBAAsB,EAAE;gBACvC,sEAAsE;gBACtE,IAAI,CAAC,oBAAoB,GAAG,sBAAsB,CAAC,OAAO,CAAC;aAC5D;iBAAM;gBACL,IAAI,CAAC,oBAAoB,GAAG,sBAAsB,CAAC;aACpD;YAED,IAAI,IAAI,CAAC,oBAAoB,EAAE;gBAC7B,IAAI,CAAC,oBAAoB,CAAC,WAAW,CACnC,mBAAmB,EAAE,2CAA2C;gBAChE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAC5B,CAAC;gBACF,IAAI,CAAC,oBAAoB,CAAC,WAAW,CACnC,OAAO,EAAE,+CAA+C;gBACxD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAC/B,CAAC;gBAEF,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE;oBAC9B,IAAI,IAAI,CAAC,kBAAkB,EAAE;wBAC3B,yFAAyF;wBACzF,IAAI,CAAC,cAAc,EAAE,CAAC;wBAEtB,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;qBAClC;yBAAM;wBACL,MAAM,CAAC,GAAG,CACR,2GAA2G,CAC5G,CAAC;qBACH;iBACF;gBAED,aAAa,CAAC,yBAAyB,GAAG,IAAI,CAAC;aAChD;iBAAM;gBACL,MAAM,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;aAC5F;SACF;aAAM;YACL,MAAM,CAAC,GAAG,CACR,qHAAqH,CACtH,CAAC;SACH;IACH,CAAC;IAED;;;;OAIG;IACK,WAAW;;QACjB,IAAI,IAAI,CAAC,kBAAkB,EAAE;YAC3B,MAAM,CAAC,GAAG,CACR,uGAAuG,CACxG,CAAC;YACF,IAAI,CAAC,yBAAyB,EAAE,CAAC;YACjC,IAAI,CAAC,wBAAwB,EAAE,CAAC;SACjC;QAED,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,CAC9C,0BAA0B,CAAC,8BAA8B,CAAC,mBAAmB,CAAC,CAC/E,CAAC;QAEF,IAAI,IAAI,CAAC,QAAQ,CAAC,0BAA0B,EAAE;YAC5C,IAAI,CAAC,yBAAyB,GAAG,iBAAiB,CAAC;gBACjD,EAAE,EAAE,uBAAuB;gBAC3B,IAAI,EAAE,uBAAuB;gBAC7B,cAAc,EAAE,MAAA,IAAI,CAAC,kBAAkB,0CAAE,cAAc;aACxD,CAAC,CAAC;SACJ;QAED,IAAI,CAAC,mBAAmB,GAAG,UAAU,CACnC,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,EACzC,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CACnC,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,cAAc;;QACpB,MAAM,qBAAqB,GAAG,kBAAkB,EAAE,CAAC;QAEnD,iDAAiD;QACjD,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC;QAExC,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE;YAC9B,MAAM,CAAC,IAAI,CACT,yGAAyG,CAC1G,CAAC;YAEF,OAAO;SACR;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,oBAAoB,CAAC,eAAe,EAAE,CAAC;QAE1D,IAAI,KAAK,EAAE;YACT,IAAI,IAAI,CAAC,kBAAkB,EAAE;gBAC3B,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,EAAE;oBACrD,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBACnE,MAAM,cAAc,GAClB,CAAC,gBAAgB;wBACjB,IAAI,CAAC,QAAQ,CAAC,0BAA0B;wBACxC,6BAA6B,CAAC;4BAC5B,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,kBAAkB;4BACrC,kBAAkB,EAAE,IAAI;yBACzB,CAAC,CAAC;oBAEL,CAAC,gBAAgB;yBACf,MAAA,IAAI,CAAC,2BAA2B,0CAAE,IAAI,CACpC,iBAAiB,EACjB,CAAC,EAAE,0BAA0B,EAAiB,EAAE,EAAE;;4BAChD,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;4BACnC,IAAI,CAAC,UAAU,EAAE;gCACf,MAAM,CAAC,IAAI,CACT,6FAA6F,CAC9F,CAAC;gCACF,OAAO;6BACR;4BAED,IAAI,yBAAyB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE;gCAC7C,MAAM,CAAC,IAAI,CACT,+FAA+F,CAChG,CAAC;gCACF,OAAO;6BACR;4BAED,IAAI,CAAC,cAAc,EAAE;gCACnB,OAAO;6BACR;4BAED,IAAI,UAAU,CAAC,cAAc,CAAC,CAAC,cAAc,MAAK,MAAA,aAAa,EAAE,0CAAE,WAAW,GAAG,MAAM,CAAA,EAAE;gCACvF,MAAM,CAAC,IAAI,CACT,gKAAgK,CACjK,CAAC;gCACF,OAAO;6BACR;4BAED,cAAc,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;4BAC/B,cAAc,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;4BAC/C,MAAM,QAAQ,GAAG,UAAU,CAAC,cAAc,CAAC,CAAC;4BAE5C,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC;4BACvC,MAAM,aAAa,GAAG,QAAQ,CAAC,eAAe,CAAC;4BAC/C,IAAI,CAAC,WAAW,IAAI,CAAC,aAAa,EAAE;gCAClC,OAAO;6BACR;4BAED,cAAc,CAAC,yBAAyB,EAAE,CAAC,WAAW,GAAG,aAAa,CAAC,GAAG,IAAI,EAAE,aAAa,CAAC,CAAC;wBACjG,CAAC,CACF,CAAA,CAAC;oBAEJ,MAAA,IAAI,CAAC,yBAAyB,0CAAE,UAAU,CAAC,4BAA4B,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;oBACrF,MAAA,IAAI,CAAC,yBAAyB,0CAAE,SAAS,CAAC,IAAI,CAAC,CAAC;oBAChD,MAAA,IAAI,CAAC,yBAAyB,0CAAE,GAAG,CAAC,qBAAqB,CAAC,CAAC;oBAC3D,IAAI,CAAC,yBAAyB,GAAG,SAAS,CAAC;oBAE3C,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,CAAC,SAAS,EAAsC,CAAC;oBAEhG,MAAM,IAAI,mCACL,eAAe,CAAC,IAAI,KACvB,KAAK,EAAE;4BACL,IAAI,EAAE,KAAK,CAAC,IAAI;4BAChB,GAAG,EAAE,KAAK,CAAC,GAAG;4BACd,uDAAuD;4BACvD,MAAM,EAAE,EAAE;4BACV,WAAW,EAAE,gBAAgB;yBAC9B,EACD,aAAa,EAAE,aAAa;4BAC1B,CAAC,CAAC;gCACE,IAAI,EAAE,aAAa,CAAC,IAAI;gCACxB,GAAG,EAAE,aAAa,CAAC,GAAG;gCACtB,uDAAuD;gCACvD,MAAM,EAAE,EAAE;6BACX;4BACH,CAAC,CAAC,IAAI,GACT,CAAC;oBAEF,MAAM,cAAc,mCACf,eAAe,KAClB,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,IAAI,kCACC,eAAe,CAAC,IAAI,KACvB,oBAAoB,EAAE,KAAK,CAAC,IAAI,KAElC,IAAI,GACL,CAAC;oBAEF,MAAM,YAAY,GAAG,IAAI,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC;oBAC/D,IAAI,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC;oBAExD,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,KAAK,YAAY,CAAC,IAAI,CAAC;oBAC/D,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAC7B,YAAY,CAAC,IAAI,EACjB,YAAY,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,wBAAwB,CAClE,CAAC;oBAEF,MAAA,IAAI,CAAC,eAAe,qDAAG,YAAY,CAAC,CAAC;iBACtC;gBAED,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACpC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;gBAE1B,uDAAuD;gBACvD,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;aACrC;SACF;IACH,CAAC;IAED,4DAA4D;IACpD,oBAAoB,CAAC,cAAkC;;QAC7D,IAAI,YAAY,GAAG,MAAA,IAAI,CAAC,eAAe,uEAAQ,cAAc,EAAG,CAAC;QAEjE,mEAAmE;QACnE,IAAI,CAAC,YAAY,EAAE;YACjB,MAAM,CAAC,KAAK,CACV,4DAA4D,YAAY,2DAA2D,CACpI,CAAC;YAEF,YAAY,mCACP,cAAc,KACjB,OAAO,EAAE,KAAK,GACf,CAAC;SACH;QAED,8GAA8G;QAC9G,IAAI,YAAY,CAAC,OAAO,KAAK,KAAK,EAAE;YAClC,MAAM,CAAC,GAAG,CACR,+DAA+D,YAAY,CAAC,IAAI,0BAA0B,CAC3G,CAAC;SACH;aAAM;YACL,+DAA+D;YAC/D,IAAI,CAAC,wBAAwB,EAAE,CAAC;SACjC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;IAWD,wEAAwE;IAChE,yBAAyB;QAC/B,IAAI,IAAI,CAAC,kBAAkB,EAAE;YAC3B,IAAI,CAAC,kBAAkB,CAAC,OAAO,GAAG,KAAK,CAAC;YACxC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC;YACjC,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;SACrC;QACD,IAAI,IAAI,CAAC,yBAAyB,EAAE;YAClC,IAAI,CAAC,yBAAyB,GAAG,SAAS,CAAC;SAC5C;IACH,CAAC;IAED;;OAEG;IACK,wBAAwB;QAC9B,IAAI,OAAO,IAAI,CAAC,mBAAmB,KAAK,WAAW,EAAE;YACnD,YAAY,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YACvC,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;SACtC;IACH,CAAC;;AA7Ua,kDAAmB,GAAW,qBAAqB,CAAC;AAgVpE;;;GAGG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,8BAA8B,CAAC;AAE/E,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,IAAI,EAAE,cAAc;IACpB,EAAE,EAAE,YAAY;IAChB,IAAI,EAAE;QACJ,yBAAyB,EAAE,8BAA8B,CAAC,mBAAmB;KAC9E;IACD,IAAI,EAAE,EAAE;CACT,CAAC","sourcesContent":["/* eslint-disable max-lines */\nimport { getActiveSpan, setMeasurement, spanToJSON, startInactiveSpan } from '@sentry/core';\nimport type { Span, Transaction as TransactionType, TransactionContext } from '@sentry/types';\nimport { logger, timestampInSeconds } from '@sentry/utils';\n\nimport type { NewFrameEvent } from '../utils/sentryeventemitter';\nimport { type SentryEventEmitter, createSentryEventEmitter, NewFrameEventName } from '../utils/sentryeventemitter';\nimport { RN_GLOBAL_OBJ } from '../utils/worldwide';\nimport { NATIVE } from '../wrapper';\nimport type { OnConfirmRoute, TransactionCreator } from './routingInstrumentation';\nimport { InternalRoutingInstrumentation } from './routingInstrumentation';\nimport { manualInitialDisplaySpans, startTimeToInitialDisplaySpan } from './timetodisplay';\nimport type { BeforeNavigate, ReactNavigationTransactionContext, RouteChangeContextData } from './types';\nimport { customTransactionSource, defaultTransactionSource, getBlankTransactionContext } from './utils';\n\nexport interface NavigationRoute {\n name: string;\n key: string;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n params?: Record<string, any>;\n}\n\ninterface NavigationContainer {\n addListener: (type: string, listener: () => void) => void;\n getCurrentRoute: () => NavigationRoute;\n}\n\ninterface ReactNavigationOptions {\n /**\n * How long the instrumentation will wait for the route to mount after a change has been initiated,\n * before the transaction is discarded.\n * Time is in ms.\n *\n * @default 1000\n */\n routeChangeTimeoutMs: number;\n\n /**\n * Time to initial display measures the time it takes from\n * navigation dispatch to the render of the first frame of the new screen.\n *\n * @default false\n */\n enableTimeToInitialDisplay: boolean;\n}\n\nconst defaultOptions: ReactNavigationOptions = {\n routeChangeTimeoutMs: 1000,\n enableTimeToInitialDisplay: false,\n};\n\n/**\n * Instrumentation for React-Navigation V5 and above. See docs or sample app for usage.\n *\n * How this works:\n * - `_onDispatch` is called every time a dispatch happens and sets an IdleTransaction on the scope without any route context.\n * - `_onStateChange` is then called AFTER the state change happens due to a dispatch and sets the route context onto the active transaction.\n * - If `_onStateChange` isn't called within `STATE_CHANGE_TIMEOUT_DURATION` of the dispatch, then the transaction is not sampled and finished.\n */\nexport class ReactNavigationInstrumentation extends InternalRoutingInstrumentation {\n public static instrumentationName: string = 'react-navigation-v5';\n\n public readonly name: string = ReactNavigationInstrumentation.instrumentationName;\n\n private _navigationContainer: NavigationContainer | null = null;\n private _newScreenFrameEventEmitter: SentryEventEmitter | null = null;\n\n private readonly _maxRecentRouteLen: number = 200;\n\n private _latestRoute?: NavigationRoute;\n private _latestTransaction?: TransactionType;\n private _navigationProcessingSpan?: Span;\n\n private _initialStateHandled: boolean = false;\n private _stateChangeTimeout?: number | undefined;\n private _recentRouteKeys: string[] = [];\n\n private _options: ReactNavigationOptions;\n\n public constructor(options: Partial<ReactNavigationOptions> = {}) {\n super();\n\n this._options = {\n ...defaultOptions,\n ...options,\n };\n\n if (this._options.enableTimeToInitialDisplay) {\n this._newScreenFrameEventEmitter = createSentryEventEmitter();\n this._newScreenFrameEventEmitter.initAsync(NewFrameEventName);\n NATIVE.initNativeReactNavigationNewFrameTracking().catch((reason: unknown) => {\n logger.error(`[ReactNavigationInstrumentation] Failed to initialize native new frame tracking: ${reason}`);\n });\n }\n }\n\n /**\n * Extends by calling _handleInitialState at the end.\n */\n public registerRoutingInstrumentation(\n listener: TransactionCreator,\n beforeNavigate: BeforeNavigate,\n onConfirmRoute: OnConfirmRoute,\n ): void {\n super.registerRoutingInstrumentation(listener, beforeNavigate, onConfirmRoute);\n\n // We create an initial state here to ensure a transaction gets created before the first route mounts.\n if (!this._initialStateHandled) {\n this._onDispatch();\n if (this._navigationContainer) {\n // Navigation container already registered, just populate with route state\n this._onStateChange();\n\n this._initialStateHandled = true;\n }\n }\n }\n\n /**\n * Pass the ref to the navigation container to register it to the instrumentation\n * @param navigationContainerRef Ref to a `NavigationContainer`\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types\n public registerNavigationContainer(navigationContainerRef: any): void {\n /* We prevent duplicate routing instrumentation to be initialized on fast refreshes\n\n Explanation: If the user triggers a fast refresh on the file that the instrumentation is\n initialized in, it will initialize a new instance and will cause undefined behavior.\n */\n if (!RN_GLOBAL_OBJ.__sentry_rn_v5_registered) {\n if ('current' in navigationContainerRef) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n this._navigationContainer = navigationContainerRef.current;\n } else {\n this._navigationContainer = navigationContainerRef;\n }\n\n if (this._navigationContainer) {\n this._navigationContainer.addListener(\n '__unsafe_action__', // This action is emitted on every dispatch\n this._onDispatch.bind(this),\n );\n this._navigationContainer.addListener(\n 'state', // This action is emitted on every state change\n this._onStateChange.bind(this),\n );\n\n if (!this._initialStateHandled) {\n if (this._latestTransaction) {\n // If registerRoutingInstrumentation was called first _onDispatch has already been called\n this._onStateChange();\n\n this._initialStateHandled = true;\n } else {\n logger.log(\n '[ReactNavigationInstrumentation] Navigation container registered, but integration has not been setup yet.',\n );\n }\n }\n\n RN_GLOBAL_OBJ.__sentry_rn_v5_registered = true;\n } else {\n logger.warn('[ReactNavigationInstrumentation] Received invalid navigation container ref!');\n }\n } else {\n logger.log(\n '[ReactNavigationInstrumentation] Instrumentation already exists, but register has been called again, doing nothing.',\n );\n }\n }\n\n /**\n * To be called on every React-Navigation action dispatch.\n * It does not name the transaction or populate it with route information. Instead, it waits for the state to fully change\n * and gets the route information from there, @see _onStateChange\n */\n private _onDispatch(): void {\n if (this._latestTransaction) {\n logger.log(\n '[ReactNavigationInstrumentation] A transaction was detected that turned out to be a noop, discarding.',\n );\n this._discardLatestTransaction();\n this._clearStateChangeTimeout();\n }\n\n this._latestTransaction = this.onRouteWillChange(\n getBlankTransactionContext(ReactNavigationInstrumentation.instrumentationName),\n );\n\n if (this._options.enableTimeToInitialDisplay) {\n this._navigationProcessingSpan = startInactiveSpan({\n op: 'navigation.processing',\n name: 'Navigation processing',\n startTimestamp: this._latestTransaction?.startTimestamp,\n });\n }\n\n this._stateChangeTimeout = setTimeout(\n this._discardLatestTransaction.bind(this),\n this._options.routeChangeTimeoutMs,\n );\n }\n\n /**\n * To be called AFTER the state has been changed to populate the transaction with the current route.\n */\n private _onStateChange(): void {\n const stateChangedTimestamp = timestampInSeconds();\n\n // Use the getCurrentRoute method to be accurate.\n const previousRoute = this._latestRoute;\n\n if (!this._navigationContainer) {\n logger.warn(\n '[ReactNavigationInstrumentation] Missing navigation container ref. Route transactions will not be sent.',\n );\n\n return;\n }\n\n const route = this._navigationContainer.getCurrentRoute();\n\n if (route) {\n if (this._latestTransaction) {\n if (!previousRoute || previousRoute.key !== route.key) {\n const routeHasBeenSeen = this._recentRouteKeys.includes(route.key);\n const latestTtidSpan =\n !routeHasBeenSeen &&\n this._options.enableTimeToInitialDisplay &&\n startTimeToInitialDisplaySpan({\n name: `${route.name} initial display`,\n isAutoInstrumented: true,\n });\n\n !routeHasBeenSeen &&\n this._newScreenFrameEventEmitter?.once(\n NewFrameEventName,\n ({ newFrameTimestampInSeconds }: NewFrameEvent) => {\n const activeSpan = getActiveSpan();\n if (!activeSpan) {\n logger.warn(\n '[ReactNavigationInstrumentation] No active span found to attach ui.load.initial_display to.',\n );\n return;\n }\n\n if (manualInitialDisplaySpans.has(activeSpan)) {\n logger.warn(\n '[ReactNavigationInstrumentation] Detected manual instrumentation for the current active span.',\n );\n return;\n }\n\n if (!latestTtidSpan) {\n return;\n }\n\n if (spanToJSON(latestTtidSpan).parent_span_id !== getActiveSpan()?.spanContext().spanId) {\n logger.warn(\n '[ReactNavigationInstrumentation] Currently Active Span changed before the new frame was rendered, _latestTtidSpan is not a child of the currently active span.',\n );\n return;\n }\n\n latestTtidSpan.setStatus('ok');\n latestTtidSpan.end(newFrameTimestampInSeconds);\n const ttidSpan = spanToJSON(latestTtidSpan);\n\n const ttidSpanEnd = ttidSpan.timestamp;\n const ttidSpanStart = ttidSpan.start_timestamp;\n if (!ttidSpanEnd || !ttidSpanStart) {\n return;\n }\n\n setMeasurement('time_to_initial_display', (ttidSpanEnd - ttidSpanStart) * 1000, 'millisecond');\n },\n );\n\n this._navigationProcessingSpan?.updateName(`Processing navigation to ${route.name}`);\n this._navigationProcessingSpan?.setStatus('ok');\n this._navigationProcessingSpan?.end(stateChangedTimestamp);\n this._navigationProcessingSpan = undefined;\n\n const originalContext = this._latestTransaction.toContext() as typeof BLANK_TRANSACTION_CONTEXT;\n\n const data: RouteChangeContextData = {\n ...originalContext.data,\n route: {\n name: route.name,\n key: route.key,\n // TODO: filter PII params instead of dropping them all\n params: {},\n hasBeenSeen: routeHasBeenSeen,\n },\n previousRoute: previousRoute\n ? {\n name: previousRoute.name,\n key: previousRoute.key,\n // TODO: filter PII params instead of dropping them all\n params: {},\n }\n : null,\n };\n\n const updatedContext: ReactNavigationTransactionContext = {\n ...originalContext,\n name: route.name,\n tags: {\n ...originalContext.tags,\n 'routing.route.name': route.name,\n },\n data,\n };\n\n const finalContext = this._prepareFinalContext(updatedContext);\n this._latestTransaction.updateWithContext(finalContext);\n\n const isCustomName = updatedContext.name !== finalContext.name;\n this._latestTransaction.setName(\n finalContext.name,\n isCustomName ? customTransactionSource : defaultTransactionSource,\n );\n\n this._onConfirmRoute?.(finalContext);\n }\n\n this._pushRecentRouteKey(route.key);\n this._latestRoute = route;\n\n // Clear the latest transaction as it has been handled.\n this._latestTransaction = undefined;\n }\n }\n }\n\n /** Creates final transaction context before confirmation */\n private _prepareFinalContext(updatedContext: TransactionContext): TransactionContext {\n let finalContext = this._beforeNavigate?.({ ...updatedContext });\n\n // This block is to catch users not returning a transaction context\n if (!finalContext) {\n logger.error(\n `[ReactNavigationInstrumentation] beforeNavigate returned ${finalContext}, return context.sampled = false to not send transaction.`,\n );\n\n finalContext = {\n ...updatedContext,\n sampled: false,\n };\n }\n\n // Note: finalContext.sampled will be false at this point only if the user sets it to be so in beforeNavigate.\n if (finalContext.sampled === false) {\n logger.log(\n `[ReactNavigationInstrumentation] Will not send transaction \"${finalContext.name}\" due to beforeNavigate.`,\n );\n } else {\n // Clear the timeout so the transaction does not get cancelled.\n this._clearStateChangeTimeout();\n }\n\n return finalContext;\n }\n\n /** Pushes a recent route key, and removes earlier routes when there is greater than the max length */\n private _pushRecentRouteKey = (key: string): void => {\n this._recentRouteKeys.push(key);\n\n if (this._recentRouteKeys.length > this._maxRecentRouteLen) {\n this._recentRouteKeys = this._recentRouteKeys.slice(this._recentRouteKeys.length - this._maxRecentRouteLen);\n }\n };\n\n /** Cancels the latest transaction so it does not get sent to Sentry. */\n private _discardLatestTransaction(): void {\n if (this._latestTransaction) {\n this._latestTransaction.sampled = false;\n this._latestTransaction.finish();\n this._latestTransaction = undefined;\n }\n if (this._navigationProcessingSpan) {\n this._navigationProcessingSpan = undefined;\n }\n }\n\n /**\n *\n */\n private _clearStateChangeTimeout(): void {\n if (typeof this._stateChangeTimeout !== 'undefined') {\n clearTimeout(this._stateChangeTimeout);\n this._stateChangeTimeout = undefined;\n }\n }\n}\n\n/**\n * Backwards compatibility alias for ReactNavigationInstrumentation\n * @deprecated Use ReactNavigationInstrumentation\n */\nexport const ReactNavigationV5Instrumentation = ReactNavigationInstrumentation;\n\nexport const BLANK_TRANSACTION_CONTEXT = {\n name: 'Route Change',\n op: 'navigation',\n tags: {\n 'routing.instrumentation': ReactNavigationInstrumentation.instrumentationName,\n },\n data: {},\n};\n"]}
@@ -0,0 +1,46 @@
1
+ import type { Span, StartSpanOptions } from '@sentry/types';
2
+ import * as React from 'react';
3
+ /**
4
+ * Flags of active spans with manual initial display.
5
+ */
6
+ export declare const manualInitialDisplaySpans: WeakMap<Span, true>;
7
+ export type TimeToDisplayProps = {
8
+ children?: React.ReactNode;
9
+ spanName?: string;
10
+ record?: boolean;
11
+ };
12
+ /**
13
+ * Component to measure time to initial display.
14
+ *
15
+ * The initial display is recorded when the component prop `record` is true.
16
+ *
17
+ * <TimeToInitialDisplay record />
18
+ */
19
+ export declare function TimeToInitialDisplay(props: TimeToDisplayProps): React.ReactElement;
20
+ /**
21
+ * Component to measure time to full display.
22
+ *
23
+ * The initial display is recorded when the component prop `record` is true.
24
+ *
25
+ * <TimeToInitialDisplay record />
26
+ */
27
+ export declare function TimeToFullDisplay(props: TimeToDisplayProps): React.ReactElement;
28
+ /**
29
+ * Starts a new span for the initial display.
30
+ *
31
+ * Returns current span if already exists in the currently active span.
32
+ */
33
+ export declare function startTimeToInitialDisplaySpan(options?: Exclude<StartSpanOptions, 'op' | 'name'> & {
34
+ name?: string;
35
+ isAutoInstrumented?: boolean;
36
+ }): Span | undefined;
37
+ /**
38
+ * Starts a new span for the full display.
39
+ *
40
+ * Returns current span if already exists in the currently active span.
41
+ */
42
+ export declare function startTimeToFullDisplaySpan(options?: Omit<StartSpanOptions, 'op' | 'name'> & {
43
+ name?: string;
44
+ timeoutMs?: number;
45
+ }): Span | undefined;
46
+ //# sourceMappingURL=timetodisplay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timetodisplay.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/timetodisplay.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAC,gBAAgB,EAAG,MAAM,eAAe,CAAC;AAE5D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B;;GAEG;AACH,eAAO,MAAM,yBAAyB,qBAA4B,CAAC;AAOnE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAOlF;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAE/E;AA2BD;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAE,GACnG,IAAI,GAAG,SAAS,CAiClB;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,MAAM,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAEnF,GACA,IAAI,GAAG,SAAS,CAoDlB"}
@@ -0,0 +1,198 @@
1
+ import { getActiveSpan, Span as SpanClass, spanToJSON, startInactiveSpan } from '@sentry/core';
2
+ import { fill, logger } from '@sentry/utils';
3
+ import * as React from 'react';
4
+ import { getRNSentryOnDrawReporter, nativeComponentExists } from './timetodisplaynative';
5
+ import { setSpanDurationAsMeasurement } from './utils';
6
+ let nativeComponentMissingLogged = false;
7
+ /**
8
+ * Flags of active spans with manual initial display.
9
+ */
10
+ export const manualInitialDisplaySpans = new WeakMap();
11
+ /**
12
+ * Flag full display called before initial display for an active span.
13
+ */
14
+ const fullDisplayBeforeInitialDisplay = new WeakMap();
15
+ /**
16
+ * Component to measure time to initial display.
17
+ *
18
+ * The initial display is recorded when the component prop `record` is true.
19
+ *
20
+ * <TimeToInitialDisplay record />
21
+ */
22
+ export function TimeToInitialDisplay(props) {
23
+ const activeSpan = getActiveSpan();
24
+ if (activeSpan) {
25
+ manualInitialDisplaySpans.set(activeSpan, true);
26
+ }
27
+ return React.createElement(TimeToDisplay, { initialDisplay: props.record }, props.children);
28
+ }
29
+ /**
30
+ * Component to measure time to full display.
31
+ *
32
+ * The initial display is recorded when the component prop `record` is true.
33
+ *
34
+ * <TimeToInitialDisplay record />
35
+ */
36
+ export function TimeToFullDisplay(props) {
37
+ return React.createElement(TimeToDisplay, { fullDisplay: props.record }, props.children);
38
+ }
39
+ function TimeToDisplay(props) {
40
+ const RNSentryOnDrawReporter = getRNSentryOnDrawReporter();
41
+ if (__DEV__ && !nativeComponentMissingLogged && !nativeComponentExists) {
42
+ nativeComponentMissingLogged = true;
43
+ logger.error('RNSentryOnDrawReporter is not available on the web, Expo Go and New Architecture. Run native build or report an issue at https://github.com/getsentry/sentry-react-native');
44
+ }
45
+ const onDraw = (event) => onDrawNextFrame(event);
46
+ return (React.createElement(React.Fragment, null,
47
+ React.createElement(RNSentryOnDrawReporter, { onDrawNextFrame: onDraw, initialDisplay: props.initialDisplay, fullDisplay: props.fullDisplay }),
48
+ props.children));
49
+ }
50
+ /**
51
+ * Starts a new span for the initial display.
52
+ *
53
+ * Returns current span if already exists in the currently active span.
54
+ */
55
+ export function startTimeToInitialDisplaySpan(options) {
56
+ var _a;
57
+ const activeSpan = getActiveSpan();
58
+ if (!activeSpan) {
59
+ logger.warn(`[TimeToDisplay] No active span found to attach ui.load.initial_display to.`);
60
+ return;
61
+ }
62
+ if (!(activeSpan instanceof SpanClass)) {
63
+ logger.warn(`[TimeToDisplay] Active span is not instance of Span class.`);
64
+ return;
65
+ }
66
+ const existingSpan = (_a = activeSpan.spanRecorder) === null || _a === void 0 ? void 0 : _a.spans.find((span) => spanToJSON(span).op === 'ui.load.initial_display');
67
+ if (existingSpan) {
68
+ logger.debug(`[TimeToDisplay] Found existing ui.load.initial_display span.`);
69
+ return existingSpan;
70
+ }
71
+ const initialDisplaySpan = startInactiveSpan(Object.assign({ op: 'ui.load.initial_display', name: 'Time To Initial Display', startTimestamp: spanToJSON(activeSpan).start_timestamp }, options));
72
+ if (!initialDisplaySpan) {
73
+ return;
74
+ }
75
+ if (!(options === null || options === void 0 ? void 0 : options.isAutoInstrumented)) {
76
+ manualInitialDisplaySpans.set(activeSpan, true);
77
+ }
78
+ return initialDisplaySpan;
79
+ }
80
+ /**
81
+ * Starts a new span for the full display.
82
+ *
83
+ * Returns current span if already exists in the currently active span.
84
+ */
85
+ export function startTimeToFullDisplaySpan(options = {
86
+ timeoutMs: 30000,
87
+ }) {
88
+ var _a;
89
+ const activeSpan = getActiveSpan();
90
+ if (!activeSpan) {
91
+ logger.warn(`[TimeToDisplay] No active span found to attach ui.load.full_display to.`);
92
+ return;
93
+ }
94
+ if (!(activeSpan instanceof SpanClass)) {
95
+ logger.warn(`[TimeToDisplay] Active span is not instance of Span class.`);
96
+ return;
97
+ }
98
+ const descendantSpans = ((_a = activeSpan.spanRecorder) === null || _a === void 0 ? void 0 : _a.spans) || [];
99
+ const initialDisplaySpan = descendantSpans.find((span) => spanToJSON(span).op === 'ui.load.initial_display');
100
+ if (!initialDisplaySpan) {
101
+ logger.warn(`[TimeToDisplay] No initial display span found to attach ui.load.full_display to.`);
102
+ return;
103
+ }
104
+ const existingSpan = descendantSpans.find((span) => spanToJSON(span).op === 'ui.load.full_display');
105
+ if (existingSpan) {
106
+ logger.debug(`[TimeToDisplay] Found existing ui.load.full_display span.`);
107
+ return existingSpan;
108
+ }
109
+ const fullDisplaySpan = startInactiveSpan(Object.assign({ op: 'ui.load.full_display', name: 'Time To Full Display', startTimestamp: spanToJSON(initialDisplaySpan).start_timestamp }, options));
110
+ if (!fullDisplaySpan) {
111
+ return;
112
+ }
113
+ const timeout = setTimeout(() => {
114
+ if (spanToJSON(fullDisplaySpan).timestamp) {
115
+ return;
116
+ }
117
+ fullDisplaySpan.setStatus('deadline_exceeded');
118
+ fullDisplaySpan.end(spanToJSON(initialDisplaySpan).timestamp);
119
+ setSpanDurationAsMeasurement('time_to_full_display', fullDisplaySpan);
120
+ logger.warn(`[TimeToDisplay] Full display span deadline_exceeded.`);
121
+ }, options.timeoutMs);
122
+ fill(fullDisplaySpan, 'end', (originalEnd) => (endTimestamp) => {
123
+ clearTimeout(timeout);
124
+ originalEnd.call(fullDisplaySpan, endTimestamp);
125
+ });
126
+ return fullDisplaySpan;
127
+ }
128
+ function onDrawNextFrame(event) {
129
+ logger.debug(`[TimeToDisplay] onDrawNextFrame: ${JSON.stringify(event.nativeEvent)}`);
130
+ if (event.nativeEvent.type === 'fullDisplay') {
131
+ return updateFullDisplaySpan(event.nativeEvent.newFrameTimestampInSeconds);
132
+ }
133
+ if (event.nativeEvent.type === 'initialDisplay') {
134
+ return updateInitialDisplaySpan(event.nativeEvent.newFrameTimestampInSeconds);
135
+ }
136
+ }
137
+ function updateInitialDisplaySpan(frameTimestampSeconds) {
138
+ const span = startTimeToInitialDisplaySpan();
139
+ if (!span) {
140
+ logger.warn(`[TimeToDisplay] No span found or created, possibly performance is disabled.`);
141
+ return;
142
+ }
143
+ const activeSpan = getActiveSpan();
144
+ if (!activeSpan) {
145
+ logger.warn(`[TimeToDisplay] No active span found to attach ui.load.initial_display to.`);
146
+ return;
147
+ }
148
+ if (spanToJSON(span).parent_span_id !== spanToJSON(activeSpan).span_id) {
149
+ logger.warn(`[TimeToDisplay] Initial display span is not a child of current active span.`);
150
+ return;
151
+ }
152
+ if (spanToJSON(span).timestamp) {
153
+ logger.warn(`[TimeToDisplay] ${spanToJSON(span).description} span already ended.`);
154
+ return;
155
+ }
156
+ span.end(frameTimestampSeconds);
157
+ span.setStatus('ok');
158
+ logger.debug(`[TimeToDisplay] ${spanToJSON(span).description} span updated with end timestamp.`);
159
+ if (fullDisplayBeforeInitialDisplay.has(activeSpan)) {
160
+ fullDisplayBeforeInitialDisplay.delete(activeSpan);
161
+ updateFullDisplaySpan(frameTimestampSeconds, span);
162
+ }
163
+ setSpanDurationAsMeasurement('time_to_initial_display', span);
164
+ }
165
+ function updateFullDisplaySpan(frameTimestampSeconds, passedInitialDisplaySpan) {
166
+ var _a;
167
+ const activeSpan = getActiveSpan();
168
+ if (!activeSpan) {
169
+ logger.warn(`[TimeToDisplay] No active span found to attach ui.load.full_display to.`);
170
+ return;
171
+ }
172
+ if (!(activeSpan instanceof SpanClass)) {
173
+ logger.warn(`[TimeToDisplay] Active span is not instance of Span class.`);
174
+ return;
175
+ }
176
+ const existingInitialDisplaySpan = passedInitialDisplaySpan
177
+ || ((_a = activeSpan.spanRecorder) === null || _a === void 0 ? void 0 : _a.spans.find((span) => spanToJSON(span).op === 'ui.load.initial_display'));
178
+ const initialDisplayEndTimestamp = existingInitialDisplaySpan && spanToJSON(existingInitialDisplaySpan).timestamp;
179
+ if (!initialDisplayEndTimestamp) {
180
+ fullDisplayBeforeInitialDisplay.set(activeSpan, true);
181
+ logger.warn(`[TimeToDisplay] Full display called before initial display for active span.`);
182
+ return;
183
+ }
184
+ const span = startTimeToFullDisplaySpan();
185
+ if (!span) {
186
+ logger.warn(`[TimeToDisplay] No span found or created, possibly performance is disabled.`);
187
+ return;
188
+ }
189
+ if (spanToJSON(span).timestamp) {
190
+ logger.warn(`[TimeToDisplay] ${spanToJSON(span).description} span already ended.`);
191
+ return;
192
+ }
193
+ span.end(frameTimestampSeconds);
194
+ span.setStatus('ok');
195
+ logger.debug(`[TimeToDisplay] ${spanToJSON(span).description} span updated with end timestamp.`);
196
+ setSpanDurationAsMeasurement('time_to_full_display', span);
197
+ }
198
+ //# sourceMappingURL=timetodisplay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timetodisplay.js","sourceRoot":"","sources":["../../../src/js/tracing/timetodisplay.tsx"],"names":[],"mappings":"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","sourcesContent":["import { getActiveSpan, Span as SpanClass, spanToJSON, startInactiveSpan } from '@sentry/core';\nimport type { Span,StartSpanOptions } from '@sentry/types';\nimport { fill, logger } from '@sentry/utils';\nimport * as React from 'react';\n\nimport { getRNSentryOnDrawReporter, nativeComponentExists } from './timetodisplaynative';\nimport type {RNSentryOnDrawNextFrameEvent } from './timetodisplaynative.types';\nimport { setSpanDurationAsMeasurement } from './utils';\n\nlet nativeComponentMissingLogged = false;\n\n/**\n * Flags of active spans with manual initial display.\n */\nexport const manualInitialDisplaySpans = new WeakMap<Span, true>();\n\n/**\n * Flag full display called before initial display for an active span.\n */\nconst fullDisplayBeforeInitialDisplay = new WeakMap<Span, true>();\n\nexport type TimeToDisplayProps = {\n children?: React.ReactNode;\n spanName?: string;\n record?: boolean;\n};\n\n/**\n * Component to measure time to initial display.\n *\n * The initial display is recorded when the component prop `record` is true.\n *\n * <TimeToInitialDisplay record />\n */\nexport function TimeToInitialDisplay(props: TimeToDisplayProps): React.ReactElement {\n const activeSpan = getActiveSpan();\n if (activeSpan) {\n manualInitialDisplaySpans.set(activeSpan, true);\n }\n\n return <TimeToDisplay initialDisplay={props.record}>{props.children}</TimeToDisplay>;\n}\n\n/**\n * Component to measure time to full display.\n *\n * The initial display is recorded when the component prop `record` is true.\n *\n * <TimeToInitialDisplay record />\n */\nexport function TimeToFullDisplay(props: TimeToDisplayProps): React.ReactElement {\n return <TimeToDisplay fullDisplay={props.record}>{props.children}</TimeToDisplay>;\n}\n\nfunction TimeToDisplay(props: {\n children?: React.ReactNode;\n initialDisplay?: boolean;\n fullDisplay?: boolean;\n}): React.ReactElement {\n const RNSentryOnDrawReporter = getRNSentryOnDrawReporter();\n\n if (__DEV__ && !nativeComponentMissingLogged && !nativeComponentExists) {\n nativeComponentMissingLogged = true;\n logger.error('RNSentryOnDrawReporter is not available on the web, Expo Go and New Architecture. Run native build or report an issue at https://github.com/getsentry/sentry-react-native');\n }\n\n const onDraw = (event: { nativeEvent: RNSentryOnDrawNextFrameEvent }): void => onDrawNextFrame(event);\n\n return (\n <>\n <RNSentryOnDrawReporter\n onDrawNextFrame={onDraw}\n initialDisplay={props.initialDisplay}\n fullDisplay={props.fullDisplay} />\n {props.children}\n </>\n );\n}\n\n/**\n * Starts a new span for the initial display.\n *\n * Returns current span if already exists in the currently active span.\n */\nexport function startTimeToInitialDisplaySpan(\n options?: Exclude<StartSpanOptions, 'op' | 'name'> & { name?: string; isAutoInstrumented?: boolean },\n): Span | undefined {\n const activeSpan = getActiveSpan();\n if (!activeSpan) {\n logger.warn(`[TimeToDisplay] No active span found to attach ui.load.initial_display to.`);\n return;\n }\n\n if (!(activeSpan instanceof SpanClass)) {\n logger.warn(`[TimeToDisplay] Active span is not instance of Span class.`);\n return;\n }\n\n const existingSpan = activeSpan.spanRecorder?.spans.find((span) => spanToJSON(span).op === 'ui.load.initial_display');\n if (existingSpan) {\n logger.debug(`[TimeToDisplay] Found existing ui.load.initial_display span.`);\n return existingSpan\n }\n\n const initialDisplaySpan = startInactiveSpan({\n op: 'ui.load.initial_display',\n name: 'Time To Initial Display',\n startTimestamp: spanToJSON(activeSpan).start_timestamp,\n ...options,\n });\n\n if (!initialDisplaySpan) {\n return;\n }\n\n if (!options?.isAutoInstrumented) {\n manualInitialDisplaySpans.set(activeSpan, true);\n }\n return initialDisplaySpan;\n}\n\n/**\n * Starts a new span for the full display.\n *\n * Returns current span if already exists in the currently active span.\n */\nexport function startTimeToFullDisplaySpan(\n options: Omit<StartSpanOptions, 'op' | 'name'> & { name?: string, timeoutMs?: number } = {\n timeoutMs: 30_000,\n },\n): Span | undefined {\n const activeSpan = getActiveSpan();\n if (!activeSpan) {\n logger.warn(`[TimeToDisplay] No active span found to attach ui.load.full_display to.`);\n return;\n }\n\n if (!(activeSpan instanceof SpanClass)) {\n logger.warn(`[TimeToDisplay] Active span is not instance of Span class.`);\n return;\n }\n\n const descendantSpans = activeSpan.spanRecorder?.spans || [];\n\n const initialDisplaySpan = descendantSpans.find((span) => spanToJSON(span).op === 'ui.load.initial_display');\n if (!initialDisplaySpan) {\n logger.warn(`[TimeToDisplay] No initial display span found to attach ui.load.full_display to.`);\n return;\n }\n\n const existingSpan = descendantSpans.find((span) => spanToJSON(span).op === 'ui.load.full_display');\n if (existingSpan) {\n logger.debug(`[TimeToDisplay] Found existing ui.load.full_display span.`);\n return existingSpan;\n }\n\n const fullDisplaySpan = startInactiveSpan({\n op: 'ui.load.full_display',\n name: 'Time To Full Display',\n startTimestamp: spanToJSON(initialDisplaySpan).start_timestamp,\n ...options,\n });\n if (!fullDisplaySpan) {\n return;\n }\n\n const timeout = setTimeout(() => {\n if (spanToJSON(fullDisplaySpan).timestamp) {\n return;\n }\n fullDisplaySpan.setStatus('deadline_exceeded');\n fullDisplaySpan.end(spanToJSON(initialDisplaySpan).timestamp);\n setSpanDurationAsMeasurement('time_to_full_display', fullDisplaySpan);\n logger.warn(`[TimeToDisplay] Full display span deadline_exceeded.`);\n }, options.timeoutMs);\n\n fill(fullDisplaySpan, 'end', (originalEnd: SpanClass['end']) => (endTimestamp?: Parameters<SpanClass['end']>[0]) => {\n clearTimeout(timeout);\n originalEnd.call(fullDisplaySpan, endTimestamp);\n });\n\n return fullDisplaySpan;\n}\n\nfunction onDrawNextFrame(event: { nativeEvent: RNSentryOnDrawNextFrameEvent }): void {\n logger.debug(`[TimeToDisplay] onDrawNextFrame: ${JSON.stringify(event.nativeEvent)}`);\n if (event.nativeEvent.type === 'fullDisplay') {\n return updateFullDisplaySpan(event.nativeEvent.newFrameTimestampInSeconds);\n }\n if (event.nativeEvent.type === 'initialDisplay') {\n return updateInitialDisplaySpan(event.nativeEvent.newFrameTimestampInSeconds);\n }\n}\n\nfunction updateInitialDisplaySpan(frameTimestampSeconds: number): void {\n const span = startTimeToInitialDisplaySpan();\n if (!span) {\n logger.warn(`[TimeToDisplay] No span found or created, possibly performance is disabled.`);\n return;\n }\n\n const activeSpan = getActiveSpan();\n if (!activeSpan) {\n logger.warn(`[TimeToDisplay] No active span found to attach ui.load.initial_display to.`);\n return;\n }\n\n if (spanToJSON(span).parent_span_id !== spanToJSON(activeSpan).span_id) {\n logger.warn(`[TimeToDisplay] Initial display span is not a child of current active span.`);\n return;\n }\n\n if (spanToJSON(span).timestamp) {\n logger.warn(`[TimeToDisplay] ${spanToJSON(span).description} span already ended.`);\n return;\n }\n\n span.end(frameTimestampSeconds);\n span.setStatus('ok');\n logger.debug(`[TimeToDisplay] ${spanToJSON(span).description} span updated with end timestamp.`);\n\n if (fullDisplayBeforeInitialDisplay.has(activeSpan)) {\n fullDisplayBeforeInitialDisplay.delete(activeSpan);\n updateFullDisplaySpan(frameTimestampSeconds, span);\n }\n\n setSpanDurationAsMeasurement('time_to_initial_display', span);\n}\n\nfunction updateFullDisplaySpan(frameTimestampSeconds: number, passedInitialDisplaySpan?: Span): void {\n const activeSpan = getActiveSpan();\n if (!activeSpan) {\n logger.warn(`[TimeToDisplay] No active span found to attach ui.load.full_display to.`);\n return;\n }\n\n if (!(activeSpan instanceof SpanClass)) {\n logger.warn(`[TimeToDisplay] Active span is not instance of Span class.`);\n return;\n }\n\n const existingInitialDisplaySpan = passedInitialDisplaySpan\n || activeSpan.spanRecorder?.spans.find((span) => spanToJSON(span).op === 'ui.load.initial_display');\n const initialDisplayEndTimestamp = existingInitialDisplaySpan && spanToJSON(existingInitialDisplaySpan).timestamp;\n if (!initialDisplayEndTimestamp) {\n fullDisplayBeforeInitialDisplay.set(activeSpan, true);\n logger.warn(`[TimeToDisplay] Full display called before initial display for active span.`);\n return;\n }\n\n const span = startTimeToFullDisplaySpan();\n if (!span) {\n logger.warn(`[TimeToDisplay] No span found or created, possibly performance is disabled.`);\n return;\n }\n\n if (spanToJSON(span).timestamp) {\n logger.warn(`[TimeToDisplay] ${spanToJSON(span).description} span already ended.`);\n return;\n }\n\n span.end(frameTimestampSeconds);\n\n span.setStatus('ok');\n logger.debug(`[TimeToDisplay] ${spanToJSON(span).description} span updated with end timestamp.`);\n\n setSpanDurationAsMeasurement('time_to_full_display', span);\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import * as React from 'react';
2
+ import type { HostComponent } from 'react-native';
3
+ import type { RNSentryOnDrawReporterProps } from './timetodisplaynative.types';
4
+ export declare const nativeComponentExists: boolean;
5
+ /**
6
+ * This is a fallback component for environments where the native component is not available.
7
+ */
8
+ declare class RNSentryOnDrawReporterNoop extends React.Component<RNSentryOnDrawReporterProps> {
9
+ render(): React.ReactNode;
10
+ }
11
+ declare let RNSentryOnDrawReporter: HostComponent<RNSentryOnDrawReporterProps> | typeof RNSentryOnDrawReporterNoop;
12
+ /**
13
+ * Native component that reports the on draw timestamp.
14
+ */
15
+ export declare const getRNSentryOnDrawReporter: () => typeof RNSentryOnDrawReporter;
16
+ export {};
17
+ //# sourceMappingURL=timetodisplaynative.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timetodisplaynative.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/timetodisplaynative.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAC,MAAM,cAAc,CAAC;AAGjD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAI/E,eAAO,MAAM,qBAAqB,SAEzB,CAAC;AAEV;;GAEG;AACH,cAAM,0BAA2B,SAAQ,KAAK,CAAC,SAAS,CAAC,2BAA2B,CAAC;IAC5E,MAAM,IAAI,KAAK,CAAC,SAAS;CAKjC;AAED,QAAA,IAAI,sBAAsB,EAAE,aAAa,CAAC,2BAA2B,CAAC,GAAG,OAAO,0BAA0B,CAAC;AAE3G;;GAEG;AACH,eAAO,MAAM,yBAAyB,QAAO,6BAO5C,CAAA"}
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ import { requireNativeComponent, UIManager, View } from 'react-native';
3
+ const RNSentryOnDrawReporterClass = 'RNSentryOnDrawReporter';
4
+ export const nativeComponentExists = UIManager.hasViewManagerConfig
5
+ ? UIManager.hasViewManagerConfig(RNSentryOnDrawReporterClass)
6
+ : false;
7
+ /**
8
+ * This is a fallback component for environments where the native component is not available.
9
+ */
10
+ class RNSentryOnDrawReporterNoop extends React.Component {
11
+ render() {
12
+ return (React.createElement(View, Object.assign({}, this.props)));
13
+ }
14
+ }
15
+ let RNSentryOnDrawReporter;
16
+ /**
17
+ * Native component that reports the on draw timestamp.
18
+ */
19
+ export const getRNSentryOnDrawReporter = () => {
20
+ if (!RNSentryOnDrawReporter) {
21
+ RNSentryOnDrawReporter = nativeComponentExists
22
+ ? requireNativeComponent(RNSentryOnDrawReporterClass)
23
+ : RNSentryOnDrawReporterNoop;
24
+ }
25
+ return RNSentryOnDrawReporter;
26
+ };
27
+ //# sourceMappingURL=timetodisplaynative.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timetodisplaynative.js","sourceRoot":"","sources":["../../../src/js/tracing/timetodisplaynative.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,sBAAsB,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAIvE,MAAM,2BAA2B,GAAG,wBAAwB,CAAC;AAE7D,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC,oBAAoB;IACjE,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,2BAA2B,CAAC;IAC7D,CAAC,CAAC,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,0BAA2B,SAAQ,KAAK,CAAC,SAAsC;IAC5E,MAAM;QACX,OAAO,CACL,oBAAC,IAAI,oBAAK,IAAI,CAAC,KAAK,EAAI,CACzB,CAAC;IACJ,CAAC;CACF;AAED,IAAI,sBAAsG,CAAC;AAE3G;;GAEG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAkC,EAAE;IAC3E,IAAI,CAAC,sBAAsB,EAAE;QAC3B,sBAAsB,GAAG,qBAAqB;YAC5C,CAAC,CAAC,sBAAsB,CAAC,2BAA2B,CAAC;YACrD,CAAC,CAAC,0BAA0B,CAAC;KAChC;IACD,OAAO,sBAAsB,CAAC;AAChC,CAAC,CAAA","sourcesContent":["import * as React from 'react';\nimport type { HostComponent} from 'react-native';\nimport { requireNativeComponent, UIManager, View } from 'react-native';\n\nimport type { RNSentryOnDrawReporterProps } from './timetodisplaynative.types';\n\nconst RNSentryOnDrawReporterClass = 'RNSentryOnDrawReporter';\n\nexport const nativeComponentExists = UIManager.hasViewManagerConfig\n ? UIManager.hasViewManagerConfig(RNSentryOnDrawReporterClass)\n : false;\n\n/**\n * This is a fallback component for environments where the native component is not available.\n */\nclass RNSentryOnDrawReporterNoop extends React.Component<RNSentryOnDrawReporterProps> {\n public render(): React.ReactNode {\n return (\n <View {...this.props} />\n );\n }\n}\n\nlet RNSentryOnDrawReporter: HostComponent<RNSentryOnDrawReporterProps> | typeof RNSentryOnDrawReporterNoop;\n\n/**\n * Native component that reports the on draw timestamp.\n */\nexport const getRNSentryOnDrawReporter = (): typeof RNSentryOnDrawReporter => {\n if (!RNSentryOnDrawReporter) {\n RNSentryOnDrawReporter = nativeComponentExists\n ? requireNativeComponent(RNSentryOnDrawReporterClass)\n : RNSentryOnDrawReporterNoop;\n }\n return RNSentryOnDrawReporter;\n}\n"]}
@@ -0,0 +1,14 @@
1
+ /// <reference types="react" />
2
+ export interface RNSentryOnDrawNextFrameEvent {
3
+ newFrameTimestampInSeconds: number;
4
+ type: 'initialDisplay' | 'fullDisplay';
5
+ }
6
+ export interface RNSentryOnDrawReporterProps {
7
+ children?: React.ReactNode;
8
+ onDrawNextFrame: (event: {
9
+ nativeEvent: RNSentryOnDrawNextFrameEvent;
10
+ }) => void;
11
+ initialDisplay?: boolean;
12
+ fullDisplay?: boolean;
13
+ }
14
+ //# sourceMappingURL=timetodisplaynative.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timetodisplaynative.types.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/timetodisplaynative.types.ts"],"names":[],"mappings":";AAAA,MAAM,WAAW,4BAA4B;IAC3C,0BAA0B,EAAE,MAAM,CAAC;IACnC,IAAI,EAAE,gBAAgB,GAAG,aAAa,CAAC;CACxC;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,eAAe,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,4BAA4B,CAAA;KAAE,KAAK,IAAI,CAAC;IAChF,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=timetodisplaynative.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timetodisplaynative.types.js","sourceRoot":"","sources":["../../../src/js/tracing/timetodisplaynative.types.ts"],"names":[],"mappings":"","sourcesContent":["export interface RNSentryOnDrawNextFrameEvent {\n newFrameTimestampInSeconds: number;\n type: 'initialDisplay' | 'fullDisplay';\n}\n\nexport interface RNSentryOnDrawReporterProps {\n children?: React.ReactNode;\n onDrawNextFrame: (event: { nativeEvent: RNSentryOnDrawNextFrameEvent }) => void;\n initialDisplay?: boolean;\n fullDisplay?: boolean;\n}\n"]}
@@ -1,5 +1,5 @@
1
- import type { IdleTransaction, Span, Transaction } from '@sentry/core';
2
- import type { TransactionContext, TransactionSource } from '@sentry/types';
1
+ import { type IdleTransaction, type Span as SpanClass, type Transaction } from '@sentry/core';
2
+ import type { Span, TransactionContext, TransactionSource } from '@sentry/types';
3
3
  export declare const defaultTransactionSource: TransactionSource;
4
4
  export declare const customTransactionSource: TransactionSource;
5
5
  export declare const getBlankTransactionContext: (name: string) => TransactionContext;
@@ -19,9 +19,14 @@ export declare function getTimeOriginMilliseconds(): number;
19
19
  /**
20
20
  * Calls the callback every time a child span of the transaction is finished.
21
21
  */
22
- export declare function instrumentChildSpanFinish(transaction: Transaction, callback: (span: Span, endTimestamp?: number) => void): void;
22
+ export declare function instrumentChildSpanFinish(transaction: Transaction, callback: (span: SpanClass, endTimestamp?: number) => void): void;
23
23
  /**
24
24
  * Determines if the timestamp is now or within the specified margin of error from now.
25
25
  */
26
26
  export declare function isNearToNow(timestamp: number): boolean;
27
+ /**
28
+ * Sets the duration of the span as a measurement.
29
+ * Uses `setMeasurement` function from @sentry/core.
30
+ */
31
+ export declare function setSpanDurationAsMeasurement(name: string, span: Span): void;
27
32
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAG3E,eAAO,MAAM,wBAAwB,EAAE,iBAA+B,CAAC;AACvE,eAAO,MAAM,uBAAuB,EAAE,iBAA4B,CAAC;AAEnE,eAAO,MAAM,0BAA0B,SAAU,MAAM,KAAG,kBAYzD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAI5C;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,eAAe,EAC5B,YAAY,EAAE,MAAM,GACnB,IAAI,CAON;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,GACpD,IAAI,CAkBN;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEtD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,IAAI,IAAI,SAAS,EACtB,KAAK,WAAW,EAGjB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGjF,eAAO,MAAM,wBAAwB,EAAE,iBAA+B,CAAC;AACvE,eAAO,MAAM,uBAAuB,EAAE,iBAA4B,CAAC;AAEnE,eAAO,MAAM,0BAA0B,SAAU,MAAM,KAAG,kBAYzD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAI5C;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,eAAe,EAC5B,YAAY,EAAE,MAAM,GACnB,IAAI,CAON;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,GACzD,IAAI,CA2BN;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAQ3E"}
@@ -1,3 +1,4 @@
1
+ import { setMeasurement, spanToJSON, } from '@sentry/core';
1
2
  import { timestampInSeconds } from '@sentry/utils';
2
3
  export const defaultTransactionSource = 'component';
3
4
  export const customTransactionSource = 'custom';
@@ -52,6 +53,12 @@ export function instrumentChildSpanFinish(transaction, callback) {
52
53
  originalSpanFinish.apply(span, [endTimestamp]);
53
54
  callback(span, endTimestamp);
54
55
  };
56
+ // eslint-disable-next-line @typescript-eslint/unbound-method
57
+ const originalSpanEnd = span.end;
58
+ span.end = (endTimestamp) => {
59
+ originalSpanEnd.apply(span, [endTimestamp]);
60
+ callback(span, endTimestamp);
61
+ };
55
62
  };
56
63
  }
57
64
  }
@@ -61,4 +68,16 @@ export function instrumentChildSpanFinish(transaction, callback) {
61
68
  export function isNearToNow(timestamp) {
62
69
  return Math.abs(timestampInSeconds() - timestamp) <= MARGIN_OF_ERROR_SECONDS;
63
70
  }
71
+ /**
72
+ * Sets the duration of the span as a measurement.
73
+ * Uses `setMeasurement` function from @sentry/core.
74
+ */
75
+ export function setSpanDurationAsMeasurement(name, span) {
76
+ const spanEnd = spanToJSON(span).timestamp;
77
+ const spanStart = spanToJSON(span).start_timestamp;
78
+ if (!spanEnd || !spanStart) {
79
+ return;
80
+ }
81
+ setMeasurement(name, (spanEnd - spanStart) * 1000, 'millisecond');
82
+ }
64
83
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/js/tracing/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,CAAC,MAAM,wBAAwB,GAAsB,WAAW,CAAC;AACvE,MAAM,CAAC,MAAM,uBAAuB,GAAsB,QAAQ,CAAC;AAEnE,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,IAAY,EAAsB,EAAE;IAC7E,OAAO;QACL,IAAI,EAAE,cAAc;QACpB,EAAE,EAAE,YAAY;QAChB,IAAI,EAAE;YACJ,yBAAyB,EAAE,IAAI;SAChC;QACD,IAAI,EAAE,EAAE;QACR,QAAQ,EAAE;YACR,MAAM,EAAE,wBAAwB;SACjC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAE5C,MAAM,sBAAsB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAE1C;;GAEG;AACH,MAAM,UAAU,yBAAyB,CACvC,aAAqB,EACrB,WAA4B,EAC5B,YAAoB;IAEpB,MAAM,IAAI,GAAG,YAAY,GAAG,WAAW,CAAC,cAAc,CAAC;IACvD,MAAM,qBAAqB,GAAG,YAAY,IAAI,CAAC,IAAI,GAAG,aAAa,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC;IACjF,IAAI,qBAAqB,EAAE;QACzB,WAAW,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC3C,WAAW,CAAC,MAAM,CAAC,gCAAgC,EAAE,MAAM,CAAC,CAAC;KAC9D;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB;IACvC,OAAO,sBAAsB,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB,CACvC,WAAwB,EACxB,QAAqD;IAErD,IAAI,WAAW,CAAC,YAAY,EAAE;QAC5B,6DAA6D;QAC7D,MAAM,WAAW,GAAG,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC;QAEjD,WAAW,CAAC,YAAY,CAAC,GAAG,GAAG,CAAC,IAAU,EAAQ,EAAE;YAClD,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;YAEpD,6DAA6D;YAC7D,MAAM,kBAAkB,GAAG,IAAI,CAAC,MAAM,CAAC;YAEvC,IAAI,CAAC,MAAM,GAAG,CAAC,YAAqB,EAAE,EAAE;gBACtC,kBAAkB,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;gBAE/C,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;YAC/B,CAAC,CAAC;QACJ,CAAC,CAAC;KACH;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,GAAG,SAAS,CAAC,IAAI,uBAAuB,CAAC;AAC/E,CAAC","sourcesContent":["import type { IdleTransaction, Span, Transaction } from '@sentry/core';\nimport type { TransactionContext, TransactionSource } from '@sentry/types';\nimport { timestampInSeconds } from '@sentry/utils';\n\nexport const defaultTransactionSource: TransactionSource = 'component';\nexport const customTransactionSource: TransactionSource = 'custom';\n\nexport const getBlankTransactionContext = (name: string): TransactionContext => {\n return {\n name: 'Route Change',\n op: 'navigation',\n tags: {\n 'routing.instrumentation': name,\n },\n data: {},\n metadata: {\n source: defaultTransactionSource,\n },\n };\n};\n\n/**\n * A margin of error of 50ms is allowed for the async native bridge call.\n * Anything larger would reduce the accuracy of our frames measurements.\n */\nexport const MARGIN_OF_ERROR_SECONDS = 0.05;\n\nconst timeOriginMilliseconds = Date.now();\n\n/**\n *\n */\nexport function adjustTransactionDuration(\n maxDurationMs: number,\n transaction: IdleTransaction,\n endTimestamp: number,\n): void {\n const diff = endTimestamp - transaction.startTimestamp;\n const isOutdatedTransaction = endTimestamp && (diff > maxDurationMs || diff < 0);\n if (isOutdatedTransaction) {\n transaction.setStatus('deadline_exceeded');\n transaction.setTag('maxTransactionDurationExceeded', 'true');\n }\n}\n\n/**\n * Returns the timestamp where the JS global scope was initialized.\n */\nexport function getTimeOriginMilliseconds(): number {\n return timeOriginMilliseconds;\n}\n\n/**\n * Calls the callback every time a child span of the transaction is finished.\n */\nexport function instrumentChildSpanFinish(\n transaction: Transaction,\n callback: (span: Span, endTimestamp?: number) => void,\n): void {\n if (transaction.spanRecorder) {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const originalAdd = transaction.spanRecorder.add;\n\n transaction.spanRecorder.add = (span: Span): void => {\n originalAdd.apply(transaction.spanRecorder, [span]);\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const originalSpanFinish = span.finish;\n\n span.finish = (endTimestamp?: number) => {\n originalSpanFinish.apply(span, [endTimestamp]);\n\n callback(span, endTimestamp);\n };\n };\n }\n}\n\n/**\n * Determines if the timestamp is now or within the specified margin of error from now.\n */\nexport function isNearToNow(timestamp: number): boolean {\n return Math.abs(timestampInSeconds() - timestamp) <= MARGIN_OF_ERROR_SECONDS;\n}\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/js/tracing/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type IdleTransaction,\n type Span as SpanClass,\n type Transaction,\n setMeasurement,\n spanToJSON,\n} from '@sentry/core';\nimport type { Span, TransactionContext, TransactionSource } from '@sentry/types';\nimport { timestampInSeconds } from '@sentry/utils';\n\nexport const defaultTransactionSource: TransactionSource = 'component';\nexport const customTransactionSource: TransactionSource = 'custom';\n\nexport const getBlankTransactionContext = (name: string): TransactionContext => {\n return {\n name: 'Route Change',\n op: 'navigation',\n tags: {\n 'routing.instrumentation': name,\n },\n data: {},\n metadata: {\n source: defaultTransactionSource,\n },\n };\n};\n\n/**\n * A margin of error of 50ms is allowed for the async native bridge call.\n * Anything larger would reduce the accuracy of our frames measurements.\n */\nexport const MARGIN_OF_ERROR_SECONDS = 0.05;\n\nconst timeOriginMilliseconds = Date.now();\n\n/**\n *\n */\nexport function adjustTransactionDuration(\n maxDurationMs: number,\n transaction: IdleTransaction,\n endTimestamp: number,\n): void {\n const diff = endTimestamp - transaction.startTimestamp;\n const isOutdatedTransaction = endTimestamp && (diff > maxDurationMs || diff < 0);\n if (isOutdatedTransaction) {\n transaction.setStatus('deadline_exceeded');\n transaction.setTag('maxTransactionDurationExceeded', 'true');\n }\n}\n\n/**\n * Returns the timestamp where the JS global scope was initialized.\n */\nexport function getTimeOriginMilliseconds(): number {\n return timeOriginMilliseconds;\n}\n\n/**\n * Calls the callback every time a child span of the transaction is finished.\n */\nexport function instrumentChildSpanFinish(\n transaction: Transaction,\n callback: (span: SpanClass, endTimestamp?: number) => void,\n): void {\n if (transaction.spanRecorder) {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const originalAdd = transaction.spanRecorder.add;\n\n transaction.spanRecorder.add = (span: SpanClass): void => {\n originalAdd.apply(transaction.spanRecorder, [span]);\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const originalSpanFinish = span.finish;\n\n span.finish = (endTimestamp?: number) => {\n originalSpanFinish.apply(span, [endTimestamp]);\n\n callback(span, endTimestamp);\n };\n\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const originalSpanEnd = span.end;\n\n span.end = (endTimestamp?: number) => {\n originalSpanEnd.apply(span, [endTimestamp]);\n\n callback(span, endTimestamp);\n };\n };\n }\n}\n\n/**\n * Determines if the timestamp is now or within the specified margin of error from now.\n */\nexport function isNearToNow(timestamp: number): boolean {\n return Math.abs(timestampInSeconds() - timestamp) <= MARGIN_OF_ERROR_SECONDS;\n}\n\n/**\n * Sets the duration of the span as a measurement.\n * Uses `setMeasurement` function from @sentry/core.\n */\nexport function setSpanDurationAsMeasurement(name: string, span: Span): void {\n const spanEnd = spanToJSON(span).timestamp;\n const spanStart = spanToJSON(span).start_timestamp;\n if (!spanEnd || !spanStart) {\n return;\n }\n\n setMeasurement(name, (spanEnd - spanStart) * 1000, 'millisecond');\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import type { NativeModule } from 'react-native';
2
+ import { NativeEventEmitter } from 'react-native';
3
+ export declare const NewFrameEventName = "rn_sentry_new_frame";
4
+ export type NewFrameEventName = typeof NewFrameEventName;
5
+ export type NewFrameEvent = {
6
+ newFrameTimestampInSeconds: number;
7
+ };
8
+ export interface SentryEventEmitter {
9
+ /**
10
+ * Initializes the native event emitter
11
+ * This method is synchronous in JS but the native event emitter starts asynchronously
12
+ * https://github.com/facebook/react-native/blob/d09c02f9e2d468e4d0bde51890e312ae7003a3e6/packages/react-native/React/Modules/RCTEventEmitter.m#L95
13
+ */
14
+ initAsync: (eventType: NewFrameEventName) => void;
15
+ closeAllAsync: () => void;
16
+ addListener: (eventType: NewFrameEventName, listener: (event: NewFrameEvent) => void) => void;
17
+ removeListener: (eventType: NewFrameEventName, listener: (event: NewFrameEvent) => void) => void;
18
+ once: (eventType: NewFrameEventName, listener: (event: NewFrameEvent) => void) => void;
19
+ }
20
+ /**
21
+ * Creates emitter that allows to listen to native RNSentry events
22
+ */
23
+ export declare function createSentryEventEmitter(sentryNativeModule?: NativeModule | undefined, createNativeEventEmitter?: (nativeModule: NativeModule | undefined) => NativeEventEmitter): SentryEventEmitter;
24
+ //# sourceMappingURL=sentryeventemitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentryeventemitter.d.ts","sourceRoot":"","sources":["../../../src/js/utils/sentryeventemitter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAuB,YAAY,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAIlD,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AACvD,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAC;AACzD,MAAM,MAAM,aAAa,GAAG;IAAE,0BAA0B,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,SAAS,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAClD,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,WAAW,EAAE,CAAC,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,KAAK,IAAI,CAAC;IAC9F,cAAc,EAAE,CAAC,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,KAAK,IAAI,CAAC;IACjG,IAAI,EAAE,CAAC,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,KAAK,IAAI,CAAC;CACxF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,kBAAkB,GAAE,YAAY,GAAG,SAA+B,EAClE,wBAAwB,GAAE,CAAC,YAAY,EAAE,YAAY,GAAG,SAAS,KAAK,kBAChC,GACrC,kBAAkB,CA4DpB"}