@multiplayer-app/session-recorder-react-native 1.0.1-beta.4 → 1.0.1-beta.6

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 (180) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/AndroidManifest.xml +2 -2
  3. package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativeConfig.kt +1 -1
  4. package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativeModule.kt +9 -9
  5. package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativePackage.kt +2 -2
  6. package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/model/TargetInfo.kt +1 -1
  7. package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/util/ViewUtils.kt +2 -2
  8. package/lib/module/SessionRecorderNativeSpec.js +5 -0
  9. package/lib/module/SessionRecorderNativeSpec.js.map +1 -0
  10. package/lib/module/components/SessionRecorderWidget/ErrorBanner.js.map +1 -1
  11. package/lib/module/components/SessionRecorderWidget/ModalHeader.js.map +1 -1
  12. package/lib/module/components/SessionRecorderWidget/SessionRecorderWidget.js.map +1 -1
  13. package/lib/module/components/SessionRecorderWidget/icons.js.map +1 -1
  14. package/lib/module/components/SessionRecorderWidget/styles.js.map +1 -1
  15. package/lib/module/config/constants.js.map +1 -1
  16. package/lib/module/config/defaults.js.map +1 -1
  17. package/lib/module/config/masking.js.map +1 -1
  18. package/lib/module/config/session-recorder.js.map +1 -1
  19. package/lib/module/config/validators.js.map +1 -1
  20. package/lib/module/config/widget.js.map +1 -1
  21. package/lib/module/context/SessionRecorderStore.js.map +1 -1
  22. package/lib/module/context/useSessionRecorderStore.js.map +1 -1
  23. package/lib/module/context/useStoreSelector.js.map +1 -1
  24. package/lib/module/native/SessionRecorderNative.js +16 -11
  25. package/lib/module/native/SessionRecorderNative.js.map +1 -1
  26. package/lib/module/native/index.js.map +1 -1
  27. package/lib/module/otel/helpers.js +1 -1
  28. package/lib/module/otel/helpers.js.map +1 -1
  29. package/lib/module/otel/index.js.map +1 -1
  30. package/lib/module/otel/instrumentations/index.js.map +1 -1
  31. package/lib/module/patch/xhr.js.map +1 -1
  32. package/lib/module/recorder/eventExporter.js.map +1 -1
  33. package/lib/module/recorder/gestureRecorder.js.map +1 -1
  34. package/lib/module/recorder/index.js.map +1 -1
  35. package/lib/module/recorder/navigationTracker.js.map +1 -1
  36. package/lib/module/recorder/screenRecorder.js.map +1 -1
  37. package/lib/module/services/api.service.js.map +1 -1
  38. package/lib/module/services/network.service.js.map +1 -1
  39. package/lib/module/services/screenMaskingService.js +1 -1
  40. package/lib/module/services/screenMaskingService.js.map +1 -1
  41. package/lib/module/services/storage.service.js.map +1 -1
  42. package/lib/module/session-recorder.js.map +1 -1
  43. package/lib/module/types/index.js.map +1 -1
  44. package/lib/module/types/session-recorder.js.map +1 -1
  45. package/lib/module/utils/app-metadata.js +2 -2
  46. package/lib/module/utils/constants.optional.js.map +1 -1
  47. package/lib/module/utils/createStore.js.map +1 -1
  48. package/lib/module/utils/logger.js +0 -8
  49. package/lib/module/utils/logger.js.map +1 -1
  50. package/lib/module/utils/platform.js +1 -1
  51. package/lib/module/utils/platform.js.map +1 -1
  52. package/lib/module/utils/rrweb-events.js.map +1 -1
  53. package/lib/module/utils/session.js.map +1 -1
  54. package/lib/module/utils/shallowEqual.js.map +1 -1
  55. package/lib/module/utils/time.js.map +1 -1
  56. package/lib/module/version.js +1 -1
  57. package/lib/typescript/src/SessionRecorderNativeSpec.d.ts +42 -0
  58. package/lib/typescript/src/SessionRecorderNativeSpec.d.ts.map +1 -0
  59. package/lib/typescript/src/components/ScreenRecorderView/index.d.ts +1 -1
  60. package/lib/typescript/src/components/SessionRecorderWidget/ErrorBanner.d.ts.map +1 -1
  61. package/lib/typescript/src/components/SessionRecorderWidget/ModalHeader.d.ts.map +1 -1
  62. package/lib/typescript/src/components/SessionRecorderWidget/SessionRecorderWidget.d.ts.map +1 -1
  63. package/lib/typescript/src/components/SessionRecorderWidget/icons.d.ts.map +1 -1
  64. package/lib/typescript/src/components/SessionRecorderWidget/index.d.ts +1 -1
  65. package/lib/typescript/src/components/SessionRecorderWidget/styles.d.ts.map +1 -1
  66. package/lib/typescript/src/components/index.d.ts.map +1 -1
  67. package/lib/typescript/src/config/constants.d.ts.map +1 -1
  68. package/lib/typescript/src/config/defaults.d.ts.map +1 -1
  69. package/lib/typescript/src/config/index.d.ts.map +1 -1
  70. package/lib/typescript/src/config/masking.d.ts.map +1 -1
  71. package/lib/typescript/src/config/session-recorder.d.ts.map +1 -1
  72. package/lib/typescript/src/config/validators.d.ts.map +1 -1
  73. package/lib/typescript/src/config/widget.d.ts +1 -1
  74. package/lib/typescript/src/config/widget.d.ts.map +1 -1
  75. package/lib/typescript/src/context/SessionRecorderStore.d.ts.map +1 -1
  76. package/lib/typescript/src/context/useSessionRecorderStore.d.ts +3 -3
  77. package/lib/typescript/src/context/useSessionRecorderStore.d.ts.map +1 -1
  78. package/lib/typescript/src/context/useStoreSelector.d.ts.map +1 -1
  79. package/lib/typescript/src/index.d.ts.map +1 -1
  80. package/lib/typescript/src/native/SessionRecorderNative.d.ts +3 -54
  81. package/lib/typescript/src/native/SessionRecorderNative.d.ts.map +1 -1
  82. package/lib/typescript/src/native/index.d.ts +1 -1
  83. package/lib/typescript/src/native/index.d.ts.map +1 -1
  84. package/lib/typescript/src/otel/helpers.d.ts.map +1 -1
  85. package/lib/typescript/src/otel/index.d.ts.map +1 -1
  86. package/lib/typescript/src/otel/instrumentations/index.d.ts.map +1 -1
  87. package/lib/typescript/src/patch/index.d.ts.map +1 -1
  88. package/lib/typescript/src/patch/xhr.d.ts.map +1 -1
  89. package/lib/typescript/src/recorder/eventExporter.d.ts.map +1 -1
  90. package/lib/typescript/src/recorder/gestureRecorder.d.ts.map +1 -1
  91. package/lib/typescript/src/recorder/index.d.ts.map +1 -1
  92. package/lib/typescript/src/recorder/navigationTracker.d.ts.map +1 -1
  93. package/lib/typescript/src/recorder/screenRecorder.d.ts.map +1 -1
  94. package/lib/typescript/src/services/api.service.d.ts.map +1 -1
  95. package/lib/typescript/src/services/network.service.d.ts.map +1 -1
  96. package/lib/typescript/src/services/screenMaskingService.d.ts +1 -1
  97. package/lib/typescript/src/services/screenMaskingService.d.ts.map +1 -1
  98. package/lib/typescript/src/services/storage.service.d.ts.map +1 -1
  99. package/lib/typescript/src/session-recorder.d.ts.map +1 -1
  100. package/lib/typescript/src/types/configs.d.ts.map +1 -1
  101. package/lib/typescript/src/types/index.d.ts.map +1 -1
  102. package/lib/typescript/src/types/session-recorder.d.ts +4 -4
  103. package/lib/typescript/src/types/session-recorder.d.ts.map +1 -1
  104. package/lib/typescript/src/types/session.d.ts.map +1 -1
  105. package/lib/typescript/src/utils/app-metadata.d.ts.map +1 -1
  106. package/lib/typescript/src/utils/constants.optional.d.ts.map +1 -1
  107. package/lib/typescript/src/utils/constants.optional.expo.d.ts.map +1 -1
  108. package/lib/typescript/src/utils/createStore.d.ts.map +1 -1
  109. package/lib/typescript/src/utils/index.d.ts.map +1 -1
  110. package/lib/typescript/src/utils/logger.d.ts +1 -1
  111. package/lib/typescript/src/utils/logger.d.ts.map +1 -1
  112. package/lib/typescript/src/utils/platform.d.ts.map +1 -1
  113. package/lib/typescript/src/utils/request-utils.d.ts.map +1 -1
  114. package/lib/typescript/src/utils/rrweb-events.d.ts.map +1 -1
  115. package/lib/typescript/src/utils/session.d.ts.map +1 -1
  116. package/lib/typescript/src/utils/shallowEqual.d.ts.map +1 -1
  117. package/lib/typescript/src/utils/time.d.ts.map +1 -1
  118. package/lib/typescript/src/utils/type-utils.d.ts.map +1 -1
  119. package/lib/typescript/src/version.d.ts.map +1 -1
  120. package/package.json +2 -2
  121. package/src/SessionRecorderNativeSpec.ts +53 -0
  122. package/src/components/ScreenRecorderView/index.ts +1 -1
  123. package/src/components/SessionRecorderWidget/ErrorBanner.tsx +14 -14
  124. package/src/components/SessionRecorderWidget/ModalHeader.tsx +11 -9
  125. package/src/components/SessionRecorderWidget/SessionRecorderWidget.tsx +70 -56
  126. package/src/components/SessionRecorderWidget/icons.tsx +58 -30
  127. package/src/components/SessionRecorderWidget/index.ts +1 -1
  128. package/src/components/SessionRecorderWidget/styles.ts +17 -18
  129. package/src/components/index.ts +2 -2
  130. package/src/config/constants.ts +19 -20
  131. package/src/config/defaults.ts +35 -31
  132. package/src/config/index.ts +5 -5
  133. package/src/config/masking.ts +44 -18
  134. package/src/config/session-recorder.ts +54 -26
  135. package/src/config/validators.ts +43 -20
  136. package/src/config/widget.ts +24 -15
  137. package/src/context/SessionRecorderStore.ts +19 -18
  138. package/src/context/useSessionRecorderStore.ts +17 -10
  139. package/src/context/useStoreSelector.ts +20 -18
  140. package/src/index.ts +7 -7
  141. package/src/native/SessionRecorderNative.ts +68 -112
  142. package/src/native/index.ts +5 -1
  143. package/src/otel/helpers.ts +109 -93
  144. package/src/otel/index.ts +46 -49
  145. package/src/otel/instrumentations/index.ts +44 -41
  146. package/src/patch/index.ts +1 -1
  147. package/src/patch/xhr.ts +77 -78
  148. package/src/recorder/eventExporter.ts +63 -68
  149. package/src/recorder/gestureRecorder.ts +359 -212
  150. package/src/recorder/index.ts +75 -62
  151. package/src/recorder/navigationTracker.ts +120 -97
  152. package/src/recorder/screenRecorder.ts +214 -163
  153. package/src/services/api.service.ts +49 -48
  154. package/src/services/network.service.ts +67 -58
  155. package/src/services/screenMaskingService.ts +81 -50
  156. package/src/services/storage.service.ts +99 -70
  157. package/src/session-recorder.ts +270 -214
  158. package/src/types/configs.ts +53 -31
  159. package/src/types/expo-constants.d.ts +2 -2
  160. package/src/types/index.ts +16 -18
  161. package/src/types/session-recorder.ts +106 -111
  162. package/src/types/session.ts +45 -45
  163. package/src/utils/app-metadata.ts +9 -9
  164. package/src/utils/constants.optional.expo.ts +3 -3
  165. package/src/utils/constants.optional.ts +14 -12
  166. package/src/utils/createStore.ts +23 -20
  167. package/src/utils/index.ts +7 -7
  168. package/src/utils/logger.ts +87 -58
  169. package/src/utils/platform.ts +149 -118
  170. package/src/utils/request-utils.ts +15 -15
  171. package/src/utils/rrweb-events.ts +47 -34
  172. package/src/utils/session.ts +15 -12
  173. package/src/utils/shallowEqual.ts +16 -10
  174. package/src/utils/time.ts +7 -4
  175. package/src/utils/type-utils.ts +36 -36
  176. package/src/version.ts +1 -1
  177. package/android/src/main/java/com/multiplayer/sessionrecordernative/SessionRecorderNativeModuleSpec.kt +0 -51
  178. package/android/src/main/java/com/xxx/XxxModule.kt +0 -23
  179. package/ios/Xxx.h +0 -5
  180. package/ios/Xxx.mm +0 -21
