react-native-inapp-inspector 2.3.19 → 2.3.21

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 (353) hide show
  1. package/README.md +14 -14
  2. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +67 -0
  3. package/dist/commonjs/analytics.js +1 -24
  4. package/dist/commonjs/bundle.js +1 -16
  5. package/dist/commonjs/capture.js +1 -35
  6. package/dist/commonjs/components/AnalyticsDetail.js +92 -623
  7. package/dist/commonjs/components/AnalyticsEventCard.js +54 -350
  8. package/dist/commonjs/components/AnalyticsGraph.js +70 -370
  9. package/dist/commonjs/components/AnimatedEntrance.js +1 -11
  10. package/dist/commonjs/components/AppHeaderLogo.js +7 -134
  11. package/dist/commonjs/components/BrandCircleIcon.js +73 -118
  12. package/dist/commonjs/components/BrandSquareIcon.js +63 -108
  13. package/dist/commonjs/components/CodeSnippet.js +48 -623
  14. package/dist/commonjs/components/ConsoleLogCard.js +63 -537
  15. package/dist/commonjs/components/CopyButton.js +3 -89
  16. package/dist/commonjs/components/DiffViewer.js +19 -83
  17. package/dist/commonjs/components/DomainHeader.js +27 -127
  18. package/dist/commonjs/components/EmptyState.js +10 -87
  19. package/dist/commonjs/components/EndOfListFooter.js +7 -81
  20. package/dist/commonjs/components/ErrorBoundary.js +72 -566
  21. package/dist/commonjs/components/HeadersSection.js +31 -110
  22. package/dist/commonjs/components/HighlightText.js +17 -58
  23. package/dist/commonjs/components/Inspector/AboutModal.d.ts +6 -0
  24. package/dist/commonjs/components/Inspector/AboutModal.js +243 -0
  25. package/dist/commonjs/components/Inspector/AnalyticsFilterModal.js +98 -797
  26. package/dist/commonjs/components/Inspector/AnalyticsTab.js +284 -930
  27. package/dist/commonjs/components/Inspector/BundleTab.js +551 -2918
  28. package/dist/commonjs/components/Inspector/ConsoleTab.js +79 -492
  29. package/dist/commonjs/components/Inspector/CrashDetail.js +123 -672
  30. package/dist/commonjs/components/Inspector/CrashFilterModal.js +66 -498
  31. package/dist/commonjs/components/Inspector/CrashTab.js +98 -670
  32. package/dist/commonjs/components/Inspector/DebuggingTab.js +74 -875
  33. package/dist/commonjs/components/Inspector/DeviceInfoTab.js +143 -907
  34. package/dist/commonjs/components/Inspector/FabLauncher.js +42 -466
  35. package/dist/commonjs/components/Inspector/FeatureUnderDevNotice.js +6 -99
  36. package/dist/commonjs/components/Inspector/InspectorContext.js +1 -21
  37. package/dist/commonjs/components/Inspector/InspectorHeader.js +191 -1110
  38. package/dist/commonjs/components/Inspector/LogDetail.js +168 -994
  39. package/dist/commonjs/components/Inspector/MainScreen.js +52 -335
  40. package/dist/commonjs/components/Inspector/MediaGalleryTab.js +97 -644
  41. package/dist/commonjs/components/Inspector/MediaPreviewModal.js +90 -605
  42. package/dist/commonjs/components/Inspector/NavigationTracker.js +1 -12
  43. package/dist/commonjs/components/Inspector/NetworkDetail.js +89 -411
  44. package/dist/commonjs/components/Inspector/NetworkFilterModal.js +60 -576
  45. package/dist/commonjs/components/Inspector/NetworkTab.js +84 -514
  46. package/dist/commonjs/components/Inspector/NpmStarPrompt.js +18 -434
  47. package/dist/commonjs/components/Inspector/NpmUpdateToast.js +25 -298
  48. package/dist/commonjs/components/Inspector/PerformanceTab.js +182 -1397
  49. package/dist/commonjs/components/Inspector/ReduxDetail.js +188 -1089
  50. package/dist/commonjs/components/Inspector/ReduxTab.js +85 -701
  51. package/dist/commonjs/components/Inspector/SettingsPanel.js +427 -3807
  52. package/dist/commonjs/components/Inspector/SkeletonPlaceholder.js +11 -180
  53. package/dist/commonjs/components/Inspector/StorageTab.js +93 -826
  54. package/dist/commonjs/components/Inspector/TabBar.js +36 -334
  55. package/dist/commonjs/components/Inspector/UpdateAvailableModal.js +37 -412
  56. package/dist/commonjs/components/JsonViewer.js +168 -381
  57. package/dist/commonjs/components/LogCard.js +57 -567
  58. package/dist/commonjs/components/LogSyntaxHighlighter.js +11 -130
  59. package/dist/commonjs/components/MetaAccordion.js +69 -238
  60. package/dist/commonjs/components/MiniBarChart.js +3 -56
  61. package/dist/commonjs/components/MiniLineChart.js +3 -58
  62. package/dist/commonjs/components/NetworkIcons.d.ts +13 -0
  63. package/dist/commonjs/components/NetworkIcons.js +521 -1022
  64. package/dist/commonjs/components/QRCodeView.js +5 -79
  65. package/dist/commonjs/components/SectionHeader.js +15 -37
  66. package/dist/commonjs/components/SegmentedTabs.js +7 -62
  67. package/dist/commonjs/components/SkeletonPlaceholder.js +11 -180
  68. package/dist/commonjs/components/Slider.js +15 -243
  69. package/dist/commonjs/components/SourcePageCard.js +30 -89
  70. package/dist/commonjs/components/Toast.js +5 -147
  71. package/dist/commonjs/components/TouchableScale.js +3 -24
  72. package/dist/commonjs/components/TreeNode.js +55 -130
  73. package/dist/commonjs/console.js +1 -16
  74. package/dist/commonjs/constants/index.js +1 -32
  75. package/dist/commonjs/constants/telemetryConfig.js +1 -6
  76. package/dist/commonjs/constants/version.d.ts +1 -1
  77. package/dist/commonjs/constants/version.js +1 -4
  78. package/dist/commonjs/crash.js +1 -23
  79. package/dist/commonjs/customHooks/analyticsLogger.js +1 -250
  80. package/dist/commonjs/customHooks/bundleAnalyzer.js +1 -957
  81. package/dist/commonjs/customHooks/consoleLogger.js +6 -445
  82. package/dist/commonjs/customHooks/crashHandler.js +7 -656
  83. package/dist/commonjs/customHooks/logFilters.js +1 -23
  84. package/dist/commonjs/customHooks/networkLogger.js +2 -536
  85. package/dist/commonjs/customHooks/performanceTracker.js +63 -750
  86. package/dist/commonjs/customHooks/reduxLogger.js +2 -313
  87. package/dist/commonjs/customHooks/storageInspector.js +1 -435
  88. package/dist/commonjs/customHooks/useAccordion.js +1 -18
  89. package/dist/commonjs/decorators/index.js +1 -100
  90. package/dist/commonjs/helpers/gaAnalyticsRegistry.js +1 -148
  91. package/dist/commonjs/helpers/index.d.ts +1 -0
  92. package/dist/commonjs/helpers/index.js +8 -921
  93. package/dist/commonjs/helpers/memoryManager.js +1 -99
  94. package/dist/commonjs/helpers/qrGenerator.js +1 -367
  95. package/dist/commonjs/helpers/remoteConfig.js +1 -165
  96. package/dist/commonjs/helpers/searchQueryParser.js +1 -62
  97. package/dist/commonjs/helpers/settingsStore.js +1 -144
  98. package/dist/commonjs/helpers/telemetry.js +1 -501
  99. package/dist/commonjs/helpers/toast.js +1 -20
  100. package/dist/commonjs/i18n/index.js +1 -90
  101. package/dist/commonjs/i18n/locales/en.json +1 -1140
  102. package/dist/commonjs/index.js +5 -2168
  103. package/dist/commonjs/metro.js +6 -468
  104. package/dist/commonjs/native/NativeInspector.d.ts +1 -0
  105. package/dist/commonjs/native/NativeInspector.js +1 -415
  106. package/dist/commonjs/native/NativeNetworkInspector.js +1 -4
  107. package/dist/commonjs/network.js +1 -13
  108. package/dist/commonjs/performance.js +1 -29
  109. package/dist/commonjs/redux.js +1 -20
  110. package/dist/commonjs/storage.js +1 -14
  111. package/dist/commonjs/styles/AppColors.js +1 -423
  112. package/dist/commonjs/styles/AppFonts.js +1 -17
  113. package/dist/commonjs/styles/common.js +1 -190
  114. package/dist/commonjs/styles/index.d.ts +6 -0
  115. package/dist/commonjs/styles/index.js +1 -1589
  116. package/dist/commonjs/types/enums.d.ts +0 -1
  117. package/dist/commonjs/types/enums.js +1 -152
  118. package/dist/commonjs/types/index.js +1 -18
  119. package/dist/commonjs/types/interfaces.d.ts +2 -2
  120. package/dist/commonjs/types/interfaces.js +1 -2
  121. package/dist/esm/analytics.js +1 -2
  122. package/dist/esm/bundle.js +1 -2
  123. package/dist/esm/capture.js +1 -32
  124. package/dist/esm/components/AnalyticsDetail.js +171 -664
  125. package/dist/esm/components/AnalyticsEventCard.js +77 -335
  126. package/dist/esm/components/AnalyticsGraph.js +95 -357
  127. package/dist/esm/components/AnimatedEntrance.js +1 -6
  128. package/dist/esm/components/AppHeaderLogo.js +7 -94
  129. package/dist/esm/components/BrandCircleIcon.js +114 -119
  130. package/dist/esm/components/BrandSquareIcon.js +73 -78
  131. package/dist/esm/components/CodeSnippet.js +58 -595
  132. package/dist/esm/components/ConsoleLogCard.js +91 -526
  133. package/dist/esm/components/CopyButton.js +3 -51
  134. package/dist/esm/components/DiffViewer.js +22 -48
  135. package/dist/esm/components/DomainHeader.js +43 -138
  136. package/dist/esm/components/EmptyState.js +17 -56
  137. package/dist/esm/components/EndOfListFooter.js +15 -83
  138. package/dist/esm/components/ErrorBoundary.js +125 -582
  139. package/dist/esm/components/HeadersSection.js +52 -93
  140. package/dist/esm/components/HighlightText.js +17 -53
  141. package/dist/esm/components/Inspector/AboutModal.d.ts +6 -0
  142. package/dist/esm/components/Inspector/AboutModal.js +243 -0
  143. package/dist/esm/components/Inspector/AnalyticsFilterModal.js +225 -886
  144. package/dist/esm/components/Inspector/AnalyticsTab.js +296 -904
  145. package/dist/esm/components/Inspector/BundleTab.js +1175 -3502
  146. package/dist/esm/components/Inspector/ConsoleTab.js +113 -488
  147. package/dist/esm/components/Inspector/CrashDetail.js +236 -747
  148. package/dist/esm/components/Inspector/CrashFilterModal.js +149 -541
  149. package/dist/esm/components/Inspector/CrashTab.js +177 -711
  150. package/dist/esm/components/Inspector/DebuggingTab.js +164 -925
  151. package/dist/esm/components/Inspector/DeviceInfoTab.js +199 -924
  152. package/dist/esm/components/Inspector/FabLauncher.js +68 -454
  153. package/dist/esm/components/Inspector/FeatureUnderDevNotice.js +14 -70
  154. package/dist/esm/components/Inspector/InspectorContext.js +1 -12
  155. package/dist/esm/components/Inspector/InspectorHeader.js +282 -1163
  156. package/dist/esm/components/Inspector/LogDetail.js +235 -1023
  157. package/dist/esm/components/Inspector/MainScreen.js +78 -323
  158. package/dist/esm/components/Inspector/MediaGalleryTab.js +143 -650
  159. package/dist/esm/components/Inspector/MediaPreviewModal.js +138 -613
  160. package/dist/esm/components/Inspector/NavigationTracker.js +1 -10
  161. package/dist/esm/components/Inspector/NetworkDetail.js +138 -455
  162. package/dist/esm/components/Inspector/NetworkFilterModal.js +122 -597
  163. package/dist/esm/components/Inspector/NetworkTab.js +125 -517
  164. package/dist/esm/components/Inspector/NpmStarPrompt.js +58 -434
  165. package/dist/esm/components/Inspector/NpmUpdateToast.js +45 -281
  166. package/dist/esm/components/Inspector/PerformanceTab.js +393 -1570
  167. package/dist/esm/components/Inspector/ReduxDetail.js +307 -1170
  168. package/dist/esm/components/Inspector/ReduxTab.js +144 -722
  169. package/dist/esm/components/Inspector/SettingsPanel.js +643 -3985
  170. package/dist/esm/components/Inspector/SkeletonPlaceholder.js +30 -163
  171. package/dist/esm/components/Inspector/StorageTab.js +172 -866
  172. package/dist/esm/components/Inspector/TabBar.js +45 -305
  173. package/dist/esm/components/Inspector/UpdateAvailableModal.js +80 -418
  174. package/dist/esm/components/JsonViewer.js +179 -354
  175. package/dist/esm/components/LogCard.js +93 -565
  176. package/dist/esm/components/LogSyntaxHighlighter.js +11 -123
  177. package/dist/esm/components/MetaAccordion.js +136 -267
  178. package/dist/esm/components/MiniBarChart.js +3 -18
  179. package/dist/esm/components/MiniLineChart.js +3 -20
  180. package/dist/esm/components/NetworkIcons.d.ts +13 -0
  181. package/dist/esm/components/NetworkIcons.js +523 -838
  182. package/dist/esm/components/QRCodeView.js +8 -45
  183. package/dist/esm/components/SectionHeader.js +22 -39
  184. package/dist/esm/components/SegmentedTabs.js +8 -58
  185. package/dist/esm/components/SkeletonPlaceholder.js +30 -163
  186. package/dist/esm/components/Slider.js +24 -213
  187. package/dist/esm/components/SourcePageCard.js +65 -86
  188. package/dist/esm/components/Toast.js +10 -117
  189. package/dist/esm/components/TouchableScale.js +3 -19
  190. package/dist/esm/components/TreeNode.js +58 -95
  191. package/dist/esm/console.js +1 -2
  192. package/dist/esm/constants/index.js +1 -28
  193. package/dist/esm/constants/telemetryConfig.js +1 -3
  194. package/dist/esm/constants/version.d.ts +1 -1
  195. package/dist/esm/constants/version.js +1 -1
  196. package/dist/esm/crash.js +1 -2
  197. package/dist/esm/customHooks/analyticsLogger.js +1 -230
  198. package/dist/esm/customHooks/bundleAnalyzer.js +1 -944
  199. package/dist/esm/customHooks/consoleLogger.js +6 -432
  200. package/dist/esm/customHooks/crashHandler.js +7 -630
  201. package/dist/esm/customHooks/logFilters.js +1 -20
  202. package/dist/esm/customHooks/networkLogger.js +2 -519
  203. package/dist/esm/customHooks/performanceTracker.js +63 -723
  204. package/dist/esm/customHooks/reduxLogger.js +2 -293
  205. package/dist/esm/customHooks/storageInspector.js +1 -418
  206. package/dist/esm/customHooks/useAccordion.js +1 -16
  207. package/dist/esm/decorators/index.js +1 -95
  208. package/dist/esm/helpers/gaAnalyticsRegistry.js +1 -143
  209. package/dist/esm/helpers/index.d.ts +1 -0
  210. package/dist/esm/helpers/index.js +8 -861
  211. package/dist/esm/helpers/memoryManager.js +1 -91
  212. package/dist/esm/helpers/qrGenerator.js +1 -364
  213. package/dist/esm/helpers/remoteConfig.js +1 -160
  214. package/dist/esm/helpers/searchQueryParser.js +1 -59
  215. package/dist/esm/helpers/settingsStore.js +1 -134
  216. package/dist/esm/helpers/telemetry.js +1 -489
  217. package/dist/esm/helpers/toast.js +1 -15
  218. package/dist/esm/i18n/index.js +1 -78
  219. package/dist/esm/i18n/locales/en.json +1 -1140
  220. package/dist/esm/index.js +7 -1982
  221. package/dist/esm/metro.js +6 -459
  222. package/dist/esm/native/NativeInspector.d.ts +1 -0
  223. package/dist/esm/native/NativeInspector.js +1 -382
  224. package/dist/esm/native/NativeNetworkInspector.js +1 -2
  225. package/dist/esm/network.js +1 -1
  226. package/dist/esm/performance.js +1 -2
  227. package/dist/esm/redux.js +1 -1
  228. package/dist/esm/storage.js +1 -1
  229. package/dist/esm/styles/AppColors.js +1 -417
  230. package/dist/esm/styles/AppFonts.js +1 -13
  231. package/dist/esm/styles/common.js +1 -186
  232. package/dist/esm/styles/index.d.ts +6 -0
  233. package/dist/esm/styles/index.js +1 -1551
  234. package/dist/esm/types/enums.d.ts +0 -1
  235. package/dist/esm/types/enums.js +1 -149
  236. package/dist/esm/types/index.js +1 -2
  237. package/dist/esm/types/interfaces.d.ts +2 -2
  238. package/dist/esm/types/interfaces.js +1 -1
  239. package/ios/NetworkInspectorModule.mm +60 -12
  240. package/package.json +5 -3
  241. package/src/native/NativeInspector.ts +18 -0
  242. package/dist/commonjs/components/Inspector/FeedbackModal.d.ts +0 -7
  243. package/dist/commonjs/components/Inspector/FeedbackModal.js +0 -552
  244. package/dist/esm/components/Inspector/FeedbackModal.d.ts +0 -7
  245. package/dist/esm/components/Inspector/FeedbackModal.js +0 -515
  246. package/src/analytics.ts +0 -35
  247. package/src/bundle.ts +0 -25
  248. package/src/capture.ts +0 -109
  249. package/src/components/AnalyticsDetail.tsx +0 -870
  250. package/src/components/AnalyticsEventCard.tsx +0 -444
  251. package/src/components/AnalyticsGraph.tsx +0 -583
  252. package/src/components/AnimatedEntrance.tsx +0 -20
  253. package/src/components/AppHeaderLogo.tsx +0 -143
  254. package/src/components/BrandCircleIcon.tsx +0 -144
  255. package/src/components/BrandSquareIcon.tsx +0 -184
  256. package/src/components/CodeSnippet.tsx +0 -725
  257. package/src/components/ConsoleLogCard.tsx +0 -679
  258. package/src/components/CopyButton.tsx +0 -87
  259. package/src/components/DiffViewer.tsx +0 -82
  260. package/src/components/DomainHeader.tsx +0 -224
  261. package/src/components/EmptyState.tsx +0 -97
  262. package/src/components/EndOfListFooter.tsx +0 -100
  263. package/src/components/ErrorBoundary.tsx +0 -688
  264. package/src/components/HeadersSection.tsx +0 -172
  265. package/src/components/HighlightText.tsx +0 -100
  266. package/src/components/Inspector/AnalyticsFilterModal.tsx +0 -1250
  267. package/src/components/Inspector/AnalyticsTab.tsx +0 -1362
  268. package/src/components/Inspector/BundleTab.tsx +0 -4768
  269. package/src/components/Inspector/ConsoleTab.tsx +0 -726
  270. package/src/components/Inspector/CrashDetail.tsx +0 -997
  271. package/src/components/Inspector/CrashFilterModal.tsx +0 -721
  272. package/src/components/Inspector/CrashTab.tsx +0 -933
  273. package/src/components/Inspector/DebuggingTab.tsx +0 -1103
  274. package/src/components/Inspector/DeviceInfoTab.tsx +0 -1397
  275. package/src/components/Inspector/FabLauncher.tsx +0 -590
  276. package/src/components/Inspector/FeatureUnderDevNotice.tsx +0 -94
  277. package/src/components/Inspector/FeedbackModal.tsx +0 -626
  278. package/src/components/Inspector/InspectorContext.tsx +0 -28
  279. package/src/components/Inspector/InspectorHeader.tsx +0 -1583
  280. package/src/components/Inspector/LogDetail.tsx +0 -1431
  281. package/src/components/Inspector/MainScreen.tsx +0 -460
  282. package/src/components/Inspector/MediaGalleryTab.tsx +0 -829
  283. package/src/components/Inspector/MediaPreviewModal.tsx +0 -783
  284. package/src/components/Inspector/NavigationTracker.tsx +0 -13
  285. package/src/components/Inspector/NetworkDetail.tsx +0 -778
  286. package/src/components/Inspector/NetworkFilterModal.tsx +0 -730
  287. package/src/components/Inspector/NetworkTab.tsx +0 -820
  288. package/src/components/Inspector/NpmStarPrompt.tsx +0 -532
  289. package/src/components/Inspector/NpmUpdateToast.tsx +0 -387
  290. package/src/components/Inspector/PerformanceTab.tsx +0 -1967
  291. package/src/components/Inspector/ReduxDetail.tsx +0 -1651
  292. package/src/components/Inspector/ReduxTab.tsx +0 -987
  293. package/src/components/Inspector/SettingsPanel.tsx +0 -5136
  294. package/src/components/Inspector/SkeletonPlaceholder.tsx +0 -206
  295. package/src/components/Inspector/StorageTab.tsx +0 -1159
  296. package/src/components/Inspector/TabBar.tsx +0 -431
  297. package/src/components/Inspector/UpdateAvailableModal.tsx +0 -555
  298. package/src/components/JsonViewer.tsx +0 -549
  299. package/src/components/LogCard.tsx +0 -694
  300. package/src/components/LogSyntaxHighlighter.tsx +0 -178
  301. package/src/components/MetaAccordion.tsx +0 -355
  302. package/src/components/MiniBarChart.tsx +0 -42
  303. package/src/components/MiniLineChart.tsx +0 -33
  304. package/src/components/NetworkIcons.tsx +0 -2655
  305. package/src/components/QRCodeView.tsx +0 -75
  306. package/src/components/SectionHeader.tsx +0 -101
  307. package/src/components/SegmentedTabs.tsx +0 -84
  308. package/src/components/SkeletonPlaceholder.tsx +0 -206
  309. package/src/components/Slider.tsx +0 -300
  310. package/src/components/SourcePageCard.tsx +0 -150
  311. package/src/components/Toast.tsx +0 -131
  312. package/src/components/TouchableScale.tsx +0 -61
  313. package/src/components/TreeNode.tsx +0 -171
  314. package/src/console.ts +0 -24
  315. package/src/constants/index.ts +0 -41
  316. package/src/constants/telemetryConfig.ts +0 -4
  317. package/src/constants/version.ts +0 -3
  318. package/src/crash.ts +0 -32
  319. package/src/customHooks/analyticsLogger.ts +0 -336
  320. package/src/customHooks/bundleAnalyzer.ts +0 -1256
  321. package/src/customHooks/consoleLogger.ts +0 -530
  322. package/src/customHooks/crashHandler.ts +0 -944
  323. package/src/customHooks/logFilters.ts +0 -32
  324. package/src/customHooks/networkLogger.ts +0 -624
  325. package/src/customHooks/performanceTracker.ts +0 -1014
  326. package/src/customHooks/reduxLogger.ts +0 -406
  327. package/src/customHooks/storageInspector.ts +0 -513
  328. package/src/customHooks/useAccordion.tsx +0 -27
  329. package/src/decorators/index.ts +0 -184
  330. package/src/helpers/gaAnalyticsRegistry.ts +0 -204
  331. package/src/helpers/index.ts +0 -1092
  332. package/src/helpers/memoryManager.ts +0 -138
  333. package/src/helpers/qrGenerator.ts +0 -430
  334. package/src/helpers/remoteConfig.ts +0 -212
  335. package/src/helpers/searchQueryParser.ts +0 -84
  336. package/src/helpers/settingsStore.ts +0 -171
  337. package/src/helpers/telemetry.ts +0 -568
  338. package/src/helpers/toast.ts +0 -17
  339. package/src/i18n/index.ts +0 -104
  340. package/src/i18n/locales/en.json +0 -1141
  341. package/src/index.tsx +0 -2650
  342. package/src/metro.ts +0 -598
  343. package/src/network.ts +0 -22
  344. package/src/performance.ts +0 -38
  345. package/src/redux.ts +0 -23
  346. package/src/storage.ts +0 -18
  347. package/src/styles/AppColors.ts +0 -455
  348. package/src/styles/AppFonts.ts +0 -26
  349. package/src/styles/common.ts +0 -209
  350. package/src/styles/index.ts +0 -1606
  351. package/src/types/enums.ts +0 -210
  352. package/src/types/index.ts +0 -34
  353. package/src/types/interfaces.ts +0 -535
