@multiplayer-app/session-recorder-react-native 0.0.1 → 1.0.0
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/README.md +708 -83
- package/SessionRecorderNative.podspec +26 -0
- package/android/build.gradle +34 -0
- package/copy-react-native-dist.sh +34 -16
- package/dist/components/ScreenRecorderView/ScreenRecorderView.js +1 -1
- package/dist/components/ScreenRecorderView/ScreenRecorderView.js.map +1 -1
- package/dist/components/SessionRecorderWidget/ErrorBanner.d.ts +7 -0
- package/dist/components/SessionRecorderWidget/ErrorBanner.js +1 -0
- package/dist/components/SessionRecorderWidget/ErrorBanner.js.map +1 -0
- package/dist/components/SessionRecorderWidget/FinalPopover.d.ts +12 -0
- package/dist/components/SessionRecorderWidget/FinalPopover.js +1 -0
- package/dist/components/SessionRecorderWidget/FinalPopover.js.map +1 -0
- package/dist/components/SessionRecorderWidget/FloatingButton.d.ts +8 -0
- package/dist/components/SessionRecorderWidget/FloatingButton.js +1 -0
- package/dist/components/SessionRecorderWidget/FloatingButton.js.map +1 -0
- package/dist/components/SessionRecorderWidget/InitialPopover.d.ts +16 -0
- package/dist/components/SessionRecorderWidget/InitialPopover.js +1 -0
- package/dist/components/SessionRecorderWidget/InitialPopover.js.map +1 -0
- package/dist/components/SessionRecorderWidget/ModalContainer.d.ts +8 -0
- package/dist/components/SessionRecorderWidget/ModalContainer.js +1 -0
- package/dist/components/SessionRecorderWidget/ModalContainer.js.map +1 -0
- package/dist/components/SessionRecorderWidget/ModalHeader.d.ts +6 -0
- package/dist/components/SessionRecorderWidget/ModalHeader.js +1 -0
- package/dist/components/SessionRecorderWidget/ModalHeader.js.map +1 -0
- package/dist/components/SessionRecorderWidget/SessionRecorderWidget.d.ts +5 -0
- package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js +1 -0
- package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js.map +1 -0
- package/dist/components/SessionRecorderWidget/icons.d.ts +11 -0
- package/dist/components/SessionRecorderWidget/icons.js +1 -0
- package/dist/components/SessionRecorderWidget/icons.js.map +1 -0
- package/dist/components/SessionRecorderWidget/index.d.ts +2 -0
- package/dist/components/SessionRecorderWidget/index.js +1 -0
- package/dist/components/SessionRecorderWidget/index.js.map +1 -0
- package/dist/components/SessionRecorderWidget/styles.d.ts +165 -0
- package/dist/components/SessionRecorderWidget/styles.js +1 -0
- package/dist/components/SessionRecorderWidget/styles.js.map +1 -0
- package/dist/components/index.d.ts +2 -1
- package/dist/components/index.js +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/config/constants.js +1 -1
- package/dist/config/constants.js.map +1 -1
- package/dist/config/defaults.d.ts +4 -4
- package/dist/config/defaults.js +1 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/masking.d.ts +2 -2
- package/dist/config/masking.js +1 -1
- package/dist/config/masking.js.map +1 -1
- package/dist/config/session-recorder.js +1 -1
- package/dist/config/session-recorder.js.map +1 -1
- package/dist/config/validators.d.ts +1 -1
- package/dist/config/validators.js +1 -1
- package/dist/config/validators.js.map +1 -1
- package/dist/config/widget.d.ts +9 -0
- package/dist/config/widget.js +1 -0
- package/dist/config/widget.js.map +1 -0
- package/dist/context/SessionRecorderContext.d.ts +12 -3
- package/dist/context/SessionRecorderContext.js +1 -1
- package/dist/context/SessionRecorderContext.js.map +1 -1
- package/dist/context/SessionRecorderStore.d.ts +12 -0
- package/dist/context/SessionRecorderStore.js +1 -0
- package/dist/context/SessionRecorderStore.js.map +1 -0
- package/dist/context/useSessionRecorderStore.d.ts +8 -0
- package/dist/context/useSessionRecorderStore.js +1 -0
- package/dist/context/useSessionRecorderStore.js.map +1 -0
- package/dist/context/useStoreSelector.d.ts +4 -0
- package/dist/context/useStoreSelector.js +1 -0
- package/dist/context/useStoreSelector.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/native/GestureRecorderNative.d.ts +57 -0
- package/dist/native/GestureRecorderNative.js +1 -0
- package/dist/native/GestureRecorderNative.js.map +1 -0
- package/dist/native/SessionRecorderNative.d.ts +33 -0
- package/dist/native/SessionRecorderNative.js +1 -0
- package/dist/native/SessionRecorderNative.js.map +1 -0
- package/dist/native/index.d.ts +2 -0
- package/dist/native/index.js +1 -0
- package/dist/native/index.js.map +1 -0
- package/dist/otel/index.js +1 -1
- package/dist/otel/index.js.map +1 -1
- package/dist/patch/xhr.js +1 -1
- package/dist/patch/xhr.js.map +1 -1
- package/dist/recorder/eventExporter.d.ts +4 -1
- package/dist/recorder/eventExporter.js +1 -1
- package/dist/recorder/eventExporter.js.map +1 -1
- package/dist/recorder/gestureRecorder.d.ts +28 -62
- package/dist/recorder/gestureRecorder.js +1 -1
- package/dist/recorder/gestureRecorder.js.map +1 -1
- package/dist/recorder/index.d.ts +2 -0
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.js.map +1 -1
- package/dist/recorder/navigationTracker.d.ts +4 -19
- package/dist/recorder/navigationTracker.js +1 -1
- package/dist/recorder/navigationTracker.js.map +1 -1
- package/dist/recorder/screenRecorder.d.ts +11 -5
- package/dist/recorder/screenRecorder.js +1 -1
- package/dist/recorder/screenRecorder.js.map +1 -1
- package/dist/services/api.service.d.ts +12 -3
- package/dist/services/api.service.js +1 -1
- package/dist/services/api.service.js.map +1 -1
- package/dist/services/network.service.d.ts +46 -0
- package/dist/services/network.service.js +1 -0
- package/dist/services/network.service.js.map +1 -0
- package/dist/services/screenMaskingService.d.ts +47 -0
- package/dist/services/screenMaskingService.js +1 -0
- package/dist/services/screenMaskingService.js.map +1 -0
- package/dist/services/storage.service.d.ts +18 -2
- package/dist/services/storage.service.js +1 -1
- package/dist/services/storage.service.js.map +1 -1
- package/dist/session-recorder.d.ts +18 -33
- package/dist/session-recorder.js +1 -1
- package/dist/session-recorder.js.map +1 -1
- package/dist/types/configs.d.ts +85 -0
- package/dist/types/configs.js +1 -0
- package/dist/types/configs.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/session-recorder.d.ts +105 -132
- package/dist/types/session-recorder.js +1 -1
- package/dist/types/session-recorder.js.map +1 -1
- package/dist/utils/constants.optional.d.ts +21 -0
- package/dist/utils/constants.optional.expo.d.ts +3 -0
- package/dist/utils/constants.optional.expo.js +1 -0
- package/dist/utils/constants.optional.expo.js.map +1 -0
- package/dist/utils/constants.optional.js +1 -0
- package/dist/utils/constants.optional.js.map +1 -0
- package/dist/utils/createStore.d.ts +8 -0
- package/dist/utils/createStore.js +1 -0
- package/dist/utils/createStore.js.map +1 -0
- package/dist/utils/logger.d.ts +2 -7
- package/dist/utils/logger.js +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/platform.d.ts +11 -0
- package/dist/utils/platform.js +1 -1
- package/dist/utils/platform.js.map +1 -1
- package/dist/utils/rrweb-events.d.ts +4 -3
- package/dist/utils/rrweb-events.js +1 -1
- package/dist/utils/rrweb-events.js.map +1 -1
- package/dist/utils/session.d.ts +2 -1
- package/dist/utils/session.js +1 -1
- package/dist/utils/session.js.map +1 -1
- package/dist/utils/shallowEqual.d.ts +1 -0
- package/dist/utils/shallowEqual.js +1 -0
- package/dist/utils/shallowEqual.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/ios/GestureRecorderNative.m +21 -0
- package/ios/GestureRecorderNative.swift +316 -0
- package/ios/SessionRecorderNative.m +17 -0
- package/ios/SessionRecorderNative.podspec +26 -0
- package/ios/SessionRecorderNative.swift +599 -0
- package/package.json +15 -16
- package/react-native.config.js +12 -0
- package/RRWEB_INTEGRATION.md +0 -336
- package/VIEWSHOT_INTEGRATION_TEST.md +0 -123
- package/babel.config.js +0 -13
- package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.d.ts +0 -6
- package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js +0 -1
- package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js.map +0 -1
- package/dist/components/GestureCaptureWrapper/index.d.ts +0 -1
- package/dist/components/GestureCaptureWrapper/index.js +0 -1
- package/dist/components/GestureCaptureWrapper/index.js.map +0 -1
- package/dist/components/GestureCaptureWrapper.d.ts +0 -6
- package/dist/components/GestureCaptureWrapper.js +0 -1
- package/dist/components/GestureCaptureWrapper.js.map +0 -1
- package/dist/expo.d.ts +0 -7
- package/dist/expo.js +0 -1
- package/dist/expo.js.map +0 -1
- package/dist/otel/instrumentations/gestureInstrumentation.d.ts +0 -15
- package/dist/otel/instrumentations/gestureInstrumentation.js +0 -1
- package/dist/otel/instrumentations/gestureInstrumentation.js.map +0 -1
- package/dist/otel/instrumentations/reactNativeInstrumentation.d.ts +0 -8
- package/dist/otel/instrumentations/reactNativeInstrumentation.js +0 -1
- package/dist/otel/instrumentations/reactNativeInstrumentation.js.map +0 -1
- package/dist/otel/instrumentations/reactNavigationInstrumentation.d.ts +0 -13
- package/dist/otel/instrumentations/reactNavigationInstrumentation.js +0 -1
- package/dist/otel/instrumentations/reactNavigationInstrumentation.js.map +0 -1
- package/dist/recorder/gestureHandlerRecorder.d.ts +0 -19
- package/dist/recorder/gestureHandlerRecorder.js +0 -1
- package/dist/recorder/gestureHandlerRecorder.js.map +0 -1
- package/dist/types/rrweb.d.ts +0 -118
- package/dist/types/rrweb.js +0 -1
- package/dist/types/rrweb.js.map +0 -1
- 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/index.ts +0 -1
- package/src/config/constants.ts +0 -60
- package/src/config/defaults.ts +0 -82
- package/src/config/index.ts +0 -6
- package/src/config/masking.ts +0 -27
- package/src/config/session-recorder.ts +0 -55
- package/src/config/validators.ts +0 -31
- package/src/context/SessionRecorderContext.tsx +0 -75
- package/src/expo.ts +0 -11
- package/src/index.ts +0 -17
- 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 -142
- 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 -498
- package/src/services/api.service.ts +0 -203
- package/src/services/storage.service.ts +0 -158
- package/src/session-recorder.ts +0 -600
- package/src/types/expo.d.ts +0 -23
- package/src/types/index.ts +0 -28
- package/src/types/session-recorder.ts +0 -423
- 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/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/src/session-recorder.ts
DELETED
|
@@ -1,600 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { SessionType } from '@multiplayer-app/session-recorder-common'
|
|
3
|
-
import { Observable } from 'lib0/observable'
|
|
4
|
-
|
|
5
|
-
import { TracerReactNativeSDK } from './otel'
|
|
6
|
-
import { RecorderReactNativeSDK } from './recorder'
|
|
7
|
-
import { logger } from './utils'
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
ISession,
|
|
11
|
-
SessionState,
|
|
12
|
-
ISessionRecorder,
|
|
13
|
-
SessionRecorderConfigs,
|
|
14
|
-
SessionRecorderOptions,
|
|
15
|
-
EventRecorder
|
|
16
|
-
} from './types'
|
|
17
|
-
import { getFormattedDate, isSessionActive, getNavigatorInfo } from './utils'
|
|
18
|
-
import { setMaxCapturingHttpPayloadSize, setShouldRecordHttpData } from './patch/xhr'
|
|
19
|
-
import { DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE, getSessionRecorderConfig } from './config'
|
|
20
|
-
|
|
21
|
-
import { StorageService } from './services/storage.service'
|
|
22
|
-
import { ApiService, StartSessionRequest, StopSessionRequest } from './services/api.service'
|
|
23
|
-
import { eventWithTime } from '@rrweb/types'
|
|
24
|
-
|
|
25
|
-
// Utility functions for React Native
|
|
26
|
-
|
|
27
|
-
type SessionRecorderEvents =
|
|
28
|
-
| 'state-change'
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class SessionRecorder extends Observable<SessionRecorderEvents> implements ISessionRecorder, EventRecorder {
|
|
32
|
-
private _isInitialized = false
|
|
33
|
-
private _configs: SessionRecorderConfigs | null = null
|
|
34
|
-
private _apiService = new ApiService()
|
|
35
|
-
private _tracer = new TracerReactNativeSDK()
|
|
36
|
-
private _recorder = new RecorderReactNativeSDK()
|
|
37
|
-
private _storageService = new StorageService()
|
|
38
|
-
private _startRequestController: AbortController | null = null
|
|
39
|
-
|
|
40
|
-
// Session ID and state are stored in AsyncStorage
|
|
41
|
-
private _sessionId: string | null = null
|
|
42
|
-
get sessionId(): string | null {
|
|
43
|
-
return this._sessionId
|
|
44
|
-
}
|
|
45
|
-
set sessionId(sessionId: string | null) {
|
|
46
|
-
this._sessionId = sessionId
|
|
47
|
-
if (sessionId) {
|
|
48
|
-
this._storageService.saveSessionId(sessionId)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
private _sessionType: SessionType = SessionType.PLAIN
|
|
53
|
-
get sessionType(): SessionType {
|
|
54
|
-
return this._sessionType
|
|
55
|
-
}
|
|
56
|
-
set sessionType(sessionType: SessionType) {
|
|
57
|
-
this._sessionType = sessionType
|
|
58
|
-
this._storageService.saveSessionType(sessionType)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
get continuousRecording(): boolean {
|
|
62
|
-
return this.sessionType === SessionType.CONTINUOUS
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
private _sessionState: SessionState | null = null
|
|
66
|
-
get sessionState(): SessionState | null {
|
|
67
|
-
return this._sessionState || SessionState.stopped
|
|
68
|
-
}
|
|
69
|
-
set sessionState(state: SessionState | null) {
|
|
70
|
-
this._sessionState = state
|
|
71
|
-
this.emit('state-change', [state || SessionState.stopped])
|
|
72
|
-
if (state) {
|
|
73
|
-
this._storageService.saveSessionState(state)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
private _session: ISession | null = null
|
|
78
|
-
get session(): ISession | null {
|
|
79
|
-
return this._session
|
|
80
|
-
}
|
|
81
|
-
set session(session: ISession | null) {
|
|
82
|
-
this._session = session
|
|
83
|
-
if (session) {
|
|
84
|
-
this._storageService.saveSessionObject(session)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
private _sessionAttributes: Record<string, any> | null = null
|
|
89
|
-
get sessionAttributes(): Record<string, any> {
|
|
90
|
-
return this._sessionAttributes || {}
|
|
91
|
-
}
|
|
92
|
-
set sessionAttributes(attributes: Record<string, any> | null) {
|
|
93
|
-
this._sessionAttributes = attributes
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Error message getter and setter
|
|
98
|
-
*/
|
|
99
|
-
public get error(): string {
|
|
100
|
-
return this._error || ''
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
public set error(v: string) {
|
|
104
|
-
this._error = v
|
|
105
|
-
}
|
|
106
|
-
private _error: string = ''
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* React Native doesn't have HTML elements, so we return null
|
|
110
|
-
*/
|
|
111
|
-
public get sessionWidgetButtonElement(): any {
|
|
112
|
-
return null
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Initialize debugger with default or custom configurations
|
|
117
|
-
*/
|
|
118
|
-
constructor() {
|
|
119
|
-
super()
|
|
120
|
-
// Initialize with stored session data if available
|
|
121
|
-
StorageService.initialize()
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
private async _loadStoredSessionData(): Promise<void> {
|
|
125
|
-
try {
|
|
126
|
-
await StorageService.initialize()
|
|
127
|
-
const storedData = await this._storageService.getAllSessionData()
|
|
128
|
-
if (isSessionActive(storedData.sessionObject, storedData.sessionType === SessionType.CONTINUOUS)) {
|
|
129
|
-
this.session = storedData.sessionObject
|
|
130
|
-
this.sessionId = storedData.sessionId
|
|
131
|
-
this.sessionType = storedData.sessionType || SessionType.PLAIN
|
|
132
|
-
this.sessionState = storedData.sessionState
|
|
133
|
-
} else {
|
|
134
|
-
this.session = null
|
|
135
|
-
this.sessionId = null
|
|
136
|
-
this.sessionState = null
|
|
137
|
-
this.sessionType = SessionType.PLAIN
|
|
138
|
-
}
|
|
139
|
-
} catch (error) {
|
|
140
|
-
logger.error('SessionRecorder', 'Failed to load stored session data', error)
|
|
141
|
-
this.session = null
|
|
142
|
-
this.sessionId = null
|
|
143
|
-
this.sessionState = null
|
|
144
|
-
this.sessionType = SessionType.PLAIN
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Initialize the session debugger
|
|
150
|
-
* @param configs - custom configurations for session debugger
|
|
151
|
-
*/
|
|
152
|
-
public async init(configs: SessionRecorderOptions): Promise<void> {
|
|
153
|
-
this._configs = getSessionRecorderConfig({ ...this._configs, ...configs })
|
|
154
|
-
this._isInitialized = true
|
|
155
|
-
this._checkOperation('init')
|
|
156
|
-
await this._loadStoredSessionData()
|
|
157
|
-
setMaxCapturingHttpPayloadSize(this._configs.maxCapturingHttpPayloadSize || DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE)
|
|
158
|
-
setShouldRecordHttpData(!this._configs.captureBody, this._configs.captureHeaders)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
this._apiService.init(this._configs)
|
|
163
|
-
this._tracer.init(this._configs)
|
|
164
|
-
} catch (error) {
|
|
165
|
-
logger.error('SessionRecorder', 'Failed to initialize API service', error)
|
|
166
|
-
}
|
|
167
|
-
if (this._configs.apiKey) {
|
|
168
|
-
this._recorder.init(this._configs)
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (this.sessionId && (this.sessionState === SessionState.started || this.sessionState === SessionState.paused)) {
|
|
172
|
-
this._start()
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Start a new session
|
|
179
|
-
* @param type - the type of session to start
|
|
180
|
-
* @param session - the session to start
|
|
181
|
-
*/
|
|
182
|
-
public async start(type: SessionType = SessionType.PLAIN, session?: ISession): Promise<void> {
|
|
183
|
-
this._checkOperation('start')
|
|
184
|
-
// If continuous recording is disabled, force plain mode
|
|
185
|
-
if (type === SessionType.CONTINUOUS && !this._configs?.showContinuousRecording) {
|
|
186
|
-
type = SessionType.PLAIN
|
|
187
|
-
}
|
|
188
|
-
this.sessionType = type
|
|
189
|
-
this._startRequestController = new AbortController()
|
|
190
|
-
if (session) {
|
|
191
|
-
this._setupSessionAndStart(session, true)
|
|
192
|
-
} else {
|
|
193
|
-
await this._createSessionAndStart()
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Stop the current session with an optional comment
|
|
199
|
-
* @param comment - user-provided comment to include in session session attributes
|
|
200
|
-
*/
|
|
201
|
-
public async stop(comment?: string): Promise<void> {
|
|
202
|
-
try {
|
|
203
|
-
this._checkOperation('stop')
|
|
204
|
-
this._stop()
|
|
205
|
-
if (this.continuousRecording) {
|
|
206
|
-
await this._apiService.stopContinuousDebugSession(this.sessionId!)
|
|
207
|
-
this.sessionType = SessionType.PLAIN
|
|
208
|
-
} else {
|
|
209
|
-
const request: StopSessionRequest = {
|
|
210
|
-
sessionAttributes: { comment },
|
|
211
|
-
stoppedAt: Date.now(),
|
|
212
|
-
}
|
|
213
|
-
const response = await this._apiService.stopSession(this.sessionId!, request)
|
|
214
|
-
}
|
|
215
|
-
this._clearSession()
|
|
216
|
-
} catch (error: any) {
|
|
217
|
-
this.error = error.message
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Pause the current session
|
|
223
|
-
*/
|
|
224
|
-
public async pause(): Promise<void> {
|
|
225
|
-
try {
|
|
226
|
-
this._checkOperation('pause')
|
|
227
|
-
this._pause()
|
|
228
|
-
} catch (error: any) {
|
|
229
|
-
this.error = error.message
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Resume the current session
|
|
235
|
-
*/
|
|
236
|
-
public async resume(): Promise<void> {
|
|
237
|
-
try {
|
|
238
|
-
this._checkOperation('resume')
|
|
239
|
-
this._resume()
|
|
240
|
-
} catch (error: any) {
|
|
241
|
-
this.error = error.message
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Cancel the current session
|
|
247
|
-
*/
|
|
248
|
-
public async cancel(): Promise<void> {
|
|
249
|
-
try {
|
|
250
|
-
this._checkOperation('cancel')
|
|
251
|
-
this._stop()
|
|
252
|
-
if (this.continuousRecording) {
|
|
253
|
-
await this._apiService.stopContinuousDebugSession(this.sessionId!)
|
|
254
|
-
this.sessionType = SessionType.PLAIN
|
|
255
|
-
} else {
|
|
256
|
-
await this._apiService.cancelSession(this.sessionId!)
|
|
257
|
-
}
|
|
258
|
-
this._clearSession()
|
|
259
|
-
} catch (error: any) {
|
|
260
|
-
this.error = error.message
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Save the continuous recording session
|
|
266
|
-
*/
|
|
267
|
-
public async save(): Promise<any> {
|
|
268
|
-
try {
|
|
269
|
-
this._checkOperation('save')
|
|
270
|
-
if (!this.continuousRecording || !this._configs?.showContinuousRecording) {
|
|
271
|
-
return
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const res = await this._apiService.saveContinuousDebugSession(
|
|
275
|
-
this.sessionId!,
|
|
276
|
-
{
|
|
277
|
-
sessionAttributes: this.sessionAttributes,
|
|
278
|
-
resourceAttributes: getNavigatorInfo(),
|
|
279
|
-
stoppedAt: Date.now(),
|
|
280
|
-
name: this.sessionAttributes.userName
|
|
281
|
-
? `${this.sessionAttributes.userName}'s session on ${getFormattedDate(
|
|
282
|
-
Date.now(),
|
|
283
|
-
{ month: 'short', day: 'numeric' },
|
|
284
|
-
)}`
|
|
285
|
-
: `Session on ${getFormattedDate(Date.now())}`,
|
|
286
|
-
},
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
return res
|
|
290
|
-
} catch (error: any) {
|
|
291
|
-
this.error = error.message
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Set the session attributes
|
|
297
|
-
* @param attributes - the attributes to set
|
|
298
|
-
*/
|
|
299
|
-
public setSessionAttributes(attributes: Record<string, any>): void {
|
|
300
|
-
this._sessionAttributes = attributes
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Set a custom click handler for the recording button
|
|
305
|
-
* @param handler - function that will be invoked when the button is clicked
|
|
306
|
-
*/
|
|
307
|
-
public set recordingButtonClickHandler(handler: () => boolean | void) {
|
|
308
|
-
// React Native doesn't have HTML elements, so this is a no-op
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* @description Check if session should be started/stopped automatically
|
|
313
|
-
* @param {ISession} [sessionPayload]
|
|
314
|
-
* @returns {Promise<void>}
|
|
315
|
-
*/
|
|
316
|
-
public async checkRemoteContinuousSession(
|
|
317
|
-
sessionPayload?: Omit<ISession, '_id' | 'shortId'>,
|
|
318
|
-
): Promise<void> {
|
|
319
|
-
this._checkOperation('autoStartRemoteContinuousSession')
|
|
320
|
-
if (!this._configs?.showContinuousRecording) {
|
|
321
|
-
return
|
|
322
|
-
}
|
|
323
|
-
const payload = {
|
|
324
|
-
sessionAttributes: {
|
|
325
|
-
...this.sessionAttributes,
|
|
326
|
-
...(sessionPayload?.sessionAttributes || {}),
|
|
327
|
-
},
|
|
328
|
-
resourceAttributes: {
|
|
329
|
-
...getNavigatorInfo(),
|
|
330
|
-
...(sessionPayload?.resourceAttributes || {}),
|
|
331
|
-
},
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const { state } = await this._apiService.checkRemoteSession(payload)
|
|
335
|
-
|
|
336
|
-
if (state == 'START') {
|
|
337
|
-
if (this.sessionState !== SessionState.started) {
|
|
338
|
-
await this.start(SessionType.CONTINUOUS)
|
|
339
|
-
}
|
|
340
|
-
} else if (state == 'STOP') {
|
|
341
|
-
if (this.sessionState !== SessionState.stopped) {
|
|
342
|
-
await this.stop()
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Create a new session and start it
|
|
349
|
-
*/
|
|
350
|
-
private async _createSessionAndStart(): Promise<void> {
|
|
351
|
-
const signal = this._startRequestController?.signal
|
|
352
|
-
try {
|
|
353
|
-
const payload = {
|
|
354
|
-
sessionAttributes: this.sessionAttributes,
|
|
355
|
-
resourceAttributes: getNavigatorInfo(),
|
|
356
|
-
name: this.sessionAttributes.userName
|
|
357
|
-
? `${this.sessionAttributes.userName}'s session on ${getFormattedDate(Date.now(), { month: 'short', day: 'numeric' })}`
|
|
358
|
-
: `Session on ${getFormattedDate(Date.now())}`,
|
|
359
|
-
}
|
|
360
|
-
const request: StartSessionRequest = !this.continuousRecording ?
|
|
361
|
-
payload : { debugSessionData: payload }
|
|
362
|
-
|
|
363
|
-
const session = this.continuousRecording
|
|
364
|
-
? await this._apiService.startContinuousDebugSession(request, signal)
|
|
365
|
-
: await this._apiService.startSession(request, signal)
|
|
366
|
-
|
|
367
|
-
if (session) {
|
|
368
|
-
session.sessionType = this.continuousRecording
|
|
369
|
-
? SessionType.CONTINUOUS
|
|
370
|
-
: SessionType.PLAIN
|
|
371
|
-
this._setupSessionAndStart(session, false)
|
|
372
|
-
}
|
|
373
|
-
} catch (error: any) {
|
|
374
|
-
this.error = error.message
|
|
375
|
-
if (this.continuousRecording) {
|
|
376
|
-
this.sessionType = SessionType.PLAIN
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Start tracing and recording for the session
|
|
383
|
-
*/
|
|
384
|
-
private _start(): void {
|
|
385
|
-
this.sessionState = SessionState.started
|
|
386
|
-
if (this.sessionId) {
|
|
387
|
-
this._tracer.start(this.sessionId, this.sessionType)
|
|
388
|
-
this._recorder.start(this.sessionId, this.sessionType)
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Stop tracing and recording for the session
|
|
394
|
-
*/
|
|
395
|
-
private _stop(): void {
|
|
396
|
-
this.sessionState = SessionState.stopped
|
|
397
|
-
this._tracer.shutdown()
|
|
398
|
-
this._recorder.stop()
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Pause the session tracing and recording
|
|
403
|
-
*/
|
|
404
|
-
private _pause(): void {
|
|
405
|
-
this._tracer.shutdown()
|
|
406
|
-
this._recorder.stop()
|
|
407
|
-
this.sessionState = SessionState.paused
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Resume the session tracing and recording
|
|
412
|
-
*/
|
|
413
|
-
private _resume(): void {
|
|
414
|
-
if (this.sessionId) {
|
|
415
|
-
this._tracer.setSessionId(this.sessionId, this.sessionType)
|
|
416
|
-
this._recorder.start(this.sessionId, this.sessionType)
|
|
417
|
-
}
|
|
418
|
-
this.sessionState = SessionState.started
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
private _setupSessionAndStart(session: ISession, configureExporters: boolean = true): void {
|
|
422
|
-
if (configureExporters && session.tempApiKey) {
|
|
423
|
-
this._configs!.apiKey = session.tempApiKey
|
|
424
|
-
this._recorder.init(this._configs!)
|
|
425
|
-
this._tracer.init(this._configs!)
|
|
426
|
-
this._apiService.updateConfigs({ apiKey: this._configs!.apiKey })
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
this._setSession(session)
|
|
430
|
-
this._start()
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
/**
|
|
434
|
-
* Set the session ID in storage
|
|
435
|
-
* @param sessionId - the session ID to set or clear
|
|
436
|
-
*/
|
|
437
|
-
private _setSession(
|
|
438
|
-
session: ISession,
|
|
439
|
-
): void {
|
|
440
|
-
this.session = { ...session, createdAt: session.createdAt || new Date().toISOString() }
|
|
441
|
-
this.sessionId = session?.shortId || session?._id
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
private _clearSession(): void {
|
|
445
|
-
this.session = null
|
|
446
|
-
this.sessionId = null
|
|
447
|
-
this.sessionState = SessionState.stopped
|
|
448
|
-
this._storageService.clearSessionData()
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Check the operation validity based on the session state and action
|
|
453
|
-
* @param action - action being checked ('init', 'start', 'stop', 'cancel', 'pause', 'resume')
|
|
454
|
-
*/
|
|
455
|
-
private _checkOperation(
|
|
456
|
-
action:
|
|
457
|
-
| 'init'
|
|
458
|
-
| 'start'
|
|
459
|
-
| 'stop'
|
|
460
|
-
| 'cancel'
|
|
461
|
-
| 'pause'
|
|
462
|
-
| 'resume'
|
|
463
|
-
| 'save'
|
|
464
|
-
| 'autoStartRemoteContinuousSession',
|
|
465
|
-
payload?: any,
|
|
466
|
-
): void {
|
|
467
|
-
if (!this._isInitialized) {
|
|
468
|
-
throw new Error(
|
|
469
|
-
'Configuration not initialized. Call init() before performing any actions.',
|
|
470
|
-
)
|
|
471
|
-
}
|
|
472
|
-
switch (action) {
|
|
473
|
-
case 'start':
|
|
474
|
-
if (this.sessionState === SessionState.started) {
|
|
475
|
-
throw new Error('Session is already started.')
|
|
476
|
-
}
|
|
477
|
-
break
|
|
478
|
-
case 'stop':
|
|
479
|
-
if (this.sessionState !== SessionState.paused && this.sessionState !== SessionState.started) {
|
|
480
|
-
throw new Error('Cannot stop. Session is not currently started.')
|
|
481
|
-
}
|
|
482
|
-
break
|
|
483
|
-
case 'cancel':
|
|
484
|
-
if (this.sessionState === SessionState.stopped) {
|
|
485
|
-
throw new Error('Cannot cancel. Session has already been stopped.')
|
|
486
|
-
}
|
|
487
|
-
break
|
|
488
|
-
case 'pause':
|
|
489
|
-
if (this.sessionState !== SessionState.started) {
|
|
490
|
-
throw new Error('Cannot pause. Session is not running.')
|
|
491
|
-
}
|
|
492
|
-
break
|
|
493
|
-
case 'resume':
|
|
494
|
-
if (this.sessionState !== SessionState.paused) {
|
|
495
|
-
throw new Error('Cannot resume. Session is not paused.')
|
|
496
|
-
}
|
|
497
|
-
break
|
|
498
|
-
case 'save':
|
|
499
|
-
if (!this.continuousRecording) {
|
|
500
|
-
throw new Error('Cannot save continuous recording session. Continuous recording is not enabled.')
|
|
501
|
-
}
|
|
502
|
-
if (this.sessionState !== SessionState.started) {
|
|
503
|
-
throw new Error('Cannot save continuous recording session. Session is not started.')
|
|
504
|
-
}
|
|
505
|
-
break
|
|
506
|
-
case 'autoStartRemoteContinuousSession':
|
|
507
|
-
if (this.sessionState !== SessionState.stopped) {
|
|
508
|
-
throw new Error('Cannot start remote continuous session. Session is not stopped.')
|
|
509
|
-
}
|
|
510
|
-
break
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
// Session attributes
|
|
514
|
-
setSessionAttribute(key: string, value: any): void {
|
|
515
|
-
if (this._session) {
|
|
516
|
-
if (!this._session.sessionAttributes) {
|
|
517
|
-
this._session.sessionAttributes = {}
|
|
518
|
-
}
|
|
519
|
-
this._session.sessionAttributes[key] = value
|
|
520
|
-
this._session.updatedAt = new Date().toISOString()
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Record a custom rrweb event
|
|
526
|
-
* Note: Screen capture and touch events are recorded automatically when session is started
|
|
527
|
-
* @param event - The rrweb event to record
|
|
528
|
-
*/
|
|
529
|
-
recordEvent(event: eventWithTime): void {
|
|
530
|
-
if (!this._isInitialized || this.sessionState !== SessionState.started) {
|
|
531
|
-
return
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// Forward the event to the recorder SDK
|
|
535
|
-
this._recorder.recordEvent(event)
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Record touch start event (internal use - touch recording is automatic)
|
|
540
|
-
* @param x - X coordinate
|
|
541
|
-
* @param y - Y coordinate
|
|
542
|
-
* @param target - Target element identifier
|
|
543
|
-
* @param pressure - Touch pressure
|
|
544
|
-
* @internal
|
|
545
|
-
*/
|
|
546
|
-
recordTouchStart(x: number, y: number, target?: string, pressure?: number): void {
|
|
547
|
-
if (!this._isInitialized || this.sessionState !== SessionState.started) {
|
|
548
|
-
return
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
// Forward to gesture recorder
|
|
552
|
-
this._recorder.recordTouchStart(x, y, target, pressure)
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
/**
|
|
556
|
-
* Record touch move event (internal use - touch recording is automatic)
|
|
557
|
-
* @param x - X coordinate
|
|
558
|
-
* @param y - Y coordinate
|
|
559
|
-
* @param target - Target element identifier
|
|
560
|
-
* @param pressure - Touch pressure
|
|
561
|
-
* @internal
|
|
562
|
-
*/
|
|
563
|
-
recordTouchMove(x: number, y: number, target?: string, pressure?: number): void {
|
|
564
|
-
if (!this._isInitialized || this.sessionState !== SessionState.started) {
|
|
565
|
-
return
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// Forward to gesture recorder
|
|
569
|
-
this._recorder.recordTouchMove(x, y, target, pressure)
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Record touch end event (internal use - touch recording is automatic)
|
|
574
|
-
* @param x - X coordinate
|
|
575
|
-
* @param y - Y coordinate
|
|
576
|
-
* @param target - Target element identifier
|
|
577
|
-
* @param pressure - Touch pressure
|
|
578
|
-
* @internal
|
|
579
|
-
*/
|
|
580
|
-
recordTouchEnd(x: number, y: number, target?: string, pressure?: number): void {
|
|
581
|
-
if (!this._isInitialized || this.sessionState !== SessionState.started) {
|
|
582
|
-
return
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// Forward to gesture recorder
|
|
586
|
-
this._recorder.recordTouchEnd(x, y, target, pressure)
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/**
|
|
590
|
-
* Set the viewshot ref for screen capture
|
|
591
|
-
* @param ref - React Native View ref for screen capture
|
|
592
|
-
*/
|
|
593
|
-
setViewShotRef(ref: any): void {
|
|
594
|
-
if (this._recorder) {
|
|
595
|
-
this._recorder.setViewShotRef(ref)
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
export default new SessionRecorder()
|
package/src/types/expo.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { SessionRecorderOptions, PlatformInfo } from './index'
|
|
2
|
-
|
|
3
|
-
declare module '@multiplayer-app/session-recorder-react-native' {
|
|
4
|
-
export interface ExpoSessionRecorderOptions extends SessionRecorderOptions {
|
|
5
|
-
platform: 'expo'
|
|
6
|
-
expoVersion?: string
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface ExpoPlatformInfo {
|
|
10
|
-
isExpo: true
|
|
11
|
-
isReactNative: true
|
|
12
|
-
platform: 'ios' | 'android' | 'web'
|
|
13
|
-
expoVersion: string
|
|
14
|
-
deviceType: 'expo'
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function isExpoEnvironment(): boolean
|
|
18
|
-
export function getPlatformAttributes(): Record<string, any>
|
|
19
|
-
export function detectPlatform(): PlatformInfo | ExpoPlatformInfo
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Expo-specific exports
|
|
23
|
-
export * from './index'
|
package/src/types/index.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export * from './session-recorder'
|
|
2
|
-
export * from './session'
|
|
3
|
-
|
|
4
|
-
// Import types for use in this file
|
|
5
|
-
import type { eventWithTime } from '@rrweb/types'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export interface ReactNativeScreenData {
|
|
9
|
-
width: number
|
|
10
|
-
height: number
|
|
11
|
-
base64Image: string
|
|
12
|
-
timestamp: number
|
|
13
|
-
screenName?: string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface ReactNativeTouchData {
|
|
17
|
-
pageX: number
|
|
18
|
-
pageY: number
|
|
19
|
-
target?: string
|
|
20
|
-
pressure?: number
|
|
21
|
-
timestamp: number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// Event recording interface
|
|
26
|
-
export interface EventRecorder {
|
|
27
|
-
recordEvent(event: eventWithTime): void
|
|
28
|
-
}
|