react-native-inapp-inspector 1.1.24 → 1.1.25

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 (79) hide show
  1. package/dist/commonjs/components/AnalyticsEventCard.js +94 -88
  2. package/dist/commonjs/components/ConsoleLogCard.js +49 -25
  3. package/dist/commonjs/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
  4. package/dist/commonjs/components/Inspector/AnalyticsFilterModal.js +1002 -0
  5. package/dist/commonjs/components/Inspector/AnalyticsTab.js +637 -10
  6. package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -1
  7. package/dist/commonjs/components/Inspector/BundleTab.js +826 -406
  8. package/dist/commonjs/components/Inspector/InspectorContext.js +6 -1
  9. package/dist/commonjs/components/Inspector/InspectorHeader.js +13 -5
  10. package/dist/commonjs/components/Inspector/LogDetail.js +223 -111
  11. package/dist/commonjs/components/Inspector/MainScreen.js +1 -1
  12. package/dist/commonjs/components/Inspector/NetworkDetail.js +131 -81
  13. package/dist/commonjs/components/Inspector/PerformanceTab.js +20 -10
  14. package/dist/commonjs/components/NetworkIcons.d.ts +14 -0
  15. package/dist/commonjs/components/NetworkIcons.js +93 -1
  16. package/dist/commonjs/components/SegmentedTabs.js +31 -22
  17. package/dist/commonjs/components/TreeNode.js +13 -12
  18. package/dist/commonjs/constants/version.d.ts +1 -1
  19. package/dist/commonjs/constants/version.js +1 -1
  20. package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +5 -1
  21. package/dist/commonjs/customHooks/bundleAnalyzer.js +497 -126
  22. package/dist/commonjs/customHooks/consoleLogger.js +83 -45
  23. package/dist/commonjs/customHooks/networkLogger.js +10 -1
  24. package/dist/commonjs/customHooks/performanceTracker.d.ts +8 -0
  25. package/dist/commonjs/customHooks/performanceTracker.js +44 -3
  26. package/dist/commonjs/decorators/index.d.ts +49 -0
  27. package/dist/commonjs/decorators/index.js +142 -0
  28. package/dist/commonjs/helpers/gaAnalyticsRegistry.d.ts +18 -0
  29. package/dist/commonjs/helpers/gaAnalyticsRegistry.js +174 -0
  30. package/dist/commonjs/helpers/index.d.ts +7 -4
  31. package/dist/commonjs/helpers/index.js +102 -64
  32. package/dist/commonjs/i18n/locales/en.json +51 -4
  33. package/dist/commonjs/index.d.ts +4 -0
  34. package/dist/commonjs/index.js +216 -8
  35. package/dist/commonjs/styles/index.d.ts +4 -1
  36. package/dist/commonjs/styles/index.js +22 -19
  37. package/dist/commonjs/types/enums.d.ts +115 -9
  38. package/dist/commonjs/types/enums.js +103 -2
  39. package/dist/commonjs/types/interfaces.d.ts +19 -0
  40. package/dist/esm/components/AnalyticsEventCard.js +95 -89
  41. package/dist/esm/components/ConsoleLogCard.js +49 -25
  42. package/dist/esm/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
  43. package/dist/esm/components/Inspector/AnalyticsFilterModal.js +964 -0
  44. package/dist/esm/components/Inspector/AnalyticsTab.js +639 -12
  45. package/dist/esm/components/Inspector/BundleTab.d.ts +1 -1
  46. package/dist/esm/components/Inspector/BundleTab.js +827 -407
  47. package/dist/esm/components/Inspector/InspectorContext.js +1 -0
  48. package/dist/esm/components/Inspector/InspectorHeader.js +14 -6
  49. package/dist/esm/components/Inspector/LogDetail.js +223 -111
  50. package/dist/esm/components/Inspector/MainScreen.js +2 -2
  51. package/dist/esm/components/Inspector/NetworkDetail.js +132 -49
  52. package/dist/esm/components/Inspector/PerformanceTab.js +20 -10
  53. package/dist/esm/components/NetworkIcons.d.ts +14 -0
  54. package/dist/esm/components/NetworkIcons.js +78 -0
  55. package/dist/esm/components/SegmentedTabs.js +31 -22
  56. package/dist/esm/components/TreeNode.js +13 -12
  57. package/dist/esm/constants/version.d.ts +1 -1
  58. package/dist/esm/constants/version.js +1 -1
  59. package/dist/esm/customHooks/bundleAnalyzer.d.ts +5 -1
  60. package/dist/esm/customHooks/bundleAnalyzer.js +493 -125
  61. package/dist/esm/customHooks/consoleLogger.js +83 -45
  62. package/dist/esm/customHooks/networkLogger.js +10 -1
  63. package/dist/esm/customHooks/performanceTracker.d.ts +8 -0
  64. package/dist/esm/customHooks/performanceTracker.js +38 -1
  65. package/dist/esm/decorators/index.d.ts +49 -0
  66. package/dist/esm/decorators/index.js +137 -0
  67. package/dist/esm/helpers/gaAnalyticsRegistry.d.ts +18 -0
  68. package/dist/esm/helpers/gaAnalyticsRegistry.js +169 -0
  69. package/dist/esm/helpers/index.d.ts +7 -4
  70. package/dist/esm/helpers/index.js +100 -63
  71. package/dist/esm/i18n/locales/en.json +51 -4
  72. package/dist/esm/index.d.ts +4 -0
  73. package/dist/esm/index.js +190 -8
  74. package/dist/esm/styles/index.d.ts +4 -1
  75. package/dist/esm/styles/index.js +22 -19
  76. package/dist/esm/types/enums.d.ts +115 -9
  77. package/dist/esm/types/enums.js +102 -3
  78. package/dist/esm/types/interfaces.d.ts +19 -0
  79. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { Clipboard, Platform, ToastAndroid, Alert, NativeModules, Linking } from 'react-native';
