@multiplayer-app/session-recorder-react-native 0.0.1-beta.1 → 0.0.1-beta.11

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 (162) hide show
  1. package/SessionRecorderNative.podspec +29 -0
  2. package/android/build.gradle +32 -0
  3. package/copy-react-native-dist.sh +4 -10
  4. package/dist/components/MaskableComponent.d.ts +22 -0
  5. package/dist/components/MaskableComponent.js +1 -0
  6. package/dist/components/MaskableComponent.js.map +1 -0
  7. package/dist/components/MaskableTextInput.d.ts +14 -0
  8. package/dist/components/MaskableTextInput.js +1 -0
  9. package/dist/components/MaskableTextInput.js.map +1 -0
  10. package/dist/components/SessionRecorderWidget/FinalPopover.d.ts +11 -0
  11. package/dist/components/SessionRecorderWidget/FinalPopover.js +1 -0
  12. package/dist/components/SessionRecorderWidget/FinalPopover.js.map +1 -0
  13. package/dist/components/SessionRecorderWidget/FloatingButton.d.ts +8 -0
  14. package/dist/components/SessionRecorderWidget/FloatingButton.js +1 -0
  15. package/dist/components/SessionRecorderWidget/FloatingButton.js.map +1 -0
  16. package/dist/components/SessionRecorderWidget/InitialPopover.d.ts +13 -0
  17. package/dist/components/SessionRecorderWidget/InitialPopover.js +1 -0
  18. package/dist/components/SessionRecorderWidget/InitialPopover.js.map +1 -0
  19. package/dist/components/SessionRecorderWidget/ModalContainer.d.ts +8 -0
  20. package/dist/components/SessionRecorderWidget/ModalContainer.js +1 -0
  21. package/dist/components/SessionRecorderWidget/ModalContainer.js.map +1 -0
  22. package/dist/components/SessionRecorderWidget/ModalHeader.d.ts +6 -0
  23. package/dist/components/SessionRecorderWidget/ModalHeader.js +1 -0
  24. package/dist/components/SessionRecorderWidget/ModalHeader.js.map +1 -0
  25. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.d.ts +5 -0
  26. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js +1 -0
  27. package/dist/components/SessionRecorderWidget/SessionRecorderWidget.js.map +1 -0
  28. package/dist/components/SessionRecorderWidget/icons.d.ts +11 -0
  29. package/dist/components/SessionRecorderWidget/icons.js +1 -0
  30. package/dist/components/SessionRecorderWidget/icons.js.map +1 -0
  31. package/dist/components/SessionRecorderWidget/index.d.ts +2 -0
  32. package/dist/components/SessionRecorderWidget/index.js +1 -0
  33. package/dist/components/SessionRecorderWidget/index.js.map +1 -0
  34. package/dist/components/SessionRecorderWidget/styles.d.ts +145 -0
  35. package/dist/components/SessionRecorderWidget/styles.js +1 -0
  36. package/dist/components/SessionRecorderWidget/styles.js.map +1 -0
  37. package/dist/components/index.d.ts +2 -0
  38. package/dist/components/index.js +1 -1
  39. package/dist/components/index.js.map +1 -1
  40. package/dist/config/defaults.js +1 -1
  41. package/dist/config/defaults.js.map +1 -1
  42. package/dist/config/masking.js +1 -1
  43. package/dist/config/masking.js.map +1 -1
  44. package/dist/context/SessionRecorderContext.d.ts +5 -3
  45. package/dist/context/SessionRecorderContext.js +1 -1
  46. package/dist/context/SessionRecorderContext.js.map +1 -1
  47. package/dist/index.d.ts +0 -1
  48. package/dist/index.js +1 -1
  49. package/dist/index.js.map +1 -1
  50. package/dist/native/ScreenMasking.d.ts +21 -0
  51. package/dist/native/ScreenMasking.js +1 -0
  52. package/dist/native/ScreenMasking.js.map +1 -0
  53. package/dist/native/SessionRecorderNative.d.ts +21 -0
  54. package/dist/native/SessionRecorderNative.js +1 -0
  55. package/dist/native/SessionRecorderNative.js.map +1 -0
  56. package/dist/patch/xhr.js +1 -1
  57. package/dist/patch/xhr.js.map +1 -1
  58. package/dist/recorder/screenRecorder.d.ts +1 -0
  59. package/dist/recorder/screenRecorder.js +1 -1
  60. package/dist/recorder/screenRecorder.js.map +1 -1
  61. package/dist/recorder/screenshotManager.d.ts +10 -0
  62. package/dist/recorder/screenshotManager.js +1 -0
  63. package/dist/recorder/screenshotManager.js.map +1 -0
  64. package/dist/services/screenMaskingService.d.ts +39 -0
  65. package/dist/services/screenMaskingService.js +1 -0
  66. package/dist/services/screenMaskingService.js.map +1 -0
  67. package/dist/services/storage.service.d.ts +18 -2
  68. package/dist/services/storage.service.js +1 -1
  69. package/dist/services/storage.service.js.map +1 -1
  70. package/dist/session-recorder.d.ts +2 -1
  71. package/dist/session-recorder.js +1 -1
  72. package/dist/session-recorder.js.map +1 -1
  73. package/dist/types/session-recorder.d.ts +6 -0
  74. package/dist/types/session-recorder.js.map +1 -1
  75. package/dist/utils/componentRegistry.d.ts +64 -0
  76. package/dist/utils/componentRegistry.js +1 -0
  77. package/dist/utils/componentRegistry.js.map +1 -0
  78. package/dist/utils/nativeModuleTest.d.ts +8 -0
  79. package/dist/utils/nativeModuleTest.js +1 -0
  80. package/dist/utils/nativeModuleTest.js.map +1 -0
  81. package/dist/utils/platform.d.ts +3 -0
  82. package/dist/utils/reactNativeHierarchyExtractor.d.ts +38 -0
  83. package/dist/utils/reactNativeHierarchyExtractor.js +1 -0
  84. package/dist/utils/reactNativeHierarchyExtractor.js.map +1 -0
  85. package/dist/utils/screenshotMasker.d.ts +96 -0
  86. package/dist/utils/screenshotMasker.js +1 -0
  87. package/dist/utils/screenshotMasker.js.map +1 -0
  88. package/dist/utils/viewHierarchyTracker.d.ts +89 -0
  89. package/dist/utils/viewHierarchyTracker.js +1 -0
  90. package/dist/utils/viewHierarchyTracker.js.map +1 -0
  91. package/ios/SessionRecorderNative.m +17 -0
  92. package/ios/SessionRecorderNative.podspec +26 -0
  93. package/ios/SessionRecorderNative.swift +205 -0
  94. package/package.json +22 -7
  95. package/react-native.config.js +15 -0
  96. package/RRWEB_INTEGRATION.md +0 -336
  97. package/VIEWSHOT_INTEGRATION_TEST.md +0 -123
  98. package/babel.config.js +0 -13
  99. package/dist/components/GestureCaptureWrapper.d.ts +0 -6
  100. package/dist/components/GestureCaptureWrapper.js +0 -1
  101. package/dist/components/GestureCaptureWrapper.js.map +0 -1
  102. package/dist/expo.d.ts +0 -7
  103. package/dist/expo.js +0 -1
  104. package/dist/expo.js.map +0 -1
  105. package/dist/otel/instrumentations/gestureInstrumentation.d.ts +0 -15
  106. package/dist/otel/instrumentations/gestureInstrumentation.js +0 -1
  107. package/dist/otel/instrumentations/gestureInstrumentation.js.map +0 -1
  108. package/dist/otel/instrumentations/reactNativeInstrumentation.d.ts +0 -8
  109. package/dist/otel/instrumentations/reactNativeInstrumentation.js +0 -1
  110. package/dist/otel/instrumentations/reactNativeInstrumentation.js.map +0 -1
  111. package/dist/otel/instrumentations/reactNavigationInstrumentation.d.ts +0 -13
  112. package/dist/otel/instrumentations/reactNavigationInstrumentation.js +0 -1
  113. package/dist/otel/instrumentations/reactNavigationInstrumentation.js.map +0 -1
  114. package/dist/recorder/gestureHandlerRecorder.d.ts +0 -19
  115. package/dist/recorder/gestureHandlerRecorder.js +0 -1
  116. package/dist/recorder/gestureHandlerRecorder.js.map +0 -1
  117. package/dist/types/rrweb.d.ts +0 -118
  118. package/dist/types/rrweb.js +0 -1
  119. package/dist/types/rrweb.js.map +0 -1
  120. package/scripts/generate-app-metadata.js +0 -173
  121. package/src/components/GestureCaptureWrapper/GestureCaptureWrapper.tsx +0 -86
  122. package/src/components/GestureCaptureWrapper/index.ts +0 -1
  123. package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +0 -72
  124. package/src/components/ScreenRecorderView/index.ts +0 -1
  125. package/src/components/index.ts +0 -1
  126. package/src/config/constants.ts +0 -60
  127. package/src/config/defaults.ts +0 -82
  128. package/src/config/index.ts +0 -6
  129. package/src/config/masking.ts +0 -27
  130. package/src/config/session-recorder.ts +0 -55
  131. package/src/config/validators.ts +0 -31
  132. package/src/context/SessionRecorderContext.tsx +0 -75
  133. package/src/expo.ts +0 -11
  134. package/src/index.ts +0 -17
  135. package/src/otel/helpers.ts +0 -275
  136. package/src/otel/index.ts +0 -138
  137. package/src/otel/instrumentations/index.ts +0 -115
  138. package/src/patch/index.ts +0 -1
  139. package/src/patch/xhr.ts +0 -142
  140. package/src/recorder/eventExporter.ts +0 -141
  141. package/src/recorder/gestureRecorder.ts +0 -498
  142. package/src/recorder/index.ts +0 -179
  143. package/src/recorder/navigationTracker.ts +0 -449
  144. package/src/recorder/screenRecorder.ts +0 -498
  145. package/src/services/api.service.ts +0 -203
  146. package/src/services/storage.service.ts +0 -158
  147. package/src/session-recorder.ts +0 -600
  148. package/src/types/expo.d.ts +0 -23
  149. package/src/types/index.ts +0 -28
  150. package/src/types/session-recorder.ts +0 -423
  151. package/src/types/session.ts +0 -65
  152. package/src/utils/app-metadata.ts +0 -31
  153. package/src/utils/index.ts +0 -8
  154. package/src/utils/logger.ts +0 -225
  155. package/src/utils/platform.ts +0 -384
  156. package/src/utils/request-utils.ts +0 -61
  157. package/src/utils/rrweb-events.ts +0 -309
  158. package/src/utils/session.ts +0 -18
  159. package/src/utils/time.ts +0 -17
  160. package/src/utils/type-utils.ts +0 -75
  161. package/src/version.ts +0 -1
  162. package/tsconfig.json +0 -24
