@multiplayer-app/session-recorder-react-native 0.0.1-beta.6 → 0.0.1-beta.8
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.
- package/copy-react-native-dist.sh +3 -3
- package/docs/NATIVE_MODULE_SETUP.md +175 -0
- package/ios/SessionRecorderNative.podspec +5 -0
- package/package.json +11 -1
- package/plugin/package.json +20 -0
- package/plugin/src/index.js +42 -0
- package/react-native.config.js +1 -1
- package/android/src/main/AndroidManifest.xml +0 -2
- package/android/src/main/java/com/multiplayer/sessionrecorder/ScreenMaskingModule.kt +0 -202
- package/android/src/main/java/com/multiplayer/sessionrecorder/ScreenMaskingPackage.kt +0 -16
- package/android/src/main/java/com/multiplayer/sessionrecorder/SessionRecorderModule.kt +0 -202
- package/android/src/main/java/com/multiplayer/sessionrecorder/SessionRecorderPackage.kt +0 -16
- package/babel.config.js +0 -13
- package/docs/AUTO_METADATA_DETECTION.md +0 -108
- package/docs/TROUBLESHOOTING.md +0 -168
- package/ios/ScreenMasking.m +0 -12
- package/ios/ScreenMasking.podspec +0 -21
- package/ios/ScreenMasking.swift +0 -205
- package/ios/SessionRecorder.podspec +0 -21
- package/scripts/generate-app-metadata.js +0 -173
- package/src/components/GestureCaptureWrapper/GestureCaptureWrapper.tsx +0 -86
- package/src/components/GestureCaptureWrapper/index.ts +0 -1
- package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +0 -72
- package/src/components/ScreenRecorderView/index.ts +0 -1
- package/src/components/SessionRecorderWidget/FinalPopover.tsx +0 -62
- package/src/components/SessionRecorderWidget/FloatingButton.tsx +0 -136
- package/src/components/SessionRecorderWidget/InitialPopover.tsx +0 -89
- package/src/components/SessionRecorderWidget/ModalContainer.tsx +0 -128
- package/src/components/SessionRecorderWidget/ModalHeader.tsx +0 -24
- package/src/components/SessionRecorderWidget/SessionRecorderWidget.tsx +0 -109
- package/src/components/SessionRecorderWidget/icons.tsx +0 -52
- package/src/components/SessionRecorderWidget/index.ts +0 -3
- package/src/components/SessionRecorderWidget/styles.ts +0 -150
- package/src/components/index.ts +0 -3
- package/src/config/constants.ts +0 -60
- package/src/config/defaults.ts +0 -83
- package/src/config/index.ts +0 -6
- package/src/config/masking.ts +0 -28
- package/src/config/session-recorder.ts +0 -55
- package/src/config/validators.ts +0 -31
- package/src/context/SessionRecorderContext.tsx +0 -53
- package/src/index.ts +0 -9
- package/src/native/ScreenMasking.ts +0 -34
- package/src/native/SessionRecorderNative.ts +0 -34
- package/src/otel/helpers.ts +0 -275
- package/src/otel/index.ts +0 -138
- package/src/otel/instrumentations/index.ts +0 -115
- package/src/patch/index.ts +0 -1
- package/src/patch/xhr.ts +0 -141
- package/src/recorder/eventExporter.ts +0 -141
- package/src/recorder/gestureRecorder.ts +0 -498
- package/src/recorder/index.ts +0 -179
- package/src/recorder/navigationTracker.ts +0 -449
- package/src/recorder/screenRecorder.ts +0 -527
- package/src/services/api.service.ts +0 -203
- package/src/services/screenMaskingService.ts +0 -118
- package/src/services/storage.service.ts +0 -199
- package/src/session-recorder.ts +0 -606
- package/src/types/expo.d.ts +0 -23
- package/src/types/index.ts +0 -28
- package/src/types/session-recorder.ts +0 -429
- package/src/types/session.ts +0 -65
- package/src/utils/app-metadata.ts +0 -31
- package/src/utils/index.ts +0 -8
- package/src/utils/logger.ts +0 -225
- package/src/utils/nativeModuleTest.ts +0 -60
- package/src/utils/platform.ts +0 -384
- package/src/utils/request-utils.ts +0 -61
- package/src/utils/rrweb-events.ts +0 -309
- package/src/utils/session.ts +0 -18
- package/src/utils/time.ts +0 -17
- package/src/utils/type-utils.ts +0 -75
- package/src/version.ts +0 -1
- package/tsconfig.json +0 -24
- /package/ios/{SessionRecorder.m → SessionRecorderNative.m} +0 -0
- /package/ios/{SessionRecorder.swift → SessionRecorderNative.swift} +0 -0
|
@@ -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
|
-
}
|