react-native-inapp-inspector 1.1.35 → 2.0.0

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 (82) hide show
  1. package/README.md +123 -101
  2. package/android/build.gradle +13 -1
  3. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +904 -0
  4. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.kt +17 -0
  5. package/dist/commonjs/components/CopyButton.js +3 -0
  6. package/dist/commonjs/components/ErrorBoundary.js +3 -0
  7. package/dist/commonjs/components/Inspector/AnalyticsTab.js +7 -3
  8. package/dist/commonjs/components/Inspector/BundleTab.js +1 -1
  9. package/dist/commonjs/components/Inspector/ConsoleTab.js +1 -1
  10. package/dist/commonjs/components/Inspector/CrashTab.js +97 -90
  11. package/dist/commonjs/components/Inspector/InspectorHeader.js +12 -8
  12. package/dist/commonjs/components/Inspector/MainScreen.js +14 -101
  13. package/dist/commonjs/components/Inspector/NetworkTab.js +47 -10
  14. package/dist/commonjs/components/Inspector/PerformanceTab.js +96 -3
  15. package/dist/commonjs/components/Inspector/ReduxTab.js +18 -10
  16. package/dist/commonjs/components/Inspector/SettingsPanel.js +342 -88
  17. package/dist/commonjs/components/Inspector/TabBar.js +6 -3
  18. package/dist/commonjs/components/NetworkIcons.d.ts +1 -0
  19. package/dist/commonjs/components/NetworkIcons.js +6 -1
  20. package/dist/commonjs/components/TouchableScale.d.ts +1 -0
  21. package/dist/commonjs/components/TouchableScale.js +30 -32
  22. package/dist/commonjs/constants/index.d.ts +1 -2
  23. package/dist/commonjs/constants/index.js +4 -0
  24. package/dist/commonjs/constants/version.d.ts +1 -1
  25. package/dist/commonjs/constants/version.js +1 -1
  26. package/dist/commonjs/customHooks/analyticsLogger.d.ts +4 -0
  27. package/dist/commonjs/customHooks/analyticsLogger.js +7 -5
  28. package/dist/commonjs/customHooks/crashHandler.js +7 -0
  29. package/dist/commonjs/helpers/index.js +10 -1
  30. package/dist/commonjs/helpers/settingsStore.d.ts +9 -0
  31. package/dist/commonjs/helpers/settingsStore.js +59 -3
  32. package/dist/commonjs/index.d.ts +1 -0
  33. package/dist/commonjs/index.js +229 -39
  34. package/dist/commonjs/native/NativeInspector.d.ts +125 -0
  35. package/dist/commonjs/native/NativeInspector.js +313 -0
  36. package/dist/commonjs/native/NativeNetworkInspector.d.ts +22 -0
  37. package/dist/commonjs/native/NativeNetworkInspector.js +4 -0
  38. package/dist/commonjs/styles/index.js +6 -6
  39. package/dist/commonjs/types/enums.d.ts +1 -1
  40. package/dist/commonjs/types/interfaces.d.ts +9 -0
  41. package/dist/esm/components/CopyButton.js +3 -0
  42. package/dist/esm/components/ErrorBoundary.js +3 -0
  43. package/dist/esm/components/Inspector/AnalyticsTab.js +7 -3
  44. package/dist/esm/components/Inspector/BundleTab.js +1 -1
  45. package/dist/esm/components/Inspector/ConsoleTab.js +1 -1
  46. package/dist/esm/components/Inspector/CrashTab.js +97 -90
  47. package/dist/esm/components/Inspector/InspectorHeader.js +13 -9
  48. package/dist/esm/components/Inspector/MainScreen.js +15 -69
  49. package/dist/esm/components/Inspector/NetworkTab.js +47 -10
  50. package/dist/esm/components/Inspector/PerformanceTab.js +96 -3
  51. package/dist/esm/components/Inspector/ReduxTab.js +18 -10
  52. package/dist/esm/components/Inspector/SettingsPanel.js +344 -90
  53. package/dist/esm/components/Inspector/TabBar.js +7 -4
  54. package/dist/esm/components/NetworkIcons.d.ts +1 -0
  55. package/dist/esm/components/NetworkIcons.js +4 -0
  56. package/dist/esm/components/TouchableScale.d.ts +1 -0
  57. package/dist/esm/components/TouchableScale.js +31 -33
  58. package/dist/esm/constants/index.d.ts +1 -2
  59. package/dist/esm/constants/index.js +4 -0
  60. package/dist/esm/constants/version.d.ts +1 -1
  61. package/dist/esm/constants/version.js +1 -1
  62. package/dist/esm/customHooks/analyticsLogger.d.ts +4 -0
  63. package/dist/esm/customHooks/analyticsLogger.js +5 -4
  64. package/dist/esm/customHooks/crashHandler.js +7 -0
  65. package/dist/esm/helpers/index.js +10 -1
  66. package/dist/esm/helpers/settingsStore.d.ts +9 -0
  67. package/dist/esm/helpers/settingsStore.js +58 -3
  68. package/dist/esm/index.d.ts +1 -0
  69. package/dist/esm/index.js +215 -39
  70. package/dist/esm/native/NativeInspector.d.ts +125 -0
  71. package/dist/esm/native/NativeInspector.js +289 -0
  72. package/dist/esm/native/NativeNetworkInspector.d.ts +22 -0
  73. package/dist/esm/native/NativeNetworkInspector.js +2 -0
  74. package/dist/esm/styles/index.js +6 -6
  75. package/dist/esm/types/enums.d.ts +1 -1
  76. package/dist/esm/types/interfaces.d.ts +9 -0
  77. package/ios/NetworkInspectorModule.h +6 -0
  78. package/ios/NetworkInspectorModule.m +720 -5
  79. package/package.json +13 -2
  80. package/react-native-inapp-inspector.podspec +7 -2
  81. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +0 -97
  82. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +0 -28
