@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
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Centralized logger utility for the session recorder
3
+ * Provides consistent logging across all components
4
+ */
5
+
6
+ export enum LogLevel {
7
+ DEBUG = 0,
8
+ INFO = 1,
9
+ WARN = 2,
10
+ ERROR = 3
11
+ }
12
+
13
+ export interface LoggerConfig {
14
+ level: LogLevel
15
+ enableConsole: boolean
16
+ enablePrefix: boolean
17
+ prefix: string
18
+ }
19
+
20
+ class Logger {
21
+ private config: LoggerConfig = {
22
+ level: LogLevel.INFO,
23
+ enableConsole: true,
24
+ enablePrefix: true,
25
+ prefix: '[SessionRecorder]',
26
+ }
27
+
28
+ private componentPrefixes: Map<string, string> = new Map([
29
+ ['ScreenRecorder', '📸'],
30
+ ['GestureRecorder', '👆'],
31
+ ['GestureCaptureWrapper', '📸'],
32
+ ['SessionRecorderContext', '🎯'],
33
+ ['EventExporter', '📤'],
34
+ ['NavigationTracker', '📸'],
35
+ ['RecorderReactNativeSDK', '📤'],
36
+ ['DEBUGGER_LIB', '🔍'],
37
+ ])
38
+
39
+ /**
40
+ * Configure the logger
41
+ * @param config - Logger configuration
42
+ */
43
+ configure(config: Partial<LoggerConfig>): void {
44
+ this.config = { ...this.config, ...config }
45
+ }
46
+
47
+ /**
48
+ * Set the log level
49
+ * @param level - Log level to set
50
+ */
51
+ setLevel(level: LogLevel): void {
52
+ this.config.level = level
53
+ }
54
+
55
+ /**
56
+ * Enable or disable console output
57
+ * @param enabled - Whether to enable console output
58
+ */
59
+ setConsoleEnabled(enabled: boolean): void {
60
+ this.config.enableConsole = enabled
61
+ }
62
+
63
+ /**
64
+ * Add or update a component prefix
65
+ * @param component - Component name
66
+ * @param emoji - Emoji prefix for the component
67
+ */
68
+ setComponentPrefix(component: string, emoji: string): void {
69
+ this.componentPrefixes.set(component, emoji)
70
+ }
71
+
72
+ /**
73
+ * Get the formatted prefix for a component
74
+ * @param component - Component name
75
+ * @returns Formatted prefix string
76
+ */
77
+ private getPrefix(component: string): string {
78
+ if (!this.config.enablePrefix) return ''
79
+
80
+ const emoji = this.componentPrefixes.get(component) || '📝'
81
+ return `${this.config.prefix} ${emoji} [${component}]`
82
+ }
83
+
84
+ /**
85
+ * Check if a log level should be output
86
+ * @param level - Log level to check
87
+ * @returns True if should output
88
+ */
89
+ private shouldLog(level: LogLevel): boolean {
90
+ return level >= this.config.level && this.config.enableConsole
91
+ }
92
+
93
+ /**
94
+ * Format the log message
95
+ * @param component - Component name
96
+ * @param level - Log level
97
+ * @param message - Log message
98
+ * @param data - Additional data to log
99
+ * @returns Formatted log message
100
+ */
101
+ private formatMessage(component: string, level: LogLevel, message: string, data?: any): string {
102
+ const prefix = this.getPrefix(component)
103
+ const timestamp = new Date().toISOString()
104
+ const levelName = LogLevel[level]
105
+
106
+ let formattedMessage = `${prefix} ${levelName} ${message}`
107
+
108
+ if (data !== undefined) {
109
+ formattedMessage += ` ${JSON.stringify(data)}`
110
+ }
111
+
112
+ return formattedMessage
113
+ }
114
+
115
+ /**
116
+ * Log a debug message
117
+ * @param component - Component name
118
+ * @param message - Log message
119
+ * @param data - Additional data to log
120
+ */
121
+ debug(component: string, message: string, data?: any): void {
122
+ if (!this.shouldLog(LogLevel.DEBUG)) return
123
+
124
+ const formattedMessage = this.formatMessage(component, LogLevel.DEBUG, message, data)
125
+ // eslint-disable-next-line no-console
126
+ console.log(formattedMessage)
127
+ }
128
+
129
+ /**
130
+ * Log an info message
131
+ * @param component - Component name
132
+ * @param message - Log message
133
+ * @param data - Additional data to log
134
+ */
135
+ info(component: string, message: string, data?: any): void {
136
+ if (!this.shouldLog(LogLevel.INFO)) return
137
+
138
+ const formattedMessage = this.formatMessage(component, LogLevel.INFO, message, data)
139
+ // eslint-disable-next-line no-console
140
+ console.log(formattedMessage)
141
+ }
142
+
143
+ /**
144
+ * Log a warning message
145
+ * @param component - Component name
146
+ * @param message - Log message
147
+ * @param data - Additional data to log
148
+ */
149
+ warn(component: string, message: string, data?: any): void {
150
+ if (!this.shouldLog(LogLevel.WARN)) return
151
+
152
+ const formattedMessage = this.formatMessage(component, LogLevel.WARN, message, data)
153
+ // eslint-disable-next-line no-console
154
+ console.warn(formattedMessage)
155
+ }
156
+
157
+ /**
158
+ * Log an error message
159
+ * @param component - Component name
160
+ * @param message - Log message
161
+ * @param data - Additional data to log
162
+ */
163
+ error(component: string, message: string, data?: any): void {
164
+ if (!this.shouldLog(LogLevel.ERROR)) return
165
+
166
+ const formattedMessage = this.formatMessage(component, LogLevel.ERROR, message, data)
167
+ // eslint-disable-next-line no-console
168
+ console.error(formattedMessage)
169
+ }
170
+
171
+ /**
172
+ * Log a success message (info level with success emoji)
173
+ * @param component - Component name
174
+ * @param message - Log message
175
+ * @param data - Additional data to log
176
+ */
177
+ success(component: string, message: string, data?: any): void {
178
+ if (!this.shouldLog(LogLevel.INFO)) return
179
+
180
+ const prefix = this.getPrefix(component)
181
+ const timestamp = new Date().toISOString()
182
+ const formattedMessage = `${prefix} ✅ ${message}`
183
+
184
+ let fullMessage = formattedMessage
185
+ if (data !== undefined) {
186
+ fullMessage += ` ${JSON.stringify(data)}`
187
+ }
188
+
189
+ // eslint-disable-next-line no-console
190
+ console.log(fullMessage)
191
+ }
192
+
193
+ /**
194
+ * Log a failure message (error level with failure emoji)
195
+ * @param component - Component name
196
+ * @param message - Log message
197
+ * @param data - Additional data to log
198
+ */
199
+ failure(component: string, message: string, data?: any): void {
200
+ if (!this.shouldLog(LogLevel.ERROR)) return
201
+
202
+ const prefix = this.getPrefix(component)
203
+ const timestamp = new Date().toISOString()
204
+ const formattedMessage = `${prefix} ❌ ${message}`
205
+
206
+ let fullMessage = formattedMessage
207
+ if (data !== undefined) {
208
+ fullMessage += ` ${JSON.stringify(data)}`
209
+ }
210
+
211
+ // eslint-disable-next-line no-console
212
+ console.error(fullMessage)
213
+ }
214
+ }
215
+
216
+ // Export a singleton instance
217
+ export const logger = new Logger()
218
+
219
+ // Export convenience functions for common use cases
220
+ export const logDebug = (component: string, message: string, data?: any) => logger.debug(component, message, data)
221
+ export const logInfo = (component: string, message: string, data?: any) => logger.info(component, message, data)
222
+ export const logWarn = (component: string, message: string, data?: any) => logger.warn(component, message, data)
223
+ export const logError = (component: string, message: string, data?: any) => logger.error(component, message, data)
224
+ export const logSuccess = (component: string, message: string, data?: any) => logger.success(component, message, data)
225
+ export const logFailure = (component: string, message: string, data?: any) => logger.failure(component, message, data)
@@ -1,3 +1,15 @@
1
+ import { IResourceAttributes } from '../types'
2
+ import Constants from 'expo-constants'
3
+ import { Platform, Dimensions, PixelRatio } from 'react-native'
4
+ import { version } from '../version'
5
+ import { getAutoDetectedAppMetadata } from './app-metadata'
6
+
7
+ // Global app metadata configuration for non-Expo apps
8
+ let globalAppMetadata: { name?: string; version?: string; bundleId?: string } = {}
9
+
10
+ // Cache for auto-detected metadata to avoid repeated file reads
11
+ let autoDetectedMetadata: { name?: string; version?: string; bundleId?: string } | null = null
12
+
1
13
  export interface PlatformInfo {
2
14
  isExpo: boolean
3
15
  isReactNative: boolean
@@ -10,12 +22,11 @@ export interface PlatformInfo {
10
22
  export function detectPlatform(): PlatformInfo {
11
23
  try {
12
24
  // Check if we're in an Expo environment
13
- const expoConstants = require('expo-constants')
14
- const isExpo = !!expoConstants.default?.expoVersion || !!expoConstants.expoVersion
25
+ const isExpo = !!Constants.default?.expoVersion || !!Constants.expoVersion
15
26
 
16
27
  if (isExpo) {
17
- const expoVersion = expoConstants.default?.expoVersion || expoConstants.expoVersion
18
- const platform = expoConstants.default?.platform || expoConstants.platform
28
+ const expoVersion = Constants.default?.expoVersion || Constants.expoVersion
29
+ const platform = Constants.default?.platform || Constants.platform
19
30
 
20
31
  return {
21
32
  isExpo: true,
@@ -27,8 +38,6 @@ export function detectPlatform(): PlatformInfo {
27
38
  }
28
39
 
29
40
  // Fallback to React Native detection
30
- const { Platform } = require('react-native')
31
-
32
41
  return {
33
42
  isExpo: false,
34
43
  isReactNative: true,
@@ -73,3 +82,309 @@ export function isExpoEnvironment(): boolean {
73
82
  export function isReactNativeEnvironment(): boolean {
74
83
  return detectPlatform().isReactNative
75
84
  }
85
+
86
+ /**
87
+ * Configure app metadata for non-Expo React Native apps
88
+ * Call this function in your app initialization to provide app information
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * import { configureAppMetadata } from '@multiplayer-app/session-recorder-react-native'
93
+ *
94
+ * // In your App.tsx or index.js
95
+ * configureAppMetadata({
96
+ * name: 'My Awesome App',
97
+ * version: '1.2.3',
98
+ * bundleId: 'com.mycompany.myapp',
99
+ * buildNumber: '123',
100
+ * displayName: 'My App'
101
+ * })
102
+ * ```
103
+ */
104
+ export function configureAppMetadata(metadata: {
105
+ name?: string;
106
+ version?: string;
107
+ bundleId?: string;
108
+ buildNumber?: string;
109
+ displayName?: string;
110
+ }): void {
111
+ globalAppMetadata = { ...globalAppMetadata, ...metadata }
112
+ }
113
+
114
+ /**
115
+ * Get configured app metadata
116
+ */
117
+ export function getConfiguredAppMetadata(): {
118
+ name?: string;
119
+ version?: string;
120
+ bundleId?: string;
121
+ buildNumber?: string;
122
+ displayName?: string;
123
+ } {
124
+ return { ...globalAppMetadata }
125
+ }
126
+
127
+ /**
128
+ * Automatically detect app metadata from common configuration files
129
+ * This runs without developer intervention
130
+ */
131
+ function autoDetectAppMetadata(): { name?: string; version?: string; bundleId?: string } {
132
+ if (autoDetectedMetadata) {
133
+ return autoDetectedMetadata
134
+ }
135
+
136
+ try {
137
+ // Get auto-detected metadata from build-time generated file
138
+ const autoMetadata = getAutoDetectedAppMetadata()
139
+
140
+ // Filter out undefined values
141
+ const metadata: { name?: string; version?: string; bundleId?: string } = {}
142
+ if (autoMetadata.name) metadata.name = autoMetadata.name
143
+ if (autoMetadata.version) metadata.version = autoMetadata.version
144
+ if (autoMetadata.bundleId) metadata.bundleId = autoMetadata.bundleId
145
+
146
+ autoDetectedMetadata = metadata
147
+ return metadata
148
+ } catch (error) {
149
+ // Silently fail - this is optional auto-detection
150
+ autoDetectedMetadata = {}
151
+ return {}
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Enhanced app metadata detection with automatic fallbacks
157
+ */
158
+ function getAppMetadata(): { name?: string; version?: string; bundleId?: string } {
159
+ // Priority order:
160
+ // 1. Expo config (if available)
161
+ // 2. Manually configured metadata
162
+ // 3. Auto-detected metadata
163
+ // 4. Fallbacks
164
+
165
+ const expoMetadata = getExpoMetadata()
166
+ const configuredMetadata = getConfiguredAppMetadata()
167
+ const autoMetadata = autoDetectAppMetadata()
168
+
169
+ return {
170
+ name: expoMetadata.name || configuredMetadata.name || autoMetadata.name,
171
+ version: expoMetadata.version || configuredMetadata.version || autoMetadata.version,
172
+ bundleId: expoMetadata.bundleId || configuredMetadata.bundleId || autoMetadata.bundleId,
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Get metadata from Expo config
178
+ */
179
+ function getExpoMetadata(): { name?: string; version?: string; bundleId?: string } {
180
+ const expoConfig = Constants.default?.expoConfig || Constants.expoConfig
181
+ if (!expoConfig) return {}
182
+
183
+ return {
184
+ name: expoConfig.name,
185
+ version: expoConfig.version,
186
+ bundleId: expoConfig.ios?.bundleIdentifier || expoConfig.android?.package,
187
+ }
188
+ }
189
+
190
+
191
+ export const getNavigatorInfo = (): IResourceAttributes => {
192
+ const platformInfo = detectPlatform()
193
+ const screenData = Dimensions.get('window')
194
+ const screenDataScreen = Dimensions.get('screen')
195
+ const pixelRatio = PixelRatio.get()
196
+
197
+ // Get device type based on screen dimensions
198
+ const getDeviceType = (): string => {
199
+ const { width, height } = screenData
200
+ const minDimension = Math.min(width, height)
201
+ const maxDimension = Math.max(width, height)
202
+
203
+ // Rough device type detection based on screen size
204
+ if (maxDimension >= 1024) {
205
+ return 'Tablet'
206
+ } else if (minDimension >= 600) {
207
+ return 'Large Phone'
208
+ } else {
209
+ return 'Phone'
210
+ }
211
+ }
212
+
213
+ // Get orientation
214
+ const getOrientation = (): string => {
215
+ const { width, height } = screenData
216
+ return width > height ? 'Landscape' : 'Portrait'
217
+ }
218
+
219
+ // Get OS version details
220
+ const getOSInfo = (): string => {
221
+ if (platformInfo.isExpo) {
222
+ const platform = Constants.default?.platform || Constants.platform
223
+ if (platform?.ios) {
224
+ return `iOS ${Platform.Version}`
225
+ } else if (platform?.android) {
226
+ return `Android ${Platform.Version}`
227
+ }
228
+ }
229
+
230
+ if (Platform.OS === 'ios') {
231
+ return `iOS ${Platform.Version}`
232
+ } else if (Platform.OS === 'android') {
233
+ return `Android ${Platform.Version}`
234
+ }
235
+
236
+ return `${Platform.OS} ${Platform.Version || 'Unknown'}`
237
+ }
238
+
239
+ // Get device info string
240
+ const getDeviceInfo = (): string => {
241
+ const deviceType = getDeviceType()
242
+ const osInfo = getOSInfo()
243
+ return `${deviceType} - ${osInfo}`
244
+ }
245
+
246
+ // Get browser/runtime info
247
+ const getBrowserInfo = (): string => {
248
+ if (platformInfo.isExpo) {
249
+ return `Expo ${platformInfo.expoVersion || 'Unknown'} - React Native`
250
+ }
251
+ return 'React Native'
252
+ }
253
+
254
+ // Get screen size string
255
+ const getScreenSize = (): string => {
256
+ return `${Math.round(screenData.width)}x${Math.round(screenData.height)}`
257
+ }
258
+
259
+ // Get app info with fallbacks for non-Expo apps
260
+ const getAppInfo = (): string => {
261
+ const appName = getAppName()
262
+ const appVersion = getAppVersion()
263
+ return `${appName} v${appVersion}`
264
+ }
265
+
266
+ // Get app name with automatic detection
267
+ const getAppName = (): string => {
268
+ const metadata = getAppMetadata()
269
+ if (metadata.name) return metadata.name
270
+
271
+ // Try configured display name as fallback
272
+ const configuredMetadata = getConfiguredAppMetadata()
273
+ if (configuredMetadata.displayName) return configuredMetadata.displayName
274
+
275
+ // Final fallback
276
+ return 'React Native App'
277
+ }
278
+
279
+ // Get app version with automatic detection
280
+ const getAppVersion = (): string => {
281
+ const metadata = getAppMetadata()
282
+ if (metadata.version) return metadata.version
283
+
284
+ // Final fallback
285
+ return 'Unknown'
286
+ }
287
+
288
+ // Get bundle ID with automatic detection
289
+ const getBundleId = (): string => {
290
+ const metadata = getAppMetadata()
291
+ if (metadata.bundleId) return metadata.bundleId
292
+
293
+ // Fallback
294
+ return 'com.reactnative.app'
295
+ }
296
+
297
+ // Get build number with multiple fallback strategies
298
+ const getBuildNumber = (): string => {
299
+ // Try Expo config first
300
+ const expoBuildNumber = Constants.default?.expoConfig?.ios?.buildNumber ||
301
+ Constants.expoConfig?.ios?.buildNumber ||
302
+ Constants.default?.expoConfig?.android?.versionCode ||
303
+ Constants.expoConfig?.android?.versionCode
304
+ if (expoBuildNumber) return expoBuildNumber.toString()
305
+
306
+ // Try configured metadata for non-Expo apps
307
+ const configuredMetadata = getConfiguredAppMetadata()
308
+ if (configuredMetadata.buildNumber) return configuredMetadata.buildNumber
309
+
310
+ // Fallback
311
+ return '1'
312
+ }
313
+
314
+ // Get network info (basic)
315
+ const getNetworkInfo = (): string => {
316
+ // This is a basic implementation - in a real app you might want to use @react-native-community/netinfo
317
+ return 'Unknown'
318
+ }
319
+
320
+ // Get hardware info
321
+ const getHardwareInfo = (): string => {
322
+ const pixelRatioInfo = `Pixel Ratio: ${pixelRatio}`
323
+ const screenDensity = PixelRatio.getFontScale()
324
+ return `${pixelRatioInfo}, Font Scale: ${screenDensity}`
325
+ }
326
+
327
+ return {
328
+ // Core platform info
329
+ platform: platformInfo.isExpo ? 'expo' : 'react-native',
330
+ userAgent: getBrowserInfo(),
331
+ language: 'en', // Could be enhanced with react-native-localize if available
332
+ timestamp: new Date().toISOString(),
333
+
334
+ // Device and OS information
335
+ deviceInfo: getDeviceInfo(),
336
+ osInfo: getOSInfo(),
337
+ browserInfo: getBrowserInfo(),
338
+
339
+ // Screen information
340
+ screenSize: getScreenSize(),
341
+ pixelRatio: pixelRatio,
342
+ orientation: getOrientation(),
343
+ screenWidth: Math.round(screenData.width),
344
+ screenHeight: Math.round(screenData.height),
345
+ screenScale: pixelRatio,
346
+
347
+ // Device capabilities
348
+ hardwareConcurrency: 1, // React Native doesn't expose CPU cores directly
349
+ cookiesEnabled: 'N/A', // Not applicable in React Native
350
+
351
+ // App information
352
+ packageVersion: version,
353
+ appInfo: getAppInfo(),
354
+ appName: getAppName(),
355
+ appVersion: getAppVersion(),
356
+ bundleId: getBundleId(),
357
+ buildNumber: getBuildNumber(),
358
+
359
+ // Platform specific
360
+ platformType: platformInfo.platform,
361
+ platformVersion: platformInfo.platformVersion,
362
+ expoVersion: platformInfo.expoVersion,
363
+ deviceType: getDeviceType(),
364
+
365
+ // Additional device info
366
+ deviceModel: Platform.OS === 'ios' ? 'iOS Device' : 'Android Device',
367
+ deviceManufacturer: Platform.OS === 'ios' ? 'Apple' : 'Android Manufacturer',
368
+ deviceBrand: Platform.OS === 'ios' ? 'Apple' : 'Android Brand',
369
+
370
+ // Performance and hardware
371
+ hardwareInfo: getHardwareInfo(),
372
+ networkInfo: getNetworkInfo(),
373
+
374
+ // Screen details
375
+ screenDensity: PixelRatio.getFontScale(),
376
+ screenScaleFactor: pixelRatio,
377
+ windowWidth: Math.round(screenData.width),
378
+ windowHeight: Math.round(screenData.height),
379
+ fullScreenWidth: Math.round(screenDataScreen.width),
380
+ fullScreenHeight: Math.round(screenDataScreen.height),
381
+
382
+ // Environment info
383
+ isExpo: platformInfo.isExpo,
384
+ isReactNative: platformInfo.isReactNative,
385
+ environment: platformInfo.isExpo ? 'expo' : 'react-native',
386
+
387
+ // Additional platform attributes
388
+ ...getPlatformAttributes(),
389
+ }
390
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Request utility functions for React Native
3
+ */
4
+
5
+ /**
6
+ * Convert FormData to query string (React Native compatible)
7
+ * @param formData - FormData object
8
+ * @returns Query string
9
+ */
10
+ export const formDataToQuery = (formData: any): string => {
11
+ if (!formData) return ''
12
+
13
+ // In React Native, FormData is handled differently
14
+ // This is a simplified implementation
15
+ const params = new URLSearchParams()
16
+
17
+ if (typeof formData.entries === 'function') {
18
+ for (const [key, value] of formData.entries()) {
19
+ params.append(key, value as string)
20
+ }
21
+ } else if (typeof formData === 'object') {
22
+ for (const [key, value] of Object.entries(formData)) {
23
+ params.append(key, String(value))
24
+ }
25
+ }
26
+
27
+ return params.toString()
28
+ }
29
+
30
+ /**
31
+ * Convert object to query string
32
+ * @param obj - Object to convert
33
+ * @returns Query string
34
+ */
35
+ export const objectToQuery = (obj: Record<string, any>): string => {
36
+ const params = new URLSearchParams()
37
+
38
+ for (const [key, value] of Object.entries(obj)) {
39
+ if (value !== null && value !== undefined) {
40
+ params.append(key, String(value))
41
+ }
42
+ }
43
+
44
+ return params.toString()
45
+ }
46
+
47
+ /**
48
+ * Parse query string to object
49
+ * @param queryString - Query string to parse
50
+ * @returns Object with parsed parameters
51
+ */
52
+ export const queryToObject = (queryString: string): Record<string, string> => {
53
+ const params = new URLSearchParams(queryString)
54
+ const obj: Record<string, string> = {}
55
+
56
+ for (const [key, value] of params.entries()) {
57
+ obj[key] = value
58
+ }
59
+
60
+ return obj
61
+ }