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

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 (75) hide show
  1. package/app.plugin.js +42 -0
  2. package/docs/NATIVE_MODULE_SETUP.md +177 -0
  3. package/ios/SessionRecorderNative.podspec +5 -0
  4. package/package.json +10 -1
  5. package/plugin/package.json +20 -0
  6. package/plugin/src/index.js +42 -0
  7. package/android/src/main/AndroidManifest.xml +0 -2
  8. package/android/src/main/java/com/multiplayer/sessionrecorder/ScreenMaskingModule.kt +0 -202
  9. package/android/src/main/java/com/multiplayer/sessionrecorder/ScreenMaskingPackage.kt +0 -16
  10. package/android/src/main/java/com/multiplayer/sessionrecorder/SessionRecorderModule.kt +0 -202
  11. package/android/src/main/java/com/multiplayer/sessionrecorder/SessionRecorderPackage.kt +0 -16
  12. package/babel.config.js +0 -13
  13. package/docs/AUTO_METADATA_DETECTION.md +0 -108
  14. package/docs/TROUBLESHOOTING.md +0 -168
  15. package/ios/ScreenMasking.m +0 -12
  16. package/ios/ScreenMasking.podspec +0 -21
  17. package/ios/ScreenMasking.swift +0 -205
  18. package/ios/SessionRecorder.podspec +0 -21
  19. package/scripts/generate-app-metadata.js +0 -173
  20. package/src/components/GestureCaptureWrapper/GestureCaptureWrapper.tsx +0 -86
  21. package/src/components/GestureCaptureWrapper/index.ts +0 -1
  22. package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +0 -72
  23. package/src/components/ScreenRecorderView/index.ts +0 -1
  24. package/src/components/SessionRecorderWidget/FinalPopover.tsx +0 -62
  25. package/src/components/SessionRecorderWidget/FloatingButton.tsx +0 -136
  26. package/src/components/SessionRecorderWidget/InitialPopover.tsx +0 -89
  27. package/src/components/SessionRecorderWidget/ModalContainer.tsx +0 -128
  28. package/src/components/SessionRecorderWidget/ModalHeader.tsx +0 -24
  29. package/src/components/SessionRecorderWidget/SessionRecorderWidget.tsx +0 -109
  30. package/src/components/SessionRecorderWidget/icons.tsx +0 -52
  31. package/src/components/SessionRecorderWidget/index.ts +0 -3
  32. package/src/components/SessionRecorderWidget/styles.ts +0 -150
  33. package/src/components/index.ts +0 -3
  34. package/src/config/constants.ts +0 -60
  35. package/src/config/defaults.ts +0 -83
  36. package/src/config/index.ts +0 -6
  37. package/src/config/masking.ts +0 -28
  38. package/src/config/session-recorder.ts +0 -55
  39. package/src/config/validators.ts +0 -31
  40. package/src/context/SessionRecorderContext.tsx +0 -53
  41. package/src/index.ts +0 -9
  42. package/src/native/ScreenMasking.ts +0 -34
  43. package/src/native/SessionRecorderNative.ts +0 -34
  44. package/src/otel/helpers.ts +0 -275
  45. package/src/otel/index.ts +0 -138
  46. package/src/otel/instrumentations/index.ts +0 -115
  47. package/src/patch/index.ts +0 -1
  48. package/src/patch/xhr.ts +0 -141
  49. package/src/recorder/eventExporter.ts +0 -141
  50. package/src/recorder/gestureRecorder.ts +0 -498
  51. package/src/recorder/index.ts +0 -179
  52. package/src/recorder/navigationTracker.ts +0 -449
  53. package/src/recorder/screenRecorder.ts +0 -527
  54. package/src/services/api.service.ts +0 -203
  55. package/src/services/screenMaskingService.ts +0 -118
  56. package/src/services/storage.service.ts +0 -199
  57. package/src/session-recorder.ts +0 -606
  58. package/src/types/expo.d.ts +0 -23
  59. package/src/types/index.ts +0 -28
  60. package/src/types/session-recorder.ts +0 -429
  61. package/src/types/session.ts +0 -65
  62. package/src/utils/app-metadata.ts +0 -31
  63. package/src/utils/index.ts +0 -8
  64. package/src/utils/logger.ts +0 -225
  65. package/src/utils/nativeModuleTest.ts +0 -60
  66. package/src/utils/platform.ts +0 -384
  67. package/src/utils/request-utils.ts +0 -61
  68. package/src/utils/rrweb-events.ts +0 -309
  69. package/src/utils/session.ts +0 -18
  70. package/src/utils/time.ts +0 -17
  71. package/src/utils/type-utils.ts +0 -75
  72. package/src/version.ts +0 -1
  73. package/tsconfig.json +0 -24
  74. /package/ios/{SessionRecorder.m → SessionRecorderNative.m} +0 -0
  75. /package/ios/{SessionRecorder.swift → SessionRecorderNative.swift} +0 -0