package/dist/esm/index.js CHANGED
@@ -10,7 +10,8 @@ import { InspectorContext, animateNextLayout, } from './components/Inspector/Ins
10
10
  // Helpers
11
11
  import { formatDisplayUrl, getNavigationInfo, deduplicateLogs, getDomainColor, getEventCategory, } from './helpers';
12
12
  // #5 — settings persistence
13
- import { loadSettings, saveSettings, setCustomStorage, clearPersistedSettings, } from './helpers/settingsStore';
13
+ import { loadSettings, saveSettings, setCustomStorage, clearPersistedSettings, calculateRamBasedLimits, } from './helpers/settingsStore';
14
+ import { getNativeSystemMetrics, pushNativeLogRecord, fetchNativeCachedPage, } from './native/NativeInspector';
14
15
  // Network
15
16
  import { setupNetworkLogger, clearNetworkLogs, subscribeNetworkLogs, setNetworkModuleEnabled, } from './customHooks/networkLogger';
16
17
  // Console
@@ -22,6 +23,7 @@ import { subscribeAnalyticsEvents, clearAnalyticsEvents, autoSetupAnalyticsLogge
22
23
  import { getReduxState, subscribeReduxState, setReduxAutoRefresh, getLastActionForReducer, clearActionHistory, isReduxConnected, setReduxModuleEnabled, } from './customHooks/reduxLogger';
23
24
  import { setPerformanceModuleEnabled } from './customHooks/performanceTracker';
24
25
  import { setBundleModuleEnabled } from './customHooks/bundleAnalyzer';
26
+ import { showNativeFloatingButton, hideNativeFloatingButton, setNativeFloatingButtonBadge, subscribeNativeFloatingButtonPress, subscribeNativeDeviceShake, isNativeModuleAvailable, } from './native/NativeInspector';
25
27
  import { LIB_VERSION } from './constants';
26
28
  // Stylesheet
27
29
  import { toggleGlobalTheme } from './styles';
@@ -174,6 +176,9 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
174
176
  crash: false,
175
177
  });
176
178
  const [maxNetworkLogs, setMaxNetworkLogs] = useState(100);
179
+ const [maxAnalyticsEventsLimit, setMaxAnalyticsEventsLimit] = useState(250);
180
+ const [isAutoRamLimitEnabled, setIsAutoRamLimitEnabled] = useState(true);
181
+ const [deviceFreeRamMb, setDeviceFreeRamMb] = useState(1800);
177
182
  const [reduxAutoRefresh, setReduxAutoRefreshState] = useState(true);
178
183
  const [reduxExpandDepth, setReduxExpandDepth] = useState(1);
179
184
  // #6 — tab the inspector opens on. Shown with a DEFAULT badge in Settings.
@@ -189,6 +194,25 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
189
194
  setCrashModuleEnabled(!!tabVisibility.crash);
190
195
  setBundleModuleEnabled(!!tabVisibility.bundle);
191
196
  }, [tabVisibility]);
