@multiplayer-app/session-recorder-react-native 0.0.1-alpha.6 → 0.0.1-alpha.7
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/RRWEB_INTEGRATION.md +336 -0
- package/VIEWSHOT_INTEGRATION_TEST.md +123 -0
- package/copy-react-native-dist.sh +38 -0
- package/dist/config/constants.d.ts +0 -1
- package/dist/config/constants.js +1 -1
- package/dist/config/constants.js.map +1 -1
- package/dist/context/SessionRecorderContext.js +1 -1
- package/dist/context/SessionRecorderContext.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/otel/helpers.d.ts +4 -4
- package/dist/otel/helpers.js +1 -1
- package/dist/otel/helpers.js.map +1 -1
- package/dist/otel/index.js +1 -1
- package/dist/otel/index.js.map +1 -1
- package/dist/otel/instrumentations/index.d.ts +2 -3
- package/dist/otel/instrumentations/index.js +1 -1
- package/dist/otel/instrumentations/index.js.map +1 -1
- package/dist/otel/instrumentations/reactNavigationInstrumentation.d.ts +1 -0
- package/dist/otel/instrumentations/reactNavigationInstrumentation.js +1 -1
- package/dist/otel/instrumentations/reactNavigationInstrumentation.js.map +1 -1
- package/dist/recorder/eventExporter.d.ts +21 -0
- package/dist/recorder/eventExporter.js +1 -0
- package/dist/recorder/eventExporter.js.map +1 -0
- package/dist/recorder/gestureRecorder.d.ts +57 -3
- package/dist/recorder/gestureRecorder.js +1 -1
- package/dist/recorder/gestureRecorder.js.map +1 -1
- package/dist/recorder/index.d.ts +61 -7
- package/dist/recorder/index.js +1 -1
- package/dist/recorder/index.js.map +1 -1
- package/dist/recorder/screenRecorder.d.ts +58 -4
- package/dist/recorder/screenRecorder.js +1 -1
- package/dist/recorder/screenRecorder.js.map +1 -1
- package/dist/services/api.service.js.map +1 -1
- package/dist/services/storage.service.js.map +1 -1
- package/dist/session-recorder.d.ts +40 -2
- package/dist/session-recorder.js +1 -1
- package/dist/session-recorder.js.map +1 -1
- 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/rrweb.d.ts +108 -0
- package/dist/types/rrweb.js +1 -0
- package/dist/types/rrweb.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/example-usage.tsx +174 -0
- package/package.json +3 -2
- package/src/config/constants.ts +3 -3
- package/src/context/SessionRecorderContext.tsx +93 -16
- package/src/index.ts +1 -0
- package/src/otel/helpers.ts +37 -20
- package/src/otel/index.ts +7 -3
- package/src/otel/instrumentations/index.ts +79 -38
- package/src/otel/instrumentations/reactNavigationInstrumentation.ts +5 -0
- package/src/recorder/eventExporter.ts +138 -0
- package/src/recorder/gestureRecorder.ts +124 -3
- package/src/recorder/index.ts +130 -21
- package/src/recorder/screenRecorder.ts +203 -7
- package/src/services/api.service.ts +1 -8
- package/src/services/storage.service.ts +1 -0
- package/src/session-recorder.ts +91 -12
- package/src/types/index.ts +2 -1
- package/src/types/rrweb.ts +122 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# RRWeb Integration for React Native Session Recorder
|
|
2
|
+
|
|
3
|
+
This document explains the rrweb-compatible event generation system implemented for React Native session recording.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The React Native session recorder automatically generates rrweb-compatible events that can be replayed using standard rrweb players. **No manual intervention is required** - the system automatically captures:
|
|
8
|
+
|
|
9
|
+
1. **Screen snapshots** as `FullSnapshotEvent` with base64-encoded images (periodic + on interaction)
|
|
10
|
+
2. **Touch interactions** as `IncrementalSnapshotEvent` with `MouseInteraction` data (automatic)
|
|
11
|
+
3. **Navigation events** and other user interactions (automatic)
|
|
12
|
+
|
|
13
|
+
**Recording starts automatically when you call `sessionRecorder.start()` and stops when you call `sessionRecorder.stop()`.**
|
|
14
|
+
|
|
15
|
+
### 🚀 **Smart Change Detection**
|
|
16
|
+
|
|
17
|
+
The system now includes **intelligent change detection** that prevents duplicate events:
|
|
18
|
+
|
|
19
|
+
- **Automatic Comparison**: Each screen capture is compared with the previous one
|
|
20
|
+
- **Hash-Based Detection**: Uses lightweight hashing to detect changes quickly
|
|
21
|
+
- **Skip Unchanged Screens**: Only sends events when the screen actually changes
|
|
22
|
+
- **Touch-Triggered Capture**: Forces capture after touch interactions regardless of change detection
|
|
23
|
+
|
|
24
|
+
## Architecture
|
|
25
|
+
|
|
26
|
+
### Core Components
|
|
27
|
+
|
|
28
|
+
1. **RRWeb Types** (`src/types/rrweb.ts`)
|
|
29
|
+
|
|
30
|
+
- Complete TypeScript definitions for rrweb events
|
|
31
|
+
- React Native specific types for screen and touch data
|
|
32
|
+
|
|
33
|
+
2. **SessionRecorder** (`src/session-recorder.ts`)
|
|
34
|
+
|
|
35
|
+
- Main entry point with `recordEvent()` method for custom events
|
|
36
|
+
- Automatic touch recording (internal methods)
|
|
37
|
+
- Automatic screen capture coordination
|
|
38
|
+
|
|
39
|
+
3. **ScreenRecorder** (`src/recorder/screenRecorder.ts`)
|
|
40
|
+
|
|
41
|
+
- Captures screenshots and converts to `FullSnapshotEvent`
|
|
42
|
+
- Creates virtual DOM with `<img>` elements containing base64 screenshots
|
|
43
|
+
|
|
44
|
+
4. **GestureRecorder** (`src/recorder/gestureRecorder.ts`)
|
|
45
|
+
|
|
46
|
+
- Automatically converts touch events to rrweb `MouseInteraction` events
|
|
47
|
+
- Maps React Native coordinates to rrweb format
|
|
48
|
+
- Sets up automatic touch capture on session start
|
|
49
|
+
|
|
50
|
+
5. **TouchEventCapture** (`src/context/SessionRecorderContext.tsx`)
|
|
51
|
+
- React component that automatically captures touch events
|
|
52
|
+
- No manual setup required - works automatically when session is active
|
|
53
|
+
|
|
54
|
+
## Event Types Generated
|
|
55
|
+
|
|
56
|
+
### FullSnapshotEvent
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
{
|
|
60
|
+
type: EventType.FullSnapshot,
|
|
61
|
+
data: {
|
|
62
|
+
node: {
|
|
63
|
+
type: 1, // Element node
|
|
64
|
+
id: 1,
|
|
65
|
+
tagName: 'div',
|
|
66
|
+
attributes: { style: 'width: 375px; height: 667px; position: relative;' },
|
|
67
|
+
childNodes: [{
|
|
68
|
+
type: 1,
|
|
69
|
+
id: 2,
|
|
70
|
+
tagName: 'img',
|
|
71
|
+
attributes: {
|
|
72
|
+
src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...',
|
|
73
|
+
width: '375',
|
|
74
|
+
height: '667',
|
|
75
|
+
style: 'width: 375px; height: 667px;'
|
|
76
|
+
}
|
|
77
|
+
}]
|
|
78
|
+
},
|
|
79
|
+
initialOffset: { left: 0, top: 0 }
|
|
80
|
+
},
|
|
81
|
+
timestamp: 1640995200000
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### IncrementalSnapshotEvent (Touch Interactions)
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
{
|
|
89
|
+
type: EventType.IncrementalSnapshot,
|
|
90
|
+
data: {
|
|
91
|
+
source: IncrementalSource.MouseInteraction,
|
|
92
|
+
type: MouseInteractionType.TouchStart, // or TouchMove, TouchEnd
|
|
93
|
+
id: 2, // References the image node ID
|
|
94
|
+
x: 150,
|
|
95
|
+
y: 200
|
|
96
|
+
},
|
|
97
|
+
timestamp: 1640995201000
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Usage
|
|
102
|
+
|
|
103
|
+
### Basic Setup (Automatic Recording)
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { SessionRecorderProvider } from '@multiplayer-app/session-recorder-react-native'
|
|
107
|
+
|
|
108
|
+
function App() {
|
|
109
|
+
return (
|
|
110
|
+
<SessionRecorderProvider
|
|
111
|
+
options={{
|
|
112
|
+
apiKey: 'your-api-key',
|
|
113
|
+
version: '1.0.0',
|
|
114
|
+
application: 'MyApp',
|
|
115
|
+
environment: 'production',
|
|
116
|
+
recordScreen: true, // Automatic screen capture
|
|
117
|
+
recordGestures: true // Automatic touch recording
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
<YourAppContent />
|
|
121
|
+
</SessionRecorderProvider>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**That's it!** Recording is now automatic. When you start a session, the system will:
|
|
127
|
+
|
|
128
|
+
- Automatically capture screen snapshots periodically
|
|
129
|
+
- Automatically record all touch interactions
|
|
130
|
+
- Generate rrweb-compatible events without any manual intervention
|
|
131
|
+
|
|
132
|
+
### Custom Event Recording (Optional)
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { useSessionRecorder } from '@multiplayer-app/session-recorder-react-native'
|
|
136
|
+
|
|
137
|
+
function MyComponent() {
|
|
138
|
+
const { client } = useSessionRecorder()
|
|
139
|
+
|
|
140
|
+
const handleCustomEvent = () => {
|
|
141
|
+
// Record a custom rrweb event (optional)
|
|
142
|
+
client.recordEvent({
|
|
143
|
+
type: EventType.Custom,
|
|
144
|
+
data: { customData: 'value' },
|
|
145
|
+
timestamp: Date.now()
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return <Button onPress={handleCustomEvent} title='Record Custom Event' />
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Integration Points
|
|
154
|
+
|
|
155
|
+
### Screen Capture Integration
|
|
156
|
+
|
|
157
|
+
The `ScreenRecorder` class now includes **complete react-native-view-shot integration**:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
// In screenRecorder.ts - _captureScreenBase64 method
|
|
161
|
+
private async _captureScreenBase64(): Promise<string | null> {
|
|
162
|
+
try {
|
|
163
|
+
if (!this.viewShotRef) {
|
|
164
|
+
console.warn('ViewShot ref not available for screen capture')
|
|
165
|
+
return null
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Capture the screen using react-native-view-shot
|
|
169
|
+
const result = await captureRef(this.viewShotRef, {
|
|
170
|
+
format: this.captureFormat,
|
|
171
|
+
quality: this.captureQuality,
|
|
172
|
+
result: 'base64'
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
return result
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error('Failed to capture screen:', error)
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**The ViewShot ref is automatically set up** in the `TouchEventCapture` component, so no manual configuration is needed!
|
|
184
|
+
|
|
185
|
+
### Automatic ViewShot Setup
|
|
186
|
+
|
|
187
|
+
The `TouchEventCapture` component automatically sets up the ViewShot ref:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// In SessionRecorderContext.tsx
|
|
191
|
+
const TouchEventCapture: React.FC<{ children: ReactNode }> = ({ children }) => {
|
|
192
|
+
const context = useContext(SessionRecorderContext)
|
|
193
|
+
const viewShotRef = useRef<View>(null)
|
|
194
|
+
|
|
195
|
+
// Callback ref to set the viewshot ref immediately when available
|
|
196
|
+
const setViewShotRef = (ref: View | null) => {
|
|
197
|
+
if (ref && context?.client) {
|
|
198
|
+
context.client.setViewShotRef?.(ref)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<View
|
|
204
|
+
ref={(ref) => {
|
|
205
|
+
viewShotRef.current = ref
|
|
206
|
+
setViewShotRef(ref)
|
|
207
|
+
}}
|
|
208
|
+
style={{ flex: 1 }}
|
|
209
|
+
onTouchStart={handleTouchStart}
|
|
210
|
+
onTouchMove={handleTouchMove}
|
|
211
|
+
onTouchEnd={handleTouchEnd}
|
|
212
|
+
>
|
|
213
|
+
{children}
|
|
214
|
+
</View>
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
This ensures that:
|
|
220
|
+
|
|
221
|
+
- The same View that captures touch events is used for screen capture
|
|
222
|
+
- Perfect synchronization between touch interactions and screen captures
|
|
223
|
+
- No manual ref setup required
|
|
224
|
+
|
|
225
|
+
### Required Dependencies
|
|
226
|
+
|
|
227
|
+
Add these to your `package.json`:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"dependencies": {
|
|
232
|
+
"react-native-view-shot": "^3.8.0"
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Automatic Event Flow
|
|
238
|
+
|
|
239
|
+
1. **Session Start**: `SessionRecorder.start()` automatically initializes all recorders
|
|
240
|
+
2. **Automatic Screen Capture**: `ScreenRecorder` automatically captures screenshots periodically + on interactions
|
|
241
|
+
3. **Automatic Touch Events**: `TouchEventCapture` automatically captures all touch interactions
|
|
242
|
+
4. **Automatic Event Generation**: Touch events are automatically converted to rrweb `MouseInteraction` events
|
|
243
|
+
5. **Automatic Event Recording**: All events are automatically stored and can be exported
|
|
244
|
+
|
|
245
|
+
**No manual intervention required!** The entire process is automatic once you start a session.
|
|
246
|
+
|
|
247
|
+
## Customization
|
|
248
|
+
|
|
249
|
+
### Screen Capture Frequency
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// Adjust capture interval in ScreenRecorder
|
|
253
|
+
screenRecorder.setCaptureInterval(3000) // Capture every 3 seconds
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Change Detection Configuration
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
// Enable/disable change detection
|
|
260
|
+
screenRecorder.setChangeDetection(true) // Default: true
|
|
261
|
+
|
|
262
|
+
// Adjust hash sample size for change detection
|
|
263
|
+
screenRecorder.setHashSampleSize(200) // Default: 100 characters
|
|
264
|
+
|
|
265
|
+
// Force capture (bypasses change detection)
|
|
266
|
+
screenRecorder.forceCapture()
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Touch Event Throttling
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
// Adjust gesture throttling in GestureRecorder
|
|
273
|
+
gestureRecorder.setGestureThrottle(100) // Throttle to 100ms
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Event Filtering
|
|
277
|
+
|
|
278
|
+
You can filter events by modifying the `recordEvent` method in `SessionRecorder`:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
recordEvent(event: RRWebEvent): void {
|
|
282
|
+
if (!this._isInitialized || this.sessionState !== SessionState.started) {
|
|
283
|
+
return
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Add custom filtering logic here
|
|
287
|
+
if (event.type === EventType.IncrementalSnapshot) {
|
|
288
|
+
// Filter out certain touch events if needed
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
this._recorder.recordEvent(event)
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Exporting Events
|
|
296
|
+
|
|
297
|
+
To export recorded events for rrweb playback:
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
const events = sessionRecorder.getRecordedEvents()
|
|
301
|
+
// Save events to file or send to server
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Troubleshooting
|
|
305
|
+
|
|
306
|
+
### Common Issues
|
|
307
|
+
|
|
308
|
+
1. **Screen capture not working**: Ensure `react-native-view-shot` is properly installed and configured
|
|
309
|
+
2. **Touch events not recorded**: Check that `TouchEventCapture` wraps your app content
|
|
310
|
+
3. **Events not generated**: Verify session is in `started` state
|
|
311
|
+
|
|
312
|
+
### Debug Mode
|
|
313
|
+
|
|
314
|
+
Enable debug logging by setting:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
// In your app configuration
|
|
318
|
+
console.log('Recording stats:', sessionRecorder.getRecordingStats())
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Future Enhancements
|
|
322
|
+
|
|
323
|
+
- [ ] Support for multiple screen orientations
|
|
324
|
+
- [ ] Gesture recognition and classification
|
|
325
|
+
- [ ] Performance optimization for large sessions
|
|
326
|
+
- [ ] Integration with additional React Native libraries
|
|
327
|
+
- [ ] Custom event types for React Native specific interactions
|
|
328
|
+
|
|
329
|
+
## Compatibility
|
|
330
|
+
|
|
331
|
+
This implementation is compatible with:
|
|
332
|
+
|
|
333
|
+
- rrweb 1.x and 2.x
|
|
334
|
+
- React Native 0.60+
|
|
335
|
+
- iOS and Android platforms
|
|
336
|
+
- Standard rrweb players and replay tools
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# ViewShot Integration Test
|
|
2
|
+
|
|
3
|
+
This document explains how to test the complete react-native-view-shot integration.
|
|
4
|
+
|
|
5
|
+
## Test Setup
|
|
6
|
+
|
|
7
|
+
1. **Install Dependencies**
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install react-native-view-shot
|
|
11
|
+
# For iOS
|
|
12
|
+
cd ios && pod install && cd ..
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
2. **Test the Integration**
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import React from 'react'
|
|
19
|
+
import { View, Text, Button } from 'react-native'
|
|
20
|
+
import { SessionRecorderProvider, useSessionRecorder } from './src/context/SessionRecorderContext'
|
|
21
|
+
|
|
22
|
+
function TestApp() {
|
|
23
|
+
const { client } = useSessionRecorder()
|
|
24
|
+
|
|
25
|
+
const testScreenCapture = async () => {
|
|
26
|
+
// Start session to enable automatic screen capture
|
|
27
|
+
await client.start()
|
|
28
|
+
|
|
29
|
+
// Wait a moment for initial screen capture
|
|
30
|
+
setTimeout(() => {
|
|
31
|
+
console.log('Screen capture should have happened automatically!')
|
|
32
|
+
// Check the recorded events
|
|
33
|
+
const events = client.getRecordedEvents?.()
|
|
34
|
+
console.log('Recorded events:', events)
|
|
35
|
+
}, 2000)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<View style={{ flex: 1, padding: 20 }}>
|
|
40
|
+
<Text>ViewShot Integration Test</Text>
|
|
41
|
+
<Button title='Test Screen Capture' onPress={testScreenCapture} />
|
|
42
|
+
<Text>Touch the screen to test touch recording</Text>
|
|
43
|
+
</View>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default function App() {
|
|
48
|
+
return (
|
|
49
|
+
<SessionRecorderProvider
|
|
50
|
+
options={{
|
|
51
|
+
apiKey: 'test-key',
|
|
52
|
+
version: '1.0.0',
|
|
53
|
+
application: 'TestApp',
|
|
54
|
+
environment: 'test',
|
|
55
|
+
recordScreen: true,
|
|
56
|
+
recordGestures: true
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
<TestApp />
|
|
60
|
+
</SessionRecorderProvider>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Expected Behavior
|
|
66
|
+
|
|
67
|
+
When you run this test:
|
|
68
|
+
|
|
69
|
+
1. **Automatic Screen Capture**: The system should automatically capture the screen when the session starts
|
|
70
|
+
2. **Touch Recording**: Touching the screen should generate rrweb MouseInteraction events
|
|
71
|
+
3. **ViewShot Integration**: The same View that handles touch events should be used for screen capture
|
|
72
|
+
4. **Event Generation**: Both FullSnapshotEvent and IncrementalSnapshotEvent should be generated
|
|
73
|
+
|
|
74
|
+
## Verification Steps
|
|
75
|
+
|
|
76
|
+
1. **Check Console Logs**: Look for screen capture success/failure messages
|
|
77
|
+
2. **Verify Events**: Check that `getRecordedEvents()` returns both screen and touch events
|
|
78
|
+
3. **Test Touch Interactions**: Touch the screen and verify touch events are recorded
|
|
79
|
+
4. **Check Event Format**: Ensure events are in proper rrweb format
|
|
80
|
+
|
|
81
|
+
## Troubleshooting
|
|
82
|
+
|
|
83
|
+
### Common Issues
|
|
84
|
+
|
|
85
|
+
1. **"ViewShot ref not available"**: The ref setup might be timing-related
|
|
86
|
+
|
|
87
|
+
- Solution: Ensure the SessionRecorderProvider wraps your app properly
|
|
88
|
+
|
|
89
|
+
2. **Screen capture fails**: react-native-view-shot might not be properly installed
|
|
90
|
+
|
|
91
|
+
- Solution: Check installation and run `pod install` for iOS
|
|
92
|
+
|
|
93
|
+
3. **No events recorded**: Session might not be started
|
|
94
|
+
- Solution: Call `client.start()` before testing
|
|
95
|
+
|
|
96
|
+
### Debug Information
|
|
97
|
+
|
|
98
|
+
Add this to your test component to debug:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const debugInfo = () => {
|
|
102
|
+
console.log('Session State:', client.sessionState)
|
|
103
|
+
console.log('Is Recording:', client.isRecording)
|
|
104
|
+
console.log('Events Count:', client.getRecordedEvents?.()?.length || 0)
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Success Criteria
|
|
109
|
+
|
|
110
|
+
✅ Screen capture works automatically when session starts
|
|
111
|
+
✅ Touch events are recorded automatically
|
|
112
|
+
✅ Events are in proper rrweb format
|
|
113
|
+
✅ No manual ref setup required
|
|
114
|
+
✅ Perfect synchronization between touch and screen events
|
|
115
|
+
|
|
116
|
+
## Next Steps
|
|
117
|
+
|
|
118
|
+
Once the test passes:
|
|
119
|
+
|
|
120
|
+
1. Integrate into your main app
|
|
121
|
+
2. Configure capture intervals and quality as needed
|
|
122
|
+
3. Export events for rrweb playback
|
|
123
|
+
4. Customize event filtering if required
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Exit on any error
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
# Configuration
|
|
7
|
+
SOURCE_DIR="./dist"
|
|
8
|
+
TARGET_DIR="../../../sample-expo-app/node_modules/@multiplayer-app/session-recorder-react-native"
|
|
9
|
+
|
|
10
|
+
echo "📱 Copying React Native dist to sample Expo app..."
|
|
11
|
+
|
|
12
|
+
# Check if source directory exists
|
|
13
|
+
if [ ! -d "$SOURCE_DIR" ]; then
|
|
14
|
+
echo "❌ Source directory does not exist: $SOURCE_DIR"
|
|
15
|
+
echo "💡 Please run 'npm run build' in the session-recorder-react-native package first"
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Check if target directory exists
|
|
20
|
+
if [ ! -d "$TARGET_DIR" ]; then
|
|
21
|
+
echo "❌ Target directory does not exist: $TARGET_DIR"
|
|
22
|
+
echo "💡 Please make sure the sample-expo-app is set up and has the package installed"
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Create backup of existing dist if it exists
|
|
27
|
+
if [ -d "$TARGET_DIR/dist" ]; then
|
|
28
|
+
echo "📦 Creating backup of existing dist..."
|
|
29
|
+
cp -r "$TARGET_DIR/dist" "$TARGET_DIR/dist.backup.$(date +%Y%m%d_%H%M%S)"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Copy the dist folder
|
|
33
|
+
echo "🔄 Copying dist folder..."
|
|
34
|
+
cp -r "$SOURCE_DIR" "$TARGET_DIR/"
|
|
35
|
+
|
|
36
|
+
echo "✅ Successfully copied dist folder to sample Expo app!"
|
|
37
|
+
echo "📍 Source: $SOURCE_DIR"
|
|
38
|
+
echo "📍 Target: $TARGET_DIR/dist"
|
|
@@ -15,5 +15,4 @@ export declare const DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE = 100000;
|
|
|
15
15
|
export declare const SESSION_RESPONSE = "multiplayer-debug-session-response";
|
|
16
16
|
export declare const CONTINUOUS_DEBUGGING_TIMEOUT = 60000;
|
|
17
17
|
export declare const DEBUG_SESSION_MAX_DURATION_SECONDS: number;
|
|
18
|
-
export declare const PACKAGE_VERSION_EXPORT: string;
|
|
19
18
|
export declare const OTEL_IGNORE_URLS: RegExp[];
|
package/dist/config/constants.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.SESSION_UNSUBSCRIBE_EVENT=exports.SESSION_TYPE_PROP_NAME=exports.SESSION_SUBSCRIBE_EVENT=exports.SESSION_STOPPED_EVENT=exports.SESSION_STATE_PROP_NAME=exports.SESSION_STARTED_EVENT=exports.SESSION_SHORT_ID_PROP_NAME=exports.SESSION_RESPONSE=exports.SESSION_PROP_NAME=exports.SESSION_ID_PROP_NAME=exports.SESSION_CONTINUOUS_DEBUGGING_PROP_NAME=exports.SESSION_AUTO_CREATED=exports.SESSION_ADD_EVENT=exports.
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.SESSION_UNSUBSCRIBE_EVENT=exports.SESSION_TYPE_PROP_NAME=exports.SESSION_SUBSCRIBE_EVENT=exports.SESSION_STOPPED_EVENT=exports.SESSION_STATE_PROP_NAME=exports.SESSION_STARTED_EVENT=exports.SESSION_SHORT_ID_PROP_NAME=exports.SESSION_RESPONSE=exports.SESSION_PROP_NAME=exports.SESSION_ID_PROP_NAME=exports.SESSION_CONTINUOUS_DEBUGGING_PROP_NAME=exports.SESSION_AUTO_CREATED=exports.SESSION_ADD_EVENT=exports.OTEL_MP_SAMPLE_TRACE_RATIO=exports.OTEL_IGNORE_URLS=exports.DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE=exports.DEBUG_SESSION_MAX_DURATION_SECONDS=exports.CONTINUOUS_DEBUGGING_TIMEOUT=void 0;var OTEL_MP_SAMPLE_TRACE_RATIO=exports.OTEL_MP_SAMPLE_TRACE_RATIO=0.15;var SESSION_ID_PROP_NAME=exports.SESSION_ID_PROP_NAME='multiplayer-session-id';var SESSION_SHORT_ID_PROP_NAME=exports.SESSION_SHORT_ID_PROP_NAME='multiplayer-session-short-id';var SESSION_CONTINUOUS_DEBUGGING_PROP_NAME=exports.SESSION_CONTINUOUS_DEBUGGING_PROP_NAME='multiplayer-session-continuous-debugging';var SESSION_STATE_PROP_NAME=exports.SESSION_STATE_PROP_NAME='multiplayer-session-state';var SESSION_TYPE_PROP_NAME=exports.SESSION_TYPE_PROP_NAME='multiplayer-session-type';var SESSION_PROP_NAME=exports.SESSION_PROP_NAME='multiplayer-session-data';var SESSION_STARTED_EVENT=exports.SESSION_STARTED_EVENT='debug-session:started';var SESSION_STOPPED_EVENT=exports.SESSION_STOPPED_EVENT='debug-session:stopped';var SESSION_SUBSCRIBE_EVENT=exports.SESSION_SUBSCRIBE_EVENT='debug-session:subscribe';var SESSION_UNSUBSCRIBE_EVENT=exports.SESSION_UNSUBSCRIBE_EVENT='debug-session:unsubscribe';var SESSION_AUTO_CREATED=exports.SESSION_AUTO_CREATED='debug-session:auto-created';var SESSION_ADD_EVENT=exports.SESSION_ADD_EVENT='debug-session:rrweb:add-event';var DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE=exports.DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE=100000;var SESSION_RESPONSE=exports.SESSION_RESPONSE='multiplayer-debug-session-response';var CONTINUOUS_DEBUGGING_TIMEOUT=exports.CONTINUOUS_DEBUGGING_TIMEOUT=60000;var DEBUG_SESSION_MAX_DURATION_SECONDS=exports.DEBUG_SESSION_MAX_DURATION_SECONDS=10*60+30;var OTEL_IGNORE_URLS=exports.OTEL_IGNORE_URLS=[/.*\/v1\/traces/,/.*\/v0\/radar\/debug-sessions\/start$/,/.*\/v0\/radar\/debug-sessions\/[^/]+\/stop$/,/.*\/v0\/radar\/debug-sessions\/[^/]+\/cancel$/,/.*\/v0\/radar\/continuous-debug-sessions\/start$/,/.*\/v0\/radar\/continuous-debug-sessions\/[^/]+\/save$/,/.*\/v0\/radar\/continuous-debug-sessions\/[^/]+\/cancel$/,/.*\/v0\/radar\/remote-debug-session\/check$/];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAA;AAE9C,MAAM,CAAC,MAAM,oBAAoB,GAAG,wBAAwB,CAAA;AAE5D,MAAM,CAAC,MAAM,0BAA0B,GAAG,8BAA8B,CAAA;AAExE,MAAM,CAAC,MAAM,sCAAsC,GAAG,0CAA0C,CAAA;AAEhG,MAAM,CAAC,MAAM,uBAAuB,GAAG,2BAA2B,CAAA;AAElE,MAAM,CAAC,MAAM,sBAAsB,GAAG,0BAA0B,CAAA;AAEhE,MAAM,CAAC,MAAM,iBAAiB,GAAG,0BAA0B,CAAA;AAE3D,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAA;AAE5D,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAA;AAE5D,MAAM,CAAC,MAAM,uBAAuB,GAAG,yBAAyB,CAAA;AAEhE,MAAM,CAAC,MAAM,yBAAyB,GAAG,2BAA2B,CAAA;AAEpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,4BAA4B,CAAA;AAEhE,MAAM,CAAC,MAAM,iBAAiB,GAAG,+BAA+B,CAAA;AAEhE,MAAM,CAAC,MAAM,uCAAuC,GAAG,MAAM,CAAA;AAE7D,MAAM,CAAC,MAAM,gBAAgB,GAAG,oCAAoC,CAAA;AAEpE,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAA,CAAC,YAAY;AAE9D,MAAM,CAAC,MAAM,kCAAkC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA,CAAC,wCAAwC;
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAA;AAE9C,MAAM,CAAC,MAAM,oBAAoB,GAAG,wBAAwB,CAAA;AAE5D,MAAM,CAAC,MAAM,0BAA0B,GAAG,8BAA8B,CAAA;AAExE,MAAM,CAAC,MAAM,sCAAsC,GAAG,0CAA0C,CAAA;AAEhG,MAAM,CAAC,MAAM,uBAAuB,GAAG,2BAA2B,CAAA;AAElE,MAAM,CAAC,MAAM,sBAAsB,GAAG,0BAA0B,CAAA;AAEhE,MAAM,CAAC,MAAM,iBAAiB,GAAG,0BAA0B,CAAA;AAE3D,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAA;AAE5D,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAuB,CAAA;AAE5D,MAAM,CAAC,MAAM,uBAAuB,GAAG,yBAAyB,CAAA;AAEhE,MAAM,CAAC,MAAM,yBAAyB,GAAG,2BAA2B,CAAA;AAEpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,4BAA4B,CAAA;AAEhE,MAAM,CAAC,MAAM,iBAAiB,GAAG,+BAA+B,CAAA;AAEhE,MAAM,CAAC,MAAM,uCAAuC,GAAG,MAAM,CAAA;AAE7D,MAAM,CAAC,MAAM,gBAAgB,GAAG,oCAAoC,CAAA;AAEpE,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAA,CAAC,YAAY;AAE9D,MAAM,CAAC,MAAM,kCAAkC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA,CAAC,wCAAwC;AAEvG,wDAAwD;AACxD,wCAAwC;AACxC,mEAAmE;AAGnE,+CAA+C;AAC/C,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,kBAAkB;IAClB,gBAAgB;IAChB,2BAA2B;IAC3B,uCAAuC;IACvC,6CAA6C;IAC7C,+CAA+C;IAE/C,sCAAsC;IACtC,kDAAkD;IAClD,wDAAwD;IACxD,0DAA0D;IAE1D,gCAAgC;IAChC,6CAA6C;IAE7C,iEAAiE;IACjE,sBAAsB;CACvB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");var _typeof=require("@babel/runtime/helpers/typeof");Object.defineProperty(exports,"__esModule",{value:true});exports.useSessionRecorder=exports.SessionRecorderProvider=void 0;var _react=_interopRequireWildcard(require("react"));var _reactNative=require("react-native");var _sessionRecorder=_interopRequireDefault(require("../session-recorder"));var _jsxRuntime=require("react/jsx-runtime");var _this=void 0,_jsxFileName="/Users/gegham/www/multiplayer-debugger-javascript/packages/session-recorder-react-native/dist/context/SessionRecorderContext.js";function _interopRequireWildcard(e,t){if("function"==typeof WeakMap)var r=new WeakMap(),n=new WeakMap();return(_interopRequireWildcard=function _interopRequireWildcard(e,t){if(!t&&e&&e.__esModule)return e;var o,i,f={__proto__:null,"default":e};if(null===e||"object"!=_typeof(e)&&"function"!=typeof e)return f;if(o=t?n:r){if(o.has(e))return o.get(e);o.set(e,f);}for(var _t2 in e)"default"!==_t2&&{}.hasOwnProperty.call(e,_t2)&&((i=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,_t2))&&(i.get||i.set)?o(f,_t2,i):f[_t2]=e[_t2]);return f;})(e,t);}var SessionRecorderContext=(0,_react.createContext)(null);var SessionRecorderProvider=exports.SessionRecorderProvider=function SessionRecorderProvider(_ref){var children=_ref.children,client=_ref.client,options=_ref.options;var sessionRecorder=(0,_react.useMemo)(function(){if(client)return client;_sessionRecorder["default"].init(options);return _sessionRecorder["default"];},[]);return(0,_jsxRuntime.jsx)(SessionRecorderContext.Provider,{value:{client:sessionRecorder},children:(0,_jsxRuntime.
|
|
1
|
+
"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");var _typeof=require("@babel/runtime/helpers/typeof");Object.defineProperty(exports,"__esModule",{value:true});exports.useSessionRecorder=exports.SessionRecorderProvider=void 0;var _slicedToArray2=_interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));var _react=_interopRequireWildcard(require("react"));var _reactNative=require("react-native");var _types=require("../types");var _sessionRecorder=_interopRequireDefault(require("../session-recorder"));var _jsxRuntime=require("react/jsx-runtime");var _this=void 0,_jsxFileName="/Users/gegham/www/multiplayer-debugger-javascript/packages/session-recorder-react-native/dist/context/SessionRecorderContext.js";function _interopRequireWildcard(e,t){if("function"==typeof WeakMap)var r=new WeakMap(),n=new WeakMap();return(_interopRequireWildcard=function _interopRequireWildcard(e,t){if(!t&&e&&e.__esModule)return e;var o,i,f={__proto__:null,"default":e};if(null===e||"object"!=_typeof(e)&&"function"!=typeof e)return f;if(o=t?n:r){if(o.has(e))return o.get(e);o.set(e,f);}for(var _t2 in e)"default"!==_t2&&{}.hasOwnProperty.call(e,_t2)&&((i=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,_t2))&&(i.get||i.set)?o(f,_t2,i):f[_t2]=e[_t2]);return f;})(e,t);}var SessionRecorderContext=(0,_react.createContext)(null);var SessionRecorderProvider=exports.SessionRecorderProvider=function SessionRecorderProvider(_ref){var children=_ref.children,client=_ref.client,options=_ref.options;var _useState=(0,_react.useState)(null),_useState2=(0,_slicedToArray2["default"])(_useState,2),sessionState=_useState2[0],setSessionState=_useState2[1];var sessionRecorder=(0,_react.useMemo)(function(){if(client)return client;_sessionRecorder["default"].init(options);return _sessionRecorder["default"];},[]);(0,_react.useEffect)(function(){if(!sessionRecorder)return;setSessionState(sessionRecorder.sessionState);},[sessionRecorder]);var onToggleSession=function onToggleSession(){if(sessionState===_types.SessionState.started){setSessionState(_types.SessionState.stopped);sessionRecorder.stop();}else{setSessionState(_types.SessionState.started);sessionRecorder.start();}};return(0,_jsxRuntime.jsx)(SessionRecorderContext.Provider,{value:{client:sessionRecorder},children:(0,_jsxRuntime.jsxs)(TouchEventCapture,{children:[children,(0,_jsxRuntime.jsx)(_reactNative.Pressable,{onPress:onToggleSession,children:(0,_jsxRuntime.jsx)(_reactNative.View,{style:{position:'absolute',right:0,bottom:100,width:48,height:48,paddingTop:16,paddingLeft:10,backgroundColor:'red',borderTopLeftRadius:24,borderBottomLeftRadius:24},children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:{color:'white'},children:sessionState===_types.SessionState.started?'Stop':'Start'})})})]})});};var TouchEventCapture=function TouchEventCapture(_ref2){var children=_ref2.children;var context=(0,_react.useContext)(SessionRecorderContext);var viewShotRef=(0,_react.useRef)(null);(0,_react.useEffect)(function(){var _a,_b;if((context===null||context===void 0?void 0:context.client)&&viewShotRef.current){(_b=(_a=context.client).setViewShotRef)===null||_b===void 0?void 0:_b.call(_a,viewShotRef.current);}},[context===null||context===void 0?void 0:context.client]);var setViewShotRef=function setViewShotRef(ref){var _a,_b;if(ref&&(context===null||context===void 0?void 0:context.client)){(_b=(_a=context.client).setViewShotRef)===null||_b===void 0?void 0:_b.call(_a,ref);}};var handleTouchStart=function handleTouchStart(event){var _a,_b;if(!(context===null||context===void 0?void 0:context.client)||context.client.sessionState!==_types.SessionState.started)return;try{var _event$nativeEvent=event.nativeEvent,pageX=_event$nativeEvent.pageX,pageY=_event$nativeEvent.pageY,target=_event$nativeEvent.target;var pressure=event.nativeEvent.force||1.0;(_b=(_a=context.client).recordTouchStart)===null||_b===void 0?void 0:_b.call(_a,pageX,pageY,target===null||target===void 0?void 0:target.toString(),pressure);}catch(error){console.warn('Failed to record touch start event:',error);}};var handleTouchMove=function handleTouchMove(event){var _a,_b;if(!(context===null||context===void 0?void 0:context.client)||context.client.sessionState!==_types.SessionState.started)return;try{var _event$nativeEvent2=event.nativeEvent,pageX=_event$nativeEvent2.pageX,pageY=_event$nativeEvent2.pageY,target=_event$nativeEvent2.target;var pressure=event.nativeEvent.force||1.0;(_b=(_a=context.client).recordTouchMove)===null||_b===void 0?void 0:_b.call(_a,pageX,pageY,target===null||target===void 0?void 0:target.toString(),pressure);}catch(error){console.warn('Failed to record touch move event:',error);}};var handleTouchEnd=function handleTouchEnd(event){var _a,_b;if(!(context===null||context===void 0?void 0:context.client)||context.client.sessionState!==_types.SessionState.started)return;try{var _event$nativeEvent3=event.nativeEvent,pageX=_event$nativeEvent3.pageX,pageY=_event$nativeEvent3.pageY,target=_event$nativeEvent3.target;var pressure=event.nativeEvent.force||1.0;(_b=(_a=context.client).recordTouchEnd)===null||_b===void 0?void 0:_b.call(_a,pageX,pageY,target===null||target===void 0?void 0:target.toString(),pressure);}catch(error){console.warn('Failed to record touch end event:',error);}};return(0,_jsxRuntime.jsx)(_reactNative.View,{ref:setViewShotRef,style:{flex:1},onTouchStart:handleTouchStart,onTouchMove:handleTouchMove,onTouchEnd:handleTouchEnd,children:children});};var useSessionRecorder=exports.useSessionRecorder=function useSessionRecorder(){var context=(0,_react.useContext)(SessionRecorderContext);if(!context){throw new Error('useSessionRecorder must be used within a SessionRecorderProvider');}return context;};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionRecorderContext.js","sourceRoot":"","sources":["../../src/context/SessionRecorderContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,aAAa,EAAE,UAAU,EAAgC,OAAO,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"SessionRecorderContext.js","sourceRoot":"","sources":["../../src/context/SessionRecorderContext.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,CAAA;AAChB,OAAO,eAAe,MAAM,oBAAoB,CAAA;AAChD,cAAc,0CAA0C,CAAA;AACxD,cAAc,kCAAkC,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,CAAA;AAChB,OAAO,eAAe,MAAM,oBAAoB,CAAA;AAChD,cAAc,0CAA0C,CAAA;AACxD,cAAc,kCAAkC,CAAA;AAGhD,OAAO,EAAE,eAAe,EAAE,CAAA;AAC1B,iCAAiC;AACjC,eAAe,eAAe,CAAA"}
|
package/dist/otel/helpers.d.ts
CHANGED
|
@@ -35,11 +35,11 @@ export declare function processHeaders(payload: HttpPayloadData, config: TracerR
|
|
|
35
35
|
*/
|
|
36
36
|
export declare function processHttpPayload(payload: HttpPayloadData, config: TracerReactNativeConfig, span: Span): void;
|
|
37
37
|
/**
|
|
38
|
-
* Converts Headers object to plain object
|
|
38
|
+
* Converts Headers object to plain object
|
|
39
39
|
*/
|
|
40
|
-
export declare function headersToObject(headers: Record<string, string> | undefined): Record<string, string>;
|
|
40
|
+
export declare function headersToObject(headers: Headers | Record<string, string> | Record<string, string | string[]> | string[][] | undefined): Record<string, string>;
|
|
41
41
|
/**
|
|
42
|
-
* Extracts response body as string
|
|
42
|
+
* Extracts response body as string from Response object
|
|
43
43
|
*/
|
|
44
|
-
export declare function extractResponseBody(response:
|
|
44
|
+
export declare function extractResponseBody(response: Response): Promise<string | null>;
|
|
45
45
|
export declare const getExporterEndpoint: (exporterEndpoint: string) => string;
|
package/dist/otel/helpers.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:true});exports.extractResponseBody=extractResponseBody;exports.getExporterEndpoint=void 0;exports.headersToObject=headersToObject;exports.processBody=processBody;exports.processHeaders=processHeaders;exports.processHttpPayload=processHttpPayload;exports.shouldProcessTrace=shouldProcessTrace;var _regenerator=_interopRequireDefault(require("@babel/runtime/regenerator"));var _asyncToGenerator2=_interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));var
|
|
1
|
+
"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:true});exports.extractResponseBody=extractResponseBody;exports.getExporterEndpoint=void 0;exports.headersToObject=headersToObject;exports.processBody=processBody;exports.processHeaders=processHeaders;exports.processHttpPayload=processHttpPayload;exports.shouldProcessTrace=shouldProcessTrace;var _regenerator=_interopRequireDefault(require("@babel/runtime/regenerator"));var _asyncToGenerator2=_interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));var _typeof2=_interopRequireDefault(require("@babel/runtime/helpers/typeof"));var _slicedToArray2=_interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));var _sessionRecorderCommon=require("@multiplayer-app/session-recorder-common");function _createForOfIteratorHelper(r,e){var t="undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"];if(!t){if(Array.isArray(r)||(t=_unsupportedIterableToArray(r))||e&&r&&"number"==typeof r.length){t&&(r=t);var _n=0,F=function F(){};return{s:F,n:function n(){return _n>=r.length?{done:!0}:{done:!1,value:r[_n++]};},e:function e(r){throw r;},f:F};}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}var o,a=!0,u=!1;return{s:function s(){t=t.call(r);},n:function n(){var r=t.next();return a=r.done,r;},e:function e(r){u=!0,o=r;},f:function f(){try{a||null==t["return"]||t["return"]();}finally{if(u)throw o;}}};}function _unsupportedIterableToArray(r,a){if(r){if("string"==typeof r)return _arrayLikeToArray(r,a);var t={}.toString.call(r).slice(8,-1);return"Object"===t&&r.constructor&&(t=r.constructor.name),"Map"===t||"Set"===t?Array.from(r):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?_arrayLikeToArray(r,a):void 0;}}function _arrayLikeToArray(r,a){(null==a||a>r.length)&&(a=r.length);for(var e=0,n=Array(a);e<a;e++)n[e]=r[e];return n;}var schemify=_sessionRecorderCommon.SessionRecorderSdk.schemify;function shouldProcessTrace(traceId){return traceId.startsWith(_sessionRecorderCommon.MULTIPLAYER_TRACE_DEBUG_PREFIX)||traceId.startsWith(_sessionRecorderCommon.MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX);}function processBody(payload,config,span){var _a,_b;var captureBody=config.captureBody,masking=config.masking;var traceId=span.spanContext().traceId;if(!captureBody){return{};}var requestBody=payload.requestBody,responseBody=payload.responseBody;if(requestBody!==undefined&&requestBody!==null){requestBody=JSON.parse(JSON.stringify(requestBody));}if(responseBody!==undefined&&responseBody!==null){responseBody=JSON.parse(JSON.stringify(responseBody));}if(traceId.startsWith(_sessionRecorderCommon.MULTIPLAYER_TRACE_DEBUG_PREFIX)||traceId.startsWith(_sessionRecorderCommon.MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)){if(masking.isContentMaskingEnabled){requestBody=requestBody&&((_a=masking.maskBody)===null||_a===void 0?void 0:_a.call(masking,requestBody,span));responseBody=responseBody&&((_b=masking.maskBody)===null||_b===void 0?void 0:_b.call(masking,responseBody,span));}}if(typeof requestBody!=='string'){requestBody=JSON.stringify(requestBody);}if(typeof responseBody!=='string'){responseBody=JSON.stringify(responseBody);}return{requestBody:(requestBody===null||requestBody===void 0?void 0:requestBody.length)?requestBody:undefined,responseBody:(responseBody===null||responseBody===void 0?void 0:responseBody.length)?responseBody:undefined};}function processHeaders(payload,config,span){var _a,_b,_c,_d,_e;var captureHeaders=config.captureHeaders,masking=config.masking;if(!captureHeaders){return{};}var _payload$requestHeade=payload.requestHeaders,requestHeaders=_payload$requestHeade===void 0?{}:_payload$requestHeade,_payload$responseHead=payload.responseHeaders,responseHeaders=_payload$responseHead===void 0?{}:_payload$responseHead;if(!((_a=masking.headersToInclude)===null||_a===void 0?void 0:_a.length)&&!((_b=masking.headersToExclude)===null||_b===void 0?void 0:_b.length)){if(requestHeaders!==undefined&&requestHeaders!==null){requestHeaders=JSON.parse(JSON.stringify(requestHeaders));}if(responseHeaders!==undefined&&responseHeaders!==null){responseHeaders=JSON.parse(JSON.stringify(responseHeaders));}}else{if(masking.headersToInclude){var _requestHeaders={};var _responseHeaders={};var _iterator=_createForOfIteratorHelper(masking.headersToInclude),_step;try{for(_iterator.s();!(_step=_iterator.n()).done;){var headerName=_step.value;if(requestHeaders[headerName]){_requestHeaders[headerName]=requestHeaders[headerName];}if(responseHeaders[headerName]){_responseHeaders[headerName]=responseHeaders[headerName];}}}catch(err){_iterator.e(err);}finally{_iterator.f();}requestHeaders=_requestHeaders;responseHeaders=_responseHeaders;}if((_c=masking.headersToExclude)===null||_c===void 0?void 0:_c.length){var _iterator2=_createForOfIteratorHelper(masking.headersToExclude),_step2;try{for(_iterator2.s();!(_step2=_iterator2.n()).done;){var _headerName=_step2.value;delete requestHeaders[_headerName];delete responseHeaders[_headerName];}}catch(err){_iterator2.e(err);}finally{_iterator2.f();}}}var maskedRequestHeaders=((_d=masking.maskHeaders)===null||_d===void 0?void 0:_d.call(masking,requestHeaders,span))||requestHeaders;var maskedResponseHeaders=((_e=masking.maskHeaders)===null||_e===void 0?void 0:_e.call(masking,responseHeaders,span))||responseHeaders;var requestHeadersStr=typeof maskedRequestHeaders==='string'?maskedRequestHeaders:JSON.stringify(maskedRequestHeaders);var responseHeadersStr=typeof maskedResponseHeaders==='string'?maskedResponseHeaders:JSON.stringify(maskedResponseHeaders);return{requestHeaders:(requestHeadersStr===null||requestHeadersStr===void 0?void 0:requestHeadersStr.length)?requestHeadersStr:undefined,responseHeaders:(responseHeadersStr===null||responseHeadersStr===void 0?void 0:responseHeadersStr.length)?responseHeadersStr:undefined};}function processHttpPayload(payload,config,span){var traceId=span.spanContext().traceId;if(!shouldProcessTrace(traceId)){return;}var _processBody=processBody(payload,config,span),requestBody=_processBody.requestBody,responseBody=_processBody.responseBody;var _processHeaders=processHeaders(payload,config,span),requestHeaders=_processHeaders.requestHeaders,responseHeaders=_processHeaders.responseHeaders;if(requestBody){span.setAttribute(_sessionRecorderCommon.ATTR_MULTIPLAYER_HTTP_REQUEST_BODY,requestBody);}if(responseBody){span.setAttribute(_sessionRecorderCommon.ATTR_MULTIPLAYER_HTTP_RESPONSE_BODY,responseBody);}if(requestHeaders){span.setAttribute(_sessionRecorderCommon.ATTR_MULTIPLAYER_HTTP_REQUEST_HEADERS,requestHeaders);}if(responseHeaders){span.setAttribute(_sessionRecorderCommon.ATTR_MULTIPLAYER_HTTP_RESPONSE_HEADERS,responseHeaders);}}function headersToObject(headers){var result={};if(!headers){return result;}if(headers instanceof Headers){headers.forEach(function(value,key){result[key]=value;});}else if(Array.isArray(headers)){var _iterator3=_createForOfIteratorHelper(headers),_step3;try{for(_iterator3.s();!(_step3=_iterator3.n()).done;){var _step3$value=(0,_slicedToArray2["default"])(_step3.value,2),key=_step3$value[0],value=_step3$value[1];if(typeof key==='string'&&typeof value==='string'){result[key]=value;}}}catch(err){_iterator3.e(err);}finally{_iterator3.f();}}else if((0,_typeof2["default"])(headers)==='object'&&!Array.isArray(headers)){for(var _i=0,_Object$entries=Object.entries(headers);_i<_Object$entries.length;_i++){var _Object$entries$_i=(0,_slicedToArray2["default"])(_Object$entries[_i],2),_key=_Object$entries$_i[0],_value=_Object$entries$_i[1];if(typeof _key==='string'&&typeof _value==='string'){result[_key]=_value;}}}return result;}function extractResponseBody(_x){return _extractResponseBody.apply(this,arguments);}function _extractResponseBody(){_extractResponseBody=(0,_asyncToGenerator2["default"])(_regenerator["default"].mark(function _callee(response){var responseClone,_t;return _regenerator["default"].wrap(function(_context){while(1)switch(_context.prev=_context.next){case 0:if(response.body){_context.next=1;break;}return _context.abrupt("return",null);case 1:_context.prev=1;if(!(response.body instanceof ReadableStream)){_context.next=3;break;}if(!response.bodyUsed){_context.next=2;break;}return _context.abrupt("return",null);case 2:responseClone=response.clone();return _context.abrupt("return",responseClone.text());case 3:return _context.abrupt("return",JSON.stringify(response.body));case 4:_context.next=6;break;case 5:_context.prev=5;_t=_context["catch"](1);console.warn('[DEBUGGER_LIB] Failed to extract response body:',_t);return _context.abrupt("return",null);case 6:case"end":return _context.stop();}},_callee,null,[[1,5]]);}));return _extractResponseBody.apply(this,arguments);}var getExporterEndpoint=exports.getExporterEndpoint=function getExporterEndpoint(exporterEndpoint){var hasPath=exporterEndpoint&&function(){try{var url=new URL(exporterEndpoint);return url.pathname!=='/'&&url.pathname!=='';}catch(_a){return false;}}();if(hasPath){return exporterEndpoint;}var trimmedExporterEndpoint=new URL(exporterEndpoint).origin;return"".concat(trimmedExporterEndpoint,"/v1/traces");};
|