@sentry/react-native 6.0.0-alpha.0 → 6.0.0-alpha.2

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 (87) hide show
  1. package/RNSentry.podspec +22 -15
  2. package/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +4 -0
  3. package/android/src/newarch/java/io/sentry/react/RNSentryModule.java +5 -0
  4. package/android/src/oldarch/java/io/sentry/react/RNSentryModule.java +5 -0
  5. package/dist/js/NativeRNSentry.d.ts +1 -0
  6. package/dist/js/NativeRNSentry.d.ts.map +1 -1
  7. package/dist/js/NativeRNSentry.js.map +1 -1
  8. package/dist/js/index.d.ts +1 -1
  9. package/dist/js/index.d.ts.map +1 -1
  10. package/dist/js/index.js +1 -1
  11. package/dist/js/index.js.map +1 -1
  12. package/dist/js/integrations/default.d.ts.map +1 -1
  13. package/dist/js/integrations/default.js +4 -4
  14. package/dist/js/integrations/default.js.map +1 -1
  15. package/dist/js/integrations/exports.d.ts +2 -1
  16. package/dist/js/integrations/exports.d.ts.map +1 -1
  17. package/dist/js/integrations/exports.js +2 -1
  18. package/dist/js/integrations/exports.js.map +1 -1
  19. package/dist/js/options.d.ts +6 -2
  20. package/dist/js/options.d.ts.map +1 -1
  21. package/dist/js/options.js.map +1 -1
  22. package/dist/js/profiling/integration.js.map +1 -1
  23. package/dist/js/replay/networkUtils.d.ts +0 -1
  24. package/dist/js/replay/networkUtils.d.ts.map +1 -1
  25. package/dist/js/sdk.d.ts +4 -0
  26. package/dist/js/sdk.d.ts.map +1 -1
  27. package/dist/js/sdk.js +8 -0
  28. package/dist/js/sdk.js.map +1 -1
  29. package/dist/js/tools/metroconfig.d.ts +1 -1
  30. package/dist/js/tools/metroconfig.d.ts.map +1 -1
  31. package/dist/js/tools/metroconfig.js.map +1 -1
  32. package/dist/js/tools/sentryMetroSerializer.d.ts.map +1 -1
  33. package/dist/js/tools/sentryMetroSerializer.js +1 -0
  34. package/dist/js/tools/sentryMetroSerializer.js.map +1 -1
  35. package/dist/js/tools/utils.d.ts.map +1 -1
  36. package/dist/js/tools/utils.js +1 -1
  37. package/dist/js/tools/utils.js.map +1 -1
  38. package/dist/js/tools/vendor/metro/utils.d.ts.map +1 -1
  39. package/dist/js/tools/vendor/metro/utils.js +3 -0
  40. package/dist/js/tools/vendor/metro/utils.js.map +1 -1
  41. package/dist/js/tracing/integrations/nativeFrames.d.ts +1 -0
  42. package/dist/js/tracing/integrations/nativeFrames.d.ts.map +1 -1
  43. package/dist/js/tracing/integrations/nativeFrames.js +132 -193
  44. package/dist/js/tracing/integrations/nativeFrames.js.map +1 -1
  45. package/dist/js/tracing/reactnativetracing.js +1 -1
  46. package/dist/js/tracing/reactnativetracing.js.map +1 -1
  47. package/dist/js/tracing/reactnavigation.d.ts.map +1 -1
  48. package/dist/js/tracing/reactnavigation.js.map +1 -1
  49. package/dist/js/tracing/timetodisplaynative.types.d.ts +0 -1
  50. package/dist/js/tracing/timetodisplaynative.types.d.ts.map +1 -1
  51. package/dist/js/utils/AsyncExpiringMap.d.ts +56 -0
  52. package/dist/js/utils/AsyncExpiringMap.d.ts.map +1 -0
  53. package/dist/js/utils/AsyncExpiringMap.js +130 -0
  54. package/dist/js/utils/AsyncExpiringMap.js.map +1 -0
  55. package/dist/js/vendor/react-native/index.d.ts +0 -1
  56. package/dist/js/vendor/react-native/index.d.ts.map +1 -1
  57. package/dist/js/vendor/react-native/index.js.map +1 -1
  58. package/dist/js/version.d.ts +1 -1
  59. package/dist/js/version.js +1 -1
  60. package/dist/js/version.js.map +1 -1
  61. package/dist/js/wrapper.d.ts +1 -0
  62. package/dist/js/wrapper.d.ts.map +1 -1
  63. package/dist/js/wrapper.js +12 -0
  64. package/dist/js/wrapper.js.map +1 -1
  65. package/ios/RNSentry.mm +6 -4
  66. package/ios/RNSentryReplay.m +4 -0
  67. package/package.json +24 -26
  68. package/plugin/build/utils.js +6 -6
  69. package/plugin/build/withSentry.js +2 -2
  70. package/plugin/build/withSentryAndroid.js +2 -2
  71. package/plugin/build/withSentryIOS.js +3 -3
  72. package/src/js/NativeRNSentry.ts +1 -0
  73. package/ts3.8/dist/js/NativeRNSentry.d.ts +1 -0
  74. package/ts3.8/dist/js/index.d.ts +1 -1
  75. package/ts3.8/dist/js/integrations/exports.d.ts +2 -1
  76. package/ts3.8/dist/js/options.d.ts +6 -2
  77. package/ts3.8/dist/js/replay/networkUtils.d.ts +0 -1
  78. package/ts3.8/dist/js/sdk.d.ts +4 -0
  79. package/ts3.8/dist/js/tracing/integrations/nativeFrames.d.ts +1 -0
  80. package/ts3.8/dist/js/tracing/timetodisplaynative.types.d.ts +0 -1
  81. package/ts3.8/dist/js/utils/AsyncExpiringMap.d.ts +56 -0
  82. package/ts3.8/dist/js/vendor/react-native/index.d.ts +0 -1
  83. package/ts3.8/dist/js/version.d.ts +1 -1
  84. package/ts3.8/dist/js/wrapper.d.ts +1 -0
  85. package/CHANGELOG.md +0 -3332
  86. package/android/.gitignore +0 -302
  87. package/samples/react-native/react-native.config.js +0 -17
@@ -1,256 +1,195 @@
1
1
  import { __awaiter } from "tslib";
2
- import { spanToJSON } from '@sentry/core';
3
2
  import { logger, timestampInSeconds } from '@sentry/utils';
3
+ import { AsyncExpiringMap } from '../../utils/AsyncExpiringMap';
4
4
  import { isRootSpan } from '../../utils/span';
5
5
  import { NATIVE } from '../../wrapper';
6
6
  /**
7
- * Timeout from the final native frames fetch to processing the associated transaction.
8
- * If the transaction is not processed by this time, the native frames will be dropped
9
- * and not added to the event.
7
+ * Timeout from the start of a span to fetching the associated native frames.
10
8
  */
11
- const FINAL_FRAMES_TIMEOUT_MS = 2000;
12
- /** The listeners for each native frames response, keyed by traceId. This must be global to avoid closure issues and reading outdated values. */
13
- const _framesListeners = new Map();
14
- /** The native frames at the transaction finish time, keyed by traceId. This must be global to avoid closure issues and reading outdated values. */
15
- const _finishFrames = new Map();
9
+ const FETCH_FRAMES_TIMEOUT_MS = 2000;
10
+ /**
11
+ * This is the time end frames data from the native layer will be
12
+ * kept in memory and waiting for the event processing. This ensures that spans
13
+ * which are never processed are not leaking memory.
14
+ */
15
+ const END_FRAMES_TIMEOUT_MS = 2000;
16
+ /**
17
+ * This is the time start frames data from the native layer will be
18
+ * kept in memory and waiting for span end. This ensures that spans
19
+ * which never end or are not processed are not leaking memory.
20
+ */
21
+ const START_FRAMES_TIMEOUT_MS = 60000;
16
22
  /**
17
23
  * A margin of error of 50ms is allowed for the async native bridge call.
18
24
  * Anything larger would reduce the accuracy of our frames measurements.
19
25
  */
20
26
  const MARGIN_OF_ERROR_SECONDS = 0.05;
27
+ const INTEGRATION_NAME = 'NativeFrames';
28
+ export const createNativeFramesIntegrations = (enable) => {
29
+ if (!enable && NATIVE.enableNative) {
30
+ // On Android this will free up resource when JS reloaded (native modules stay) and thus JS side of the SDK reinitialized.
31
+ NATIVE.disableNativeFramesTracking();
32
+ return undefined;
33
+ }
34
+ return nativeFramesIntegration();
35
+ };
21
36
  /**
22
37
  * Instrumentation to add native slow/frozen frames measurements onto transactions.
23
38
  */
