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

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 (166) hide show
  1. package/dist/config/constants.d.ts +19 -0
  2. package/dist/config/constants.js +1 -0
  3. package/dist/config/constants.js.map +1 -0
  4. package/dist/config/defaults.d.ts +4 -0
  5. package/dist/config/defaults.js +1 -0
  6. package/dist/config/defaults.js.map +1 -0
  7. package/dist/config/index.d.ts +5 -0
  8. package/dist/config/index.js +1 -0
  9. package/dist/config/index.js.map +1 -0
  10. package/dist/config/masking.d.ts +2 -30
  11. package/dist/config/masking.js +1 -1
  12. package/dist/config/masking.js.map +1 -1
  13. package/dist/config/session-recorder.d.ts +2 -0
  14. package/dist/config/session-recorder.js +1 -0
  15. package/dist/config/session-recorder.js.map +1 -0
  16. package/dist/config/validators.d.ts +10 -0
  17. package/dist/config/validators.js +1 -0
  18. package/dist/config/validators.js.map +1 -0
  19. package/dist/expo.d.ts +2 -2
  20. package/dist/expo.js +1 -1
  21. package/dist/expo.js.map +1 -1
  22. package/dist/exporters.d.ts +3 -0
  23. package/dist/exporters.js +1 -0
  24. package/dist/exporters.js.map +1 -0
  25. package/dist/index.d.ts +3 -9
  26. package/dist/index.js +1 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/otel/helpers.d.ts +45 -3
  29. package/dist/otel/helpers.js +1 -1
  30. package/dist/otel/helpers.js.map +1 -1
  31. package/dist/otel/index.d.ts +9 -5
  32. package/dist/otel/index.js +1 -1
  33. package/dist/otel/index.js.map +1 -1
  34. package/dist/otel/instrumentations/index.js +1 -1
  35. package/dist/otel/instrumentations/index.js.map +1 -1
  36. package/dist/patch/index.d.ts +1 -0
  37. package/dist/patch/index.js +1 -0
  38. package/dist/patch/index.js.map +1 -0
  39. package/dist/patch/xhr.d.ts +2 -0
  40. package/dist/patch/xhr.js +1 -0
  41. package/dist/patch/xhr.js.map +1 -0
  42. package/dist/recorder/gestureRecorder.js +1 -1
  43. package/dist/recorder/gestureRecorder.js.map +1 -1
  44. package/dist/recorder/navigationTracker.js +1 -1
  45. package/dist/recorder/navigationTracker.js.map +1 -1
  46. package/dist/recorder/screenRecorder.d.ts +1 -0
  47. package/dist/recorder/screenRecorder.js +1 -1
  48. package/dist/recorder/screenRecorder.js.map +1 -1
  49. package/dist/services/api.service.d.ts +62 -10
  50. package/dist/services/api.service.js +1 -1
  51. package/dist/services/api.service.js.map +1 -1
  52. package/dist/services/storage.service.d.ts +23 -16
  53. package/dist/services/storage.service.js +1 -1
  54. package/dist/services/storage.service.js.map +1 -1
  55. package/dist/session-recorder.d.ts +131 -0
  56. package/dist/session-recorder.js +1 -0
  57. package/dist/session-recorder.js.map +1 -0
  58. package/dist/sessionRecorder.d.ts +113 -34
  59. package/dist/sessionRecorder.js +1 -1
  60. package/dist/sessionRecorder.js.map +1 -1
  61. package/dist/types/index.d.ts +2 -81
  62. package/dist/types/index.js +1 -1
  63. package/dist/types/index.js.map +1 -1
  64. package/dist/types/session-recorder.d.ts +366 -0
  65. package/dist/types/session-recorder.js +1 -0
  66. package/dist/types/session-recorder.js.map +1 -0
  67. package/dist/types/session.d.ts +59 -0
  68. package/dist/types/session.js +1 -0
  69. package/dist/types/session.js.map +1 -0
  70. package/dist/utils/index.d.ts +5 -0
  71. package/dist/utils/index.js +1 -0
  72. package/dist/utils/index.js.map +1 -0
  73. package/dist/utils/platform.d.ts +5 -0
  74. package/dist/utils/platform.js +1 -1
  75. package/dist/utils/platform.js.map +1 -1
  76. package/dist/utils/request-utils.d.ts +21 -0
  77. package/dist/utils/request-utils.js +1 -0
  78. package/dist/utils/request-utils.js.map +1 -0
  79. package/dist/utils/session.d.ts +5 -0
  80. package/dist/utils/session.js +1 -0
  81. package/dist/utils/session.js.map +1 -0
  82. package/dist/utils/time.d.ts +4 -0
  83. package/dist/utils/time.js +1 -0
  84. package/dist/utils/time.js.map +1 -0
  85. package/dist/utils/type-utils.d.ts +16 -0
  86. package/dist/utils/type-utils.js +1 -0
  87. package/dist/utils/type-utils.js.map +1 -0
  88. package/dist/version.d.ts +1 -1
  89. package/dist/version.js +1 -1
  90. package/package.json +3 -3
  91. package/src/config/constants.ts +60 -0
  92. package/src/config/defaults.ts +82 -0
  93. package/src/config/index.ts +6 -0
  94. package/src/config/masking.ts +10 -61
  95. package/src/config/session-recorder.ts +55 -0
  96. package/src/config/validators.ts +31 -0
  97. package/src/expo.ts +2 -22
  98. package/src/index.ts +3 -15
  99. package/src/otel/helpers.ts +247 -11
  100. package/src/otel/index.ts +109 -76
  101. package/src/otel/instrumentations/index.ts +8 -8
  102. package/src/patch/index.ts +1 -0
  103. package/src/patch/xhr.ts +142 -0
  104. package/src/recorder/gestureRecorder.ts +12 -12
  105. package/src/recorder/navigationTracker.ts +10 -10
  106. package/src/recorder/screenRecorder.ts +26 -26
  107. package/src/services/api.service.ts +169 -37
  108. package/src/services/storage.service.ts +101 -74
  109. package/src/session-recorder.ts +570 -0
  110. package/src/types/index.ts +2 -88
  111. package/src/types/session-recorder.ts +423 -0
  112. package/src/types/session.ts +65 -0
  113. package/src/utils/index.ts +6 -0
  114. package/src/utils/platform.ts +13 -0
  115. package/src/utils/request-utils.ts +61 -0
  116. package/src/utils/session.ts +18 -0
  117. package/src/utils/time.ts +17 -0
  118. package/src/utils/type-utils.ts +75 -0
  119. package/src/version.ts +1 -1
  120. package/examples/sample-expo-app/README.md +0 -142
  121. package/examples/sample-expo-app/app/(tabs)/_layout.tsx +0 -60
  122. package/examples/sample-expo-app/app/(tabs)/explore.tsx +0 -110
  123. package/examples/sample-expo-app/app/(tabs)/index.tsx +0 -125
  124. package/examples/sample-expo-app/app/(tabs)/posts.tsx +0 -96
  125. package/examples/sample-expo-app/app/(tabs)/users.tsx +0 -131
  126. package/examples/sample-expo-app/app/+not-found.tsx +0 -32
  127. package/examples/sample-expo-app/app/_layout.tsx +0 -53
  128. package/examples/sample-expo-app/app/post/[id].tsx +0 -199
  129. package/examples/sample-expo-app/app/user/[id].tsx +0 -270
  130. package/examples/sample-expo-app/app.json +0 -42
  131. package/examples/sample-expo-app/assets/fonts/SpaceMono-Regular.ttf +0 -0
  132. package/examples/sample-expo-app/assets/images/adaptive-icon.png +0 -0
  133. package/examples/sample-expo-app/assets/images/favicon.png +0 -0
  134. package/examples/sample-expo-app/assets/images/icon.png +0 -0
  135. package/examples/sample-expo-app/assets/images/partial-react-logo.png +0 -0
  136. package/examples/sample-expo-app/assets/images/react-logo.png +0 -0
  137. package/examples/sample-expo-app/assets/images/react-logo@2x.png +0 -0
  138. package/examples/sample-expo-app/assets/images/react-logo@3x.png +0 -0
  139. package/examples/sample-expo-app/assets/images/splash-icon.png +0 -0
  140. package/examples/sample-expo-app/components/Collapsible.tsx +0 -45
  141. package/examples/sample-expo-app/components/ErrorView.tsx +0 -52
  142. package/examples/sample-expo-app/components/ExternalLink.tsx +0 -24
  143. package/examples/sample-expo-app/components/HapticTab.tsx +0 -18
  144. package/examples/sample-expo-app/components/HelloWave.tsx +0 -40
  145. package/examples/sample-expo-app/components/LoadingSpinner.tsx +0 -34
  146. package/examples/sample-expo-app/components/ParallaxScrollView.tsx +0 -82
  147. package/examples/sample-expo-app/components/ThemedText.tsx +0 -60
  148. package/examples/sample-expo-app/components/ThemedView.tsx +0 -14
  149. package/examples/sample-expo-app/components/ui/IconSymbol.ios.tsx +0 -32
  150. package/examples/sample-expo-app/components/ui/IconSymbol.tsx +0 -41
  151. package/examples/sample-expo-app/components/ui/TabBarBackground.ios.tsx +0 -19
  152. package/examples/sample-expo-app/components/ui/TabBarBackground.tsx +0 -6
  153. package/examples/sample-expo-app/constants/Colors.ts +0 -26
  154. package/examples/sample-expo-app/eslint.config.js +0 -10
  155. package/examples/sample-expo-app/hooks/useApi.ts +0 -41
  156. package/examples/sample-expo-app/hooks/useColorScheme.ts +0 -1
  157. package/examples/sample-expo-app/hooks/useColorScheme.web.ts +0 -21
  158. package/examples/sample-expo-app/hooks/useThemeColor.ts +0 -21
  159. package/examples/sample-expo-app/metro.config.js +0 -26
  160. package/examples/sample-expo-app/package-lock.json +0 -26296
  161. package/examples/sample-expo-app/package.json +0 -59
  162. package/examples/sample-expo-app/scripts/reset-project.js +0 -112
  163. package/examples/sample-expo-app/services/api.ts +0 -98
  164. package/examples/sample-expo-app/tsconfig.json +0 -17
  165. package/examples/sample-expo-app/utils/navigation.ts +0 -19
  166. package/src/sessionRecorder.ts +0 -367
