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

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 (79) hide show
  1. package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.d.ts +6 -0
  2. package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js +1 -0
  3. package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js.map +1 -0
  4. package/dist/components/GestureCaptureWrapper/index.d.ts +1 -0
  5. package/dist/components/GestureCaptureWrapper/index.js +1 -0
  6. package/dist/components/GestureCaptureWrapper/index.js.map +1 -0
  7. package/dist/components/GestureCaptureWrapper.js +1 -1
  8. package/dist/components/GestureCaptureWrapper.js.map +1 -1
  9. package/dist/components/ScreenRecorderView/ScreenRecorderView.d.ts +5 -0
  10. package/dist/components/ScreenRecorderView/ScreenRecorderView.js +1 -0
  11. package/dist/components/ScreenRecorderView/ScreenRecorderView.js.map +1 -0
  12. package/dist/components/ScreenRecorderView/index.d.ts +1 -0
  13. package/dist/components/ScreenRecorderView/index.js +1 -0
  14. package/dist/components/ScreenRecorderView/index.js.map +1 -0
  15. package/dist/components/index.d.ts +1 -0
  16. package/dist/components/index.js +1 -0
  17. package/dist/components/index.js.map +1 -0
  18. package/dist/context/SessionRecorderContext.js +1 -1
  19. package/dist/context/SessionRecorderContext.js.map +1 -1
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.js +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/otel/index.d.ts +0 -2
  24. package/dist/otel/index.js.map +1 -1
  25. package/dist/otel/instrumentations/gestureInstrumentation.js +1 -1
  26. package/dist/otel/instrumentations/gestureInstrumentation.js.map +1 -1
  27. package/dist/otel/instrumentations/index.d.ts +0 -3
  28. package/dist/otel/instrumentations/index.js +1 -1
  29. package/dist/otel/instrumentations/index.js.map +1 -1
  30. package/dist/recorder/gestureRecorder.d.ts +0 -9
  31. package/dist/recorder/gestureRecorder.js +1 -1
  32. package/dist/recorder/gestureRecorder.js.map +1 -1
  33. package/dist/recorder/index.d.ts +4 -3
  34. package/dist/recorder/index.js.map +1 -1
  35. package/dist/recorder/screenRecorder.d.ts +1 -6
  36. package/dist/recorder/screenRecorder.js +1 -1
  37. package/dist/recorder/screenRecorder.js.map +1 -1
  38. package/dist/session-recorder.d.ts +3 -2
  39. package/dist/session-recorder.js.map +1 -1
  40. package/dist/types/index.d.ts +2 -16
  41. package/dist/types/index.js +1 -1
  42. package/dist/types/index.js.map +1 -1
  43. package/dist/utils/app-metadata.d.ts +16 -0
  44. package/dist/utils/app-metadata.js +1 -0
  45. package/dist/utils/app-metadata.js.map +1 -0
  46. package/dist/utils/platform.d.ts +35 -3
  47. package/dist/utils/platform.js +1 -1
  48. package/dist/utils/platform.js.map +1 -1
  49. package/dist/utils/rrweb-events.d.ts +1 -1
  50. package/dist/utils/rrweb-events.js +1 -1
  51. package/dist/utils/rrweb-events.js.map +1 -1
  52. package/dist/version.d.ts +1 -1
  53. package/dist/version.js +1 -1
  54. package/dist/version.js.map +1 -1
  55. package/docs/AUTO_METADATA_DETECTION.md +108 -0
  56. package/package.json +6 -9
  57. package/scripts/generate-app-metadata.js +173 -0
  58. package/src/components/{GestureCaptureWrapper.tsx → GestureCaptureWrapper/GestureCaptureWrapper.tsx} +1 -25
  59. package/src/components/GestureCaptureWrapper/index.ts +1 -0
  60. package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +72 -0
  61. package/src/components/ScreenRecorderView/index.ts +1 -0
  62. package/src/components/index.ts +1 -0
  63. package/src/context/SessionRecorderContext.tsx +21 -89
  64. package/src/index.ts +8 -0
  65. package/src/otel/index.ts +1 -12
  66. package/src/otel/instrumentations/index.ts +1 -6
  67. package/src/recorder/gestureRecorder.ts +10 -134
  68. package/src/recorder/index.ts +5 -4
  69. package/src/recorder/screenRecorder.ts +6 -14
  70. package/src/session-recorder.ts +2 -3
  71. package/src/types/index.ts +2 -20
  72. package/src/utils/app-metadata.ts +31 -0
  73. package/src/utils/platform.ts +303 -6
  74. package/src/utils/rrweb-events.ts +2 -4
  75. package/src/version.ts +1 -1
  76. package/src/otel/instrumentations/gestureInstrumentation.ts +0 -141
  77. package/src/otel/instrumentations/reactNativeInstrumentation.ts +0 -77
  78. package/src/otel/instrumentations/reactNavigationInstrumentation.ts +0 -119
  79. package/src/recorder/gestureHandlerRecorder.ts +0 -157
