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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/README.md +708 -83
  2. package/SessionRecorderNative.podspec +26 -0
  3. package/android/build.gradle +34 -0
  4. package/copy-react-native-dist.sh +34 -16
  5. package/dist/components/ScreenRecorderView/ScreenRecorderView.js +1 -1
  6. package/dist/components/ScreenRecorderView/ScreenRecorderView.js.map +1 -1
  7. package/dist/components/SessionRecorderWidget/ErrorBanner.d.ts +7 -0
  8. package/dist/components/SessionRecorderWidget/ErrorBanner.js +1 -0
  9. package/dist/components/SessionRecorderWidget/ErrorBanner.js.map +1 -0
  10. package/dist/components/SessionRecorderWidget/FinalPopover.d.ts +12 -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 +16 -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 +165 -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 -1
  38. package/dist/components/index.js +1 -1
  39. package/dist/components/index.js.map +1 -1
  40. package/dist/config/constants.js +1 -1
  41. package/dist/config/constants.js.map +1 -1
  42. package/dist/config/defaults.d.ts +4 -4
  43. package/dist/config/defaults.js +1 -1
  44. package/dist/config/defaults.js.map +1 -1
  45. package/dist/config/masking.d.ts +2 -2
  46. package/dist/config/masking.js +1 -1
  47. package/dist/config/masking.js.map +1 -1
  48. package/dist/config/session-recorder.js +1 -1
  49. package/dist/config/session-recorder.js.map +1 -1
  50. package/dist/config/validators.d.ts +1 -1
  51. package/dist/config/validators.js +1 -1
  52. package/dist/config/validators.js.map +1 -1
  53. package/dist/config/widget.d.ts +9 -0
  54. package/dist/config/widget.js +1 -0
  55. package/dist/config/widget.js.map +1 -0
  56. package/dist/context/SessionRecorderContext.d.ts +12 -3
  57. package/dist/context/SessionRecorderContext.js +1 -1
  58. package/dist/context/SessionRecorderContext.js.map +1 -1
  59. package/dist/context/SessionRecorderStore.d.ts +12 -0
  60. package/dist/context/SessionRecorderStore.js +1 -0
  61. package/dist/context/SessionRecorderStore.js.map +1 -0
  62. package/dist/context/useSessionRecorderStore.d.ts +8 -0
  63. package/dist/context/useSessionRecorderStore.js +1 -0
  64. package/dist/context/useSessionRecorderStore.js.map +1 -0
  65. package/dist/context/useStoreSelector.d.ts +4 -0
  66. package/dist/context/useStoreSelector.js +1 -0
  67. package/dist/context/useStoreSelector.js.map +1 -0
  68. package/dist/index.d.ts +1 -1
  69. package/dist/index.js +1 -1
  70. package/dist/index.js.map +1 -1
  71. package/dist/native/GestureRecorderNative.d.ts +57 -0
  72. package/dist/native/GestureRecorderNative.js +1 -0
  73. package/dist/native/GestureRecorderNative.js.map +1 -0
  74. package/dist/native/SessionRecorderNative.d.ts +33 -0
  75. package/dist/native/SessionRecorderNative.js +1 -0
  76. package/dist/native/SessionRecorderNative.js.map +1 -0
  77. package/dist/native/index.d.ts +2 -0
  78. package/dist/native/index.js +1 -0
  79. package/dist/native/index.js.map +1 -0
  80. package/dist/otel/index.js +1 -1
  81. package/dist/otel/index.js.map +1 -1
  82. package/dist/patch/xhr.js +1 -1
  83. package/dist/patch/xhr.js.map +1 -1
  84. package/dist/recorder/eventExporter.d.ts +4 -1
  85. package/dist/recorder/eventExporter.js +1 -1
  86. package/dist/recorder/eventExporter.js.map +1 -1
  87. package/dist/recorder/gestureRecorder.d.ts +28 -62
  88. package/dist/recorder/gestureRecorder.js +1 -1
  89. package/dist/recorder/gestureRecorder.js.map +1 -1
  90. package/dist/recorder/index.d.ts +2 -0
  91. package/dist/recorder/index.js +1 -1
  92. package/dist/recorder/index.js.map +1 -1
  93. package/dist/recorder/navigationTracker.d.ts +4 -19
  94. package/dist/recorder/navigationTracker.js +1 -1
  95. package/dist/recorder/navigationTracker.js.map +1 -1
  96. package/dist/recorder/screenRecorder.d.ts +11 -5
  97. package/dist/recorder/screenRecorder.js +1 -1
  98. package/dist/recorder/screenRecorder.js.map +1 -1
  99. package/dist/services/api.service.d.ts +12 -3
  100. package/dist/services/api.service.js +1 -1
  101. package/dist/services/api.service.js.map +1 -1
  102. package/dist/services/network.service.d.ts +46 -0
  103. package/dist/services/network.service.js +1 -0
  104. package/dist/services/network.service.js.map +1 -0
  105. package/dist/services/screenMaskingService.d.ts +47 -0
  106. package/dist/services/screenMaskingService.js +1 -0
  107. package/dist/services/screenMaskingService.js.map +1 -0
  108. package/dist/services/storage.service.d.ts +18 -2
  109. package/dist/services/storage.service.js +1 -1
  110. package/dist/services/storage.service.js.map +1 -1
  111. package/dist/session-recorder.d.ts +18 -33
  112. package/dist/session-recorder.js +1 -1
  113. package/dist/session-recorder.js.map +1 -1
  114. package/dist/types/configs.d.ts +85 -0
  115. package/dist/types/configs.js +1 -0
  116. package/dist/types/configs.js.map +1 -0
  117. package/dist/types/index.d.ts +1 -0
  118. package/dist/types/index.js +1 -1
  119. package/dist/types/index.js.map +1 -1
  120. package/dist/types/session-recorder.d.ts +105 -132
  121. package/dist/types/session-recorder.js +1 -1
  122. package/dist/types/session-recorder.js.map +1 -1
  123. package/dist/utils/constants.optional.d.ts +21 -0
  124. package/dist/utils/constants.optional.expo.d.ts +3 -0
  125. package/dist/utils/constants.optional.expo.js +1 -0
  126. package/dist/utils/constants.optional.expo.js.map +1 -0
  127. package/dist/utils/constants.optional.js +1 -0
  128. package/dist/utils/constants.optional.js.map +1 -0
  129. package/dist/utils/createStore.d.ts +8 -0
  130. package/dist/utils/createStore.js +1 -0
  131. package/dist/utils/createStore.js.map +1 -0
  132. package/dist/utils/logger.d.ts +2 -7
  133. package/dist/utils/logger.js +1 -1
  134. package/dist/utils/logger.js.map +1 -1
  135. package/dist/utils/platform.d.ts +11 -0
  136. package/dist/utils/platform.js +1 -1
  137. package/dist/utils/platform.js.map +1 -1
  138. package/dist/utils/rrweb-events.d.ts +4 -3
  139. package/dist/utils/rrweb-events.js +1 -1
  140. package/dist/utils/rrweb-events.js.map +1 -1
  141. package/dist/utils/session.d.ts +2 -1
  142. package/dist/utils/session.js +1 -1
  143. package/dist/utils/session.js.map +1 -1
  144. package/dist/utils/shallowEqual.d.ts +1 -0
  145. package/dist/utils/shallowEqual.js +1 -0
  146. package/dist/utils/shallowEqual.js.map +1 -0
  147. package/dist/version.d.ts +1 -1
  148. package/dist/version.js +1 -1
  149. package/dist/version.js.map +1 -1
  150. package/ios/GestureRecorderNative.m +21 -0
  151. package/ios/GestureRecorderNative.swift +316 -0
  152. package/ios/SessionRecorderNative.m +17 -0
  153. package/ios/SessionRecorderNative.podspec +26 -0
  154. package/ios/SessionRecorderNative.swift +599 -0
  155. package/package.json +15 -16
  156. package/react-native.config.js +12 -0
  157. package/RRWEB_INTEGRATION.md +0 -336
  158. package/VIEWSHOT_INTEGRATION_TEST.md +0 -123
  159. package/babel.config.js +0 -13
  160. package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.d.ts +0 -6
  161. package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js +0 -1
  162. package/dist/components/GestureCaptureWrapper/GestureCaptureWrapper.js.map +0 -1
  163. package/dist/components/GestureCaptureWrapper/index.d.ts +0 -1
  164. package/dist/components/GestureCaptureWrapper/index.js +0 -1
  165. package/dist/components/GestureCaptureWrapper/index.js.map +0 -1
  166. package/dist/components/GestureCaptureWrapper.d.ts +0 -6
  167. package/dist/components/GestureCaptureWrapper.js +0 -1
  168. package/dist/components/GestureCaptureWrapper.js.map +0 -1
  169. package/dist/expo.d.ts +0 -7
  170. package/dist/expo.js +0 -1
  171. package/dist/expo.js.map +0 -1
  172. package/dist/otel/instrumentations/gestureInstrumentation.d.ts +0 -15
  173. package/dist/otel/instrumentations/gestureInstrumentation.js +0 -1
  174. package/dist/otel/instrumentations/gestureInstrumentation.js.map +0 -1
  175. package/dist/otel/instrumentations/reactNativeInstrumentation.d.ts +0 -8
  176. package/dist/otel/instrumentations/reactNativeInstrumentation.js +0 -1
  177. package/dist/otel/instrumentations/reactNativeInstrumentation.js.map +0 -1
  178. package/dist/otel/instrumentations/reactNavigationInstrumentation.d.ts +0 -13
  179. package/dist/otel/instrumentations/reactNavigationInstrumentation.js +0 -1
  180. package/dist/otel/instrumentations/reactNavigationInstrumentation.js.map +0 -1
  181. package/dist/recorder/gestureHandlerRecorder.d.ts +0 -19
  182. package/dist/recorder/gestureHandlerRecorder.js +0 -1
  183. package/dist/recorder/gestureHandlerRecorder.js.map +0 -1
  184. package/dist/types/rrweb.d.ts +0 -118
  185. package/dist/types/rrweb.js +0 -1
  186. package/dist/types/rrweb.js.map +0 -1
  187. package/scripts/generate-app-metadata.js +0 -173
  188. package/src/components/GestureCaptureWrapper/GestureCaptureWrapper.tsx +0 -86
  189. package/src/components/GestureCaptureWrapper/index.ts +0 -1
  190. package/src/components/ScreenRecorderView/ScreenRecorderView.tsx +0 -72
  191. package/src/components/ScreenRecorderView/index.ts +0 -1
  192. package/src/components/index.ts +0 -1
  193. package/src/config/constants.ts +0 -60
  194. package/src/config/defaults.ts +0 -82
  195. package/src/config/index.ts +0 -6
  196. package/src/config/masking.ts +0 -27
  197. package/src/config/session-recorder.ts +0 -55
  198. package/src/config/validators.ts +0 -31
  199. package/src/context/SessionRecorderContext.tsx +0 -75
  200. package/src/expo.ts +0 -11
  201. package/src/index.ts +0 -17
  202. package/src/otel/helpers.ts +0 -275
  203. package/src/otel/index.ts +0 -138
  204. package/src/otel/instrumentations/index.ts +0 -115
  205. package/src/patch/index.ts +0 -1
  206. package/src/patch/xhr.ts +0 -142
  207. package/src/recorder/eventExporter.ts +0 -141
  208. package/src/recorder/gestureRecorder.ts +0 -498
  209. package/src/recorder/index.ts +0 -179
  210. package/src/recorder/navigationTracker.ts +0 -449
  211. package/src/recorder/screenRecorder.ts +0 -498
  212. package/src/services/api.service.ts +0 -203
  213. package/src/services/storage.service.ts +0 -158
  214. package/src/session-recorder.ts +0 -600
  215. package/src/types/expo.d.ts +0 -23
  216. package/src/types/index.ts +0 -28
  217. package/src/types/session-recorder.ts +0 -423
  218. package/src/types/session.ts +0 -65
  219. package/src/utils/app-metadata.ts +0 -31
  220. package/src/utils/index.ts +0 -8
  221. package/src/utils/logger.ts +0 -225
  222. package/src/utils/platform.ts +0 -384
  223. package/src/utils/request-utils.ts +0 -61
  224. package/src/utils/rrweb-events.ts +0 -309
  225. package/src/utils/session.ts +0 -18
  226. package/src/utils/time.ts +0 -17
  227. package/src/utils/type-utils.ts +0 -75
  228. package/src/version.ts +0 -1
  229. package/tsconfig.json +0 -24
