@multiplayer-app/session-recorder-react-native 0.0.1-alpha.9 → 0.0.1-beta.10

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 (197) hide show
  1. package/SessionRecorderNative.podspec +29 -0
  2. package/android/build.gradle +32 -0
  3. package/app.plugin.js +42 -0
  4. package/copy-react-native-dist.sh +4 -10
  5. package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js +1 -0
  6. package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js.map +1 -0
  7. package/dist/components/GestureCaptureWrapper/index.d.ts +1 -0
  8. package/dist/components/GestureCaptureWrapper/index.js +1 -0
  9. package/dist/components/GestureCaptureWrapper/index.js.map +1 -0
  10. package/dist/components/MaskableComponent.d.ts +22 -0
  11. package/dist/components/MaskableComponent.js +1 -0
  12. package/dist/components/MaskableComponent.js.map +1 -0
  13. package/dist/components/MaskableTextInput.d.ts +14 -0
  14. package/dist/components/MaskableTextInput.js +1 -0
  15. package/dist/components/MaskableTextInput.js.map +1 -0
  16. package/dist/components/ScreenRecorderView/ScreenRecorderView.d.ts +5 -0
  17. package/dist/components/ScreenRecorderView/ScreenRecorderView.js +1 -0
  18. package/dist/components/ScreenRecorderView/ScreenRecorderView.js.map +1 -0
  19. package/dist/components/ScreenRecorderView/index.d.ts +1 -0
  20. package/dist/components/ScreenRecorderView/index.js +1 -0
  21. package/dist/components/ScreenRecorderView/index.js.map +1 -0
  22. package/dist/components/SessionRecorderWidget/FinalPopover.d.ts +11 -0
  23. package/dist/components/SessionRecorderWidget/FinalPopover.js +1 -0
  24. package/dist/components/SessionRecorderWidget/FinalPopover.js.map +1 -0
  25. package/dist/components/SessionRecorderWidget/FloatingButton.d.ts +8 -0
  26. package/dist/components/SessionRecorderWidget/FloatingButton.js +1 -0
  27. package/dist/components/SessionRecorderWidget/FloatingButton.js.map +1 -0
  28. package/dist/components/SessionRecorderWidget/InitialPopover.d.ts +13 -0
  29. package/dist/components/SessionRecorderWidget/InitialPopover.js +1 -0
  30. package/dist/components/SessionRecorderWidget/InitialPopover.js.map +1 -0
  31. package/dist/components/SessionRecorderWidget/ModalContainer.d.ts +8 -0
  32. package/dist/components/SessionRecorderWidget/ModalContainer.js +1 -0
  33. package/dist/components/SessionRecorderWidget/ModalContainer.js.map +1 -0
  34. package/dist/components/SessionRecorderWidget/ModalHeader.d.ts +6 -0
  35. package/dist/components/SessionRecorderWidget/ModalHeader.js +1 -0
  36. package/dist/components/SessionRecorderWidget/ModalHeader.js.map +1 -0
  37. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.d.ts +5 -0
  38. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js +1 -0
  39. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js.map +1 -0
  40. package/dist/components/SessionRecorderWidget/icons.d.ts +11 -0
  41. package/dist/components/SessionRecorderWidget/icons.js +1 -0
  42. package/dist/components/SessionRecorderWidget/icons.js.map +1 -0
  43. package/dist/components/SessionRecorderWidget/index.d.ts +2 -0
  44. package/dist/components/SessionRecorderWidget/index.js +1 -0
  45. package/dist/components/SessionRecorderWidget/index.js.map +1 -0
  46. package/dist/components/SessionRecorderWidget/styles.d.ts +145 -0
  47. package/dist/components/SessionRecorderWidget/styles.js +1 -0
  48. package/dist/components/SessionRecorderWidget/styles.js.map +1 -0
  49. package/dist/components/index.d.ts +3 -0
  50. package/dist/components/index.js +1 -0
  51. package/dist/components/index.js.map +1 -0
  52. package/dist/config/defaults.js +1 -1
  53. package/dist/config/defaults.js.map +1 -1
  54. package/dist/config/masking.js +1 -1
  55. package/dist/config/masking.js.map +1 -1
  56. package/dist/context/SessionRecorderContext.d.ts +5 -3
  57. package/dist/context/SessionRecorderContext.js +1 -1
  58. package/dist/context/SessionRecorderContext.js.map +1 -1
  59. package/dist/index.js.map +1 -1
  60. package/dist/native/ScreenMasking.d.ts +21 -0
  61. package/dist/native/ScreenMasking.js +1 -0
  62. package/dist/native/ScreenMasking.js.map +1 -0
  63. package/dist/native/SessionRecorderNative.d.ts +21 -0
  64. package/dist/native/SessionRecorderNative.js +1 -0
  65. package/dist/native/SessionRecorderNative.js.map +1 -0
  66. package/dist/otel/index.d.ts +0 -2
  67. package/dist/otel/index.js.map +1 -1
  68. package/dist/otel/instrumentations/index.d.ts +0 -3
  69. package/dist/otel/instrumentations/index.js +1 -1
  70. package/dist/otel/instrumentations/index.js.map +1 -1
  71. package/dist/patch/xhr.js +1 -1
  72. package/dist/patch/xhr.js.map +1 -1
  73. package/dist/recorder/gestureRecorder.d.ts +0 -9
  74. package/dist/recorder/gestureRecorder.js +1 -1
  75. package/dist/recorder/gestureRecorder.js.map +1 -1
  76. package/dist/recorder/index.d.ts +4 -3
  77. package/dist/recorder/index.js.map +1 -1
  78. package/dist/recorder/screenRecorder.d.ts +2 -6
  79. package/dist/recorder/screenRecorder.js +1 -1
  80. package/dist/recorder/screenRecorder.js.map +1 -1
  81. package/dist/recorder/screenshotManager.d.ts +10 -0
  82. package/dist/recorder/screenshotManager.js +1 -0
  83. package/dist/recorder/screenshotManager.js.map +1 -0
  84. package/dist/services/screenMaskingService.d.ts +39 -0
  85. package/dist/services/screenMaskingService.js +1 -0
  86. package/dist/services/screenMaskingService.js.map +1 -0
  87. package/dist/services/storage.service.d.ts +18 -2
  88. package/dist/services/storage.service.js +1 -1
  89. package/dist/services/storage.service.js.map +1 -1
  90. package/dist/session-recorder.d.ts +4 -2
  91. package/dist/session-recorder.js +1 -1
  92. package/dist/session-recorder.js.map +1 -1
  93. package/dist/types/index.d.ts +2 -16
  94. package/dist/types/index.js +1 -1
  95. package/dist/types/index.js.map +1 -1
  96. package/dist/types/session-recorder.d.ts +6 -0
  97. package/dist/types/session-recorder.js.map +1 -1
  98. package/dist/utils/app-metadata.d.ts +16 -0
  99. package/dist/utils/app-metadata.js +1 -0
  100. package/dist/utils/app-metadata.js.map +1 -0
  101. package/dist/utils/componentRegistry.d.ts +64 -0
  102. package/dist/utils/componentRegistry.js +1 -0
  103. package/dist/utils/componentRegistry.js.map +1 -0
  104. package/dist/utils/nativeModuleTest.d.ts +8 -0
  105. package/dist/utils/nativeModuleTest.js +1 -0
  106. package/dist/utils/nativeModuleTest.js.map +1 -0
  107. package/dist/utils/platform.d.ts +35 -0
  108. package/dist/utils/platform.js +1 -1
  109. package/dist/utils/platform.js.map +1 -1
  110. package/dist/utils/reactNativeHierarchyExtractor.d.ts +38 -0
  111. package/dist/utils/reactNativeHierarchyExtractor.js +1 -0
  112. package/dist/utils/reactNativeHierarchyExtractor.js.map +1 -0
  113. package/dist/utils/rrweb-events.d.ts +1 -1
  114. package/dist/utils/rrweb-events.js +1 -1
  115. package/dist/utils/rrweb-events.js.map +1 -1
  116. package/dist/utils/screenshotMasker.d.ts +96 -0
  117. package/dist/utils/screenshotMasker.js +1 -0
  118. package/dist/utils/screenshotMasker.js.map +1 -0
  119. package/dist/utils/viewHierarchyTracker.d.ts +89 -0
  120. package/dist/utils/viewHierarchyTracker.js +1 -0
  121. package/dist/utils/viewHierarchyTracker.js.map +1 -0
  122. package/dist/version.d.ts +1 -1
  123. package/dist/version.js +1 -1
  124. package/dist/version.js.map +1 -1
  125. package/docs/NATIVE_MODULE_SETUP.md +177 -0
  126. package/ios/SessionRecorderNative.m +17 -0
  127. package/ios/SessionRecorderNative.podspec +26 -0
  128. package/ios/SessionRecorderNative.swift +205 -0
  129. package/package.json +25 -14
  130. package/plugin/package.json +20 -0
  131. package/plugin/src/index.js +42 -0
  132. package/react-native.config.js +15 -0
  133. package/RRWEB_INTEGRATION.md +0 -336
  134. package/VIEWSHOT_INTEGRATION_TEST.md +0 -123
  135. package/babel.config.js +0 -13
  136. package/dist/components/GestureCaptureWrapper.js +0 -1
  137. package/dist/components/GestureCaptureWrapper.js.map +0 -1
  138. package/dist/expo.d.ts +0 -7
  139. package/dist/expo.js +0 -1
  140. package/dist/expo.js.map +0 -1
  141. package/dist/otel/instrumentations/gestureInstrumentation.d.ts +0 -15
  142. package/dist/otel/instrumentations/gestureInstrumentation.js +0 -1
  143. package/dist/otel/instrumentations/gestureInstrumentation.js.map +0 -1
  144. package/dist/otel/instrumentations/reactNativeInstrumentation.d.ts +0 -8
  145. package/dist/otel/instrumentations/reactNativeInstrumentation.js +0 -1
  146. package/dist/otel/instrumentations/reactNativeInstrumentation.js.map +0 -1
  147. package/dist/otel/instrumentations/reactNavigationInstrumentation.d.ts +0 -13
  148. package/dist/otel/instrumentations/reactNavigationInstrumentation.js +0 -1
  149. package/dist/otel/instrumentations/reactNavigationInstrumentation.js.map +0 -1
  150. package/dist/recorder/gestureHandlerRecorder.d.ts +0 -19
  151. package/dist/recorder/gestureHandlerRecorder.js +0 -1
  152. package/dist/recorder/gestureHandlerRecorder.js.map +0 -1
  153. package/dist/types/rrweb.d.ts +0 -118
  154. package/dist/types/rrweb.js +0 -1
  155. package/dist/types/rrweb.js.map +0 -1
  156. package/src/components/GestureCaptureWrapper.tsx +0 -110
  157. package/src/config/constants.ts +0 -60
  158. package/src/config/defaults.ts +0 -82
  159. package/src/config/index.ts +0 -6
  160. package/src/config/masking.ts +0 -27
  161. package/src/config/session-recorder.ts +0 -55
  162. package/src/config/validators.ts +0 -31
  163. package/src/context/SessionRecorderContext.tsx +0 -143
  164. package/src/expo.ts +0 -11
  165. package/src/index.ts +0 -9
  166. package/src/otel/helpers.ts +0 -275
  167. package/src/otel/index.ts +0 -149
  168. package/src/otel/instrumentations/gestureInstrumentation.ts +0 -141
  169. package/src/otel/instrumentations/index.ts +0 -120
  170. package/src/otel/instrumentations/reactNativeInstrumentation.ts +0 -77
  171. package/src/otel/instrumentations/reactNavigationInstrumentation.ts +0 -119
  172. package/src/patch/index.ts +0 -1
  173. package/src/patch/xhr.ts +0 -142
  174. package/src/recorder/eventExporter.ts +0 -141
  175. package/src/recorder/gestureHandlerRecorder.ts +0 -157
  176. package/src/recorder/gestureRecorder.ts +0 -622
  177. package/src/recorder/index.ts +0 -178
  178. package/src/recorder/navigationTracker.ts +0 -449
  179. package/src/recorder/screenRecorder.ts +0 -506
  180. package/src/services/api.service.ts +0 -203
  181. package/src/services/storage.service.ts +0 -158
  182. package/src/session-recorder.ts +0 -601
  183. package/src/types/expo.d.ts +0 -23
  184. package/src/types/index.ts +0 -46
  185. package/src/types/session-recorder.ts +0 -423
  186. package/src/types/session.ts +0 -65
  187. package/src/utils/index.ts +0 -8
  188. package/src/utils/logger.ts +0 -225
  189. package/src/utils/platform.ts +0 -87
  190. package/src/utils/request-utils.ts +0 -61
  191. package/src/utils/rrweb-events.ts +0 -311
  192. package/src/utils/session.ts +0 -18
  193. package/src/utils/time.ts +0 -17
  194. package/src/utils/type-utils.ts +0 -75
  195. package/src/version.ts +0 -1
  196. package/tsconfig.json +0 -24
  197. /package/dist/components/{GestureCaptureWrapper.d.ts → GestureCaptureWrapper/GestureCaptureWrapper.d.ts} +0 -0