@@ -1,89 +1,87 @@
1
- import { SessionType } from '@multiplayer-app/session-recorder-common'
1
+ import { SessionType } from '@multiplayer-app/session-recorder-common';
2
2
  // import { pack } from '@rrweb/packer' // Removed to avoid blob creation issues in Hermes
3
- import { EventExporter } from './eventExporter'
4
- import { logger } from '../utils'
5
- import { ScreenRecorder } from './screenRecorder'
6
- import { GestureRecorder } from './gestureRecorder'
7
- import { NavigationTracker } from './navigationTracker'
8
- import { type RecorderConfig, type EventRecorder } from '../types'
9
- import { type eventWithTime } from '@rrweb/types'
3
+ import { EventExporter } from './eventExporter';
4
+ import { logger } from '../utils';
5
+ import { ScreenRecorder } from './screenRecorder';
6
+ import { GestureRecorder } from './gestureRecorder';
7
+ import { NavigationTracker } from './navigationTracker';
8
+ import { type RecorderConfig, type EventRecorder } from '../types';
9
+ import { type eventWithTime } from '@rrweb/types';
10
10
  export class RecorderReactNativeSDK implements EventRecorder {
11
- private isRecording = false
12
- private config?: RecorderConfig
13
- private screenRecorder: ScreenRecorder
14
- private gestureRecorder: GestureRecorder
15
- private navigationTracker: NavigationTracker
16
- private recordedEvents: eventWithTime[] = []
17
- private exporter: EventExporter
18
- private sessionId: string | null = null
19
- private sessionType: SessionType = SessionType.PLAIN
20
-
11
+ private isRecording = false;
12
+ private config?: RecorderConfig;
13
+ private screenRecorder: ScreenRecorder;
14
+ private gestureRecorder: GestureRecorder;
15
+ private navigationTracker: NavigationTracker;
16
+ private recordedEvents: eventWithTime[] = [];
17
+ private exporter: EventExporter;
18
+ private sessionId: string | null = null;
19
+ private sessionType: SessionType = SessionType.PLAIN;
21
20
 
22
21
  constructor() {
23
- this.screenRecorder = new ScreenRecorder()
24
- this.gestureRecorder = new GestureRecorder()
25
- this.navigationTracker = new NavigationTracker()
22
+ this.screenRecorder = new ScreenRecorder();
23
+ this.gestureRecorder = new GestureRecorder();
24
+ this.navigationTracker = new NavigationTracker();
26
25
  this.exporter = new EventExporter({
27
26
  socketUrl: '',
28
27
  apiKey: '',
29
- })
28
+ });
30
29
  }