@@ -1,202 +0,0 @@
1
- package com.multiplayer.sessionrecorder
2
-
3
- import android.app.Activity
4
- import android.graphics.*
5
- import android.util.Base64
6
- import android.view.View
7
- import android.widget.EditText
8
- import android.widget.TextView
9
- import com.facebook.react.bridge.*
10
- import java.io.ByteArrayOutputStream
11
-
12
- class SessionRecorderModule(reactContext: ReactApplicationContext) :
13
- ReactContextBaseJavaModule(reactContext) {
14
-
15
- override fun getName() = "SessionRecorderNative"
16
-
17
- @ReactMethod
18
- fun captureAndMask(promise: Promise) {
19
- val activity = currentActivity ?: return promise.reject("NO_ACTIVITY", "No activity found")
20
-
21
- try {
22
- val maskedImage = captureAndMaskScreen(activity, null)
23
- promise.resolve(maskedImage)
24
- } catch (e: Exception) {
25
- promise.reject("CAPTURE_FAILED", "Failed to capture and mask screen", e)
26
- }
27
- }
28
-
29
- @ReactMethod
30
- fun captureAndMaskWithOptions(options: ReadableMap, promise: Promise) {
31
- val activity = currentActivity ?: return promise.reject("NO_ACTIVITY", "No activity found")
32
-
33
- try {
34
- val maskedImage = captureAndMaskScreen(activity, options)
35
- promise.resolve(maskedImage)
36
- } catch (e: Exception) {
37
- promise.reject("CAPTURE_FAILED", "Failed to capture and mask screen", e)
38
- }
39
- }
40
-
41
- private fun captureAndMaskScreen(activity: Activity, options: ReadableMap?): String {
42
- val rootView = activity.window.decorView.rootView
43
-
44
- // Enable drawing cache
45
- rootView.isDrawingCacheEnabled = true
46
- rootView.buildDrawingCache()
47
-
48
- val bitmap = Bitmap.createBitmap(rootView.drawingCache)
49
- rootView.isDrawingCacheEnabled = false
50
-
51
- // Apply masking
52
- val maskedBitmap = applyMasking(bitmap, rootView, options)
53
-
54
- // Convert to base64
55
- val output = ByteArrayOutputStream()
56
- maskedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, output)
57
- val base64 = Base64.encodeToString(output.toByteArray(), Base64.DEFAULT)
58
-
59
- return base64
60
- }
61
-
62
- private fun applyMasking(bitmap: Bitmap, rootView: View, options: ReadableMap?): Bitmap {
63
- val maskedBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true)
64
- val canvas = Canvas(maskedBitmap)
65
-
66
- // Find sensitive elements
67
- val sensitiveElements = findSensitiveElements(rootView)
68
-
69
- for (element in sensitiveElements) {
70
- val location = IntArray(2)
71
- element.getLocationOnScreen(location)
72
-
73
- val frame = Rect(
74
- location[0],
75
- location[1],
76
- location[0] + element.width,
77
- location[1] + element.height
78
- )
79
-
80
- val maskingType = getMaskingType(element)
81
-
82
- when (maskingType) {
83
- MaskingType.BLUR -> applyBlurMask(canvas, frame)
84
- MaskingType.RECTANGLE -> applyRectangleMask(canvas, frame)
85
- MaskingType.PIXELATE -> applyPixelateMask(canvas, frame)
86
- MaskingType.NONE -> { /* No masking */ }
87
- }
88
- }
89
-
90
- return maskedBitmap
91
- }
92
-
93
- private fun findSensitiveElements(view: View): List<View> {
94
- val sensitiveElements = mutableListOf<View>()
95
-
96
- fun traverseView(currentView: View) {
97
- if (shouldMaskView(currentView)) {
98
- sensitiveElements.add(currentView)
99
- }
100
-
101
- if (currentView is ViewGroup) {
102
- for (i in 0 until currentView.childCount) {
103
- traverseView(currentView.getChildAt(i))
104
- }
105
- }
106
- }
107
-
108
- traverseView(view)
109
- return sensitiveElements
110
- }
111
-
112
- private fun shouldMaskView(view: View): Boolean {
113
- // Check for EditText - mask all text fields when inputMasking is enabled
114
- if (view is EditText) {
115
- return true
116
- }
117
-
118
- // Check for TextView - mask all text views when inputMasking is enabled
119
- if (view is TextView) {
120
- return true
121
- }
122
-
123
- return false
124
- }
125
-
126
- private fun getMaskingType(view: View): MaskingType {
127
- // Default masking type for all text inputs
128
- return MaskingType.RECTANGLE
129
- }
130
-
131
- private fun applyBlurMask(canvas: Canvas, frame: Rect) {
132
- val paint = Paint().apply {
133
- color = Color.BLACK
134
- alpha = 200 // Semi-transparent
135
- }
136
-
137
- canvas.drawRect(frame, paint)
138
-
139
- // Add some noise to make it look blurred
140
- paint.color = Color.WHITE
141
- paint.alpha = 80
142
-
143
- for (i in 0..20) {
144
- val randomX = frame.left + (Math.random() * frame.width()).toFloat()
145
- val randomY = frame.top + (Math.random() * frame.height()).toFloat()
146
- val randomSize = (Math.random() * 6 + 2).toFloat()
147
- canvas.drawCircle(randomX, randomY, randomSize, paint)
148
- }
149
- }
150
-
151
- private fun applyRectangleMask(canvas: Canvas, frame: Rect) {
152
- val paint = Paint().apply {
153
- color = Color.GRAY
154
- }
155
-
156
- canvas.drawRect(frame, paint)
157
-
158
- // Add some text-like pattern
159
- paint.color = Color.DKGRAY
160
- val lineHeight = 4f
161
- val spacing = 8f
162
-
163
- var y = frame.top + spacing
164
- while (y < frame.bottom - spacing) {
165
- val lineWidth = (Math.random() * frame.width() * 0.5 + frame.width() * 0.3).toFloat()
166
- val x = frame.left + (Math.random() * (frame.width() - lineWidth)).toFloat()
167
- canvas.drawRect(x, y, x + lineWidth, y + lineHeight, paint)
168
- y += lineHeight + spacing
169
- }
170
- }
171
-
172
- private fun applyPixelateMask(canvas: Canvas, frame: Rect) {
173
- val pixelSize = 8f
174
- val pixelCountX = (frame.width() / pixelSize).toInt()
175
- val pixelCountY = (frame.height() / pixelSize).toInt()
176
-
177
- val paint = Paint()
178
-
179
- for (x in 0 until pixelCountX) {
180
- for (y in 0 until pixelCountY) {
181
- val pixelFrame = RectF(
182
- frame.left + x * pixelSize,
183
- frame.top + y * pixelSize,
184
- frame.left + (x + 1) * pixelSize,
185
- frame.top + (y + 1) * pixelSize
186
- )
187
-
188
- // Use a random color for each pixel
189
- paint.color = Color.rgb(
190
- (Math.random() * 255).toInt(),
191
- (Math.random() * 255).toInt(),
192
- (Math.random() * 255).toInt()
193
- )
194
- canvas.drawRect(pixelFrame, paint)
195
- }
196
- }
197
- }
198
-
199
- private enum class MaskingType {
200
- BLUR, RECTANGLE, PIXELATE, NONE
201
- }
202
- }
@@ -1,16 +0,0 @@
1
- package com.multiplayer.sessionrecorder
2
-
3
- import com.facebook.react.ReactPackage
4
- import com.facebook.react.bridge.NativeModule
5
- import com.facebook.react.bridge.ReactApplicationContext
6
- import com.facebook.react.uimanager.ViewManager
7
-
8
- class SessionRecorderPackage : ReactPackage {
9
- override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
- return listOf(SessionRecorderModule(reactContext))
11
- }
12
-
13
- override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
14
- return emptyList()
15
- }
16
- }
package/babel.config.js DELETED
@@ -1,13 +0,0 @@
1
- module.exports = {
2
- presets: [
3
- '@react-native/babel-preset',
4
- '@babel/preset-env',
5
- '@babel/preset-typescript',
6
- ['@babel/preset-react', { runtime: 'automatic' }],
7
- ],
8
- plugins: [
9
- ['@babel/plugin-transform-private-property-in-object', { loose: true }],
10
- ['@babel/plugin-transform-private-methods', { loose: true }],
11
- ['@babel/plugin-transform-class-properties', { loose: true }],
12
- ],
13
- }
@@ -1,108 +0,0 @@
1
- # Automatic App Metadata Detection
2
-
3
- The session recorder automatically detects app metadata from your project configuration files **without requiring any developer intervention**.
4
-
5
- ## How It Works
6
-
7
- The library automatically extracts app information from common configuration files in this priority order:
8
-
9
- 1. **`app.json`** - Expo/React Native app configuration
10
- 2. **`app.config.js`** - Dynamic Expo configuration
11
- 3. **`package.json`** - Node.js package configuration (fallback)
12
-
13
- ## Supported Configuration Files
14
-
15
- ### app.json
16
-
17
- ```json
18
- {
19
- "name": "My Awesome App",
20
- "version": "1.2.3",
21
- "displayName": "My App",
22
- "ios": {
23
- "bundleIdentifier": "com.mycompany.myapp",
24
- "buildNumber": "123"
25
- },
26
- "android": {
27
- "package": "com.mycompany.myapp",
28
- "versionCode": 123
29
- }
30
- }
31
- ```
32
-
33
- ### app.config.js
34
-
35
- ```javascript
36
- export default {
37
- name: 'My Awesome App',
38
- version: '1.2.3',
39
- displayName: 'My App',
40
- ios: {
41
- bundleIdentifier: 'com.mycompany.myapp',
42
- buildNumber: '123'
43
- },
44
- android: {
45
- package: 'com.mycompany.myapp',
46
- versionCode: 123
47
- }
48
- }
49
- ```
50
-
51
- ### package.json (fallback)
52
-
53
- ```json
54
- {
55
- "name": "my-awesome-app",
56
- "version": "1.2.3"
57
- }
58
- ```
59
-
60
- ## Detected Metadata
61
-
62
- The following information is automatically extracted:
63
-
64
- - **App Name** - From `name` or `displayName`
65
- - **App Version** - From `version`
66
- - **Bundle ID** - From `ios.bundleIdentifier` or `android.package`
67
- - **Build Number** - From `ios.buildNumber` or `android.versionCode`
68
-
69
- ## Build Process
70
-
71
- The metadata detection happens automatically during the build process:
72
-
73
- 1. The build script scans your project root for configuration files
74
- 2. Extracts relevant metadata from the first found configuration
75
- 3. Generates a TypeScript file with the detected metadata
76
- 4. The session recorder uses this metadata in all recordings
77
-
78
- ## No Developer Action Required
79
-
80
- ✅ **Zero configuration** - Works out of the box
81
- ✅ **Automatic detection** - Scans common config files
82
- ✅ **Build-time generation** - No runtime file reading
83
- ✅ **Fallback support** - Graceful degradation
84
-
85
- ## Manual Override (Optional)
86
-
87
- If you need to override the auto-detected metadata, you can still use the manual configuration:
88
-
89
- ```typescript
90
- import { configureAppMetadata } from '@multiplayer-app/session-recorder-react-native'
91
-
92
- configureAppMetadata({
93
- name: 'Custom App Name',
94
- version: '2.0.0',
95
- bundleId: 'com.custom.app'
96
- })
97
- ```
98
-
99
- ## Priority Order
100
-
101
- The metadata detection follows this priority:
102
-
103
- 1. **Expo Config** (if using Expo)
104
- 2. **Manual Configuration** (if `configureAppMetadata` was called)
105
- 3. **Auto-detected Metadata** (from config files)
106
- 4. **Fallback Values** (defaults)
107
-
108
- This ensures maximum compatibility across different React Native setups while providing rich metadata for debugging sessions.
@@ -1,168 +0,0 @@
1
- # Troubleshooting Native Module Issues
2
-
3
- If you're seeing the warning "Screen masking native module is not available - auto-linking may still be in progress", follow these steps:
4
-
5
- ## Quick Fix
6
-
7
- 1. **Clean and Rebuild**:
8
-
9
- ```bash
10
- # Clean React Native cache
11
- npx react-native start --reset-cache
12
-
13
- # For iOS
14
- cd ios && pod install && cd ..
15
- npx react-native run-ios
16
-
17
- # For Android
18
- npx react-native run-android
19
- ```
20
-
21
- 2. **Check Auto-Linking**:
22
- ```bash
23
- # Check if auto-linking detected the module
24
- npx react-native config
25
- ```
26
-
27
- ## Detailed Troubleshooting
28
-
29
- ### 1. Verify Package Installation
30
-
31
- Make sure the package is properly installed:
32
-
33
- ```bash
34
- npm list @multiplayer-app/session-recorder-react-native
35
- ```
36
-
37
- ### 2. Check Native Module Registration
38
-
39
- The native module should be named `SessionRecorderNative`. You can verify this by checking the logs for:
40
-
41
- ```
42
- Available native modules: [..., SessionRecorderNative, ...]
43
- ```
44
-
45
- ### 3. iOS Specific Issues
46
-
47
- **Check Podfile**:
48
-
49
- ```ruby
50
- # In your ios/Podfile, you should see:
51
- pod 'SessionRecorderNative', :path => '../node_modules/@multiplayer-app/session-recorder-react-native/ios'
52
- ```
53
-
54
- **Clean iOS Build**:
55
-
56
- ```bash
57
- cd ios
58
- rm -rf build
59
- rm -rf Pods
60
- rm Podfile.lock
61
- pod install
62
- cd ..
63
- npx react-native run-ios
64
- ```
65
-
66
- ### 4. Android Specific Issues
67
-
68
- **Check MainApplication.java**:
69
-
70
- ```java
71
- // Should be automatically added by auto-linking:
72
- import com.multiplayer.sessionrecorder.SessionRecorderPackage;
73
-
74
- // In getPackages():
75
- new SessionRecorderPackage()
76
- ```
77
-
78
- **Clean Android Build**:
79
-
80
- ```bash
81
- cd android
82
- ./gradlew clean
83
- cd ..
84
- npx react-native run-android
85
- ```
86
-
87
- ### 5. Manual Linking (If Auto-Linking Fails)
88
-
89
- If auto-linking doesn't work, you can manually link:
90
-
91
- **iOS**:
92
-
93
- 1. Add to `ios/Podfile`:
94
- ```ruby
95
- pod 'SessionRecorderNative', :path => '../node_modules/@multiplayer-app/session-recorder-react-native/ios'
96
- ```
97
- 2. Run `pod install`
98
-
99
- **Android**:
100
-
101
- 1. Add to `android/settings.gradle`:
102
- ```gradle
103
- include ':react-native-session-recorder'
104
- project(':react-native-session-recorder').projectDir = new File(rootProject.projectDir, '../node_modules/@multiplayer-app/session-recorder-react-native/android')
105
- ```
106
- 2. Add to `android/app/build.gradle`:
107
- ```gradle
108
- dependencies {
109
- implementation project(':react-native-session-recorder')
110
- }
111
- ```
112
- 3. Add to `MainApplication.java`:
113
-
114
- ```java
115
- import com.multiplayer.sessionrecorder.SessionRecorderPackage;
116
-
117
- @Override
118
- protected List<ReactPackage> getPackages() {
119
- return Arrays.<ReactPackage>asList(
120
- new MainReactPackage(),
121
- new SessionRecorderPackage()
122
- );
123
- }
124
- ```
125
-
126
- ### 6. Debug Native Module Availability
127
-
128
- Add this to your app to debug:
129
-
130
- ```typescript
131
- import { testNativeModuleAvailability } from '@multiplayer-app/session-recorder-react-native/utils'
132
-
133
- // Call this in your app
134
- testNativeModuleAvailability()
135
- ```
136
-
137
- This will log detailed information about native module availability.
138
-
139
- ### 7. Common Issues
140
-
141
- **Issue**: "Module not found" error
142
- **Solution**: Ensure the package is installed and auto-linking is working
143
-
144
- **Issue**: "Method not available" error
145
- **Solution**: The native module is linked but methods aren't exposed. Check the native code.
146
-
147
- **Issue**: Auto-linking not working
148
- **Solution**: Check `react-native.config.js` and ensure React Native version is 0.60+
149
-
150
- **Issue**: Pod install fails
151
- **Solution**: Update CocoaPods: `sudo gem install cocoapods`
152
-
153
- ### 8. Verification
154
-
155
- Once fixed, you should see:
156
-
157
- ```
158
- ✅ SessionRecorderNative module is available!
159
- ✅ captureAndMask method is available!
160
- ✅ captureAndMaskWithOptions method is available!
161
- ```
162
-
163
- ## Still Having Issues?
164
-
165
- 1. Check the [React Native Auto-Linking documentation](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md)
166
- 2. Verify your React Native version (0.60+ required)
167
- 3. Check for conflicting native modules
168
- 4. Try creating a fresh React Native project and testing the module there
@@ -1,12 +0,0 @@
1
- #import <React/RCTBridgeModule.h>
2
-
3
- @interface RCT_EXTERN_MODULE(SessionRecorder, NSObject)
4
-
5
- RCT_EXTERN_METHOD(captureAndMask:(RCTPromiseResolveBlock)resolve
6
- reject:(RCTPromiseRejectBlock)reject)
7
-
8
- RCT_EXTERN_METHOD(captureAndMaskWithOptions:(NSDictionary *)options
9
- resolve:(RCTPromiseResolveBlock)resolve
10
- reject:(RCTPromiseRejectBlock)reject)
11
-
12
- @end
@@ -1,21 +0,0 @@
1
- require "json"
2
-
3
- package = JSON.parse(File.read(File.join(__dir__, "..", "package.json")))
4
-
5
- Pod::Spec.new do |s|
6
- s.name = "SessionRecorder"
7
- s.version = package["version"]
8
- s.summary = "Native session recorder module for React Native"
9
- s.description = "A native module that provides session recording with automatic masking of sensitive UI elements"
10
- s.homepage = "https://github.com/multiplayer-app/multiplayer-session-recorder-javascript"
11
- s.license = "MIT"
12
- s.authors = { "Multiplayer Software, Inc." => "https://www.multiplayer.app" }
13
- s.platforms = { :ios => "12.0" }
14
- s.source = { :git => "https://github.com/multiplayer-app/multiplayer-session-recorder-javascript.git", :tag => "#{s.version}" }
15
-
16
- s.source_files = "ios/**/*.{h,m,mm,swift}"
17
- s.requires_arc = true
18
-
19
- s.dependency "React-Core"
20
- s.dependency "React"
21
- end