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

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 (234) hide show
  1. package/RRWEB_INTEGRATION.md +336 -0
  2. package/VIEWSHOT_INTEGRATION_TEST.md +123 -0
  3. package/copy-react-native-dist.sh +38 -0
  4. package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.d.ts +6 -0
  5. package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js +1 -0
  6. package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js.map +1 -0
  7. package/dist/components/GestureCaptureWrapper/index.d.ts +1 -0
  8. package/dist/components/GestureCaptureWrapper/index.js +1 -0
  9. package/dist/components/GestureCaptureWrapper/index.js.map +1 -0
  10. package/dist/components/GestureCaptureWrapper.d.ts +6 -0
  11. package/dist/components/GestureCaptureWrapper.js +1 -0
  12. package/dist/components/GestureCaptureWrapper.js.map +1 -0
  13. package/dist/components/ScreenRecorderView/ScreenRecorderView.d.ts +5 -0
  14. package/dist/components/ScreenRecorderView/ScreenRecorderView.js +1 -0
  15. package/dist/components/ScreenRecorderView/ScreenRecorderView.js.map +1 -0
  16. package/dist/components/ScreenRecorderView/index.d.ts +1 -0
  17. package/dist/components/ScreenRecorderView/index.js +1 -0
  18. package/dist/components/ScreenRecorderView/index.js.map +1 -0
  19. package/dist/components/index.d.ts +1 -0
  20. package/dist/components/index.js +1 -0
  21. package/dist/components/index.js.map +1 -0
  22. package/dist/config/constants.d.ts +18 -0
  23. package/dist/config/constants.js +1 -0
  24. package/dist/config/constants.js.map +1 -0
  25. package/dist/config/defaults.d.ts +4 -0
  26. package/dist/config/defaults.js +1 -0
  27. package/dist/config/defaults.js.map +1 -0
  28. package/dist/config/index.d.ts +5 -0
  29. package/dist/config/index.js +1 -0
  30. package/dist/config/index.js.map +1 -0
  31. package/dist/config/masking.d.ts +2 -30
  32. package/dist/config/masking.js +1 -1
  33. package/dist/config/masking.js.map +1 -1
  34. package/dist/config/session-recorder.d.ts +2 -0
  35. package/dist/config/session-recorder.js +1 -0
  36. package/dist/config/session-recorder.js.map +1 -0
  37. package/dist/config/validators.d.ts +10 -0
  38. package/dist/config/validators.js +1 -0
  39. package/dist/config/validators.js.map +1 -0
  40. package/dist/context/SessionRecorderContext.d.ts +12 -0
  41. package/dist/context/SessionRecorderContext.js +1 -0
  42. package/dist/context/SessionRecorderContext.js.map +1 -0
  43. package/dist/expo.d.ts +5 -9
  44. package/dist/expo.js +1 -1
  45. package/dist/expo.js.map +1 -1
  46. package/dist/index.d.ts +6 -10
  47. package/dist/index.js +1 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/otel/helpers.d.ts +45 -3
  50. package/dist/otel/helpers.js +1 -1
  51. package/dist/otel/helpers.js.map +1 -1
  52. package/dist/otel/index.d.ts +4 -25
  53. package/dist/otel/index.js +1 -1
  54. package/dist/otel/index.js.map +1 -1
  55. package/dist/otel/instrumentations/gestureInstrumentation.js +1 -1
  56. package/dist/otel/instrumentations/gestureInstrumentation.js.map +1 -1
  57. package/dist/otel/instrumentations/index.d.ts +3 -4
  58. package/dist/otel/instrumentations/index.js +1 -1
  59. package/dist/otel/instrumentations/index.js.map +1 -1
  60. package/dist/otel/instrumentations/reactNativeInstrumentation.js +1 -1
  61. package/dist/otel/instrumentations/reactNativeInstrumentation.js.map +1 -1
  62. package/dist/otel/instrumentations/reactNavigationInstrumentation.d.ts +1 -0
  63. package/dist/otel/instrumentations/reactNavigationInstrumentation.js +1 -1
  64. package/dist/otel/instrumentations/reactNavigationInstrumentation.js.map +1 -1
  65. package/dist/patch/index.d.ts +1 -0
  66. package/dist/patch/index.js +1 -0
  67. package/dist/patch/index.js.map +1 -0
  68. package/dist/patch/xhr.d.ts +2 -0
  69. package/dist/patch/xhr.js +1 -0
  70. package/dist/patch/xhr.js.map +1 -0
  71. package/dist/recorder/eventExporter.d.ts +21 -0
  72. package/dist/recorder/eventExporter.js +1 -0
  73. package/dist/recorder/eventExporter.js.map +1 -0
  74. package/dist/recorder/gestureHandlerRecorder.d.ts +19 -0
  75. package/dist/recorder/gestureHandlerRecorder.js +1 -0
  76. package/dist/recorder/gestureHandlerRecorder.js.map +1 -0
  77. package/dist/recorder/gestureRecorder.d.ts +68 -11
  78. package/dist/recorder/gestureRecorder.js +1 -1
  79. package/dist/recorder/gestureRecorder.js.map +1 -1
  80. package/dist/recorder/index.d.ts +60 -6
  81. package/dist/recorder/index.js +1 -1
  82. package/dist/recorder/index.js.map +1 -1
  83. package/dist/recorder/navigationTracker.js +1 -1
  84. package/dist/recorder/navigationTracker.js.map +1 -1
  85. package/dist/recorder/screenRecorder.d.ts +79 -10
  86. package/dist/recorder/screenRecorder.js +1 -1
  87. package/dist/recorder/screenRecorder.js.map +1 -1
  88. package/dist/services/api.service.d.ts +62 -10
  89. package/dist/services/api.service.js +1 -1
  90. package/dist/services/api.service.js.map +1 -1
  91. package/dist/services/storage.service.d.ts +23 -16
  92. package/dist/services/storage.service.js +1 -1
  93. package/dist/services/storage.service.js.map +1 -1
  94. package/dist/session-recorder.d.ts +166 -0
  95. package/dist/session-recorder.js +1 -0
  96. package/dist/session-recorder.js.map +1 -0
  97. package/dist/types/index.d.ts +15 -76
  98. package/dist/types/index.js +1 -1
  99. package/dist/types/index.js.map +1 -1
  100. package/dist/types/rrweb.d.ts +118 -0
  101. package/dist/types/rrweb.js +1 -0
  102. package/dist/types/rrweb.js.map +1 -0
  103. package/dist/types/session-recorder.d.ts +366 -0
  104. package/dist/types/session-recorder.js +1 -0
  105. package/dist/types/session-recorder.js.map +1 -0
  106. package/dist/types/session.d.ts +59 -0
  107. package/dist/types/session.js +1 -0
  108. package/dist/types/session.js.map +1 -0
  109. package/dist/utils/app-metadata.d.ts +16 -0
  110. package/dist/utils/app-metadata.js +1 -0
  111. package/dist/utils/app-metadata.js.map +1 -0
  112. package/dist/utils/index.d.ts +7 -0
  113. package/dist/utils/index.js +1 -0
  114. package/dist/utils/index.js.map +1 -0
  115. package/dist/utils/logger.d.ts +112 -0
  116. package/dist/utils/logger.js +1 -0
  117. package/dist/utils/logger.js.map +1 -0
  118. package/dist/utils/platform.d.ts +37 -0
  119. package/dist/utils/platform.js +1 -1
  120. package/dist/utils/platform.js.map +1 -1
  121. package/dist/utils/request-utils.d.ts +21 -0
  122. package/dist/utils/request-utils.js +1 -0
  123. package/dist/utils/request-utils.js.map +1 -0
  124. package/dist/utils/rrweb-events.d.ts +65 -0
  125. package/dist/utils/rrweb-events.js +1 -0
  126. package/dist/utils/rrweb-events.js.map +1 -0
  127. package/dist/utils/session.d.ts +5 -0
  128. package/dist/utils/session.js +1 -0
  129. package/dist/utils/session.js.map +1 -0
  130. package/dist/utils/time.d.ts +4 -0
  131. package/dist/utils/time.js +1 -0
  132. package/dist/utils/time.js.map +1 -0
  133. package/dist/utils/type-utils.d.ts +16 -0
  134. package/dist/utils/type-utils.js +1 -0
  135. package/dist/utils/type-utils.js.map +1 -0
  136. package/dist/version.d.ts +1 -1
  137. package/dist/version.js +1 -1
  138. package/dist/version.js.map +1 -1
  139. package/docs/AUTO_METADATA_DETECTION.md +108 -0
  140. package/package.json +10 -9
  141. package/scripts/generate-app-metadata.js +173 -0
  142. package/src/components/GestureCaptureWrapper/GestureCaptureWrapper.tsx +86 -0
  143. package/src/components/GestureCaptureWrapper/index.ts +1 -0
  144. package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +72 -0
  145. package/src/components/ScreenRecorderView/index.ts +1 -0
  146. package/src/components/index.ts +1 -0
  147. package/src/config/constants.ts +60 -0
  148. package/src/config/defaults.ts +82 -0
  149. package/src/config/index.ts +6 -0
  150. package/src/config/masking.ts +10 -61
  151. package/src/config/session-recorder.ts +55 -0
  152. package/src/config/validators.ts +31 -0
  153. package/src/context/SessionRecorderContext.tsx +75 -0
  154. package/src/expo.ts +7 -37
  155. package/src/index.ts +14 -17
  156. package/src/otel/helpers.ts +265 -11
  157. package/src/otel/index.ts +37 -247
  158. package/src/otel/instrumentations/index.ts +82 -53
  159. package/src/patch/index.ts +1 -0
  160. package/src/patch/xhr.ts +142 -0
  161. package/src/recorder/eventExporter.ts +141 -0
  162. package/src/recorder/gestureRecorder.ts +194 -125
  163. package/src/recorder/index.ts +132 -24
  164. package/src/recorder/navigationTracker.ts +12 -10
  165. package/src/recorder/screenRecorder.ts +242 -155
  166. package/src/services/api.service.ts +170 -45
  167. package/src/services/storage.service.ts +102 -74
  168. package/src/session-recorder.ts +600 -0
  169. package/src/types/index.ts +19 -79
  170. package/src/types/session-recorder.ts +423 -0
  171. package/src/types/session.ts +65 -0
  172. package/src/utils/app-metadata.ts +31 -0
  173. package/src/utils/index.ts +8 -0
  174. package/src/utils/logger.ts +225 -0
  175. package/src/utils/platform.ts +321 -6
  176. package/src/utils/request-utils.ts +61 -0
  177. package/src/utils/rrweb-events.ts +309 -0
  178. package/src/utils/session.ts +18 -0
  179. package/src/utils/time.ts +17 -0
  180. package/src/utils/type-utils.ts +75 -0
  181. package/src/version.ts +1 -1
  182. package/dist/sessionRecorder.d.ts +0 -54
  183. package/dist/sessionRecorder.js +0 -1
  184. package/dist/sessionRecorder.js.map +0 -1
  185. package/examples/sample-expo-app/README.md +0 -142
  186. package/examples/sample-expo-app/app/(tabs)/_layout.tsx +0 -60
  187. package/examples/sample-expo-app/app/(tabs)/explore.tsx +0 -110
  188. package/examples/sample-expo-app/app/(tabs)/index.tsx +0 -125
  189. package/examples/sample-expo-app/app/(tabs)/posts.tsx +0 -96
  190. package/examples/sample-expo-app/app/(tabs)/users.tsx +0 -131
  191. package/examples/sample-expo-app/app/+not-found.tsx +0 -32
  192. package/examples/sample-expo-app/app/_layout.tsx +0 -53
  193. package/examples/sample-expo-app/app/post/[id].tsx +0 -199
  194. package/examples/sample-expo-app/app/user/[id].tsx +0 -270
  195. package/examples/sample-expo-app/app.json +0 -42
  196. package/examples/sample-expo-app/assets/fonts/SpaceMono-Regular.ttf +0 -0
  197. package/examples/sample-expo-app/assets/images/adaptive-icon.png +0 -0
  198. package/examples/sample-expo-app/assets/images/favicon.png +0 -0
  199. package/examples/sample-expo-app/assets/images/icon.png +0 -0
  200. package/examples/sample-expo-app/assets/images/partial-react-logo.png +0 -0
  201. package/examples/sample-expo-app/assets/images/react-logo.png +0 -0
  202. package/examples/sample-expo-app/assets/images/react-logo@2x.png +0 -0
  203. package/examples/sample-expo-app/assets/images/react-logo@3x.png +0 -0
  204. package/examples/sample-expo-app/assets/images/splash-icon.png +0 -0
  205. package/examples/sample-expo-app/components/Collapsible.tsx +0 -45
  206. package/examples/sample-expo-app/components/ErrorView.tsx +0 -52
  207. package/examples/sample-expo-app/components/ExternalLink.tsx +0 -24
  208. package/examples/sample-expo-app/components/HapticTab.tsx +0 -18
  209. package/examples/sample-expo-app/components/HelloWave.tsx +0 -40
  210. package/examples/sample-expo-app/components/LoadingSpinner.tsx +0 -34
  211. package/examples/sample-expo-app/components/ParallaxScrollView.tsx +0 -82
  212. package/examples/sample-expo-app/components/ThemedText.tsx +0 -60
  213. package/examples/sample-expo-app/components/ThemedView.tsx +0 -14
  214. package/examples/sample-expo-app/components/ui/IconSymbol.ios.tsx +0 -32
  215. package/examples/sample-expo-app/components/ui/IconSymbol.tsx +0 -41
  216. package/examples/sample-expo-app/components/ui/TabBarBackground.ios.tsx +0 -19
  217. package/examples/sample-expo-app/components/ui/TabBarBackground.tsx +0 -6
  218. package/examples/sample-expo-app/constants/Colors.ts +0 -26
  219. package/examples/sample-expo-app/eslint.config.js +0 -10
  220. package/examples/sample-expo-app/hooks/useApi.ts +0 -41
  221. package/examples/sample-expo-app/hooks/useColorScheme.ts +0 -1
  222. package/examples/sample-expo-app/hooks/useColorScheme.web.ts +0 -21
  223. package/examples/sample-expo-app/hooks/useThemeColor.ts +0 -21
  224. package/examples/sample-expo-app/metro.config.js +0 -26
  225. package/examples/sample-expo-app/package-lock.json +0 -26296
  226. package/examples/sample-expo-app/package.json +0 -59
  227. package/examples/sample-expo-app/scripts/reset-project.js +0 -112
  228. package/examples/sample-expo-app/services/api.ts +0 -98
  229. package/examples/sample-expo-app/tsconfig.json +0 -17
  230. package/examples/sample-expo-app/utils/navigation.ts +0 -19
  231. package/src/otel/instrumentations/gestureInstrumentation.ts +0 -141
  232. package/src/otel/instrumentations/reactNativeInstrumentation.ts +0 -164
  233. package/src/otel/instrumentations/reactNavigationInstrumentation.ts +0 -114
  234. package/src/sessionRecorder.ts +0 -367