31
30
 
32
31
  init(config: RecorderConfig): void {
33
- this.config = config
34
- this.screenRecorder.init(config, this)
35
- this.navigationTracker.init(config, this.screenRecorder)
36
- this.gestureRecorder.init(config, this, this.screenRecorder)
32
+ this.config = config;
33
+ this.screenRecorder.init(config, this);
34
+ this.navigationTracker.init(config, this.screenRecorder);
35
+ this.gestureRecorder.init(config, this, this.screenRecorder);
37
36
 
38
- this.exporter.setApiKey(config.apiKey)
39
- this.exporter.setSocketUrl(config.apiBaseUrl)
37
+ this.exporter.setApiKey(config.apiKey);
38
+ this.exporter.setSocketUrl(config.apiBaseUrl);
40
39
  }
41
40
 
42
41
  setApiKey(apiKey: string): void {
43
- this.exporter.setApiKey(apiKey)
42
+ this.exporter.setApiKey(apiKey);
44
43
  }
45
44
 
46
45
  setSocketUrl(socketUrl: string): void {
47
- this.exporter.setSocketUrl(socketUrl)
46
+ this.exporter.setSocketUrl(socketUrl);
48
47
  }
49
48
 
50
49
  start(sessionId: string | null, sessionType: SessionType): void {
51
50
  if (!this.config) {
52
- throw new Error('Configuration not initialized. Call init() before start().')
51
+ throw new Error(
52
+ 'Configuration not initialized. Call init() before start().'
53
+ );
53
54
  }
54
55
 
55
- this.sessionId = sessionId
56
- this.sessionType = sessionType
57
- this.isRecording = true
56
+ this.sessionId = sessionId;
57
+ this.sessionType = sessionType;
58
+ this.isRecording = true;
58
59
 
59
60
  // Emit recording started meta event
60
61
 
61
62
  if (this.config.recordScreen) {
62
- this.screenRecorder.start()
63
+ this.screenRecorder.start();
63
64
  }
64
65
 
65
66
  if (this.config.recordGestures) {
66
- this.gestureRecorder.start()
67
+ this.gestureRecorder.start();
67
68
  }
68
69
 
69
70
  if (this.config.recordNavigation) {
70
- this.navigationTracker.start()
71
+ this.navigationTracker.start();
71
72
  }
72
-
73
-
74
73
  }
