@multiplayer-app/session-recorder-react-native 0.0.1-beta.1 → 0.0.1-beta.11

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 (162) hide show
  1. package/SessionRecorderNative.podspec +29 -0
  2. package/android/build.gradle +32 -0
  3. package/copy-react-native-dist.sh +4 -10
  4. package/dist/components/MaskableComponent.d.ts +22 -0
  5. package/dist/components/MaskableComponent.js +1 -0
  6. package/dist/components/MaskableComponent.js.map +1 -0
  7. package/dist/components/MaskableTextInput.d.ts +14 -0
  8. package/dist/components/MaskableTextInput.js +1 -0
  9. package/dist/components/MaskableTextInput.js.map +1 -0
  10. package/dist/components/SessionRecorderWidget/FinalPopover.d.ts +11 -0
  11. package/dist/components/SessionRecorderWidget/FinalPopover.js +1 -0
  12. package/dist/components/SessionRecorderWidget/FinalPopover.js.map +1 -0
  13. package/dist/components/SessionRecorderWidget/FloatingButton.d.ts +8 -0
  14. package/dist/components/SessionRecorderWidget/FloatingButton.js +1 -0
  15. package/dist/components/SessionRecorderWidget/FloatingButton.js.map +1 -0
  16. package/dist/components/SessionRecorderWidget/InitialPopover.d.ts +13 -0
  17. package/dist/components/SessionRecorderWidget/InitialPopover.js +1 -0
  18. package/dist/components/SessionRecorderWidget/InitialPopover.js.map +1 -0
  19. package/dist/components/SessionRecorderWidget/ModalContainer.d.ts +8 -0
  20. package/dist/components/SessionRecorderWidget/ModalContainer.js +1 -0
  21. package/dist/components/SessionRecorderWidget/ModalContainer.js.map +1 -0
  22. package/dist/components/SessionRecorderWidget/ModalHeader.d.ts +6 -0
  23. package/dist/components/SessionRecorderWidget/ModalHeader.js +1 -0
  24. package/dist/components/SessionRecorderWidget/ModalHeader.js.map +1 -0
  25. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.d.ts +5 -0
  26. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js +1 -0
  27. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js.map +1 -0
  28. package/dist/components/SessionRecorderWidget/icons.d.ts +11 -0
  29. package/dist/components/SessionRecorderWidget/icons.js +1 -0
  30. package/dist/components/SessionRecorderWidget/icons.js.map +1 -0
  31. package/dist/components/SessionRecorderWidget/index.d.ts +2 -0
  32. package/dist/components/SessionRecorderWidget/index.js +1 -0
  33. package/dist/components/SessionRecorderWidget/index.js.map +1 -0
  34. package/dist/components/SessionRecorderWidget/styles.d.ts +145 -0
  35. package/dist/components/SessionRecorderWidget/styles.js +1 -0
  36. package/dist/components/SessionRecorderWidget/styles.js.map +1 -0
  37. package/dist/components/index.d.ts +2 -0
  38. package/dist/components/index.js +1 -1
  39. package/dist/components/index.js.map +1 -1
  40. package/dist/config/defaults.js +1 -1
  41. package/dist/config/defaults.js.map +1 -1
  42. package/dist/config/masking.js +1 -1
  43. package/dist/config/masking.js.map +1 -1
  44. package/dist/context/SessionRecorderContext.d.ts +5 -3
  45. package/dist/context/SessionRecorderContext.js +1 -1
  46. package/dist/context/SessionRecorderContext.js.map +1 -1
  47. package/dist/index.d.ts +0 -1
  48. package/dist/index.js +1 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/native/ScreenMasking.d.ts +21 -0
  51. package/dist/native/ScreenMasking.js +1 -0
  52. package/dist/native/ScreenMasking.js.map +1 -0
  53. package/dist/native/SessionRecorderNative.d.ts +21 -0
  54. package/dist/native/SessionRecorderNative.js +1 -0
  55. package/dist/native/SessionRecorderNative.js.map +1 -0
  56. package/dist/patch/xhr.js +1 -1
  57. package/dist/patch/xhr.js.map +1 -1
  58. package/dist/recorder/screenRecorder.d.ts +1 -0
  59. package/dist/recorder/screenRecorder.js +1 -1
  60. package/dist/recorder/screenRecorder.js.map +1 -1
  61. package/dist/recorder/screenshotManager.d.ts +10 -0
  62. package/dist/recorder/screenshotManager.js +1 -0
  63. package/dist/recorder/screenshotManager.js.map +1 -0
  64. package/dist/services/screenMaskingService.d.ts +39 -0
  65. package/dist/services/screenMaskingService.js +1 -0
  66. package/dist/services/screenMaskingService.js.map +1 -0
  67. package/dist/services/storage.service.d.ts +18 -2
  68. package/dist/services/storage.service.js +1 -1
  69. package/dist/services/storage.service.js.map +1 -1
  70. package/dist/session-recorder.d.ts +2 -1
  71. package/dist/session-recorder.js +1 -1
  72. package/dist/session-recorder.js.map +1 -1
  73. package/dist/types/session-recorder.d.ts +6 -0
  74. package/dist/types/session-recorder.js.map +1 -1
  75. package/dist/utils/componentRegistry.d.ts +64 -0
  76. package/dist/utils/componentRegistry.js +1 -0
  77. package/dist/utils/componentRegistry.js.map +1 -0
  78. package/dist/utils/nativeModuleTest.d.ts +8 -0
  79. package/dist/utils/nativeModuleTest.js +1 -0
  80. package/dist/utils/nativeModuleTest.js.map +1 -0
  81. package/dist/utils/platform.d.ts +3 -0
  82. package/dist/utils/reactNativeHierarchyExtractor.d.ts +38 -0
  83. package/dist/utils/reactNativeHierarchyExtractor.js +1 -0
  84. package/dist/utils/reactNativeHierarchyExtractor.js.map +1 -0
  85. package/dist/utils/screenshotMasker.d.ts +96 -0
  86. package/dist/utils/screenshotMasker.js +1 -0
  87. package/dist/utils/screenshotMasker.js.map +1 -0
  88. package/dist/utils/viewHierarchyTracker.d.ts +89 -0
  89. package/dist/utils/viewHierarchyTracker.js +1 -0
  90. package/dist/utils/viewHierarchyTracker.js.map +1 -0
  91. package/ios/SessionRecorderNative.m +17 -0
  92. package/ios/SessionRecorderNative.podspec +26 -0
  93. package/ios/SessionRecorderNative.swift +205 -0
  94. package/package.json +22 -7
  95. package/react-native.config.js +15 -0
  96. package/RRWEB_INTEGRATION.md +0 -336
  97. package/VIEWSHOT_INTEGRATION_TEST.md +0 -123
  98. package/babel.config.js +0 -13
  99. package/dist/components/GestureCaptureWrapper.d.ts +0 -6
  100. package/dist/components/GestureCaptureWrapper.js +0 -1
  101. package/dist/components/GestureCaptureWrapper.js.map +0 -1
  102. package/dist/expo.d.ts +0 -7
  103. package/dist/expo.js +0 -1
  104. package/dist/expo.js.map +0 -1
  105. package/dist/otel/instrumentations/gestureInstrumentation.d.ts +0 -15
  106. package/dist/otel/instrumentations/gestureInstrumentation.js +0 -1
  107. package/dist/otel/instrumentations/gestureInstrumentation.js.map +0 -1
  108. package/dist/otel/instrumentations/reactNativeInstrumentation.d.ts +0 -8
  109. package/dist/otel/instrumentations/reactNativeInstrumentation.js +0 -1
  110. package/dist/otel/instrumentations/reactNativeInstrumentation.js.map +0 -1
  111. package/dist/otel/instrumentations/reactNavigationInstrumentation.d.ts +0 -13
  112. package/dist/otel/instrumentations/reactNavigationInstrumentation.js +0 -1
  113. package/dist/otel/instrumentations/reactNavigationInstrumentation.js.map +0 -1
  114. package/dist/recorder/gestureHandlerRecorder.d.ts +0 -19
  115. package/dist/recorder/gestureHandlerRecorder.js +0 -1
  116. package/dist/recorder/gestureHandlerRecorder.js.map +0 -1
  117. package/dist/types/rrweb.d.ts +0 -118
  118. package/dist/types/rrweb.js +0 -1
  119. package/dist/types/rrweb.js.map +0 -1
  120. package/scripts/generate-app-metadata.js +0 -173
  121. package/src/components/GestureCaptureWrapper/GestureCaptureWrapper.tsx +0 -86
  122. package/src/components/GestureCaptureWrapper/index.ts +0 -1
  123. package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +0 -72
  124. package/src/components/ScreenRecorderView/index.ts +0 -1
  125. package/src/components/index.ts +0 -1
  126. package/src/config/constants.ts +0 -60
  127. package/src/config/defaults.ts +0 -82
  128. package/src/config/index.ts +0 -6
  129. package/src/config/masking.ts +0 -27
  130. package/src/config/session-recorder.ts +0 -55
  131. package/src/config/validators.ts +0 -31
  132. package/src/context/SessionRecorderContext.tsx +0 -75
  133. package/src/expo.ts +0 -11
  134. package/src/index.ts +0 -17
  135. package/src/otel/helpers.ts +0 -275
  136. package/src/otel/index.ts +0 -138
  137. package/src/otel/instrumentations/index.ts +0 -115
  138. package/src/patch/index.ts +0 -1
  139. package/src/patch/xhr.ts +0 -142
  140. package/src/recorder/eventExporter.ts +0 -141
  141. package/src/recorder/gestureRecorder.ts +0 -498
  142. package/src/recorder/index.ts +0 -179
  143. package/src/recorder/navigationTracker.ts +0 -449
  144. package/src/recorder/screenRecorder.ts +0 -498
  145. package/src/services/api.service.ts +0 -203
  146. package/src/services/storage.service.ts +0 -158
  147. package/src/session-recorder.ts +0 -600
  148. package/src/types/expo.d.ts +0 -23
  149. package/src/types/index.ts +0 -28
  150. package/src/types/session-recorder.ts +0 -423
  151. package/src/types/session.ts +0 -65
  152. package/src/utils/app-metadata.ts +0 -31
  153. package/src/utils/index.ts +0 -8
  154. package/src/utils/logger.ts +0 -225
  155. package/src/utils/platform.ts +0 -384
  156. package/src/utils/request-utils.ts +0 -61
  157. package/src/utils/rrweb-events.ts +0 -309
  158. package/src/utils/session.ts +0 -18
  159. package/src/utils/time.ts +0 -17
  160. package/src/utils/type-utils.ts +0 -75
  161. package/src/version.ts +0 -1
  162. package/tsconfig.json +0 -24