@@ -1,27 +0,0 @@
1
- import { MaskingConfig } from '../types'
2
- import { DEFAULT_MASKING_CONFIG } from './defaults'
3
- import { isValidArray, isValidBoolean, isValidFunction } from './validators'
4
- import { SessionRecorderSdk } from '@multiplayer-app/session-recorder-common'
5
-
6
- const { mask, sensitiveFields, sensitiveHeaders } = SessionRecorderSdk
7
-
8
- export const getMaskingConfig = (masking?: MaskingConfig): MaskingConfig => {
9
- const baseMasking = DEFAULT_MASKING_CONFIG
10
-
11
- if (typeof masking !== 'object') {
12
- return baseMasking
13
- }
14
-
15
- const maskHeadersList = isValidArray(masking.maskHeadersList, sensitiveHeaders)
16
- const maskBodyFieldsList = isValidArray(masking.maskBodyFieldsList, sensitiveFields)
17
-
18
- return {
19
- maskHeadersList,
20
- maskBodyFieldsList,
21
- headersToInclude: isValidArray(masking.headersToInclude, baseMasking.headersToInclude ?? []),
22
- headersToExclude: isValidArray(masking.headersToExclude, baseMasking.headersToExclude ?? []),
23
- isContentMaskingEnabled: isValidBoolean(masking.isContentMaskingEnabled, baseMasking.isContentMaskingEnabled ?? true),
24
- maskBody: isValidFunction(masking.maskBody, mask(maskBodyFieldsList)),
25
- maskHeaders: isValidFunction(masking.maskHeaders, mask(maskHeadersList)),
26
- }
27
- }
@@ -1,55 +0,0 @@
1
- import { SessionRecorderConfigs, SessionRecorderOptions, WidgetButtonPlacement } from '../types'
2
- import { BASE_CONFIG } from './defaults'
3
- import { getMaskingConfig } from './masking'
4
- import {
5
- isValidString,
6
- isValidNumber,
7
- isValidBoolean,
8
- isValidArray,
9
- isValidEnum,
10
- } from './validators'
11
-
12
- const getWidgetTextOverridesConfig = (config: any, defaultConfig: any) => {
13
- if (!config || typeof config !== 'object') {
14
- return defaultConfig
15
- }
16
- return Object.keys(defaultConfig).reduce((acc, key) => {
17
- acc[key] = isValidString(config[key], defaultConfig[key])
18
- return acc
19
- }, {} as Record<string, any>)
20
- }
21
-
22
- export const getSessionRecorderConfig = (c: SessionRecorderOptions): SessionRecorderConfigs => {
23
- if (!c) {
24
- return BASE_CONFIG
25
- }
26
-
27
- return {
28
- apiKey: isValidString(c.apiKey, BASE_CONFIG.apiKey),
29
- version: isValidString(c.version, BASE_CONFIG.version),
30
- application: isValidString(c.application, BASE_CONFIG.application),
31
- environment: isValidString(c.environment, BASE_CONFIG.environment),
32
-
33
- exporterEndpoint: isValidString(c.exporterEndpoint, BASE_CONFIG.exporterEndpoint),
34
- apiBaseUrl: isValidString(c.apiBaseUrl, BASE_CONFIG.apiBaseUrl),
35
- usePostMessageFallback: isValidBoolean(c.usePostMessageFallback, BASE_CONFIG.usePostMessageFallback),
36
-
37
- showWidget: isValidBoolean(c.showWidget, BASE_CONFIG.showWidget),
38
- showContinuousRecording: isValidBoolean(c.showContinuousRecording, BASE_CONFIG.showContinuousRecording),
39
- widgetButtonPlacement: isValidEnum<WidgetButtonPlacement>(c.widgetButtonPlacement, BASE_CONFIG.widgetButtonPlacement, Object.values(WidgetButtonPlacement) as WidgetButtonPlacement[]),
40
- ignoreUrls: isValidArray(c.ignoreUrls, BASE_CONFIG.ignoreUrls),
41
- sampleTraceRatio: isValidNumber(c.sampleTraceRatio, BASE_CONFIG.sampleTraceRatio),
42
- propagateTraceHeaderCorsUrls: c.propagateTraceHeaderCorsUrls || BASE_CONFIG.propagateTraceHeaderCorsUrls,
43
- schemifyDocSpanPayload: isValidBoolean(c.schemifyDocSpanPayload, BASE_CONFIG.schemifyDocSpanPayload),
44
- maxCapturingHttpPayloadSize: isValidNumber(c.maxCapturingHttpPayloadSize, BASE_CONFIG.maxCapturingHttpPayloadSize),
45
-
46
- masking: getMaskingConfig(c.masking),
47
- captureBody: isValidBoolean(c.captureBody, BASE_CONFIG.captureBody),
48
- captureHeaders: isValidBoolean(c.captureHeaders, BASE_CONFIG.captureHeaders),
49
- widgetTextOverrides: getWidgetTextOverridesConfig(c.widgetTextOverrides, BASE_CONFIG.widgetTextOverrides),
50
-
51
- recordScreen: isValidBoolean(c.recordScreen, BASE_CONFIG.recordScreen),
52
- recordGestures: isValidBoolean(c.recordGestures, BASE_CONFIG.recordGestures),
53
- recordNavigation: isValidBoolean(c.recordNavigation, BASE_CONFIG.recordNavigation),
54
- }
55
- }
@@ -1,31 +0,0 @@
1
- /**
2
- * Validation helper functions for configuration objects
3
- */
4
-
5
- export const isValidStringOrRegExp = (value: string | RegExp | undefined, defaultValue: string | RegExp) => {
6
- return typeof value === 'string' || value instanceof RegExp ? value : defaultValue
7
- }
8
-
9
- export const isValidString = <T extends string>(value: string | undefined | T, defaultValue: string) => {
10
- return typeof value === 'string' ? value.trim() : defaultValue
11
- }
12
-
13
- export const isValidNumber = (value: number | undefined, defaultValue: number) => {
14
- return typeof value === 'number' ? value : defaultValue
15
- }
16
-
17
- export const isValidBoolean = (value: boolean | undefined, defaultValue: boolean) => {
18
- return typeof value === 'boolean' ? value : defaultValue
19
- }
20
-
21
- export const isValidArray = (value: any[] | undefined, defaultValue: any[]) => {
22
- return Array.isArray(value) ? value : defaultValue
23
- }
24
-
25
- export const isValidEnum = <T>(value: any | T, defaultValue: T, enumValues: T[]): T => {
26
- return enumValues.includes(value as T) ? value as T : defaultValue
27
- }
28
-
29
- export const isValidFunction = (value: any, defaultValue: any) => {
30
- return typeof value === 'function' ? value : defaultValue
31
- }
@@ -1,75 +0,0 @@
1
- import React, { createContext, useContext, PropsWithChildren, useState, useEffect, useRef } from 'react'
2
- import { Pressable, Text, View } from 'react-native'
3
- import { SessionRecorderOptions, SessionState } from '../types'
4
- import SessionRecorder from '../session-recorder'
5
- import sessionRecorder from '../session-recorder'
6
- import { ScreenRecorderView } from '../components/ScreenRecorderView'
7
-
8
- interface SessionRecorderContextType {
9
- instance: typeof SessionRecorder
10
- }
11
-
12
- const SessionRecorderContext = createContext<SessionRecorderContextType | null>(null)
13
-
14
- export interface SessionRecorderProviderProps extends PropsWithChildren {
15
- options: SessionRecorderOptions
16
- }
17
-
18
- export const SessionRecorderProvider: React.FC<SessionRecorderProviderProps> = ({ children, options }) => {
19
- const [sessionState, setSessionState] = useState<SessionState | null>(null)
20
- const optionsRef = useRef<string>()
21
-
22
- useEffect(() => {
23
- const newOptions = JSON.stringify(options)
24
- if (optionsRef.current === JSON.stringify(options)) return
25
- optionsRef.current = newOptions
26
- SessionRecorder.init(options)
27
- }, [options])
28
-
29
- useEffect(() => {
30
- setSessionState(SessionRecorder.sessionState)
31
- SessionRecorder.on('state-change', (state: SessionState) => {
32
- setSessionState(state)
33
- })
34
- }, [])
35
-
36
- const onToggleSession = () => {
37
- if (SessionRecorder.sessionState === SessionState.started) {
38
- SessionRecorder.stop()
39
- } else {
40
- SessionRecorder.start()
41
- }
42
- }
43
-
44
- return (
45
- <SessionRecorderContext.Provider value={{ instance: sessionRecorder }}>
46
- <ScreenRecorderView>{children}</ScreenRecorderView>
47
- <Pressable onPress={onToggleSession}>
48
- <View
49
- style={{
50
- position: 'absolute',
51
- right: 0,
52
- bottom: 100,
53
- width: 48,
54
- height: 48,
55
- paddingTop: 16,
56
- paddingLeft: 10,
57
- backgroundColor: 'red',
58
- borderTopLeftRadius: 24,
59
- borderBottomLeftRadius: 24
60
- }}
61
- >
62
- <Text style={{ color: 'white' }}>{sessionState === SessionState.started ? 'Stop' : 'Start'}</Text>
63
- </View>
64
- </Pressable>
65
- </SessionRecorderContext.Provider>
66
- )
67
- }
68
-
69
- export const useSessionRecorder = (): SessionRecorderContextType => {
70
- const context = useContext(SessionRecorderContext)
71
- if (!context) {
72
- throw new Error('useSessionRecorder must be used within a SessionRecorderProvider')
73
- }
74
- return context
75
- }
package/src/expo.ts DELETED
@@ -1,11 +0,0 @@
1
- import './patch'
2
- import SessionRecorder from './session-recorder'
3
- export * from '@multiplayer-app/session-recorder-common'
4
- export * from './context/SessionRecorderContext'
5
-
6
- export { SessionRecorder }
7
- // Export the instance as default
8
- export default SessionRecorder
9
-
10
- // Export Expo-specific utilities
11
- export { isExpoEnvironment, getPlatformAttributes } from './utils/platform'
package/src/index.ts DELETED
@@ -1,17 +0,0 @@
1
- import './patch'
2
- import SessionRecorder from './session-recorder'
3
- export * from '@multiplayer-app/session-recorder-common'
4
- export * from './context/SessionRecorderContext'
5
-
6
- // Export platform utilities including app metadata configuration
7
- export {
8
- detectPlatform,
9
- isExpoEnvironment,
10
- configureAppMetadata,
11
- getPlatformAttributes,
12
- getConfiguredAppMetadata,
13
- } from './utils/platform'
14
-
15
- export { SessionRecorder }
16
- // Export the instance as default
17
- export default SessionRecorder
@@ -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
- }