75
74
 
76
75
  stop(): void {
77
- this.isRecording = false
78
- this.gestureRecorder.stop()
79
- this.navigationTracker.stop()
80
- this.screenRecorder.stop()
81
- this.exporter?.close()
76
+ this.isRecording = false;
77
+ this.gestureRecorder.stop();
78
+ this.navigationTracker.stop();
79
+ this.screenRecorder.stop();
80
+ this.exporter?.close();
82
81
  }
83
82
 
84
-
85
83
  setNavigationRef(ref: any): void {
86
- this.navigationTracker.setNavigationRef(ref)
84
+ this.navigationTracker.setNavigationRef(ref);
87
85
  }
88
86
 
89
87
  /**
@@ -91,7 +89,7 @@ export class RecorderReactNativeSDK implements EventRecorder {
91
89
  * @param ref - React Native View ref for screen capture
92
90
  */
93
91
  setViewShotRef(ref: any): void {
94
- this.screenRecorder.setViewShotRef(ref)
92
+ this.screenRecorder.setViewShotRef(ref);
95
93
  }
96
94
 
97
95
  /**
@@ -100,11 +98,11 @@ export class RecorderReactNativeSDK implements EventRecorder {
100
98
  */
101
99
  recordEvent(event: eventWithTime): void {
102
100
  if (!this.isRecording) {
103
- return
101
+ return;
104
102
  }
105
103
 
106
104
  if (this.exporter) {
107
- logger.debug('RecorderReactNativeSDK', 'Sending to exporter', event)
105
+ logger.debug('RecorderReactNativeSDK', 'Sending to exporter', event);
108
106
  // Skip packing to avoid blob creation issues in Hermes
109
107
  // const packedEvent = pack(event)
110
108
  this.exporter.send({
@@ -113,7 +111,7 @@ export class RecorderReactNativeSDK implements EventRecorder {
113
111
  timestamp: event.timestamp,
114
112
  debugSessionId: this.sessionId,
115
113
  debugSessionType: this.sessionType,
116
- })
114
+ });
117
115
  }