package/src/otel/index.ts CHANGED
@@ -8,95 +8,54 @@ import {
8
8
  ATTR_MULTIPLAYER_SESSION_ID,
9
9
  SessionRecorderIdGenerator,
10
10
  SessionRecorderTraceIdRatioBasedSampler,
11
+ SessionRecorderBrowserTraceExporter,
11
12
  } from '@multiplayer-app/session-recorder-common'
12
13
  import { TracerReactNativeConfig } from '../types'
13
14
  import { getInstrumentations } from './instrumentations'
14
- import { getExporterEndpoint } from './helpers'
15
+ import { getExporterEndpoint, processHttpPayload, headersToObject } from './helpers'
15
16
  import { ReactNavigationInstrumentation } from './instrumentations/reactNavigationInstrumentation'
16
17
  import { GestureInstrumentation } from './instrumentations/gestureInstrumentation'
17
- import { getHttpMaskingConfig } from '../config/masking'
18
18
  import { getPlatformAttributes } from '../utils/platform'
19
+ import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'
19
20
 
20
21
 
21
- // Create a custom TracerProvider for React Native
22
- class ReactNativeTracerProvider {
23
- private resource: any
24
- private idGenerator: any
25
- private sampler: any
26
- private spanProcessors: any[]
27
- private propagator: any
28
-
29
- constructor(options: any) {
30
- this.resource = options.resource
31
- this.idGenerator = options.idGenerator
32
- this.sampler = options.sampler
33
- this.spanProcessors = options.spanProcessors
34
- this.propagator = options.propagator
35
- }
36
-
37
- register(options: any) {
38
- // Register the provider with OpenTelemetry
39
- console.log('ReactNativeTracerProvider registered')
40
- }
41
22
 
42
- getTracer(name: string, version?: string, options?: any): any {
43
- // Return a mock tracer for now
44
- return {
45
- startSpan: (name: string, options?: any) => ({
46
- setAttribute: (key: string, value: any) => { },
47
- setStatus: (status: any) => { },
48
- end: () => { },
49
- recordException: (error: Error) => { },
50
- }),
51
- }
52
- }
53
- }
54
23
 