@@ -1,275 +0,0 @@
1
- import { Span } from '@opentelemetry/api'
2
- import {
3
- MULTIPLAYER_TRACE_DEBUG_PREFIX,
4
- MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX,
5
- ATTR_MULTIPLAYER_HTTP_REQUEST_BODY,
6
- ATTR_MULTIPLAYER_HTTP_REQUEST_HEADERS,
7
- ATTR_MULTIPLAYER_HTTP_RESPONSE_BODY,
8
- ATTR_MULTIPLAYER_HTTP_RESPONSE_HEADERS,
9
- } from '@multiplayer-app/session-recorder-common'
10
- import { logger } from '../utils'
11
- import { SessionRecorderSdk } from '@multiplayer-app/session-recorder-common'
12
- import { TracerReactNativeConfig } from '../types'
13
-
14
- const { schemify } = SessionRecorderSdk
15
-
16
- export interface HttpPayloadData {
17
- requestBody?: any
18
- responseBody?: any
19
- requestHeaders?: Record<string, string>
20
- responseHeaders?: Record<string, string>
21
- }
22
-
23
- export interface ProcessedHttpPayload {
24
- requestBody?: string
25
- responseBody?: string
26
- requestHeaders?: string
27
- responseHeaders?: string
28
- }
29
-
30
- /**
31
- * Checks if the trace should be processed based on trace ID prefixes
32
- */
33
- export function shouldProcessTrace(traceId: string): boolean {
34
- return (
35
- traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX) ||
36
- traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)
37
- )
38
- }
39
-
40
- /**
41
- * Processes request and response body based on trace type and configuration
42
- */
43
- export function processBody(
44
- payload: HttpPayloadData,
45
- config: TracerReactNativeConfig,
46
- span: Span,
47
- ): { requestBody?: string; responseBody?: string } {
48
- const { captureBody, masking } = config
49
- const traceId = span.spanContext().traceId
50
-
51
- if (!captureBody) {
52
- return {}
53
- }
54
-
55
- let { requestBody, responseBody } = payload
56
-
57
- if (requestBody !== undefined && requestBody !== null) {
58
- requestBody = JSON.parse(JSON.stringify(requestBody))
59
- }
60
- if (responseBody !== undefined && responseBody !== null) {
61
- responseBody = JSON.parse(JSON.stringify(responseBody))
62
- }
63
-
64
- // Apply masking for debug traces
65
- if (
66
- traceId.startsWith(MULTIPLAYER_TRACE_DEBUG_PREFIX) ||
67
- traceId.startsWith(MULTIPLAYER_TRACE_CONTINUOUS_DEBUG_PREFIX)
68
- ) {
69
- if (masking.isContentMaskingEnabled) {
70
- requestBody = requestBody && masking.maskBody?.(requestBody, span)
71
- responseBody = responseBody && masking.maskBody?.(responseBody, span)
72
- }
73
- }
74
-
75
- // Convert to string if needed
76
- if (typeof requestBody !== 'string') {
77
- requestBody = JSON.stringify(requestBody)
78
- }
79
-
80
- if (typeof responseBody !== 'string') {
81
- responseBody = JSON.stringify(responseBody)
82
- }
83
-
84
- return {
85
- requestBody: requestBody?.length ? requestBody : undefined,
86
- responseBody: responseBody?.length ? responseBody : undefined,
87
- }
88
- }
89
-
90
- /**
91
- * Processes request and response headers based on configuration
92
- */
93
- export function processHeaders(
94
- payload: HttpPayloadData,
95
- config: TracerReactNativeConfig,
96
- span: Span,
97
- ): { requestHeaders?: string; responseHeaders?: string } {
98
- const { captureHeaders, masking } = config
99
-
100
- if (!captureHeaders) {
101
- return {}
102
- }
103
-
104
- let { requestHeaders = {}, responseHeaders = {} } = payload
105
-
106
- // Handle header filtering
107
- if (
108
- !masking.headersToInclude?.length &&
109
- !masking.headersToExclude?.length
110
- ) {
111
- // Add null checks to prevent JSON.parse error when headers is undefined
112
- if (requestHeaders !== undefined && requestHeaders !== null) {
113
- requestHeaders = JSON.parse(JSON.stringify(requestHeaders))
114
- }
115
- if (responseHeaders !== undefined && responseHeaders !== null) {
116
- responseHeaders = JSON.parse(JSON.stringify(responseHeaders))
117
- }
118
- } else {
119
- if (masking.headersToInclude) {
120
- const _requestHeaders: Record<string, string> = {}
121
- const _responseHeaders: Record<string, string> = {}
122
-
123
- for (const headerName of masking.headersToInclude) {
124
- if (requestHeaders[headerName]) {
125
- _requestHeaders[headerName] = requestHeaders[headerName]
126
- }
127
- if (responseHeaders[headerName]) {
128
- _responseHeaders[headerName] = responseHeaders[headerName]
129
- }
130
- }
131
-
132
- requestHeaders = _requestHeaders
133
- responseHeaders = _responseHeaders
134
- }
135
-
136
- if (masking.headersToExclude?.length) {
137
- for (const headerName of masking.headersToExclude) {
138
- delete requestHeaders[headerName]
139
- delete responseHeaders[headerName]
140
- }
141
- }
142
- }
143
-
144
- // Apply masking
145
- const maskedRequestHeaders = masking.maskHeaders?.(requestHeaders, span) || requestHeaders
146
- const maskedResponseHeaders = masking.maskHeaders?.(responseHeaders, span) || responseHeaders
147
-
148
- // Convert to string
149
- const requestHeadersStr = typeof maskedRequestHeaders === 'string'
150
- ? maskedRequestHeaders
151
- : JSON.stringify(maskedRequestHeaders)
152
-
153
- const responseHeadersStr = typeof maskedResponseHeaders === 'string'
154
- ? maskedResponseHeaders
155
- : JSON.stringify(maskedResponseHeaders)
156
-
157
- return {
158
- requestHeaders: requestHeadersStr?.length ? requestHeadersStr : undefined,
159
- responseHeaders: responseHeadersStr?.length ? responseHeadersStr : undefined,
160
- }
161
- }
162
-
163
- /**
164
- * Processes HTTP payload (body and headers) and sets span attributes
165
- */
166
- export function processHttpPayload(
167
- payload: HttpPayloadData,
168
- config: TracerReactNativeConfig,
169
- span: Span,
170
- ): void {
171
- const traceId = span.spanContext().traceId
172
-
173
- if (!shouldProcessTrace(traceId)) {
174
- return
175
- }
176
-
177
- const { requestBody, responseBody } = processBody(payload, config, span)
178
- const { requestHeaders, responseHeaders } = processHeaders(payload, config, span)
179
-
180
- // Set span attributes
181
- if (requestBody) {
182
- span.setAttribute(ATTR_MULTIPLAYER_HTTP_REQUEST_BODY, requestBody)
183
- }
184
-
185
- if (responseBody) {
186
- span.setAttribute(ATTR_MULTIPLAYER_HTTP_RESPONSE_BODY, responseBody)
187
- }
188
-
189
- if (requestHeaders) {
190
- span.setAttribute(ATTR_MULTIPLAYER_HTTP_REQUEST_HEADERS, requestHeaders)
191
- }
192
-
193
- if (responseHeaders) {
194
- span.setAttribute(ATTR_MULTIPLAYER_HTTP_RESPONSE_HEADERS, responseHeaders)
195
- }
196
- }
197
-
198
- /**
199
- * Converts Headers object to plain object
200
- */
201
- export function headersToObject(headers: Headers | Record<string, string> | Record<string, string | string[]> | string[][] | undefined): Record<string, string> {
202
- const result: Record<string, string> = {}
203
-
204
- if (!headers) {
205
- return result
206
- }
207
-
208
- if (headers instanceof Headers) {
209
- headers.forEach((value: string, key: string) => {
210
- result[key] = value
211
- })
212
- } else if (Array.isArray(headers)) {
213
- // Handle array of [key, value] pairs
214
- for (const [key, value] of headers) {
215
- if (typeof key === 'string' && typeof value === 'string') {
216
- result[key] = value
217
- }
218
- }
219
- } else if (typeof headers === 'object' && !Array.isArray(headers)) {
220
- for (const [key, value] of Object.entries(headers)) {
221
- if (typeof key === 'string' && typeof value === 'string') {
222
- result[key] = value
223
- }
224
- }
225
- }
226
-
227
- return result
228
- }
229
-
230
- /**
231
- * Extracts response body as string from Response object
232
- */
233
- export async function extractResponseBody(response: Response): Promise<string | null> {
234
- if (!response.body) {
235
- return null
236
- }
237
-
238
- try {
239
- if (response.body instanceof ReadableStream) {
240
- // Check if response body is already consumed
241
- if (response.bodyUsed) {
242
- return null
243
- }
244
-
245
- const responseClone = response.clone()
246
- return responseClone.text()
247
- } else {
248
- return JSON.stringify(response.body)
249
- }
250
- } catch (error) {
251
- // If cloning fails (body already consumed), return null
252
- // eslint-disable-next-line no-console
253
- logger.warn('DEBUGGER_LIB', 'Failed to extract response body', error)
254
- return null
255
- }
256
- }
257
-
258
- export const getExporterEndpoint = (exporterEndpoint: string): string => {
259
- const hasPath = exporterEndpoint && (() => {
260
- try {
261
- const url = new URL(exporterEndpoint)
262
- return url.pathname !== '/' && url.pathname !== ''
263
- } catch {
264
- return false
265
- }
266
- })()
267
-
268
- if (hasPath) {
269
- return exporterEndpoint
270
- }
271
-
272
- const trimmedExporterEndpoint = new URL(exporterEndpoint).origin
273
-
274
- return `${trimmedExporterEndpoint}/v1/traces`
275
- }
package/src/otel/index.ts DELETED
@@ -1,138 +0,0 @@
1
- import { resourceFromAttributes } from '@opentelemetry/resources'
2
- import { W3CTraceContextPropagator } from '@opentelemetry/core'
3
- import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'
4
- import * as SemanticAttributes from '@opentelemetry/semantic-conventions'
5
- import { registerInstrumentations } from '@opentelemetry/instrumentation'
6
- import {
7
- SessionType,
8
- ATTR_MULTIPLAYER_SESSION_ID,
9
- SessionRecorderIdGenerator,
10
- SessionRecorderTraceIdRatioBasedSampler,
11
- SessionRecorderBrowserTraceExporter,
12
- } from '@multiplayer-app/session-recorder-common'
13
- import { TracerReactNativeConfig } from '../types'
14
- import { getInstrumentations } from './instrumentations'
15
- import { getExporterEndpoint } from './helpers'
16
-
17
- import { getPlatformAttributes } from '../utils/platform'
18
- import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'
19
-
20
-
21
-
22
-
23
- export class TracerReactNativeSDK {
24
- private tracerProvider?: WebTracerProvider
25
- private config?: TracerReactNativeConfig
26
-
27
- private sessionId = ''
28
- private idGenerator?: SessionRecorderIdGenerator
29
- private exporter?: any
30
- private isInitialized = false
31
-
32
- constructor() { }
33
-
34
- private _setSessionId(
35
- sessionId: string,
36
- sessionType: SessionType = SessionType.PLAIN,
37
- ) {
38
- this.sessionId = sessionId
39
- this.idGenerator?.setSessionId(sessionId, sessionType)
40
- }
41
-
42
- init(options: TracerReactNativeConfig): void {
43
- this.config = options
44
-
45
- const { application, version, environment } = this.config
46
-
47
- this.idGenerator = new SessionRecorderIdGenerator()
48
-
49
- this.exporter = new SessionRecorderBrowserTraceExporter({
50
- apiKey: options.apiKey,
51
- url: getExporterEndpoint(options.exporterEndpoint),
52
- usePostMessageFallback: options.usePostMessageFallback,
53
- })
54
-
55
- this.tracerProvider = new WebTracerProvider({
56
- resource: resourceFromAttributes({
57
- [SemanticAttributes.SEMRESATTRS_SERVICE_NAME]: application,
58
- [SemanticAttributes.SEMRESATTRS_SERVICE_VERSION]: version,
59
- [SemanticAttributes.SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: environment,
60
- ...getPlatformAttributes(),
61
- }),
62
- idGenerator: this.idGenerator,
63
- sampler: new SessionRecorderTraceIdRatioBasedSampler(this.config.sampleTraceRatio || 0.15),
64
- spanProcessors: [
65
- this._getSpanSessionIdProcessor(),
66
- new BatchSpanProcessor(this.exporter),
67
- ],
68
- })
69
-
70
- this.tracerProvider.register({
71
- propagator: new W3CTraceContextPropagator(),
72
- })
73
-
74
- // Register instrumentations
75
- registerInstrumentations({
76
- tracerProvider: this.tracerProvider,
77
- instrumentations: getInstrumentations(this.config),
78
- })
79
-
80
- this.isInitialized = true
81
- }
82
-
83
- private _getSpanSessionIdProcessor() {
84
- return {
85
- onStart: (span: any) => {
86
- if (this.sessionId) {
87
- span.setAttribute(ATTR_MULTIPLAYER_SESSION_ID, this.sessionId)
88
- }
89
- // Add React Native specific attributes
90
- span.setAttribute('platform', 'react-native')
91
- span.setAttribute('timestamp', Date.now())
92
- },
93
- onEnd: () => { },
94
- shutdown: () => Promise.resolve(),
95
- forceFlush: () => Promise.resolve(),
96
- }
97
- }
98
-
99
- start(sessionId: string, sessionType: SessionType): void {
100
- if (!this.tracerProvider) {
101
- throw new Error(
102
- 'Configuration not initialized. Call init() before start().',
103
- )
104
- }
105
-
106
- this._setSessionId(sessionId, sessionType)
107
- }
108
-
109
- stop(): void {
110
- if (!this.tracerProvider) {
111
- throw new Error(
112
- 'Configuration not initialized. Call init() before start().',
113
- )
114
- }
115
-
116
- this._setSessionId('')
117
- }
118
-
119
- setApiKey(apiKey: string): void {
120
- if (!this.exporter) {
121
- throw new Error(
122
- 'Configuration not initialized. Call init() before setApiKey().',
123
- )
124
- }
125
-
126
- this.exporter.setApiKey?.(apiKey)
127
- }
128
-
129
- setSessionId(sessionId: string, sessionType: SessionType): void {
130
- this._setSessionId(sessionId, sessionType)
131
- }
132
-
133
- // Shutdown (React Native specific)
134
- shutdown(): Promise<void> {
135
- this.isInitialized = false
136
- return Promise.resolve()
137
- }
138
- }
@@ -1,115 +0,0 @@
1
- import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch'
2
- import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request'
3
-
4
- import { logger } from '../../utils'
5
- import { OTEL_IGNORE_URLS } from '../../config'
6
- import { TracerReactNativeConfig } from '../../types'
7
- import { extractResponseBody, headersToObject, processHttpPayload } from '../helpers'
8
-
9
- export function getInstrumentations(config: TracerReactNativeConfig) {
10
-
11
- const instrumentations = []
12
-
13
- // Fetch instrumentation
14
- try {
15
- instrumentations.push(
16
- new FetchInstrumentation({
17
- clearTimingResources: false,
18
- ignoreUrls: [
19
- ...OTEL_IGNORE_URLS,
20
- ...(config.ignoreUrls || []),
21
- ],
22
- propagateTraceHeaderCorsUrls: config.propagateTraceHeaderCorsUrls,
23
- applyCustomAttributesOnSpan: async (span, request, response) => {
24
- if (!config) return
25
-
26
- const { captureBody, captureHeaders } = config
27
-
28
- try {
29
- if (!captureBody && !captureHeaders) {
30
- return
31
- }
32
-
33
- const requestBody = request.body
34
- const requestHeaders = headersToObject(request.headers)
35
- const responseHeaders = headersToObject(response instanceof Response ? response.headers : undefined)
36
-
37
- let responseBody: string | null = null
38
- if (response instanceof Response && response.body) {
39
- responseBody = await extractResponseBody(response)
40
- }
41
-
42
- const payload = {
43
- requestBody,
44
- responseBody,
45
- requestHeaders,
46
- responseHeaders,
47
- }
48
- processHttpPayload(payload, config, span)
49
- } catch (error) {
50
- // eslint-disable-next-line
51
- logger.error('DEBUGGER_LIB', 'Failed to capture fetch payload', error)
52
- }
53
- },
54
- })
55
- )
56
- } catch (error) {
57
- logger.warn('DEBUGGER_LIB', 'Fetch instrumentation not available', error)
58
- }
59
-
60
- // XMLHttpRequest instrumentation
61
- try {
62
- instrumentations.push(
63
- new XMLHttpRequestInstrumentation({
64
- clearTimingResources: false,
65
- ignoreUrls: [
66
- ...OTEL_IGNORE_URLS,
67
- ...(config.ignoreUrls || []),
68
- ],
69
- propagateTraceHeaderCorsUrls: config.propagateTraceHeaderCorsUrls,
70
- applyCustomAttributesOnSpan: (span, xhr) => {
71
- if (!config) return
72
-
73
- const { captureBody, captureHeaders } = config
74
-
75
- try {
76
- if (!captureBody && !captureHeaders) {
77
- return
78
- }
79
-
80
- // @ts-ignore
81
- const requestBody = xhr.networkRequest.requestBody
82
- // @ts-ignore
83
- const responseBody = xhr.networkRequest.responseBody
84
- // @ts-ignore
85
- const requestHeaders = xhr.networkRequest.requestHeaders || {}
86
- // @ts-ignore
87
- const responseHeaders = xhr.networkRequest.responseHeaders || {}
88
-
89
- const payload = {
90
- requestBody,
91
- responseBody,
92
- requestHeaders,
93
- responseHeaders,
94
- }
95
- processHttpPayload(payload, config, span)
96
- } catch (error) {
97
- // eslint-disable-next-line
98
- logger.error('DEBUGGER_LIB', 'Failed to capture xml-http payload', error)
99
- }
100
- },
101
- })
102
- )
103
- } catch (error) {
104
- logger.warn('DEBUGGER_LIB', 'XMLHttpRequest instrumentation not available', error)
105
- }
106
-
107
- // Custom React Native instrumentations
108
- // try {
109
- // instrumentations.push(new ReactNativeInstrumentation())
110
- // } catch (error) {
111
- // console.warn('React Native instrumentation not available:', error)
112
- // }
113
-
114
- return instrumentations
115
- }
@@ -1 +0,0 @@
1
- import './xhr'
package/src/patch/xhr.ts DELETED
@@ -1,142 +0,0 @@
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
- && requestBody.length <= 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
- && responseBody.length <= 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)