118
116
  }
119
117
 
@@ -124,12 +122,17 @@ export class RecorderReactNativeSDK implements EventRecorder {
124
122
  * @param target - Target element identifier
125
123
  * @param pressure - Touch pressure
126
124
  */
127
- recordTouchStart(x: number, y: number, target?: string, pressure?: number): void {
125
+ recordTouchStart(
126
+ x: number,
127
+ y: number,
128
+ target?: string,
129
+ pressure?: number
130
+ ): void {
128
131
  if (!this.isRecording) {
129
- return
132
+ return;
130
133
  }
131
134
 
132
- this.gestureRecorder.recordTouchStart(x, y, target, pressure)
135
+ this.gestureRecorder.recordTouchStart(x, y, target, pressure);
133
136
  }
134
137
 
135
138
  /**
@@ -139,12 +142,17 @@ export class RecorderReactNativeSDK implements EventRecorder {
139
142
  * @param target - Target element identifier
140
143
  * @param pressure - Touch pressure
141
144
  */
142
- recordTouchMove(x: number, y: number, target?: string, pressure?: number): void {
145
+ recordTouchMove(
146
+ x: number,
147
+ y: number,
148
+ target?: string,
149
+ pressure?: number
150
+ ): void {
143
151
  if (!this.isRecording) {
144
- return
152
+ return;
145
153
  }
146
154
 
147
- this.gestureRecorder.recordTouchMove(x, y, target, pressure)
155
+ this.gestureRecorder.recordTouchMove(x, y, target, pressure);
148
156
  }
149
157
 
150
158
  /**
@@ -154,12 +162,17 @@ export class RecorderReactNativeSDK implements EventRecorder {
154
162
  * @param target - Target element identifier
155
163
  * @param pressure - Touch pressure
156
164
  */
157
- recordTouchEnd(x: number, y: number, target?: string, pressure?: number): void {
165
+ recordTouchEnd(
166
+ x: number,
167
+ y: number,
168
+ target?: string,
169
+ pressure?: number
170
+ ): void {
158
171
  if (!this.isRecording) {
159
- return
172
+ return;
160
173
  }
161
174
 
162
- this.gestureRecorder.recordTouchEnd(x, y, target, pressure)
175
+ this.gestureRecorder.recordTouchEnd(x, y, target, pressure);
163
176
  }
164
177
 
165
178
  /**
@@ -167,14 +180,14 @@ export class RecorderReactNativeSDK implements EventRecorder {
167
180
  * @returns Array of recorded rrweb events
168
181
  */
169
182
  getRecordedEvents(): eventWithTime[] {
170
- return [...this.recordedEvents]
183
+ return [...this.recordedEvents];
171
184
  }
172
185
 
173
186
  /**
174
187
  * Clear all recorded events
175
188
  */
176
189
  clearRecordedEvents(): void {
177
- this.recordedEvents = []
190
+ this.recordedEvents = [];
178
191
  }
179
192
 
180
193
  /**
@@ -185,6 +198,6 @@ export class RecorderReactNativeSDK implements EventRecorder {
185
198
  return {
186
199
  totalEvents: this.recordedEvents.length,
187
200
  isRecording: this.isRecording,
188
- }
201
+ };
189
202
  }
190
203
  }
@@ -1,87 +1,98 @@
1
- import { type NavigationEvent, type RecorderConfig } from '../types'
2
- import { trace, SpanStatusCode } from '@opentelemetry/api'
3
- import { logger } from '../utils'
1
+ import { type NavigationEvent, type RecorderConfig } from '../types';
2
+ import { trace, SpanStatusCode } from '@opentelemetry/api';
3
+ import { logger } from '../utils';
4
4
 
5
5
  export class NavigationTracker {
6
- private config?: RecorderConfig
7
- private isRecording = false
8
- private navigationRef: any = null
9
- private navigationListeners: Map<string, any> = new Map()
10
- private currentRoute: string | null = null
11
- private navigationStack: string[] = []
12
- private navigationStartTime: number = 0
13
- private screenRecorder?: any // Reference to screen recorder for force capture
6
+ private config?: RecorderConfig;
7
+ private isRecording = false;
8
+ private navigationRef: any = null;
9
+ private navigationListeners: Map<string, any> = new Map();
10
+ private currentRoute: string | null = null;
11
+ private navigationStack: string[] = [];
12
+ private navigationStartTime: number = 0;
13
+ private screenRecorder?: any; // Reference to screen recorder for force capture
14
14
 
15
15
  init(config: RecorderConfig, screenRecorder?: any): void {
16
- this.config = config
17
- this.screenRecorder = screenRecorder
18
- logger.info('NavigationTracker', 'Navigation tracker initialized',
19
- { config: this.config, screenRecorder: this.screenRecorder })
16
+ this.config = config;
17
+ this.screenRecorder = screenRecorder;
18
+ logger.info('NavigationTracker', 'Navigation tracker initialized', {
19
+ config: this.config,
20
+ screenRecorder: this.screenRecorder,
21
+ });
20
22
  }
21
23
 
22
24
  setNavigationRef(ref: any): void {
23
- this.navigationRef = ref
25
+ this.navigationRef = ref;
24
26
  if (this.isRecording) {
25
- this._setupNavigationListener()
27
+ this._setupNavigationListener();
26
28
  }
27
29
  }
28
30
 
29
31
  start(): void {
30
- logger.info('NavigationTracker', 'Navigation tracking started')
31
- this.isRecording = true
32
- this.navigationStack = []
33
- this.navigationStartTime = Date.now()
34
- this._setupNavigationListener()
32
+ logger.info('NavigationTracker', 'Navigation tracking started');
33
+ this.isRecording = true;
34
+ this.navigationStack = [];
35
+ this.navigationStartTime = Date.now();
36
+ this._setupNavigationListener();
35
37
  // Navigation tracking started
36
38
  }
37
39
 
38
40
  stop(): void {
39
- this.isRecording = false
40
- this._removeNavigationListener()
41
+ this.isRecording = false;
42
+ this._removeNavigationListener();
41
43
  // Navigation tracking stopped
42
44
  }
43
45
 
44
46
  pause(): void {
45
- this.isRecording = false
47
+ this.isRecording = false;
46
48
  }
47
49
 
48
50
  resume(): void {
49
- this.isRecording = true
50
- this._setupNavigationListener()
51
+ this.isRecording = true;
52
+ this._setupNavigationListener();
51
53
  }
52
54
 
53
55
  private _setupNavigationListener(): void {
54
56
  if (!this.navigationRef) {
55
57
  // Navigation ref not set - silently continue
56
- return
58
+ return;
57
59
  }
58
60
 
59
61
  try {
60
62
  // Listen to navigation state changes
61
- const stateListener = this.navigationRef.addListener('state', (e: any) => {
62
- this._recordNavigationEvent('state_change', e.data)
63
- })
63
+ const stateListener = this.navigationRef.addListener(
64
+ 'state',
65
+ (e: any) => {
66
+ this._recordNavigationEvent('state_change', e.data);
67
+ }
68
+ );
64
69
 
65
70
  // Listen to focus events
66
- const focusListener = this.navigationRef.addListener('focus', (e: any) => {
67
- this._recordNavigationEvent('focus', e.data)
68
- })
71
+ const focusListener = this.navigationRef.addListener(
72
+ 'focus',
73
+ (e: any) => {
74
+ this._recordNavigationEvent('focus', e.data);
75
+ }
76
+ );
69
77
 
70
78
  // Listen to blur events
71
79
  const blurListener = this.navigationRef.addListener('blur', (e: any) => {
72
- this._recordNavigationEvent('blur', e.data)
73
- })
80
+ this._recordNavigationEvent('blur', e.data);
81
+ });
74
82
 
75
83
  // Listen to beforeRemove events
76
- const beforeRemoveListener = this.navigationRef.addListener('beforeRemove', (e: any) => {
77
- this._recordNavigationEvent('beforeRemove', e.data)
78
- })
84
+ const beforeRemoveListener = this.navigationRef.addListener(
85
+ 'beforeRemove',
86
+ (e: any) => {
87
+ this._recordNavigationEvent('beforeRemove', e.data);
88
+ }
89
+ );
79
90
 
80
91
  // Store listeners for cleanup
81
- this.navigationListeners.set('state', stateListener)
82
- this.navigationListeners.set('focus', focusListener)
83
- this.navigationListeners.set('blur', blurListener)
84
- this.navigationListeners.set('beforeRemove', beforeRemoveListener)
92
+ this.navigationListeners.set('state', stateListener);
93
+ this.navigationListeners.set('focus', focusListener);
94
+ this.navigationListeners.set('blur', blurListener);
95
+ this.navigationListeners.set('beforeRemove', beforeRemoveListener);
85
96
 
86
97
  // Navigation listeners setup complete
87
98
  } catch (error) {
@@ -94,10 +105,10 @@ export class NavigationTracker {
94
105
  // Remove all listeners
95
106
  this.navigationListeners.forEach((listener, _) => {
96
107
  if (listener && typeof listener.remove === 'function') {
97
- listener.remove()
108
+ listener.remove();
98
109
  }
99
- })
100
- this.navigationListeners.clear()
110
+ });
111
+ this.navigationListeners.clear();
101
112
  // Navigation listeners removed
102
113
  } catch (error) {
103
114
  // Failed to remove navigation listeners - silently continue
@@ -106,38 +117,49 @@ export class NavigationTracker {
106
117
 
107
118
  private _getFriendlyRouteTitle(): string | null {
108
119
  try {
109
- const current = this.navigationRef?.getCurrentRoute?.()
110
- if (!current) return null
120
+ const current = this.navigationRef?.getCurrentRoute?.();
121
+ if (!current) return null;
111
122
  // Prefer a title set via navigation.setOptions({ title }) if present in params
112
- const titleFromParams = (current.params && (current.params as any).title) as string | undefined
123
+ const titleFromParams = (current.params &&
124
+ (current.params as any).title) as string | undefined;
113
125
  if (titleFromParams && typeof titleFromParams === 'string') {
114
- return titleFromParams
126
+ return titleFromParams;
115
127
  }
116
128
 
117
129
  // Fallback to a prettified route name (handles Expo Router style names like 'user-posts/[id]')
118
130
  if (current.name && typeof current.name === 'string') {
119
- const raw = current.name as string
131
+ const raw = current.name as string;
120
132
  // Remove group segments like "(tabs)/" at the beginning
121
- const withoutGroups = raw.replace(/^\([^)]*\)\//, '')
133
+ const withoutGroups = raw.replace(/^\([^)]*\)\//, '');
122
134
  // Take last path segment (e.g., 'user-posts/[id]' -> '[id]' is removed later, 'post/[id]' -> 'post')
123
- const lastSegment = withoutGroups.split('/').filter(Boolean).slice(-2).join(' ')
135
+ const lastSegment = withoutGroups
136
+ .split('/')
137
+ .filter(Boolean)
138
+ .slice(-2)
139
+ .join(' ');
124
140
  // Remove dynamic segments like '[id]'
125
- const withoutParams = lastSegment.replace(/\[[^\]]+\]/g, '').trim()
141
+ const withoutParams = lastSegment.replace(/\[[^\]]+\]/g, '').trim();
126
142
  // Replace dashes/underscores with spaces and collapse spaces
127
- const spaced = withoutParams.replace(/[-_]+/g, ' ').replace(/\s+/g, ' ').trim()
143
+ const spaced = withoutParams
144
+ .replace(/[-_]+/g, ' ')
145
+ .replace(/\s+/g, ' ')
146
+ .trim();
128
147
  // Title case
129
- const titleCase = spaced.split(' ').map(w => w ? (w.charAt(0).toUpperCase() + w.slice(1)) : w).join(' ')
130
- return titleCase || raw
148
+ const titleCase = spaced
149
+ .split(' ')
150
+ .map((w) => (w ? w.charAt(0).toUpperCase() + w.slice(1) : w))
151
+ .join(' ');
152
+ return titleCase || raw;
131
153
  }
132
154
 
133
- return null
155
+ return null;
134
156
  } catch {
135
- return null
157
+ return null;
136
158
  }
137
159
  }
138
160
 
139
161
  private _recordNavigationEvent(eventType: string, data: any): void {
140
- if (!this.isRecording) return
162
+ if (!this.isRecording) return;
141
163
 
142
164
  const event: NavigationEvent = {
143
165
  type: 'navigate',
@@ -147,74 +169,75 @@ export class NavigationTracker {
147
169
  navigationDuration: Date.now() - this.navigationStartTime,
148
170
  stackDepth: this.navigationStack.length,
149
171
  },
150
- }
172
+ };
151
173
 
152
- const currentRoute = this.navigationRef?.getCurrentRoute?.() || {}
153
- const friendlyTitle = this._getFriendlyRouteTitle()
154
- const routeName = data?.routeName || currentRoute?.name
155
- const params = data?.params || currentRoute?.params
156
- const key = data?.key || currentRoute?.key
174
+ const currentRoute = this.navigationRef?.getCurrentRoute?.() || {};
175
+ const friendlyTitle = this._getFriendlyRouteTitle();
176
+ const routeName = data?.routeName || currentRoute?.name;
177
+ const params = data?.params || currentRoute?.params;
178
+ const key = data?.key || currentRoute?.key;
157
179
 
158
180
  if (routeName) {
159
- event.routeName = routeName
160
- this._updateNavigationStack(routeName, eventType)
181
+ event.routeName = routeName;
182
+ this._updateNavigationStack(routeName, eventType);
161
183
  }
162
184
  if (params) {
163
- event.params = params
185
+ event.params = params;
164
186
  }
165
187
  if (key) {
166
- event.metadata!.routeKey = key
188
+ event.metadata!.routeKey = key;
167
189
  }
168
190
  if (friendlyTitle) {
169
- event.metadata!.friendlyRouteName = friendlyTitle
191
+ event.metadata!.friendlyRouteName = friendlyTitle;
170
192
  }
171
- this._recordOpenTelemetrySpan(event)
193
+ this._recordOpenTelemetrySpan(event);
172
194
 
173
195
  // Force screen capture on navigation events
174
196
  // this.screenRecorder?.forceCapture(event.timestamp)
175
197
  }
176
198
 
177
-
178
199
  private _updateNavigationStack(routeName: string, eventType: string): void {
179
200
  if (eventType === 'focus' || eventType === 'state_change') {
180
201
  if (this.currentRoute !== routeName) {
181
- this.currentRoute = routeName
182
- this.navigationStack.push(routeName)
202
+ this.currentRoute = routeName;
203
+ this.navigationStack.push(routeName);
183
204
  }
184
205
  } else if (eventType === 'blur' || eventType === 'beforeRemove') {
185
- const index = this.navigationStack.indexOf(routeName)
206
+ const index = this.navigationStack.indexOf(routeName);
186
207
  if (index > -1) {
187
- this.navigationStack.splice(index, 1)
208
+ this.navigationStack.splice(index, 1);
188
209
  }
189
210
  }
190
211
  }
191
212
 
192
213
  private _recordOpenTelemetrySpan(event: NavigationEvent): void {
193
214
  try {
194
- const span = trace.getTracer('navigation').startSpan(`Navigation.${event.type}`, {
195
- attributes: {
196
- 'navigation.system': 'ReactNavigation',
197
- 'navigation.operation': event.type,
198
- 'navigation.type': event.type,
199
- 'navigation.timestamp': event.timestamp,
200
- 'navigation.platform': 'react-native',
201
- },
202
- })
215
+ const span = trace
216
+ .getTracer('navigation')
217
+ .startSpan(`Navigation.${event.type}`, {
218
+ attributes: {
219
+ 'navigation.system': 'ReactNavigation',
220
+ 'navigation.operation': event.type,
221
+ 'navigation.type': event.type,
222
+ 'navigation.timestamp': event.timestamp,
223
+ 'navigation.platform': 'react-native',
224
+ },
225
+ });
203
226
 
204
227
  if (event.routeName) {
205
- span.setAttribute('navigation.route_name', event.routeName)
228
+ span.setAttribute('navigation.route_name', event.routeName);
206
229
  }
207
230
  if (event.params) {
208
- span.setAttribute('navigation.params', JSON.stringify(event.params))
231
+ span.setAttribute('navigation.params', JSON.stringify(event.params));
209
232
  }
210
233
  if (event.metadata) {
211
234
  Object.entries(event.metadata).forEach(([key, value]) => {
212
- span.setAttribute(`navigation.metadata.${key}`, String(value))
213
- })
235
+ span.setAttribute(`navigation.metadata.${key}`, String(value));
236
+ });
214
237
  }
215
238
 
216
- span.setStatus({ code: SpanStatusCode.OK })
217
- span.end()
239
+ span.setStatus({ code: SpanStatusCode.OK });
240
+ span.end();
218
241
  } catch (error) {
219
242
  // Failed to record OpenTelemetry span for navigation - silently continue
220
243
  }
@@ -222,24 +245,24 @@ export class NavigationTracker {
222
245
 
223
246
  // Get current navigation state
224
247
  getCurrentRoute(): string | null {
225
- return this.currentRoute
248
+ return this.currentRoute;
226
249
  }
227
250
 
228
251
  getNavigationStack(): string[] {
229
- return [...this.navigationStack]
252
+ return [...this.navigationStack];
230
253
  }
231
254
 
232
255
  getNavigationDepth(): number {
233
- return this.navigationStack.length
256
+ return this.navigationStack.length;
234
257
  }
235
258
 
236
259
  // Get recording status
237
260
  isRecordingEnabled(): boolean {
238
- return this.isRecording
261
+ return this.isRecording;
239
262
  }
240
263
 
241
264
  // Get navigation duration
242
265
  getNavigationDuration(): number {
243
- return Date.now() - this.navigationStartTime
266
+ return Date.now() - this.navigationStartTime;
244
267
  }
245
268
  }