24
39
  export const nativeFramesIntegration = () => {
25
- const name = 'NativeFrames';
26
40
  /** The native frames at the finish time of the most recent span. */
27
- let _lastSpanFinishFrames = undefined;
28
- const _spanToNativeFramesAtStartMap = new Map();
41
+ let _lastChildSpanEndFrames = null;
42
+ const _spanToNativeFramesAtStartMap = new AsyncExpiringMap({
43
+ ttl: START_FRAMES_TIMEOUT_MS,
44
+ });
45
+ const _spanToNativeFramesAtEndMap = new AsyncExpiringMap({ ttl: END_FRAMES_TIMEOUT_MS });
29
46
  /**
30
47
  * Hooks into the client start and end span events.
31
48
  */
32
49
  const setup = (client) => {
33
- const { enableNativeFramesTracking } = client.getOptions();
34
- if (enableNativeFramesTracking && !NATIVE.enableNative) {
35
- // Do not enable native frames tracking if native is not available.
36
- logger.warn('[ReactNativeTracing] NativeFramesTracking is not available on the Web, Expo Go and other platforms without native modules.');
37
- return;
38
- }
39
- if (!enableNativeFramesTracking && NATIVE.enableNative) {
40
- // Disable native frames tracking when native available and option is false.
41
- NATIVE.disableNativeFramesTracking();
42
- return;
43
- }
44
- if (!enableNativeFramesTracking) {
45
- return;
50
+ if (!NATIVE.enableNative) {
51
+ logger.warn(`[${INTEGRATION_NAME}] This is not available on the Web, Expo Go and other platforms without native modules.`);
52
+ return undefined;
46
53
  }
47
54
  NATIVE.enableNativeFramesTracking();
48
- client.on('spanStart', _onSpanStart);
49
- client.on('spanEnd', _onSpanFinish);
50
- logger.log('[ReactNativeTracing] Native frames instrumentation initialized.');
51
- };
52
- /**
53
- * Adds frames measurements to an event. Called from a valid event processor.
54
- * Awaits for finish frames if needed.
55
- */
56
- const processEvent = (event) => {
57
- return _processEvent(event);
55
+ client.on('spanStart', fetchStartFramesForSpan);
56
+ client.on('spanEnd', fetchEndFramesForSpan);
58
57
  };
59
- /**
60
- * Fetches the native frames in background if the given span is a root span.
61
- *
62
- * @param {Span} rootSpan - The span that has started.
63
- */
64
- const _onSpanStart = (rootSpan) => {
58
+ const fetchStartFramesForSpan = (rootSpan) => {
65
59
  if (!isRootSpan(rootSpan)) {
66
60
  return;
67
61
  }
68
- logger.debug(`[NativeFrames] Fetching frames for root span start (${rootSpan.spanContext().spanId}).`);
69
- NATIVE.fetchNativeFrames()
70
- .then(frames => {
71
- if (!frames) {
72
- logger.warn(`[NativeFrames] Fetched frames for root span start (${rootSpan.spanContext().spanId}), but no frames were returned.`);
73
- return;
74
- }
75
- _spanToNativeFramesAtStartMap.set(rootSpan.spanContext().traceId, frames);
76
- })
77
- .then(undefined, error => {
78
- logger.error(`[NativeFrames] Error while fetching frames for root span start (${rootSpan.spanContext().spanId})`, error);
79
- });
62
+ const spanId = rootSpan.spanContext().spanId;
63
+ logger.debug(`[${INTEGRATION_NAME}] Fetching frames for root span start (${spanId}).`);
64
+ _spanToNativeFramesAtStartMap.set(spanId, new Promise(resolve => {
65
+ fetchNativeFrames()
66
+ .then(frames => resolve(frames))
67
+ .then(undefined, error => {
68
+ logger.debug(`[${INTEGRATION_NAME}] Error while fetching native frames.`, error);
69
+ resolve(null);
70
+ });
71
+ }));
80
72
  };
81
- /**
82
- * Called on a span finish to fetch native frames to support transactions with trimEnd.
83
- * Only to be called when a span does not have an end timestamp.
84
- */
85
- const _onSpanFinish = (span) => {
86
- if (isRootSpan(span)) {
87
- return _onTransactionFinish(span);
88
- }
73
+ const fetchEndFramesForSpan = (span) => {
89
74
  const timestamp = timestampInSeconds();
90
- void NATIVE.fetchNativeFrames()
91
- .then(frames => {
92
- if (!frames) {
75
+ const spanId = span.spanContext().spanId;
76
+ if (isRootSpan(span)) {
77
+ const hasStartFrames = _spanToNativeFramesAtStartMap.has(spanId);
78
+ if (!hasStartFrames) {
79
+ // We don't have start frames, won't be able to calculate the difference.
93
80
  return;
94
81
  }
95
- _lastSpanFinishFrames = {
96
- timestamp,
97
- nativeFrames: frames,
98
- };
99
- })
100
- .then(undefined, error => {
101
- logger.error(`[NativeFrames] Error while fetching frames for child span end.`, error);
102
- });
103
- };
104
- /**
105
- * To be called when a transaction is finished
106
- */
107
- const _onTransactionFinish = (span) => {
108
- _fetchFramesForTransaction(span).then(undefined, (reason) => {
109
- logger.error(`[NativeFrames] Error while fetching frames for root span start (${span.spanContext().spanId})`, reason);
110
- });
111
- };
112
- /**
113
- * Returns the computed frames measurements and awaits for them if they are not ready yet.
114
- */
115
- const _getFramesMeasurements = (traceId, finalEndTimestamp, startFrames) => {
116
- if (_finishFrames.has(traceId)) {
117
- logger.debug(`[NativeFrames] Native end frames already fetched for trace id (${traceId}).`);
118
- return Promise.resolve(_prepareMeasurements(traceId, finalEndTimestamp, startFrames));
82
+ logger.debug(`[${INTEGRATION_NAME}] Fetch frames for root span end (${spanId}).`);
83
+ _spanToNativeFramesAtEndMap.set(spanId, new Promise(resolve => {
84
+ fetchNativeFrames()
85
+ .then(frames => {
86
+ resolve({
87
+ timestamp,
88
+ nativeFrames: frames,
89
+ });
90
+ })
91
+ .then(undefined, error => {
92
+ logger.debug(`[${INTEGRATION_NAME}] Error while fetching native frames.`, error);
93
+ resolve(null);
94
+ });
95
+ }));
96
+ return undefined;
97
+ }
98
+ else {
99
+ logger.debug(`[${INTEGRATION_NAME}] Fetch frames for child span end (${spanId}).`);
100
+ fetchNativeFrames()
101
+ .then(frames => {
102
+ _lastChildSpanEndFrames = {
103
+ timestamp,
104
+ nativeFrames: frames,
105
+ };
106
+ })
107
+ .catch(error => logger.debug(`[${INTEGRATION_NAME}] Error while fetching native frames.`, error));
119
108
  }
120
- return new Promise(resolve => {
121
- const timeout = setTimeout(() => {
122
- logger.debug(`[NativeFrames] Native end frames listener removed by timeout for trace id (${traceId}).`);
123
- _framesListeners.delete(traceId);
124
- resolve(null);
125
- }, 2000);
126
- _framesListeners.set(traceId, () => {
127
- logger.debug(`[NativeFrames] Native end frames listener called for trace id (${traceId}).`);
128
- resolve(_prepareMeasurements(traceId, finalEndTimestamp, startFrames));
129
- clearTimeout(timeout);
130
- _framesListeners.delete(traceId);
131
- });
132
- });
133
109
  };
134
- /**
135
- * Returns the computed frames measurements given ready data
136
- */
137
- const _prepareMeasurements = (traceId, finalEndTimestamp, // The actual transaction finish time.
138
- startFrames) => {
139
- let finalFinishFrames;
140
- const finish = _finishFrames.get(traceId);
141
- if (finish &&
142
- finish.nativeFrames &&
110
+ const processEvent = (event) => __awaiter(void 0, void 0, void 0, function* () {
111
+ var _a;
112
+ if (event.type !== 'transaction' ||
113
+ !event.transaction ||
114
+ !event.contexts ||
115
+ !event.contexts.trace ||
116
+ !event.timestamp ||
117
+ !event.contexts.trace.span_id) {
118
+ return event;
119
+ }
120
+ const traceOp = event.contexts.trace.op;
121
+ const spanId = event.contexts.trace.span_id;
122
+ const startFrames = yield _spanToNativeFramesAtStartMap.pop(spanId);
123
+ if (!startFrames) {
124
+ logger.warn(`[${INTEGRATION_NAME}] Start frames of transaction ${event.transaction} (eventId, ${event.event_id}) are missing, but the transaction already ended.`);
125
+ return event;
126
+ }
127
+ const endFrames = yield _spanToNativeFramesAtEndMap.pop(spanId);
128
+ let finalEndFrames;
129
+ if (endFrames && isClose(endFrames.timestamp, event.timestamp)) {
143
130
  // Must be in the margin of error of the actual transaction finish time (finalEndTimestamp)
144
- Math.abs(finish.timestamp - finalEndTimestamp) < MARGIN_OF_ERROR_SECONDS) {
145
- logger.debug(`[NativeFrames] Using frames from root span end (traceId, ${traceId}).`);
146
- finalFinishFrames = finish.nativeFrames;
131
+ logger.debug(`[${INTEGRATION_NAME}] Using frames from root span end (spanId, ${spanId}).`);
132
+ finalEndFrames = endFrames.nativeFrames;
147
133
  }
148
- else if (_lastSpanFinishFrames &&
149
- Math.abs(_lastSpanFinishFrames.timestamp - finalEndTimestamp) < MARGIN_OF_ERROR_SECONDS) {
134
+ else if (_lastChildSpanEndFrames && isClose(_lastChildSpanEndFrames.timestamp, event.timestamp)) {
150
135
  // Fallback to the last span finish if it is within the margin of error of the actual finish timestamp.
151
136
  // This should be the case for trimEnd.
152
- logger.debug(`[NativeFrames] Using native frames from last span end (traceId, ${traceId}).`);
153
- finalFinishFrames = _lastSpanFinishFrames.nativeFrames;
137
+ logger.debug(`[${INTEGRATION_NAME}] Using native frames from last child span end (spanId, ${spanId}).`);
138
+ finalEndFrames = _lastChildSpanEndFrames.nativeFrames;
154
139
  }
155
140
  else {
156
- logger.warn(`[NativeFrames] Frames were collected within larger than margin of error delay for traceId (${traceId}). Dropping the inaccurate values.`);
157
- return null;
141
+ logger.warn(`[${INTEGRATION_NAME}] Frames were collected within larger than margin of error delay for spanId (${spanId}). Dropping the inaccurate values.`);
142
+ return event;
158
143
  }
159
144
  const measurements = {
160
145
  frames_total: {
161
- value: finalFinishFrames.totalFrames - startFrames.totalFrames,
146
+ value: finalEndFrames.totalFrames - startFrames.totalFrames,
162
147
  unit: 'none',
163
148
  },
164
149
  frames_frozen: {
165
- value: finalFinishFrames.frozenFrames - startFrames.frozenFrames,
150
+ value: finalEndFrames.frozenFrames - startFrames.frozenFrames,
166
151
  unit: 'none',
167
152
  },
168
153
  frames_slow: {
169
- value: finalFinishFrames.slowFrames - startFrames.slowFrames,
154
+ value: finalEndFrames.slowFrames - startFrames.slowFrames,
170
155
  unit: 'none',
171
156
  },
172
157
  };
173
158
  if (measurements.frames_frozen.value <= 0 &&
174
159
  measurements.frames_slow.value <= 0 &&
175
160
  measurements.frames_total.value <= 0) {
176
- logger.warn(`[NativeFrames] Detected zero slow or frozen frames. Not adding measurements to traceId (${traceId}).`);
177
- return null;
178
- }
179
- return measurements;
180
- };
181
- /**
182
- * Fetch finish frames for a transaction at the current time. Calls any awaiting listeners.
183
- */
184
- const _fetchFramesForTransaction = (span) => __awaiter(void 0, void 0, void 0, function* () {
185
- var _a;
186
- const traceId = spanToJSON(span).trace_id;
187
- if (!traceId) {
188
- return;
189
- }
190
- const startFrames = _spanToNativeFramesAtStartMap.get(span.spanContext().traceId);
191
- // This timestamp marks when the finish frames were retrieved. It should be pretty close to the transaction finish.
192
- const timestamp = timestampInSeconds();
193
- let finishFrames = null;
194
- if (startFrames) {
195
- finishFrames = yield NATIVE.fetchNativeFrames();
196
- }
197
- _finishFrames.set(traceId, {
198
- nativeFrames: finishFrames,
199
- timestamp,
200
- });
201
- (_a = _framesListeners.get(traceId)) === null || _a === void 0 ? void 0 : _a();
202
- setTimeout(() => _cancelEndFrames(span), FINAL_FRAMES_TIMEOUT_MS);
203
- });
204
- /**
205
- * On a finish frames failure, we cancel the await.
206
- */
207
- const _cancelEndFrames = (span) => {
208
- const spanJSON = spanToJSON(span);
209
- const traceId = spanJSON.trace_id;
210
- if (!traceId) {
211
- return;
212
- }
213
- if (_finishFrames.has(traceId)) {
214
- _finishFrames.delete(traceId);
215
- logger.log(`[NativeFrames] Native frames timed out for ${spanJSON.op} transaction ${spanJSON.description}. Not adding native frames measurements.`);
216
- }
217
- };
218
- /**
219
- * Adds frames measurements to an event. Called from a valid event processor.
220
- * Awaits for finish frames if needed.
221
- */
222
- const _processEvent = (event) => __awaiter(void 0, void 0, void 0, function* () {
223
- var _b;
224
- if (event.type !== 'transaction' ||
225
- !event.transaction ||
226
- !event.contexts ||
227
- !event.contexts.trace ||
228
- !event.timestamp ||
229
- !event.contexts.trace.trace_id) {
230
- return event;
231
- }
232
- const traceOp = event.contexts.trace.op;
233
- const traceId = event.contexts.trace.trace_id;
234
- const startFrames = _spanToNativeFramesAtStartMap.get(traceId);
235
- _spanToNativeFramesAtStartMap.delete(traceId);
236
- if (!startFrames) {
237
- logger.warn(`[NativeFrames] Start frames of transaction ${event.transaction} (eventId, ${event.event_id}) are missing, but it already ended.`);
161
+ logger.warn(`[${INTEGRATION_NAME}] Detected zero slow or frozen frames. Not adding measurements to spanId (${spanId}).`);
238
162
  return event;
239
163
  }
240
- const measurements = yield _getFramesMeasurements(traceId, event.timestamp, startFrames);
241
- if (!measurements) {
242
- logger.log(`[NativeFrames] Could not fetch native frames for ${traceOp} transaction ${event.transaction}. Not adding native frames measurements.`);
243
- return event;
244
- }
245
- logger.log(`[Measurements] Adding measurements to ${traceOp} transaction ${event.transaction}: ${JSON.stringify(measurements, undefined, 2)}`);
246
- event.measurements = Object.assign(Object.assign({}, ((_b = event.measurements) !== null && _b !== void 0 ? _b : {})), measurements);
247
- _finishFrames.delete(traceId);
164
+ logger.log(`[${INTEGRATION_NAME}] Adding measurements to ${traceOp} transaction ${event.transaction}: ${JSON.stringify(measurements, undefined, 2)}`);
165
+ event.measurements = Object.assign(Object.assign({}, ((_a = event.measurements) !== null && _a !== void 0 ? _a : {})), measurements);
248
166
  return event;
249
167
  });
250
168
  return {
251
- name,
169
+ name: INTEGRATION_NAME,
252
170
  setup,
253
171
  processEvent,
254
172
  };
255
173
  };
174
+ function fetchNativeFrames() {
175
+ return new Promise((resolve, reject) => {
176
+ NATIVE.fetchNativeFrames()
177
+ .then(value => {
178
+ if (!value) {
179
+ reject('Native frames response is null.');
180
+ return;
181
+ }
182
+ resolve(value);
183
+ })
184
+ .then(undefined, error => {
185
+ reject(error);
186
+ });
187
+ setTimeout(() => {
188
+ reject('Fetching native frames took too long. Dropping frames.');
189
+ }, FETCH_FRAMES_TIMEOUT_MS);
190
+ });
191
+ }
192
+ function isClose(t1, t2) {
193
+ return Math.abs(t1 - t2) < MARGIN_OF_ERROR_SECONDS;
194
+ }
256
195
  //# sourceMappingURL=nativeFrames.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nativeFrames.js","sourceRoot":"","sources":["../../../../src/js/tracing/integrations/nativeFrames.ts"],"names":[],"mappings":"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","sourcesContent":["import { spanToJSON } from '@sentry/core';\nimport type { Client, Event, Integration, Measurements, MeasurementUnit, Span } from '@sentry/types';\nimport { logger, timestampInSeconds } from '@sentry/utils';\n\nimport type { NativeFramesResponse } from '../../NativeRNSentry';\nimport type { ReactNativeClientOptions } from '../../options';\nimport { isRootSpan } from '../../utils/span';\nimport { NATIVE } from '../../wrapper';\n\n/**\n * Timeout from the final native frames fetch to processing the associated transaction.\n * If the transaction is not processed by this time, the native frames will be dropped\n * and not added to the event.\n */\nconst FINAL_FRAMES_TIMEOUT_MS = 2000;\n\nexport interface FramesMeasurements extends Measurements {\n frames_total: { value: number; unit: MeasurementUnit };\n frames_slow: { value: number; unit: MeasurementUnit };\n frames_frozen: { value: number; unit: MeasurementUnit };\n}\n\n/** The listeners for each native frames response, keyed by traceId. This must be global to avoid closure issues and reading outdated values. */\nconst _framesListeners: Map<string, () => void> = new Map();\n\n/** The native frames at the transaction finish time, keyed by traceId. This must be global to avoid closure issues and reading outdated values. */\nconst _finishFrames: Map<string, { timestamp: number; nativeFrames: NativeFramesResponse | null }> = new Map();\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 */\nconst MARGIN_OF_ERROR_SECONDS = 0.05;\n\n/**\n * Instrumentation to add native slow/frozen frames measurements onto transactions.\n */\nexport const nativeFramesIntegration = (): Integration => {\n const name: string = 'NativeFrames';\n\n /** The native frames at the finish time of the most recent span. */\n let _lastSpanFinishFrames:\n | {\n timestamp: number;\n nativeFrames: NativeFramesResponse;\n }\n | undefined = undefined;\n const _spanToNativeFramesAtStartMap: Map<string, NativeFramesResponse> = new Map();\n\n /**\n * Hooks into the client start and end span events.\n */\n const setup = (client: Client): void => {\n const { enableNativeFramesTracking } = client.getOptions() as ReactNativeClientOptions;\n\n if (enableNativeFramesTracking && !NATIVE.enableNative) {\n // Do not enable native frames tracking if native is not available.\n logger.warn(\n '[ReactNativeTracing] NativeFramesTracking is not available on the Web, Expo Go and other platforms without native modules.',\n );\n return;\n }\n\n if (!enableNativeFramesTracking && NATIVE.enableNative) {\n // Disable native frames tracking when native available and option is false.\n NATIVE.disableNativeFramesTracking();\n return;\n }\n\n if (!enableNativeFramesTracking) {\n return;\n }\n\n NATIVE.enableNativeFramesTracking();\n\n client.on('spanStart', _onSpanStart);\n client.on('spanEnd', _onSpanFinish);\n logger.log('[ReactNativeTracing] Native frames instrumentation initialized.');\n };\n\n /**\n * Adds frames measurements to an event. Called from a valid event processor.\n * Awaits for finish frames if needed.\n */\n const processEvent = (event: Event): Promise<Event> => {\n return _processEvent(event);\n };\n\n /**\n * Fetches the native frames in background if the given span is a root span.\n *\n * @param {Span} rootSpan - The span that has started.\n */\n const _onSpanStart = (rootSpan: Span): void => {\n if (!isRootSpan(rootSpan)) {\n return;\n }\n\n logger.debug(`[NativeFrames] Fetching frames for root span start (${rootSpan.spanContext().spanId}).`);\n\n NATIVE.fetchNativeFrames()\n .then(frames => {\n if (!frames) {\n logger.warn(\n `[NativeFrames] Fetched frames for root span start (${\n rootSpan.spanContext().spanId\n }), but no frames were returned.`,\n );\n return;\n }\n\n _spanToNativeFramesAtStartMap.set(rootSpan.spanContext().traceId, frames);\n })\n .then(undefined, error => {\n logger.error(\n `[NativeFrames] Error while fetching frames for root span start (${rootSpan.spanContext().spanId})`,\n error,\n );\n });\n };\n\n /**\n * Called on a span finish to fetch native frames to support transactions with trimEnd.\n * Only to be called when a span does not have an end timestamp.\n */\n const _onSpanFinish = (span: Span): void => {\n if (isRootSpan(span)) {\n return _onTransactionFinish(span);\n }\n\n const timestamp = timestampInSeconds();\n\n void NATIVE.fetchNativeFrames()\n .then(frames => {\n if (!frames) {\n return;\n }\n\n _lastSpanFinishFrames = {\n timestamp,\n nativeFrames: frames,\n };\n })\n .then(undefined, error => {\n logger.error(`[NativeFrames] Error while fetching frames for child span end.`, error);\n });\n };\n\n /**\n * To be called when a transaction is finished\n */\n const _onTransactionFinish = (span: Span): void => {\n _fetchFramesForTransaction(span).then(undefined, (reason: unknown) => {\n logger.error(\n `[NativeFrames] Error while fetching frames for root span start (${span.spanContext().spanId})`,\n reason,\n );\n });\n };\n\n /**\n * Returns the computed frames measurements and awaits for them if they are not ready yet.\n */\n const _getFramesMeasurements = (\n traceId: string,\n finalEndTimestamp: number,\n startFrames: NativeFramesResponse,\n ): Promise<FramesMeasurements | null> => {\n if (_finishFrames.has(traceId)) {\n logger.debug(`[NativeFrames] Native end frames already fetched for trace id (${traceId}).`);\n return Promise.resolve(_prepareMeasurements(traceId, finalEndTimestamp, startFrames));\n }\n\n return new Promise(resolve => {\n const timeout = setTimeout(() => {\n logger.debug(`[NativeFrames] Native end frames listener removed by timeout for trace id (${traceId}).`);\n _framesListeners.delete(traceId);\n\n resolve(null);\n }, 2000);\n\n _framesListeners.set(traceId, () => {\n logger.debug(`[NativeFrames] Native end frames listener called for trace id (${traceId}).`);\n resolve(_prepareMeasurements(traceId, finalEndTimestamp, startFrames));\n\n clearTimeout(timeout);\n _framesListeners.delete(traceId);\n });\n });\n };\n\n /**\n * Returns the computed frames measurements given ready data\n */\n const _prepareMeasurements = (\n traceId: string,\n finalEndTimestamp: number, // The actual transaction finish time.\n startFrames: NativeFramesResponse,\n ): FramesMeasurements | null => {\n let finalFinishFrames: NativeFramesResponse | undefined;\n\n const finish = _finishFrames.get(traceId);\n if (\n finish &&\n finish.nativeFrames &&\n // Must be in the margin of error of the actual transaction finish time (finalEndTimestamp)\n Math.abs(finish.timestamp - finalEndTimestamp) < MARGIN_OF_ERROR_SECONDS\n ) {\n logger.debug(`[NativeFrames] Using frames from root span end (traceId, ${traceId}).`);\n finalFinishFrames = finish.nativeFrames;\n } else if (\n _lastSpanFinishFrames &&\n Math.abs(_lastSpanFinishFrames.timestamp - finalEndTimestamp) < MARGIN_OF_ERROR_SECONDS\n ) {\n // Fallback to the last span finish if it is within the margin of error of the actual finish timestamp.\n // This should be the case for trimEnd.\n logger.debug(`[NativeFrames] Using native frames from last span end (traceId, ${traceId}).`);\n finalFinishFrames = _lastSpanFinishFrames.nativeFrames;\n } else {\n logger.warn(\n `[NativeFrames] Frames were collected within larger than margin of error delay for traceId (${traceId}). Dropping the inaccurate values.`,\n );\n return null;\n }\n\n const measurements = {\n frames_total: {\n value: finalFinishFrames.totalFrames - startFrames.totalFrames,\n unit: 'none',\n },\n frames_frozen: {\n value: finalFinishFrames.frozenFrames - startFrames.frozenFrames,\n unit: 'none',\n },\n frames_slow: {\n value: finalFinishFrames.slowFrames - startFrames.slowFrames,\n unit: 'none',\n },\n };\n\n if (\n measurements.frames_frozen.value <= 0 &&\n measurements.frames_slow.value <= 0 &&\n measurements.frames_total.value <= 0\n ) {\n logger.warn(\n `[NativeFrames] Detected zero slow or frozen frames. Not adding measurements to traceId (${traceId}).`,\n );\n return null;\n }\n\n return measurements;\n };\n\n /**\n * Fetch finish frames for a transaction at the current time. Calls any awaiting listeners.\n */\n const _fetchFramesForTransaction = async (span: Span): Promise<void> => {\n const traceId = spanToJSON(span).trace_id;\n if (!traceId) {\n return;\n }\n\n const startFrames = _spanToNativeFramesAtStartMap.get(span.spanContext().traceId);\n\n // This timestamp marks when the finish frames were retrieved. It should be pretty close to the transaction finish.\n const timestamp = timestampInSeconds();\n let finishFrames: NativeFramesResponse | null = null;\n if (startFrames) {\n finishFrames = await NATIVE.fetchNativeFrames();\n }\n\n _finishFrames.set(traceId, {\n nativeFrames: finishFrames,\n timestamp,\n });\n\n _framesListeners.get(traceId)?.();\n\n setTimeout(() => _cancelEndFrames(span), FINAL_FRAMES_TIMEOUT_MS);\n };\n\n /**\n * On a finish frames failure, we cancel the await.\n */\n const _cancelEndFrames = (span: Span): void => {\n const spanJSON = spanToJSON(span);\n const traceId = spanJSON.trace_id;\n if (!traceId) {\n return;\n }\n\n if (_finishFrames.has(traceId)) {\n _finishFrames.delete(traceId);\n\n logger.log(\n `[NativeFrames] Native frames timed out for ${spanJSON.op} transaction ${spanJSON.description}. Not adding native frames measurements.`,\n );\n }\n };\n\n /**\n * Adds frames measurements to an event. Called from a valid event processor.\n * Awaits for finish frames if needed.\n */\n const _processEvent = async (event: Event): Promise<Event> => {\n if (\n event.type !== 'transaction' ||\n !event.transaction ||\n !event.contexts ||\n !event.contexts.trace ||\n !event.timestamp ||\n !event.contexts.trace.trace_id\n ) {\n return event;\n }\n\n const traceOp = event.contexts.trace.op;\n const traceId = event.contexts.trace.trace_id;\n const startFrames = _spanToNativeFramesAtStartMap.get(traceId);\n _spanToNativeFramesAtStartMap.delete(traceId);\n if (!startFrames) {\n logger.warn(\n `[NativeFrames] Start frames of transaction ${event.transaction} (eventId, ${event.event_id}) are missing, but it already ended.`,\n );\n return event;\n }\n\n const measurements = await _getFramesMeasurements(traceId, event.timestamp, startFrames);\n\n if (!measurements) {\n logger.log(\n `[NativeFrames] Could not fetch native frames for ${traceOp} transaction ${event.transaction}. Not adding native frames measurements.`,\n );\n return event;\n }\n\n logger.log(\n `[Measurements] Adding measurements to ${traceOp} transaction ${event.transaction}: ${JSON.stringify(\n measurements,\n undefined,\n 2,\n )}`,\n );\n\n event.measurements = {\n ...(event.measurements ?? {}),\n ...measurements,\n };\n\n _finishFrames.delete(traceId);\n\n return event;\n };\n\n return {\n name,\n setup,\n processEvent,\n };\n};\n"]}
1
+ {"version":3,"file":"nativeFrames.js","sourceRoot":"","sources":["../../../../src/js/tracing/integrations/nativeFrames.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Client, Event, Integration, Measurements, MeasurementUnit, Span } from '@sentry/types';\nimport { logger, timestampInSeconds } from '@sentry/utils';\n\nimport type { NativeFramesResponse } from '../../NativeRNSentry';\nimport { AsyncExpiringMap } from '../../utils/AsyncExpiringMap';\nimport { isRootSpan } from '../../utils/span';\nimport { NATIVE } from '../../wrapper';\n\n/**\n * Timeout from the start of a span to fetching the associated native frames.\n */\nconst FETCH_FRAMES_TIMEOUT_MS = 2_000;\n\n/**\n * This is the time end frames data from the native layer will be\n * kept in memory and waiting for the event processing. This ensures that spans\n * which are never processed are not leaking memory.\n */\nconst END_FRAMES_TIMEOUT_MS = 2_000;\n\n/**\n * This is the time start frames data from the native layer will be\n * kept in memory and waiting for span end. This ensures that spans\n * which never end or are not processed are not leaking memory.\n */\nconst START_FRAMES_TIMEOUT_MS = 60_000;\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 */\nconst MARGIN_OF_ERROR_SECONDS = 0.05;\n\nconst INTEGRATION_NAME = 'NativeFrames';\n\nexport interface FramesMeasurements extends Measurements {\n frames_total: { value: number; unit: MeasurementUnit };\n frames_slow: { value: number; unit: MeasurementUnit };\n frames_frozen: { value: number; unit: MeasurementUnit };\n}\n\ninterface NativeFramesResponseWithTimestamp {\n timestamp: number;\n nativeFrames: NativeFramesResponse;\n}\n\nexport const createNativeFramesIntegrations = (enable: boolean | undefined): Integration | undefined => {\n if (!enable && NATIVE.enableNative) {\n // On Android this will free up resource when JS reloaded (native modules stay) and thus JS side of the SDK reinitialized.\n NATIVE.disableNativeFramesTracking();\n return undefined;\n }\n\n return nativeFramesIntegration();\n};\n\n/**\n * Instrumentation to add native slow/frozen frames measurements onto transactions.\n */\nexport const nativeFramesIntegration = (): Integration => {\n /** The native frames at the finish time of the most recent span. */\n let _lastChildSpanEndFrames: NativeFramesResponseWithTimestamp | null = null;\n const _spanToNativeFramesAtStartMap: AsyncExpiringMap<string, NativeFramesResponse> = new AsyncExpiringMap({\n ttl: START_FRAMES_TIMEOUT_MS,\n });\n const _spanToNativeFramesAtEndMap: AsyncExpiringMap<string, NativeFramesResponseWithTimestamp | null> =\n new AsyncExpiringMap({ ttl: END_FRAMES_TIMEOUT_MS });\n\n /**\n * Hooks into the client start and end span events.\n */\n const setup = (client: Client): void => {\n if (!NATIVE.enableNative) {\n logger.warn(\n `[${INTEGRATION_NAME}] This is not available on the Web, Expo Go and other platforms without native modules.`,\n );\n return undefined;\n }\n\n NATIVE.enableNativeFramesTracking();\n client.on('spanStart', fetchStartFramesForSpan);\n client.on('spanEnd', fetchEndFramesForSpan);\n };\n\n const fetchStartFramesForSpan = (rootSpan: Span): void => {\n if (!isRootSpan(rootSpan)) {\n return;\n }\n\n const spanId = rootSpan.spanContext().spanId;\n logger.debug(`[${INTEGRATION_NAME}] Fetching frames for root span start (${spanId}).`);\n _spanToNativeFramesAtStartMap.set(\n spanId,\n new Promise<NativeFramesResponse | null>(resolve => {\n fetchNativeFrames()\n .then(frames => resolve(frames))\n .then(undefined, error => {\n logger.debug(`[${INTEGRATION_NAME}] Error while fetching native frames.`, error);\n resolve(null);\n });\n }),\n );\n };\n\n const fetchEndFramesForSpan = (span: Span): void => {\n const timestamp = timestampInSeconds();\n const spanId = span.spanContext().spanId;\n\n if (isRootSpan(span)) {\n const hasStartFrames = _spanToNativeFramesAtStartMap.has(spanId);\n if (!hasStartFrames) {\n // We don't have start frames, won't be able to calculate the difference.\n return;\n }\n\n logger.debug(`[${INTEGRATION_NAME}] Fetch frames for root span end (${spanId}).`);\n _spanToNativeFramesAtEndMap.set(\n spanId,\n new Promise<NativeFramesResponseWithTimestamp | null>(resolve => {\n fetchNativeFrames()\n .then(frames => {\n resolve({\n timestamp,\n nativeFrames: frames,\n });\n })\n .then(undefined, error => {\n logger.debug(`[${INTEGRATION_NAME}] Error while fetching native frames.`, error);\n resolve(null);\n });\n }),\n );\n return undefined;\n } else {\n logger.debug(`[${INTEGRATION_NAME}] Fetch frames for child span end (${spanId}).`);\n fetchNativeFrames()\n .then(frames => {\n _lastChildSpanEndFrames = {\n timestamp,\n nativeFrames: frames,\n };\n })\n .catch(error => logger.debug(`[${INTEGRATION_NAME}] Error while fetching native frames.`, error));\n }\n };\n\n const processEvent = async (event: Event): Promise<Event> => {\n if (\n event.type !== 'transaction' ||\n !event.transaction ||\n !event.contexts ||\n !event.contexts.trace ||\n !event.timestamp ||\n !event.contexts.trace.span_id\n ) {\n return event;\n }\n\n const traceOp = event.contexts.trace.op;\n const spanId = event.contexts.trace.span_id;\n const startFrames = await _spanToNativeFramesAtStartMap.pop(spanId);\n if (!startFrames) {\n logger.warn(\n `[${INTEGRATION_NAME}] Start frames of transaction ${event.transaction} (eventId, ${event.event_id}) are missing, but the transaction already ended.`,\n );\n return event;\n }\n\n const endFrames = await _spanToNativeFramesAtEndMap.pop(spanId);\n let finalEndFrames: NativeFramesResponse | undefined;\n\n if (endFrames && isClose(endFrames.timestamp, event.timestamp)) {\n // Must be in the margin of error of the actual transaction finish time (finalEndTimestamp)\n logger.debug(`[${INTEGRATION_NAME}] Using frames from root span end (spanId, ${spanId}).`);\n finalEndFrames = endFrames.nativeFrames;\n } else if (_lastChildSpanEndFrames && isClose(_lastChildSpanEndFrames.timestamp, event.timestamp)) {\n // Fallback to the last span finish if it is within the margin of error of the actual finish timestamp.\n // This should be the case for trimEnd.\n logger.debug(`[${INTEGRATION_NAME}] Using native frames from last child span end (spanId, ${spanId}).`);\n finalEndFrames = _lastChildSpanEndFrames.nativeFrames;\n } else {\n logger.warn(\n `[${INTEGRATION_NAME}] Frames were collected within larger than margin of error delay for spanId (${spanId}). Dropping the inaccurate values.`,\n );\n return event;\n }\n\n const measurements = {\n frames_total: {\n value: finalEndFrames.totalFrames - startFrames.totalFrames,\n unit: 'none',\n },\n frames_frozen: {\n value: finalEndFrames.frozenFrames - startFrames.frozenFrames,\n unit: 'none',\n },\n frames_slow: {\n value: finalEndFrames.slowFrames - startFrames.slowFrames,\n unit: 'none',\n },\n };\n\n if (\n measurements.frames_frozen.value <= 0 &&\n measurements.frames_slow.value <= 0 &&\n measurements.frames_total.value <= 0\n ) {\n logger.warn(\n `[${INTEGRATION_NAME}] Detected zero slow or frozen frames. Not adding measurements to spanId (${spanId}).`,\n );\n return event;\n }\n\n logger.log(\n `[${INTEGRATION_NAME}] Adding measurements to ${traceOp} transaction ${event.transaction}: ${JSON.stringify(\n measurements,\n undefined,\n 2,\n )}`,\n );\n event.measurements = {\n ...(event.measurements ?? {}),\n ...measurements,\n };\n return event;\n };\n\n return {\n name: INTEGRATION_NAME,\n setup,\n processEvent,\n };\n};\n\nfunction fetchNativeFrames(): Promise<NativeFramesResponse> {\n return new Promise<NativeFramesResponse>((resolve, reject) => {\n NATIVE.fetchNativeFrames()\n .then(value => {\n if (!value) {\n reject('Native frames response is null.');\n return;\n }\n resolve(value);\n })\n .then(undefined, error => {\n reject(error);\n });\n\n setTimeout(() => {\n reject('Fetching native frames took too long. Dropping frames.');\n }, FETCH_FRAMES_TIMEOUT_MS);\n });\n}\n\nfunction isClose(t1: number, t2: number): boolean {\n return Math.abs(t1 - t2) < MARGIN_OF_ERROR_SECONDS;\n}\n"]}
@@ -17,7 +17,7 @@ export const reactNativeTracingIntegration = (options = {}) => {
17
17
  const finalOptions = Object.assign(Object.assign(Object.assign({}, defaultReactNativeTracingOptions), options), { beforeStartSpan: (_a = options.beforeStartSpan) !== null && _a !== void 0 ? _a : ((options) => options), finalTimeoutMs: (_b = options.finalTimeoutMs) !== null && _b !== void 0 ? _b : defaultIdleOptions.finalTimeout, idleTimeoutMs: (_c = options.idleTimeoutMs) !== null && _c !== void 0 ? _c : defaultIdleOptions.idleTimeout });
18
18
  const setup = (client) => {
19
19
  addDefaultOpForSpanFrom(client);
20
- instrumentOutgoingRequests({
20
+ instrumentOutgoingRequests(client, {
21
21
  traceFetch: finalOptions.traceFetch,
22
22
  traceXHR: finalOptions.traceXHR,
23
23
  shouldCreateSpanForRequest: finalOptions.shouldCreateSpanForRequest,
@@ -1 +1 @@
1
- {"version":3,"file":"reactnativetracing.js","sourceRoot":"","sources":["../../../src/js/tracing/reactnativetracing.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAErE,MAAM,CAAC,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAuDrD,MAAM,iCAAiC,GAAG,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAErE,MAAM,CAAC,MAAM,gCAAgC,GAA8B;IACzE,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,IAAI;IACd,iBAAiB,EAAE,IAAI;CACxB,CAAC;AAMF,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAC3C,UAA8C,EAAE,EAKhD,EAAE;;IACF,MAAM,KAAK,GAA4B;QACrC,YAAY,EAAE,SAAS;KACxB,CAAC;IAEF,MAAM,YAAY,iDACb,gCAAgC,GAChC,OAAO,KACV,eAAe,EAAE,MAAA,OAAO,CAAC,eAAe,mCAAI,CAAC,CAAC,OAAyB,EAAE,EAAE,CAAC,OAAO,CAAC,EACpF,cAAc,EAAE,MAAA,OAAO,CAAC,cAAc,mCAAI,kBAAkB,CAAC,YAAY,EACzE,aAAa,EAAE,MAAA,OAAO,CAAC,aAAa,mCAAI,kBAAkB,CAAC,WAAW,GACvE,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,MAAc,EAAQ,EAAE;QACrC,uBAAuB,CAAC,MAAM,CAAC,CAAC;QAEhC,0BAA0B,CAAC;YACzB,UAAU,EAAE,YAAY,CAAC,UAAU;YACnC,QAAQ,EAAE,YAAY,CAAC,QAAQ;YAC/B,0BAA0B,EAAE,YAAY,CAAC,0BAA0B;YACnE,uBAAuB,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,uBAAuB,IAAI,iCAAiC;SAC1G,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CAAC,KAAY,EAAS,EAAE;QAC3C,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,YAAY,EAAE;YACxC,KAAK,CAAC,QAAQ,CAAC,GAAG,mBAAK,UAAU,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,IAAK,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAE,CAAC;SAClF;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,OAAO;QACL,IAAI,EAAE,gBAAgB;QACtB,KAAK;QACL,YAAY;QACZ,OAAO,EAAE,YAAY;QACrB,KAAK;QACL,eAAe,EAAE,CAAC,KAAa,EAAE,EAAE;YACjC,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAIF;;GAEG;AACH,MAAM,UAAU,uCAAuC;IACrD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,CAAC,MAAM,EAAE;QACX,OAAO,SAAS,CAAC;KAClB;IAED,OAAO,gCAAgC,CAAC,MAAM,CAAC,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gCAAgC,CAAC,MAAc;IAC7D,OAAO,MAAM,CAAC,oBAAoB,CAAC,gBAAgB,CAA8C,CAAC;AACpG,CAAC","sourcesContent":["/* eslint-disable max-lines */\nimport { instrumentOutgoingRequests } from '@sentry/browser';\nimport { getClient } from '@sentry/core';\nimport type { Client, Event, Integration, StartSpanOptions } from '@sentry/types';\n\nimport { addDefaultOpForSpanFrom, defaultIdleOptions } from './span';\n\nexport const INTEGRATION_NAME = 'ReactNativeTracing';\n\nexport interface ReactNativeTracingOptions {\n /**\n * The time that has to pass without any span being created.\n * If this time is exceeded, the idle span will finish.\n *\n * @default 1_000 (ms)\n */\n idleTimeoutMs?: number;\n\n /**\n * The max. time an idle span may run.\n * If this time is exceeded, the idle span will finish no matter what.\n *\n * @default 60_0000 (ms)\n */\n finalTimeoutMs?: number;\n\n /**\n * Flag to disable patching all together for fetch requests.\n *\n * @default true\n */\n traceFetch: boolean;\n\n /**\n * Flag to disable patching all together for xhr requests.\n *\n * @default true\n */\n traceXHR: boolean;\n\n /**\n * If true, Sentry will capture http timings and add them to the corresponding http spans.\n *\n * @default true\n */\n enableHTTPTimings: boolean;\n\n /**\n * A callback which is called before a span for a navigation is started.\n * It receives the options passed to `startSpan`, and expects to return an updated options object.\n */\n beforeStartSpan?: (options: StartSpanOptions) => StartSpanOptions;\n\n /**\n * This function will be called before creating a span for a request with the given url.\n * Return false if you don't want a span for the given url.\n *\n * @default (url: string) => true\n */\n shouldCreateSpanForRequest?(this: void, url: string): boolean;\n}\n\nconst DEFAULT_TRACE_PROPAGATION_TARGETS = ['localhost', /^\\/(?!\\/)/];\n\nexport const defaultReactNativeTracingOptions: ReactNativeTracingOptions = {\n traceFetch: true,\n traceXHR: true,\n enableHTTPTimings: true,\n};\n\nexport type ReactNativeTracingState = {\n currentRoute: string | undefined;\n};\n\nexport const reactNativeTracingIntegration = (\n options: Partial<ReactNativeTracingOptions> = {},\n): Integration & {\n options: ReactNativeTracingOptions;\n state: ReactNativeTracingState;\n setCurrentRoute: (route: string) => void;\n} => {\n const state: ReactNativeTracingState = {\n currentRoute: undefined,\n };\n\n const finalOptions = {\n ...defaultReactNativeTracingOptions,\n ...options,\n beforeStartSpan: options.beforeStartSpan ?? ((options: StartSpanOptions) => options),\n finalTimeoutMs: options.finalTimeoutMs ?? defaultIdleOptions.finalTimeout,\n idleTimeoutMs: options.idleTimeoutMs ?? defaultIdleOptions.idleTimeout,\n };\n\n const setup = (client: Client): void => {\n addDefaultOpForSpanFrom(client);\n\n instrumentOutgoingRequests({\n traceFetch: finalOptions.traceFetch,\n traceXHR: finalOptions.traceXHR,\n shouldCreateSpanForRequest: finalOptions.shouldCreateSpanForRequest,\n tracePropagationTargets: client.getOptions().tracePropagationTargets || DEFAULT_TRACE_PROPAGATION_TARGETS,\n });\n };\n\n const processEvent = (event: Event): Event => {\n if (event.contexts && state.currentRoute) {\n event.contexts.app = { view_names: [state.currentRoute], ...event.contexts.app };\n }\n return event;\n };\n\n return {\n name: INTEGRATION_NAME,\n setup,\n processEvent,\n options: finalOptions,\n state,\n setCurrentRoute: (route: string) => {\n state.currentRoute = route;\n },\n };\n};\n\nexport type ReactNativeTracingIntegration = ReturnType<typeof reactNativeTracingIntegration>;\n\n/**\n * Returns the current React Native Tracing integration.\n */\nexport function getCurrentReactNativeTracingIntegration(): ReactNativeTracingIntegration | undefined {\n const client = getClient();\n if (!client) {\n return undefined;\n }\n\n return getReactNativeTracingIntegration(client);\n}\n\n/**\n * Returns React Native Tracing integration of given client.\n */\nexport function getReactNativeTracingIntegration(client: Client): ReactNativeTracingIntegration | undefined {\n return client.getIntegrationByName(INTEGRATION_NAME) as ReactNativeTracingIntegration | undefined;\n}\n"]}
1
+ {"version":3,"file":"reactnativetracing.js","sourceRoot":"","sources":["../../../src/js/tracing/reactnativetracing.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAErE,MAAM,CAAC,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAuDrD,MAAM,iCAAiC,GAAG,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAErE,MAAM,CAAC,MAAM,gCAAgC,GAA8B;IACzE,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,IAAI;IACd,iBAAiB,EAAE,IAAI;CACxB,CAAC;AAMF,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAC3C,UAA8C,EAAE,EAKhD,EAAE;;IACF,MAAM,KAAK,GAA4B;QACrC,YAAY,EAAE,SAAS;KACxB,CAAC;IAEF,MAAM,YAAY,iDACb,gCAAgC,GAChC,OAAO,KACV,eAAe,EAAE,MAAA,OAAO,CAAC,eAAe,mCAAI,CAAC,CAAC,OAAyB,EAAE,EAAE,CAAC,OAAO,CAAC,EACpF,cAAc,EAAE,MAAA,OAAO,CAAC,cAAc,mCAAI,kBAAkB,CAAC,YAAY,EACzE,aAAa,EAAE,MAAA,OAAO,CAAC,aAAa,mCAAI,kBAAkB,CAAC,WAAW,GACvE,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,MAAc,EAAQ,EAAE;QACrC,uBAAuB,CAAC,MAAM,CAAC,CAAC;QAEhC,0BAA0B,CAAC,MAAM,EAAE;YACjC,UAAU,EAAE,YAAY,CAAC,UAAU;YACnC,QAAQ,EAAE,YAAY,CAAC,QAAQ;YAC/B,0BAA0B,EAAE,YAAY,CAAC,0BAA0B;YACnE,uBAAuB,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,uBAAuB,IAAI,iCAAiC;SAC1G,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CAAC,KAAY,EAAS,EAAE;QAC3C,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,YAAY,EAAE;YACxC,KAAK,CAAC,QAAQ,CAAC,GAAG,mBAAK,UAAU,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,IAAK,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAE,CAAC;SAClF;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,OAAO;QACL,IAAI,EAAE,gBAAgB;QACtB,KAAK;QACL,YAAY;QACZ,OAAO,EAAE,YAAY;QACrB,KAAK;QACL,eAAe,EAAE,CAAC,KAAa,EAAE,EAAE;YACjC,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAIF;;GAEG;AACH,MAAM,UAAU,uCAAuC;IACrD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,CAAC,MAAM,EAAE;QACX,OAAO,SAAS,CAAC;KAClB;IAED,OAAO,gCAAgC,CAAC,MAAM,CAAC,CAAC;AAClD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gCAAgC,CAAC,MAAc;IAC7D,OAAO,MAAM,CAAC,oBAAoB,CAAC,gBAAgB,CAA8C,CAAC;AACpG,CAAC","sourcesContent":["/* eslint-disable max-lines */\nimport { instrumentOutgoingRequests } from '@sentry/browser';\nimport { getClient } from '@sentry/core';\nimport type { Client, Event, Integration, StartSpanOptions } from '@sentry/types';\n\nimport { addDefaultOpForSpanFrom, defaultIdleOptions } from './span';\n\nexport const INTEGRATION_NAME = 'ReactNativeTracing';\n\nexport interface ReactNativeTracingOptions {\n /**\n * The time that has to pass without any span being created.\n * If this time is exceeded, the idle span will finish.\n *\n * @default 1_000 (ms)\n */\n idleTimeoutMs?: number;\n\n /**\n * The max. time an idle span may run.\n * If this time is exceeded, the idle span will finish no matter what.\n *\n * @default 60_0000 (ms)\n */\n finalTimeoutMs?: number;\n\n /**\n * Flag to disable patching all together for fetch requests.\n *\n * @default true\n */\n traceFetch: boolean;\n\n /**\n * Flag to disable patching all together for xhr requests.\n *\n * @default true\n */\n traceXHR: boolean;\n\n /**\n * If true, Sentry will capture http timings and add them to the corresponding http spans.\n *\n * @default true\n */\n enableHTTPTimings: boolean;\n\n /**\n * A callback which is called before a span for a navigation is started.\n * It receives the options passed to `startSpan`, and expects to return an updated options object.\n */\n beforeStartSpan?: (options: StartSpanOptions) => StartSpanOptions;\n\n /**\n * This function will be called before creating a span for a request with the given url.\n * Return false if you don't want a span for the given url.\n *\n * @default (url: string) => true\n */\n shouldCreateSpanForRequest?(this: void, url: string): boolean;\n}\n\nconst DEFAULT_TRACE_PROPAGATION_TARGETS = ['localhost', /^\\/(?!\\/)/];\n\nexport const defaultReactNativeTracingOptions: ReactNativeTracingOptions = {\n traceFetch: true,\n traceXHR: true,\n enableHTTPTimings: true,\n};\n\nexport type ReactNativeTracingState = {\n currentRoute: string | undefined;\n};\n\nexport const reactNativeTracingIntegration = (\n options: Partial<ReactNativeTracingOptions> = {},\n): Integration & {\n options: ReactNativeTracingOptions;\n state: ReactNativeTracingState;\n setCurrentRoute: (route: string) => void;\n} => {\n const state: ReactNativeTracingState = {\n currentRoute: undefined,\n };\n\n const finalOptions = {\n ...defaultReactNativeTracingOptions,\n ...options,\n beforeStartSpan: options.beforeStartSpan ?? ((options: StartSpanOptions) => options),\n finalTimeoutMs: options.finalTimeoutMs ?? defaultIdleOptions.finalTimeout,\n idleTimeoutMs: options.idleTimeoutMs ?? defaultIdleOptions.idleTimeout,\n };\n\n const setup = (client: Client): void => {\n addDefaultOpForSpanFrom(client);\n\n instrumentOutgoingRequests(client, {\n traceFetch: finalOptions.traceFetch,\n traceXHR: finalOptions.traceXHR,\n shouldCreateSpanForRequest: finalOptions.shouldCreateSpanForRequest,\n tracePropagationTargets: client.getOptions().tracePropagationTargets || DEFAULT_TRACE_PROPAGATION_TARGETS,\n });\n };\n\n const processEvent = (event: Event): Event => {\n if (event.contexts && state.currentRoute) {\n event.contexts.app = { view_names: [state.currentRoute], ...event.contexts.app };\n }\n return event;\n };\n\n return {\n name: INTEGRATION_NAME,\n setup,\n processEvent,\n options: finalOptions,\n state,\n setCurrentRoute: (route: string) => {\n state.currentRoute = route;\n },\n };\n};\n\nexport type ReactNativeTracingIntegration = ReturnType<typeof reactNativeTracingIntegration>;\n\n/**\n * Returns the current React Native Tracing integration.\n */\nexport function getCurrentReactNativeTracingIntegration(): ReactNativeTracingIntegration | undefined {\n const client = getClient();\n if (!client) {\n return undefined;\n }\n\n return getReactNativeTracingIntegration(client);\n}\n\n/**\n * Returns React Native Tracing integration of given client.\n */\nexport function getReactNativeTracingIntegration(client: Client): ReactNativeTracingIntegration | undefined {\n return client.getIntegrationByName(INTEGRATION_NAME) as ReactNativeTracingIntegration | undefined;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"reactnavigation.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/reactnavigation.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAU,WAAW,EAAQ,MAAM,eAAe,CAAC;AAqB/D,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAIlD,UAAU,iCAAiC;IACzC;;;;;OAKG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAE7B;;;;;OAKG;IACH,0BAA0B,EAAE,OAAO,CAAC;IAEpC;;;;;OAKG;IACH,qCAAqC,EAAE,OAAO,CAAC;CAChD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,kGAIpC,QAAQ,iCAAiC,CAAC;IAC3C;;;OAGG;0DACmD,OAAO,KAAK,IAAI;CA6QvE,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IAEZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B"}
1
+ {"version":3,"file":"reactnavigation.d.ts","sourceRoot":"","sources":["../../../src/js/tracing/reactnavigation.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAU,WAAW,EAAQ,MAAM,eAAe,CAAC;AAqB/D,eAAO,MAAM,gBAAgB,oBAAoB,CAAC;AAIlD,UAAU,iCAAiC;IACzC;;;;;OAKG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAE7B;;;;;OAKG;IACH,0BAA0B,EAAE,OAAO,CAAC;IAEpC;;;;;OAKG;IACH,qCAAqC,EAAE,OAAO,CAAC;CAChD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,kGAIpC,QAAQ,iCAAiC,CAAC;IAC3C;;;OAGG;0DACmD,OAAO,KAAK,IAAI;CA4QvE,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IAEZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"reactnavigation.js","sourceRoot":"","sources":["../../../src/js/tracing/reactnavigation.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,OAAO,EACL,aAAa,EACb,aAAa,EACb,SAAS,EACT,4BAA4B,EAC5B,cAAc,EACd,UAAU,EACV,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAG1E,OAAO,EAA2B,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACnH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAE7D,OAAO,EAAE,gCAAgC,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,gCAAgC,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EACL,4BAA4B,EAC5B,kBAAkB,EAClB,mCAAmC,EACnC,uBAAuB,IAAI,8BAA8B,GAC1D,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,iBAAiB,CAAC;AAC3F,OAAO,EAAE,kCAAkC,EAAE,MAAM,SAAS,CAAC;AAE7D,MAAM,CAAC,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAElD,MAAM,2BAA2B,GAAG,GAAG,CAAC;AA4BxC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,EACzC,oBAAoB,GAAG,IAAK,EAC5B,0BAA0B,GAAG,KAAK,EAClC,qCAAqC,GAAG,IAAI,MACE,EAAE,EAMhD,EAAE;IACF,IAAI,mBAAoD,CAAC;IACzD,IAAI,0BAA0D,CAAC;IAE/D,IAAI,OAAkD,CAAC;IACvD,IAAI,eAAe,GAAyD,kBAAkB,CAAC;IAC/F,IAAI,WAAwC,CAAC;IAE7C,IAAI,oBAAsC,CAAC;IAC3C,IAAI,wBAA0C,CAAC;IAE/C,IAAI,mBAAmB,GAAY,KAAK,CAAC;IACzC,IAAI,kBAA6D,CAAC;IAClE,IAAI,eAAe,GAAa,EAAE,CAAC;IAEnC,IAAI,0BAA0B,EAAE;QAC9B,0BAA0B,GAAG,wBAAwB,EAAE,CAAC;QACxD,0BAA0B,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACxD,MAAM,CAAC,yCAAyC,EAAE,CAAC,KAAK,CAAC,CAAC,MAAe,EAAE,EAAE;YAC3E,MAAM,CAAC,KAAK,CAAC,GAAG,gBAAgB,oDAAoD,MAAM,EAAE,CAAC,CAAC;QAChG,CAAC,CAAC,CAAC;KACJ;IAED;;OAEG;IACH,MAAM,aAAa,GAAG,CAAC,MAAc,EAAQ,EAAE;QAC7C,OAAO,GAAG,gCAAgC,CAAC,MAAM,CAAC,CAAC;QACnD,IAAI,OAAO,EAAE;YACX,eAAe,GAAG;gBAChB,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,cAAc;gBAC5C,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,aAAa;aAC3C,CAAC;SACH;QAED,IAAI,mBAAmB,EAAE;YACvB,sGAAsG;YACtG,OAAO,SAAS,CAAC;SAClB;QAED,uBAAuB,EAAE,CAAC;QAE1B,IAAI,CAAC,mBAAmB,EAAE;YACxB,8FAA8F;YAC9F,OAAO,SAAS,CAAC;SAClB;QAED,0EAA0E;QAC1E,0CAA0C,EAAE,CAAC;QAC7C,mBAAmB,GAAG,IAAI,CAAC;IAC7B,CAAC,CAAC;IAEF,MAAM,2BAA2B,GAAG,CAAC,sBAA+B,EAAQ,EAAE;QAC5E;;;;WAIG;QACH,IAAI,aAAa,CAAC,yBAAyB,EAAE;YAC3C,MAAM,CAAC,GAAG,CACR,GAAG,gBAAgB,qFAAqF,CACzG,CAAC;YACF,OAAO,SAAS,CAAC;SAClB;QAED,IAAI,aAAa,CAAC,sBAAsB,CAAC,IAAI,SAAS,IAAI,sBAAsB,EAAE;YAChF,mBAAmB,GAAG,sBAAsB,CAAC,OAA8B,CAAC;SAC7E;aAAM;YACL,mBAAmB,GAAG,sBAA6C,CAAC;SACrE;QACD,IAAI,CAAC,mBAAmB,EAAE;YACxB,MAAM,CAAC,IAAI,CAAC,GAAG,gBAAgB,6CAA6C,CAAC,CAAC;YAC9E,OAAO,SAAS,CAAC;SAClB;QAED,2CAA2C;QAC3C,mBAAmB,CAAC,WAAW,CAAC,mBAAmB,EAAE,uBAAuB,CAAC,CAAC;QAC9E,mBAAmB,CAAC,WAAW,CAAC,OAAO,EAAE,0CAA0C,CAAC,CAAC;QACrF,aAAa,CAAC,yBAAyB,GAAG,IAAI,CAAC;QAE/C,IAAI,mBAAmB,EAAE;YACvB,OAAO,SAAS,CAAC;SAClB;QAED,IAAI,CAAC,oBAAoB,EAAE;YACzB,MAAM,CAAC,GAAG,CAAC,GAAG,gBAAgB,2EAA2E,CAAC,CAAC;YAC3G,OAAO,SAAS,CAAC;SAClB;QAED,gEAAgE;QAChE,gEAAgE;QAChE,gDAAgD;QAChD,0CAA0C,EAAE,CAAC;QAC7C,mBAAmB,GAAG,IAAI,CAAC;IAC7B,CAAC,CAAC;IAEF;;;;OAIG;IACH,MAAM,uBAAuB,GAAG,GAAS,EAAE;QACzC,IAAI,oBAAoB,EAAE;YACxB,MAAM,CAAC,GAAG,CAAC,GAAG,gBAAgB,uEAAuE,CAAC,CAAC;YACvG,yBAAyB,EAAE,CAAC;YAC5B,uBAAuB,EAAE,CAAC;SAC3B;QAED,oBAAoB,GAAG,8BAA8B,CACnD,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,eAAe;YACxC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,mCAAmC,EAAE,CAAC;YACxE,CAAC,CAAC,mCAAmC,EAAE,EACzC,eAAe,CAChB,CAAC;QACF,IAAI,qCAAqC,EAAE;YACzC,yBAAyB,CAAC,SAAS,EAAE,EAAE,oBAAoB,CAAC,CAAC;SAC9D;QAED,IAAI,0BAA0B,EAAE;YAC9B,wBAAwB,GAAG,iBAAiB,CAAC;gBAC3C,EAAE,EAAE,uBAAuB;gBAC3B,IAAI,EAAE,uBAAuB;gBAC7B,SAAS,EAAE,oBAAoB,IAAI,UAAU,CAAC,oBAAoB,CAAC,CAAC,eAAe;aACpF,CAAC,CAAC;SACJ;QAED,kBAAkB,GAAG,UAAU,CAAC,yBAAyB,EAAE,oBAAoB,CAAC,CAAC;IACnF,CAAC,CAAC;IAEF;;OAEG;IACH,MAAM,0CAA0C,GAAG,GAAS,EAAE;QAC5D,MAAM,qBAAqB,GAAG,kBAAkB,EAAE,CAAC;QACnD,MAAM,aAAa,GAAG,WAAW,CAAC;QAElC,IAAI,CAAC,mBAAmB,EAAE;YACxB,MAAM,CAAC,IAAI,CAAC,GAAG,gBAAgB,yEAAyE,CAAC,CAAC;YAC1G,OAAO,SAAS,CAAC;SAClB;QAED,MAAM,KAAK,GAAG,mBAAmB,CAAC,eAAe,EAAE,CAAC;QACpD,IAAI,CAAC,KAAK,EAAE;YACV,MAAM,CAAC,KAAK,CAAC,IAAI,gBAAgB,uDAAuD,CAAC,CAAC;YAC1F,OAAO,SAAS,CAAC;SAClB;QAED,IAAI,CAAC,oBAAoB,EAAE;YACzB,MAAM,CAAC,KAAK,CACV,IAAI,gBAAgB,qFAAqF,CAC1G,CAAC;YACF,OAAO,SAAS,CAAC;SAClB;QAED,IAAI,aAAa,IAAI,aAAa,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,EAAE;YACpD,MAAM,CAAC,KAAK,CAAC,IAAI,gBAAgB,gEAAgE,CAAC,CAAC;YACnG,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC9B,WAAW,GAAG,KAAK,CAAC;YAEpB,uDAAuD;YACvD,oBAAoB,GAAG,SAAS,CAAC;YACjC,OAAO,SAAS,CAAC;SAClB;QAED,MAAM,gBAAgB,GAAG,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE7D,MAAM,cAAc,GAClB,CAAC,gBAAgB;YACjB,0BAA0B;YAC1B,6BAA6B,CAAC;gBAC5B,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,kBAAkB;gBACrC,kBAAkB,EAAE,IAAI;aACzB,CAAC,CAAC;QAEL,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;QACpD,CAAC,gBAAgB;YACf,cAAc;aACd,0BAA0B,aAA1B,0BAA0B,uBAA1B,0BAA0B,CAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,EAAE,0BAA0B,EAAiB,EAAE,EAAE;gBACpG,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;gBACnC,IAAI,UAAU,IAAI,yBAAyB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE;oBAC3D,MAAM,CAAC,IAAI,CAAC,+FAA+F,CAAC,CAAC;oBAC7G,OAAO;iBACR;gBAED,cAAc,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;gBACnD,cAAc,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;gBAC/C,kCAAkC,CAAC,yBAAyB,EAAE,cAAc,EAAE,sBAAsB,CAAC,CAAC;YACxG,CAAC,CAAC,CAAA,CAAC;QAEL,wBAAwB,aAAxB,wBAAwB,uBAAxB,wBAAwB,CAAE,UAAU,CAAC,4BAA4B,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/E,wBAAwB,aAAxB,wBAAwB,uBAAxB,wBAAwB,CAAE,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QAC9D,wBAAwB,aAAxB,wBAAwB,uBAAxB,wBAAwB,CAAE,GAAG,CAAC,qBAAqB,CAAC,CAAC;QACrD,wBAAwB,GAAG,SAAS,CAAC;QAErC,IAAI,UAAU,CAAC,oBAAoB,CAAC,CAAC,WAAW,KAAK,4BAA4B,EAAE;YACjF,oBAAoB,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;SAC7C;QACD,oBAAoB,CAAC,aAAa,CAAC;YACjC,YAAY,EAAE,KAAK,CAAC,IAAI;YACxB,WAAW,EAAE,KAAK,CAAC,GAAG;YACtB,uDAAuD;YACvD,sBAAsB;YACtB,qBAAqB,EAAE,gBAAgB;YACvC,qBAAqB,EAAE,aAAa,aAAb,aAAa,uBAAb,aAAa,CAAE,IAAI;YAC1C,oBAAoB,EAAE,aAAa,aAAb,aAAa,uBAAb,aAAa,CAAE,GAAG;YACxC,uDAAuD;YACvD,+BAA+B;YAC/B,CAAC,gCAAgC,CAAC,EAAE,WAAW;YAC/C,CAAC,4BAA4B,CAAC,EAAE,YAAY;SAC7C,CAAC,CAAC;QAEH,+DAA+D;QAC/D,uBAAuB,EAAE,CAAC;QAE1B,aAAa,CAAC;YACZ,QAAQ,EAAE,YAAY;YACtB,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,iBAAiB,KAAK,CAAC,IAAI,EAAE;YACtC,IAAI,EAAE;gBACJ,IAAI,EAAE,aAAa,aAAb,aAAa,uBAAb,aAAa,CAAE,IAAI;gBACzB,EAAE,EAAE,KAAK,CAAC,IAAI;aACf;SACF,CAAC,CAAC;QAEH,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEpC,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9B,WAAW,GAAG,KAAK,CAAC;QACpB,uDAAuD;QACvD,oBAAoB,GAAG,SAAS,CAAC;IACnC,CAAC,CAAC;IAEF,sGAAsG;IACtG,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAQ,EAAE;QAC/C,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE1B,IAAI,eAAe,CAAC,MAAM,GAAG,2BAA2B,EAAE;YACxD,eAAe,GAAG,eAAe,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,GAAG,2BAA2B,CAAC,CAAC;SAC/F;IACH,CAAC,CAAC;IAEF,wEAAwE;IACxE,MAAM,yBAAyB,GAAG,GAAS,EAAE;QAC3C,IAAI,oBAAoB,EAAE;YACxB,IAAI,YAAY,CAAC,oBAAoB,CAAC,EAAE;gBACtC,oBAAoB,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC;aAC1C;YACD,qCAAqC;YACrC,oBAAoB,CAAC,GAAG,EAAE,CAAC;YAC3B,oBAAoB,GAAG,SAAS,CAAC;SAClC;QACD,IAAI,wBAAwB,EAAE;YAC5B,wBAAwB,GAAG,SAAS,CAAC;SACtC;IACH,CAAC,CAAC;IAEF,MAAM,uBAAuB,GAAG,GAAS,EAAE;QACzC,IAAI,OAAO,kBAAkB,KAAK,WAAW,EAAE;YAC7C,YAAY,CAAC,kBAAkB,CAAC,CAAC;YACjC,kBAAkB,GAAG,SAAS,CAAC;SAChC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,IAAI,EAAE,gBAAgB;QACtB,aAAa;QACb,2BAA2B;KAC5B,CAAC;AACJ,CAAC,CAAC","sourcesContent":["/* eslint-disable max-lines */\nimport {\n addBreadcrumb,\n getActiveSpan,\n getClient,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SPAN_STATUS_OK,\n spanToJSON,\n startInactiveSpan,\n} from '@sentry/core';\nimport type { Client, Integration, Span } from '@sentry/types';\nimport { isPlainObject, logger, timestampInSeconds } from '@sentry/utils';\n\nimport type { NewFrameEvent } from '../utils/sentryeventemitter';\nimport { type SentryEventEmitter, createSentryEventEmitter, NewFrameEventName } from '../utils/sentryeventemitter';\nimport { isSentrySpan } from '../utils/span';\nimport { RN_GLOBAL_OBJ } from '../utils/worldwide';\nimport { NATIVE } from '../wrapper';\nimport { ignoreEmptyBackNavigation } from './onSpanEndUtils';\nimport type { ReactNativeTracingIntegration } from './reactnativetracing';\nimport { getReactNativeTracingIntegration } from './reactnativetracing';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from './semanticAttributes';\nimport {\n DEFAULT_NAVIGATION_SPAN_NAME,\n defaultIdleOptions,\n getDefaultIdleNavigationSpanOptions,\n startIdleNavigationSpan as startGenericIdleNavigationSpan,\n} from './span';\nimport { manualInitialDisplaySpans, startTimeToInitialDisplaySpan } from './timetodisplay';\nimport { setSpanDurationAsMeasurementOnSpan } from './utils';\n\nexport const INTEGRATION_NAME = 'ReactNavigation';\n\nconst NAVIGATION_HISTORY_MAX_SIZE = 200;\n\ninterface ReactNavigationIntegrationOptions {\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 *\n * @default 1_000 (ms)\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 /**\n * Does not sample transactions that are from routes that have been seen any more and don't have any spans.\n * This removes a lot of the clutter as most back navigation transactions are now ignored.\n *\n * @default true\n */\n ignoreEmptyBackNavigationTransactions: boolean;\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 const reactNavigationIntegration = ({\n routeChangeTimeoutMs = 1_000,\n enableTimeToInitialDisplay = false,\n ignoreEmptyBackNavigationTransactions = true,\n}: Partial<ReactNavigationIntegrationOptions> = {}): Integration & {\n /**\n * Pass the ref to the navigation container to register it to the instrumentation\n * @param navigationContainerRef Ref to a `NavigationContainer`\n */\n registerNavigationContainer: (navigationContainerRef: unknown) => void;\n} => {\n let navigationContainer: NavigationContainer | undefined;\n let newScreenFrameEventEmitter: SentryEventEmitter | undefined;\n\n let tracing: ReactNativeTracingIntegration | undefined;\n let idleSpanOptions: Parameters<typeof startGenericIdleNavigationSpan>[1] = defaultIdleOptions;\n let latestRoute: NavigationRoute | undefined;\n\n let latestNavigationSpan: Span | undefined;\n let navigationProcessingSpan: Span | undefined;\n\n let initialStateHandled: boolean = false;\n let stateChangeTimeout: ReturnType<typeof setTimeout> | undefined;\n let recentRouteKeys: string[] = [];\n\n if (enableTimeToInitialDisplay) {\n newScreenFrameEventEmitter = createSentryEventEmitter();\n newScreenFrameEventEmitter.initAsync(NewFrameEventName);\n NATIVE.initNativeReactNavigationNewFrameTracking().catch((reason: unknown) => {\n logger.error(`${INTEGRATION_NAME} Failed to initialize native new frame tracking: ${reason}`);\n });\n }\n\n /**\n * Set the initial state and start initial navigation span for the current screen.\n */\n const afterAllSetup = (client: Client): void => {\n tracing = getReactNativeTracingIntegration(client);\n if (tracing) {\n idleSpanOptions = {\n finalTimeout: tracing.options.finalTimeoutMs,\n idleTimeout: tracing.options.idleTimeoutMs,\n };\n }\n\n if (initialStateHandled) {\n // We create an initial state here to ensure a transaction gets created before the first route mounts.\n return undefined;\n }\n\n startIdleNavigationSpan();\n\n if (!navigationContainer) {\n // This is expected as navigation container is registered after the root component is mounted.\n return undefined;\n }\n\n // Navigation container already registered, just populate with route state\n updateLatestNavigationSpanWithCurrentRoute();\n initialStateHandled = true;\n };\n\n const registerNavigationContainer = (navigationContainerRef: unknown): 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 logger.log(\n `${INTEGRATION_NAME} Instrumentation already exists, but register has been called again, doing nothing.`,\n );\n return undefined;\n }\n\n if (isPlainObject(navigationContainerRef) && 'current' in navigationContainerRef) {\n navigationContainer = navigationContainerRef.current as NavigationContainer;\n } else {\n navigationContainer = navigationContainerRef as NavigationContainer;\n }\n if (!navigationContainer) {\n logger.warn(`${INTEGRATION_NAME} Received invalid navigation container ref!`);\n return undefined;\n }\n\n // This action is emitted on every dispatch\n navigationContainer.addListener('__unsafe_action__', startIdleNavigationSpan);\n navigationContainer.addListener('state', updateLatestNavigationSpanWithCurrentRoute);\n RN_GLOBAL_OBJ.__sentry_rn_v5_registered = true;\n\n if (initialStateHandled) {\n return undefined;\n }\n\n if (!latestNavigationSpan) {\n logger.log(`${INTEGRATION_NAME} Navigation container registered, but integration has not been setup yet.`);\n return undefined;\n }\n\n // Navigation Container is registered after the first navigation\n // Initial navigation span was started, after integration setup,\n // so now we populate it with the current route.\n updateLatestNavigationSpanWithCurrentRoute();\n initialStateHandled = true;\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 updateLatestNavigationSpanWithCurrentRoute\n */\n const startIdleNavigationSpan = (): void => {\n if (latestNavigationSpan) {\n logger.log(`${INTEGRATION_NAME} A transaction was detected that turned out to be a noop, discarding.`);\n _discardLatestTransaction();\n clearStateChangeTimeout();\n }\n\n latestNavigationSpan = startGenericIdleNavigationSpan(\n tracing && tracing.options.beforeStartSpan\n ? tracing.options.beforeStartSpan(getDefaultIdleNavigationSpanOptions())\n : getDefaultIdleNavigationSpanOptions(),\n idleSpanOptions,\n );\n if (ignoreEmptyBackNavigationTransactions) {\n ignoreEmptyBackNavigation(getClient(), latestNavigationSpan);\n }\n\n if (enableTimeToInitialDisplay) {\n navigationProcessingSpan = startInactiveSpan({\n op: 'navigation.processing',\n name: 'Navigation processing',\n startTime: latestNavigationSpan && spanToJSON(latestNavigationSpan).start_timestamp,\n });\n }\n\n stateChangeTimeout = setTimeout(_discardLatestTransaction, routeChangeTimeoutMs);\n };\n\n /**\n * To be called AFTER the state has been changed to populate the transaction with the current route.\n */\n const updateLatestNavigationSpanWithCurrentRoute = (): void => {\n const stateChangedTimestamp = timestampInSeconds();\n const previousRoute = latestRoute;\n\n if (!navigationContainer) {\n logger.warn(`${INTEGRATION_NAME} Missing navigation container ref. Route transactions will not be sent.`);\n return undefined;\n }\n\n const route = navigationContainer.getCurrentRoute();\n if (!route) {\n logger.debug(`[${INTEGRATION_NAME}] Navigation state changed, but no route is rendered.`);\n return undefined;\n }\n\n if (!latestNavigationSpan) {\n logger.debug(\n `[${INTEGRATION_NAME}] Navigation state changed, but navigation transaction was not started on dispatch.`,\n );\n return undefined;\n }\n\n if (previousRoute && previousRoute.key === route.key) {\n logger.debug(`[${INTEGRATION_NAME}] Navigation state changed, but route is the same as previous.`);\n pushRecentRouteKey(route.key);\n latestRoute = route;\n\n // Clear the latest transaction as it has been handled.\n latestNavigationSpan = undefined;\n return undefined;\n }\n\n const routeHasBeenSeen = recentRouteKeys.includes(route.key);\n\n const latestTtidSpan =\n !routeHasBeenSeen &&\n enableTimeToInitialDisplay &&\n startTimeToInitialDisplaySpan({\n name: `${route.name} initial display`,\n isAutoInstrumented: true,\n });\n\n const navigationSpanWithTtid = latestNavigationSpan;\n !routeHasBeenSeen &&\n latestTtidSpan &&\n newScreenFrameEventEmitter?.once(NewFrameEventName, ({ newFrameTimestampInSeconds }: NewFrameEvent) => {\n const activeSpan = getActiveSpan();\n if (activeSpan && manualInitialDisplaySpans.has(activeSpan)) {\n logger.warn('[ReactNavigationInstrumentation] Detected manual instrumentation for the current active span.');\n return;\n }\n\n latestTtidSpan.setStatus({ code: SPAN_STATUS_OK });\n latestTtidSpan.end(newFrameTimestampInSeconds);\n setSpanDurationAsMeasurementOnSpan('time_to_initial_display', latestTtidSpan, navigationSpanWithTtid);\n });\n\n navigationProcessingSpan?.updateName(`Processing navigation to ${route.name}`);\n navigationProcessingSpan?.setStatus({ code: SPAN_STATUS_OK });\n navigationProcessingSpan?.end(stateChangedTimestamp);\n navigationProcessingSpan = undefined;\n\n if (spanToJSON(latestNavigationSpan).description === DEFAULT_NAVIGATION_SPAN_NAME) {\n latestNavigationSpan.updateName(route.name);\n }\n latestNavigationSpan.setAttributes({\n 'route.name': route.name,\n 'route.key': route.key,\n // TODO: filter PII params instead of dropping them all\n // 'route.params': {},\n 'route.has_been_seen': routeHasBeenSeen,\n 'previous_route.name': previousRoute?.name,\n 'previous_route.key': previousRoute?.key,\n // TODO: filter PII params instead of dropping them all\n // 'previous_route.params': {},\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n });\n\n // Clear the timeout so the transaction does not get cancelled.\n clearStateChangeTimeout();\n\n addBreadcrumb({\n category: 'navigation',\n type: 'navigation',\n message: `Navigation to ${route.name}`,\n data: {\n from: previousRoute?.name,\n to: route.name,\n },\n });\n\n tracing?.setCurrentRoute(route.key);\n\n pushRecentRouteKey(route.key);\n latestRoute = route;\n // Clear the latest transaction as it has been handled.\n latestNavigationSpan = undefined;\n };\n\n /** Pushes a recent route key, and removes earlier routes when there is greater than the max length */\n const pushRecentRouteKey = (key: string): void => {\n recentRouteKeys.push(key);\n\n if (recentRouteKeys.length > NAVIGATION_HISTORY_MAX_SIZE) {\n recentRouteKeys = recentRouteKeys.slice(recentRouteKeys.length - NAVIGATION_HISTORY_MAX_SIZE);\n }\n };\n\n /** Cancels the latest transaction so it does not get sent to Sentry. */\n const _discardLatestTransaction = (): void => {\n if (latestNavigationSpan) {\n if (isSentrySpan(latestNavigationSpan)) {\n latestNavigationSpan['_sampled'] = false;\n }\n // TODO: What if it's not SentrySpan?\n latestNavigationSpan.end();\n latestNavigationSpan = undefined;\n }\n if (navigationProcessingSpan) {\n navigationProcessingSpan = undefined;\n }\n };\n\n const clearStateChangeTimeout = (): void => {\n if (typeof stateChangeTimeout !== 'undefined') {\n clearTimeout(stateChangeTimeout);\n stateChangeTimeout = undefined;\n }\n };\n\n return {\n name: INTEGRATION_NAME,\n afterAllSetup,\n registerNavigationContainer,\n };\n};\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"]}
1
+ {"version":3,"file":"reactnavigation.js","sourceRoot":"","sources":["../../../src/js/tracing/reactnavigation.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable max-lines */\nimport {\n addBreadcrumb,\n getActiveSpan,\n getClient,\n SEMANTIC_ATTRIBUTE_SENTRY_OP,\n SPAN_STATUS_OK,\n spanToJSON,\n startInactiveSpan,\n} from '@sentry/core';\nimport type { Client, Integration, Span } from '@sentry/types';\nimport { isPlainObject, logger, timestampInSeconds } from '@sentry/utils';\n\nimport type { NewFrameEvent } from '../utils/sentryeventemitter';\nimport { type SentryEventEmitter, createSentryEventEmitter, NewFrameEventName } from '../utils/sentryeventemitter';\nimport { isSentrySpan } from '../utils/span';\nimport { RN_GLOBAL_OBJ } from '../utils/worldwide';\nimport { NATIVE } from '../wrapper';\nimport { ignoreEmptyBackNavigation } from './onSpanEndUtils';\nimport type { ReactNativeTracingIntegration } from './reactnativetracing';\nimport { getReactNativeTracingIntegration } from './reactnativetracing';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from './semanticAttributes';\nimport {\n DEFAULT_NAVIGATION_SPAN_NAME,\n defaultIdleOptions,\n getDefaultIdleNavigationSpanOptions,\n startIdleNavigationSpan as startGenericIdleNavigationSpan,\n} from './span';\nimport { manualInitialDisplaySpans, startTimeToInitialDisplaySpan } from './timetodisplay';\nimport { setSpanDurationAsMeasurementOnSpan } from './utils';\n\nexport const INTEGRATION_NAME = 'ReactNavigation';\n\nconst NAVIGATION_HISTORY_MAX_SIZE = 200;\n\ninterface ReactNavigationIntegrationOptions {\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 *\n * @default 1_000 (ms)\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 /**\n * Does not sample transactions that are from routes that have been seen any more and don't have any spans.\n * This removes a lot of the clutter as most back navigation transactions are now ignored.\n *\n * @default true\n */\n ignoreEmptyBackNavigationTransactions: boolean;\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 const reactNavigationIntegration = ({\n routeChangeTimeoutMs = 1_000,\n enableTimeToInitialDisplay = false,\n ignoreEmptyBackNavigationTransactions = true,\n}: Partial<ReactNavigationIntegrationOptions> = {}): Integration & {\n /**\n * Pass the ref to the navigation container to register it to the instrumentation\n * @param navigationContainerRef Ref to a `NavigationContainer`\n */\n registerNavigationContainer: (navigationContainerRef: unknown) => void;\n} => {\n let navigationContainer: NavigationContainer | undefined;\n let newScreenFrameEventEmitter: SentryEventEmitter | undefined;\n\n let tracing: ReactNativeTracingIntegration | undefined;\n let idleSpanOptions: Parameters<typeof startGenericIdleNavigationSpan>[1] = defaultIdleOptions;\n let latestRoute: NavigationRoute | undefined;\n\n let latestNavigationSpan: Span | undefined;\n let navigationProcessingSpan: Span | undefined;\n\n let initialStateHandled: boolean = false;\n let stateChangeTimeout: ReturnType<typeof setTimeout> | undefined;\n let recentRouteKeys: string[] = [];\n\n if (enableTimeToInitialDisplay) {\n newScreenFrameEventEmitter = createSentryEventEmitter();\n newScreenFrameEventEmitter.initAsync(NewFrameEventName);\n NATIVE.initNativeReactNavigationNewFrameTracking().catch((reason: unknown) => {\n logger.error(`${INTEGRATION_NAME} Failed to initialize native new frame tracking: ${reason}`);\n });\n }\n\n /**\n * Set the initial state and start initial navigation span for the current screen.\n */\n const afterAllSetup = (client: Client): void => {\n tracing = getReactNativeTracingIntegration(client);\n if (tracing) {\n idleSpanOptions = {\n finalTimeout: tracing.options.finalTimeoutMs,\n idleTimeout: tracing.options.idleTimeoutMs,\n };\n }\n\n if (initialStateHandled) {\n // We create an initial state here to ensure a transaction gets created before the first route mounts.\n return undefined;\n }\n\n startIdleNavigationSpan();\n\n if (!navigationContainer) {\n // This is expected as navigation container is registered after the root component is mounted.\n return undefined;\n }\n\n // Navigation container already registered, just populate with route state\n updateLatestNavigationSpanWithCurrentRoute();\n initialStateHandled = true;\n };\n\n const registerNavigationContainer = (navigationContainerRef: unknown): 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 logger.log(\n `${INTEGRATION_NAME} Instrumentation already exists, but register has been called again, doing nothing.`,\n );\n return undefined;\n }\n\n if (isPlainObject(navigationContainerRef) && 'current' in navigationContainerRef) {\n navigationContainer = navigationContainerRef.current as NavigationContainer;\n } else {\n navigationContainer = navigationContainerRef as NavigationContainer;\n }\n if (!navigationContainer) {\n logger.warn(`${INTEGRATION_NAME} Received invalid navigation container ref!`);\n return undefined;\n }\n\n // This action is emitted on every dispatch\n navigationContainer.addListener('__unsafe_action__', startIdleNavigationSpan);\n navigationContainer.addListener('state', updateLatestNavigationSpanWithCurrentRoute);\n RN_GLOBAL_OBJ.__sentry_rn_v5_registered = true;\n\n if (initialStateHandled) {\n return undefined;\n }\n\n if (!latestNavigationSpan) {\n logger.log(`${INTEGRATION_NAME} Navigation container registered, but integration has not been setup yet.`);\n return undefined;\n }\n\n // Navigation Container is registered after the first navigation\n // Initial navigation span was started, after integration setup,\n // so now we populate it with the current route.\n updateLatestNavigationSpanWithCurrentRoute();\n initialStateHandled = true;\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 updateLatestNavigationSpanWithCurrentRoute\n */\n const startIdleNavigationSpan = (): void => {\n if (latestNavigationSpan) {\n logger.log(`${INTEGRATION_NAME} A transaction was detected that turned out to be a noop, discarding.`);\n _discardLatestTransaction();\n clearStateChangeTimeout();\n }\n\n latestNavigationSpan = startGenericIdleNavigationSpan(\n tracing && tracing.options.beforeStartSpan\n ? tracing.options.beforeStartSpan(getDefaultIdleNavigationSpanOptions())\n : getDefaultIdleNavigationSpanOptions(),\n idleSpanOptions,\n );\n if (ignoreEmptyBackNavigationTransactions) {\n ignoreEmptyBackNavigation(getClient(), latestNavigationSpan);\n }\n\n if (enableTimeToInitialDisplay) {\n navigationProcessingSpan = startInactiveSpan({\n op: 'navigation.processing',\n name: 'Navigation processing',\n startTime: latestNavigationSpan && spanToJSON(latestNavigationSpan).start_timestamp,\n });\n }\n\n stateChangeTimeout = setTimeout(_discardLatestTransaction, routeChangeTimeoutMs);\n };\n\n /**\n * To be called AFTER the state has been changed to populate the transaction with the current route.\n */\n const updateLatestNavigationSpanWithCurrentRoute = (): void => {\n const stateChangedTimestamp = timestampInSeconds();\n const previousRoute = latestRoute;\n\n if (!navigationContainer) {\n logger.warn(`${INTEGRATION_NAME} Missing navigation container ref. Route transactions will not be sent.`);\n return undefined;\n }\n\n const route = navigationContainer.getCurrentRoute();\n if (!route) {\n logger.debug(`[${INTEGRATION_NAME}] Navigation state changed, but no route is rendered.`);\n return undefined;\n }\n\n if (!latestNavigationSpan) {\n logger.debug(\n `[${INTEGRATION_NAME}] Navigation state changed, but navigation transaction was not started on dispatch.`,\n );\n return undefined;\n }\n\n if (previousRoute && previousRoute.key === route.key) {\n logger.debug(`[${INTEGRATION_NAME}] Navigation state changed, but route is the same as previous.`);\n pushRecentRouteKey(route.key);\n latestRoute = route;\n\n // Clear the latest transaction as it has been handled.\n latestNavigationSpan = undefined;\n return undefined;\n }\n\n const routeHasBeenSeen = recentRouteKeys.includes(route.key);\n const latestTtidSpan =\n !routeHasBeenSeen &&\n enableTimeToInitialDisplay &&\n startTimeToInitialDisplaySpan({\n name: `${route.name} initial display`,\n isAutoInstrumented: true,\n });\n\n const navigationSpanWithTtid = latestNavigationSpan;\n !routeHasBeenSeen &&\n latestTtidSpan &&\n newScreenFrameEventEmitter?.once(NewFrameEventName, ({ newFrameTimestampInSeconds }: NewFrameEvent) => {\n const activeSpan = getActiveSpan();\n if (activeSpan && manualInitialDisplaySpans.has(activeSpan)) {\n logger.warn('[ReactNavigationInstrumentation] Detected manual instrumentation for the current active span.');\n return;\n }\n\n latestTtidSpan.setStatus({ code: SPAN_STATUS_OK });\n latestTtidSpan.end(newFrameTimestampInSeconds);\n setSpanDurationAsMeasurementOnSpan('time_to_initial_display', latestTtidSpan, navigationSpanWithTtid);\n });\n\n navigationProcessingSpan?.updateName(`Processing navigation to ${route.name}`);\n navigationProcessingSpan?.setStatus({ code: SPAN_STATUS_OK });\n navigationProcessingSpan?.end(stateChangedTimestamp);\n navigationProcessingSpan = undefined;\n\n if (spanToJSON(latestNavigationSpan).description === DEFAULT_NAVIGATION_SPAN_NAME) {\n latestNavigationSpan.updateName(route.name);\n }\n latestNavigationSpan.setAttributes({\n 'route.name': route.name,\n 'route.key': route.key,\n // TODO: filter PII params instead of dropping them all\n // 'route.params': {},\n 'route.has_been_seen': routeHasBeenSeen,\n 'previous_route.name': previousRoute?.name,\n 'previous_route.key': previousRoute?.key,\n // TODO: filter PII params instead of dropping them all\n // 'previous_route.params': {},\n [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component',\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',\n });\n\n // Clear the timeout so the transaction does not get cancelled.\n clearStateChangeTimeout();\n\n addBreadcrumb({\n category: 'navigation',\n type: 'navigation',\n message: `Navigation to ${route.name}`,\n data: {\n from: previousRoute?.name,\n to: route.name,\n },\n });\n\n tracing?.setCurrentRoute(route.key);\n\n pushRecentRouteKey(route.key);\n latestRoute = route;\n // Clear the latest transaction as it has been handled.\n latestNavigationSpan = undefined;\n };\n\n /** Pushes a recent route key, and removes earlier routes when there is greater than the max length */\n const pushRecentRouteKey = (key: string): void => {\n recentRouteKeys.push(key);\n\n if (recentRouteKeys.length > NAVIGATION_HISTORY_MAX_SIZE) {\n recentRouteKeys = recentRouteKeys.slice(recentRouteKeys.length - NAVIGATION_HISTORY_MAX_SIZE);\n }\n };\n\n /** Cancels the latest transaction so it does not get sent to Sentry. */\n const _discardLatestTransaction = (): void => {\n if (latestNavigationSpan) {\n if (isSentrySpan(latestNavigationSpan)) {\n latestNavigationSpan['_sampled'] = false;\n }\n // TODO: What if it's not SentrySpan?\n latestNavigationSpan.end();\n latestNavigationSpan = undefined;\n }\n if (navigationProcessingSpan) {\n navigationProcessingSpan = undefined;\n }\n };\n\n const clearStateChangeTimeout = (): void => {\n if (typeof stateChangeTimeout !== 'undefined') {\n clearTimeout(stateChangeTimeout);\n stateChangeTimeout = undefined;\n }\n };\n\n return {\n name: INTEGRATION_NAME,\n afterAllSetup,\n registerNavigationContainer,\n };\n};\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"]}