@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.
- package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.d.ts +6 -0
- package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js +1 -0
- package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js.map +1 -0
- package/dist/components/GestureCaptureWrapper/index.d.ts +1 -0
- package/dist/components/GestureCaptureWrapper/index.js +1 -0
- package/dist/components/GestureCaptureWrapper/index.js.map +1 -0
- package/dist/components/GestureCaptureWrapper.js +1 -1
- package/dist/components/GestureCaptureWrapper.js.map +1 -1
- package/dist/components/ScreenRecorderView/ScreenRecorderView.d.ts +5 -0
- package/dist/components/ScreenRecorderView/ScreenRecorderView.js +1 -0
- package/dist/components/ScreenRecorderView/ScreenRecorderView.js.map +1 -0
- package/dist/components/ScreenRecorderView/index.d.ts +1 -0
- package/dist/components/ScreenRecorderView/index.js +1 -0
- package/dist/components/ScreenRecorderView/index.js.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -0
- package/dist/context/SessionRecorderContext.js +1 -1
- package/dist/context/SessionRecorderContext.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/otel/index.d.ts +0 -2
- package/dist/otel/index.js.map +1 -1
- package/dist/otel/instrumentations/gestureInstrumentation.js +1 -1
- package/dist/otel/instrumentations/gestureInstrumentation.js.map +1 -1
- package/dist/otel/instrumentations/index.d.ts +0 -3
- package/dist/otel/instrumentations/index.js +1 -1
- package/dist/otel/instrumentations/index.js.map +1 -1
- package/dist/recorder/gestureRecorder.d.ts +0 -9
- package/dist/recorder/gestureRecorder.js +1 -1
- package/dist/recorder/gestureRecorder.js.map +1 -1
- package/dist/recorder/index.d.ts +4 -3
- package/dist/recorder/index.js.map +1 -1
- package/dist/recorder/screenRecorder.d.ts +1 -6
- package/dist/recorder/screenRecorder.js +1 -1
- package/dist/recorder/screenRecorder.js.map +1 -1
- package/dist/session-recorder.d.ts +3 -2
- package/dist/session-recorder.js.map +1 -1
- package/dist/types/index.d.ts +2 -16
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/app-metadata.d.ts +16 -0
- package/dist/utils/app-metadata.js +1 -0
- package/dist/utils/app-metadata.js.map +1 -0
- package/dist/utils/platform.d.ts +35 -3
- package/dist/utils/platform.js +1 -1
- package/dist/utils/platform.js.map +1 -1
- package/dist/utils/rrweb-events.d.ts +1 -1
- package/dist/utils/rrweb-events.js +1 -1
- package/dist/utils/rrweb-events.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/AUTO_METADATA_DETECTION.md +108 -0
- package/package.json +6 -9
- package/scripts/generate-app-metadata.js +173 -0
- package/src/components/{GestureCaptureWrapper.tsx → GestureCaptureWrapper/GestureCaptureWrapper.tsx} +1 -25
- package/src/components/GestureCaptureWrapper/index.ts +1 -0
- package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +72 -0
- package/src/components/ScreenRecorderView/index.ts +1 -0
- package/src/components/index.ts +1 -0
- package/src/context/SessionRecorderContext.tsx +21 -89
- package/src/index.ts +8 -0
- package/src/otel/index.ts +1 -12
- package/src/otel/instrumentations/index.ts +1 -6
- package/src/recorder/gestureRecorder.ts +10 -134
- package/src/recorder/index.ts +5 -4
- package/src/recorder/screenRecorder.ts +6 -14
- package/src/session-recorder.ts +2 -3
- package/src/types/index.ts +2 -20
- package/src/utils/app-metadata.ts +31 -0
- package/src/utils/platform.ts +303 -6
- package/src/utils/rrweb-events.ts +2 -4
- package/src/version.ts +1 -1
- package/src/otel/instrumentations/gestureInstrumentation.ts +0 -141
- package/src/otel/instrumentations/reactNativeInstrumentation.ts +0 -77
- package/src/otel/instrumentations/reactNavigationInstrumentation.ts +0 -119
- package/src/recorder/gestureHandlerRecorder.ts +0 -157
package/src/utils/platform.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
82
|
-
|
|
83
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
}
|