1
+ import { Clipboard, Platform, ToastAndroid, Alert, NativeModules, Linking, TurboModuleRegistry, } from 'react-native';
2
2
  // Stylesheet
3
3
  import { AppColors } from '../styles/AppColors';
4
4
  // Constants
@@ -52,58 +52,83 @@ export const getSize = (data) => {
52
52
  return '—';
53
53
  }
54
54
  };
55
- // Try loading community clipboard or native clipboard modules with graceful fallbacks
55
+ // Safely detect and load clipboard natively without static require() calls that Metro fails on
56
56
  const getClipboardModule = () => {
57
+ // 1. Check TurboModuleRegistry (TurboModules / New Architecture)
57
58
  try {
58
- // @ts-ignore
59
- const community = require('@react-native-clipboard/clipboard');
60
- if (community?.default?.setString)
61
- return community.default;
62
- if (community?.setString)
63
- return community;
59
+ const tmReg = TurboModuleRegistry || globalThis.__turboModuleProxy;
60
+ if (tmReg?.get) {
61
+ const tm = tmReg.get('RNCClipboard') ||
62
+ tmReg.get('NativeClipboard') ||
63
+ tmReg.get('ExpoClipboard');
64
+ if (tm &&
65
+ (typeof tm.setString === 'function' ||
66
+ typeof tm.setStringAsync === 'function')) {
67
+ return tm;
68
+ }
69
+ }
64
70
  }
65
71
  catch { }
72
+ // 2. Check legacy NativeModules table (Bridge / Old Architecture)
66
73
  try {
67
- // @ts-ignore
68
- const oldCommunity = require('@react-native-community/clipboard');
69
- if (oldCommunity?.default?.setString)
70
- return oldCommunity.default;
71
- if (oldCommunity?.setString)
72
- return oldCommunity;
74
+ if (NativeModules?.RNCClipboard &&
75
+ typeof NativeModules.RNCClipboard.setString === 'function') {
76
+ return NativeModules.RNCClipboard;
77
+ }
78
+ if (NativeModules?.ExpoClipboard &&
79
+ typeof NativeModules.ExpoClipboard.setStringAsync === 'function') {
80
+ return NativeModules.ExpoClipboard;
81
+ }
82
+ if (NativeModules?.Clipboard &&
83
+ typeof NativeModules.Clipboard.setString === 'function') {
84
+ return NativeModules.Clipboard;
85
+ }
73
86
  }
74
87
  catch { }
88
+ // 3. Check legacy React Native core Clipboard
75
89
  try {
76
- if (NativeModules?.RNCClipboard?.setString)
77
- return NativeModules.RNCClipboard;
90
+ if (Clipboard && typeof Clipboard.setString === 'function') {
91
+ return Clipboard;
92
+ }
78
93
  }
79
94
  catch { }
80
- return Clipboard;
95
+ return null;
81
96
  };
