@multiplayer-app/session-recorder-react-native 0.0.1-alpha.3 → 0.0.1-alpha.4
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/dist/context/SessionRecorderContext.d.ts +13 -0
- package/dist/context/SessionRecorderContext.js +1 -0
- package/dist/context/SessionRecorderContext.js.map +1 -0
- package/dist/expo.d.ts +5 -9
- package/dist/expo.js +1 -1
- package/dist/expo.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/otel/index.d.ts +0 -23
- package/dist/otel/index.js +1 -1
- package/dist/otel/index.js.map +1 -1
- package/dist/otel/instrumentations/index.d.ts +4 -1
- package/dist/otel/instrumentations/index.js +1 -1
- package/dist/otel/instrumentations/index.js.map +1 -1
- package/dist/otel/instrumentations/reactNativeInstrumentation.js +1 -1
- package/dist/otel/instrumentations/reactNativeInstrumentation.js.map +1 -1
- package/dist/recorder/gestureRecorder.js +1 -1
- package/dist/recorder/gestureRecorder.js.map +1 -1
- package/dist/recorder/screenRecorder.js +1 -1
- package/dist/recorder/screenRecorder.js.map +1 -1
- package/dist/session-recorder.d.ts +3 -9
- package/dist/session-recorder.js +1 -1
- package/dist/session-recorder.js.map +1 -1
- package/dist/utils/platform.js +1 -1
- package/dist/utils/platform.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -5
- package/src/context/SessionRecorderContext.tsx +71 -0
- package/src/expo.ts +7 -17
- package/src/index.ts +4 -4
- package/src/otel/index.ts +1 -237
- package/src/otel/instrumentations/index.ts +3 -11
- package/src/otel/instrumentations/reactNativeInstrumentation.ts +8 -96
- package/src/recorder/gestureRecorder.ts +1 -3
- package/src/recorder/screenRecorder.ts +7 -29
- package/src/session-recorder.ts +3 -58
- package/src/utils/platform.ts +5 -6
- package/src/version.ts +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InstrumentationBase } from '@opentelemetry/instrumentation'
|
|
2
2
|
import { trace, SpanStatusCode } from '@opentelemetry/api'
|
|
3
|
+
import AsyncStorage from '@react-native-async-storage/async-storage'
|
|
3
4
|
|
|
4
5
|
export class ReactNativeInstrumentation extends InstrumentationBase {
|
|
5
6
|
constructor() {
|
|
@@ -13,9 +14,8 @@ export class ReactNativeInstrumentation extends InstrumentationBase {
|
|
|
13
14
|
enable(): void {
|
|
14
15
|
// Try to wrap AsyncStorage if it's available
|
|
15
16
|
try {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
this._wrap(asyncStorage, 'AsyncStorage', this._wrapAsyncStorage)
|
|
17
|
+
if (AsyncStorage) {
|
|
18
|
+
this._wrap(AsyncStorage, 'setItem', this._wrapAsyncStorage)
|
|
19
19
|
}
|
|
20
20
|
} catch (error) {
|
|
21
21
|
console.warn('@react-native-async-storage/async-storage is not available. AsyncStorage instrumentation will be disabled.')
|
|
@@ -25,24 +25,19 @@ export class ReactNativeInstrumentation extends InstrumentationBase {
|
|
|
25
25
|
disable(): void {
|
|
26
26
|
// Try to unwrap AsyncStorage if it was wrapped
|
|
27
27
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this._unwrap(asyncStorage, 'AsyncStorage')
|
|
28
|
+
if (AsyncStorage) {
|
|
29
|
+
this._unwrap(AsyncStorage, 'setItem')
|
|
31
30
|
}
|
|
32
31
|
} catch (error) {
|
|
33
32
|
// AsyncStorage was not available, nothing to unwrap
|
|
34
33
|
}
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
private _wrapAsyncStorage(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// Wrap setItem
|
|
41
|
-
const originalSetItem = originalModule.setItem
|
|
42
|
-
originalModule.setItem = async function (key: string, value: string) {
|
|
36
|
+
private _wrapAsyncStorage(originalMethod: any) {
|
|
37
|
+
return async function (this: any, key: string, value: string) {
|
|
43
38
|
const startTime = Date.now()
|
|
44
39
|
try {
|
|
45
|
-
const result = await
|
|
40
|
+
const result = await originalMethod.call(this, key, value)
|
|
46
41
|
|
|
47
42
|
const span = trace.getTracer('react-native').startSpan('AsyncStorage.setItem', {
|
|
48
43
|
attributes: {
|
|
@@ -77,88 +72,5 @@ export class ReactNativeInstrumentation extends InstrumentationBase {
|
|
|
77
72
|
throw error
|
|
78
73
|
}
|
|
79
74
|
}
|
|
80
|
-
|
|
81
|
-
// Wrap getItem
|
|
82
|
-
const originalGetItem = originalModule.getItem
|
|
83
|
-
originalModule.getItem = async function (key: string) {
|
|
84
|
-
const startTime = Date.now()
|
|
85
|
-
try {
|
|
86
|
-
const result = await originalGetItem.call(this, key)
|
|
87
|
-
|
|
88
|
-
const span = trace.getTracer('react-native').startSpan('AsyncStorage.getItem', {
|
|
89
|
-
attributes: {
|
|
90
|
-
'storage.operation': 'getItem',
|
|
91
|
-
'storage.key': key,
|
|
92
|
-
'storage.value_length': result ? result.length : 0,
|
|
93
|
-
'storage.duration': Date.now() - startTime,
|
|
94
|
-
},
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
span.setStatus({ code: SpanStatusCode.OK })
|
|
98
|
-
span.end()
|
|
99
|
-
|
|
100
|
-
return result
|
|
101
|
-
} catch (error) {
|
|
102
|
-
const span = trace.getTracer('react-native').startSpan('AsyncStorage.getItem', {
|
|
103
|
-
attributes: {
|
|
104
|
-
'storage.operation': 'getItem',
|
|
105
|
-
'storage.key': key,
|
|
106
|
-
'storage.error': true,
|
|
107
|
-
'storage.duration': Date.now() - startTime,
|
|
108
|
-
},
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
112
|
-
span.setStatus({ code: SpanStatusCode.ERROR, message: errorMessage })
|
|
113
|
-
if (error instanceof Error) {
|
|
114
|
-
span.recordException(error)
|
|
115
|
-
}
|
|
116
|
-
span.end()
|
|
117
|
-
|
|
118
|
-
throw error
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Wrap removeItem
|
|
123
|
-
const originalRemoveItem = originalModule.removeItem
|
|
124
|
-
originalModule.removeItem = async function (key: string) {
|
|
125
|
-
const startTime = Date.now()
|
|
126
|
-
try {
|
|
127
|
-
const result = await originalRemoveItem.call(this, key)
|
|
128
|
-
|
|
129
|
-
const span = trace.getTracer('react-native').startSpan('AsyncStorage.removeItem', {
|
|
130
|
-
attributes: {
|
|
131
|
-
'storage.operation': 'removeItem',
|
|
132
|
-
'storage.key': key,
|
|
133
|
-
'storage.duration': Date.now() - startTime,
|
|
134
|
-
},
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
span.setStatus({ code: SpanStatusCode.OK })
|
|
138
|
-
span.end()
|
|
139
|
-
|
|
140
|
-
return result
|
|
141
|
-
} catch (error) {
|
|
142
|
-
const span = trace.getTracer('react-native').startSpan('AsyncStorage.removeItem', {
|
|
143
|
-
attributes: {
|
|
144
|
-
'storage.operation': 'removeItem',
|
|
145
|
-
'storage.key': key,
|
|
146
|
-
'storage.error': true,
|
|
147
|
-
'storage.duration': Date.now() - startTime,
|
|
148
|
-
},
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
152
|
-
span.setStatus({ code: SpanStatusCode.ERROR, message: errorMessage })
|
|
153
|
-
if (error instanceof Error) {
|
|
154
|
-
span.recordException(error)
|
|
155
|
-
}
|
|
156
|
-
span.end()
|
|
157
|
-
|
|
158
|
-
throw error
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return originalModule
|
|
163
75
|
}
|
|
164
76
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GestureEvent, RecorderConfig } from '../types'
|
|
2
2
|
import { trace, SpanStatusCode } from '@opentelemetry/api'
|
|
3
|
+
import { Dimensions } from 'react-native'
|
|
3
4
|
|
|
4
5
|
export class GestureRecorder {
|
|
5
6
|
private config?: RecorderConfig
|
|
@@ -39,7 +40,6 @@ export class GestureRecorder {
|
|
|
39
40
|
|
|
40
41
|
private _getScreenDimensions(): void {
|
|
41
42
|
try {
|
|
42
|
-
const { Dimensions } = require('react-native')
|
|
43
43
|
this.screenDimensions = Dimensions.get('window')
|
|
44
44
|
} catch (error) {
|
|
45
45
|
// Failed to get screen dimensions - silently continue
|
|
@@ -70,8 +70,6 @@ export class GestureRecorder {
|
|
|
70
70
|
private _setupGlobalGestureListener(): void {
|
|
71
71
|
try {
|
|
72
72
|
// Listen for touch events at the app level
|
|
73
|
-
const { TouchableWithoutFeedback } = require('react-native')
|
|
74
|
-
|
|
75
73
|
// This is a simplified implementation - in production you'd use react-native-gesture-handler
|
|
76
74
|
// Global gesture listener setup complete
|
|
77
75
|
} catch (error) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ScreenEvent, RecorderConfig } from '../types'
|
|
2
2
|
import { trace, SpanStatusCode } from '@opentelemetry/api'
|
|
3
|
+
import { Dimensions, AppRegistry } from 'react-native'
|
|
4
|
+
import { captureRef, captureScreen } from 'react-native-view-shot'
|
|
3
5
|
|
|
4
6
|
export class ScreenRecorder {
|
|
5
7
|
private config?: RecorderConfig
|
|
@@ -44,7 +46,6 @@ export class ScreenRecorder {
|
|
|
44
46
|
|
|
45
47
|
private _getScreenDimensions(): void {
|
|
46
48
|
try {
|
|
47
|
-
const { Dimensions } = require('react-native')
|
|
48
49
|
this.screenDimensions = Dimensions.get('window')
|
|
49
50
|
} catch (error) {
|
|
50
51
|
// Failed to get screen dimensions - silently continue
|
|
@@ -109,11 +110,8 @@ export class ScreenRecorder {
|
|
|
109
110
|
|
|
110
111
|
private async _performScreenCapture(): Promise<string | null> {
|
|
111
112
|
try {
|
|
112
|
-
// Try react-native-view-shot first
|
|
113
|
-
|
|
114
|
-
if (ViewShot && ViewShot.captureRef) {
|
|
115
|
-
return await this._captureWithViewShot()
|
|
116
|
-
}
|
|
113
|
+
// Try react-native-view-shot screen capture first
|
|
114
|
+
return await this._captureWithViewShot()
|
|
117
115
|
} catch (error) {
|
|
118
116
|
// react-native-view-shot not available - silently continue
|
|
119
117
|
}
|
|
@@ -134,21 +132,9 @@ export class ScreenRecorder {
|
|
|
134
132
|
|
|
135
133
|
private async _captureWithViewShot(): Promise<string | null> {
|
|
136
134
|
try {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// Get the root view reference
|
|
140
|
-
const { findNodeHandle } = require('react-native')
|
|
141
|
-
const rootViewRef = this._getRootViewRef()
|
|
142
|
-
|
|
143
|
-
if (rootViewRef) {
|
|
144
|
-
const options = {
|
|
145
|
-
format: this.captureFormat,
|
|
146
|
-
quality: this.captureQuality,
|
|
147
|
-
result: 'data-uri',
|
|
148
|
-
}
|
|
135
|
+
// Use captureScreen for full screen capture
|
|
149
136
|
|
|
150
|
-
|
|
151
|
-
}
|
|
137
|
+
return await captureScreen()
|
|
152
138
|
} catch (error) {
|
|
153
139
|
// Failed to capture with ViewShot - silently continue
|
|
154
140
|
}
|
|
@@ -158,7 +144,6 @@ export class ScreenRecorder {
|
|
|
158
144
|
private async _captureWithScreenshot(): Promise<string | null> {
|
|
159
145
|
try {
|
|
160
146
|
const Screenshot = require('react-native-screenshot')
|
|
161
|
-
|
|
162
147
|
const options = {
|
|
163
148
|
format: this.captureFormat,
|
|
164
149
|
quality: this.captureQuality,
|
|
@@ -174,7 +159,6 @@ export class ScreenRecorder {
|
|
|
174
159
|
private _getRootViewRef(): any {
|
|
175
160
|
try {
|
|
176
161
|
// Try to get the root view reference
|
|
177
|
-
const { AppRegistry } = require('react-native')
|
|
178
162
|
const appName = AppRegistry.getAppKeys()[0]
|
|
179
163
|
return appName ? { current: null } : null
|
|
180
164
|
} catch (error) {
|
|
@@ -269,15 +253,9 @@ export class ScreenRecorder {
|
|
|
269
253
|
quality?: number
|
|
270
254
|
}): Promise<string | null> {
|
|
271
255
|
try {
|
|
272
|
-
const ViewShot = require('react-native-view-shot')
|
|
273
256
|
|
|
274
|
-
const captureOptions = {
|
|
275
|
-
format: options?.format || this.captureFormat,
|
|
276
|
-
quality: options?.quality || this.captureQuality,
|
|
277
|
-
result: 'data-uri',
|
|
278
|
-
}
|
|
279
257
|
|
|
280
|
-
return await
|
|
258
|
+
return await captureRef(elementRef)
|
|
281
259
|
} catch (error) {
|
|
282
260
|
// Failed to capture specific element - silently continue
|
|
283
261
|
return null
|
package/src/session-recorder.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { ApiService, StartSessionRequest, StopSessionRequest } from './services/
|
|
|
21
21
|
// Utility functions for React Native
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
class SessionRecorder implements ISessionRecorder {
|
|
25
25
|
private _isInitialized = false
|
|
26
26
|
private _configs: SessionRecorderConfigs | null = null
|
|
27
27
|
private _apiService = new ApiService()
|
|
@@ -500,53 +500,6 @@ export class SessionRecorder implements ISessionRecorder {
|
|
|
500
500
|
break
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
|
-
|
|
504
|
-
// Navigation methods
|
|
505
|
-
setNavigationRef(ref: any): void {
|
|
506
|
-
if (this._isInitialized) {
|
|
507
|
-
this._tracer.setNavigationRef(ref)
|
|
508
|
-
this._recorder.setNavigationRef(ref)
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Gesture methods
|
|
513
|
-
enableGestureTracking(): void {
|
|
514
|
-
if (this._isInitialized) {
|
|
515
|
-
this._tracer.enableGestureTracking()
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
disableGestureTracking(): void {
|
|
520
|
-
if (this._isInitialized) {
|
|
521
|
-
this._tracer.disableGestureTracking()
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// Manual recording methods
|
|
526
|
-
recordTap(x: number, y: number, target?: string): void {
|
|
527
|
-
if (this._isInitialized) {
|
|
528
|
-
this._tracer.recordTap(x, y, target)
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
recordSwipe(direction: string, target?: string): void {
|
|
533
|
-
if (this._isInitialized) {
|
|
534
|
-
this._tracer.recordSwipe(direction, target)
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
recordNavigate(routeName: string, params?: Record<string, any>): void {
|
|
539
|
-
if (this._isInitialized) {
|
|
540
|
-
this._tracer.recordNavigate(routeName, params)
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
recordGoBack(): void {
|
|
545
|
-
if (this._isInitialized) {
|
|
546
|
-
this._tracer.recordGoBack()
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
503
|
// Session attributes
|
|
551
504
|
setSessionAttribute(key: string, value: any): void {
|
|
552
505
|
if (this._session) {
|
|
@@ -557,14 +510,6 @@ export class SessionRecorder implements ISessionRecorder {
|
|
|
557
510
|
this._session.updatedAt = new Date().toISOString()
|
|
558
511
|
}
|
|
559
512
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
captureException(error: Error, context?: Record<string, any>): void {
|
|
565
|
-
if (this._isInitialized) {
|
|
566
|
-
this._tracer.captureException(error, context)
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
|
|
570
513
|
}
|
|
514
|
+
|
|
515
|
+
export default new SessionRecorder()
|
package/src/utils/platform.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { IResourceAttributes } from '../types'
|
|
2
|
+
import Constants from 'expo-constants'
|
|
3
|
+
import { Platform } from 'react-native'
|
|
2
4
|
|
|
3
5
|
export interface PlatformInfo {
|
|
4
6
|
isExpo: boolean
|
|
@@ -12,12 +14,11 @@ export interface PlatformInfo {
|
|
|
12
14
|
export function detectPlatform(): PlatformInfo {
|
|
13
15
|
try {
|
|
14
16
|
// Check if we're in an Expo environment
|
|
15
|
-
const
|
|
16
|
-
const isExpo = !!expoConstants.default?.expoVersion || !!expoConstants.expoVersion
|
|
17
|
+
const isExpo = !!Constants.default?.expoVersion || !!Constants.expoVersion
|
|
17
18
|
|
|
18
19
|
if (isExpo) {
|
|
19
|
-
const expoVersion =
|
|
20
|
-
const platform =
|
|
20
|
+
const expoVersion = Constants.default?.expoVersion || Constants.expoVersion
|
|
21
|
+
const platform = Constants.default?.platform || Constants.platform
|
|
21
22
|
|
|
22
23
|
return {
|
|
23
24
|
isExpo: true,
|
|
@@ -29,8 +30,6 @@ export function detectPlatform(): PlatformInfo {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
// Fallback to React Native detection
|
|
32
|
-
const { Platform } = require('react-native')
|
|
33
|
-
|
|
34
33
|
return {
|
|
35
34
|
isExpo: false,
|
|
36
35
|
isReactNative: true,
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "0.0.1-alpha.
|
|
1
|
+
export const version = "0.0.1-alpha.4"
|