@@ -1,622 +0,0 @@
1
- import { GestureEvent, RecorderConfig, EventType, IncrementalSource, MouseInteractionType, EventRecorder, eventWithTime } from '../types'
2
- import { trace, SpanStatusCode } from '@opentelemetry/api'
3
- import { Dimensions } from 'react-native'
4
- import { logger } from '../utils'
5
-
6
- export class GestureRecorder implements EventRecorder {
7
- private config?: RecorderConfig
8
- private isRecording = false
9
- private events: GestureEvent[] = []
10
- private gestureHandlers: Map<string, any> = new Map()
11
- private screenDimensions: { width: number; height: number } | null = null
12
- private lastGestureTime: number = 0
13
- private gestureThrottleMs: number = 50 // Throttle gestures to avoid spam
14
- private lastTouchTime: number = 0
15
- private touchThrottleMs: number = 100 // Throttle touch events to max 10 per second
16
-
17
- // Cyclic call detection
18
- private isRecordingGesture = false
19
- private gestureCallStack: string[] = []
20
- private maxGestureCallDepth = 5
21
- private eventRecorder?: EventRecorder
22
- private imageNodeId: number = 1 // ID of the image node for touch interactions
23
- private screenRecorder?: any // Reference to screen recorder for force capture
24
- init(config: RecorderConfig, eventRecorder?: EventRecorder, screenRecorder?: any): void {
25
- this.config = config
26
- this.eventRecorder = eventRecorder
27
- this.screenRecorder = screenRecorder
28
- this._getScreenDimensions()
29
- }
30
-
31
- start(): void {
32
- logger.info('GestureRecorder', 'Gesture recording started')
33
- this.isRecording = true
34
- this.events = []
35
- this._setupGestureHandlers()
36
- this._setupAutomaticTouchCapture()
37
- // Gesture recording started
38
- }
39
-
40
- stop(): void {
41
- this.isRecording = false
42
- this._removeGestureHandlers()
43
- // Gesture recording stopped
44
- }
45
-
46
- pause(): void {
47
- this.isRecording = false
48
- }
49
-
50
- resume(): void {
51
- this.isRecording = true
52
- }
53
-
54
- // Input component registration temporarily disabled
55
-
56
- private _getScreenDimensions(): void {
57
- try {
58
- this.screenDimensions = Dimensions.get('window')
59
- } catch (error) {
60
- // Failed to get screen dimensions - silently continue
61
- this.screenDimensions = { width: 375, height: 667 } // Default fallback
62
- }
63
- }
64
-
65
- private _setupInputTracking(): void {
66
- // Set up React Native input component tracking
67
- try {
68
- // This would integrate with React Native's component tracking
69
- // For now, we'll provide methods for manual registration
70
- // Input tracking setup complete
71
- } catch (error) {
72
- // Failed to setup input tracking - silently continue
73
- }
74
- }
75
-
76
- private _setupGestureHandlers(): void {
77
- // This would integrate with react-native-gesture-handler
78
- // For now, we'll create a comprehensive implementation that can be easily integrated
79
- // Setting up gesture handlers
80
-
81
- // Set up global gesture listener
82
- this._setupGlobalGestureListener()
83
- }
84
-
85
- private _setupGlobalGestureListener(): void {
86
- try {
87
- // Listen for touch events at the app level
88
- // This is a simplified implementation - in production you'd use react-native-gesture-handler
89
- // Global gesture listener setup complete
90
- } catch (error) {
91
- // Failed to setup global gesture listener - silently continue
92
- }
93
- }
94
-
95
- private _setupAutomaticTouchCapture(): void {
96
- try {
97
- // This method sets up automatic touch capture
98
- // The actual touch capture is handled by the TouchEventCapture component
99
- // in the SessionRecorderContext, which automatically calls our recording methods
100
-
101
- // We can add any additional setup here if needed
102
- // For now, the TouchEventCapture component handles everything automatically
103
- } catch (error) {
104
- // Failed to setup automatic touch capture - silently continue
105
- }
106
- }
107
-
108
- private _removeGestureHandlers(): void {
109
- this.gestureHandlers.clear()
110
- // Gesture handlers removed
111
- }
112
-
113
- private _recordEvent(event: GestureEvent): void {
114
- if (!this.isRecording) return
115
-
116
- // Throttle gestures to avoid spam
117
- const now = Date.now()
118
- if (now - this.lastGestureTime < this.gestureThrottleMs) {
119
- return
120
- }
121
- this.lastGestureTime = now
122
-
123
- this.events.push(event)
124
- this._sendEvent(event)
125
- this._recordOpenTelemetrySpan(event)
126
- }
127
-
128
-
129
-
130
- private _sendEvent(event: GestureEvent): void {
131
- // Send event to backend or store locally
132
- // Gesture event recorded
133
- }
134
-
135
- private _recordOpenTelemetrySpan(event: GestureEvent): void {
136
- try {
137
- const span = trace.getTracer('gesture').startSpan(`Gesture.${event.type}`, {
138
- attributes: {
139
- 'gesture.type': event.type,
140
- 'gesture.timestamp': event.timestamp,
141
- 'gesture.platform': 'react-native',
142
- },
143
- })
144
-
145
- if (event.coordinates) {
146
- span.setAttribute('gesture.coordinates.x', event.coordinates.x)
147
- span.setAttribute('gesture.coordinates.y', event.coordinates.y)
148
-
149
- // Calculate relative position
150
- if (this.screenDimensions) {
151
- const relativeX = event.coordinates.x / this.screenDimensions.width
152
- const relativeY = event.coordinates.y / this.screenDimensions.height
153
- span.setAttribute('gesture.coordinates.relative_x', relativeX)
154
- span.setAttribute('gesture.coordinates.relative_y', relativeY)
155
- }
156
- }
157
-
158
- if (event.target) {
159
- span.setAttribute('gesture.target', event.target)
160
- }
161
-
162
- if (event.metadata) {
163
- Object.entries(event.metadata).forEach(([key, value]) => {
164
- span.setAttribute(`gesture.metadata.${key}`, String(value))
165
- })
166
- }
167
-
168
- span.setStatus({ code: SpanStatusCode.OK })
169
- span.end()
170
- } catch (error) {
171
- // Failed to record OpenTelemetry span for gesture - silently continue
172
- }
173
- }
174
-
175
- // Public methods for manual event recording
176
- recordTap(x: number, y: number, target?: string, pressure?: number): void {
177
- const event: GestureEvent = {
178
- type: 'tap',
179
- timestamp: Date.now(),
180
- coordinates: { x, y },
181
- target,
182
- metadata: {
183
- pressure: pressure || 1.0,
184
- screenWidth: this.screenDimensions?.width,
185
- screenHeight: this.screenDimensions?.height,
186
- },
187
- }
188
-
189
- this._recordEvent(event)
190
- }
191
-
192
- recordSwipe(direction: string, target?: string, velocity?: number, distance?: number): void {
193
- const event: GestureEvent = {
194
- type: 'swipe',
195
- timestamp: Date.now(),
196
- target,
197
- metadata: {
198
- direction,
199
- velocity: velocity || 0,
200
- distance: distance || 0,
201
- screenWidth: this.screenDimensions?.width,
202
- screenHeight: this.screenDimensions?.height,
203
- },
204
- }
205
-
206
- this._recordEvent(event)
207
- }
208
-
209
- recordPinch(scale: number, target?: string, velocity?: number): void {
210
- const event: GestureEvent = {
211
- type: 'pinch',
212
- timestamp: Date.now(),
213
- target,
214
- metadata: {
215
- scale,
216
- velocity: velocity || 0,
217
- screenWidth: this.screenDimensions?.width,
218
- screenHeight: this.screenDimensions?.height,
219
- },
220
- }
221
-
222
- this._recordEvent(event)
223
- }
224
-
225
- recordPan(deltaX: number, deltaY: number, target?: string, velocity?: number): void {
226
- const event: GestureEvent = {
227
- type: 'pan',
228
- timestamp: Date.now(),
229
- target,
230
- metadata: {
231
- deltaX,
232
- deltaY,
233
- velocity: velocity || 0,
234
- screenWidth: this.screenDimensions?.width,
235
- screenHeight: this.screenDimensions?.height,
236
- },
237
- }
238
-
239
- this._recordEvent(event)
240
- }
241
-
242
- recordLongPress(duration: number, target?: string, pressure?: number): void {
243
- const event: GestureEvent = {
244
- type: 'longPress',
245
- timestamp: Date.now(),
246
- target,
247
- metadata: {
248
- duration,
249
- pressure: pressure || 1.0,
250
- screenWidth: this.screenDimensions?.width,
251
- screenHeight: this.screenDimensions?.height,
252
- },
253
- }
254
-
255
- this._recordEvent(event)
256
- }
257
-
258
- recordDoubleTap(x: number, y: number, target?: string): void {
259
- const event: GestureEvent = {
260
- type: 'doubleTap',
261
- timestamp: Date.now(),
262
- coordinates: { x, y },
263
- target,
264
- metadata: {
265
- screenWidth: this.screenDimensions?.width,
266
- screenHeight: this.screenDimensions?.height,
267
- },
268
- }
269
-
270
- this._recordEvent(event)
271
- }
272
-
273
- recordRotate(rotation: number, target?: string, velocity?: number): void {
274
- const event: GestureEvent = {
275
- type: 'rotate',
276
- timestamp: Date.now(),
277
- target,
278
- metadata: {
279
- rotation,
280
- velocity: velocity || 0,
281
- screenWidth: this.screenDimensions?.width,
282
- screenHeight: this.screenDimensions?.height,
283
- },
284
- }
285
-
286
- this._recordEvent(event)
287
- }
288
-
289
- recordFling(direction: string, velocity: number, target?: string): void {
290
- const event: GestureEvent = {
291
- type: 'fling',
292
- timestamp: Date.now(),
293
- target,
294
- metadata: {
295
- direction,
296
- velocity,
297
- screenWidth: this.screenDimensions?.width,
298
- screenHeight: this.screenDimensions?.height,
299
- },
300
- }
301
-
302
- this._recordEvent(event)
303
- }
304
-
305
- // Advanced gesture tracking methods
306
- recordMultiTouch(touchCount: number, target?: string): void {
307
- const event: GestureEvent = {
308
- type: 'multiTouch',
309
- timestamp: Date.now(),
310
- target,
311
- metadata: {
312
- touchCount,
313
- screenWidth: this.screenDimensions?.width,
314
- screenHeight: this.screenDimensions?.height,
315
- },
316
- }
317
-
318
- this._recordEvent(event)
319
- }
320
-
321
- recordScroll(direction: string, distance: number, velocity: number, target?: string): void {
322
- const event: GestureEvent = {
323
- type: 'scroll',
324
- timestamp: Date.now(),
325
- target,
326
- metadata: {
327
- direction,
328
- distance,
329
- velocity,
330
- screenWidth: this.screenDimensions?.width,
331
- screenHeight: this.screenDimensions?.height,
332
- },
333
- }
334
-
335
- this._recordEvent(event)
336
- }
337
-
338
- recordZoom(scale: number, target?: string, velocity?: number): void {
339
- const event: GestureEvent = {
340
- type: 'zoom',
341
- timestamp: Date.now(),
342
- target,
343
- metadata: {
344
- scale,
345
- velocity: velocity || 0,
346
- screenWidth: this.screenDimensions?.width,
347
- screenHeight: this.screenDimensions?.height,
348
- },
349
- }
350
-
351
- this._recordEvent(event)
352
- }
353
-
354
- // Gesture sequence tracking
355
- recordGestureSequence(gestures: string[], duration: number, target?: string): void {
356
- const event: GestureEvent = {
357
- type: 'gestureSequence',
358
- timestamp: Date.now(),
359
- target,
360
- metadata: {
361
- gestures: gestures.join(','),
362
- duration,
363
- gestureCount: gestures.length,
364
- screenWidth: this.screenDimensions?.width,
365
- screenHeight: this.screenDimensions?.height,
366
- },
367
- }
368
-
369
- this._recordEvent(event)
370
- }
371
-
372
- // Error tracking for gesture failures
373
- recordGestureError(error: Error, gestureType: string, target?: string): void {
374
- const event: GestureEvent = {
375
- type: 'gestureError',
376
- timestamp: Date.now(),
377
- target,
378
- metadata: {
379
- errorType: error.name,
380
- errorMessage: error.message,
381
- gestureType,
382
- screenWidth: this.screenDimensions?.width,
383
- screenHeight: this.screenDimensions?.height,
384
- },
385
- }
386
-
387
- this._recordEvent(event)
388
-
389
- // Also record as OpenTelemetry error span
390
- try {
391
- const span = trace.getTracer('gesture').startSpan(`Gesture.${gestureType}.error`, {
392
- attributes: {
393
- 'gesture.type': gestureType,
394
- 'gesture.error': true,
395
- 'gesture.error.type': error.name,
396
- 'gesture.error.message': error.message,
397
- 'gesture.timestamp': Date.now(),
398
- },
399
- })
400
-
401
- span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })
402
- span.recordException(error)
403
- span.end()
404
- } catch (spanError) {
405
- // Failed to record error span - silently continue
406
- }
407
- }
408
-
409
- // Performance monitoring
410
- recordGesturePerformance(gestureType: string, duration: number, target?: string): void {
411
- const event: GestureEvent = {
412
- type: 'gesturePerformance',
413
- timestamp: Date.now(),
414
- target,
415
- metadata: {
416
- gestureType,
417
- duration,
418
- performance: 'monitoring',
419
- screenWidth: this.screenDimensions?.width,
420
- screenHeight: this.screenDimensions?.height,
421
- },
422
- }
423
-
424
- this._recordEvent(event)
425
- }
426
-
427
- // Get recorded events
428
- getEvents(): GestureEvent[] {
429
- return [...this.events]
430
- }
431
-
432
- // Clear events
433
- clearEvents(): void {
434
- this.events = []
435
- }
436
-
437
- // Get event statistics
438
- getEventStats(): Record<string, number> {
439
- const stats: Record<string, number> = {}
440
- this.events.forEach(event => {
441
- stats[event.type] = (stats[event.type] || 0) + 1
442
- })
443
- return stats
444
- }
445
-
446
- // Set gesture throttle
447
- setGestureThrottle(throttleMs: number): void {
448
- this.gestureThrottleMs = throttleMs
449
- }
450
-
451
- // Get recording status
452
- isRecordingEnabled(): boolean {
453
- return this.isRecording
454
- }
455
-
456
- /**
457
- * Record an rrweb event
458
- * @param event - The rrweb event to record
459
- */
460
- recordEvent(event: any): void {
461
- if (this.eventRecorder) {
462
- this.eventRecorder.recordEvent(event)
463
- }
464
- }
465
-
466
- /**
467
- * Create and emit a rrweb MouseInteraction event for touch interactions
468
- * @param x - X coordinate
469
- * @param y - Y coordinate
470
- * @param interactionType - Type of interaction (TouchStart, TouchMove, TouchEnd, etc.)
471
- * @param target - Target element identifier
472
- */
473
- private _createMouseInteractionEvent(
474
- x: number,
475
- y: number,
476
- interactionType: MouseInteractionType,
477
- target?: string,
478
- ): void {
479
- const incrementalSnapshotEvent: eventWithTime = {
480
- type: EventType.IncrementalSnapshot,
481
- data: {
482
- source: IncrementalSource.MouseInteraction,
483
- type: interactionType,
484
- id: this.imageNodeId, // Reference to the image node
485
- x: x, // Preserve decimal precision like web rrweb
486
- y: y, // Preserve decimal precision like web rrweb
487
- pointerType: 2, // 2 = Touch for React Native (0=Mouse, 1=Pen, 2=Touch)
488
- },
489
- timestamp: Date.now(),
490
- }
491
-
492
- this.recordEvent(incrementalSnapshotEvent)
493
- }
494
-
495
- /**
496
- * Create mouse move event with positions array (like web rrweb)
497
- * @param x - X coordinate
498
- * @param y - Y coordinate
499
- * @param target - Target element identifier
500
- */
501
- private _createMouseMoveEvent(x: number, y: number, target?: string): void {
502
- const incrementalSnapshotEvent: eventWithTime = {
503
- type: EventType.IncrementalSnapshot,
504
- data: {
505
- source: IncrementalSource.MouseMove, // Use MouseMove instead of MouseInteraction
506
- positions: [
507
- {
508
- x: x, // Preserve decimal precision like web rrweb
509
- y: y, // Preserve decimal precision like web rrweb
510
- id: this.imageNodeId, // Reference to the image node
511
- timeOffset: 0, // No time offset for single position
512
- },
513
- ],
514
- },
515
- timestamp: Date.now(),
516
- }
517
-
518
- this.recordEvent(incrementalSnapshotEvent)
519
- }
520
-
521
- /**
522
- * Record touch start event as rrweb MouseInteraction
523
- * @param x - X coordinate
524
- * @param y - Y coordinate
525
- * @param target - Target element identifier
526
- * @param pressure - Touch pressure (optional)
527
- */
528
- recordTouchStart(x: number, y: number, target?: string, pressure?: number): void {
529
- // Throttle touch events to prevent spam
530
- const now = Date.now()
531
- if (now - this.lastTouchTime < this.touchThrottleMs) {
532
- logger.debug('GestureRecorder', `Touch start throttled (${now - this.lastTouchTime}ms < ${this.touchThrottleMs}ms)`)
533
- return
534
- }
535
- this.lastTouchTime = now
536
-
537
- logger.debug('GestureRecorder', 'Touch start recorded', { x, y, target, pressure })
538
- // Record as MouseDown (type: 1) like web rrweb
539
- this._createMouseInteractionEvent(x, y, MouseInteractionType.MouseDown, target)
540
- }
541
-
542
- /**
543
- * Record touch move event as rrweb MouseMove with positions array
544
- * @param x - X coordinate
545
- * @param y - Y coordinate
546
- * @param target - Target element identifier
547
- * @param pressure - Touch pressure (optional)
548
- */
549
- recordTouchMove(x: number, y: number, target?: string, pressure?: number): void {
550
- // Throttle touch move events more aggressively
551
- const now = Date.now()
552
- if (now - this.lastTouchTime < this.touchThrottleMs * 2) { // 200ms throttle for move events
553
- logger.debug('GestureRecorder', `Touch move throttled (${now - this.lastTouchTime}ms < ${this.touchThrottleMs * 2}ms)`)
554
- return
555
- }
556
- this.lastTouchTime = now
557
-
558
- logger.debug('GestureRecorder', 'Touch move recorded', { x, y, target, pressure })
559
- // Record as MouseMove with positions array (like web rrweb)
560
- this._createMouseMoveEvent(x, y, target)
561
- }
562
-
563
- /**
564
- * Record touch end event as rrweb MouseInteraction
565
- * @param x - X coordinate
566
- * @param y - Y coordinate
567
- * @param target - Target element identifier
568
- * @param pressure - Touch pressure (optional)
569
- */
570
- recordTouchEnd(x: number, y: number, target?: string, pressure?: number): void {
571
- // Cyclic call detection
572
- if (this.isRecordingGesture) {
573
- logger.error('GestureRecorder', 'CYCLIC CALL DETECTED! Already recording gesture', this.gestureCallStack)
574
- return
575
- }
576
-
577
- if (this.gestureCallStack.length >= this.maxGestureCallDepth) {
578
- logger.error('GestureRecorder', 'MAX GESTURE CALL DEPTH REACHED!', this.gestureCallStack)
579
- return
580
- }
581
-
582
- this.isRecordingGesture = true
583
- this.gestureCallStack.push('recordTouchEnd')
584
-
585
- try {
586
- logger.debug('GestureRecorder', 'Touch end recorded', { x, y, target, pressure })
587
- // Always record touch end (no throttling for completion)
588
- this.recordTap(x, y, target, pressure)
589
- // Record as MouseUp (type: 0) like web rrweb
590
- this._createMouseInteractionEvent(x, y, MouseInteractionType.MouseUp, target)
591
- // Also record Click (type: 2) like web rrweb
592
- this._createMouseInteractionEvent(x, y, MouseInteractionType.Click, target)
593
-
594
- // Only force screen capture on touch end (not on every touch event)
595
- logger.debug('GestureRecorder', 'Forcing screen capture after touch end')
596
- this.screenRecorder?.forceCapture()
597
- } finally {
598
- this.isRecordingGesture = false
599
- this.gestureCallStack.pop()
600
- }
601
- }
602
-
603
- /**
604
- * Record touch cancel event as rrweb MouseInteraction
605
- * @param x - X coordinate
606
- * @param y - Y coordinate
607
- * @param target - Target element identifier
608
- */
609
- recordTouchCancel(x: number, y: number, target?: string): void {
610
- // Record as MouseUp (type: 0) like web rrweb for touch cancel
611
- this._createMouseInteractionEvent(x, y, MouseInteractionType.MouseUp, target)
612
- }
613
-
614
- /**
615
- * Set the image node ID for touch interactions
616
- * This should be called when a new screen snapshot is created
617
- * @param nodeId - The ID of the image node in the current snapshot
618
- */
619
- setImageNodeId(nodeId: number): void {
620
- this.imageNodeId = nodeId
621
- }
622
- }