package/src/index.tsx DELETED
@@ -1,2650 +0,0 @@
1
- import React, {useEffect, useMemo, useRef, useState, useCallback} from 'react';
2
- import {
3
- Alert,
4
- Animated,
5
- Easing,
6
- PanResponder,
7
- Platform,
8
- UIManager,
9
- LogBox,
10
- InteractionManager,
11
- } from 'react-native';
12
- import {NavigationContext} from '@react-navigation/native';
13
-
14
- // i18n
15
- import {I18nextProvider, i18n} from './i18n';
16
-
17
- // Components
18
- import ErrorBoundary from './components/ErrorBoundary';
19
- import MainScreen from './components/Inspector/MainScreen';
20
- import {
21
- InspectorContext,
22
- animateNextLayout,
23
- InspectorContextValue,
24
- } from './components/Inspector/InspectorContext';
25
-
26
- // Helpers
27
- import {
28
- formatDisplayUrl,
29
- getNavigationInfo,
30
- getLogPageName,
31
- deduplicateLogs,
32
- getDomainColor,
33
- getEventCategory,
34
- matchNetworkLogQuery,
35
- setupMemoryWarningHandler,
36
- pruneAllLogs,
37
- subscribeMemoryWarning,
38
- } from './helpers';
39
- // #5 — settings persistence
40
- import {
41
- loadSettings,
42
- saveSettings,
43
- setCustomStorage,
44
- clearPersistedSettings,
45
- calculateRamBasedLimits,
46
- } from './helpers/settingsStore';
47
- import {
48
- getNativeSystemMetrics,
49
- pushNativeLogRecord,
50
- fetchNativeCachedPage,
51
- } from './native/NativeInspector';
52
-
53
- // Network
54
- import {
55
- setupNetworkLogger,
56
- clearNetworkLogs,
57
- subscribeNetworkLogs,
58
- setNetworkModuleEnabled,
59
- setMaxNetworkLogsLimit,
60
- setRouteInfoProvider,
61
- } from './customHooks/networkLogger';
62
-
63
- // Console
64
- import {
65
- setupConsoleLogger,
66
- clearConsoleLogs,
67
- subscribeConsoleLogs,
68
- getConsoleLogs,
69
- setConsoleModuleEnabled,
70
- setMaxConsoleLogsLimit,
71
- } from './customHooks/consoleLogger';
72
- import {IGNORED_LOG_PREFIXES} from './customHooks/logFilters';
73
-
74
- // Crash Protection
75
- import {
76
- setupGlobalCrashHandler,
77
- subscribeCrashEvents,
78
- emitCrashEvent,
79
- getCrashRecords,
80
- clearCrashRecords,
81
- simulateTestCrash,
82
- exportCrashReport,
83
- parseCrashStackTrace,
84
- setMaxCrashLogsLimit,
85
- setCrashModuleEnabled,
86
- } from './customHooks/crashHandler';
87
-
88
- import {
89
- trackActiveTelemetryHeartbeat,
90
- setTelemetryEnabled,
91
- trackInspectorTabView,
92
- trackInspectorScreenView,
93
- } from './helpers/telemetry';
94
-
95
- import {
96
- subscribeAnalyticsEvents,
97
- clearAnalyticsEvents,
98
- autoSetupAnalyticsLogger,
99
- isAnalyticsConnected,
100
- setAnalyticsModuleEnabled,
101
- setMaxAnalyticsLogsLimit,
102
- } from './customHooks/analyticsLogger';
103
-
104
- import {
105
- getReduxState,
106
- subscribeReduxState,
107
- setReduxAutoRefresh,
108
- getLastActionForReducer,
109
- clearActionHistory,
110
- isReduxConnected,
111
- setReduxModuleEnabled,
112
- } from './customHooks/reduxLogger';
113
-
114
- import {setPerformanceModuleEnabled} from './customHooks/performanceTracker';
115
- import {setBundleModuleEnabled} from './customHooks/bundleAnalyzer';
116
- import {
117
- showNativeFloatingButton,
118
- hideNativeFloatingButton,
119
- setNativeFloatingButtonBadge,
120
- subscribeNativeFloatingButtonPress,
121
- subscribeNativeDeviceShake,
122
- startNativeFpsMonitoring,
123
- stopNativeFpsMonitoring,
124
- getNativeFpsMetrics,
125
- getNativeStorageItem,
126
- setNativeStorageItem,
127
- isNativeModuleAvailable,
128
- } from './native/NativeInspector';
129
-
130
- import {
131
- fetchRemoteConfigModuleStatus,
132
- isFirebaseRemoteConfigAvailable,
133
- } from './helpers/remoteConfig';
134
- import {ScreenCapture} from './capture';
135
-
136
- // Constants
137
- import {
138
- StatusFilter,
139
- NetworkLog,
140
- RouteInfo,
141
- LocalFilter,
142
- SortOrder,
143
- GroupedListItem,
144
- ActiveTab,
145
- SettingsSubTab,
146
- ConsoleLog,
147
- Method,
148
- AnalyticsEvent,
149
- AnalyticsFilters,
150
- NetworkInspectorProps,
151
- CrashRecord,
152
- ParsedStackFrame,
153
- CrashBreadcrumb,
154
- SearchScope,
155
- } from './types';
156
- import {LIB_VERSION} from './constants';
157
-
158
- // Stylesheet
159
- import {toggleGlobalTheme} from './styles';
160
-
161
- const NetworkInspector = ({
162
- enabled = true,
163
- storage,
164
- navigationRef,
165
- appIcon,
166
- environment,
167
- initialVisible = false,
168
- visible: controlledVisible,
169
- remoteConfig,
170
- }: NetworkInspectorProps): React.JSX.Element | null => {
171
- // Set custom storage synchronously during render phase
172
- setCustomStorage(storage || null);
173
-
174
- const [isDark, setIsDark] = useState(false);
175
- const [reduxState, setReduxState] = useState<any>(null);
176
- // Per-reducer last action is kept in component state so the
177
- // Redux tab re-renders live on every dispatch, independent of the state tree ref.
178
- const [reduxLastActionMap, setReduxLastActionMap] = useState<
179
- Record<string, any>
180
- >({});
181
- // Inspector panel height as a percentage of the screen (configurable in Settings).
182
- const [modalHeightPercent, setModalHeightPercent] = useState<number>(90);
183
- const [modalAnimationType, setModalAnimationType] = useState<'slide' | 'fade' | 'none'>('slide');
184
-
185
- const [logs, setLogs] = useState<NetworkLog[]>([]);
186
- const [visible, setVisible] = useState<boolean>(
187
- controlledVisible ?? initialVisible ?? false,
188
- );
189
- const [isMinimized, setIsMinimized] = useState<boolean>(false);
190
-
191
- const minimizeInspector = useCallback(() => {
192
- setIsMinimized(true);
193
- setVisible(false);
194
- }, []);
195
-
196
- useEffect(() => {
197
- if (controlledVisible !== undefined) {
198
- setVisible(controlledVisible);
199
- }
200
- }, [controlledVisible]);
201
-
202
- const [isReady, setIsReady] = useState(false);
203
- const [selected, setSelected] = useState<NetworkLog | null>(null);
204
- const [selectedLogs, setSelectedLogs] = useState<Set<number>>(new Set());
205
- const [search, setSearch] = useState('');
206
- const [searchScope, setSearchScope] = useState<SearchScope>('all');
207
- const [isRegexSearch, setIsRegexSearch] = useState<boolean>(false);
208
- const [isCaseSensitive, setIsCaseSensitive] = useState<boolean>(false);
209
- const [quickFilter, setQuickFilter] = useState<string>('all');
210
- const [detailSearch, setDetailSearch] = useState('');
211
- const [reduxSearch, setReduxSearch] = useState('');
212
- const [selectedReduxSlice, setSelectedReduxSlice] = useState<string | null>(null);
213
- const [selectedReduxAction, setSelectedReduxAction] = useState<any | null>(null);
214
- const [reduxActiveSubTab, setReduxActiveSubTab] = useState<'state' | 'timeline'>('state');
215
-
216
- const [apiDetailActiveTab, setApiDetailActiveTab] = useState<
217
- 'metadata' | 'headers' | 'request' | 'response'
218
- >('response');
219
-
220
- useEffect(() => {
221
- LogBox.ignoreAllLogs(enabled);
222
- return () => {
223
- LogBox.ignoreAllLogs(false);
224
- };
225
- }, [enabled]);
226
-
227
- useEffect(() => {
228
- if (selected) {
229
- setApiDetailActiveTab('response');
230
- setDetailSearch('');
231
- }
232
- }, [selected]);
233
- const [statusFilters, setStatusFilters] = useState<Set<StatusFilter>>(
234
- new Set(),
235
- );
236
- const [methodFilters, setMethodFilters] = useState<Set<Method>>(new Set());
237
- const [sectionFilters, setSectionFilters] = useState<
238
- Record<string, Set<LocalFilter>>
239
- >({});
240
- const [collapsedSections, setCollapsedSections] = useState<Set<string>>(
241
- new Set(),
242
- );
243
- const [showHeaderInfo, setShowHeaderInfo] = useState(true);
244
- const [sortOrder, setSortOrder] = useState<SortOrder>('newest');
245
- // #7 — sort order for the Logs (console) tab
246
- const [logSortOrder, setLogSortOrder] = useState<SortOrder>('newest');
247
- const [reqExpanded, setReqExpanded] = useState<boolean | undefined>(true);
248
- const [resExpanded, setResExpanded] = useState<boolean | undefined>(true);
249
- const [showReqDiff, setShowReqDiff] = useState<boolean>(false);
250
- const [showResDiff, setShowResDiff] = useState<boolean>(false);
251
-
252
- // ─── Analytics state ───────────────────────────────────────────────────────
253
- const [activeTab, setActiveTab] = useState<ActiveTab>('apis');
254
- const [analyticsEvents, setAnalyticsEvents] = useState<AnalyticsEvent[]>([]);
255
-
256
- // ─── Logs state ────────────────────────────────────────────────────────────
257
- const [consoleLogs, setConsoleLogs] = useState<ConsoleLog[]>([]);
258
- const [selectedLog, setSelectedLog] = useState<ConsoleLog | null>(null);
259
- // ─── Pause states ─────────────────────────────────────────────────────────
260
- const [isNetworkPaused, setIsNetworkPaused] = useState<boolean>(false);
261
- const isNetworkPausedRef = useRef(isNetworkPaused);
262
- isNetworkPausedRef.current = isNetworkPaused;
263
- const latestNetworkLogsRef = useRef<NetworkLog[]>([]);
264
-
265
- const [isConsolePaused, setIsConsolePaused] = useState<boolean>(false);
266
- const isConsolePausedRef = useRef(isConsolePaused);
267
- isConsolePausedRef.current = isConsolePaused;
268
- const latestConsoleLogsRef = useRef<ConsoleLog[]>([]);
269
-
270
- const [isAnalyticsPaused, setIsAnalyticsPaused] = useState<boolean>(false);
271
- const isAnalyticsPausedRef = useRef(isAnalyticsPaused);
272
- isAnalyticsPausedRef.current = isAnalyticsPaused;
273
- const latestAnalyticsEventsRef = useRef<AnalyticsEvent[]>([]);
274
-
275
- const [lastReadLogsCount, setLastReadLogsCount] = useState(0);
276
- const [lastReadApisCount, setLastReadApisCount] = useState(0);
277
- const [lastReadCrashesCount, setLastReadCrashesCount] = useState(0);
278
- const [mediaCount, setMediaCount] = useState(0);
279
-
280
- const refreshMediaCount = useCallback(async () => {
281
- try {
282
- const items = await ScreenCapture.getMediaList();
283
- const count = items ? items.length : 0;
284
- setMediaCount(count);
285
- return count;
286
- } catch {
287
- setMediaCount(0);
288
- return 0;
289
- }
290
- }, []);
291
-
292
- useEffect(() => {
293
- if (visible) {
294
- refreshMediaCount();
295
- }
296
- }, [visible, refreshMediaCount]);
297
-
298
- // ─── Crash state ───────────────────────────────────────────────────────────
299
- const [crashRecords, setCrashRecords] = useState<CrashRecord[]>(() =>
300
- getCrashRecords(),
301
- );
302
- const [selectedCrash, setSelectedCrash] = useState<CrashRecord | null>(null);
303
- const [maxCrashLogs, setMaxCrashLogs] = useState<number>(50);
304
-
305
- useEffect(() => {
306
- setMaxCrashLogsLimit(maxCrashLogs);
307
- }, [maxCrashLogs]);
308
-
309
- useEffect(() => {
310
- if (visible) {
311
- if (activeTab === 'apis') {
312
- setLastReadApisCount(logs.length);
313
- }
314
- if (activeTab === 'logs') {
315
- setLastReadLogsCount(consoleLogs.length);
316
- }
317
- if (activeTab === 'crash') {
318
- setLastReadCrashesCount(crashRecords.length);
319
- }
320
- }
321
- }, [visible]);
322
-
323
- useEffect(() => {
324
- if (activeTab === 'apis') {
325
- setLastReadApisCount(logs.length);
326
- }
327
- }, [activeTab, logs.length]);
328
-
329
- useEffect(() => {
330
- if (activeTab === 'logs') {
331
- setLastReadLogsCount(consoleLogs.length);
332
- }
333
- }, [activeTab, consoleLogs.length]);
334
-
335
- useEffect(() => {
336
- if (activeTab === 'crash') {
337
- setLastReadCrashesCount(crashRecords.length);
338
- }
339
- }, [activeTab, crashRecords.length]);
340
-
341
- const [maxConsoleLogs, setMaxConsoleLogs] = useState<number>(100);
342
- const [showConsoleLevels, setShowConsoleLevels] = useState<{
343
- info: boolean;
344
- warn: boolean;
345
- error: boolean;
346
- }>({
347
- info: true,
348
- warn: true,
349
- error: true,
350
- });
351
- const visibleConsoleLogs = useMemo(() => {
352
- const filtered = consoleLogs.filter(log => {
353
- const type = log.type;
354
- if (type === 'info' && !showConsoleLevels?.info) return false;
355
- if (type === 'warn' && !showConsoleLevels?.warn) return false;
356
- if (type === 'error' && !showConsoleLevels?.error) return false;
357
-
358
- const message = log.message || '';
359
- const allPrefixes = [
360
- ...((IGNORED_LOG_PREFIXES && IGNORED_LOG_PREFIXES.info) || []),
361
- ...((IGNORED_LOG_PREFIXES && IGNORED_LOG_PREFIXES.warn) || []),
362
- ...((IGNORED_LOG_PREFIXES && IGNORED_LOG_PREFIXES.error) || []),
363
- ].filter(p => typeof p === 'string' && p.trim().length > 0);
364
- const isIgnored = allPrefixes.some(
365
- prefix =>
366
- message
367
- .toLowerCase()
368
- .trim()
369
- .startsWith(prefix.toLowerCase().trim()) ||
370
- message.toLowerCase().trim().includes(prefix.toLowerCase().trim()),
371
- );
372
- return !isIgnored;
373
- });
374
- return filtered.slice(0, maxConsoleLogs);
375
- }, [consoleLogs, showConsoleLevels, maxConsoleLogs]);
376
- const [logSearch, setLogSearch] = useState('');
377
- const [logFilters, setLogFilters] = useState<
378
- Set<'all' | 'info' | 'warn' | 'error' | 'user-log' | 'analytics'>
379
- >(new Set(['all']));
380
-
381
- // ─── Settings state ──────────────────────────────────────────────────────────
382
- const [settingsPage, setSettingsPage] = useState<
383
- | 'main'
384
- | 'apis'
385
- | 'logs'
386
- | 'analytics'
387
- | 'redux'
388
- | null
389
- >(null);
390
- const [isFeedbackOpen, setIsFeedbackOpen] = useState<boolean>(false);
391
- const [settingsActiveSubTab, setSettingsActiveSubTab] = useState<SettingsSubTab>('module');
392
- const [tabVisibility, setTabVisibility] = useState<
393
- Record<ActiveTab, boolean>
394
- >({
395
- apis: true,
396
- logs: true,
397
- analytics: false,
398
- redux: false,
399
- bundle: false,
400
- performance: false,
401
- crash: false,
402
- device: false,
403
- storage: false,
404
- debugging: false,
405
- media: true,
406
- });
407
-
408
- const [maxNetworkLogs, setMaxNetworkLogs] = useState<number>(250);
409
- const [maxAnalyticsEventsLimit, setMaxAnalyticsEventsLimit] = useState<number>(150);
410
- const [isAutoRamLimitEnabled, setIsAutoRamLimitEnabled] = useState<boolean>(true);
411
- const [deviceFreeRamMb, setDeviceFreeRamMb] = useState<number>(1800);
412
-
413
- const [reduxAutoRefresh, setReduxAutoRefreshState] = useState<boolean>(true);
414
- const [reduxExpandDepth, setReduxExpandDepth] = useState<number>(1);
415
-
416
- // #6 — tab the inspector opens on. Shown with a DEFAULT badge in Settings.
417
- const [defaultTab, setDefaultTab] = useState<ActiveTab>('apis');
418
- const [showDuplicateLogs, setShowDuplicateLogs] = useState<boolean>(false);
419
- const [showUpdateToast, setShowUpdateToast] = useState<boolean>(true);
420
-
421
- // Synchronize runtime background listeners with active settings
422
- useEffect(() => {
423
- setNetworkModuleEnabled(!!tabVisibility.apis);
424
- setConsoleModuleEnabled(!!tabVisibility.logs);
425
- setAnalyticsModuleEnabled(!!tabVisibility.analytics);
426
- setReduxModuleEnabled(!!tabVisibility.redux);
427
- setPerformanceModuleEnabled(!!tabVisibility.performance);
428
- setCrashModuleEnabled(!!tabVisibility.crash);
429
- setBundleModuleEnabled(!!tabVisibility.bundle);
430
- }, [tabVisibility]);
431
-
432
- // Query native hardware RAM and auto-tune limits if auto-RAM is active
433
- useEffect(() => {
434
- getNativeSystemMetrics()
435
- .then(metrics => {
436
- if (metrics) {
437
- const freeRam = metrics.totalPhysicalRamMb - metrics.residentRamMb;
438
- const freeMb = Math.max(200, Math.round(freeRam > 0 ? freeRam : 1500));
439
- setDeviceFreeRamMb(freeMb);
440
- if (isAutoRamLimitEnabled) {
441
- const profile = calculateRamBasedLimits(freeMb);
442
- setMaxNetworkLogs(profile.maxNetworkLogs);
443
- setMaxConsoleLogs(profile.maxConsoleLogs);
444
- setMaxAnalyticsEventsLimit(profile.maxAnalyticsEvents);
445
- setMaxCrashLogs(profile.maxCrashRecords);
446
- }
447
- }
448
- })
449
- .catch(() => {});
450
- }, [isAutoRamLimitEnabled]);
451
-
452
- const resetToDefaults = async () => {
453
- await clearPersistedSettings();
454
- setIsDark(false);
455
- toggleGlobalTheme(false);
456
- setModalHeightPercent(90);
457
- setModalAnimationType('slide');
458
- setTabVisibility({
459
- apis: true,
460
- logs: true,
461
- analytics: false,
462
- redux: false,
463
- bundle: false,
464
- performance: false,
465
- crash: false,
466
- device: false,
467
- storage: false,
468
- debugging: false,
469
- media: true,
470
- });
471
-
472
- setDefaultTab('apis');
473
- setIsAutoRamLimitEnabled(true);
474
- const profile = calculateRamBasedLimits(deviceFreeRamMb);
475
- setMaxCrashLogs(profile.maxCrashRecords);
476
- setMaxNetworkLogs(profile.maxNetworkLogs);
477
- setMaxConsoleLogs(profile.maxConsoleLogs);
478
- setMaxAnalyticsEventsLimit(profile.maxAnalyticsEvents);
479
- setShowConsoleLevels({
480
- info: true,
481
- warn: true,
482
- error: true,
483
- });
484
- setReduxAutoRefreshState(true);
485
- setReduxExpandDepth(1);
486
- setShowDuplicateLogs(false);
487
- setShowUpdateToast(true);
488
- Alert.alert('Settings Reset', 'All settings have been reset to default values.');
489
- };
490
-
491
- // #5 — hydrate persisted settings once, then auto-save on any change.
492
- const settingsHydratedRef = useRef(false);
493
- useEffect(() => {
494
- let cancelled = false;
495
- loadSettings().then(saved => {
496
- if (cancelled) return;
497
- if (saved.isDark != null) {
498
- setIsDark(saved.isDark);
499
- toggleGlobalTheme(saved.isDark);
500
- }
501
- if (saved.modalHeightPercent != null)
502
- setModalHeightPercent(saved.modalHeightPercent);
503
- if (saved.modalAnimationType)
504
- setModalAnimationType(saved.modalAnimationType as 'slide' | 'fade' | 'none');
505
- if (saved.tabVisibility)
506
- setTabVisibility(prev => ({
507
- ...prev,
508
- ...(saved.tabVisibility as Record<ActiveTab, boolean>),
509
- apis: true, // APIs is always required
510
- }));
511
- if (saved.defaultTab) setDefaultTab(saved.defaultTab as ActiveTab);
512
- if (saved.isAutoRamLimitEnabled != null)
513
- setIsAutoRamLimitEnabled(saved.isAutoRamLimitEnabled);
514
- if (saved.maxNetworkLogs != null) setMaxNetworkLogs(saved.maxNetworkLogs);
515
- if (saved.maxConsoleLogs != null) setMaxConsoleLogs(saved.maxConsoleLogs);
516
- if (saved.maxAnalyticsEventsLimit != null)
517
- setMaxAnalyticsEventsLimit(saved.maxAnalyticsEventsLimit);
518
- if (saved.maxCrashLogs != null) setMaxCrashLogs(saved.maxCrashLogs);
519
- if (saved.showConsoleLevels)
520
- setShowConsoleLevels(saved.showConsoleLevels);
521
- if (saved.reduxAutoRefresh != null)
522
- setReduxAutoRefreshState(saved.reduxAutoRefresh);
523
- if (saved.reduxExpandDepth != null)
524
- setReduxExpandDepth(saved.reduxExpandDepth);
525
- if (saved.showDuplicateLogs != null)
526
- setShowDuplicateLogs(saved.showDuplicateLogs);
527
- if (saved.showUpdateToast != null)
528
- setShowUpdateToast(saved.showUpdateToast);
529
- if (saved.defaultTab) {
530
- const dt = saved.defaultTab as ActiveTab;
531
- const vis = {
532
- ...{
533
- apis: true,
534
- logs: true,
535
- analytics: false,
536
- redux: false,
537
- bundle: false,
538
- performance: false,
539
- crash: false,
540
- device: false,
541
- storage: false,
542
- },
543
- ...(saved.tabVisibility || {}),
544
- apis: true,
545
- } as Record<ActiveTab, boolean>;
546
- setActiveTab(vis[dt] ? dt : 'apis');
547
- }
548
- settingsHydratedRef.current = true;
549
- });
550
- return () => {
551
- cancelled = true;
552
- };
553
- }, []);
554
-
555
- // Fetch and apply module enable/disable statuses from Firebase Remote Config if configured or available
556
- useEffect(() => {
557
- let isMounted = true;
558
- if (remoteConfig === false) return;
559
-
560
- fetchRemoteConfigModuleStatus(typeof remoteConfig === 'object' ? remoteConfig : undefined)
561
- .then(remoteModules => {
562
- if (isMounted && remoteModules) {
563
- setTabVisibility(prev => ({
564
- ...prev,
565
- ...remoteModules,
566
- apis: remoteModules.apis ?? true,
567
- }));
568
- }
569
- })
570
- .catch(() => {});
571
-
572
- return () => {
573
- isMounted = false;
574
- };
575
- }, [remoteConfig]);
576
-
577
- useEffect(() => {
578
- if (!settingsHydratedRef.current) return;
579
- saveSettings({
580
- isDark,
581
- modalHeightPercent,
582
- modalAnimationType,
583
- tabVisibility,
584
- defaultTab,
585
- maxNetworkLogs,
586
- maxConsoleLogs,
587
- maxAnalyticsEventsLimit,
588
- maxCrashLogs,
589
- isAutoRamLimitEnabled,
590
- showConsoleLevels,
591
- reduxAutoRefresh,
592
- reduxExpandDepth,
593
- showDuplicateLogs,
594
- showUpdateToast,
595
- });
596
- }, [
597
- isDark,
598
- modalHeightPercent,
599
- modalAnimationType,
600
- tabVisibility,
601
- defaultTab,
602
- maxNetworkLogs,
603
- maxConsoleLogs,
604
- maxAnalyticsEventsLimit,
605
- maxCrashLogs,
606
- isAutoRamLimitEnabled,
607
- showConsoleLevels,
608
- reduxAutoRefresh,
609
- reduxExpandDepth,
610
- showDuplicateLogs,
611
- ]);
612
-
613
- // #1 — check NPM for a newer published version; surfaces an animated dot
614
- // in the header next to the npm chip when an update is available.
615
- const [latestNpmVersion, setLatestNpmVersion] = useState<string | null>(null);
616
- const updateAvailable = useMemo(() => {
617
- if (!latestNpmVersion) return false;
618
- const parse = (v: string) =>
619
- v
620
- .replace(/^v/, '')
621
- .split('.')
622
- .map(n => parseInt(n, 10) || 0);
623
- const cur = parse(LIB_VERSION);
624
- const latest = parse(latestNpmVersion);
625
- for (let i = 0; i < 3; i++) {
626
- if ((latest[i] || 0) > (cur[i] || 0)) return true;
627
- if ((latest[i] || 0) < (cur[i] || 0)) return false;
628
- }
629
- return false;
630
- }, [latestNpmVersion]);
631
-
632
- useEffect(() => {
633
- // Only check for npm updates when running in local development / debug mode (__DEV__)
634
- // Never show or check in production / bundled application
635
- if (typeof __DEV__ === 'undefined' || !__DEV__) {
636
- return;
637
- }
638
- let cancelled = false;
639
- fetch('https://registry.npmjs.org/react-native-inapp-inspector/latest')
640
- .then(res => (res.ok ? res.json() : null))
641
- .then(data => {
642
- if (!cancelled && data && typeof data.version === 'string') {
643
- setLatestNpmVersion(data.version);
644
- }
645
- })
646
- .catch(() => {
647
- // Offline / blocked — silently skip the update check.
648
- });
649
- return () => {
650
- cancelled = true;
651
- };
652
- }, []);
653
-
654
- useEffect(() => {
655
- setReduxAutoRefresh(reduxAutoRefresh);
656
- }, [reduxAutoRefresh]);
657
-
658
- // Auto-unselect Redux or Analytics tab if module is not connected / available
659
- useEffect(() => {
660
- if (activeTab === 'redux' && !isReduxConnected()) {
661
- setActiveTab('apis');
662
- }
663
- if (activeTab === 'analytics' && !isAnalyticsConnected()) {
664
- setActiveTab('apis');
665
- }
666
- }, [activeTab]);
667
-
668
- const toggleTabVisibility = (key: ActiveTab) => {
669
- if (key === 'apis') return;
670
- if (key === 'redux' && !isReduxConnected()) return;
671
- if (key === 'analytics' && !isAnalyticsConnected()) return;
672
- setTabVisibility(prev => {
673
- const nextVal = !prev[key];
674
- const newVisibility = {...prev, [key]: nextVal};
675
- if (!nextVal && activeTab === key) {
676
- animateNextLayout();
677
- setActiveTab('apis');
678
- }
679
- // #6 — a hidden module can't be the default landing tab.
680
- if (!nextVal && defaultTab === key) {
681
- setDefaultTab('apis');
682
- }
683
- return newVisibility;
684
- });
685
- };
686
-
687
- const loadNativePage = useCallback(async (tabKey: ActiveTab, query: string = '') => {
688
- if (tabKey === 'apis' || tabKey === 'logs' || tabKey === 'analytics' || tabKey === 'crash') {
689
- const pageData = await fetchNativeCachedPage(tabKey, 0, 100, query);
690
- if (pageData && pageData.items && pageData.items.length > 0) {
691
- if (tabKey === 'apis') setLogs(pageData.items);
692
- else if (tabKey === 'logs') setConsoleLogs(pageData.items);
693
- else if (tabKey === 'analytics') setAnalyticsEvents(pageData.items);
694
- else if (tabKey === 'crash') setCrashRecords(pageData.items);
695
- }
696
- }
697
- }, []);
698
-
699
- const switchActiveTab = useCallback((key: ActiveTab) => {
700
- if (key === 'redux' && !isReduxConnected()) return;
701
- if (key === 'analytics' && !isAnalyticsConnected()) return;
702
-
703
- setSelected(null);
704
- setSelectedEvent(null);
705
- setSelectedLog(null);
706
- setSelectedReduxSlice(null);
707
- setSelectedReduxAction(null);
708
- setSelectedCrash(null);
709
-
710
- if (typeof React.startTransition === 'function') {
711
- React.startTransition(() => {
712
- setActiveTab(key);
713
- loadNativePage(key);
714
- });
715
- } else {
716
- setActiveTab(key);
717
- loadNativePage(key);
718
- }
719
- trackInspectorTabView(key);
720
- }, [loadNativePage]);
721
-
722
- const [selectedEvent, setSelectedEvent] = useState<AnalyticsEvent | null>(
723
- null,
724
- );
725
- const [analyticsSearch, setAnalyticsSearch] = useState('');
726
- const [analyticsFilters, setAnalyticsFilters] = useState<AnalyticsFilters>({
727
- categories: new Set(['all']),
728
- screens: new Set(),
729
- sources: new Set(['all']),
730
- userTypes: new Set(['all']),
731
- timeWindow: 'all',
732
- payloadComplexity: 'all',
733
- hasRevenue: false,
734
- hasItems: false,
735
- hasUserProps: false,
736
- hasParams: false,
737
- onlyDuplicates: false,
738
- onlyConversions: false,
739
- sortBy: 'time_desc',
740
- });
741
-
742
- const isAnalyticsFilterApplied = useMemo(() => {
743
- if (analyticsFilters.categories.size > 0 && !analyticsFilters.categories.has('all')) return true;
744
- if (analyticsFilters.screens.size > 0) return true;
745
- if (analyticsFilters.sources.size > 0 && !analyticsFilters.sources.has('all')) return true;
746
- if (analyticsFilters.userTypes.size > 0 && !analyticsFilters.userTypes.has('all')) return true;
747
- if (analyticsFilters.timeWindow !== 'all') return true;
748
- if (analyticsFilters.payloadComplexity !== 'all') return true;
749
- if (analyticsFilters.hasRevenue) return true;
750
- if (analyticsFilters.hasItems) return true;
751
- if (analyticsFilters.hasUserProps) return true;
752
- if (analyticsFilters.hasParams) return true;
753
- if (analyticsFilters.onlyDuplicates) return true;
754
- if (analyticsFilters.onlyConversions) return true;
755
- if (analyticsFilters.sortBy !== 'time_desc') return true;
756
- return false;
757
- }, [analyticsFilters]);
758
-
759
- const resetAnalyticsFilters = useCallback(() => {
760
- setAnalyticsFilters({
761
- categories: new Set(['all']),
762
- screens: new Set(),
763
- sources: new Set(['all']),
764
- userTypes: new Set(['all']),
765
- timeWindow: 'all',
766
- payloadComplexity: 'all',
767
- hasRevenue: false,
768
- hasItems: false,
769
- hasUserProps: false,
770
- hasParams: false,
771
- onlyDuplicates: false,
772
- onlyConversions: false,
773
- sortBy: 'time_desc',
774
- });
775
- }, []);
776
-
777
- const [isAnalyticsLayoutReady, setIsAnalyticsLayoutReady] = useState(false);
778
- const [analyticsHeaderExpanded, setAnalyticsHeaderExpanded] = useState(false);
779
-
780
- const [newEventIds, setNewEventIds] = useState<Set<number>>(new Set());
781
- const prevEventIdsRef = useRef<Set<number>>(new Set());
782
-
783
- const [navState, setNavState] = useState<any>(null);
784
- const navigationContext = React.useContext(NavigationContext);
785
- const hasNavigationContext = navigationContext !== undefined;
786
-
787
- const currentRouteRef = useRef<RouteInfo>({
788
- path: '',
789
- params: null,
790
- });
791
-
792
- useEffect(() => {
793
- setRouteInfoProvider(() => currentRouteRef.current);
794
- return () => {
795
- setRouteInfoProvider(null);
796
- };
797
- }, []);
798
-
799
- useEffect(() => {
800
- if (navState) {
801
- const info = getNavigationInfo(navState);
802
- if (info?.path) {
803
- currentRouteRef.current = info;
804
- }
805
- }
806
- }, [navState]);
807
-
808
- useEffect(() => {
809
- if (!navigationRef) return;
810
-
811
- const updateState = () => {
812
- try {
813
- if (typeof navigationRef.isReady === 'function' && navigationRef.isReady()) {
814
- const currentRoute = typeof navigationRef.getCurrentRoute === 'function'
815
- ? navigationRef.getCurrentRoute()
816
- : null;
817
- if (currentRoute?.name) {
818
- currentRouteRef.current = {
819
- path: currentRoute.name,
820
- params: currentRoute.params || null,
821
- };
822
- }
823
- const state = navigationRef.getRootState();
824
- if (state) {
825
- setNavState(state);
826
- const routeInfo = getNavigationInfo(state);
827
- if (routeInfo?.path) {
828
- currentRouteRef.current = routeInfo;
829
- }
830
- }
831
- }
832
- } catch (err) {
833
- // Safe check
834
- }
835
- };
836
-
837
- // Initialize state
838
- updateState();
839
-
840
- // Listen to changes
841
- const unsubscribe = typeof navigationRef.addListener === 'function'
842
- ? navigationRef.addListener('state', () => {
843
- updateState();
844
- })
845
- : null;
846
-
847
- return () => {
848
- if (unsubscribe) {
849
- unsubscribe();
850
- }
851
- };
852
- }, [navigationRef]);
853
-
854
- const logRouteMapRef = useRef<Map<number, RouteInfo>>(new Map());
855
- const prevLogIdsRef = useRef<Set<number>>(new Set());
856
- const [newLogIds, setNewLogIds] = useState<Set<number>>(new Set());
857
-
858
- const pulseAnim = useRef(new Animated.Value(1)).current;
859
- const activePulseAnim = useRef(new Animated.Value(0.4)).current;
860
- const unreadPulseAnim = useRef(new Animated.Value(1)).current;
861
- // #4 — diagonal light streak sweeping across the floating launcher
862
- const fabShineAnim = useRef(new Animated.Value(0)).current;
863
- // #11 — header "clear all" icon spin/scale animation
864
- const clearAnim = useRef(new Animated.Value(0)).current;
865
-
866
- // #4 — draggable floating launcher (drag anywhere on screen)
867
- const fabPan = useRef(new Animated.ValueXY({x: 0, y: 0})).current;
868
- const fabPanRef = useRef({x: 0, y: 0});
869
- useEffect(() => {
870
- const idX = fabPan.x.addListener(v => (fabPanRef.current.x = v.value));
871
- const idY = fabPan.y.addListener(v => (fabPanRef.current.y = v.value));
872
- return () => {
873
- fabPan.x.removeListener(idX);
874
- fabPan.y.removeListener(idY);
875
- };
876
- }, [fabPan]);
877
- const fabDraggedRef = useRef(false);
878
- const fabPanResponder = useRef(
879
- PanResponder.create({
880
- // Let taps fall through to the launcher; only hijack once the
881
- // finger actually moves, so onPress still fires on a tap.
882
- onStartShouldSetPanResponder: () => false,
883
- onMoveShouldSetPanResponder: (_e, g) =>
884
- Math.abs(g.dx) > 4 || Math.abs(g.dy) > 4,
885
- onPanResponderGrant: () => {
886
- fabDraggedRef.current = true;
887
- fabPan.setOffset({
888
- x: fabPanRef.current.x,
889
- y: fabPanRef.current.y,
890
- });
891
- fabPan.setValue({x: 0, y: 0});
892
- },
893
- onPanResponderMove: Animated.event([null, {dx: fabPan.x, dy: fabPan.y}], {
894
- useNativeDriver: false,
895
- }),
896
- onPanResponderRelease: () => {
897
- fabPan.flattenOffset();
898
- // small delay so the trailing tap (if any) is ignored
899
- setTimeout(() => {
900
- fabDraggedRef.current = false;
901
- }, 50);
902
- },
903
- onPanResponderTerminate: () => {
904
- fabPan.flattenOffset();
905
- fabDraggedRef.current = false;
906
- },
907
- }),
908
- ).current;
909
-
910
- // #2 — scroll-to-top button for the main APIs list, always visible at the
911
- // bottom right of the list.
912
-
913
- const runClearAllWithAnimation = useCallback(() => {
914
- Animated.sequence([
915
- Animated.timing(clearAnim, {
916
- toValue: 1,
917
- duration: 320,
918
- useNativeDriver: false,
919
- }),
920
- Animated.timing(clearAnim, {
921
- toValue: 0,
922
- duration: 0,
923
- useNativeDriver: false,
924
- }),
925
- ]).start();
926
- handleClearAll();
927
- }, [clearAnim]);
928
-
929
- useEffect(() => {
930
- if (Platform.OS === 'android') {
931
- UIManager.setLayoutAnimationEnabledExperimental?.(true);
932
- }
933
- }, []);
934
-
935
- useEffect(() => {
936
- if (visible) return;
937
- const loop = Animated.loop(
938
- Animated.sequence([
939
- Animated.timing(pulseAnim, {
940
- toValue: 1.2,
941
- duration: 800,
942
- useNativeDriver: false,
943
- }),
944
- Animated.timing(pulseAnim, {
945
- toValue: 1,
946
- duration: 800,
947
- useNativeDriver: false,
948
- }),
949
- ]),
950
- );
951
- loop.start();
952
- return () => loop.stop();
953
- }, [pulseAnim, visible]);
954
-
955
- // #4 — sweep the shine streak across the launcher, pause, repeat.
956
- useEffect(() => {
957
- if (visible) return;
958
- const loop = Animated.loop(
959
- Animated.sequence([
960
- Animated.timing(fabShineAnim, {
961
- toValue: 1,
962
- duration: 3200,
963
- easing: Easing.inOut(Easing.ease),
964
- useNativeDriver: false,
965
- }),
966
- Animated.delay(3500),
967
- Animated.timing(fabShineAnim, {
968
- toValue: 0,
969
- duration: 0,
970
- useNativeDriver: false,
971
- }),
972
- ]),
973
- );
974
- loop.start();
975
- return () => loop.stop();
976
- }, [fabShineAnim, visible]);
977
-
978
- useEffect(() => {
979
- const loop = Animated.loop(
980
- Animated.sequence([
981
- Animated.timing(activePulseAnim, {
982
- toValue: 1,
983
- duration: 1200,
984
- useNativeDriver: false,
985
- }),
986
- Animated.timing(activePulseAnim, {
987
- toValue: 0.4,
988
- duration: 1200,
989
- useNativeDriver: false,
990
- }),
991
- ]),
992
- );
993
- loop.start();
994
- return () => loop.stop();
995
- }, [activePulseAnim]);
996
-
997
- useEffect(() => {
998
- const loop = Animated.loop(
999
- Animated.sequence([
1000
- Animated.timing(unreadPulseAnim, {
1001
- toValue: 1.3,
1002
- duration: 800,
1003
- useNativeDriver: false,
1004
- }),
1005
- Animated.timing(unreadPulseAnim, {
1006
- toValue: 0.8,
1007
- duration: 800,
1008
- useNativeDriver: false,
1009
- }),
1010
- ]),
1011
- );
1012
- loop.start();
1013
- return () => loop.stop();
1014
- }, [unreadPulseAnim]);
1015
-
1016
- const isNativeModule = useMemo(() => isNativeModuleAvailable(), []);
1017
- const useNativeFab =
1018
- (Platform.OS === 'ios' || Platform.OS === 'android') && isNativeModule;
1019
-
1020
-
1021
-
1022
- // Always hide legacy native circular button in favor of the React Native music-player dock
1023
- useEffect(() => {
1024
- if (useNativeFab) {
1025
- hideNativeFloatingButton().catch(() => {});
1026
- }
1027
- }, [useNativeFab, visible]);
1028
-
1029
- // Subscribe to native UI-thread floating button tap events
1030
- useEffect(() => {
1031
- if (!useNativeFab) return;
1032
- const unsubscribe = subscribeNativeFloatingButtonPress(() => {
1033
- hideNativeFloatingButton().catch(() => {});
1034
- setVisible(true);
1035
- });
1036
- return () => {
1037
- unsubscribe();
1038
- };
1039
- }, [useNativeFab]);
1040
-
1041
- // Subscribe to physical hardware shake events (or Cmd+Ctrl+Z on iOS simulator)
1042
- useEffect(() => {
1043
- if (!useNativeFab) return;
1044
- const unsubscribe = subscribeNativeDeviceShake(() => {
1045
- setVisible(prev => {
1046
- const next = !prev;
1047
- if (next) {
1048
- hideNativeFloatingButton().catch(() => {});
1049
- }
1050
- return next;
1051
- });
1052
- });
1053
- return () => {
1054
- unsubscribe();
1055
- };
1056
- }, [useNativeFab]);
1057
-
1058
- const isVisibleRefObj = useRef(visible);
1059
-
1060
- // #6 — every time the inspector is opened, land on chosen default tab & sync latest logs
1061
- useEffect(() => {
1062
- isVisibleRefObj.current = visible;
1063
- if (visible) {
1064
- trackInspectorScreenView('InspectorModal');
1065
- const target =
1066
- defaultTab === 'apis' || tabVisibility?.[defaultTab]
1067
- ? defaultTab
1068
- : 'apis';
1069
- setActiveTab(target);
1070
- trackInspectorTabView(target);
1071
-
1072
-
1073
-
1074
- // Instant synchronization of data collected while modal was closed
1075
- if (latestNetworkLogsRef.current.length > 0) {
1076
- const deduped = deduplicateLogs(latestNetworkLogsRef.current);
1077
- const incoming = new Set(deduped.map(l => l.id));
1078
- prevLogIdsRef.current = incoming;
1079
- setLogs(deduped);
1080
- }
1081
- if (latestConsoleLogsRef.current.length > 0) {
1082
- setConsoleLogs(latestConsoleLogsRef.current);
1083
- }
1084
- if (latestAnalyticsEventsRef.current.length > 0) {
1085
- setAnalyticsEvents(latestAnalyticsEventsRef.current);
1086
- }
1087
- const freshState = getReduxState();
1088
- if (freshState) {
1089
- setReduxState(typeof freshState === 'object' ? {...freshState} : freshState);
1090
- setReduxLastActionMap({...getLastActionForReducer()});
1091
- }
1092
- const freshCrashes = getCrashRecords();
1093
- if (freshCrashes.length > 0) {
1094
- setCrashRecords(freshCrashes);
1095
- }
1096
-
1097
- const frame = requestAnimationFrame(() => {
1098
- setIsReady(true);
1099
- });
1100
- return () => {
1101
- cancelAnimationFrame(frame);
1102
- };
1103
- } else {
1104
- setIsReady(false);
1105
- setSelectedLogs(new Set());
1106
- }
1107
- }, [visible]);
1108
-
1109
- useEffect(() => {
1110
- setupNetworkLogger();
1111
- clearNetworkLogs();
1112
- setupConsoleLogger();
1113
- autoSetupAnalyticsLogger();
1114
- setupGlobalCrashHandler();
1115
- const cleanupMemoryWarning = setupMemoryWarningHandler();
1116
-
1117
- const isVisibleRef = isVisibleRefObj;
1118
-
1119
- const unsubscribeCrash = subscribeCrashEvents(crashInfo => {
1120
- if (crashInfo.message === '__CLEARED__') {
1121
- setCrashRecords([]);
1122
- setSelectedCrash(null);
1123
- return;
1124
- }
1125
- const updated = getCrashRecords();
1126
- if (updated.length > 0) {
1127
- pushNativeLogRecord('crash', JSON.stringify(updated[0]));
1128
- }
1129
- if (isVisibleRef.current) {
1130
- setCrashRecords(updated);
1131
- }
1132
- });
1133
-
1134
- // Sync crashes recorded before this subscription ran (e.g. a render crash
1135
- // during the very first commit, which fires before effects are mounted).
1136
- const pendingCrashes = getCrashRecords();
1137
- if (pendingCrashes.length > 0) {
1138
- pendingCrashes.forEach(c => pushNativeLogRecord('crash', JSON.stringify(c)));
1139
- setCrashRecords(pendingCrashes);
1140
- }
1141
-
1142
- let timeoutId: ReturnType<typeof setTimeout>;
1143
-
1144
- let isFirstNetworkCall = true;
1145
- const unsubscribe = subscribeNetworkLogs((raw: NetworkLog[]) => {
1146
- latestNetworkLogsRef.current = raw;
1147
- if (raw.length > 0) {
1148
- pushNativeLogRecord('apis', JSON.stringify(raw[0]));
1149
- }
1150
- if (isNetworkPausedRef.current) return;
1151
- if (!isVisibleRef.current) return; // ZERO-RENDER INACTIVE MODE
1152
-
1153
- clearTimeout(timeoutId);
1154
- const updateNetworkState = () => {
1155
- const deduped = deduplicateLogs(raw);
1156
- const incoming = new Set(deduped.map(l => l.id));
1157
- const freshIds = new Set<number>();
1158
- incoming.forEach(id => {
1159
- if (!prevLogIdsRef.current.has(id)) freshIds.add(id);
1160
- });
1161
- prevLogIdsRef.current = incoming;
1162
- if (freshIds.size > 0) {
1163
- freshIds.forEach(id => {
1164
- if (!logRouteMapRef.current.has(id)) {
1165
- const matchedLog = deduped.find(l => l.id === id);
1166
- const resolvedRoute =
1167
- (matchedLog as any)?.routeInfo || currentRouteRef.current;
1168
- logRouteMapRef.current.set(id, resolvedRoute);
1169
- }
1170
- });
1171
- setNewLogIds(freshIds);
1172
- setTimeout(() => setNewLogIds(new Set()), 1200);
1173
- }
1174
- setLogs(deduped);
1175
- };
1176
-
1177
- if (isFirstNetworkCall) {
1178
- isFirstNetworkCall = false;
1179
- const deduped = deduplicateLogs(raw);
1180
- const incoming = new Set(deduped.map(l => l.id));
1181
- prevLogIdsRef.current = incoming;
1182
- deduped.forEach(l => {
1183
- if (!logRouteMapRef.current.has(l.id)) {
1184
- const resolvedRoute =
1185
- (l as any)?.routeInfo || currentRouteRef.current;
1186
- logRouteMapRef.current.set(l.id, resolvedRoute);
1187
- }
1188
- });
1189
- setLogs(deduped);
1190
- } else {
1191
- timeoutId = setTimeout(updateNetworkState, 250);
1192
- }
1193
- });
1194
-
1195
- // ─── Analytics subscription ──────────────────────────────────────────────
1196
- let analyticsTimeoutId: ReturnType<typeof setTimeout>;
1197
- let isFirstAnalyticsCall = true;
1198
-
1199
- const unsubscribeAnalytics = subscribeAnalyticsEvents(
1200
- (raw: AnalyticsEvent[]) => {
1201
- latestAnalyticsEventsRef.current = raw;
1202
- if (raw.length > 0) {
1203
- pushNativeLogRecord('analytics', JSON.stringify(raw[0]));
1204
- }
1205
- if (isAnalyticsPausedRef.current) return;
1206
- if (!isVisibleRef.current) return; // ZERO-RENDER INACTIVE MODE
1207
-
1208
- clearTimeout(analyticsTimeoutId);
1209
- const updateAnalyticsState = () => {
1210
- const incoming = new Set(raw.map(e => e.id));
1211
- const freshIds = new Set<number>();
1212
- incoming.forEach(id => {
1213
- if (!prevEventIdsRef.current.has(id)) freshIds.add(id);
1214
- });
1215
- prevEventIdsRef.current = incoming;
1216
- if (freshIds.size > 0) {
1217
- freshIds.forEach(id => {
1218
- if (!logRouteMapRef.current.has(id + 1000000)) {
1219
- logRouteMapRef.current.set(
1220
- id + 1000000,
1221
- currentRouteRef.current,
1222
- );
1223
- }
1224
- });
1225
- setNewEventIds(freshIds);
1226
- setTimeout(() => setNewEventIds(new Set()), 1200);
1227
- }
1228
- setAnalyticsEvents(raw);
1229
- };
1230
-
1231
- if (isFirstAnalyticsCall) {
1232
- isFirstAnalyticsCall = false;
1233
- const incoming = new Set(raw.map(e => e.id));
1234
- prevEventIdsRef.current = incoming;
1235
- setAnalyticsEvents(raw);
1236
- } else {
1237
- analyticsTimeoutId = setTimeout(updateAnalyticsState, 200);
1238
- }
1239
- },
1240
- );
1241
-
1242
- // ─── Console subscription ────────────────────────────────────────────────
1243
- let consoleTimeoutId: ReturnType<typeof setTimeout>;
1244
- let isFirstConsoleCall = true;
1245
-
1246
- const unsubscribeConsole = subscribeConsoleLogs((raw: ConsoleLog[]) => {
1247
- latestConsoleLogsRef.current = raw;
1248
- if (raw.length > 0) {
1249
- pushNativeLogRecord('logs', JSON.stringify(raw[0]));
1250
- }
1251
- if (isConsolePausedRef.current) return;
1252
- if (!isVisibleRef.current) return; // ZERO-RENDER INACTIVE MODE
1253
-
1254
- clearTimeout(consoleTimeoutId);
1255
- if (isFirstConsoleCall) {
1256
- isFirstConsoleCall = false;
1257
- setConsoleLogs(raw);
1258
- } else {
1259
- consoleTimeoutId = setTimeout(() => {
1260
- setConsoleLogs(raw);
1261
- }, 200);
1262
- }
1263
- });
1264
-
1265
- const initialReduxState = getReduxState();
1266
- if (initialReduxState) {
1267
- setReduxState(typeof initialReduxState === 'object' ? {...initialReduxState} : initialReduxState);
1268
- setReduxLastActionMap({...getLastActionForReducer()});
1269
- }
1270
- const unsubscribeRedux = subscribeReduxState(() => {
1271
- if (!isVisibleRef.current) return; // ZERO-RENDER INACTIVE MODE
1272
- const freshState = getReduxState();
1273
- setReduxState(freshState && typeof freshState === 'object' ? {...freshState} : freshState);
1274
- setReduxLastActionMap({...getLastActionForReducer()});
1275
- });
1276
-
1277
- return () => {
1278
- unsubscribe();
1279
- clearTimeout(timeoutId);
1280
- unsubscribeAnalytics();
1281
- clearTimeout(analyticsTimeoutId);
1282
- unsubscribeConsole();
1283
- clearTimeout(consoleTimeoutId);
1284
- unsubscribeRedux();
1285
- unsubscribeCrash();
1286
- cleanupMemoryWarning();
1287
- };
1288
- }, []);
1289
-
1290
- useEffect(() => {
1291
- setMaxNetworkLogsLimit(maxNetworkLogs);
1292
- }, [maxNetworkLogs]);
1293
-
1294
- useEffect(() => {
1295
- setMaxCrashLogsLimit(maxCrashLogs);
1296
- }, [maxCrashLogs]);
1297
-
1298
- useEffect(() => {
1299
- if (!isNetworkPaused && latestNetworkLogsRef.current.length > 0) {
1300
- const deduped = deduplicateLogs(latestNetworkLogsRef.current);
1301
- setLogs(deduped);
1302
- }
1303
- }, [isNetworkPaused]);
1304
-
1305
- useEffect(() => {
1306
- if (!isAnalyticsPaused && latestAnalyticsEventsRef.current.length > 0) {
1307
- setAnalyticsEvents(latestAnalyticsEventsRef.current);
1308
- }
1309
- }, [isAnalyticsPaused]);
1310
-
1311
- useEffect(() => {
1312
- if (!isConsolePaused && latestConsoleLogsRef.current.length > 0) {
1313
- setConsoleLogs(latestConsoleLogsRef.current);
1314
- }
1315
- }, [isConsolePaused]);
1316
-
1317
- useEffect(() => {
1318
- setReqExpanded(true);
1319
- setResExpanded(true);
1320
- setShowReqDiff(false);
1321
- setShowResDiff(false);
1322
- setDetailSearch('');
1323
- }, [selected]);
1324
-
1325
- const filteredLogs = useMemo(() => {
1326
- let result = logs.filter(log => {
1327
- // 1. Quick Filter Check (All, Errors, Success, Slow, GET, POST, GraphQL)
1328
- if (quickFilter && quickFilter !== 'all') {
1329
- if (quickFilter === 'errors') {
1330
- const isErr =
1331
- log.status === 0 ||
1332
- log.status == null ||
1333
- (typeof log.status === 'number' && log.status >= 400);
1334
- if (!isErr) return false;
1335
- } else if (quickFilter === 'success') {
1336
- const isSuccess =
1337
- typeof log.status === 'number' &&
1338
- log.status >= 200 &&
1339
- log.status < 400;
1340
- if (!isSuccess) return false;
1341
- } else if (quickFilter === 'slow') {
1342
- if ((log.duration || 0) < 500) return false;
1343
- } else if (quickFilter === 'graphql') {
1344
- const isGql =
1345
- (log.url || '').toLowerCase().includes('graphql') ||
1346
- (log.client || '').toLowerCase().includes('graphql') ||
1347
- (log.client || '').toLowerCase().includes('apollo');
1348
- if (!isGql) return false;
1349
- } else {
1350
- if (log.method?.toUpperCase() !== quickFilter.toUpperCase()) {
1351
- return false;
1352
- }
1353
- }
1354
- }
1355
-
1356
- // 2. Status Filter Check (from filters accordion dropdown)
1357
- if (statusFilters.size > 0) {
1358
- const matched = [...statusFilters].some(f => {
1359
- if (f === 'ALL') return true;
1360
- if (f === 'Failed') return log.status === 0 || log.status == null;
1361
- const codeNum = parseInt(f, 10);
1362
- if (!isNaN(codeNum)) {
1363
- return Number(log.status) === codeNum;
1364
- }
1365
- return String(log.status)[0] === f[0];
1366
- });
1367
- if (!matched) return false;
1368
- }
1369
-
1370
- // 3. Method Filter Check (from filters accordion dropdown)
1371
- if (methodFilters.size > 0) {
1372
- const matchedMethod = [...methodFilters].some(m => {
1373
- if (m === 'ALL') return true;
1374
- return log.method?.toUpperCase() === m;
1375
- });
1376
- if (!matchedMethod) return false;
1377
- }
1378
-
1379
- // 4. Advanced Search Query Engine Check with Scope, Regex and Case-Sensitivity
1380
- if (search && search.trim().length > 0) {
1381
- const routePath = logRouteMapRef.current.get(log.id)?.path || '';
1382
- const isMatch = matchNetworkLogQuery(log, search, routePath, {
1383
- scope: searchScope,
1384
- isRegex: isRegexSearch,
1385
- isCaseSensitive: isCaseSensitive,
1386
- });
1387
- if (!isMatch) return false;
1388
- }
1389
-
1390
- return true;
1391
- });
1392
-
1393
- if (sortOrder === 'oldest') {
1394
- result = [...result].reverse();
1395
- }
1396
-
1397
- // #9 — collapse consecutive identical requests
1398
- if (!showDuplicateLogs) {
1399
- const collapsed: NetworkLog[] = [];
1400
- for (const log of result) {
1401
- const last = collapsed[collapsed.length - 1];
1402
- if (
1403
- last &&
1404
- last.method === log.method &&
1405
- last.url === log.url &&
1406
- last.status === log.status
1407
- ) {
1408
- collapsed[collapsed.length - 1] = {
1409
- ...last,
1410
- duplicateCount: (last.duplicateCount || 1) + 1,
1411
- };
1412
- } else {
1413
- collapsed.push({...log, duplicateCount: 1});
1414
- }
1415
- }
1416
- result = collapsed;
1417
- }
1418
-
1419
- return result.slice(0, maxNetworkLogs);
1420
- }, [
1421
- logs,
1422
- search,
1423
- searchScope,
1424
- isRegexSearch,
1425
- isCaseSensitive,
1426
- quickFilter,
1427
- statusFilters,
1428
- methodFilters,
1429
- sortOrder,
1430
- maxNetworkLogs,
1431
- showDuplicateLogs,
1432
- ]);
1433
-
1434
- const availableMethods = useMemo(() => {
1435
- const methods = new Set<Method>();
1436
- logs.forEach(log => {
1437
- if (log.method) methods.add(log.method.toUpperCase() as Method);
1438
- });
1439
- return ['ALL' as Method, ...Array.from(methods)];
1440
- }, [logs]);
1441
-
1442
- const toggleSectionFilter = useCallback(
1443
- (pageName: string, filter: LocalFilter) => {
1444
- setSectionFilters(prev => {
1445
- const current =
1446
- prev[pageName] || new Set(['success', 'failed', 'loading']);
1447
- const next = new Set(current);
1448
-
1449
- if (next.has(filter)) {
1450
- next.delete(filter);
1451
- } else {
1452
- next.add(filter);
1453
- }
1454
-
1455
- return {...prev, [pageName]: next};
1456
- });
1457
- },
1458
- [],
1459
- );
1460
-
1461
- const toggleSectionCollapse = useCallback((pageName: string) => {
1462
- setCollapsedSections(prev => {
1463
- const next = new Set(prev);
1464
- if (next.has(pageName)) next.delete(pageName);
1465
- else next.add(pageName);
1466
- return next;
1467
- });
1468
- }, []);
1469
-
1470
- const groupedData = useMemo(() => {
1471
- const result: GroupedListItem[] = [];
1472
- const groupMap = new Map<
1473
- string,
1474
- {pageName: string; color: string; logs: NetworkLog[]}
1475
- >();
1476
-
1477
- for (let i = 0; i < filteredLogs.length; i++) {
1478
- const log = filteredLogs[i];
1479
- const routeInfo =
1480
- logRouteMapRef.current.get(log.id) || (log as any)?.routeInfo;
1481
- const pageName = getLogPageName(log, routeInfo);
1482
-
1483
- let group = groupMap.get(pageName);
1484
- if (!group) {
1485
- group = {pageName, color: getDomainColor(pageName), logs: []};
1486
- groupMap.set(pageName, group);
1487
- }
1488
- group.logs.push(log);
1489
- }
1490
-
1491
- let idx = 0;
1492
- groupMap.forEach(g => {
1493
- let success = 0;
1494
- let failed = 0;
1495
- let loading = 0;
1496
-
1497
- g.logs.forEach(l => {
1498
- if (l.status == null) loading++;
1499
- else if (l.status === 0 || l.status >= 400) failed++;
1500
- else success++;
1501
- });
1502
-
1503
- const activeFilters =
1504
- sectionFilters[g.pageName] || new Set(['success', 'failed', 'loading']);
1505
-
1506
- const isCollapsed = collapsedSections.has(g.pageName);
1507
- const timestamp = g.logs[0]?.startTime || 0;
1508
-
1509
- result.push({
1510
- type: 'header',
1511
- id: `hdr-${g.pageName}`,
1512
- pageName: g.pageName,
1513
- color: g.color,
1514
- stats: {success, failed, loading},
1515
- timestamp,
1516
- activeFilters,
1517
- isCollapsed,
1518
- isFirst: idx === 0,
1519
- });
1520
- idx++;
1521
-
1522
- if (!isCollapsed) {
1523
- const displayLogs = g.logs.filter(l => {
1524
- if (l.status == null) return activeFilters.has('loading');
1525
- if (l.status === 0 || l.status >= 400)
1526
- return activeFilters.has('failed');
1527
- return activeFilters.has('success');
1528
- });
1529
-
1530
- displayLogs.forEach((log, index) => {
1531
- result.push({
1532
- type: 'log',
1533
- id: `log-${log.id}`,
1534
- log,
1535
- isLast: index === displayLogs.length - 1,
1536
- color: g.color,
1537
- });
1538
- });
1539
- }
1540
- });
1541
-
1542
- return result;
1543
- }, [filteredLogs, logs, sectionFilters, collapsedSections]);
1544
-
1545
- const {minStart, totalRange} = useMemo(() => {
1546
- if (filteredLogs.length === 0) return {minStart: 0, totalRange: 0};
1547
- const min = Math.min(...filteredLogs.map(l => l.startTime));
1548
- const max = Math.max(
1549
- ...filteredLogs.map(l => l.startTime + (l.duration || 10)),
1550
- );
1551
- return {minStart: min, totalRange: max - min};
1552
- }, [filteredLogs]);
1553
-
1554
- const prevSameRequest = useMemo(() => {
1555
- if (!selected) return null;
1556
- const index = logs.findIndex(l => l.id === selected.id);
1557
- if (index === -1) return null;
1558
- for (let i = index + 1; i < logs.length; i++) {
1559
- const l = logs[i];
1560
- if (l.url === selected.url && l.method === selected.method) {
1561
- return l;
1562
- }
1563
- }
1564
- return null;
1565
- }, [selected, logs]);
1566
-
1567
- const prevRequestData = prevSameRequest ? prevSameRequest.request : null;
1568
- const prevResponseData = prevSameRequest ? prevSameRequest.response : null;
1569
-
1570
- const filteredAnalyticsEvents = useMemo(() => {
1571
- let events = analyticsEvents;
1572
-
1573
- // Search query filter
1574
- if (analyticsSearch && analyticsSearch.trim().length > 0) {
1575
- const queryTokens = analyticsSearch.trim().toLowerCase().split(/\s+/).filter(Boolean);
1576
- events = events.filter(e => {
1577
- const searchTarget = [
1578
- e.name || '',
1579
- JSON.stringify(e.params || ''),
1580
- JSON.stringify(e.userProperties || ''),
1581
- e.screenName || '',
1582
- e.pageTitle || '',
1583
- e.userId || '',
1584
- e.source || '',
1585
- ].join(' ').toLowerCase();
1586
-
1587
- return queryTokens.every(token => searchTarget.includes(token));
1588
- });
1589
- }
1590
-
1591
- // Category filter
1592
- if (analyticsFilters.categories.size > 0 && !analyticsFilters.categories.has('all')) {
1593
- events = events.filter(e => {
1594
- const cat = getEventCategory(e.name);
1595
- return analyticsFilters.categories.has(cat);
1596
- });
1597
- }
1598
-
1599
- // Time window filter
1600
- if (analyticsFilters.timeWindow !== 'all') {
1601
- const now = Date.now();
1602
- const cutoff =
1603
- analyticsFilters.timeWindow === '1m'
1604
- ? now - 60 * 1000
1605
- : analyticsFilters.timeWindow === '5m'
1606
- ? now - 5 * 60 * 1000
1607
- : analyticsFilters.timeWindow === '15m'
1608
- ? now - 15 * 60 * 1000
1609
- : now - 60 * 60 * 1000;
1610
- events = events.filter(e => e.timestamp >= cutoff);
1611
- }
1612
-
1613
- // Source filter
1614
- if (analyticsFilters.sources.size > 0 && !analyticsFilters.sources.has('all')) {
1615
- events = events.filter(e => analyticsFilters.sources.has(e.source || 'manual'));
1616
- }
1617
-
1618
- // User identification filter
1619
- if (analyticsFilters.userTypes.size > 0 && !analyticsFilters.userTypes.has('all')) {
1620
- events = events.filter(e => {
1621
- const isIdentified = Boolean(e.userId && e.userId.trim() !== '');
1622
- if (analyticsFilters.userTypes.has('identified') && isIdentified) return true;
1623
- if (analyticsFilters.userTypes.has('anonymous') && !isIdentified) return true;
1624
- return false;
1625
- });
1626
- }
1627
-
1628
- // Payload complexity filter
1629
- if (analyticsFilters.payloadComplexity !== 'all') {
1630
- if (analyticsFilters.payloadComplexity === 'none') {
1631
- events = events.filter(
1632
- e => !e.params || Object.keys(e.params).length === 0,
1633
- );
1634
- } else if (analyticsFilters.payloadComplexity === 'simple') {
1635
- events = events.filter(
1636
- e => e.params && Object.keys(e.params).length <= 3,
1637
- );
1638
- } else if (analyticsFilters.payloadComplexity === 'heavy') {
1639
- events = events.filter(
1640
- e => e.params && Object.keys(e.params).length > 3,
1641
- );
1642
- }
1643
- }
1644
-
1645
- // Conversion / Goal events filter
1646
- if (analyticsFilters.onlyConversions) {
1647
- const CONVERSION_PATTERNS = [
1648
- 'purchase',
1649
- 'item_purchase',
1650
- 'ecommerce_purchase',
1651
- 'sign_up',
1652
- 'login',
1653
- 'lead',
1654
- 'generate_lead',
1655
- 'tutorial_complete',
1656
- 'add_payment_info',
1657
- 'begin_checkout',
1658
- 'spend_virtual_currency',
1659
- ];
1660
- events = events.filter(e =>
1661
- CONVERSION_PATTERNS.some(pat => e.name.toLowerCase().includes(pat)),
1662
- );
1663
- }
1664
-
1665
- // Screen filter
1666
- if (analyticsFilters.screens.size > 0) {
1667
- events = events.filter(e => {
1668
- const scr =
1669
- e.screenName ||
1670
- e.params?.firebase_screen ||
1671
- e.params?.screen_name ||
1672
- e.params?.firebase_screen_class ||
1673
- e.screenClass ||
1674
- '';
1675
- return analyticsFilters.screens.has(scr);
1676
- });
1677
- }
1678
-
1679
- // Has Revenue filter
1680
- if (analyticsFilters.hasRevenue) {
1681
- events = events.filter(
1682
- e =>
1683
- (e.params?.value != null && !isNaN(Number(e.params.value))) ||
1684
- (e.params?.price != null && !isNaN(Number(e.params.price))),
1685
- );
1686
- }
1687
-
1688
- // Has Items filter
1689
- if (analyticsFilters.hasItems) {
1690
- events = events.filter(
1691
- e => Array.isArray(e.params?.items) && e.params.items.length > 0,
1692
- );
1693
- }
1694
-
1695
- // Has User Props filter
1696
- if (analyticsFilters.hasUserProps) {
1697
- events = events.filter(
1698
- e => e.userProperties && Object.keys(e.userProperties).length > 0,
1699
- );
1700
- }
1701
-
1702
- // Has Params filter
1703
- if (analyticsFilters.hasParams) {
1704
- events = events.filter(
1705
- e => e.params && Object.keys(e.params).length > 0,
1706
- );
1707
- }
1708
-
1709
- // Deduplication
1710
- const deduplicatedEvents: (AnalyticsEvent & {count?: number})[] = [];
1711
- for (const e of events) {
1712
- if (deduplicatedEvents.length === 0) {
1713
- deduplicatedEvents.push({...e, count: 1});
1714
- continue;
1715
- }
1716
- const last = deduplicatedEvents[deduplicatedEvents.length - 1];
1717
- if (
1718
- last.name === e.name &&
1719
- JSON.stringify(last.params) === JSON.stringify(e.params) &&
1720
- JSON.stringify(last.userProperties) === JSON.stringify(e.userProperties)
1721
- ) {
1722
- last.count = (last.count || 1) + 1;
1723
- // Point to the newest timestamp and id
1724
- last.timestamp = e.timestamp;
1725
- last.id = e.id;
1726
- } else {
1727
- deduplicatedEvents.push({...e, count: 1});
1728
- }
1729
- }
1730
-
1731
- let result = deduplicatedEvents;
1732
-
1733
- // Only duplicates filter
1734
- if (analyticsFilters.onlyDuplicates) {
1735
- result = result.filter(e => (e.count || 1) > 1);
1736
- }
1737
-
1738
- // Sorting
1739
- if (analyticsFilters.sortBy === 'time_asc') {
1740
- result = [...result].sort((a, b) => a.timestamp - b.timestamp);
1741
- } else if (analyticsFilters.sortBy === 'revenue_desc') {
1742
- result = [...result].sort((a, b) => {
1743
- const valA = Number(a.params?.value ?? a.params?.price ?? 0);
1744
- const valB = Number(b.params?.value ?? b.params?.price ?? 0);
1745
- return valB - valA;
1746
- });
1747
- } else if (analyticsFilters.sortBy === 'count_desc') {
1748
- result = [...result].sort((a, b) => (b.count || 1) - (a.count || 1));
1749
- }
1750
-
1751
- return result.slice(0, maxAnalyticsEventsLimit);
1752
- }, [analyticsEvents, analyticsSearch, analyticsFilters, maxAnalyticsEventsLimit]);
1753
-
1754
- const filteredConsoleLogs = useMemo(() => {
1755
- let result = visibleConsoleLogs;
1756
-
1757
- // Filters check
1758
- if (logFilters.size > 0 && !logFilters.has('all')) {
1759
- result = result.filter(log => {
1760
- if (logFilters.has(log.type)) return true;
1761
- if (logFilters.has('user-log') && log.sourceMethod === 'log')
1762
- return true;
1763
- if (
1764
- logFilters.has('analytics') &&
1765
- log.message?.toLowerCase().includes('[analytics error]')
1766
- )
1767
- return true;
1768
- return false;
1769
- });
1770
- }
1771
-
1772
- // Straightforward keyword search
1773
- if (logSearch && logSearch.trim().length > 0) {
1774
- const queryTokens = logSearch.trim().toLowerCase().split(/\s+/).filter(Boolean);
1775
- result = result.filter(log => {
1776
- const searchTarget = [
1777
- log.message || '',
1778
- log.caller || '',
1779
- log.type || '',
1780
- log.sourceMethod || '',
1781
- log.stack || '',
1782
- ].join(' ').toLowerCase();
1783
-
1784
- return queryTokens.every(token => {
1785
- if (token === 'error' || token === 'err') {
1786
- return log.type === 'error' || searchTarget.includes(token);
1787
- }
1788
- if (token === 'warn' || token === 'warning') {
1789
- return log.type === 'warn' || searchTarget.includes(token);
1790
- }
1791
- if (token === 'info') {
1792
- return log.type === 'info' || searchTarget.includes(token);
1793
- }
1794
- return searchTarget.includes(token);
1795
- });
1796
- });
1797
- }
1798
-
1799
- // Apply sort order (newest/oldest first)
1800
- result = [...result].sort((a, b) =>
1801
- logSortOrder === 'newest'
1802
- ? b.timestamp - a.timestamp
1803
- : a.timestamp - b.timestamp,
1804
- );
1805
-
1806
- // Collapse consecutive identical messages into one row with a ×N counter unless duplicates are explicitly enabled
1807
- if (!showDuplicateLogs) {
1808
- const collapsed: ConsoleLog[] = [];
1809
- for (const log of result) {
1810
- const last = collapsed[collapsed.length - 1];
1811
- if (
1812
- last &&
1813
- last.type === log.type &&
1814
- last.sourceMethod === log.sourceMethod &&
1815
- last.message === log.message
1816
- ) {
1817
- collapsed[collapsed.length - 1] = {
1818
- ...last,
1819
- duplicateCount: (last.duplicateCount || 1) + 1,
1820
- };
1821
- } else {
1822
- collapsed.push({...log, duplicateCount: 1});
1823
- }
1824
- }
1825
- result = collapsed;
1826
- }
1827
-
1828
- return result;
1829
- }, [
1830
- visibleConsoleLogs,
1831
- logFilters,
1832
- logSearch,
1833
- logSortOrder,
1834
- showDuplicateLogs,
1835
- ]);
1836
-
1837
- const logCounts = useMemo(() => {
1838
- let searchedLogs = visibleConsoleLogs;
1839
- if (logSearch && logSearch.trim().length > 0) {
1840
- const queryTokens = logSearch.trim().toLowerCase().split(/\s+/).filter(Boolean);
1841
- searchedLogs = visibleConsoleLogs.filter(log => {
1842
- const searchTarget = [
1843
- log.message || '',
1844
- log.caller || '',
1845
- log.type || '',
1846
- log.sourceMethod || '',
1847
- log.stack || '',
1848
- ].join(' ').toLowerCase();
1849
-
1850
- return queryTokens.every(token => {
1851
- if (token === 'error' || token === 'err') {
1852
- return log.type === 'error' || searchTarget.includes(token);
1853
- }
1854
- if (token === 'warn' || token === 'warning') {
1855
- return log.type === 'warn' || searchTarget.includes(token);
1856
- }
1857
- if (token === 'info') {
1858
- return log.type === 'info' || searchTarget.includes(token);
1859
- }
1860
- return searchTarget.includes(token);
1861
- });
1862
- });
1863
- }
1864
-
1865
- const total = visibleConsoleLogs.length;
1866
-
1867
- return {
1868
- all: `${searchedLogs.length}/${total}`,
1869
- info: `${searchedLogs.filter(l => l.type === 'info').length}/${total}`,
1870
- warn: `${searchedLogs.filter(l => l.type === 'warn').length}/${total}`,
1871
- error: `${searchedLogs.filter(l => l.type === 'error').length}/${total}`,
1872
- 'user-log': `${
1873
- searchedLogs.filter(l => l.sourceMethod === 'log').length
1874
- }/${total}`,
1875
- analytics: `${
1876
- searchedLogs.filter(l =>
1877
- l.message.toLowerCase().includes('[analytics error]'),
1878
- ).length
1879
- }/${total}`,
1880
- };
1881
- }, [visibleConsoleLogs, logSearch]);
1882
-
1883
- const closeModal = useCallback(() => {
1884
- setVisible(false);
1885
- setIsMinimized(false);
1886
- setTimeout(() => {
1887
- setSelected(null);
1888
- setSelectedEvent(null);
1889
- setSelectedLog(null);
1890
- setSelectedReduxSlice(null);
1891
- setSelectedReduxAction(null);
1892
- setSelectedCrash(null);
1893
- }, 300);
1894
- }, []);
1895
-
1896
- function handleClearAll() {
1897
- clearNetworkLogs();
1898
- setLogs([]);
1899
- setSelectedLogs(new Set());
1900
- setSectionFilters({});
1901
- setCollapsedSections(new Set());
1902
- setStatusFilters(new Set());
1903
- setMethodFilters(new Set());
1904
- setSearch('');
1905
- prevLogIdsRef.current = new Set();
1906
- logRouteMapRef.current = new Map();
1907
- // Also clear analytics
1908
- clearAnalyticsEvents();
1909
- setAnalyticsEvents([]);
1910
- setAnalyticsSearch('');
1911
- prevEventIdsRef.current = new Set();
1912
- // Also clear console logs
1913
- clearConsoleLogs();
1914
- setConsoleLogs([]);
1915
- setLogFilters(new Set(['all']));
1916
- setLogSearch('');
1917
- }
1918
-
1919
- function handleDelete() {
1920
- if (activeTab === 'logs') {
1921
- Alert.alert(
1922
- 'Clear Logs',
1923
- 'Are you sure you want to clear all console logs?',
1924
- [
1925
- {text: 'Cancel', style: 'cancel'},
1926
- {
1927
- text: 'Clear All',
1928
- onPress: () => {
1929
- clearConsoleLogs();
1930
- setConsoleLogs([]);
1931
- },
1932
- style: 'destructive',
1933
- },
1934
- ],
1935
- );
1936
- return;
1937
- }
1938
- if (activeTab === 'analytics') {
1939
- Alert.alert(
1940
- 'Clear Analytics',
1941
- 'Are you sure you want to clear all analytics events?',
1942
- [
1943
- {text: 'Cancel', style: 'cancel'},
1944
- {
1945
- text: 'Clear All',
1946
- onPress: () => {
1947
- clearAnalyticsEvents();
1948
- setAnalyticsEvents([]);
1949
- setSelectedEvent(null);
1950
- prevEventIdsRef.current = new Set();
1951
- },
1952
- style: 'destructive',
1953
- },
1954
- ],
1955
- );
1956
- return;
1957
- }
1958
- if (activeTab === 'redux') {
1959
- Alert.alert(
1960
- 'Clear Redux Timeline',
1961
- 'Are you sure you want to clear the dispatched action history?',
1962
- [
1963
- {text: 'Cancel', style: 'cancel'},
1964
- {
1965
- text: 'Clear All',
1966
- onPress: () => {
1967
- clearActionHistory();
1968
- setReduxLastActionMap({});
1969
- },
1970
- style: 'destructive',
1971
- },
1972
- ],
1973
- );
1974
- return;
1975
- }
1976
- if (activeTab === 'crash') {
1977
- Alert.alert(
1978
- 'Clear Crash History',
1979
- 'Are you sure you want to clear all intercepted crash records?',
1980
- [
1981
- {text: 'Cancel', style: 'cancel'},
1982
- {
1983
- text: 'Clear All',
1984
- onPress: () => {
1985
- clearCrashRecords();
1986
- setCrashRecords([]);
1987
- setSelectedCrash(null);
1988
- },
1989
- style: 'destructive',
1990
- },
1991
- ],
1992
- );
1993
- return;
1994
- }
1995
- // Default: APIs tab. Only clears NETWORK logs — never touches the other tabs.
1996
- if (selectedLogs.size > 0) {
1997
- setLogs(prev => prev.filter(l => !selectedLogs.has(l.id)));
1998
- setSelectedLogs(new Set());
1999
- } else {
2000
- Alert.alert(
2001
- 'Clear Logs',
2002
- 'Are you sure you want to clear all network logs?',
2003
- [
2004
- {text: 'Cancel', style: 'cancel'},
2005
- {
2006
- text: 'Clear All',
2007
- onPress: clearNetworkOnly,
2008
- style: 'destructive',
2009
- },
2010
- ],
2011
- );
2012
- }
2013
- }
2014
-
2015
- // Clears ONLY network logs + their derived selection/filter state.
2016
- function clearNetworkOnly() {
2017
- clearNetworkLogs();
2018
- setLogs([]);
2019
- setSelectedLogs(new Set());
2020
- setSectionFilters({});
2021
- setCollapsedSections(new Set());
2022
- setStatusFilters(new Set());
2023
- setMethodFilters(new Set());
2024
- prevLogIdsRef.current = new Set();
2025
- logRouteMapRef.current = new Map();
2026
- }
2027
-
2028
- const detailTitle = useMemo(() => {
2029
- if (!selected) return '';
2030
- try {
2031
- const path = new URL(selected.url).pathname;
2032
- const parts = path.split('/').filter(Boolean);
2033
- return parts.length > 0 ? `/${parts.slice(-2).join('/')}` : '/';
2034
- } catch {
2035
- const parts = selected.url.split('/').filter(Boolean);
2036
- return parts.length > 0 ? `/${parts.slice(-2).join('/')}` : selected.url;
2037
- }
2038
- }, [selected]);
2039
-
2040
- const detailDisplayUrl = useMemo(() => {
2041
- return selected ? formatDisplayUrl(selected.url) : '';
2042
- }, [selected]);
2043
-
2044
- const toggleSelect = useCallback((id: number) => {
2045
- setSelectedLogs(prev => {
2046
- const next = new Set(prev);
2047
- if (next.has(id)) next.delete(id);
2048
- else next.add(id);
2049
- return next;
2050
- });
2051
- }, []);
2052
-
2053
- const contextValue: InspectorContextValue = useMemo(
2054
- () => ({
2055
- // ─── Modal / launcher ───────────────────────────────────────────────
2056
- visible,
2057
- setVisible,
2058
- closeModal,
2059
- isMinimized,
2060
- setIsMinimized,
2061
- minimizeInspector,
2062
- isReady,
2063
- enabled,
2064
- isEnabled: enabled,
2065
- appIcon,
2066
- environment,
2067
- modalHeightPercent,
2068
- setModalHeightPercent,
2069
- modalAnimationType,
2070
- setModalAnimationType,
2071
- hasNavigationContext,
2072
- setNavState,
2073
-
2074
- // ─── Tabs ───────────────────────────────────────────────────────────
2075
- activeTab,
2076
- switchActiveTab,
2077
- tabVisibility,
2078
- toggleTabVisibility,
2079
- lastReadApisCount,
2080
- lastReadLogsCount,
2081
- mediaCount,
2082
- refreshMediaCount,
2083
-
2084
- // ─── Selection / header state ───────────────────────────────────────
2085
- selected,
2086
- setSelected,
2087
- selectedEvent,
2088
- setSelectedEvent,
2089
- selectedLog,
2090
- setSelectedLog,
2091
- showHeaderInfo,
2092
- setShowHeaderInfo,
2093
- settingsPage,
2094
- setSettingsPage,
2095
- isFeedbackOpen,
2096
- setIsFeedbackOpen,
2097
- updateAvailable,
2098
- latestNpmVersion,
2099
- clearAnim,
2100
- activePulseAnim,
2101
- unreadPulseAnim,
2102
- runClearAllWithAnimation,
2103
-
2104
- // ─── FAB / launcher ─────────────────────────────────────────────────
2105
- useNativeFab,
2106
- fabPan,
2107
- fabPanResponder,
2108
- fabDraggedRef,
2109
- pulseAnim,
2110
- fabShineAnim,
2111
-
2112
- // ─── Network (APIs) ─────────────────────────────────────────────────
2113
- logs,
2114
- filteredLogs,
2115
- groupedData,
2116
- search,
2117
- setSearch,
2118
- searchScope,
2119
- setSearchScope,
2120
- isRegexSearch,
2121
- setIsRegexSearch,
2122
- isCaseSensitive,
2123
- setIsCaseSensitive,
2124
- quickFilter,
2125
- setQuickFilter,
2126
- statusFilters,
2127
- setStatusFilters,
2128
- methodFilters,
2129
- setMethodFilters,
2130
- availableMethods,
2131
- sortOrder,
2132
- setSortOrder,
2133
- selectedLogs,
2134
- toggleSelect,
2135
- minStart,
2136
- totalRange,
2137
- newLogIds,
2138
- toggleSectionFilter,
2139
- toggleSectionCollapse,
2140
- handleDelete,
2141
- isNetworkPaused,
2142
- setIsNetworkPaused,
2143
-
2144
- // ─── Network detail ─────────────────────────────────────────────────
2145
- detailTitle,
2146
- detailDisplayUrl,
2147
- apiDetailActiveTab,
2148
- setApiDetailActiveTab,
2149
- detailSearch,
2150
- setDetailSearch,
2151
- reqExpanded,
2152
- setReqExpanded,
2153
- resExpanded,
2154
- setResExpanded,
2155
- showReqDiff,
2156
- setShowReqDiff,
2157
- showResDiff,
2158
- setShowResDiff,
2159
- prevRequestData,
2160
- prevResponseData,
2161
- logRouteMapRef,
2162
-
2163
- // ─── Console (Logs) ─────────────────────────────────────────────────
2164
- consoleLogs,
2165
- visibleConsoleLogs,
2166
- filteredConsoleLogs,
2167
- logSearch,
2168
- setLogSearch,
2169
- logFilters,
2170
- setLogFilters,
2171
- logCounts,
2172
- logSortOrder,
2173
- setLogSortOrder,
2174
- isConsolePaused,
2175
- setIsConsolePaused,
2176
-
2177
- // ─── Analytics ──────────────────────────────────────────────────────
2178
- analyticsEvents,
2179
- filteredAnalyticsEvents,
2180
- analyticsSearch,
2181
- setAnalyticsSearch,
2182
- analyticsFilters,
2183
- setAnalyticsFilters,
2184
- isAnalyticsFilterApplied,
2185
- resetAnalyticsFilters,
2186
- newEventIds,
2187
- isAnalyticsLayoutReady,
2188
- setIsAnalyticsLayoutReady,
2189
- analyticsHeaderExpanded,
2190
- setAnalyticsHeaderExpanded,
2191
- isAnalyticsPaused,
2192
- setIsAnalyticsPaused,
2193
-
2194
- // ─── Redux ──────────────────────────────────────────────────────────
2195
- reduxState,
2196
- setReduxState,
2197
- reduxLastActionMap,
2198
- reduxSearch,
2199
- setReduxSearch,
2200
- selectedReduxSlice,
2201
- setSelectedReduxSlice,
2202
- selectedReduxAction,
2203
- setSelectedReduxAction,
2204
- reduxActiveSubTab,
2205
- setReduxActiveSubTab,
2206
-
2207
- // ─── Crash ───────────────────────────────────────────────────────────
2208
- crashRecords,
2209
- setCrashRecords,
2210
- selectedCrash,
2211
- setSelectedCrash,
2212
- lastReadCrashesCount,
2213
- maxCrashLogs,
2214
- setMaxCrashLogs,
2215
- clearAllCrashes: () => {
2216
- clearCrashRecords();
2217
- setCrashRecords([]);
2218
- setSelectedCrash(null);
2219
- },
2220
-
2221
- // ─── Settings ───────────────────────────────────────────────────────
2222
- settingsActiveSubTab,
2223
- setSettingsActiveSubTab,
2224
- defaultTab,
2225
- setDefaultTab,
2226
- isDark,
2227
- setIsDark,
2228
- showDuplicateLogs,
2229
- setShowDuplicateLogs,
2230
- showUpdateToast,
2231
- setShowUpdateToast,
2232
- showConsoleLevels,
2233
- setShowConsoleLevels,
2234
- resetToDefaults,
2235
- storage,
2236
- maxNetworkLogs,
2237
- setMaxNetworkLogs,
2238
- maxConsoleLogs,
2239
- setMaxConsoleLogs,
2240
- maxAnalyticsEventsLimit,
2241
- setMaxAnalyticsEventsLimit,
2242
- isAutoRamLimitEnabled,
2243
- setIsAutoRamLimitEnabled,
2244
- deviceFreeRamMb,
2245
- reduxAutoRefresh,
2246
- setReduxAutoRefreshState,
2247
- reduxExpandDepth,
2248
- setReduxExpandDepth,
2249
- }),
2250
- [
2251
- visible,
2252
- closeModal,
2253
- isReady,
2254
- enabled,
2255
- appIcon,
2256
- environment,
2257
- modalHeightPercent,
2258
- modalAnimationType,
2259
- hasNavigationContext,
2260
- setNavState,
2261
- activeTab,
2262
- switchActiveTab,
2263
- tabVisibility,
2264
- toggleTabVisibility,
2265
- lastReadApisCount,
2266
- lastReadLogsCount,
2267
- mediaCount,
2268
- refreshMediaCount,
2269
- selected,
2270
- selectedEvent,
2271
- selectedLog,
2272
- showHeaderInfo,
2273
- settingsPage,
2274
- isFeedbackOpen,
2275
- updateAvailable,
2276
- latestNpmVersion,
2277
- clearAnim,
2278
- activePulseAnim,
2279
- unreadPulseAnim,
2280
- runClearAllWithAnimation,
2281
- useNativeFab,
2282
- fabPan,
2283
- fabPanResponder,
2284
- pulseAnim,
2285
- fabShineAnim,
2286
- logs,
2287
- filteredLogs,
2288
- groupedData,
2289
- search,
2290
- searchScope,
2291
- isRegexSearch,
2292
- isCaseSensitive,
2293
- quickFilter,
2294
- statusFilters,
2295
- methodFilters,
2296
- availableMethods,
2297
- sortOrder,
2298
- selectedLogs,
2299
- toggleSelect,
2300
- minStart,
2301
- totalRange,
2302
- newLogIds,
2303
- toggleSectionFilter,
2304
- toggleSectionCollapse,
2305
- handleDelete,
2306
- isNetworkPaused,
2307
- detailTitle,
2308
- detailDisplayUrl,
2309
- apiDetailActiveTab,
2310
- detailSearch,
2311
- reqExpanded,
2312
- resExpanded,
2313
- showReqDiff,
2314
- showResDiff,
2315
- prevRequestData,
2316
- prevResponseData,
2317
- consoleLogs,
2318
- visibleConsoleLogs,
2319
- filteredConsoleLogs,
2320
- logSearch,
2321
- logFilters,
2322
- logCounts,
2323
- logSortOrder,
2324
- isConsolePaused,
2325
- analyticsEvents,
2326
- filteredAnalyticsEvents,
2327
- analyticsSearch,
2328
- analyticsFilters,
2329
- isAnalyticsFilterApplied,
2330
- resetAnalyticsFilters,
2331
- newEventIds,
2332
- isAnalyticsLayoutReady,
2333
- analyticsHeaderExpanded,
2334
- isAnalyticsPaused,
2335
- reduxState,
2336
- reduxLastActionMap,
2337
- reduxSearch,
2338
- selectedReduxSlice,
2339
- selectedReduxAction,
2340
- reduxActiveSubTab,
2341
- crashRecords,
2342
- selectedCrash,
2343
- lastReadCrashesCount,
2344
- maxCrashLogs,
2345
- settingsActiveSubTab,
2346
- defaultTab,
2347
- isDark,
2348
- showDuplicateLogs,
2349
- showUpdateToast,
2350
- showConsoleLevels,
2351
- storage,
2352
- maxNetworkLogs,
2353
- maxConsoleLogs,
2354
- maxAnalyticsEventsLimit,
2355
- isAutoRamLimitEnabled,
2356
- deviceFreeRamMb,
2357
- reduxAutoRefresh,
2358
- reduxExpandDepth,
2359
- isMinimized,
2360
- ],
2361
- );
2362
-
2363
- return (
2364
- <InspectorContext.Provider value={contextValue}>
2365
- <MainScreen />
2366
- </InspectorContext.Provider>
2367
- );
2368
- };
2369
-
2370
- const NetworkInspectorWrapper = (props: NetworkInspectorProps) => {
2371
- const enabled = props?.enabled ?? true;
2372
- const telemetry = props?.telemetry ?? true;
2373
-
2374
- useEffect(() => {
2375
- if (!enabled) {
2376
- // When inspector is disabled: hide FAB and kill all telemetry
2377
- hideNativeFloatingButton().catch(() => {});
2378
- setTelemetryEnabled(false);
2379
- } else if (telemetry) {
2380
- // Inspector is enabled and telemetry is on — fire heartbeat
2381
- trackActiveTelemetryHeartbeat(true, props?.environment);
2382
- } else {
2383
- // Inspector is enabled but telemetry is explicitly off
2384
- setTelemetryEnabled(false);
2385
- }
2386
- return () => {
2387
- hideNativeFloatingButton().catch(() => {});
2388
- };
2389
- }, [enabled, telemetry, props?.environment]);
2390
-
2391
- // If enabled is false, return null immediately with 0 overhead
2392
- if (!enabled) {
2393
- return null;
2394
- }
2395
-
2396
- return (
2397
- <I18nextProvider i18n={i18n}>
2398
- <ErrorBoundary fallbackType="inline">
2399
- <NetworkInspector
2400
- {...props}
2401
- enabled={enabled}
2402
- />
2403
- </ErrorBoundary>
2404
- </I18nextProvider>
2405
- );
2406
- };
2407
-
2408
- export default NetworkInspectorWrapper;
2409
-
2410
- // Re-export public APIs
2411
- export {
2412
- setupNetworkLogger,
2413
- clearNetworkLogs,
2414
- subscribeNetworkLogs,
2415
- addAxiosInterceptors,
2416
- } from './customHooks/networkLogger';
2417
-
2418
- export {
2419
- setupConsoleLogger,
2420
- clearConsoleLogs,
2421
- subscribeConsoleLogs,
2422
- } from './customHooks/consoleLogger';
2423
-
2424
- export {
2425
- setupAnalyticsLogger,
2426
- logAnalyticsEvent,
2427
- subscribeAnalyticsEvents,
2428
- clearAnalyticsEvents,
2429
- getCurrentUserProperties,
2430
- getCurrentUserId,
2431
- getDefaultEventParameters,
2432
- getCollectionEnabled,
2433
- } from './customHooks/analyticsLogger';
2434
-
2435
- export {
2436
- setupGlobalCrashHandler,
2437
- subscribeCrashEvents,
2438
- emitCrashEvent,
2439
- getCrashRecords,
2440
- clearCrashRecords,
2441
- simulateTestCrash,
2442
- exportCrashReport,
2443
- parseCrashStackTrace,
2444
- recordCustomCrash,
2445
- addCrashBreadcrumb,
2446
- recordNavigationBreadcrumb,
2447
- recordNetworkBreadcrumb,
2448
- recordReduxBreadcrumb,
2449
- recordUserActionBreadcrumb,
2450
- computeCrashFingerprint,
2451
- type CrashEventPayload,
2452
- } from './customHooks/crashHandler';
2453
-
2454
- export {default as CrashTab} from './components/Inspector/CrashTab';
2455
- export {default as ErrorBoundary} from './components/ErrorBoundary';
2456
-
2457
- export {
2458
- connectReduxStore,
2459
- inspectorReduxMiddleware,
2460
- getReduxState,
2461
- subscribeReduxState,
2462
- getActionHistory,
2463
- clearActionHistory,
2464
- getLastActionForReducer,
2465
- } from './customHooks/reduxLogger';
2466
-
2467
- export {
2468
- getEventCategory,
2469
- registerGAPlugin,
2470
- type GAPlugin,
2471
- } from './helpers/gaAnalyticsRegistry';
2472
-
2473
- export {
2474
- usePerformanceTracker,
2475
- useComponentProfiler,
2476
- useNavigationProfiler,
2477
- trackComponentRender,
2478
- trackNavigationTransition,
2479
- trackHeavyTask,
2480
- measureAsync,
2481
- getHermesMemoryStats,
2482
- registerComponentProfile,
2483
- subscribeRenderProfiles,
2484
- getRenderProfiles,
2485
- logPerformanceEvent,
2486
- clearPerformanceEvents,
2487
- subscribePerformanceEvents,
2488
- getPerformanceEvents,
2489
- getInitialRenderProfiles,
2490
- getInitialPerformanceEvents,
2491
- generateFixSnippet,
2492
- } from './customHooks/performanceTracker';
2493
-
2494
- export {
2495
- InspectLog,
2496
- InspectTrackTime,
2497
- InspectCatch,
2498
- type InspectLogOptions,
2499
- } from './decorators';
2500
-
2501
- export {
2502
- getNativeDeviceMetrics,
2503
- enableNativeCrashProtection,
2504
- subscribeNativeCrashes,
2505
- showNativeFloatingButton,
2506
- hideNativeFloatingButton,
2507
- setNativeFloatingButtonBadge,
2508
- subscribeNativeFloatingButtonPress,
2509
- subscribeNativeDeviceShake,
2510
- startNativeFpsMonitoring,
2511
- stopNativeFpsMonitoring,
2512
- getNativeFpsMetrics,
2513
- getNativeStorageItem,
2514
- setNativeStorageItem,
2515
- isNativeModuleAvailable,
2516
- type NativeDeviceMetrics,
2517
- type NativeCrashEvent,
2518
- type FloatingButtonOptions,
2519
- type NativeFpsMetrics,
2520
- } from './native/NativeInspector';
2521
-
2522
- export {
2523
- setupMemoryWarningHandler,
2524
- pruneAllLogs,
2525
- subscribeMemoryWarning,
2526
- type MemoryPruneSummary,
2527
- } from './helpers';
2528
-
2529
- export {
2530
- setMaxNetworkLogsLimit,
2531
- getMaxNetworkLogsLimit,
2532
- pruneNetworkLogs,
2533
- } from './customHooks/networkLogger';
2534
-
2535
- export {
2536
- setMaxConsoleLogsLimit,
2537
- getMaxConsoleLogsLimit,
2538
- pruneConsoleLogs,
2539
- } from './customHooks/consoleLogger';
2540
-
2541
- export {
2542
- setMaxReduxHistoryLimit,
2543
- getMaxReduxHistoryLimit,
2544
- pruneReduxHistory,
2545
- } from './customHooks/reduxLogger';
2546
-
2547
- export {
2548
- setMaxAnalyticsLogsLimit,
2549
- getMaxAnalyticsLogsLimit,
2550
- pruneAnalyticsLogs,
2551
- } from './customHooks/analyticsLogger';
2552
-
2553
- export {
2554
- getMaxCrashLogsLimit,
2555
- pruneCrashRecords,
2556
- } from './customHooks/crashHandler';
2557
-
2558
- export {
2559
- setMaxPerformanceEventsLimit,
2560
- getMaxPerformanceEventsLimit,
2561
- prunePerformanceEvents,
2562
- } from './customHooks/performanceTracker';
2563
-
2564
- export {
2565
- BrandSquareIcon,
2566
- BrandCircleIcon,
2567
- } from './components/NetworkIcons';
2568
-
2569
- export {
2570
- connectAsyncStorage,
2571
- connectMMKV,
2572
- isAsyncStorageConnected,
2573
- isMMKVConnected,
2574
- getRegisteredMMKVInstanceIds,
2575
- fetchStorageEntries,
2576
- setStorageEntry,
2577
- removeStorageEntry,
2578
- clearStorageDriver,
2579
- subscribeToStorageChanges,
2580
- type StorageEntry,
2581
- type StorageDriver,
2582
- } from './customHooks/storageInspector';
2583
-
2584
- export {
2585
- ActiveTab,
2586
- Method,
2587
- StatusFilter,
2588
- SortOrder,
2589
- LocalFilter,
2590
- ModalAnimationType,
2591
- SettingsPage,
2592
- SettingsSubTab,
2593
- LogFilter,
2594
- ConsoleLogType,
2595
- AnalyticsEventSource,
2596
- GAEventCategory,
2597
- StackFrameType,
2598
- DiffResultType,
2599
- BundleSubTab,
2600
- PerformanceSubTab,
2601
- CrashType,
2602
- CrashExportFormat,
2603
- CrashDetailSubTab,
2604
- CrashFilterType,
2605
- BreadcrumbType,
2606
- } from './types';
2607
-
2608
- export {
2609
- AppFonts,
2610
- setAppFonts,
2611
- type AppFontConfig,
2612
- } from './styles/AppFonts';
2613
-
2614
- export {
2615
- AppColors,
2616
- setAppColors,
2617
- getThemeColors,
2618
- updateAppColorsTheme,
2619
- } from './styles/AppColors';
2620
-
2621
- export {
2622
- t,
2623
- i18n,
2624
- useTranslation,
2625
- setLanguage,
2626
- addTranslations,
2627
- setTranslations,
2628
- I18nextProvider,
2629
- } from './i18n';
2630
-
2631
- export {
2632
- fetchRemoteConfigModuleStatus,
2633
- isFirebaseRemoteConfigAvailable,
2634
- } from './helpers/remoteConfig';
2635
-
2636
- export {
2637
- ScreenCapture,
2638
- ScreenRecorder,
2639
- type ScreenshotOptions,
2640
- type ScreenshotResult,
2641
- type RecordingOptions,
2642
- type RecordingResult,
2643
- type GifConversionOptions,
2644
- type CapturedMediaItem,
2645
- type ImageFormat,
2646
- type AudioSource,
2647
- type RecordingFormat,
2648
- } from './capture';
2649
-
2650
- export { LIB_VERSION } from './constants/version';