197
+ // Query native hardware RAM and auto-tune limits if auto-RAM is active
198
+ useEffect(() => {
199
+ getNativeSystemMetrics()
200
+ .then(metrics => {
201
+ if (metrics) {
202
+ const freeRam = metrics.totalPhysicalRamMb - metrics.residentRamMb;
203
+ const freeMb = Math.max(200, Math.round(freeRam > 0 ? freeRam : 1500));
204
+ setDeviceFreeRamMb(freeMb);
205
+ if (isAutoRamLimitEnabled) {
206
+ const profile = calculateRamBasedLimits(freeMb);
207
+ setMaxNetworkLogs(profile.maxNetworkLogs);
208
+ setMaxConsoleLogs(profile.maxConsoleLogs);
209
+ setMaxAnalyticsEventsLimit(profile.maxAnalyticsEvents);
210
+ setMaxCrashLogs(profile.maxCrashRecords);
211
+ }
212
+ }
213
+ })
214
+ .catch(() => { });
215
+ }, [isAutoRamLimitEnabled]);
192
216
  const resetToDefaults = async () => {
193
217
  await clearPersistedSettings();
194
218
  setIsDark(false);
@@ -205,9 +229,12 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
205
229
  crash: false,
206
230
  });
207
231
  setDefaultTab('apis');
208
- setMaxCrashLogs(100);
209
- setMaxNetworkLogs(100);
210
- setMaxConsoleLogs(200);
232
+ setIsAutoRamLimitEnabled(true);
233
+ const profile = calculateRamBasedLimits(deviceFreeRamMb);
234
+ setMaxCrashLogs(profile.maxCrashRecords);
235
+ setMaxNetworkLogs(profile.maxNetworkLogs);
236
+ setMaxConsoleLogs(profile.maxConsoleLogs);
237
+ setMaxAnalyticsEventsLimit(profile.maxAnalyticsEvents);
211
238
  setShowConsoleLevels({
212
239
  info: true,
213
240
  warn: true,
@@ -241,10 +268,16 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
241
268
  }));
242
269
  if (saved.defaultTab)
243
270
  setDefaultTab(saved.defaultTab);
271
+ if (saved.isAutoRamLimitEnabled != null)
272
+ setIsAutoRamLimitEnabled(saved.isAutoRamLimitEnabled);
244
273
  if (saved.maxNetworkLogs != null)
245
274
  setMaxNetworkLogs(saved.maxNetworkLogs);
246
275
  if (saved.maxConsoleLogs != null)
247
276
  setMaxConsoleLogs(saved.maxConsoleLogs);
277
+ if (saved.maxAnalyticsEventsLimit != null)
278
+ setMaxAnalyticsEventsLimit(saved.maxAnalyticsEventsLimit);
279
+ if (saved.maxCrashLogs != null)
280
+ setMaxCrashLogs(saved.maxCrashLogs);
248
281
  if (saved.showConsoleLevels)
249
282
  setShowConsoleLevels(saved.showConsoleLevels);
250
283
  if (saved.reduxAutoRefresh != null)
@@ -287,6 +320,9 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
287
320
  defaultTab,
288
321
  maxNetworkLogs,
289
322
  maxConsoleLogs,
323
+ maxAnalyticsEventsLimit,
324
+ maxCrashLogs,
325
+ isAutoRamLimitEnabled,
290
326
  showConsoleLevels,
291
327
  reduxAutoRefresh,
292
328
  reduxExpandDepth,
@@ -300,6 +336,9 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
300
336
  defaultTab,
301
337
  maxNetworkLogs,
302
338
  maxConsoleLogs,
339
+ maxAnalyticsEventsLimit,
340
+ maxCrashLogs,
341
+ isAutoRamLimitEnabled,
303
342
  showConsoleLevels,
304
343
  reduxAutoRefresh,
305
344
  reduxExpandDepth,
@@ -374,13 +413,43 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
374
413
  return newVisibility;
375
414
  });
376
415
  };