@@ -1,86 +1,115 @@
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'
1
6
  import { TracerReactNativeConfig } from '../../types'
2
- import { ReactNativeInstrumentation } from './reactNativeInstrumentation'
3
- import { getHttpMaskingConfig } from '../../config/masking'
7
+ import { extractResponseBody, headersToObject, processHttpPayload } from '../helpers'
4
8
 
5
9
  export function getInstrumentations(config: TracerReactNativeConfig) {
6
- const httpMaskingConfig = getHttpMaskingConfig(config.httpMasking)
10
+
7
11
  const instrumentations = []
8
12
 
9
13
  // Fetch instrumentation
10
14
  try {
11
- const { FetchInstrumentation } = require('@opentelemetry/instrumentation-fetch')
12
15
  instrumentations.push(
13
16
  new FetchInstrumentation({
14
- ignoreUrls: config.ignoreUrls || [],
15
- propagateTraceHeaderCorsUrls: config.propagateTraceHeaderCorsUrls || [],
16
- applyCustomAttributesOnSpan: (span: any, request: any) => {
17
- if (config.captureHeaders) {
18
- const headers = request.headers
19
- if (headers && httpMaskingConfig.maskHeaders) {
20
- const maskedHeaders = httpMaskingConfig.maskHeaders(headers, span)
21
- Object.keys(maskedHeaders).forEach(key => {
22
- span.setAttribute(`http.request.header.${key}`, maskedHeaders[key])
23
- })
24
- } else if (headers) {
25
- Object.keys(headers).forEach(key => {
26
- span.setAttribute(`http.request.header.${key}`, headers[key])
27
- })
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
28
31
  }
29
- }
30
- },
31
- applyCustomAttributesOnSpanResponse: (span: any, response: any) => {
32
- if (config.captureBody && httpMaskingConfig.maskBody) {
33
- const maskedBody = httpMaskingConfig.maskBody(response.body, span)
34
- span.setAttribute('http.response.body', JSON.stringify(maskedBody))
35
- } else if (config.captureBody) {
36
- span.setAttribute('http.response.body', JSON.stringify(response.body))
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)
37
52
  }
38
53
  },
39
54
  })
40
55
  )
41
56
  } catch (error) {
42
- console.warn('Fetch instrumentation not available:', error)
57
+ logger.warn('DEBUGGER_LIB', 'Fetch instrumentation not available', error)
43
58
  }