55
24
  export class TracerReactNativeSDK {
56
- private tracerProvider?: ReactNativeTracerProvider
25
+ private tracerProvider?: WebTracerProvider
57
26
  private config?: TracerReactNativeConfig
58
- private httpMaskingConfig: any
27
+
59
28
  private sessionId = ''
60
- private idGenerator: SessionRecorderIdGenerator
29
+ private idGenerator?: SessionRecorderIdGenerator
61
30
  private exporter?: any
62
- private navigationInstrumentation: ReactNavigationInstrumentation
63
- private gestureInstrumentation: GestureInstrumentation
31
+ private navigationInstrumentation?: ReactNavigationInstrumentation
32
+ private gestureInstrumentation?: GestureInstrumentation
64
33
  private isInitialized = false
65
34
 
66
- constructor() {
67
- this.idGenerator = new SessionRecorderIdGenerator()
68
- this.navigationInstrumentation = new ReactNavigationInstrumentation()
69
- this.gestureInstrumentation = new GestureInstrumentation()
70
- }
35
+ constructor() { }
71
36
 
72
37
  private _setSessionId(
73
38
  sessionId: string,
74
39
  sessionType: SessionType = SessionType.PLAIN,
75
40
  ) {
76
41
  this.sessionId = sessionId
77
- this.idGenerator.setSessionId(sessionId, sessionType)
42
+ this.idGenerator?.setSessionId(sessionId, sessionType)
78
43
  }
79
44
 
80
45
  init(options: TracerReactNativeConfig): void {
81
- if (this.isInitialized) {
82
- console.warn('TracerReactNativeSDK already initialized')
83
- return
84
- }
85
-
86
46
  this.config = options
87
- this.httpMaskingConfig = getHttpMaskingConfig(options.httpMasking)
47
+
88
48
  const { application, version, environment } = this.config
89
49
 
90
- // Create OTLP exporter
91
- const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http')
92
- this.exporter = new OTLPTraceExporter({
50
+ this.idGenerator = new SessionRecorderIdGenerator()
51
+
52
+ this.exporter = new SessionRecorderBrowserTraceExporter({
53
+ apiKey: options.apiKey,
93
54
  url: getExporterEndpoint(options.exporterEndpoint),
94
- headers: {
95
- 'Authorization': `Bearer ${options.apiKey}`,
96
- },
55
+ usePostMessageFallback: options.usePostMessageFallback,
97
56
  })
98
57
 
99
- this.tracerProvider = new ReactNativeTracerProvider({
58
+ this.tracerProvider = new WebTracerProvider({
100
59
  resource: resourceFromAttributes({
101
60
  [SemanticAttributes.SEMRESATTRS_SERVICE_NAME]: application,
102
61
  [SemanticAttributes.SEMRESATTRS_SERVICE_VERSION]: version,
@@ -121,8 +80,11 @@ export class TracerReactNativeSDK {
121
80
  instrumentations: getInstrumentations(this.config),
122
81
  })
123
82
 
83
+ // Initialize React Native specific instrumentations
84
+ this.navigationInstrumentation = new ReactNavigationInstrumentation()
85
+ this.gestureInstrumentation = new GestureInstrumentation()
86
+
124
87
  this.isInitialized = true
125
- console.log('TracerReactNativeSDK initialized successfully')
126
88
  }
127
89
 
128
90
  private _getSpanSessionIdProcessor() {
@@ -141,64 +103,94 @@ export class TracerReactNativeSDK {
141
103
  }
142
104
  }
143
105
 
106
+ start(sessionId: string, sessionType: SessionType): void {
107
+ if (!this.tracerProvider) {
108
+ throw new Error(
109
+ 'Configuration not initialized. Call init() before start().',
110
+ )
111
+ }
112
+
113
+ this._setSessionId(sessionId, sessionType)
114
+ }
115
+
116
+ stop(): void {
117
+ if (!this.tracerProvider) {
118
+ throw new Error(
119
+ 'Configuration not initialized. Call init() before start().',
120
+ )
121
+ }
122
+
123
+ this._setSessionId('')
124
+ }
125
+
126
+ setApiKey(apiKey: string): void {
127
+ if (!this.exporter) {
128
+ throw new Error(
129
+ 'Configuration not initialized. Call init() before setApiKey().',
130
+ )
131
+ }
132
+
133
+ this.exporter.setApiKey?.(apiKey)
134
+ }
135
+
144
136
  setSessionId(sessionId: string, sessionType: SessionType): void {
145
137
  this._setSessionId(sessionId, sessionType)
146
138
  }
147
139
 
148
140
  // Navigation instrumentation methods
149
141
  setNavigationRef(ref: any): void {
150
- this.navigationInstrumentation.setNavigationRef(ref)
142
+ this.navigationInstrumentation?.setNavigationRef(ref)
151
143
  }
152
144
 
153
145
  recordNavigate(routeName: string, params?: Record<string, any>): void {
154
- this.navigationInstrumentation.recordNavigate(routeName, params)
146
+ this.navigationInstrumentation?.recordNavigate(routeName, params)
155
147
  }
156
148
 
157
149
  recordGoBack(): void {
158
- this.navigationInstrumentation.recordGoBack()
150
+ this.navigationInstrumentation?.recordGoBack()
159
151
  }
160
152
 
161
153
  recordReset(routes: any[]): void {
162
- this.navigationInstrumentation.recordReset(routes)
154
+ this.navigationInstrumentation?.recordReset(routes)
163
155
  }
164
156
 
165
157
  // Gesture instrumentation methods
166
158
  enableGestureTracking(): void {
167
- this.gestureInstrumentation.enable()
159
+ this.gestureInstrumentation?.enable()
168
160
  }
169
161
 
170
162
  disableGestureTracking(): void {
171
- this.gestureInstrumentation.disable()
163
+ this.gestureInstrumentation?.disable()
172
164
  }
173
165
 
174
166
  recordTap(x: number, y: number, target?: string): void {
175
- this.gestureInstrumentation.recordTap(x, y, target)
167
+ this.gestureInstrumentation?.recordTap(x, y, target)
176
168
  }
177
169
 
178
170
  recordSwipe(direction: string, target?: string): void {
179
- this.gestureInstrumentation.recordSwipe(direction, target)
171
+ this.gestureInstrumentation?.recordSwipe(direction, target)
180
172
  }
181
173
 
182
174
  recordPinch(scale: number, target?: string): void {
183
- this.gestureInstrumentation.recordPinch(scale, target)
175
+ this.gestureInstrumentation?.recordPinch(scale, target)
184
176
  }
185
177
 
186
178
  recordPan(deltaX: number, deltaY: number, target?: string): void {
187
- this.gestureInstrumentation.recordPan(deltaX, deltaY, target)
179
+ this.gestureInstrumentation?.recordPan(deltaX, deltaY, target)
188
180
  }
189
181
 
190
182
  recordLongPress(duration: number, target?: string): void {
191
- this.gestureInstrumentation.recordLongPress(duration, target)
183
+ this.gestureInstrumentation?.recordLongPress(duration, target)
192
184
  }
193
185
 
194
186
  recordGestureError(error: Error, gestureType: string): void {
195
- this.gestureInstrumentation.recordGestureError(error, gestureType)
187
+ this.gestureInstrumentation?.recordGestureError(error, gestureType)
196
188
  }
197
189
 
198
190
  // Performance monitoring methods
199
191
  startTrace(name: string, attributes?: Record<string, any>): any {
200
192
  if (!this.isInitialized) {
201
- console.warn('TracerReactNativeSDK not initialized')
193
+ // Tracer not initialized - silently return
202
194
  return null
203
195
  }
204
196
 
@@ -242,8 +234,8 @@ export class TracerReactNativeSDK {
242
234
  span.end()
243
235
  }
244
236
 
245
- // Network monitoring
246
- recordNetworkRequest(url: string, method: string, statusCode: number, duration: number): void {
237
+ // Network monitoring with enhanced HTTP payload processing
238
+ recordNetworkRequest(url: string, method: string, statusCode: number, duration: number, payload?: any): void {
247
239
  if (!this.isInitialized) return
248
240
 
249
241
  const { trace } = require('@opentelemetry/api')
@@ -257,6 +249,48 @@ export class TracerReactNativeSDK {
257
249
  },
258
250
  })
259
251
 
252
+ // Process HTTP payload if provided
253
+ if (payload && this.config) {
254
+ processHttpPayload(payload, this.config, span)
255
+ }
256
+
257
+ span.setStatus({ code: statusCode >= 400 ? 2 : 1 })
258
+ span.end()
259
+ }
260
+
261
+ // Enhanced HTTP request recording with headers and body
262
+ recordHttpRequest(url: string, method: string, requestHeaders?: Record<string, string>, requestBody?: any): any {
263
+ if (!this.isInitialized) return null
264
+
265
+ const { trace } = require('@opentelemetry/api')
266
+ const span = trace.startSpan('http.request', {
267
+ attributes: {
268
+ 'http.url': url,
269
+ 'http.method': method,
270
+ 'http.request_headers': headersToObject(requestHeaders),
271
+ },
272
+ })
273
+
274
+ // Process request payload
275
+ if (requestBody && this.config) {
276
+ processHttpPayload({ requestBody, requestHeaders }, this.config, span)
277
+ }
278
+
279
+ return span
280
+ }
281
+
282
+ // Enhanced HTTP response recording
283
+ recordHttpResponse(span: any, statusCode: number, responseHeaders?: Record<string, string>, responseBody?: any): void {
284
+ if (!span || !this.isInitialized) return
285
+
286
+ span.setAttribute('http.status_code', statusCode)
287
+ span.setAttribute('http.response_headers', headersToObject(responseHeaders))
288
+
289
+ // Process response payload
290
+ if (responseBody && this.config) {
291
+ processHttpPayload({ responseBody, responseHeaders }, this.config, span)
292
+ }
293
+
260
294
  span.setStatus({ code: statusCode >= 400 ? 2 : 1 })
261
295
  span.end()
262
296
  }
@@ -339,10 +373,9 @@ export class TracerReactNativeSDK {
339
373
  span.end()
340
374
  }
341
375
 
342
- // Shutdown
376
+ // Shutdown (React Native specific)
343
377
  shutdown(): Promise<void> {
344
378
  this.isInitialized = false
345
- console.log('TracerReactNativeSDK shutdown')
346
379
  return Promise.resolve()
347
380
  }
348
381
  }
@@ -1,9 +1,9 @@
1
1
  import { TracerReactNativeConfig } from '../../types'
2
2
  import { ReactNativeInstrumentation } from './reactNativeInstrumentation'
3
- import { getHttpMaskingConfig } from '../../config/masking'
3
+ import { getMaskingConfig } from '../../config/masking'
4
4
 
5
5
  export function getInstrumentations(config: TracerReactNativeConfig) {
6
- const httpMaskingConfig = getHttpMaskingConfig(config.httpMasking)
6
+ const masking = getMaskingConfig(config.masking)
7
7
  const instrumentations = []
8
8
 
9
9
  // Fetch instrumentation
@@ -16,8 +16,8 @@ export function getInstrumentations(config: TracerReactNativeConfig) {
16
16
  applyCustomAttributesOnSpan: (span: any, request: any) => {
17
17
  if (config.captureHeaders) {
18
18
  const headers = request.headers
19
- if (headers && httpMaskingConfig.maskHeaders) {
20
- const maskedHeaders = httpMaskingConfig.maskHeaders(headers, span)
19
+ if (headers && masking.maskHeaders) {
20
+ const maskedHeaders = masking.maskHeaders(headers, span)
21
21
  Object.keys(maskedHeaders).forEach(key => {
22
22
  span.setAttribute(`http.request.header.${key}`, maskedHeaders[key])
23
23
  })
@@ -29,8 +29,8 @@ export function getInstrumentations(config: TracerReactNativeConfig) {
29
29
  }
30
30
  },
31
31
  applyCustomAttributesOnSpanResponse: (span: any, response: any) => {
32
- if (config.captureBody && httpMaskingConfig.maskBody) {
33
- const maskedBody = httpMaskingConfig.maskBody(response.body, span)
32
+ if (config.captureBody && masking.maskBody) {
33
+ const maskedBody = masking.maskBody(response.body, span)
34
34
  span.setAttribute('http.response.body', JSON.stringify(maskedBody))
35
35
  } else if (config.captureBody) {
36
36
  span.setAttribute('http.response.body', JSON.stringify(response.body))
@@ -52,8 +52,8 @@ export function getInstrumentations(config: TracerReactNativeConfig) {
52
52
  applyCustomAttributesOnSpan: (span: any, xhr: any) => {
53
53
  if (config.captureHeaders) {
54
54
  const headers = xhr.getAllResponseHeaders()
55
- if (headers && httpMaskingConfig.maskHeaders) {
56
- const maskedHeaders = httpMaskingConfig.maskHeaders(headers, span)
55
+ if (headers && masking.maskHeaders) {
56
+ const maskedHeaders = masking.maskHeaders(headers, span)
57
57
  Object.keys(maskedHeaders).forEach(key => {
58
58
  span.setAttribute(`http.response.header.${key}`, maskedHeaders[key])
59
59
  })
@@ -0,0 +1 @@
1
+ import './xhr'
@@ -0,0 +1,142 @@
1
+ import {
2
+
3
+ isFormData,
4
+ isNullish,
5
+ isObject,
6
+ isString,
7
+ } from '../utils/type-utils'
8
+ import { formDataToQuery } from '../utils/request-utils'
9
+ import { DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE } from '../config'
10
+
11
+ let recordRequestHeaders = true
12
+ let recordResponseHeaders = true
13
+ const shouldRecordBody = true
14
+ let maxCapturingHttpPayloadSize = DEFAULT_MAX_HTTP_CAPTURING_PAYLOAD_SIZE
15
+
16
+ export const setMaxCapturingHttpPayloadSize = (_maxCapturingHttpPayloadSize: number) => {
17
+ maxCapturingHttpPayloadSize = _maxCapturingHttpPayloadSize
18
+ }
19
+
20
+ export const setShouldRecordHttpData = (shouldRecordBody: boolean, shouldRecordHeaders: boolean) => {
21
+ recordRequestHeaders = shouldRecordHeaders
22
+ recordResponseHeaders = shouldRecordHeaders
23
+ // eslint-disable-next-line
24
+ shouldRecordBody = shouldRecordBody
25
+ }
26
+
27
+ function _tryReadXHRBody({
28
+ body,
29
+ url,
30
+ }: {
31
+ body: any | null | undefined
32
+ url: string | URL | RequestInfo
33
+ }): string | null {
34
+ if (isNullish(body)) {
35
+ return null
36
+ }
37
+
38
+ if (isString(body)) {
39
+ return body
40
+ }
41
+
42
+
43
+ if (isFormData(body)) {
44
+ return formDataToQuery(body)
45
+ }
46
+
47
+ if (isObject(body)) {
48
+ try {
49
+ return JSON.stringify(body)
50
+ } catch {
51
+ return '[XHR] Failed to stringify response object'
52
+ }
53
+ }
54
+
55
+ return `[XHR] Cannot read body of type ${Object.prototype.toString.call(body)}`
56
+ }
57
+
58
+ (function (xhr) {
59
+ const originalOpen = XMLHttpRequest.prototype.open
60
+
61
+ xhr.open = function (
62
+ method: string,
63
+ url: string | URL,
64
+ async = true,
65
+ username?: string | null,
66
+ password?: string | null,
67
+ ) {
68
+ const xhr = this as XMLHttpRequest
69
+ const networkRequest: {
70
+ requestHeaders?: any,
71
+ requestBody?: any,
72
+ responseHeaders?: any,
73
+ responseBody?: any,
74
+ } = {}
75
+
76
+
77
+ // @ts-ignore
78
+ const requestHeaders: Record<string, string> = {}
79
+ const originalSetRequestHeader = xhr.setRequestHeader.bind(xhr)
80
+ xhr.setRequestHeader = (header: string, value: string) => {
81
+ requestHeaders[header] = value
82
+ return originalSetRequestHeader(header, value)
83
+ }
84
+ if (recordRequestHeaders) {
85
+ networkRequest.requestHeaders = requestHeaders
86
+ }
87
+
88
+ const originalSend = xhr.send.bind(xhr)
89
+ xhr.send = (body) => {
90
+ if (shouldRecordBody) {
91
+ const requestBody = _tryReadXHRBody({ body, url })
92
+
93
+ if (
94
+ requestBody?.length
95
+ && new Blob([requestBody]).size <= maxCapturingHttpPayloadSize
96
+ ) {
97
+ networkRequest.requestBody = requestBody
98
+ }
99
+ }
100
+ return originalSend(body)
101
+ }
102
+
103
+ xhr.addEventListener('readystatechange', () => {
104
+ if (xhr.readyState !== xhr.DONE) {
105
+ return
106
+ }
107
+
108
+
109
+ // @ts-ignore
110
+ const responseHeaders: Record<string, string> = {}
111
+ const rawHeaders = xhr.getAllResponseHeaders()
112
+ const headers = rawHeaders.trim().split(/[\r\n]+/)
113
+ headers.forEach((line) => {
114
+ const parts = line.split(': ')
115
+ const header = parts.shift()
116
+ const value = parts.join(': ')
117
+ if (header) {
118
+ responseHeaders[header] = value
119
+ }
120
+ })
121
+ if (recordResponseHeaders) {
122
+ networkRequest.responseHeaders = responseHeaders
123
+ }
124
+ if (shouldRecordBody) {
125
+ const responseBody = _tryReadXHRBody({ body: xhr.response, url })
126
+
127
+ if (
128
+ responseBody?.length
129
+ && new Blob([responseBody]).size <= maxCapturingHttpPayloadSize
130
+ ) {
131
+ networkRequest.responseBody = responseBody
132
+ }
133
+ }
134
+ })
135
+
136
+
137
+ // @ts-ignore
138
+ xhr.networkRequest = networkRequest
139
+
140
+ originalOpen.call(xhr, method, url as string, async, username, password)
141
+ }
142
+ })(XMLHttpRequest.prototype)
@@ -18,13 +18,13 @@ export class GestureRecorder {
18
18
  this.isRecording = true
19
19
  this.events = []
20
20
  this._setupGestureHandlers()
21
- console.log('Gesture recording started')
21
+ // Gesture recording started
22
22
  }
23
23
 
24
24
  stop(): void {
25
25
  this.isRecording = false
26
26
  this._removeGestureHandlers()
27
- console.log('Gesture recording stopped')
27
+ // Gesture recording stopped
28
28
  }
29
29
 
30
30
  pause(): void {
@@ -42,7 +42,7 @@ export class GestureRecorder {
42
42
  const { Dimensions } = require('react-native')
43
43
  this.screenDimensions = Dimensions.get('window')
44
44
  } catch (error) {
45
- console.warn('Failed to get screen dimensions:', error)
45
+ // Failed to get screen dimensions - silently continue
46
46
  this.screenDimensions = { width: 375, height: 667 } // Default fallback
47
47
  }
48
48
  }
@@ -52,16 +52,16 @@ export class GestureRecorder {
52
52
  try {
53
53
  // This would integrate with React Native's component tracking
54
54
  // For now, we'll provide methods for manual registration
55
- console.log('Input tracking setup complete')
55
+ // Input tracking setup complete
56
56
  } catch (error) {
57
- console.warn('Failed to setup input tracking:', error)
57
+ // Failed to setup input tracking - silently continue
58
58
  }
59
59
  }
60
60
 
61
61
  private _setupGestureHandlers(): void {
62
62
  // This would integrate with react-native-gesture-handler
63
63
  // For now, we'll create a comprehensive implementation that can be easily integrated
64
- console.log('Setting up gesture handlers')
64
+ // Setting up gesture handlers
65
65
 
66
66
  // Set up global gesture listener
67
67
  this._setupGlobalGestureListener()
@@ -73,15 +73,15 @@ export class GestureRecorder {
73
73
  const { TouchableWithoutFeedback } = require('react-native')
74
74
 
75
75
  // This is a simplified implementation - in production you'd use react-native-gesture-handler
76
- console.log('Global gesture listener setup complete')
76
+ // Global gesture listener setup complete
77
77
  } catch (error) {
78
- console.warn('Failed to setup global gesture listener:', error)
78
+ // Failed to setup global gesture listener - silently continue
79
79
  }
80
80
  }
81
81
 
82
82
  private _removeGestureHandlers(): void {
83
83
  this.gestureHandlers.clear()
84
- console.log('Gesture handlers removed')
84
+ // Gesture handlers removed
85
85
  }
86
86
 
87
87
  private _recordEvent(event: GestureEvent): void {
@@ -103,7 +103,7 @@ export class GestureRecorder {
103
103
 
104
104
  private _sendEvent(event: GestureEvent): void {
105
105
  // Send event to backend or store locally
106
- console.log('Gesture event recorded:', event)
106
+ // Gesture event recorded
107
107
  }
108
108
 
109
109
  private _recordOpenTelemetrySpan(event: GestureEvent): void {
@@ -142,7 +142,7 @@ export class GestureRecorder {
142
142
  span.setStatus({ code: SpanStatusCode.OK })
143
143
  span.end()
144
144
  } catch (error) {
145
- console.warn('Failed to record OpenTelemetry span for gesture:', error)
145
+ // Failed to record OpenTelemetry span for gesture - silently continue
146
146
  }
147
147
  }
148
148
 
@@ -376,7 +376,7 @@ export class GestureRecorder {
376
376
  span.recordException(error)
377
377
  span.end()
378
378
  } catch (spanError) {
379
- console.warn('Failed to record error span:', spanError)
379
+ // Failed to record error span - silently continue
380
380
  }
381
381
  }
382
382
 
@@ -28,13 +28,13 @@ export class NavigationTracker {
28
28
  this.navigationStack = []
29
29
  this.navigationStartTime = Date.now()
30
30
  this._setupNavigationListener()
31
- console.log('Navigation tracking started')
31
+ // Navigation tracking started
32
32
  }
33
33
 
34
34
  stop(): void {
35
35
  this.isRecording = false
36
36
  this._removeNavigationListener()
37
- console.log('Navigation tracking stopped')
37
+ // Navigation tracking stopped
38
38
  }
39
39
 
40
40
  pause(): void {
@@ -48,7 +48,7 @@ export class NavigationTracker {
48
48
 
49
49
  private _setupNavigationListener(): void {
50
50
  if (!this.navigationRef) {
51
- console.warn('Navigation ref not set')
51
+ // Navigation ref not set - silently continue
52
52
  return
53
53
  }
54
54
 
@@ -79,9 +79,9 @@ export class NavigationTracker {
79
79
  this.navigationListeners.set('blur', blurListener)
80
80
  this.navigationListeners.set('beforeRemove', beforeRemoveListener)
81
81
 
82
- console.log('Navigation listeners setup complete')
82
+ // Navigation listeners setup complete
83
83
  } catch (error) {
84
- console.warn('Failed to setup navigation listeners:', error)
84
+ // Failed to setup navigation listeners - silently continue
85
85
  }
86
86
  }
87
87
 
@@ -94,9 +94,9 @@ export class NavigationTracker {
94
94
  }
95
95
  })
96
96
  this.navigationListeners.clear()
97
- console.log('Navigation listeners removed')
97
+ // Navigation listeners removed
98
98
  } catch (error) {
99
- console.warn('Failed to remove navigation listeners:', error)
99
+ // Failed to remove navigation listeners - silently continue
100
100
  }
101
101
  }
102
102
 
@@ -148,7 +148,7 @@ export class NavigationTracker {
148
148
  }
149
149
 
150
150
  private _sendEvent(event: NavigationEvent): void {
151
- console.log('Navigation event recorded:', event)
151
+ // Navigation event recorded
152
152
  }
153
153
 
154
154
  private _recordOpenTelemetrySpan(event: NavigationEvent): void {
@@ -178,7 +178,7 @@ export class NavigationTracker {
178
178
  span.setStatus({ code: SpanStatusCode.OK })
179
179
  span.end()
180
180
  } catch (error) {
181
- console.warn('Failed to record OpenTelemetry span for navigation:', error)
181
+ // Failed to record OpenTelemetry span for navigation - silently continue
182
182
  }
183
183
  }
184
184
 
@@ -399,7 +399,7 @@ export class NavigationTracker {
399
399
  span.recordException(error)
400
400
  span.end()
401
401
  } catch (spanError) {
402
- console.warn('Failed to record error span:', spanError)
402
+ // Failed to record error span - silently continue
403
403
  }
404
404
  }
405
405