@@ -1,179 +0,0 @@
1
- import { SessionType } from '@multiplayer-app/session-recorder-common'
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 { RecorderConfig, EventRecorder } from '../types'
9
- import { eventWithTime } from '@rrweb/types'
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 | undefined
18
- private sessionId: string | null = null
19
- private sessionType: SessionType = SessionType.PLAIN
20
-
21
-
22
- constructor() {
23
- this.screenRecorder = new ScreenRecorder()
24
- this.gestureRecorder = new GestureRecorder()
25
- this.navigationTracker = new NavigationTracker()
26
- }
27
-
28
- init(config: RecorderConfig): void {
29
- this.config = config
30
- this.gestureRecorder.init(config, this, this.screenRecorder)
31
- this.navigationTracker.init(config)
32
- this.screenRecorder.init(config, this)
33
- this.exporter = new EventExporter({
34
- socketUrl: config.apiBaseUrl || '',
35
- apiKey: config.apiKey,
36
- })
37
- }
38
-
39
- start(sessionId: string | null, sessionType: SessionType): void {
40
- if (!this.config) {
41
- throw new Error('Configuration not initialized. Call init() before start().')
42
- }
43
-
44
- this.sessionId = sessionId
45
- this.sessionType = sessionType
46
- this.isRecording = true
47
-
48
- // Emit recording started meta event
49
-
50
- if (this.config.recordScreen) {
51
- this.screenRecorder.start()
52
- }
53
-
54
- if (this.config.recordGestures) {
55
- this.gestureRecorder.start()
56
- }
57
-
58
- if (this.config.recordNavigation) {
59
- this.navigationTracker.start()
60
- }
61
-
62
-
63
- }
64
-
65
- stop(): void {
66
- this.isRecording = false
67
- this.gestureRecorder.stop()
68
- this.navigationTracker.stop()
69
- this.screenRecorder.stop()
70
- this.exporter?.close()
71
- }
72
-
73
-
74
- setNavigationRef(ref: any): void {
75
- this.navigationTracker.setNavigationRef(ref)
76
- }
77
-
78
- /**
79
- * Set the viewshot ref for screen capture
80
- * @param ref - React Native View ref for screen capture
81
- */
82
- setViewShotRef(ref: any): void {
83
- this.screenRecorder.setViewShotRef(ref)
84
- }
85
-
86
- /**
87
- * Record an rrweb event
88
- * @param event - The rrweb event to record
89
- */
90
- recordEvent(event: eventWithTime): void {
91
- if (!this.isRecording) {
92
- return
93
- }
94
-
95
- if (this.exporter) {
96
- logger.debug('RecorderReactNativeSDK', 'Sending to exporter', event)
97
- // Skip packing to avoid blob creation issues in Hermes
98
- // const packedEvent = pack(event)
99
- this.exporter.send({
100
- event: event, // Send raw event instead of packed
101
- eventType: event.type,
102
- timestamp: event.timestamp,
103
- debugSessionId: this.sessionId,
104
- debugSessionType: this.sessionType,
105
- })
106
- }
107
- }
108
-
109
- /**
110
- * Record touch start event
111
- * @param x - X coordinate
112
- * @param y - Y coordinate
113
- * @param target - Target element identifier
114
- * @param pressure - Touch pressure
115
- */
116
- recordTouchStart(x: number, y: number, target?: string, pressure?: number): void {
117
- if (!this.isRecording) {
118
- return
119
- }
120
-
121
- this.gestureRecorder.recordTouchStart(x, y, target, pressure)
122
- }
123
-
124
- /**
125
- * Record touch move event
126
- * @param x - X coordinate
127
- * @param y - Y coordinate
128
- * @param target - Target element identifier
129
- * @param pressure - Touch pressure
130
- */
131
- recordTouchMove(x: number, y: number, target?: string, pressure?: number): void {
132
- if (!this.isRecording) {
133
- return
134
- }
135
-
136
- this.gestureRecorder.recordTouchMove(x, y, target, pressure)
137
- }
138
-
139
- /**
140
- * Record touch end event
141
- * @param x - X coordinate
142
- * @param y - Y coordinate
143
- * @param target - Target element identifier
144
- * @param pressure - Touch pressure
145
- */
146
- recordTouchEnd(x: number, y: number, target?: string, pressure?: number): void {
147
- if (!this.isRecording) {
148
- return
149
- }
150
-
151
- this.gestureRecorder.recordTouchEnd(x, y, target, pressure)
152
- }
153
-
154
- /**
155
- * Get all recorded events
156
- * @returns Array of recorded rrweb events
157
- */
158
- getRecordedEvents(): eventWithTime[] {
159
- return [...this.recordedEvents]
160
- }
161
-
162
- /**
163
- * Clear all recorded events
164
- */
165
- clearRecordedEvents(): void {
166
- this.recordedEvents = []
167
- }
168
-
169
- /**
170
- * Get recording statistics
171
- * @returns Recording statistics
172
- */
173
- getRecordingStats(): { totalEvents: number; isRecording: boolean } {
174
- return {
175
- totalEvents: this.recordedEvents.length,
176
- isRecording: this.isRecording,
177
- }
178
- }
179
- }
@@ -1,449 +0,0 @@
1
- import { NavigationEvent, RecorderConfig } from '../types'
2
- import { trace, SpanStatusCode } from '@opentelemetry/api'
3
- import { logger } from '../utils'
4
-
5
- export class NavigationTracker {
6
- private config?: RecorderConfig
7
- private isRecording = false
8
- private navigationRef: any = null
9
- private events: NavigationEvent[] = []
10
- private navigationListeners: Map<string, any> = new Map()
11
- private currentRoute: string | null = null
12
- private navigationStack: string[] = []
13
- private navigationStartTime: number = 0
14
-
15
- init(config: RecorderConfig): void {
16
- this.config = config
17
- }
18
-
19
- setNavigationRef(ref: any): void {
20
- this.navigationRef = ref
21
- if (this.isRecording) {
22
- this._setupNavigationListener()
23
- }
24
- }
25
-
26
- start(): void {
27
- logger.info('NavigationTracker', 'Navigation tracking started')
28
- this.isRecording = true
29
- this.events = []
30
- this.navigationStack = []
31
- this.navigationStartTime = Date.now()
32
- this._setupNavigationListener()
33
- // Navigation tracking started
34
- }
35
-
36
- stop(): void {
37
- this.isRecording = false
38
- this._removeNavigationListener()
39
- // Navigation tracking stopped
40
- }
41
-
42
- pause(): void {
43
- this.isRecording = false
44
- }
45
-
46
- resume(): void {
47
- this.isRecording = true
48
- this._setupNavigationListener()
49
- }
50
-
51
- private _setupNavigationListener(): void {
52
- if (!this.navigationRef) {
53
- // Navigation ref not set - silently continue
54
- return
55
- }
56
-
57
- try {
58
- // Listen to navigation state changes
59
- const stateListener = this.navigationRef.addListener('state', (e: any) => {
60
- this._recordNavigationEvent('state_change', e.data)
61
- })
62
-
63
- // Listen to focus events
64
- const focusListener = this.navigationRef.addListener('focus', (e: any) => {
65
- this._recordNavigationEvent('focus', e.data)
66
- })
67
-
68
- // Listen to blur events
69
- const blurListener = this.navigationRef.addListener('blur', (e: any) => {
70
- this._recordNavigationEvent('blur', e.data)
71
- })
72
-
73
- // Listen to beforeRemove events
74
- const beforeRemoveListener = this.navigationRef.addListener('beforeRemove', (e: any) => {
75
- this._recordNavigationEvent('beforeRemove', e.data)
76
- })
77
-
78
- // Store listeners for cleanup
79
- this.navigationListeners.set('state', stateListener)
80
- this.navigationListeners.set('focus', focusListener)
81
- this.navigationListeners.set('blur', blurListener)
82
- this.navigationListeners.set('beforeRemove', beforeRemoveListener)
83
-
84
- // Navigation listeners setup complete
85
- } catch (error) {
86
- // Failed to setup navigation listeners - silently continue
87
- }
88
- }
89
-
90
- private _removeNavigationListener(): void {
91
- try {
92
- // Remove all listeners
93
- this.navigationListeners.forEach((listener, key) => {
94
- if (listener && typeof listener.remove === 'function') {
95
- listener.remove()
96
- }
97
- })
98
- this.navigationListeners.clear()
99
- // Navigation listeners removed
100
- } catch (error) {
101
- // Failed to remove navigation listeners - silently continue
102
- }
103
- }
104
-
105
- private _recordNavigationEvent(eventType: string, data: any): void {
106
- if (!this.isRecording) return
107
-
108
- const event: NavigationEvent = {
109
- type: 'navigate', // Default type
110
- timestamp: Date.now(),
111
- metadata: {
112
- eventType,
113
- navigationDuration: Date.now() - this.navigationStartTime,
114
- stackDepth: this.navigationStack.length,
115
- },
116
- }
117
-
118
- if (data) {
119
- if (data.routeName) {
120
- event.routeName = data.routeName
121
- this._updateNavigationStack(data.routeName, eventType)
122
- }
123
- if (data.params) {
124
- event.params = data.params
125
- }
126
- if (data.key) {
127
- event.metadata!.routeKey = data.key
128
- }
129
- }
130
-
131
- this.events.push(event)
132
- this._sendEvent(event)
133
- this._recordOpenTelemetrySpan(event)
134
- }
135
-
136
-
137
-
138
- private _updateNavigationStack(routeName: string, eventType: string): void {
139
- if (eventType === 'focus' || eventType === 'state_change') {
140
- if (this.currentRoute !== routeName) {
141
- this.currentRoute = routeName
142
- this.navigationStack.push(routeName)
143
- }
144
- } else if (eventType === 'blur' || eventType === 'beforeRemove') {
145
- const index = this.navigationStack.indexOf(routeName)
146
- if (index > -1) {
147
- this.navigationStack.splice(index, 1)
148
- }
149
- }
150
- }
151
-
152
- private _sendEvent(event: NavigationEvent): void {
153
- // Navigation event recorded
154
- }
155
-
156
- private _recordOpenTelemetrySpan(event: NavigationEvent): void {
157
- try {
158
- const span = trace.getTracer('navigation').startSpan(`Navigation.${event.type}`, {
159
- attributes: {
160
- 'navigation.system': 'ReactNavigation',
161
- 'navigation.operation': event.type,
162
- 'navigation.type': event.type,
163
- 'navigation.timestamp': event.timestamp,
164
- 'navigation.platform': 'react-native',
165
- },
166
- })
167
-
168
- if (event.routeName) {
169
- span.setAttribute('navigation.route_name', event.routeName)
170
- }
171
- if (event.params) {
172
- span.setAttribute('navigation.params', JSON.stringify(event.params))
173
- }
174
- if (event.metadata) {
175
- Object.entries(event.metadata).forEach(([key, value]) => {
176
- span.setAttribute(`navigation.metadata.${key}`, String(value))
177
- })
178
- }
179
-
180
- span.setStatus({ code: SpanStatusCode.OK })
181
- span.end()
182
- } catch (error) {
183
- // Failed to record OpenTelemetry span for navigation - silently continue
184
- }
185
- }
186
-
187
- // Public methods for manual event recording
188
- recordNavigate(routeName: string, params?: Record<string, any>): void {
189
- const event: NavigationEvent = {
190
- type: 'navigate',
191
- timestamp: Date.now(),
192
- routeName,
193
- params,
194
- metadata: {
195
- navigationDuration: Date.now() - this.navigationStartTime,
196
- stackDepth: this.navigationStack.length,
197
- manual: true,
198
- },
199
- }
200
-
201
- this._updateNavigationStack(routeName, 'focus')
202
- this._recordEvent(event)
203
- }
204
-
205
- recordGoBack(): void {
206
- const event: NavigationEvent = {
207
- type: 'goBack',
208
- timestamp: Date.now(),
209
- metadata: {
210
- navigationDuration: Date.now() - this.navigationStartTime,
211
- stackDepth: this.navigationStack.length,
212
- manual: true,
213
- },
214
- }
215
-
216
- this._recordEvent(event)
217
- }
218
-
219
- recordReset(routes: any[]): void {
220
- const event: NavigationEvent = {
221
- type: 'reset',
222
- timestamp: Date.now(),
223
- metadata: {
224
- navigationDuration: Date.now() - this.navigationStartTime,
225
- routesCount: routes.length,
226
- manual: true,
227
- },
228
- }
229
-
230
- // Update navigation stack
231
- this.navigationStack = routes.map(route => route.name || route.routeName)
232
- if (routes.length > 0) {
233
- this.currentRoute = routes[0].name || routes[0].routeName
234
- }
235
-
236
- this._recordEvent(event)
237
- this._recordEvent(event)
238
- }
239
-
240
- private _recordEvent(event: NavigationEvent): void {
241
- if (!this.isRecording) return
242
-
243
- this.events.push(event)
244
- this._sendEvent(event)
245
- this._recordOpenTelemetrySpan(event)
246
- }
247
-
248
- // Advanced navigation tracking methods
249
- recordDeepLink(url: string, params?: Record<string, any>): void {
250
- const event: NavigationEvent = {
251
- type: 'navigate',
252
- timestamp: Date.now(),
253
- routeName: 'deepLink',
254
- params: { url, ...params },
255
- metadata: {
256
- navigationDuration: Date.now() - this.navigationStartTime,
257
- stackDepth: this.navigationStack.length,
258
- deepLink: true,
259
- },
260
- }
261
-
262
- this._recordEvent(event)
263
- this._recordEvent(event)
264
- }
265
-
266
- recordTabChange(tabName: string, tabIndex: number): void {
267
- const event: NavigationEvent = {
268
- type: 'navigate',
269
- timestamp: Date.now(),
270
- routeName: tabName,
271
- params: { tabIndex },
272
- metadata: {
273
- navigationDuration: Date.now() - this.navigationStartTime,
274
- stackDepth: this.navigationStack.length,
275
- tabChange: true,
276
- tabIndex,
277
- },
278
- }
279
-
280
- this._recordEvent(event)
281
- this._recordEvent(event)
282
- }
283
-
284
- recordModalOpen(modalName: string, params?: Record<string, any>): void {
285
- const event: NavigationEvent = {
286
- type: 'navigate',
287
- timestamp: Date.now(),
288
- routeName: modalName,
289
- params,
290
- metadata: {
291
- navigationDuration: Date.now() - this.navigationStartTime,
292
- stackDepth: this.navigationStack.length,
293
- modal: true,
294
- },
295
- }
296
-
297
- this._recordEvent(event)
298
- this._recordEvent(event)
299
- }
300
-
301
- recordModalClose(modalName: string): void {
302
- const event: NavigationEvent = {
303
- type: 'goBack',
304
- timestamp: Date.now(),
305
- routeName: modalName,
306
- metadata: {
307
- navigationDuration: Date.now() - this.navigationStartTime,
308
- stackDepth: this.navigationStack.length,
309
- modal: true,
310
- modalClose: true,
311
- },
312
- }
313
-
314
- this._recordEvent(event)
315
- this._recordEvent(event)
316
- }
317
-
318
- recordStackPush(routeName: string, params?: Record<string, any>): void {
319
- const event: NavigationEvent = {
320
- type: 'navigate',
321
- timestamp: Date.now(),
322
- routeName,
323
- params,
324
- metadata: {
325
- navigationDuration: Date.now() - this.navigationStartTime,
326
- stackDepth: this.navigationStack.length,
327
- stackOperation: 'push',
328
- },
329
- }
330
-
331
- this._recordEvent(event)
332
- this._recordEvent(event)
333
- }
334
-
335
- recordStackPop(routeName?: string): void {
336
- const event: NavigationEvent = {
337
- type: 'goBack',
338
- timestamp: Date.now(),
339
- routeName,
340
- metadata: {
341
- navigationDuration: Date.now() - this.navigationStartTime,
342
- stackDepth: this.navigationStack.length,
343
- stackOperation: 'pop',
344
- },
345
- }
346
-
347
- this._recordEvent(event)
348
- this._recordEvent(event)
349
- }
350
-
351
- // Performance monitoring
352
- recordNavigationPerformance(routeName: string, loadTime: number): void {
353
- const event: NavigationEvent = {
354
- type: 'navigate',
355
- timestamp: Date.now(),
356
- routeName,
357
- metadata: {
358
- navigationDuration: Date.now() - this.navigationStartTime,
359
- stackDepth: this.navigationStack.length,
360
- performance: 'monitoring',
361
- loadTime,
362
- },
363
- }
364
-
365
- this._recordEvent(event)
366
- this._recordEvent(event)
367
- }
368
-
369
- // Error tracking
370
- recordNavigationError(error: Error, routeName?: string): void {
371
- const event: NavigationEvent = {
372
- type: 'navigate',
373
- timestamp: Date.now(),
374
- routeName,
375
- metadata: {
376
- navigationDuration: Date.now() - this.navigationStartTime,
377
- stackDepth: this.navigationStack.length,
378
- error: true,
379
- errorType: error.name,
380
- errorMessage: error.message,
381
- },
382
- }
383
-
384
- this._recordEvent(event)
385
- this._recordEvent(event)
386
-
387
- // Also record as OpenTelemetry error span
388
- try {
389
- const span = trace.getTracer('navigation').startSpan('Navigation.error', {
390
- attributes: {
391
- 'navigation.system': 'ReactNavigation',
392
- 'navigation.error': true,
393
- 'navigation.error.type': error.name,
394
- 'navigation.error.message': error.message,
395
- 'navigation.route_name': routeName || 'unknown',
396
- 'navigation.timestamp': Date.now(),
397
- },
398
- })
399
-
400
- span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })
401
- span.recordException(error)
402
- span.end()
403
- } catch (spanError) {
404
- // Failed to record error span - silently continue
405
- }
406
- }
407
-
408
- // Get current navigation state
409
- getCurrentRoute(): string | null {
410
- return this.currentRoute
411
- }
412
-
413
- getNavigationStack(): string[] {
414
- return [...this.navigationStack]
415
- }
416
-
417
- getNavigationDepth(): number {
418
- return this.navigationStack.length
419
- }
420
-
421
- // Get recorded events
422
- getEvents(): NavigationEvent[] {
423
- return [...this.events]
424
- }
425
-
426
- // Clear events
427
- clearEvents(): void {
428
- this.events = []
429
- }
430
-
431
- // Get navigation statistics
432
- getNavigationStats(): Record<string, number> {
433
- const stats: Record<string, number> = {}
434
- this.events.forEach(event => {
435
- stats[event.type] = (stats[event.type] || 0) + 1
436
- })
437
- return stats
438
- }
439
-
440
- // Get recording status
441
- isRecordingEnabled(): boolean {
442
- return this.isRecording
443
- }
444
-
445
- // Get navigation duration
446
- getNavigationDuration(): number {
447
- return Date.now() - this.navigationStartTime
448
- }
449
- }