416
+ const loadNativePage = useCallback(async (tabKey, query = '') => {
417
+ if (tabKey === 'apis' || tabKey === 'logs' || tabKey === 'analytics' || tabKey === 'crash') {
418
+ const pageData = await fetchNativeCachedPage(tabKey, 0, 100, query);
419
+ if (pageData && pageData.items && pageData.items.length > 0) {
420
+ if (tabKey === 'apis')
421
+ setLogs(pageData.items);
422
+ else if (tabKey === 'logs')
423
+ setConsoleLogs(pageData.items);
424
+ else if (tabKey === 'analytics')
425
+ setAnalyticsEvents(pageData.items);
426
+ else if (tabKey === 'crash')
427
+ setCrashRecords(pageData.items);
428
+ }
429
+ }
430
+ }, []);
377
431
  const switchActiveTab = useCallback((key) => {
378
432
  if (key === 'redux' && !isReduxConnected())
379
433
  return;
380
434
  if (key === 'analytics' && !isAnalyticsConnected())
381
435
  return;
382
- setActiveTab(key);
383
- }, []);
436
+ setSelected(null);
437
+ setSelectedEvent(null);
438
+ setSelectedLog(null);
439
+ setSelectedReduxSlice(null);
440
+ setSelectedReduxAction(null);
441
+ setSelectedCrash(null);
442
+ if (typeof React.startTransition === 'function') {
443
+ React.startTransition(() => {
444
+ setActiveTab(key);
445
+ loadNativePage(key);
446
+ });
447
+ }
448
+ else {
449
+ setActiveTab(key);
450
+ loadNativePage(key);
451
+ }
452
+ }, [loadNativePage]);
384
453
  const [selectedEvent, setSelectedEvent] = useState(null);
385
454
  const [analyticsSearch, setAnalyticsSearch] = useState('');
386
455
  const [analyticsFilters, setAnalyticsFilters] = useState({
@@ -628,6 +697,53 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
628
697
  loop.start();
629
698
  return () => loop.stop();
630
699
  }, [unreadPulseAnim]);
700
+ const isNativeModule = useMemo(() => isNativeModuleAvailable(), []);
701
+ const useNativeFab = (Platform.OS === 'ios' || Platform.OS === 'android') && isNativeModule;
702
+ // 100% Native Main-Thread Floating Button Lifecycle
703
+ useEffect(() => {
704
+ if (!useNativeFab || !isEnabled || !enabled) {
705
+ if (useNativeFab) {
706
+ hideNativeFloatingButton();
707
+ }
708
+ return;
709
+ }
710
+ if (visible) {
711
+ hideNativeFloatingButton();
712
+ }
713
+ else {
714
+ showNativeFloatingButton();
715
+ setNativeFloatingButtonBadge(logs.length > 0 || analyticsEvents.length > 0);
716
+ }
717
+ }, [
718
+ useNativeFab,
719
+ isEnabled,
720
+ enabled,
721
+ visible,
722
+ logs.length,
723
+ analyticsEvents.length,
724
+ ]);
725
+ // Subscribe to native UI-thread floating button tap events
726
+ useEffect(() => {
727
+ if (!useNativeFab)
728
+ return;
729
+ const unsubscribe = subscribeNativeFloatingButtonPress(() => {
730
+ setVisible(true);
731
+ });
732
+ return () => {
733
+ unsubscribe();
734
+ };
735
+ }, [useNativeFab]);
736
+ // Subscribe to physical hardware shake events (or Cmd+Ctrl+Z on iOS simulator)
737
+ useEffect(() => {
738
+ if (!useNativeFab)
739
+ return;
740
+ const unsubscribe = subscribeNativeDeviceShake(() => {
741
+ setVisible(prev => !prev);
742
+ });
743
+ return () => {
744
+ unsubscribe();
745
+ };
746
+ }, [useNativeFab]);
631
747
  const isVisibleRefObj = useRef(visible);
632
748
  // #6 — every time the inspector is opened, land on chosen default tab & sync latest logs
