@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.
Files changed (76) hide show
  1. package/copy-react-native-dist.sh +3 -3
  2. package/docs/NATIVE_MODULE_SETUP.md +175 -0
  3. package/ios/SessionRecorderNative.podspec +5 -0
  4. package/package.json +11 -1
  5. package/plugin/package.json +20 -0
  6. package/plugin/src/index.js +42 -0
  7. package/react-native.config.js +1 -1
  8. package/android/src/main/AndroidManifest.xml +0 -2
  9. package/android/src/main/java/com/multiplayer/sessionrecorder/ScreenMaskingModule.kt +0 -202
  10. package/android/src/main/java/com/multiplayer/sessionrecorder/ScreenMaskingPackage.kt +0 -16
  11. package/android/src/main/java/com/multiplayer/sessionrecorder/SessionRecorderModule.kt +0 -202
  12. package/android/src/main/java/com/multiplayer/sessionrecorder/SessionRecorderPackage.kt +0 -16
  13. package/babel.config.js +0 -13
  14. package/docs/AUTO_METADATA_DETECTION.md +0 -108
  15. package/docs/TROUBLESHOOTING.md +0 -168
  16. package/ios/ScreenMasking.m +0 -12
  17. package/ios/ScreenMasking.podspec +0 -21
  18. package/ios/ScreenMasking.swift +0 -205
  19. package/ios/SessionRecorder.podspec +0 -21
  20. package/scripts/generate-app-metadata.js +0 -173
  21. package/src/components/GestureCaptureWrapper/GestureCaptureWrapper.tsx +0 -86
  22. package/src/components/GestureCaptureWrapper/index.ts +0 -1
  23. package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +0 -72
  24. package/src/components/ScreenRecorderView/index.ts +0 -1
  25. package/src/components/SessionRecorderWidget/FinalPopover.tsx +0 -62
  26. package/src/components/SessionRecorderWidget/FloatingButton.tsx +0 -136
  27. package/src/components/SessionRecorderWidget/InitialPopover.tsx +0 -89
  28. package/src/components/SessionRecorderWidget/ModalContainer.tsx +0 -128
  29. package/src/components/SessionRecorderWidget/ModalHeader.tsx +0 -24
  30. package/src/components/SessionRecorderWidget/SessionRecorderWidget.tsx +0 -109
  31. package/src/components/SessionRecorderWidget/icons.tsx +0 -52
  32. package/src/components/SessionRecorderWidget/index.ts +0 -3
  33. package/src/components/SessionRecorderWidget/styles.ts +0 -150
  34. package/src/components/index.ts +0 -3
  35. package/src/config/constants.ts +0 -60
  36. package/src/config/defaults.ts +0 -83
  37. package/src/config/index.ts +0 -6
  38. package/src/config/masking.ts +0 -28
  39. package/src/config/session-recorder.ts +0 -55
  40. package/src/config/validators.ts +0 -31
  41. package/src/context/SessionRecorderContext.tsx +0 -53
  42. package/src/index.ts +0 -9
  43. package/src/native/ScreenMasking.ts +0 -34
  44. package/src/native/SessionRecorderNative.ts +0 -34
  45. package/src/otel/helpers.ts +0 -275
  46. package/src/otel/index.ts +0 -138
  47. package/src/otel/instrumentations/index.ts +0 -115
  48. package/src/patch/index.ts +0 -1
  49. package/src/patch/xhr.ts +0 -141
  50. package/src/recorder/eventExporter.ts +0 -141
  51. package/src/recorder/gestureRecorder.ts +0 -498
  52. package/src/recorder/index.ts +0 -179
  53. package/src/recorder/navigationTracker.ts +0 -449
  54. package/src/recorder/screenRecorder.ts +0 -527
  55. package/src/services/api.service.ts +0 -203
  56. package/src/services/screenMaskingService.ts +0 -118
  57. package/src/services/storage.service.ts +0 -199
  58. package/src/session-recorder.ts +0 -606
  59. package/src/types/expo.d.ts +0 -23
  60. package/src/types/index.ts +0 -28
  61. package/src/types/session-recorder.ts +0 -429
  62. package/src/types/session.ts +0 -65
  63. package/src/utils/app-metadata.ts +0 -31
  64. package/src/utils/index.ts +0 -8
  65. package/src/utils/logger.ts +0 -225
  66. package/src/utils/nativeModuleTest.ts +0 -60
  67. package/src/utils/platform.ts +0 -384
  68. package/src/utils/request-utils.ts +0 -61
  69. package/src/utils/rrweb-events.ts +0 -309
  70. package/src/utils/session.ts +0 -18
  71. package/src/utils/time.ts +0 -17
  72. package/src/utils/type-utils.ts +0 -75
  73. package/src/version.ts +0 -1
  74. package/tsconfig.json +0 -24
  75. /package/ios/{SessionRecorder.m → SessionRecorderNative.m} +0 -0
  76. /package/ios/{SessionRecorder.swift → SessionRecorderNative.swift} +0 -0