82
97
  export const copyToClipboard = (value, label) => {
83
98
  const resolved = typeof value === 'function' ? value() : value;
84
99
  const text = typeof resolved === 'string' ? resolved : JSON.stringify(resolved, null, 2);
85
100
  const textToCopy = text ?? '';
101
+ let copied = false;
86
102
  try {
87
103
  const cb = getClipboardModule();
88
- if (cb && typeof cb.setString === 'function') {
89
- cb.setString(textToCopy);
90
- }
91
- else {
92
- Clipboard.setString(textToCopy);
104
+ if (cb) {
105
+ if (typeof cb.setStringAsync === 'function') {
106
+ cb.setStringAsync(textToCopy);
107
+ copied = true;
108
+ }
109
+ else if (typeof cb.setString === 'function') {
110
+ cb.setString(textToCopy);
111
+ copied = true;
112
+ }
93
113
  }
94
114
  }
95
- catch {
115
+ catch { }
116
+ if (!copied) {
96
117
  try {
97
- Clipboard.setString(textToCopy);
118
+ if (typeof navigator !== 'undefined' && navigator?.clipboard?.writeText) {
119
+ navigator.clipboard.writeText(textToCopy);
120
+ copied = true;
121
+ }
98
122
  }
99
123
  catch { }
100
124
  }
101
- if (Platform.OS === 'android') {
102
- ToastAndroid.show(`${label} copied`, ToastAndroid.SHORT);
103
- }
104
- else {
105
- Alert.alert('Copied', `${label} copied to clipboard`, [{ text: 'OK' }]);
125
+ // Visual toast feedback on Android if ToastAndroid exists
126
+ try {
127
+ if (Platform.OS === 'android' && ToastAndroid?.show) {
128
+ ToastAndroid.show(`${label} copied`, ToastAndroid.SHORT);
129
+ }
106
130
  }
131
+ catch { }
107
132
  };
108
133
  export const getPath = (url) => {
109
134
  try {
@@ -445,6 +470,10 @@ export const parseStackLine = (rawLine, isOrigin = false) => {
445
470
  functionName = parenMatch[1].trim() || '<anonymous>';
446
471
  locationPart = parenMatch[2].trim();
447
472
  }
473
+ // Clean Babel/Hermes artifacts in function names like ?anon_0_, _callee$, etc.
474
+ if (functionName.startsWith('?anon_') || functionName === '?') {
475
+ functionName = 'anonymous';
476
+ }
448
477
  // Remove "address at " prefix if present in Hermes
449
478
  locationPart = locationPart.replace(/^address at /, '');
450
479
  // Extract file, line, and col from locationPart (e.g. "path/to/file.tsx:42:15" or "http://...:42:15")
@@ -472,20 +501,56 @@ export const parseStackLine = (rawLine, isOrigin = false) => {
472
501
  const fileExt = ext === 'tsx' || ext === 'jsx' || ext === 'ts' || ext === 'js'
473
502
  ? ext
474
503
  : 'other';
475
- const isUserCode = !cleanPath.includes('node_modules') &&
476
- !cleanPath.includes('react-native/') &&
477
- !cleanPath.includes('react-native-inapp-inspector') &&
478
- (fileExt === 'tsx' || fileExt === 'jsx' || fileExt === 'ts' || fileExt === 'js');
504
+ const isNative = locationPart === 'native' || cleanPath === 'native';
505
+ const isInternalBytecode = cleanPath.includes('InternalBytecode') ||
506
+ cleanPath.includes('metro-runtime') ||
507
+ cleanPath.includes('regenerator-runtime') ||
508
+ functionName === 'tryCallOne' ||
509
+ functionName === 'asyncGeneratorStep' ||
510
+ functionName === '_next' ||
511
+ (functionName === 'next' && isNative);
512
+ const isDependency = cleanPath.includes('node_modules') ||
513
+ cleanPath.includes('react-native/Libraries') ||
514
+ (cleanPath.includes('react-native-inapp-inspector') && !cleanPath.includes('/example/'));
515
+ const isUserCode = !isNative &&
516
+ !isInternalBytecode &&
517
+ !isDependency &&
518
+ (fileExt === 'tsx' || fileExt === 'jsx' || fileExt === 'ts' || fileExt === 'js' || !fileName.includes('.bundle'));
519
+ const frameType = isUserCode
520
+ ? 'app'
521
+ : isDependency
522
+ ? 'dependency'
523
+ : isNative
524
+ ? 'native'
525
+ : 'runtime';
526
+ const isRuntimeNoise = isInternalBytecode || isNative || functionName === 'asyncGeneratorStep' || functionName === '_next';
527
+ // Format clean relative project path
528
+ let relativePath = cleanPath;
529
+ if (relativePath.includes('/example/')) {
530
+ relativePath = relativePath.substring(relativePath.indexOf('/example/') + 9);
531
+ }
532
+ else if (relativePath.includes('/src/')) {
533
+ relativePath = relativePath.substring(relativePath.indexOf('/src/') + 1);
534
+ }
535
+ else if (relativePath.includes('/node_modules/')) {
536
+ relativePath = relativePath.substring(relativePath.indexOf('/node_modules/') + 14);
537
+ }
538
+ const copyableLocation = lineNumber
539
+ ? `${fileName}:${lineNumber}${columnNumber ? `:${columnNumber}` : ''}`
540
+ : fileName;
479
541
  return {
480
542
  raw: rawLine,
481
543
  functionName,
482
544
  fileName,
483
- fullPath: cleanPath,
545
+ fullPath: relativePath,
484
546
  fileExt,
547
+ frameType,
485
548
  isUserCode,
549
+ isRuntimeNoise,
486
550
  lineNumber,
487
551
  columnNumber,
488
552
  isOrigin,
553
+ copyableLocation,
489
554
  };
490
555
  };
491
556
  // ─── Analytics Helpers ────────────────────────────────────────────────────────
@@ -507,35 +572,7 @@ export const getEventColor = (name) => {
507
572
  }
508
573
  return ANALYTICS_EVENT_PALETTE[Math.abs(hash) % ANALYTICS_EVENT_PALETTE.length];
509
574
  };
510
- export const getEventCategory = (name) => {
511
- if (!name)
512
- return 'custom';
513
- const lowercaseName = name.toLowerCase();
514
- if (lowercaseName === 'screen_view' || lowercaseName === 'page_view') {
515
- return 'page_view';
516
- }
517
- // Ecommerce events
518
- const ecommerceEvents = [
519
- 'purchase', 'add_to_cart', 'begin_checkout', 'view_item',
520
- 'select_item', 'remove_from_cart', 'view_cart',
521
- 'add_shipping_info', 'add_payment_info', 'refund',
522
- 'view_item_list', 'select_promotion', 'view_promotion'
523
- ];
524
- if (ecommerceEvents.includes(lowercaseName)) {
525
- return 'ecommerce';
526
- }
527
- // Firebase System Auto-events
528
- const systemEvents = [
529
- 'first_open', 'session_start', 'user_engagement',
530
- 'app_clear_data', 'app_exception', 'app_update', 'os_update',
531
- 'notification_receive', 'notification_open', 'notification_dismiss',
532
- 'screen_active', 'screen_inactive'
533
- ];
534
- if (systemEvents.includes(lowercaseName) || lowercaseName.startsWith('firebase_') || lowercaseName.startsWith('_')) {
535
- return 'system';
536
- }
537
- return 'custom';
538
- };
575
+ export { getEventCategory, registerGAPlugin, } from './gaAnalyticsRegistry';
539
576
  export const getCategoryColors = (category) => {
540
577
  switch (category) {
541
578
  case 'page_view':
@@ -211,7 +211,29 @@
211
211
  "filterWarn": "Warn",
212
212
  "filterError": "Error",
213
213
  "filterUserLog": "User Log",
214
- "filterAnalytics": "Analytics"
214
+ "filterAnalytics": "Analytics",
215
+ "tabOutput": "Output",
216
+ "tabArgs": "Args ({{count}})",
217
+ "tabStack": "Stack Trace",
218
+ "tabMetadata": "Metadata",
219
+ "callStack": "Call Stack ({{count}} frames)",
220
+ "errorStack": "Error Exception Stack ({{count}} frames)",
221
+ "callOriginStack": "Call Origin Stack",
222
+ "errorThrownStack": "Error Thrown Stack",
223
+ "appCodeScope": "🎯 App Code ({{count}})",
224
+ "allFramesScope": "📜 All Frames ({{count}})",
225
+ "cardsView": "Cards",
226
+ "rawTraceView": "Raw",
227
+ "noStackAvailable": "No call stack frames found matching the active filter.",
228
+ "originBadge": "#1 ORIGIN",
229
+ "appCodeBadge": "App Code",
230
+ "dependencyBadge": "Dependency",
231
+ "nativeBadge": "Native",
232
+ "hermesVmBadge": "Hermes VM",
233
+ "fullStackTrace": "Full Stack Trace",
234
+ "lineCol": "Line {{line}}, Col {{col}}",
235
+ "lineColShort": "L{{line}}:C{{col}}",
236
+ "searchInLogDetails": "Search in log details..."
215
237
  },
216
238
  "analytics": {
217
239
  "title": "Analytics",
@@ -398,11 +420,36 @@
398
420
  "catTsDesc": "App screen components, hooks, and business logic",
399
421
  "catJsonTitle": "JSON Data & Localizations",
400
422
  "catJsonDesc": "i18n translation dictionaries and static configs",
401
- "catRowSub": "{{count}} items • {{desc}}",
402
- "totalBundleAssets": "TOTAL BUNDLE ASSETS",
423
+ "totalBundleAssets": "TOTAL HOST PROJECT FILES",
403
424
  "totalBundleValue": "{{kb}} KB (~{{mb}} MB)",
404
- "totalFormula": "Sum: {{images}} KB + {{js}} KB + {{fonts}} KB + {{ts}} KB + {{json}} KB = {{total}} KB ({{count}} items • 100%)",
425
+ "totalFormula": "Sum of host source files: {{images}} KB + {{js}} KB + {{fonts}} KB + {{ts}} KB + {{json}} KB = {{total}} KB ({{count}} items)",
426
+ "prodIosApp": "iOS App (.ipa)",
427
+ "prodAndroidAab": "Android AAB (.aab)",
428
+ "prodAndroidApk": "Universal APK (.apk)",
429
+ "prodHeroTitleIos": "iOS Production Binary Footprint",
430
+ "prodHeroTitleAab": "Google Play App Bundle (.aab)",
431
+ "prodHeroTitleApk": "Universal Standalone APK (.apk)",
432
+ "prodHeroSubIos": "App Store install & over-the-air cellular download estimates",
433
+ "prodHeroSubAab": "Optimized per-device dynamic delivery split APK architecture",
434
+ "prodHeroSubApk": "Multi-ABI universal install archive for sideloading & direct distribution",
435
+ "prodInstallSize": "INSTALL SIZE",
436
+ "prodInstallHint": "On-device uncompressed footprint",
437
+ "prodDownloadSize": "DOWNLOAD SIZE",
438
+ "prodDownloadHint": "Store network transfer payload",
439
+ "prodCompression": "COMPRESSION",
440
+ "prodCompressionHint": "Bytecode & asset ratio",
441
+ "prodFormatArch": "FORMAT ARCHITECTURE",
442
+ "prodFormatHintIos": "Apple ARM64 runtime",
443
+ "prodFormatHintAab": "Dynamic Google Play delivery",
444
+ "prodFormatHintApk": "Sideload universal package",
445
+ "prodArchTitle": "Binary Component Architecture",
446
+ "prodArchSub": "Compiled native libraries, runtime bytecodes, assets, and signature blocks",
447
+ "prodTotalIos": "TOTAL IOS APP BINARY",
448
+ "prodTotalAab": "TOTAL ANDROID AAB BUNDLE",
449
+ "prodTotalApk": "TOTAL UNIVERSAL APK",
450
+ "prodTotalFormula": "From {{devKb}} KB (~{{devMb}} MB • {{count}} total assets) dev source bundle → {{installMb}} MB optimized native binary ({{pct}}% bytecode/asset compression).",
405
451
  "tabOverview": "Overview",
452
+ "tabProduction": "Production ({{count}})",
406
453
  "tabFiles": "Files ({{count}})",
407
454
  "tabPackages": "Packages ({{count}})",
408
455
  "tabMedia": "Media ({{count}})",
@@ -7,3 +7,7 @@ export { setupConsoleLogger, clearConsoleLogs, subscribeConsoleLogs, } from './c
7
7
  export { setupAnalyticsLogger, logAnalyticsEvent, subscribeAnalyticsEvents, clearAnalyticsEvents, getCurrentUserProperties, getCurrentUserId, getDefaultEventParameters, getCollectionEnabled, } from './customHooks/analyticsLogger';
8
8
  export { default as ErrorBoundary } from './components/ErrorBoundary';
9
9
  export { connectReduxStore, inspectorReduxMiddleware, getReduxState, subscribeReduxState, getActionHistory, clearActionHistory, getLastActionForReducer, } from './customHooks/reduxLogger';
10
+ export { getEventCategory, registerGAPlugin, type GAPlugin, } from './helpers/gaAnalyticsRegistry';
11
+ export { usePerformanceTracker, logPerformanceEvent, clearPerformanceEvents, subscribePerformanceEvents, getPerformanceEvents, } from './customHooks/performanceTracker';
12
+ export { InspectLog, InspectTrackTime, InspectCatch, type InspectLogOptions, } from './decorators';
13
+ export { ActiveTab, Method, StatusFilter, SortOrder, LocalFilter, ModalAnimationType, SettingsPage, SettingsSubTab, LogFilter, ConsoleLogType, AnalyticsEventSource, GAEventCategory, StackFrameType, DiffResultType, BundleSubTab, PerformanceSubTab, } from './types';
package/dist/esm/index.js CHANGED
@@ -8,7 +8,7 @@ import ErrorBoundary from './components/ErrorBoundary';
8
8
  import MainScreen from './components/Inspector/MainScreen';
9
9
  import { InspectorContext, animateNextLayout, } from './components/Inspector/InspectorContext';
10
10
  // Helpers
11
- import { formatDisplayUrl, getNavigationInfo, deduplicateLogs, getDomainColor, } from './helpers';
11
+ import { formatDisplayUrl, getNavigationInfo, deduplicateLogs, getDomainColor, getEventCategory, } from './helpers';
12
12
  // #5 — settings persistence
13
13
  import { loadSettings, saveSettings, setCustomStorage, clearPersistedSettings, } from './helpers/settingsStore';
14
14
  // Network
@@ -332,7 +332,67 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
332
332
  }, []);
333
333
  const [selectedEvent, setSelectedEvent] = useState(null);
334
334
  const [analyticsSearch, setAnalyticsSearch] = useState('');
335
- const [hideScreenView] = useState(true);
335
+ const [analyticsFilters, setAnalyticsFilters] = useState({
336
+ categories: new Set(['all']),
337
+ screens: new Set(),
338
+ sources: new Set(['all']),
339
+ userTypes: new Set(['all']),
340
+ timeWindow: 'all',
341
+ payloadComplexity: 'all',
342
+ hasRevenue: false,
343
+ hasItems: false,
344
+ hasUserProps: false,
345
+ hasParams: false,
346
+ onlyDuplicates: false,
347
+ onlyConversions: false,
348
+ sortBy: 'time_desc',
349
+ });
350
+ const isAnalyticsFilterApplied = useMemo(() => {
351
+ if (analyticsFilters.categories.size > 0 && !analyticsFilters.categories.has('all'))
352
+ return true;
353
+ if (analyticsFilters.screens.size > 0)
354
+ return true;
355
+ if (analyticsFilters.sources.size > 0 && !analyticsFilters.sources.has('all'))
356
+ return true;
357
+ if (analyticsFilters.userTypes.size > 0 && !analyticsFilters.userTypes.has('all'))
358
+ return true;
359
+ if (analyticsFilters.timeWindow !== 'all')
360
+ return true;
361
+ if (analyticsFilters.payloadComplexity !== 'all')
362
+ return true;
363
+ if (analyticsFilters.hasRevenue)
364
+ return true;
365
+ if (analyticsFilters.hasItems)
366
+ return true;
367
+ if (analyticsFilters.hasUserProps)
368
+ return true;
369
+ if (analyticsFilters.hasParams)
370
+ return true;
371
+ if (analyticsFilters.onlyDuplicates)
372
+ return true;
373
+ if (analyticsFilters.onlyConversions)
374
+ return true;
375
+ if (analyticsFilters.sortBy !== 'time_desc')
376
+ return true;
377
+ return false;
378
+ }, [analyticsFilters]);
379
+ const resetAnalyticsFilters = useCallback(() => {
380
+ setAnalyticsFilters({
381
+ categories: new Set(['all']),
382
+ screens: new Set(),
383
+ sources: new Set(['all']),
384
+ userTypes: new Set(['all']),
385
+ timeWindow: 'all',
386
+ payloadComplexity: 'all',
387
+ hasRevenue: false,
388
+ hasItems: false,
389
+ hasUserProps: false,
390
+ hasParams: false,
391
+ onlyDuplicates: false,
392
+ onlyConversions: false,
393
+ sortBy: 'time_desc',
394
+ });
395
+ }, []);
336
396
  const [isAnalyticsLayoutReady, setIsAnalyticsLayoutReady] = useState(false);
337
397
  const [analyticsHeaderExpanded, setAnalyticsHeaderExpanded] = useState(false);
338
398
  const [newEventIds, setNewEventIds] = useState(new Set());
@@ -866,15 +926,110 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
866
926
  const prevResponseData = prevSameRequest ? prevSameRequest.response : null;
867
927
  const filteredAnalyticsEvents = useMemo(() => {
868
928
  let events = analyticsEvents;
869
- if (hideScreenView) {
870
- events = events.filter(e => e.name !== 'screen_view');
871
- }
929
+ // Search query filter
872
930
  if (analyticsSearch) {
873
931
  const s = analyticsSearch.toLowerCase();
874
932
  events = events.filter(e => e.name.toLowerCase().includes(s) ||
875
- JSON.stringify(e.params).toLowerCase().includes(s) ||
933
+ JSON.stringify(e.params || {}).toLowerCase().includes(s) ||
934
+ (e.screenName ?? '').toLowerCase().includes(s) ||
876
935
  (e.pageTitle ?? '').toLowerCase().includes(s));
877
936
  }
937
+ // Category filter
938
+ if (analyticsFilters.categories.size > 0 && !analyticsFilters.categories.has('all')) {
939
+ events = events.filter(e => {
940
+ const cat = getEventCategory(e.name);
941
+ return analyticsFilters.categories.has(cat);
942
+ });
943
+ }
944
+ // Time window filter
945
+ if (analyticsFilters.timeWindow !== 'all') {
946
+ const now = Date.now();
947
+ const cutoff = analyticsFilters.timeWindow === '1m'
948
+ ? now - 60 * 1000
949
+ : analyticsFilters.timeWindow === '5m'
950
+ ? now - 5 * 60 * 1000
951
+ : analyticsFilters.timeWindow === '15m'
952
+ ? now - 15 * 60 * 1000
953
+ : now - 60 * 60 * 1000;
954
+ events = events.filter(e => e.timestamp >= cutoff);
955
+ }
956
+ // Source filter
957
+ if (analyticsFilters.sources.size > 0 && !analyticsFilters.sources.has('all')) {
958
+ events = events.filter(e => analyticsFilters.sources.has(e.source || 'manual'));
959
+ }
960
+ // User identification filter
961
+ if (analyticsFilters.userTypes.size > 0 && !analyticsFilters.userTypes.has('all')) {
962
+ events = events.filter(e => {
963
+ const isIdentified = Boolean(e.userId && e.userId.trim() !== '');
964
+ if (analyticsFilters.userTypes.has('identified') && isIdentified)
965
+ return true;
966
+ if (analyticsFilters.userTypes.has('anonymous') && !isIdentified)
967
+ return true;
968
+ return false;
969
+ });
970
+ }
971
+ // Payload complexity filter
972
+ if (analyticsFilters.payloadComplexity !== 'all') {
973
+ events = events.filter(e => {
974
+ const count = e.params ? Object.keys(e.params).length : 0;
975
+ if (analyticsFilters.payloadComplexity === 'none')
976
+ return count === 0;
977
+ if (analyticsFilters.payloadComplexity === 'simple')
978
+ return count >= 1 && count <= 5;
979
+ if (analyticsFilters.payloadComplexity === 'heavy')
980
+ return count > 5;
981
+ return true;
982
+ });
983
+ }
984
+ // Conversion / Goal events filter
985
+ if (analyticsFilters.onlyConversions) {
986
+ const CONVERSION_PATTERNS = [
987
+ 'purchase',
988
+ 'item_purchase',
989
+ 'ecommerce_purchase',
990
+ 'sign_up',
991
+ 'login',
992
+ 'lead',
993
+ 'generate_lead',
994
+ 'tutorial_complete',
995
+ 'add_payment_info',
996
+ 'begin_checkout',
997
+ 'spend_virtual_currency',
998
+ ];
999
+ events = events.filter(e => CONVERSION_PATTERNS.some(pat => e.name.toLowerCase().includes(pat)));
1000
+ }
1001
+ // Screen filter
1002
+ if (analyticsFilters.screens.size > 0) {
1003
+ events = events.filter(e => {
1004
+ const scr = e.screenName ||
1005
+ e.params?.firebase_screen ||
1006
+ e.params?.screen_name ||
1007
+ e.params?.firebase_screen_class ||
1008
+ e.screenClass ||
1009
+ '';
1010
+ return analyticsFilters.screens.has(scr);
1011
+ });
1012
+ }
1013
+ // Has Revenue filter
1014
+ if (analyticsFilters.hasRevenue) {
1015
+ events = events.filter(e => {
1016
+ const val = e.params?.value ?? e.params?.price;
1017
+ return val !== undefined && val !== null && Number(val) > 0;
1018
+ });
1019
+ }
1020
+ // Has Items filter
1021
+ if (analyticsFilters.hasItems) {
1022
+ events = events.filter(e => Array.isArray(e.params?.items) && e.params.items.length > 0);
1023
+ }
1024
+ // Has User Props filter
1025
+ if (analyticsFilters.hasUserProps) {
1026
+ events = events.filter(e => e.userProperties && Object.keys(e.userProperties).length > 0);
1027
+ }
1028
+ // Has Params filter
1029
+ if (analyticsFilters.hasParams) {
1030
+ events = events.filter(e => e.params && Object.keys(e.params).length > 0);
1031
+ }
1032
+ // Deduplication
878
1033
  const deduplicatedEvents = [];
879
1034
  for (const e of events) {
880
1035
  if (deduplicatedEvents.length === 0) {
@@ -894,8 +1049,27 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
894
1049
  deduplicatedEvents.push({ ...e, count: 1 });
895
1050
  }
896
1051
  }
897
- return deduplicatedEvents;
898
- }, [analyticsEvents, analyticsSearch, hideScreenView]);
1052
+ let result = deduplicatedEvents;
1053
+ // Only duplicates filter
1054
+ if (analyticsFilters.onlyDuplicates) {
1055
+ result = result.filter(e => (e.count || 1) > 1);
1056
+ }
1057
+ // Sorting
1058
+ if (analyticsFilters.sortBy === 'time_asc') {
1059
+ result = [...result].sort((a, b) => a.timestamp - b.timestamp);
1060
+ }
1061
+ else if (analyticsFilters.sortBy === 'revenue_desc') {
1062
+ result = [...result].sort((a, b) => {
1063
+ const valA = Number(a.params?.value ?? a.params?.price ?? 0);
1064
+ const valB = Number(b.params?.value ?? b.params?.price ?? 0);
1065
+ return valB - valA;
1066
+ });
1067
+ }
1068
+ else if (analyticsFilters.sortBy === 'count_desc') {
1069
+ result = [...result].sort((a, b) => (b.count || 1) - (a.count || 1));
1070
+ }
1071
+ return result;
1072
+ }, [analyticsEvents, analyticsSearch, analyticsFilters]);
899
1073
  const filteredConsoleLogs = useMemo(() => {
900
1074
  let result = visibleConsoleLogs;
901
1075
  // Filters check
@@ -1194,6 +1368,10 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1194
1368
  filteredAnalyticsEvents,
1195
1369
  analyticsSearch,
1196
1370
  setAnalyticsSearch,
1371
+ analyticsFilters,
1372
+ setAnalyticsFilters,
1373
+ isAnalyticsFilterApplied,
1374
+ resetAnalyticsFilters,
1197
1375
  newEventIds,
1198
1376
  isAnalyticsLayoutReady,
1199
1377
  setIsAnalyticsLayoutReady,
@@ -1258,3 +1436,7 @@ export { setupConsoleLogger, clearConsoleLogs, subscribeConsoleLogs, } from './c
1258
1436
  export { setupAnalyticsLogger, logAnalyticsEvent, subscribeAnalyticsEvents, clearAnalyticsEvents, getCurrentUserProperties, getCurrentUserId, getDefaultEventParameters, getCollectionEnabled, } from './customHooks/analyticsLogger';
1259
1437
  export { default as ErrorBoundary } from './components/ErrorBoundary';
1260
1438
  export { connectReduxStore, inspectorReduxMiddleware, getReduxState, subscribeReduxState, getActionHistory, clearActionHistory, getLastActionForReducer, } from './customHooks/reduxLogger';
1439
+ export { getEventCategory, registerGAPlugin, } from './helpers/gaAnalyticsRegistry';
1440
+ export { usePerformanceTracker, logPerformanceEvent, clearPerformanceEvents, subscribePerformanceEvents, getPerformanceEvents, } from './customHooks/performanceTracker';
1441
+ export { InspectLog, InspectTrackTime, InspectCatch, } from './decorators';
1442
+ export { ActiveTab, Method, StatusFilter, SortOrder, LocalFilter, ModalAnimationType, SettingsPage, SettingsSubTab, LogFilter, ConsoleLogType, AnalyticsEventSource, GAEventCategory, StackFrameType, DiffResultType, BundleSubTab, PerformanceSubTab, } from './types';
@@ -641,6 +641,10 @@ export declare const getRawStyles: (colors: typeof AppColors) => {
641
641
  fontSize: number;
642
642
  color: string;
643
643
  letterSpacing: number;
644
+ backgroundColor: string;
645
+ paddingHorizontal: number;
646
+ paddingVertical: number;
647
+ borderRadius: number;
644
648
  };
645
649
  slugText: {
646
650
  fontFamily: string;
@@ -1457,7 +1461,6 @@ export declare const getRawStyles: (colors: typeof AppColors) => {
1457
1461
  flexDirection: string;
1458
1462
  justifyContent: string;
1459
1463
  alignItems: string;
1460
- marginBottom: number;
1461
1464
  };
1462
1465
  analyticsHeaderTitle: {
1463
1466
  fontFamily: string;
@@ -574,10 +574,10 @@ export const getRawStyles = (colors) => ({
574
574
  flexDirection: 'row',
575
575
  alignItems: 'center',
576
576
  justifyContent: 'space-between',
577
- backgroundColor: colors.grayBackground,
578
- borderRadius: 7,
577
+ backgroundColor: `${colors.brandPurple}08`,
578
+ borderRadius: 8,
579
579
  borderWidth: 1,
580
- borderColor: colors.grayBorderSecondary,
580
+ borderColor: `${colors.brandPurple}18`,
581
581
  paddingHorizontal: 8,
582
582
  paddingVertical: 5,
583
583
  marginVertical: 5,
@@ -592,12 +592,16 @@ export const getRawStyles = (colors) => ({
592
592
  slugTag: {
593
593
  fontFamily: AppFonts.interBold,
594
594
  fontSize: 8.5,
595
- color: colors.grayTextWeak,
595
+ color: colors.brandPurple,
596
596
  letterSpacing: 0.5,
597
+ backgroundColor: `${colors.brandPurple}15`,
598
+ paddingHorizontal: 4,
599
+ paddingVertical: 1,
600
+ borderRadius: 4,
597
601
  },
598
602
  slugText: {
599
603
  fontFamily: AppFonts.interBold,
600
- fontSize: 12.5,
604
+ fontSize: 12,
601
605
  color: colors.primaryBlack,
602
606
  flexShrink: 1,
603
607
  },
@@ -1333,11 +1337,11 @@ export const getRawStyles = (colors) => ({
1333
1337
  backgroundColor: colors.primaryLight,
1334
1338
  borderRadius: 12,
1335
1339
  borderWidth: 1,
1336
- borderColor: colors.grayBorderSecondary,
1337
- padding: 14,
1338
- marginHorizontal: 12,
1340
+ borderColor: `${colors.brandPurple}20`,
1341
+ padding: 12,
1342
+ marginHorizontal: 10,
1339
1343
  marginTop: 8,
1340
- marginBottom: 8,
1344
+ marginBottom: 6,
1341
1345
  shadowColor: colors.black,
1342
1346
  shadowOpacity: 0.03,
1343
1347
  shadowRadius: 4,
@@ -1348,11 +1352,10 @@ export const getRawStyles = (colors) => ({
1348
1352
  flexDirection: 'row',
1349
1353
  justifyContent: 'space-between',
1350
1354
  alignItems: 'center',
1351
- marginBottom: 10,
1352
1355
  },
1353
1356
  analyticsHeaderTitle: {
1354
1357
  fontFamily: AppFonts.interBold,
1355
- fontSize: 14,
1358
+ fontSize: 13.5,
1356
1359
  color: colors.primaryBlack,
1357
1360
  },
1358
1361
  statusDot: {
@@ -1363,21 +1366,21 @@ export const getRawStyles = (colors) => ({
1363
1366
  analyticsHeaderSubtitle: {
1364
1367
  fontFamily: AppFonts.interMedium,
1365
1368
  fontSize: 11,
1366
- color: colors.grayText,
1369
+ color: colors.grayTextWeak,
1367
1370
  marginTop: 2,
1368
1371
  },
1369
1372
  analyticsHeaderToggle: {
1370
- paddingVertical: 4,
1371
- paddingHorizontal: 8,
1372
- borderRadius: 4,
1373
- backgroundColor: colors.grayBackground,
1373
+ paddingVertical: 4.5,
1374
+ paddingHorizontal: 9,
1375
+ borderRadius: 7,
1376
+ backgroundColor: `${colors.brandPurple}12`,
1374
1377
  borderWidth: 1,
1375
- borderColor: colors.grayBorderSecondary,
1378
+ borderColor: `${colors.brandPurple}25`,
1376
1379
  },
1377
1380
  analyticsHeaderToggleText: {
1378
1381
  fontFamily: AppFonts.interBold,
1379
- fontSize: 10,
1380
- color: colors.purple,
1382
+ fontSize: 10.5,
1383
+ color: colors.brandPurple,
1381
1384
  },
1382
1385
  analyticsStatsRow: {
1383
1386
  flexDirection: 'row',