633
749
  useEffect(() => {
@@ -694,6 +810,9 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
694
810
  return;
695
811
  }
696
812
  const updated = getCrashRecords();
813
+ if (updated.length > 0) {
814
+ pushNativeLogRecord('crash', JSON.stringify(updated[0]));
815
+ }
697
816
  if (isVisibleRef.current) {
698
817
  setCrashRecords(updated);
699
818
  }
@@ -702,12 +821,16 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
702
821
  // during the very first commit, which fires before effects are mounted).
703
822
  const pendingCrashes = getCrashRecords();
704
823
  if (pendingCrashes.length > 0) {
824
+ pendingCrashes.forEach(c => pushNativeLogRecord('crash', JSON.stringify(c)));
705
825
  setCrashRecords(pendingCrashes);
706
826
  }
707
827
  let timeoutId;
708
828
  let isFirstNetworkCall = true;
709
829
  const unsubscribe = subscribeNetworkLogs((raw) => {
710
830
  latestNetworkLogsRef.current = raw;
831
+ if (raw.length > 0) {
832
+ pushNativeLogRecord('apis', JSON.stringify(raw[0]));
833
+ }
711
834
  if (isNetworkPausedRef.current)
712
835
  return;
713
836
  if (!isVisibleRef.current)
@@ -749,6 +872,9 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
749
872
  let isFirstAnalyticsCall = true;
750
873
  const unsubscribeAnalytics = subscribeAnalyticsEvents((raw) => {
751
874
  latestAnalyticsEventsRef.current = raw;
875
+ if (raw.length > 0) {
876
+ pushNativeLogRecord('analytics', JSON.stringify(raw[0]));
877
+ }
752
878
  if (isAnalyticsPausedRef.current)
753
879
  return;
754
880
  if (!isVisibleRef.current)
@@ -788,6 +914,9 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
788
914
  let isFirstConsoleCall = true;
789
915
  const unsubscribeConsole = subscribeConsoleLogs((raw) => {
790
916
  latestConsoleLogsRef.current = raw;
917
+ if (raw.length > 0) {
918
+ pushNativeLogRecord('logs', JSON.stringify(raw[0]));
919
+ }
791
920
  if (isConsolePausedRef.current)
792
921
  return;
793
922
  if (!isVisibleRef.current)
@@ -858,6 +987,10 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
858
987
  return true;
859
988
  if (f === 'Failed')
860
989
  return log.status === 0 || log.status == null;
990
+ const codeNum = parseInt(f, 10);
991
+ if (!isNaN(codeNum)) {
992
+ return Number(log.status) === codeNum;
993
+ }
861
994
  return String(log.status)[0] === f[0];
862
995
  });
863
996
  if (!matched)
@@ -873,9 +1006,26 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
873
1006
  if (!matchedMethod)
874
1007
  return false;
875
1008
  }
876
- // Search Bar Check
877
- if (search && !log.url?.toLowerCase().includes(search.toLowerCase()))
878
- return false;
1009
+ // Comprehensive Search Bar Check
1010
+ if (search && search.trim().length > 0) {
1011
+ const queryTokens = search.trim().toLowerCase().split(/\s+/).filter(Boolean);
1012
+ const routePath = logRouteMapRef.current.get(log.id)?.path || '';
1013
+ const reqStr = typeof log.request === 'string' ? log.request : JSON.stringify(log.request || '');
1014
+ const resStr = typeof log.response === 'string' ? log.response : JSON.stringify(log.response || '');
1015
+ const searchTarget = [
1016
+ log.method || '',
1017
+ log.url || '',
1018
+ String(log.status ?? ''),
1019
+ routePath,
1020
+ reqStr,
1021
+ resStr,
1022
+ JSON.stringify(log.requestHeaders || ''),
1023
+ JSON.stringify(log.responseHeaders || ''),
1024
+ ].join(' ').toLowerCase();
1025
+ const isMatch = queryTokens.every(token => searchTarget.includes(token));
1026
+ if (!isMatch)
1027
+ return false;
1028
+ }
879
1029
  return true;
880
1030
  });
881
1031
  if (sortOrder === 'oldest') {
@@ -1030,12 +1180,20 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1030
1180
  const filteredAnalyticsEvents = useMemo(() => {
1031
1181
  let events = analyticsEvents;
1032
1182
  // Search query filter
1033
- if (analyticsSearch) {
1034
- const s = analyticsSearch.toLowerCase();
1035
- events = events.filter(e => e.name.toLowerCase().includes(s) ||
1036
- JSON.stringify(e.params || {}).toLowerCase().includes(s) ||
1037
- (e.screenName ?? '').toLowerCase().includes(s) ||
1038
- (e.pageTitle ?? '').toLowerCase().includes(s));
1183
+ if (analyticsSearch && analyticsSearch.trim().length > 0) {
1184
+ const queryTokens = analyticsSearch.trim().toLowerCase().split(/\s+/).filter(Boolean);
1185
+ events = events.filter(e => {
1186
+ const searchTarget = [
1187
+ e.name || '',
1188
+ JSON.stringify(e.params || ''),
1189
+ JSON.stringify(e.userProperties || ''),
1190
+ e.screenName || '',
1191
+ e.pageTitle || '',
1192
+ e.userId || '',
1193
+ e.source || '',
1194
+ ].join(' ').toLowerCase();
1195
+ return queryTokens.every(token => searchTarget.includes(token));
1196
+ });
1039
1197
  }
1040
1198
  // Category filter
1041
1199
  if (analyticsFilters.categories.size > 0 && !analyticsFilters.categories.has('all')) {
@@ -1073,16 +1231,15 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1073
1231
  }
1074
1232
  // Payload complexity filter
1075
1233
  if (analyticsFilters.payloadComplexity !== 'all') {
1076
- events = events.filter(e => {
1077
- const count = e.params ? Object.keys(e.params).length : 0;
1078
- if (analyticsFilters.payloadComplexity === 'none')
1079
- return count === 0;
1080
- if (analyticsFilters.payloadComplexity === 'simple')
1081
- return count >= 1 && count <= 5;
1082
- if (analyticsFilters.payloadComplexity === 'heavy')
1083
- return count > 5;
1084
- return true;
1085
- });
1234
+ if (analyticsFilters.payloadComplexity === 'none') {
1235
+ events = events.filter(e => !e.params || Object.keys(e.params).length === 0);
1236
+ }
1237
+ else if (analyticsFilters.payloadComplexity === 'simple') {
1238
+ events = events.filter(e => e.params && Object.keys(e.params).length <= 3);
1239
+ }
1240
+ else if (analyticsFilters.payloadComplexity === 'heavy') {
1241
+ events = events.filter(e => e.params && Object.keys(e.params).length > 3);
1242
+ }
1086
1243
  }
1087
1244
  // Conversion / Goal events filter
1088
1245
  if (analyticsFilters.onlyConversions) {
@@ -1115,10 +1272,8 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1115
1272
  }
1116
1273
  // Has Revenue filter
1117
1274
  if (analyticsFilters.hasRevenue) {
1118
- events = events.filter(e => {
1119
- const val = e.params?.value ?? e.params?.price;
1120
- return val !== undefined && val !== null && Number(val) > 0;
1121
- });
1275
+ events = events.filter(e => (e.params?.value != null && !isNaN(Number(e.params.value))) ||
1276
+ (e.params?.price != null && !isNaN(Number(e.params.price))));
1122
1277
  }
1123
1278
  // Has Items filter
1124
1279
  if (analyticsFilters.hasItems) {
@@ -1183,16 +1338,23 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1183
1338
  if (logFilters.has('user-log') && log.sourceMethod === 'log')
1184
1339
  return true;
1185
1340
  if (logFilters.has('analytics') &&
1186
- log.message.toLowerCase().includes('[analytics error]'))
1341
+ log.message?.toLowerCase().includes('[analytics error]'))
1187
1342
  return true;
1188
1343
  return false;
1189
1344
  });
1190
1345
  }
1191
- // Search bar check
1192
- if (logSearch) {
1193
- const s = logSearch.toLowerCase();
1194
- result = result.filter(log => log.message.toLowerCase().includes(s) ||
1195
- (log.caller ?? '').toLowerCase().includes(s));
1346
+ // Comprehensive Search bar check
1347
+ if (logSearch && logSearch.trim().length > 0) {
1348
+ const queryTokens = logSearch.trim().toLowerCase().split(/\s+/).filter(Boolean);
1349
+ result = result.filter(log => {
1350
+ const searchTarget = [
1351
+ log.message || '',
1352
+ log.caller || '',
1353
+ log.type || '',
1354
+ log.sourceMethod || '',
1355
+ ].join(' ').toLowerCase();
1356
+ return queryTokens.every(token => searchTarget.includes(token));
1357
+ });
1196
1358
  }
1197
1359
  // #7 — apply sort order (newest/oldest first)
1198
1360
  result = [...result].sort((a, b) => logSortOrder === 'newest'
@@ -1229,10 +1391,17 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1229
1391
  ]);
1230
1392
  const logCounts = useMemo(() => {
1231
1393
  let searchedLogs = visibleConsoleLogs;
1232
- if (logSearch) {
1233
- const s = logSearch.toLowerCase();
1234
- searchedLogs = visibleConsoleLogs.filter(log => log.message.toLowerCase().includes(s) ||
1235
- (log.caller ?? '').toLowerCase().includes(s));
1394
+ if (logSearch && logSearch.trim().length > 0) {
1395
+ const queryTokens = logSearch.trim().toLowerCase().split(/\s+/).filter(Boolean);
1396
+ searchedLogs = visibleConsoleLogs.filter(log => {
1397
+ const searchTarget = [
1398
+ log.message || '',
1399
+ log.caller || '',
1400
+ log.type || '',
1401
+ log.sourceMethod || '',
1402
+ ].join(' ').toLowerCase();
1403
+ return queryTokens.every(token => searchTarget.includes(token));
1404
+ });
1236
1405
  }
1237
1406
  const total = visibleConsoleLogs.length;
1238
1407
  return {
@@ -1426,6 +1595,7 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1426
1595
  unreadPulseAnim,
1427
1596
  runClearAllWithAnimation,
1428
1597
  // ─── FAB / launcher ─────────────────────────────────────────────────
1598
+ useNativeFab,
1429
1599
  fabPan,
1430
1600
  fabPanResponder,
1431
1601
  fabDraggedRef,
@@ -1543,6 +1713,11 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1543
1713
  setMaxNetworkLogs,
1544
1714
  maxConsoleLogs,
1545
1715
  setMaxConsoleLogs,
1716
+ maxAnalyticsEventsLimit,
1717
+ setMaxAnalyticsEventsLimit,
1718
+ isAutoRamLimitEnabled,
1719
+ setIsAutoRamLimitEnabled,
1720
+ deviceFreeRamMb,
1546
1721
  reduxAutoRefresh,
1547
1722
  setReduxAutoRefreshState,
1548
1723
  reduxExpandDepth,
@@ -1576,4 +1751,5 @@ export { connectReduxStore, inspectorReduxMiddleware, getReduxState, subscribeRe
1576
1751
  export { getEventCategory, registerGAPlugin, } from './helpers/gaAnalyticsRegistry';
1577
1752
  export { usePerformanceTracker, useComponentProfiler, useNavigationProfiler, trackComponentRender, trackNavigationTransition, trackHeavyTask, measureAsync, getHermesMemoryStats, registerComponentProfile, subscribeRenderProfiles, getRenderProfiles, logPerformanceEvent, clearPerformanceEvents, subscribePerformanceEvents, getPerformanceEvents, getInitialRenderProfiles, getInitialPerformanceEvents, generateFixSnippet, } from './customHooks/performanceTracker';
1578
1753
  export { InspectLog, InspectTrackTime, InspectCatch, } from './decorators';
1754
+ export { getNativeDeviceMetrics, enableNativeCrashProtection, subscribeNativeCrashes, showNativeFloatingButton, hideNativeFloatingButton, setNativeFloatingButtonBadge, subscribeNativeFloatingButtonPress, subscribeNativeDeviceShake, startNativeFpsMonitoring, stopNativeFpsMonitoring, getNativeFpsMetrics, getNativeStorageItem, setNativeStorageItem, isNativeModuleAvailable, } from './native/NativeInspector';
1579
1755
  export { ActiveTab, Method, StatusFilter, SortOrder, LocalFilter, ModalAnimationType, SettingsPage, SettingsSubTab, LogFilter, ConsoleLogType, AnalyticsEventSource, GAEventCategory, StackFrameType, DiffResultType, BundleSubTab, PerformanceSubTab, CrashType, CrashExportFormat, CrashDetailSubTab, CrashFilterType, BreadcrumbType, } from './types';
@@ -0,0 +1,125 @@
1
+ export interface NativeDeviceMetrics {
2
+ totalRAM?: number;
3
+ freeRAM?: number;
4
+ usedRAM?: number;
5
+ residentMemory?: number;
6
+ virtualMemory?: number;
7
+ isLowMemory?: boolean;
8
+ nativeHeapAllocated?: number;
9
+ nativeHeapSize?: number;
10
+ nativeHeapFree?: number;
11
+ freeStorage?: number;
12
+ totalStorage?: number;
13
+ batteryPercent?: number;
14
+ isCharging?: boolean;
15
+ deviceModel?: string;
16
+ deviceBrand?: string;
17
+ osVersion?: string;
18
+ apiLevel?: number;
19
+ cpuAbi?: string;
20
+ }
21
+ export interface NativeCrashEvent {
22
+ platform: 'android' | 'ios';
23
+ error: string;
24
+ name?: string;
25
+ stack?: string;
26
+ threadName?: string;
27
+ timestamp: number;
28
+ }
29
+ export declare const isNativeModuleAvailable: () => boolean;
30
+ /**
31
+ * Retrieves low-level native hardware and system metrics (RAM, Heap, Disk, Battery, CPU).
32
+ */
33
+ export declare const getNativeDeviceMetrics: () => Promise<NativeDeviceMetrics | null>;
34
+ /**
35
+ * Enables native signal and uncaught exception protection in the iOS / Android runtime.
36
+ * Automatically ensures the native floating icon is shown and available before UI renders.
37
+ */
38
+ export declare const enableNativeCrashProtection: (options?: {
39
+ showFloatingButton?: boolean;
40
+ }) => Promise<boolean>;
41
+ /**
42
+ * Subscribes to fatal native crash events caught by the iOS / Kotlin exception handlers.
43
+ */
44
+ export declare const subscribeNativeCrashes: (callback: (event: NativeCrashEvent) => void) => (() => void);
45
+ export interface FloatingButtonOptions {
46
+ size?: number;
47
+ x?: number;
48
+ y?: number;
49
+ }
50
+ /**
51
+ * Displays the 100% native main-thread floating overlay button.
52
+ * Dragging & touches run on the native UI thread, completely isolated from JS thread stalls.
53
+ */
54
+ export declare const showNativeFloatingButton: (options?: FloatingButtonOptions) => Promise<boolean>;
55
+ /**
56
+ * Hides the native floating overlay button.
57
+ */
58
+ export declare const hideNativeFloatingButton: () => Promise<boolean>;
59
+ /**
60
+ * Updates the badge/status dot on the native floating overlay button.
61
+ */
62
+ export declare const setNativeFloatingButtonBadge: (hasBadge: boolean) => Promise<boolean>;
63
+ /**
64
+ * Subscribes to tap events on the native floating button.
65
+ */
66
+ export declare const subscribeNativeFloatingButtonPress: (callback: () => void) => (() => void);
67
+ /**
68
+ * Starts hardware-accurate native UI thread FPS tracking (CADisplayLink / Choreographer).
69
+ */
70
+ export declare const startNativeFpsMonitoring: () => Promise<boolean>;
71
+ /**
72
+ * Stops native FPS tracking.
73
+ */
74
+ export declare const stopNativeFpsMonitoring: () => Promise<boolean>;
75
+ export interface NativeFpsMetrics {
76
+ fps: number;
77
+ targetFps: number;
78
+ }
79
+ /**
80
+ * Reads the latest live hardware FPS from CADisplayLink (iOS) or Choreographer (Android).
81
+ */
82
+ export declare const getNativeFpsMetrics: () => Promise<NativeFpsMetrics>;
83
+ /**
84
+ * Subscribes to physical hardware shake events (or Cmd+Ctrl+Z on iOS simulator).
85
+ */
86
+ export declare const subscribeNativeDeviceShake: (callback: () => void) => (() => void);
87
+ /**
88
+ * Reads a persisted value synchronously from native NSUserDefaults / SharedPreferences.
89
+ */
90
+ export declare const getNativeStorageItem: (key: string) => Promise<string | null>;
91
+ /**
92
+ * Persists a key-value pair to native NSUserDefaults / SharedPreferences with 0ms latency.
93
+ */
94
+ export declare const setNativeStorageItem: (key: string, value: string | null) => Promise<boolean>;
95
+ export type HapticStyle = 'light' | 'medium' | 'heavy' | 'success' | 'warning' | 'error';
96
+ /**
97
+ * Triggers hardware native haptic feedback (iOS UIFeedbackGenerator / Android VibrationEffect).
98
+ */
99
+ export declare const triggerNativeHaptic: (style?: HapticStyle) => Promise<boolean>;
100
+ export interface NativeSystemMetrics {
101
+ thermalState: 'nominal' | 'fair' | 'serious' | 'critical';
102
+ residentRamMb: number;
103
+ totalPhysicalRamMb: number;
104
+ fps: number;
105
+ activeCpuCores: number;
106
+ }
107
+ /**
108
+ * Retrieves live system thermal state and RAM telemetry directly from native layer.
109
+ */
110
+ export declare const getNativeSystemMetrics: () => Promise<NativeSystemMetrics | null>;
111
+ /**
112
+ * Pushes a log payload to native background worker queues for instant native page caching.
113
+ */
114
+ export declare const pushNativeLogRecord: (pageKey: "apis" | "logs" | "analytics" | "crash", jsonPayload: string) => Promise<boolean>;
115
+ export interface NativeCachedPageResult<T = any> {
116
+ pageKey: string;
117
+ total: number;
118
+ offset: number;
119
+ items: T[];
120
+ }
121
+ /**
122
+ * Fetches pre-indexed, pre-sliced page data directly from native background memory queues.
123
+ * Delivers instantaneous 0ms page loading without blocking JS render thread.
124
+ */
125
+ export declare const fetchNativeCachedPage: <T = any>(pageKey: "apis" | "logs" | "analytics" | "crash", offset?: number, limit?: number, query?: string) => Promise<NativeCachedPageResult<T> | null>;