@@ -1,6 +1,14 @@
1
1
  import { IResourceAttributes } from '../types'
2
2
  import Constants from 'expo-constants'
3
- import { Platform } from 'react-native'
3
+ import { Platform, Dimensions, PixelRatio } from 'react-native'
4
+ import { version } from '../version'
5
+ import { getAutoDetectedAppMetadata } from './app-metadata'
6
+
7
+ // Global app metadata configuration for non-Expo apps
8
+ let globalAppMetadata: { name?: string; version?: string; bundleId?: string } = {}
9
+
10
+ // Cache for auto-detected metadata to avoid repeated file reads
11
+ let autoDetectedMetadata: { name?: string; version?: string; bundleId?: string } | null = null
4
12
 
5
13
  export interface PlatformInfo {
6
14
  isExpo: boolean
@@ -52,7 +60,7 @@ export function getPlatformAttributes(): Record<string, any> {
52
60
  const platformInfo = detectPlatform()
53
61
 
54
62
  const attributes: Record<string, any> = {
55
- 'platform': platformInfo.isExpo ? 'expo' : 'react-native',
63
+ platform: platformInfo.isExpo ? 'expo' : 'react-native',
56
64
  'device.type': platformInfo.deviceType,
57
65
  }
58
66
 
@@ -75,13 +83,302 @@ export function isReactNativeEnvironment(): boolean {
75
83
  return detectPlatform().isReactNative
76
84
  }
77
85
 
86
+ /**
87
+ * Configure app metadata for non-Expo React Native apps
88
+ * Call this function in your app initialization to provide app information
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * import { configureAppMetadata } from '@multiplayer-app/session-recorder-react-native'
93
+ *
94
+ * // In your App.tsx or index.js
95
+ * configureAppMetadata({
96
+ * name: 'My Awesome App',
97
+ * version: '1.2.3',
98
+ * bundleId: 'com.mycompany.myapp',
99
+ * buildNumber: '123',
100
+ * displayName: 'My App',
101
+ * })
102
+ * ```
103
+ */
104
+ export function configureAppMetadata(metadata: {
105
+ name?: string
106
+ version?: string
107
+ bundleId?: string
108
+ buildNumber?: string
109
+ displayName?: string
110
+ }): void {
111
+ globalAppMetadata = { ...globalAppMetadata, ...metadata }
112
+ }
113
+
114
+ /**
115
+ * Get configured app metadata
116
+ */
117
+ export function getConfiguredAppMetadata(): { name?: string, version?: string, bundleId?: string, buildNumber?: string, displayName?: string, } {
118
+ return { ...globalAppMetadata }
119
+ }
120
+
121
+ /**
122
+ * Automatically detect app metadata from common configuration files
123
+ * This runs without developer intervention
124
+ */
125
+ function autoDetectAppMetadata(): { name?: string; version?: string; bundleId?: string } {
126
+ if (autoDetectedMetadata) {
127
+ return autoDetectedMetadata
128
+ }
129
+
130
+ try {
131
+ // Get auto-detected metadata from build-time generated file
132
+ const autoMetadata = getAutoDetectedAppMetadata()
133
+
134
+ // Filter out undefined values
135
+ const metadata: { name?: string; version?: string; bundleId?: string } = {}
136
+ if (autoMetadata.name) metadata.name = autoMetadata.name
137
+ if (autoMetadata.version) metadata.version = autoMetadata.version
138
+ if (autoMetadata.bundleId) metadata.bundleId = autoMetadata.bundleId
139
+
140
+ autoDetectedMetadata = metadata
141
+ return metadata
142
+ } catch (error) {
143
+ // Silently fail - this is optional auto-detection
144
+ autoDetectedMetadata = {}
145
+ return {}
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Enhanced app metadata detection with automatic fallbacks
151
+ */
152
+ function getAppMetadata(): { name?: string; version?: string; bundleId?: string } {
153
+ // Priority order:
154
+ // 1. Expo config (if available)
155
+ // 2. Manually configured metadata
156
+ // 3. Auto-detected metadata
157
+ // 4. Fallbacks
158
+
159
+ const expoMetadata = getExpoMetadata()
160
+ const configuredMetadata = getConfiguredAppMetadata()
161
+ const autoMetadata = autoDetectAppMetadata()
162
+
163
+ return {
164
+ name: expoMetadata.name || configuredMetadata.name || autoMetadata.name,
165
+ version: expoMetadata.version || configuredMetadata.version || autoMetadata.version,
166
+ bundleId: expoMetadata.bundleId || configuredMetadata.bundleId || autoMetadata.bundleId,
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Get metadata from Expo config
172
+ */
173
+ function getExpoMetadata(): { name?: string; version?: string; bundleId?: string } {
174
+ const expoConfig = Constants.default?.expoConfig || Constants.expoConfig
175
+ if (!expoConfig) return {}
176
+
177
+ return {
178
+ name: expoConfig.name,
179
+ version: expoConfig.version,
180
+ bundleId: expoConfig.ios?.bundleIdentifier || expoConfig.android?.package,
181
+ }
182
+ }
78
183
 
79
184
  export const getNavigatorInfo = (): IResourceAttributes => {
185
+ const platformInfo = detectPlatform()
186
+ const screenData = Dimensions.get('window')
187
+ const screenDataScreen = Dimensions.get('screen')
188
+ const pixelRatio = PixelRatio.get()
189
+
190
+ // Get device type based on screen dimensions
191
+ const getDeviceType = (): string => {
192
+ const { width, height } = screenData
193
+ const minDimension = Math.min(width, height)
194
+ const maxDimension = Math.max(width, height)
195
+
196
+ // Rough device type detection based on screen size
197
+ if (maxDimension >= 1024) {
198
+ return 'Tablet'
199
+ } else if (minDimension >= 600) {
200
+ return 'Large Phone'
201
+ } else {
202
+ return 'Phone'
203
+ }
204
+ }
205
+
206
+ // Get orientation
207
+ const getOrientation = (): string => {
208
+ const { width, height } = screenData
209
+ return width > height ? 'Landscape' : 'Portrait'
210
+ }
211
+
212
+ // Get OS version details
213
+ const getOSInfo = (): string => {
214
+ if (platformInfo.isExpo) {
215
+ const platform = Constants.default?.platform || Constants.platform
216
+ if (platform?.ios) {
217
+ return `iOS ${Platform.Version}`
218
+ } else if (platform?.android) {
219
+ return `Android ${Platform.Version}`
220
+ }
221
+ }
222
+
223
+ if (Platform.OS === 'ios') {
224
+ return `iOS ${Platform.Version}`
225
+ } else if (Platform.OS === 'android') {
226
+ return `Android ${Platform.Version}`
227
+ }
228
+
229
+ return `${Platform.OS} ${Platform.Version || 'Unknown'}`
230
+ }
231
+
232
+ // Get device info string
233
+ const getDeviceInfo = (): string => {
234
+ const deviceType = getDeviceType()
235
+ const osInfo = getOSInfo()
236
+ return `${deviceType} - ${osInfo}`
237
+ }
238
+
239
+ // Get browser/runtime info
240
+ const getBrowserInfo = (): string => {
241
+ if (platformInfo.isExpo) {
242
+ return `Expo ${platformInfo.expoVersion || 'Unknown'} - React Native`
243
+ }
244
+ return 'React Native'
245
+ }
246
+
247
+ // Get screen size string
248
+ const getScreenSize = (): string => {
249
+ return `${Math.round(screenData.width)}x${Math.round(screenData.height)}`
250
+ }
251
+
252
+ // Get app info with fallbacks for non-Expo apps
253
+ const getAppInfo = (): string => {
254
+ const appName = getAppName()
255
+ const appVersion = getAppVersion()
256
+ return `${appName} v${appVersion}`
257
+ }
258
+
259
+ // Get app name with automatic detection
260
+ const getAppName = (): string => {
261
+ const metadata = getAppMetadata()
262
+ if (metadata.name) return metadata.name
263
+
264
+ // Try configured display name as fallback
265
+ const configuredMetadata = getConfiguredAppMetadata()
266
+ if (configuredMetadata.displayName) return configuredMetadata.displayName
267
+
268
+ // Final fallback
269
+ return 'React Native App'
270
+ }
271
+
272
+ // Get app version with automatic detection
273
+ const getAppVersion = (): string => {
274
+ const metadata = getAppMetadata()
275
+ if (metadata.version) return metadata.version
276
+
277
+ // Final fallback
278
+ return 'Unknown'
279
+ }
280
+
281
+ // Get bundle ID with automatic detection
282
+ const getBundleId = (): string => {
283
+ const metadata = getAppMetadata()
284
+ if (metadata.bundleId) return metadata.bundleId
285
+
286
+ // Fallback
287
+ return 'com.reactnative.app'
288
+ }
289
+
290
+ // Get build number with multiple fallback strategies
291
+ const getBuildNumber = (): string => {
292
+ // Try Expo config first
293
+ const expoBuildNumber =
294
+ Constants.default?.expoConfig?.ios?.buildNumber ||
295
+ Constants.expoConfig?.ios?.buildNumber ||
296
+ Constants.default?.expoConfig?.android?.versionCode ||
297
+ Constants.expoConfig?.android?.versionCode
298
+ if (expoBuildNumber) return expoBuildNumber.toString()
299
+
300
+ // Try configured metadata for non-Expo apps
301
+ const configuredMetadata = getConfiguredAppMetadata()
302
+ if (configuredMetadata.buildNumber) return configuredMetadata.buildNumber
303
+
304
+ // Fallback
305
+ return '1'
306
+ }
307
+
308
+ // Get network info (basic)
309
+ const getNetworkInfo = (): string => {
310
+ // This is a basic implementation - in a real app you might want to use @react-native-community/netinfo
311
+ return 'Unknown'
312
+ }
313
+
314
+ // Get hardware info
315
+ const getHardwareInfo = (): string => {
316
+ const pixelRatioInfo = `Pixel Ratio: ${pixelRatio}`
317
+ const screenDensity = PixelRatio.getFontScale()
318
+ return `${pixelRatioInfo}, Font Scale: ${screenDensity}`
319
+ }
320
+
80
321
  return {
81
- platform: 'react-native',
82
- userAgent: 'React Native',
83
- language: 'en',
322
+ // Core platform info
323
+ platform: platformInfo.isExpo ? 'expo' : 'react-native',
324
+ userAgent: getBrowserInfo(),
325
+ language: 'en', // Could be enhanced with react-native-localize if available
84
326
  timestamp: new Date().toISOString(),
327
+
328
+ // Device and OS information
329
+ deviceInfo: getDeviceInfo(),
330
+ osInfo: getOSInfo(),
331
+ browserInfo: getBrowserInfo(),
332
+
333
+ // Screen information
334
+ screenSize: getScreenSize(),
335
+ pixelRatio: pixelRatio,
336
+ orientation: getOrientation(),
337
+ screenWidth: Math.round(screenData.width),
338
+ screenHeight: Math.round(screenData.height),
339
+ screenScale: pixelRatio,
340
+
341
+ // Device capabilities
342
+ hardwareConcurrency: 1, // React Native doesn't expose CPU cores directly
343
+ cookiesEnabled: 'N/A', // Not applicable in React Native
344
+
345
+ // App information
346
+ packageVersion: version,
347
+ appInfo: getAppInfo(),
348
+ appName: getAppName(),
349
+ appVersion: getAppVersion(),
350
+ bundleId: getBundleId(),
351
+ buildNumber: getBuildNumber(),
352
+
353
+ // Platform specific
354
+ platformType: platformInfo.platform,
355
+ platformVersion: platformInfo.platformVersion,
356
+ expoVersion: platformInfo.expoVersion,
357
+ deviceType: getDeviceType(),
358
+
359
+ // Additional device info
360
+ deviceModel: Platform.OS === 'ios' ? 'iOS Device' : 'Android Device',
361
+ deviceManufacturer: Platform.OS === 'ios' ? 'Apple' : 'Android Manufacturer',
362
+ deviceBrand: Platform.OS === 'ios' ? 'Apple' : 'Android Brand',
363
+
364
+ // Performance and hardware
365
+ hardwareInfo: getHardwareInfo(),
366
+ networkInfo: getNetworkInfo(),
367
+
368
+ // Screen details
369
+ screenDensity: PixelRatio.getFontScale(),
370
+ screenScaleFactor: pixelRatio,
371
+ windowWidth: Math.round(screenData.width),
372
+ windowHeight: Math.round(screenData.height),
373
+ fullScreenWidth: Math.round(screenDataScreen.width),
374
+ fullScreenHeight: Math.round(screenDataScreen.height),
375
+
376
+ // Environment info
377
+ isExpo: platformInfo.isExpo,
378
+ isReactNative: platformInfo.isReactNative,
379
+ environment: platformInfo.isExpo ? 'expo' : 'react-native',
380
+
381
+ // Additional platform attributes
85
382
  ...getPlatformAttributes(),
86
383
  }
87
- }
384
+ }
@@ -39,10 +39,9 @@ export function createFullSnapshotEvent(
39
39
  nodeIdCounter: { current: number },
40
40
  ): eventWithTime {
41
41
  // Create a virtual DOM node representing the screen as an image
42
- const imageNodeId = nodeIdCounter.current++
43
42
  const imageNode: serializedNodeWithId = {
44
43
  type: NodeType.Element,
45
- id: imageNodeId,
44
+ id: 0,
46
45
  tagName: 'img',
47
46
  attributes: {
48
47
  src: `data:image/${captureFormat};base64,${base64Image}`,
@@ -137,7 +136,6 @@ export function createFullSnapshotEvent(
137
136
  */
138
137
  export function createIncrementalSnapshotWithImageUpdate(
139
138
  base64Image: string,
140
- imageNodeId: number,
141
139
  captureFormat: string = 'jpg',
142
140
  ): eventWithTime {
143
141
  const mutationData: mutationData = {
@@ -145,7 +143,7 @@ export function createIncrementalSnapshotWithImageUpdate(
145
143
  texts: [],
146
144
  attributes: [
147
145
  {
148
- id: imageNodeId,
146
+ id: 0,
149
147
  attributes: {
150
148
  src: `data:image/${captureFormat};base64,${base64Image}`,
151
149
  },
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = "0.0.1-alpha.9"
1
+ export const version = "0.0.1"
@@ -1,141 +0,0 @@
1
- import { InstrumentationBase } from '@opentelemetry/instrumentation'
2
- import { trace, SpanStatusCode } from '@opentelemetry/api'
3
-
4
- export class GestureInstrumentation extends InstrumentationBase {
5
- private _isEnabled = false
6
-
7
- constructor() {
8
- super('react-native-gesture', '1.0.0', {})
9
- }
10
-
11
- init(): void {
12
- // Initialize the instrumentation
13
- }
14
-
15
- enable(): void {
16
- this._isEnabled = true
17
- }
18
-
19
- disable(): void {
20
- this._isEnabled = false
21
- }
22
-
23
- isEnabled(): boolean {
24
- return this._isEnabled
25
- }
26
-
27
- // Manual gesture tracking methods
28
- recordTap(x: number, y: number, target?: string) {
29
- if (!this._isEnabled) return
30
-
31
- const span = trace.getTracer('gesture').startSpan('Gesture.tap', {
32
- attributes: {
33
- 'gesture.type': 'tap',
34
- 'gesture.coordinates.x': x,
35
- 'gesture.coordinates.y': y,
36
- 'gesture.timestamp': Date.now(),
37
- },
38
- })
39
-
40
- if (target) {
41
- span.setAttribute('gesture.target', target)
42
- }
43
-
44
- span.setStatus({ code: SpanStatusCode.OK })
45
- span.end()
46
- }
47
-
48
- recordSwipe(direction: string, target?: string) {
49
- if (!this._isEnabled) return
50
-
51
- const span = trace.getTracer('gesture').startSpan('Gesture.swipe', {
52
- attributes: {
53
- 'gesture.type': 'swipe',
54
- 'gesture.direction': direction,
55
- 'gesture.timestamp': Date.now(),
56
- },
57
- })
58
-
59
- if (target) {
60
- span.setAttribute('gesture.target', target)
61
- }
62
-
63
- span.setStatus({ code: SpanStatusCode.OK })
64
- span.end()
65
- }
66
-
67
- recordPinch(scale: number, target?: string) {
68
- if (!this._isEnabled) return
69
-
70
- const span = trace.getTracer('gesture').startSpan('Gesture.pinch', {
71
- attributes: {
72
- 'gesture.type': 'pinch',
73
- 'gesture.scale': scale,
74
- 'gesture.timestamp': Date.now(),
75
- },
76
- })
77
-
78
- if (target) {
79
- span.setAttribute('gesture.target', target)
80
- }
81
-
82
- span.setStatus({ code: SpanStatusCode.OK })
83
- span.end()
84
- }
85
-
86
- recordPan(deltaX: number, deltaY: number, target?: string) {
87
- if (!this._isEnabled) return
88
-
89
- const span = trace.getTracer('gesture').startSpan('Gesture.pan', {
90
- attributes: {
91
- 'gesture.type': 'pan',
92
- 'gesture.delta_x': deltaX,
93
- 'gesture.delta_y': deltaY,
94
- 'gesture.timestamp': Date.now(),
95
- },
96
- })
97
-
98
- if (target) {
99
- span.setAttribute('gesture.target', target)
100
- }
101
-
102
- span.setStatus({ code: SpanStatusCode.OK })
103
- span.end()
104
- }
105
-
106
- recordLongPress(duration: number, target?: string) {
107
- if (!this._isEnabled) return
108
-
109
- const span = trace.getTracer('gesture').startSpan('Gesture.longPress', {
110
- attributes: {
111
- 'gesture.type': 'longPress',
112
- 'gesture.duration': duration,
113
- 'gesture.timestamp': Date.now(),
114
- },
115
- })
116
-
117
- if (target) {
118
- span.setAttribute('gesture.target', target)
119
- }
120
-
121
- span.setStatus({ code: SpanStatusCode.OK })
122
- span.end()
123
- }
124
-
125
- // Error tracking for gesture failures
126
- recordGestureError(error: Error, gestureType: string) {
127
- if (!this._isEnabled) return
128
-
129
- const span = trace.getTracer('gesture').startSpan(`Gesture.${gestureType}.error`, {
130
- attributes: {
131
- 'gesture.type': gestureType,
132
- 'gesture.error': true,
133
- 'gesture.timestamp': Date.now(),
134
- },
135
- })
136
-
137
- span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })
138
- span.recordException(error)
139
- span.end()
140
- }
141
- }
@@ -1,77 +0,0 @@
1
- import { InstrumentationBase } from '@opentelemetry/instrumentation'
2
- import { logger } from '../../utils'
3
- import { trace, SpanStatusCode } from '@opentelemetry/api'
4
- import AsyncStorage from '@react-native-async-storage/async-storage'
5
-
6
- export class ReactNativeInstrumentation extends InstrumentationBase {
7
- constructor() {
8
- super('react-native', '1.0.0', {})
9
- }
10
-
11
- init(): void {
12
- // Initialize the instrumentation
13
- }
14
-
15
- enable(): void {
16
- // Try to wrap AsyncStorage if it's available
17
- try {
18
- if (AsyncStorage) {
19
- this._wrap(AsyncStorage, 'setItem', this._wrapAsyncStorage)
20
- }
21
- } catch (error) {
22
- logger.warn('DEBUGGER_LIB', '@react-native-async-storage/async-storage is not available. AsyncStorage instrumentation will be disabled.')
23
- }
24
- }
25
-
26
- disable(): void {
27
- // Try to unwrap AsyncStorage if it was wrapped
28
- try {
29
- if (AsyncStorage) {
30
- this._unwrap(AsyncStorage, 'setItem')
31
- }
32
- } catch (error) {
33
- // AsyncStorage was not available, nothing to unwrap
34
- }
35
- }
36
-
37
- private _wrapAsyncStorage(originalMethod: any) {
38
- return async function (this: any, key: string, value: string) {
39
- const startTime = Date.now()
40
- try {
41
- const result = await originalMethod.call(this, key, value)
42
-
43
- const span = trace.getTracer('react-native').startSpan('AsyncStorage.setItem', {
44
- attributes: {
45
- 'storage.operation': 'setItem',
46
- 'storage.key': key,
47
- 'storage.value_length': value.length,
48
- 'storage.duration': Date.now() - startTime,
49
- },
50
- })
51
-
52
- span.setStatus({ code: SpanStatusCode.OK })
53
- span.end()
54
-
55
- return result
56
- } catch (error) {
57
- const span = trace.getTracer('react-native').startSpan('AsyncStorage.setItem', {
58
- attributes: {
59
- 'storage.operation': 'setItem',
60
- 'storage.key': key,
61
- 'storage.error': true,
62
- 'storage.duration': Date.now() - startTime,
63
- },
64
- })
65
-
66
- const errorMessage = error instanceof Error ? error.message : String(error)
67
- span.setStatus({ code: SpanStatusCode.ERROR, message: errorMessage })
68
- if (error instanceof Error) {
69
- span.recordException(error)
70
- }
71
- span.end()
72
-
73
- throw error
74
- }
75
- }
76
- }
77
- }
@@ -1,119 +0,0 @@
1
- import { InstrumentationBase } from '@opentelemetry/instrumentation'
2
- import { trace, SpanStatusCode } from '@opentelemetry/api'
3
-
4
- export class ReactNavigationInstrumentation extends InstrumentationBase {
5
- private navigationRef: any = null
6
-
7
- constructor() {
8
- super('react-navigation', '1.0.0', {})
9
- }
10
-
11
- init(): void {
12
- // Initialize the instrumentation
13
- }
14
-
15
- enable(): void {
16
- // Enable the instrumentation
17
- super.enable()
18
- }
19
-
20
- setNavigationRef(ref: any) {
21
- this.navigationRef = ref
22
- this._setupNavigationListener()
23
- }
24
-
25
- private _setupNavigationListener() {
26
- if (!this.navigationRef) return
27
-
28
- // Listen to navigation state changes
29
- this.navigationRef.addListener('state', (e: any) => {
30
- this._recordNavigationEvent('state_change', e.data)
31
- })
32
-
33
- // Listen to focus events
34
- this.navigationRef.addListener('focus', (e: any) => {
35
- this._recordNavigationEvent('focus', e.data)
36
- })
37
-
38
- // Listen to blur events
39
- this.navigationRef.addListener('blur', (e: any) => {
40
- this._recordNavigationEvent('blur', e.data)
41
- })
42
- }
43
-
44
- private _recordNavigationEvent(eventType: string, data: any) {
45
- const span = trace.getTracer('navigation').startSpan(`Navigation.${eventType}`, {
46
- attributes: {
47
- 'navigation.system': 'ReactNavigation',
48
- 'navigation.operation': eventType,
49
- 'navigation.type': eventType,
50
- 'navigation.timestamp': Date.now(),
51
- },
52
- })
53
-
54
- if (data) {
55
- if (data.routeName) {
56
- span.setAttribute('navigation.route_name', data.routeName)
57
- }
58
- if (data.params) {
59
- span.setAttribute('navigation.params', JSON.stringify(data.params))
60
- }
61
- if (data.key) {
62
- span.setAttribute('navigation.key', data.key)
63
- }
64
- }
65
-
66
- span.setStatus({ code: SpanStatusCode.OK })
67
- span.end()
68
- }
69
-
70
- // Manual navigation tracking methods
71
- recordNavigate(routeName: string, params?: Record<string, any>) {
72
- const span = trace.getTracer('navigation').startSpan('Navigation.navigate', {
73
- attributes: {
74
- 'navigation.system': 'ReactNavigation',
75
- 'navigation.operation': 'navigate',
76
- 'navigation.route_name': routeName,
77
- 'navigation.timestamp': Date.now(),
78
- },
79
- })
80
-
81
- if (params) {
82
- span.setAttribute('navigation.params', JSON.stringify(params))
83
- }
84
-
85
- span.setStatus({ code: SpanStatusCode.OK })
86
- span.end()
87
- }
88
-
89
- recordGoBack() {
90
- const span = trace.getTracer('navigation').startSpan('Navigation.goBack', {
91
- attributes: {
92
- 'navigation.system': 'ReactNavigation',
93
- 'navigation.operation': 'goBack',
94
- 'navigation.timestamp': Date.now(),
95
- },
96
- })
97
-
98
- span.setStatus({ code: SpanStatusCode.OK })
99
- span.end()
100
- }
101
-
102
- recordReset(routes: any[]) {
103
- const span = trace.getTracer('navigation').startSpan('Navigation.reset', {
104
- attributes: {
105
- 'navigation.system': 'ReactNavigation',
106
- 'navigation.operation': 'reset',
107
- 'navigation.routes_count': routes.length,
108
- 'navigation.timestamp': Date.now(),
109
- },
110
- })
111
-
112
- if (routes.length > 0) {
113
- span.setAttribute('navigation.initial_route', routes[0].name)
114
- }
115
-
116
- span.setStatus({ code: SpanStatusCode.OK })
117
- span.end()
118
- }
119
- }