44
59
 
45
60
  // XMLHttpRequest instrumentation
46
61
  try {
47
- const { XMLHttpRequestInstrumentation } = require('@opentelemetry/instrumentation-xml-http-request')
48
62
  instrumentations.push(
49
63
  new XMLHttpRequestInstrumentation({
50
- ignoreUrls: config.ignoreUrls || [],
51
- propagateTraceHeaderCorsUrls: config.propagateTraceHeaderCorsUrls || [],
52
- applyCustomAttributesOnSpan: (span: any, xhr: any) => {
53
- if (config.captureHeaders) {
54
- const headers = xhr.getAllResponseHeaders()
55
- if (headers && httpMaskingConfig.maskHeaders) {
56
- const maskedHeaders = httpMaskingConfig.maskHeaders(headers, span)
57
- Object.keys(maskedHeaders).forEach(key => {
58
- span.setAttribute(`http.response.header.${key}`, maskedHeaders[key])
59
- })
60
- } else if (headers) {
61
- Object.keys(headers).forEach(key => {
62
- span.setAttribute(`http.response.header.${key}`, headers[key])
63
- })
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,
64
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)
65
99
  }
66
100
  },
67
101
  })
68
102
  )
69
103
  } catch (error) {
70
- console.warn('XMLHttpRequest instrumentation not available:', error)
104
+ logger.warn('DEBUGGER_LIB', 'XMLHttpRequest instrumentation not available', error)
71
105
  }
