@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.
- package/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativeConfig.kt +1 -1
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativeModule.kt +9 -9
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativePackage.kt +2 -2
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/model/TargetInfo.kt +1 -1
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/util/ViewUtils.kt +2 -2
- package/lib/module/SessionRecorderNativeSpec.js +5 -0
- package/lib/module/SessionRecorderNativeSpec.js.map +1 -0
- package/lib/module/components/SessionRecorderWidget/ErrorBanner.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/ModalHeader.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/SessionRecorderWidget.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/icons.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/styles.js.map +1 -1
- package/lib/module/config/constants.js.map +1 -1
- package/lib/module/config/defaults.js.map +1 -1
- package/lib/module/config/masking.js.map +1 -1
- package/lib/module/config/session-recorder.js.map +1 -1
- package/lib/module/config/validators.js.map +1 -1
- package/lib/module/config/widget.js.map +1 -1
- package/lib/module/context/SessionRecorderStore.js.map +1 -1
- package/lib/module/context/useSessionRecorderStore.js.map +1 -1
- package/lib/module/context/useStoreSelector.js.map +1 -1
- package/lib/module/native/SessionRecorderNative.js +16 -11
- package/lib/module/native/SessionRecorderNative.js.map +1 -1
- package/lib/module/native/index.js.map +1 -1
- package/lib/module/otel/helpers.js +1 -1
- package/lib/module/otel/helpers.js.map +1 -1
- package/lib/module/otel/index.js.map +1 -1
- package/lib/module/otel/instrumentations/index.js.map +1 -1
- package/lib/module/patch/xhr.js.map +1 -1
- package/lib/module/recorder/eventExporter.js.map +1 -1
- package/lib/module/recorder/gestureRecorder.js.map +1 -1
- package/lib/module/recorder/index.js.map +1 -1
- package/lib/module/recorder/navigationTracker.js.map +1 -1
- package/lib/module/recorder/screenRecorder.js.map +1 -1
- package/lib/module/services/api.service.js.map +1 -1
- package/lib/module/services/network.service.js.map +1 -1
- package/lib/module/services/screenMaskingService.js +1 -1
- package/lib/module/services/screenMaskingService.js.map +1 -1
- package/lib/module/services/storage.service.js.map +1 -1
- package/lib/module/session-recorder.js.map +1 -1
- package/lib/module/types/index.js.map +1 -1
- package/lib/module/types/session-recorder.js.map +1 -1
- package/lib/module/utils/app-metadata.js +2 -2
- package/lib/module/utils/constants.optional.js.map +1 -1
- package/lib/module/utils/createStore.js.map +1 -1
- package/lib/module/utils/logger.js +0 -8
- package/lib/module/utils/logger.js.map +1 -1
- package/lib/module/utils/platform.js +1 -1
- package/lib/module/utils/platform.js.map +1 -1
- package/lib/module/utils/rrweb-events.js.map +1 -1
- package/lib/module/utils/session.js.map +1 -1
- package/lib/module/utils/shallowEqual.js.map +1 -1
- package/lib/module/utils/time.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/SessionRecorderNativeSpec.d.ts +42 -0
- package/lib/typescript/src/SessionRecorderNativeSpec.d.ts.map +1 -0
- package/lib/typescript/src/components/ScreenRecorderView/index.d.ts +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/ErrorBanner.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/ModalHeader.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/SessionRecorderWidget.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/icons.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/index.d.ts +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/config/constants.d.ts.map +1 -1
- package/lib/typescript/src/config/defaults.d.ts.map +1 -1
- package/lib/typescript/src/config/index.d.ts.map +1 -1
- package/lib/typescript/src/config/masking.d.ts.map +1 -1
- package/lib/typescript/src/config/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/config/validators.d.ts.map +1 -1
- package/lib/typescript/src/config/widget.d.ts +1 -1
- package/lib/typescript/src/config/widget.d.ts.map +1 -1
- package/lib/typescript/src/context/SessionRecorderStore.d.ts.map +1 -1
- package/lib/typescript/src/context/useSessionRecorderStore.d.ts +3 -3
- package/lib/typescript/src/context/useSessionRecorderStore.d.ts.map +1 -1
- package/lib/typescript/src/context/useStoreSelector.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/SessionRecorderNative.d.ts +3 -54
- package/lib/typescript/src/native/SessionRecorderNative.d.ts.map +1 -1
- package/lib/typescript/src/native/index.d.ts +1 -1
- package/lib/typescript/src/native/index.d.ts.map +1 -1
- package/lib/typescript/src/otel/helpers.d.ts.map +1 -1
- package/lib/typescript/src/otel/index.d.ts.map +1 -1
- package/lib/typescript/src/otel/instrumentations/index.d.ts.map +1 -1
- package/lib/typescript/src/patch/index.d.ts.map +1 -1
- package/lib/typescript/src/patch/xhr.d.ts.map +1 -1
- package/lib/typescript/src/recorder/eventExporter.d.ts.map +1 -1
- package/lib/typescript/src/recorder/gestureRecorder.d.ts.map +1 -1
- package/lib/typescript/src/recorder/index.d.ts.map +1 -1
- package/lib/typescript/src/recorder/navigationTracker.d.ts.map +1 -1
- package/lib/typescript/src/recorder/screenRecorder.d.ts.map +1 -1
- package/lib/typescript/src/services/api.service.d.ts.map +1 -1
- package/lib/typescript/src/services/network.service.d.ts.map +1 -1
- package/lib/typescript/src/services/screenMaskingService.d.ts +1 -1
- package/lib/typescript/src/services/screenMaskingService.d.ts.map +1 -1
- package/lib/typescript/src/services/storage.service.d.ts.map +1 -1
- package/lib/typescript/src/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/types/configs.d.ts.map +1 -1
- package/lib/typescript/src/types/index.d.ts.map +1 -1
- package/lib/typescript/src/types/session-recorder.d.ts +4 -4
- package/lib/typescript/src/types/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/types/session.d.ts.map +1 -1
- package/lib/typescript/src/utils/app-metadata.d.ts.map +1 -1
- package/lib/typescript/src/utils/constants.optional.d.ts.map +1 -1
- package/lib/typescript/src/utils/constants.optional.expo.d.ts.map +1 -1
- package/lib/typescript/src/utils/createStore.d.ts.map +1 -1
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/logger.d.ts +1 -1
- package/lib/typescript/src/utils/logger.d.ts.map +1 -1
- package/lib/typescript/src/utils/platform.d.ts.map +1 -1
- package/lib/typescript/src/utils/request-utils.d.ts.map +1 -1
- package/lib/typescript/src/utils/rrweb-events.d.ts.map +1 -1
- package/lib/typescript/src/utils/session.d.ts.map +1 -1
- package/lib/typescript/src/utils/shallowEqual.d.ts.map +1 -1
- package/lib/typescript/src/utils/time.d.ts.map +1 -1
- package/lib/typescript/src/utils/type-utils.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/SessionRecorderNativeSpec.ts +53 -0
- package/src/components/ScreenRecorderView/index.ts +1 -1
- package/src/components/SessionRecorderWidget/ErrorBanner.tsx +14 -14
- package/src/components/SessionRecorderWidget/ModalHeader.tsx +11 -9
- package/src/components/SessionRecorderWidget/SessionRecorderWidget.tsx +70 -56
- package/src/components/SessionRecorderWidget/icons.tsx +58 -30
- package/src/components/SessionRecorderWidget/index.ts +1 -1
- package/src/components/SessionRecorderWidget/styles.ts +17 -18
- package/src/components/index.ts +2 -2
- package/src/config/constants.ts +19 -20
- package/src/config/defaults.ts +35 -31
- package/src/config/index.ts +5 -5
- package/src/config/masking.ts +44 -18
- package/src/config/session-recorder.ts +54 -26
- package/src/config/validators.ts +43 -20
- package/src/config/widget.ts +24 -15
- package/src/context/SessionRecorderStore.ts +19 -18
- package/src/context/useSessionRecorderStore.ts +17 -10
- package/src/context/useStoreSelector.ts +20 -18
- package/src/index.ts +7 -7
- package/src/native/SessionRecorderNative.ts +68 -112
- package/src/native/index.ts +5 -1
- package/src/otel/helpers.ts +109 -93
- package/src/otel/index.ts +46 -49
- package/src/otel/instrumentations/index.ts +44 -41
- package/src/patch/index.ts +1 -1
- package/src/patch/xhr.ts +77 -78
- package/src/recorder/eventExporter.ts +63 -68
- package/src/recorder/gestureRecorder.ts +359 -212
- package/src/recorder/index.ts +75 -62
- package/src/recorder/navigationTracker.ts +120 -97
- package/src/recorder/screenRecorder.ts +214 -163
- package/src/services/api.service.ts +49 -48
- package/src/services/network.service.ts +67 -58
- package/src/services/screenMaskingService.ts +81 -50
- package/src/services/storage.service.ts +99 -70
- package/src/session-recorder.ts +270 -214
- package/src/types/configs.ts +53 -31
- package/src/types/expo-constants.d.ts +2 -2
- package/src/types/index.ts +16 -18
- package/src/types/session-recorder.ts +106 -111
- package/src/types/session.ts +45 -45
- package/src/utils/app-metadata.ts +9 -9
- package/src/utils/constants.optional.expo.ts +3 -3
- package/src/utils/constants.optional.ts +14 -12
- package/src/utils/createStore.ts +23 -20
- package/src/utils/index.ts +7 -7
- package/src/utils/logger.ts +87 -58
- package/src/utils/platform.ts +149 -118
- package/src/utils/request-utils.ts +15 -15
- package/src/utils/rrweb-events.ts +47 -34
- package/src/utils/session.ts +15 -12
- package/src/utils/shallowEqual.ts +16 -10
- package/src/utils/time.ts +7 -4
- package/src/utils/type-utils.ts +36 -36
- package/src/version.ts +1 -1
- package/android/src/main/java/com/multiplayer/sessionrecordernative/SessionRecorderNativeModuleSpec.kt +0 -51
- package/android/src/main/java/com/xxx/XxxModule.kt +0 -23
- package/ios/Xxx.h +0 -5
- package/ios/Xxx.mm +0 -21
package/src/recorder/index.ts
CHANGED
|
@@ -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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
62
|
-
|
|
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(
|
|
67
|
-
|
|
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(
|
|
77
|
-
|
|
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 &&
|
|
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
|
|
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
|
|
143
|
+
const spaced = withoutParams
|
|
144
|
+
.replace(/[-_]+/g, ' ')
|
|
145
|
+
.replace(/\s+/g, ' ')
|
|
146
|
+
.trim();
|
|
128
147
|
// Title case
|
|
129
|
-
const titleCase = spaced
|
|
130
|
-
|
|
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
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
}
|