@@ -1,203 +0,0 @@
1
- import { SessionRecorderOptions, IResourceAttributes, ISessionAttributes } from '../types'
2
-
3
- export interface StartSessionRequest {
4
- name?: string
5
- stoppedAt?: string | number
6
- sessionAttributes?: ISessionAttributes
7
- resourceAttributes?: IResourceAttributes
8
- debugSessionData?: Record<string, any>
9
- }
10
-
11
- export interface StopSessionRequest {
12
- sessionAttributes?: ISessionAttributes
13
- stoppedAt: string | number
14
- }
15
-
16
- export class ApiService {
17
- private config?: SessionRecorderOptions
18
- private baseUrl: string = 'https://api.multiplayer.app'
19
-
20
- constructor() {
21
- this.config = {
22
- apiKey: '',
23
- apiBaseUrl: '',
24
- exporterEndpoint: '',
25
- version: '',
26
- application: '',
27
- environment: '',
28
- } as SessionRecorderOptions
29
- }
30
-
31
- init(config: SessionRecorderOptions): void {
32
-
33
- this.config = {
34
- ...this.config,
35
- ...config,
36
- }
37
- if (config.apiBaseUrl) {
38
- this.baseUrl = config.apiBaseUrl
39
- }
40
- }
41
-
42
- /**
43
- * Update the API service configuration
44
- * @param config - Partial configuration to update
45
- */
46
- public updateConfigs(config: Partial<SessionRecorderOptions>) {
47
- if (this.config) {
48
- this.config = { ...this.config, ...config }
49
- }
50
- }
51
-
52
- /**
53
- * Make a request to the session debugger API
54
- * @param path - API endpoint path (relative to the base URL)
55
- * @param method - HTTP method (GET, POST, PATCH, etc.)
56
- * @param body - request payload
57
- * @param signal - AbortSignal to set request's signal
58
- */
59
- private async makeRequest(
60
- path: string,
61
- method: string,
62
- body?: any,
63
- signal?: AbortSignal,
64
- ): Promise<any> {
65
- const url = `${this.baseUrl}/v0/radar${path}`
66
- const params = {
67
- method,
68
- body: body ? JSON.stringify(body) : null,
69
- headers: {
70
- 'Content-Type': 'application/json',
71
- ...(this.config?.apiKey && { 'X-Api-Key': this.config.apiKey }),
72
- },
73
- }
74
-
75
- try {
76
- const response = await fetch(url, {
77
- ...params,
78
- signal,
79
- })
80
-
81
- if (!response.ok) {
82
- throw new Error('Network response was not ok: ' + response.statusText)
83
- }
84
-
85
- if (response.status === 204) {
86
- return null
87
- }
88
-
89
- return await response.json()
90
- } catch (error: any) {
91
- if (error?.name === 'AbortError') {
92
- throw new Error('Request aborted')
93
- }
94
- throw new Error('Error making request: ' + error.message)
95
- }
96
- }
97
-
98
- /**
99
- * Start a new debug session
100
- * @param request - Session start request data
101
- * @param signal - Optional AbortSignal for request cancellation
102
- */
103
- async startSession(
104
- request: StartSessionRequest,
105
- signal?: AbortSignal,
106
- ): Promise<any> {
107
- return this.makeRequest(
108
- '/debug-sessions/start',
109
- 'POST',
110
- request,
111
- signal,
112
- )
113
- }
114
-
115
- /**
116
- * Stop an active debug session
117
- * @param sessionId - ID of the session to stop
118
- * @param request - Session stop request data
119
- */
120
- async stopSession(
121
- sessionId: string,
122
- request: StopSessionRequest,
123
- ): Promise<any> {
124
- return this.makeRequest(
125
- `/debug-sessions/${sessionId}/stop`,
126
- 'PATCH',
127
- request,
128
- )
129
- }
130
-
131
- /**
132
- * Cancel an active debug session
133
- * @param sessionId - ID of the session to cancel
134
- */
135
- async cancelSession(sessionId: string): Promise<any> {
136
- return this.makeRequest(
137
- `/debug-sessions/${sessionId}/cancel`,
138
- 'DELETE',
139
- )
140
- }
141
-
142
- /**
143
- * Start a new continuous debug session
144
- * @param request - Session start request data
145
- * @param signal - Optional AbortSignal for request cancellation
146
- */
147
- async startContinuousDebugSession(
148
- request: StartSessionRequest,
149
- signal?: AbortSignal,
150
- ): Promise<any> {
151
- return this.makeRequest(
152
- '/continuous-debug-sessions/start',
153
- 'POST',
154
- request,
155
- signal,
156
- )
157
- }
158
-
159
- /**
160
- * Save a continuous debug session
161
- * @param sessionId - ID of the session to save
162
- * @param request - Session save request data
163
- * @param signal - Optional AbortSignal for request cancellation
164
- */
165
- async saveContinuousDebugSession(
166
- sessionId: string,
167
- request: StartSessionRequest,
168
- signal?: AbortSignal,
169
- ): Promise<any> {
170
- return this.makeRequest(
171
- `/continuous-debug-sessions/${sessionId}/save`,
172
- 'POST',
173
- request,
174
- signal,
175
- )
176
- }
177
-
178
- /**
179
- * Stop an active continuous debug session
180
- * @param sessionId - ID of the session to stop
181
- */
182
- async stopContinuousDebugSession(sessionId: string): Promise<any> {
183
- return this.makeRequest(
184
- `/continuous-debug-sessions/${sessionId}/cancel`,
185
- 'DELETE',
186
- )
187
- }
188
-
189
- /**
190
- * Check debug session should be started remotely
191
- */
192
- async checkRemoteSession(
193
- requestBody: StartSessionRequest,
194
- signal?: AbortSignal,
195
- ): Promise<{ state: 'START' | 'STOP' }> {
196
- return this.makeRequest(
197
- '/remote-debug-session/check',
198
- 'POST',
199
- requestBody,
200
- signal,
201
- )
202
- }
203
- }
@@ -1,118 +0,0 @@
1
- import SessionRecorderNative, { MaskingOptions } from '../native/SessionRecorderNative'
2
- import { logger } from '../utils'
3
- import { testNativeModuleAvailability } from '../utils/nativeModuleTest'
4
-
5
- export interface ScreenMaskingConfig {
6
- /** Whether screen masking is enabled */
7
- enabled: boolean
8
- /** Whether to mask all input fields automatically */
9
- inputMasking: boolean
10
- /** Default masking options */
11
- defaultOptions?: MaskingOptions
12
- }
13
-
14
- export class ScreenMaskingService {
15
- private config: ScreenMaskingConfig
16
- private isAvailable: boolean = false
17
-
18
- constructor(config: ScreenMaskingConfig = { enabled: true, inputMasking: true }) {
19
- this.config = config
20
- this.checkAvailability()
21
- }
22
-
23
- /**
24
- * Check if the native masking module is available
25
- */
26
- private checkAvailability(): void {
27
- try {
28
- // Try to access the native module to check if it's available
29
- if (SessionRecorderNative && typeof SessionRecorderNative.captureAndMask === 'function') {
30
- this.isAvailable = true
31
- logger.info('ScreenMaskingService', 'Screen masking native module is available')
32
- } else {
33
- this.isAvailable = false
34
- logger.warn('ScreenMaskingService', 'Screen masking native module is not available - auto-linking may still be in progress')
35
-
36
- // Run diagnostic test
37
- testNativeModuleAvailability()
38
-
39
- // Retry after a delay for auto-linking
40
- setTimeout(() => {
41
- this.checkAvailability()
42
- }, 2000)
43
- }
44
- } catch (error) {
45
- this.isAvailable = false
46
- logger.error('ScreenMaskingService', 'Error checking screen masking availability:', error)
47
- }
48
- }
49
-
50
- /**
51
- * Capture screen with masking applied
52
- */
53
- async captureMaskedScreen(options?: MaskingOptions): Promise<string | null> {
54
- if (!this.isAvailable || !this.config.enabled) {
55
- logger.warn('ScreenMaskingService', 'Screen masking is not available or disabled')
56
- return null
57
- }
58
-
59
- try {
60
- const maskingOptions: MaskingOptions = {
61
- ...this.config.defaultOptions,
62
- ...options,
63
- inputMasking: this.config.inputMasking,
64
- }
65
-
66
- const maskedImageBase64 = await SessionRecorderNative.captureAndMaskWithOptions(maskingOptions)
67
- logger.info('ScreenMaskingService', 'Successfully captured masked screen')
68
- return maskedImageBase64
69
- } catch (error) {
70
- logger.error('ScreenMaskingService', 'Failed to capture masked screen:', error)
71
- return null
72
- }
73
- }
74
-
75
- /**
76
- * Capture screen with basic masking (no custom options)
77
- */
78
- async captureMaskedScreenBasic(): Promise<string | null> {
79
- if (!this.isAvailable || !this.config.enabled) {
80
- logger.warn('ScreenMaskingService', 'Screen masking is not available or disabled')
81
- return null
82
- }
83
-
84
- try {
85
- const maskedImageBase64 = await SessionRecorderNative.captureAndMask()
86
- logger.info('ScreenMaskingService', 'Successfully captured masked screen (basic)')
87
- return maskedImageBase64
88
- } catch (error) {
89
- logger.error('ScreenMaskingService', 'Failed to capture masked screen (basic):', error)
90
- return null
91
- }
92
- }
93
-
94
- /**
95
- * Update the masking configuration
96
- */
97
- updateConfig(config: Partial<ScreenMaskingConfig>): void {
98
- this.config = { ...this.config, ...config }
99
- logger.info('ScreenMaskingService', 'Screen masking configuration updated')
100
- }
101
-
102
- /**
103
- * Check if screen masking is available
104
- */
105
- isScreenMaskingAvailable(): boolean {
106
- return this.isAvailable && this.config.enabled
107
- }
108
-
109
- /**
110
- * Get the current configuration
111
- */
112
- getConfig(): ScreenMaskingConfig {
113
- return { ...this.config }
114
- }
115
- }
116
-
117
- // Create a singleton instance
118
- export const screenMaskingService = new ScreenMaskingService()
@@ -1,199 +0,0 @@
1
- import AsyncStorage from '@react-native-async-storage/async-storage'
2
- import { SessionType } from '@multiplayer-app/session-recorder-common'
3
- import { ISession, SessionState } from '../types'
4
- import { logger } from '../utils'
5
-
6
- interface CacheData {
7
- sessionId: string | null
8
- sessionType: SessionType | null
9
- sessionState: SessionState | null
10
- sessionObject: ISession | null
11
- floatingButtonPosition: { x: number; y: number } | null
12
- }
13
-
14
- export class StorageService {
15
- private static readonly SESSION_ID_KEY = 'session_id'
16
- private static readonly SESSION_TYPE_KEY = 'session_type'
17
- private static readonly SESSION_STATE_KEY = 'session_state'
18
- private static readonly SESSION_OBJECT_KEY = 'session_object'
19
- private static readonly FLOATING_BUTTON_POSITION_KEY = 'floating_button_position'
20
-
21
- private static cache: CacheData = {
22
- sessionId: null,
23
- sessionType: null,
24
- sessionState: null,
25
- sessionObject: null,
26
- floatingButtonPosition: null,
27
- }
28
-
29
- private static cacheInitialized = false
30
- private static instance: StorageService | null = null
31
- private static positionSaveTimeout: NodeJS.Timeout | null = null
32
-
33
- private constructor() {
34
- // Private constructor for singleton
35
- }
36
-
37
- static getInstance(): StorageService {
38
- if (!StorageService.instance) {
39
- StorageService.instance = new StorageService()
40
- StorageService.initialize()
41
- }
42
- return StorageService.instance
43
- }
44
-
45
- private static async initializeCache(): Promise<void> {
46
- if (StorageService.cacheInitialized) return
47
-
48
- try {
49
- const [sessionId, sessionType, sessionState, sessionObject, floatingButtonPosition] = await Promise.all([
50
- AsyncStorage.getItem(StorageService.SESSION_ID_KEY),
51
- AsyncStorage.getItem(StorageService.SESSION_TYPE_KEY),
52
- AsyncStorage.getItem(StorageService.SESSION_STATE_KEY),
53
- AsyncStorage.getItem(StorageService.SESSION_OBJECT_KEY),
54
- AsyncStorage.getItem(StorageService.FLOATING_BUTTON_POSITION_KEY),
55
- ])
56
-
57
- StorageService.cache = {
58
- sessionId,
59
- sessionType: sessionType as SessionType | null,
60
- sessionState: sessionState as SessionState | null,
61
- sessionObject: sessionObject ? JSON.parse(sessionObject) : null,
62
- floatingButtonPosition: floatingButtonPosition ? JSON.parse(floatingButtonPosition) : null,
63
- }
64
- StorageService.cacheInitialized = true
65
- } catch (error) {
66
- // Failed to initialize cache - silently continue
67
- StorageService.cacheInitialized = true // Mark as initialized to prevent retries
68
- }
69
- }
70
-
71
- saveSessionId(sessionId: string): void {
72
- try {
73
- StorageService.cache.sessionId = sessionId
74
- AsyncStorage.setItem(StorageService.SESSION_ID_KEY, sessionId).catch(error => {
75
- // Failed to persist session ID - silently continue
76
- })
77
- } catch (error) {
78
- // Failed to save session ID - silently continue
79
- throw error
80
- }
81
- }
82
-
83
- getSessionId(): string | null {
84
- return StorageService.cache.sessionId
85
- }
86
-
87
- saveSessionType(sessionType: SessionType): void {
88
- try {
89
- StorageService.cache.sessionType = sessionType
90
- AsyncStorage.setItem(StorageService.SESSION_TYPE_KEY, sessionType).catch(error => {
91
- // Failed to persist session type - silently continue
92
- })
93
- } catch (error) {
94
- // Failed to save session type - silently continue
95
- throw error
96
- }
97
- }
98
-
99
- getSessionType(): SessionType | null {
100
- return StorageService.cache.sessionType
101
- }
102
-
103
- saveSessionState(state: SessionState): void {
104
- try {
105
- StorageService.cache.sessionState = state
106
-
107
- AsyncStorage.setItem(StorageService.SESSION_STATE_KEY, state).catch(error => {
108
- // Failed to persist session state - silently continue
109
- })
110
- } catch (error) {
111
- // Failed to save session state - silently continue
112
- throw error
113
- }
114
- }
115
-
116
- getSessionState(): SessionState | null {
117
- return StorageService.cache.sessionState
118
- }
119
-
120
- saveSessionObject(session: ISession): void {
121
- try {
122
- StorageService.cache.sessionObject = session
123
- AsyncStorage.setItem(StorageService.SESSION_OBJECT_KEY, JSON.stringify(session)).catch(error => {
124
- // Failed to persist session object - silently continue
125
- })
126
- } catch (error) {
127
- // Failed to save session object - silently continue
128
- throw error
129
- }
130
- }
131
-
132
- getSessionObject(): ISession | null {
133
- return StorageService.cache.sessionObject
134
- }
135
-
136
- clearSessionData(): void {
137
- try {
138
- // Clear cache immediately
139
- StorageService.cache = {
140
- ...StorageService.cache,
141
- sessionId: null,
142
- sessionType: null,
143
- sessionState: null,
144
- sessionObject: null,
145
- }
146
-
147
- // Clear persistent storage asynchronously
148
- AsyncStorage.multiRemove([
149
- StorageService.SESSION_ID_KEY,
150
- StorageService.SESSION_TYPE_KEY,
151
- StorageService.SESSION_STATE_KEY,
152
- StorageService.SESSION_OBJECT_KEY,
153
- ]).catch(error => {
154
- // Failed to clear session data from storage - silently continue
155
- })
156
- } catch (error) {
157
- // Failed to clear session data - silently continue
158
- throw error
159
- }
160
- }
161
-
162
- getAllSessionData(): Omit<CacheData, 'floatingButtonPosition'> {
163
- return {
164
- sessionId: StorageService.cache.sessionId,
165
- sessionType: StorageService.cache.sessionType,
166
- sessionState: StorageService.cache.sessionState,
167
- sessionObject: StorageService.cache.sessionObject,
168
- }
169
- }
170
-
171
- saveFloatingButtonPosition(position: { x: number; y: number }): void {
172
- try {
173
- StorageService.cache.floatingButtonPosition = position
174
-
175
- // Debounce AsyncStorage writes to avoid excessive I/O
176
- if (StorageService.positionSaveTimeout) {
177
- clearTimeout(StorageService.positionSaveTimeout)
178
- }
179
-
180
- StorageService.positionSaveTimeout = setTimeout(() => {
181
- AsyncStorage.setItem(StorageService.FLOATING_BUTTON_POSITION_KEY, JSON.stringify(position)).catch(error => {
182
- logger.error('StorageService', 'Failed to persist floating button position', error)
183
- })
184
- }, 100) // 100ms debounce
185
- } catch (error) {
186
- logger.error('StorageService', 'Failed to save floating button position', error)
187
- throw error
188
- }
189
- }
190
-
191
- getFloatingButtonPosition(): { x: number; y: number } | null {
192
- return StorageService.cache.floatingButtonPosition
193
- }
194
-
195
- // Initialize cache on first use - call this method when the service is first used
196
- static async initialize(): Promise<void> {
197
- await StorageService.initializeCache()
198
- }
199
- }