72
106
 
73
107
  // Custom React Native instrumentations
74
- try {
75
- instrumentations.push(new ReactNativeInstrumentation())
76
- } catch (error) {
77
- console.warn('React Native instrumentation not available:', error)
78
- }
108
+ // try {
109
+ // instrumentations.push(new ReactNativeInstrumentation())
110
+ // } catch (error) {
111
+ // console.warn('React Native instrumentation not available:', error)
112
+ // }
79
113
 
80
114
  return instrumentations
81
115
  }
82
-
83
- // Export custom instrumentations for manual use
84
- export { ReactNativeInstrumentation } from './reactNativeInstrumentation'
85
- export { ReactNavigationInstrumentation } from './reactNavigationInstrumentation'
86
- export { GestureInstrumentation } from './gestureInstrumentation'
@@ -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
+ && 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)
@@ -0,0 +1,141 @@
1
+ import io, { Socket } from 'socket.io-client'
2
+
3
+ import { ISession } from '../types'
4
+ import { logger } from '../utils'
5
+
6
+ import {
7
+ SESSION_ADD_EVENT,
8
+ SESSION_AUTO_CREATED,
9
+ SESSION_STOPPED_EVENT,
10
+ SESSION_SUBSCRIBE_EVENT,
11
+ SESSION_UNSUBSCRIBE_EVENT,
12
+ } from '../config'
13
+
14
+ const MAX_RECONNECTION_ATTEMPTS = 2
15
+
16
+ export class EventExporter {
17
+ private socket: Socket | null = null
18
+ private queue: any[] = []
19
+ private isConnecting: boolean = false
20
+ private isConnected: boolean = false
21
+ private attempts: number = 0
22
+ private sessionId: string | null = null
23
+
24
+ constructor(private options: { socketUrl: string, apiKey: string }) { }
25
+
26
+ private init(): void {
27
+ if (this.isConnecting || this.isConnected) return
28
+ this.attempts++
29
+ this.isConnecting = true
30
+ this.socket = io(this.options.socketUrl, {
31
+ path: '/v0/radar/ws',
32
+ auth: {
33
+ 'x-api-key': this.options.apiKey,
34
+ },
35
+ reconnectionAttempts: 2,
36
+ transports: ['websocket'],
37
+ })
38
+
39
+ // this.socket.on('connect', () => {
40
+ // this.isConnecting = false
41
+ // this.isConnected = true
42
+ // this.usePostMessage = false
43
+ // this.flushQueue()
44
+ // })
45
+
46
+ this.socket.on('ready', () => {
47
+ this.isConnecting = false
48
+ this.isConnected = true
49
+ logger.info('EventExporter', 'Connected to server')
50
+ this.flushQueue()
51
+ })
52
+
53
+ this.socket.on('disconnect', (err: any) => {
54
+ this.isConnecting = false
55
+ this.isConnected = false
56
+ logger.info('EventExporter', 'Disconnected from server')
57
+ })
58
+
59
+ this.socket.on('connect_error', (err: any) => {
60
+ this.isConnecting = false
61
+ this.isConnected = false
62
+ this.checkReconnectionAttempts()
63
+ logger.error('EventExporter', 'Error connecting to server', err)
64
+ })
65
+
66
+ this.socket.on(SESSION_STOPPED_EVENT, (data: any) => {
67
+
68
+ this.unsubscribeFromSession()
69
+ })
70
+
71
+ this.socket.on(SESSION_AUTO_CREATED, (data: any) => {
72
+
73
+ })
74
+ }
75
+
76
+ private checkReconnectionAttempts(): void {
77
+ if (this.attempts >= MAX_RECONNECTION_ATTEMPTS) {
78
+
79
+ this.flushQueue()
80
+ }
81
+ }
82
+
83
+
84
+ private flushQueue(): void {
85
+ while (this.queue.length > 0 && (this.socket?.connected)) {
86
+ const event = this.queue.shift()
87
+ if (!event) continue
88
+
89
+ if (this.socket?.connected) {
90
+ this.socket.emit(event.name, event.data)
91
+ }
92
+ }
93
+ }
94
+
95
+ private unsubscribeFromSession() {
96
+ const payload = {
97
+ debugSessionId: this.sessionId,
98
+ }
99
+ if (this.socket?.connected) {
100
+ this.socket.emit(SESSION_UNSUBSCRIBE_EVENT, payload)
101
+ }
102
+ }
103
+
104
+ public send(event: any): void {
105
+ if (this.socket?.connected) {
106
+ this.socket.emit(SESSION_ADD_EVENT, event)
107
+ } else {
108
+ this.queue.push({ data: event, name: SESSION_ADD_EVENT })
109
+ this.init()
110
+ }
111
+ }
112
+
113
+ public subscribeToSession(session: ISession): void {
114
+ this.sessionId = session.shortId || session._id
115
+ const payload = {
116
+ projectId: session.project,
117
+ workspaceId: session.workspace,
118
+ debugSessionId: this.sessionId,
119
+ sessionType: session.creationType,
120
+ }
121
+ if (this.socket?.connected) {
122
+ this.socket.emit(SESSION_SUBSCRIBE_EVENT, payload)
123
+ } else {
124
+ this.queue.push({ data: payload, name: SESSION_SUBSCRIBE_EVENT })
125
+ this.init()
126
+ }
127
+ }
128
+
129
+ public close(): void {
130
+ if (this.socket?.connected) {
131
+ setTimeout(() => {
132
+ this.unsubscribeFromSession()
133
+ this.attempts = 0
134
+ this.isConnected = false
135
+ this.isConnecting = false
136
+ this.socket?.disconnect()
137
+ this.socket = null
138
+ }, 500)
139
+ }
140
+ }
141
+ }