react-native-inapp-inspector 1.1.28 → 1.1.31

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 (77) hide show
  1. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +29 -80
  2. package/dist/commonjs/components/AnalyticsDetail.js +668 -108
  3. package/dist/commonjs/components/AppHeaderLogo.js +15 -15
  4. package/dist/commonjs/components/ConsoleLogCard.js +112 -66
  5. package/dist/commonjs/components/CopyButton.js +15 -1
  6. package/dist/commonjs/components/DomainHeader.js +64 -32
  7. package/dist/commonjs/components/HighlightText.js +7 -1
  8. package/dist/commonjs/components/Inspector/AnalyticsTab.js +332 -221
  9. package/dist/commonjs/components/Inspector/ConsoleTab.js +162 -56
  10. package/dist/commonjs/components/Inspector/InspectorHeader.js +70 -119
  11. package/dist/commonjs/components/Inspector/LogDetail.js +53 -17
  12. package/dist/commonjs/components/Inspector/MainScreen.js +4 -0
  13. package/dist/commonjs/components/Inspector/NetworkDetail.js +24 -24
  14. package/dist/commonjs/components/Inspector/NetworkTab.js +4 -1
  15. package/dist/commonjs/components/Inspector/ReduxDetail.js +237 -42
  16. package/dist/commonjs/components/Inspector/ReduxTab.js +456 -35
  17. package/dist/commonjs/components/JsonViewer.js +26 -4
  18. package/dist/commonjs/components/LogCard.js +103 -11
  19. package/dist/commonjs/components/NetworkIcons.js +19 -10
  20. package/dist/commonjs/components/Toast.d.ts +3 -0
  21. package/dist/commonjs/components/Toast.js +152 -0
  22. package/dist/commonjs/components/TouchableScale.d.ts +1 -0
  23. package/dist/commonjs/components/TouchableScale.js +3 -3
  24. package/dist/commonjs/components/TreeNode.js +12 -2
  25. package/dist/commonjs/constants/index.js +6 -6
  26. package/dist/commonjs/constants/version.d.ts +1 -1
  27. package/dist/commonjs/constants/version.js +1 -1
  28. package/dist/commonjs/customHooks/reduxLogger.js +128 -5
  29. package/dist/commonjs/helpers/index.d.ts +4 -0
  30. package/dist/commonjs/helpers/index.js +107 -92
  31. package/dist/commonjs/helpers/toast.d.ts +4 -0
  32. package/dist/commonjs/helpers/toast.js +20 -0
  33. package/dist/commonjs/i18n/locales/en.json +49 -3
  34. package/dist/commonjs/styles/AppColors.d.ts +6 -0
  35. package/dist/commonjs/styles/AppColors.js +6 -0
  36. package/dist/commonjs/styles/index.d.ts +82 -15
  37. package/dist/commonjs/styles/index.js +92 -26
  38. package/dist/commonjs/types/interfaces.d.ts +7 -0
  39. package/dist/esm/components/AnalyticsDetail.js +670 -110
  40. package/dist/esm/components/AppHeaderLogo.js +15 -15
  41. package/dist/esm/components/ConsoleLogCard.js +114 -68
  42. package/dist/esm/components/CopyButton.js +15 -1
  43. package/dist/esm/components/DomainHeader.js +64 -32
  44. package/dist/esm/components/HighlightText.js +7 -1
  45. package/dist/esm/components/Inspector/AnalyticsTab.js +334 -223
  46. package/dist/esm/components/Inspector/ConsoleTab.js +163 -57
  47. package/dist/esm/components/Inspector/InspectorHeader.js +71 -120
  48. package/dist/esm/components/Inspector/LogDetail.js +55 -19
  49. package/dist/esm/components/Inspector/MainScreen.js +4 -0
  50. package/dist/esm/components/Inspector/NetworkDetail.js +25 -25
  51. package/dist/esm/components/Inspector/NetworkTab.js +4 -1
  52. package/dist/esm/components/Inspector/ReduxDetail.js +239 -44
  53. package/dist/esm/components/Inspector/ReduxTab.js +458 -37
  54. package/dist/esm/components/JsonViewer.js +26 -4
  55. package/dist/esm/components/LogCard.js +105 -13
  56. package/dist/esm/components/NetworkIcons.js +19 -10
  57. package/dist/esm/components/Toast.d.ts +3 -0
  58. package/dist/esm/components/Toast.js +117 -0
  59. package/dist/esm/components/TouchableScale.d.ts +1 -0
  60. package/dist/esm/components/TouchableScale.js +3 -3
  61. package/dist/esm/components/TreeNode.js +12 -2
  62. package/dist/esm/constants/index.js +6 -6
  63. package/dist/esm/constants/version.d.ts +1 -1
  64. package/dist/esm/constants/version.js +1 -1
  65. package/dist/esm/customHooks/reduxLogger.js +128 -5
  66. package/dist/esm/helpers/index.d.ts +4 -0
  67. package/dist/esm/helpers/index.js +102 -92
  68. package/dist/esm/helpers/toast.d.ts +4 -0
  69. package/dist/esm/helpers/toast.js +15 -0
  70. package/dist/esm/i18n/locales/en.json +49 -3
  71. package/dist/esm/styles/AppColors.d.ts +6 -0
  72. package/dist/esm/styles/AppColors.js +6 -0
  73. package/dist/esm/styles/index.d.ts +82 -15
  74. package/dist/esm/styles/index.js +92 -26
  75. package/dist/esm/types/interfaces.d.ts +7 -0
  76. package/ios/NetworkInspectorModule.m +0 -10
  77. package/package.json +13 -10
@@ -1,9 +1,9 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
- import { Animated, View, Pressable, Text, StyleSheet } from 'react-native';
2
+ import { Alert, Animated, Linking, View, Pressable, Text, StyleSheet, } from 'react-native';
3
3
  import Svg, { Path } from 'react-native-svg';
4
4
  import { AppColors } from '../styles/AppColors';
5
5
  import { METHOD_COLORS } from '../constants';
6
- import { getStatusColor, getDurationColor, getPath, getBaseUrl, formatDateTime, getSize, } from '../helpers';
6
+ import { getStatusColor, getDurationColor, formatDateTime, getSize, } from '../helpers';
7
7
  import { CalendarIcon, ClockIcon, SizeIcon } from './NetworkIcons';
8
8
  import { AppFonts } from '../styles/AppFonts';
9
9
  import styles from '../styles';
@@ -13,6 +13,27 @@ import { useTranslation } from '../i18n';
13
13
  const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, timelineTotalRange, isNew, isSelected, onToggleSelect, searchStr, }) {
14
14
  const { t } = useTranslation();
15
15
  const methodColor = METHOD_COLORS[item.method] ?? METHOD_COLORS.ALL;
16
+ const handleOpenUrl = (e) => {
17
+ e?.stopPropagation?.();
18
+ Alert.alert(t('common.openInBrowser') || 'Open in Browser', `${t('common.openInBrowserPrompt') || 'Are you sure you want to open this URL in your external browser?'}\n\n${item.url}`, [
19
+ { text: t('common.cancel') || 'Cancel', style: 'cancel' },
20
+ {
21
+ text: t('common.open') || 'Open',
22
+ onPress: () => {
23
+ Linking.canOpenURL(item.url)
24
+ .then(supported => {
25
+ if (supported) {
26
+ Linking.openURL(item.url);
27
+ }
28
+ else {
29
+ Linking.openURL(item.url).catch(() => { });
30
+ }
31
+ })
32
+ .catch(() => { });
33
+ },
34
+ },
35
+ ]);
36
+ };
16
37
  const isFailed = item.status === 0 || (item.status != null && item.status >= 400);
17
38
  const isLoading = item.status == null;
18
39
  const statusColor = getStatusColor(item.status);
@@ -38,10 +59,6 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
38
59
  }).start();
39
60
  }
40
61
  }, [isNew]);
41
- const path = getPath(item.url);
42
- const baseUrl = getBaseUrl(item.url) || item.url;
43
- const slug = path && path !== '/' ? path : item.url;
44
- const showSlug = slug !== baseUrl && slug !== item.url;
45
62
  const triggeredAt = formatDateTime(item.startTime);
46
63
  const isJson = item.url.split('?')[0].toLowerCase().endsWith('.json');
47
64
  return (<TouchableScale onPress={onPress} style={[
@@ -52,7 +69,7 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
52
69
  },
53
70
  ]}>
54
71
  <View style={styles.cardBody}>
55
- {/* Row 1: Header (Checkbox, Serial, Method Badge, Base URL / Host with HTTPS, Status Pill) */}
72
+ {/* Row 1: Header (Checkbox, Serial, Method Badge, Client Tag, Status Pill) */}
56
73
  <View style={styles.cardHeaderRow}>
57
74
  <View style={styles.cardHeaderLeft}>
58
75
  <Pressable onPress={() => onToggleSelect(item.id)} hitSlop={12} style={[
@@ -112,7 +129,82 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
112
129
  </Text>
113
130
  </View>)}
114
131
 
115
- <HighlightText text={baseUrl || item.url} search={searchStr} style={styles.cardHostText} highlightStyle={styles.highlight} numberOfLines={1} ellipsizeMode="tail"/>
132
+ {/* Protocol / HTTPS Badge */}
133
+ <View style={[
134
+ styles.chip,
135
+ {
136
+ backgroundColor: item.url.toLowerCase().startsWith('https')
137
+ ? '#ECFDF5'
138
+ : '#FFFBEB',
139
+ borderColor: item.url.toLowerCase().startsWith('https')
140
+ ? '#A7F3D0'
141
+ : '#FDE68A',
142
+ paddingHorizontal: 4,
143
+ paddingVertical: 1,
144
+ borderRadius: 4,
145
+ },
146
+ ]}>
147
+ <Text style={[
148
+ styles.chipText,
149
+ {
150
+ fontFamily: AppFonts.interBold,
151
+ fontSize: 8,
152
+ color: item.url.toLowerCase().startsWith('https')
153
+ ? '#059669'
154
+ : '#D97706',
155
+ },
156
+ ]}>
157
+ {item.url.toLowerCase().startsWith('https') ? 'HTTPS' : 'HTTP'}
158
+ </Text>
159
+ </View>
160
+
161
+ {/* GraphQL Indicator */}
162
+ {(item.url.toLowerCase().includes('graphql') ||
163
+ item.client === 'apollo' ||
164
+ item.client === 'graphql') && (<View style={[
165
+ styles.chip,
166
+ {
167
+ backgroundColor: '#FDF2F8',
168
+ borderColor: '#FBCFE8',
169
+ paddingHorizontal: 4,
170
+ paddingVertical: 1,
171
+ borderRadius: 4,
172
+ },
173
+ ]}>
174
+ <Text style={[
175
+ styles.chipText,
176
+ {
177
+ fontFamily: AppFonts.interBold,
178
+ fontSize: 8,
179
+ color: '#DB2777',
180
+ },
181
+ ]}>
182
+ GQL
183
+ </Text>
184
+ </View>)}
185
+
186
+ {/* In-Line Duration / Latency Pill */}
187
+ {item.duration != null && !isFailed && (<View style={[
188
+ styles.chip,
189
+ {
190
+ backgroundColor: `${durationColor}12`,
191
+ borderColor: `${durationColor}2E`,
192
+ paddingHorizontal: 4.5,
193
+ paddingVertical: 1,
194
+ borderRadius: 4,
195
+ },
196
+ ]}>
197
+ <Text style={[
198
+ styles.chipText,
199
+ {
200
+ fontFamily: AppFonts.interBold,
201
+ fontSize: 8.5,
202
+ color: durationColor,
203
+ },
204
+ ]}>
205
+ {item.duration}ms
206
+ </Text>
207
+ </View>)}
116
208
  </View>
117
209
 
118
210
  <View style={[
@@ -149,12 +241,12 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
149
241
  </View>
150
242
  </View>
151
243
 
152
- {/* Row 2: Full URL Capsule Container */}
244
+ {/* Row 2: Full URL Capsule Container (Clickable with link prompt) */}
153
245
  <View style={styles.cardSlugBox}>
154
- <View style={styles.slugLeft}>
155
- <Text style={styles.slugTag}>URL</Text>
156
- <HighlightText text={item.url} search={searchStr} style={styles.slugText} highlightStyle={styles.highlight} numberOfLines={2} ellipsizeMode="middle"/>
157
- </View>
246
+ <Pressable onPress={handleOpenUrl} style={styles.slugLeft} hitSlop={6}>
247
+ <Text style={styles.slugTag}>URL ↗</Text>
248
+ <HighlightText text={item.url} search={searchStr} style={[styles.slugText, { color: AppColors.skyBlue, textDecorationLine: 'underline' }]} highlightStyle={styles.highlight} numberOfLines={2} ellipsizeMode="middle"/>
249
+ </Pressable>
158
250
 
159
251
  <View style={styles.slugRight}>
160
252
  {isJson && (<View style={[
@@ -362,15 +362,21 @@ export const ForwardChevronIcon = ({ color = AppColors.grayTextWeak, size = 14,
362
362
  export const BundleIcon = PackageIcon;
363
363
  export const LiveStateIcon = ({ color = AppColors.purple, size = 14, }) => {
364
364
  return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
365
- <Path d="M12 3v3M12 18v3M3 12h3M18 12h3" stroke={color} strokeWidth="2" strokeLinecap="round"/>
366
- <Circle cx="12" cy="12" r="3.5" stroke={color} strokeWidth="1.8" fill={`${color}22`}/>
367
- <Circle cx="12" cy="12" r="1.5" fill={color}/>
365
+ <Circle cx="12" cy="12" r="9" stroke={color} strokeWidth="1.8" strokeDasharray="3 3" opacity={0.6}/>
366
+ <Circle cx="12" cy="12" r="4.5" stroke={color} strokeWidth="1.8"/>
367
+ <Circle cx="12" cy="12" r="2" fill={color}/>
368
+ <Path d="M12 2v2.5M12 19.5V22M2 12h2.5M19.5 12H22" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
368
369
  </Svg>);
369
370
  };
370
371
  export const TimelineIcon = ({ color = AppColors.purple, size = 14, }) => {
371
372
  return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
372
- <Circle cx="12" cy="12" r="8.5" stroke={color} strokeWidth="1.8"/>
373
- <Path d="M12 7.5v4.5l3 2" stroke={color} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
373
+ <Path d="M6 3v18" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
374
+ <Circle cx="6" cy="6" r="2.5" stroke={color} strokeWidth="1.8" fill={`${color}25`}/>
375
+ <Path d="M6 12h5a3 3 0 0 0 3-3V6" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
376
+ <Circle cx="14" cy="6" r="2.5" stroke={color} strokeWidth="1.8" fill={color}/>
377
+ <Circle cx="6" cy="18" r="2.5" stroke={color} strokeWidth="1.8" fill={color}/>
378
+ <Path d="M6 18h8" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
379
+ <Circle cx="17" cy="18" r="2.5" stroke={color} strokeWidth="1.8" fill={`${color}25`}/>
374
380
  </Svg>);
375
381
  };
376
382
  export const RefreshCcwIcon = ({ color = AppColors.grayTextWeak, size = 14, }) => {
@@ -380,15 +386,18 @@ export const RefreshCcwIcon = ({ color = AppColors.grayTextWeak, size = 14, }) =
380
386
  };
381
387
  export const StorageIcon = ({ color = AppColors.purple, size = 14, }) => {
382
388
  return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
383
- <Ellipse cx="12" cy="6" rx="8" ry="2.8" stroke={color} strokeWidth="1.8"/>
384
- <Path d="M20 12c0 1.55-3.58 2.8-8 2.8s-8-1.25-8-2.8" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
385
- <Path d="M4 6v12c0 1.55 3.58 2.8 8 2.8s8-1.25 8-2.8V6" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
389
+ <Ellipse cx="12" cy="5" rx="8" ry="2.5" stroke={color} strokeWidth="1.8"/>
390
+ <Path d="M4 5v4.5c0 1.38 3.58 2.5 8 2.5s8-1.12 8-2.5V5" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
391
+ <Path d="M4 9.5v4.5c0 1.38 3.58 2.5 8 2.5s8-1.12 8-2.5v-4.5" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
392
+ <Path d="M4 14v4.5c0 1.38 3.58 2.5 8 2.5s8-1.12 8-2.5V14" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
386
393
  </Svg>);
387
394
  };
388
395
  export const MetadataIcon = ({ color = AppColors.purple, size = 14, }) => {
389
396
  return (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
390
- <Circle cx="12" cy="12" r="8.5" stroke={color} strokeWidth="1.8"/>
391
- <Path d="M12 16v-4M12 8.2h.01" stroke={color} strokeWidth="2" strokeLinecap="round"/>
397
+ <Rect x="3" y="3" width="18" height="18" rx="4" stroke={color} strokeWidth="1.8"/>
398
+ <Path d="M7 8h10M7 12h6M7 16h7" stroke={color} strokeWidth="1.8" strokeLinecap="round"/>
399
+ <Circle cx="16.5" cy="12" r="1.5" fill={color}/>
400
+ <Circle cx="16.5" cy="16" r="1.5" fill={color}/>
392
401
  </Svg>);
393
402
  };
394
403
  export const ReduxIcon = ({ color = AppColors.purple, size = 14, }) => {
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const Toast: React.MemoExoticComponent<() => React.JSX.Element>;
3
+ export default Toast;
@@ -0,0 +1,117 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { Animated, StyleSheet, Text, View, Platform } from 'react-native';
3
+ import { subscribeToast } from '../helpers/toast';
4
+ import { CheckIcon } from './NetworkIcons';
5
+ import { AppColors } from '../styles/AppColors';
6
+ import { AppFonts } from '../styles/AppFonts';
7
+ const Toast = React.memo(() => {
8
+ const [toastMessage, setToastMessage] = useState(null);
9
+ const opacityAnim = useRef(new Animated.Value(0)).current;
10
+ const translateYAnim = useRef(new Animated.Value(20)).current;
11
+ const hideTimerRef = useRef(null);
12
+ useEffect(() => {
13
+ const unsubscribe = subscribeToast((message) => {
14
+ if (hideTimerRef.current) {
15
+ clearTimeout(hideTimerRef.current);
16
+ }
17
+ setToastMessage(message);
18
+ Animated.parallel([
19
+ Animated.timing(opacityAnim, {
20
+ toValue: 1,
21
+ duration: 180,
22
+ useNativeDriver: true,
23
+ }),
24
+ Animated.spring(translateYAnim, {
25
+ toValue: 0,
26
+ tension: 80,
27
+ friction: 8,
28
+ useNativeDriver: true,
29
+ }),
30
+ ]).start();
31
+ hideTimerRef.current = setTimeout(() => {
32
+ Animated.parallel([
33
+ Animated.timing(opacityAnim, {
34
+ toValue: 0,
35
+ duration: 200,
36
+ useNativeDriver: true,
37
+ }),
38
+ Animated.timing(translateYAnim, {
39
+ toValue: 20,
40
+ duration: 200,
41
+ useNativeDriver: true,
42
+ }),
43
+ ]).start(() => {
44
+ setToastMessage(null);
45
+ });
46
+ }, 2000);
47
+ });
48
+ return () => {
49
+ unsubscribe();
50
+ if (hideTimerRef.current) {
51
+ clearTimeout(hideTimerRef.current);
52
+ }
53
+ };
54
+ }, [opacityAnim, translateYAnim]);
55
+ if (!toastMessage) {
56
+ return null;
57
+ }
58
+ return (<Animated.View pointerEvents="none" style={[
59
+ styles.toastContainer,
60
+ {
61
+ opacity: opacityAnim,
62
+ transform: [{ translateY: translateYAnim }],
63
+ },
64
+ ]}>
65
+ <View style={styles.toastCard}>
66
+ <View style={styles.iconCircle}>
67
+ <CheckIcon color={AppColors.white} size={11}/>
68
+ </View>
69
+ <Text style={styles.toastText} numberOfLines={2}>
70
+ {toastMessage}
71
+ </Text>
72
+ </View>
73
+ </Animated.View>);
74
+ });
75
+ const styles = StyleSheet.create({
76
+ toastContainer: {
77
+ position: 'absolute',
78
+ bottom: Platform.OS === 'ios' ? 44 : 28,
79
+ left: 20,
80
+ right: 20,
81
+ alignItems: 'center',
82
+ justifyContent: 'center',
83
+ zIndex: 999999,
84
+ },
85
+ toastCard: {
86
+ flexDirection: 'row',
87
+ alignItems: 'center',
88
+ backgroundColor: '#0F172AEE', // dark slate glass
89
+ paddingVertical: 10,
90
+ paddingHorizontal: 16,
91
+ borderRadius: 24,
92
+ shadowColor: '#000000',
93
+ shadowOffset: { width: 0, height: 6 },
94
+ shadowOpacity: 0.28,
95
+ shadowRadius: 10,
96
+ elevation: 8,
97
+ borderWidth: 1,
98
+ borderColor: '#33415588',
99
+ maxWidth: '90%',
100
+ gap: 9,
101
+ },
102
+ iconCircle: {
103
+ width: 18,
104
+ height: 18,
105
+ borderRadius: 9,
106
+ backgroundColor: AppColors.greenColor,
107
+ alignItems: 'center',
108
+ justifyContent: 'center',
109
+ },
110
+ toastText: {
111
+ color: AppColors.white,
112
+ fontFamily: AppFonts.interBold,
113
+ fontSize: 12.5,
114
+ letterSpacing: -0.1,
115
+ },
116
+ });
117
+ export default Toast;
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  declare const TouchableScale: React.NamedExoticComponent<{
3
3
  onPress?: () => void;
4
+ onLongPress?: () => void;
4
5
  style?: any;
5
6
  children?: React.ReactNode;
6
7
  hitSlop?: any;
@@ -1,8 +1,8 @@
1
1
  import React, { useRef } from 'react';
2
2
  import { Animated, Pressable, StyleSheet, Platform } from 'react-native';
3
- const TouchableScale = React.memo(function TouchableScale({ onPress, style, children, hitSlop, disabled, }) {
3
+ const TouchableScale = React.memo(function TouchableScale({ onPress, onLongPress, style, children, hitSlop, disabled, }) {
4
4
  if (Platform.OS === 'android') {
5
- return (<Pressable disabled={disabled} onPress={onPress} hitSlop={hitSlop} style={({ pressed }) => [
5
+ return (<Pressable disabled={disabled} onPress={onPress} onLongPress={onLongPress} hitSlop={hitSlop} style={({ pressed }) => [
6
6
  style,
7
7
  { opacity: pressed ? 0.75 : 1 },
8
8
  ]}>
@@ -38,7 +38,7 @@ const TouchableScale = React.memo(function TouchableScale({ onPress, style, chil
38
38
  flexShrink: flattenedStyle.flexShrink,
39
39
  gap: flattenedStyle.gap,
40
40
  };
41
- return (<Pressable disabled={disabled} style={style} onPressIn={() => animatePress(true)} onPressOut={() => animatePress(false)} onPress={onPress} hitSlop={hitSlop}>
41
+ return (<Pressable disabled={disabled} style={style} onPressIn={() => animatePress(true)} onPressOut={() => animatePress(false)} onPress={onPress} onLongPress={onLongPress} hitSlop={hitSlop}>
42
42
  <Animated.View style={[{ opacity, transform: [{ scale }] }, layoutStyle]}>
43
43
  {children}
44
44
  </Animated.View>
@@ -7,9 +7,19 @@ import { ChevronIcon, DocIcon } from './NetworkIcons';
7
7
  // Stylesheet
8
8
  import { AppColors } from '../styles/AppColors';
9
9
  import styles from '../styles';
10
- const TreeNode = React.memo(function TreeNode({ data, name, level = 0, search, forceOpen, defaultExpandDepth, }) {
11
- const [localOpen, setLocalOpen] = useState(forceOpen || level < (defaultExpandDepth ?? 1));
10
+ const TreeNode = React.memo(function TreeNode({ data, name, level = 0, search, forceOpen, defaultExpandDepth = 1, }) {
11
+ const [localOpen, setLocalOpen] = useState(() => {
12
+ if (forceOpen !== undefined) {
13
+ return forceOpen;
14
+ }
15
+ return defaultExpandDepth !== undefined ? level < defaultExpandDepth : level < 1;
16
+ });
12
17
  const open = localOpen;
18
+ useEffect(() => {
19
+ if (forceOpen !== undefined) {
20
+ setLocalOpen(forceOpen);
21
+ }
22
+ }, [forceOpen]);
13
23
  const isObject = typeof data === 'object' && data !== null;
14
24
  const isArray = Array.isArray(data);
15
25
  // Declare all hooks at the top unconditionally (prevents "Rendered fewer hooks than expected" error)
@@ -9,12 +9,12 @@ export const STATUS_FILTERS = [
9
9
  'Failed',
10
10
  ];
11
11
  export const METHOD_COLORS = {
12
- ALL: AppColors.grayText,
13
- GET: AppColors.purple,
14
- POST: AppColors.greenColor,
15
- PUT: AppColors.lightOrange,
16
- PATCH: AppColors.offerPurple,
17
- DELETE: AppColors.errorColor,
12
+ ALL: '#64748B', // Slate
13
+ GET: '#059669', // Emerald
14
+ POST: '#2563EB', // Royal Blue
15
+ PUT: '#D97706', // Amber Gold
16
+ PATCH: '#7C3AED', // Rich Violet
17
+ DELETE: '#DC2626', // Crimson Red
18
18
  };
19
19
  export const DOMAIN_COLORS = AppColors.domainColors;
20
20
  export const DURATION_FAST_MS = 200;
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "1.1.28";
1
+ export declare const LIB_VERSION = "1.1.31";
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED FILE — do not edit by hand.
2
2
  // Regenerated from package.json on every build by scripts/gen-version.js.
3
- export const LIB_VERSION = '1.1.28';
3
+ export const LIB_VERSION = '1.1.31';
@@ -11,6 +11,7 @@
11
11
  // that bypass the wrapped dispatch (thunk/saga internals capture the raw
12
12
  // dispatch reference at store-creation time) still update the state tree
13
13
  // and per-reducer "last action" consistently.
14
+ import { parseStackLine } from '../helpers';
14
15
  let currentReduxState = null;
15
16
  const listeners = new Set();
16
17
  let globalReduxAutoRefresh = true;
@@ -63,7 +64,106 @@ function actionTypeOf(action) {
63
64
  }
64
65
  return 'UNKNOWN_ACTION';
65
66
  }
66
- function recordAction(action, prevState, nextState) {
67
+ function detectActionOrigin(stack, actionType, actionObj) {
68
+ const stackLower = (stack || '').toLowerCase();
69
+ const typeLower = (actionType || '').toLowerCase();
70
+ // Explicit hint on action if developer tagged it
71
+ if (actionObj && actionObj.__origin) {
72
+ const raw = String(actionObj.__origin).toLowerCase();
73
+ if (raw.includes('saga'))
74
+ return { originType: 'saga' };
75
+ if (raw.includes('thunk'))
76
+ return { originType: 'thunk' };
77
+ if (raw.includes('ui'))
78
+ return { originType: 'ui' };
79
+ }
80
+ // 1. Saga Detection (Stack + Action naming conventions)
81
+ const isSaga = stackLower.includes('redux-saga') ||
82
+ stackLower.includes('runputeffect') ||
83
+ stackLower.includes('proc.js') ||
84
+ stackLower.includes('effects.js') ||
85
+ stackLower.includes('sagamiddleware') ||
86
+ stackLower.includes('saga.ts') ||
87
+ stackLower.includes('saga.js') ||
88
+ stackLower.includes('sagas') ||
89
+ typeLower.includes('saga') ||
90
+ typeLower.startsWith('saga/') ||
91
+ typeLower.startsWith('@saga/');
92
+ // 2. Thunk / RTK Query Detection
93
+ const isThunk = stackLower.includes('createasyncthunk') ||
94
+ stackLower.includes('redux-thunk') ||
95
+ stackLower.includes('rtk-query') ||
96
+ stackLower.includes('thunkmiddleware') ||
97
+ stackLower.includes('actioncreator') ||
98
+ typeLower.includes('thunk') ||
99
+ typeLower.endsWith('/pending') ||
100
+ typeLower.endsWith('/fulfilled') ||
101
+ typeLower.endsWith('/rejected');
102
+ // 3. Parse Stack Frames to find best application caller
103
+ const lines = (stack || '').split('\n').map(l => l.trim()).filter(Boolean);
104
+ let bestFrame = null;
105
+ for (let i = 0; i < lines.length; i++) {
106
+ const line = lines[i];
107
+ // Ignore logger / middleware lines inside inspector
108
+ if (line.includes('reduxLogger') ||
109
+ line.includes('inspectorReduxMiddleware') ||
110
+ line.includes('recordAction') ||
111
+ line.includes('redux.js') ||
112
+ line.includes('redux.cjs')) {
113
+ continue;
114
+ }
115
+ const parsed = parseStackLine(line, false);
116
+ if (parsed.frameType === 'app' || (!parsed.isRuntimeNoise && parsed.fileName !== 'Unknown')) {
117
+ bestFrame = parsed;
118
+ break;
119
+ }
120
+ }
121
+ if (!bestFrame && lines.length > 0) {
122
+ for (let i = 0; i < lines.length; i++) {
123
+ if (!lines[i].includes('reduxLogger') && !lines[i].includes('inspectorReduxMiddleware')) {
124
+ bestFrame = parseStackLine(lines[i], false);
125
+ break;
126
+ }
127
+ }
128
+ }
129
+ let originType = 'direct';
130
+ if (isSaga || (bestFrame && bestFrame.fileName?.toLowerCase().includes('saga'))) {
131
+ originType = 'saga';
132
+ }
133
+ else if (isThunk || (bestFrame && bestFrame.fileName?.toLowerCase().includes('thunk'))) {
134
+ originType = 'thunk';
135
+ }
136
+ else if (bestFrame &&
137
+ (bestFrame.fileName?.endsWith('.tsx') ||
138
+ bestFrame.functionName?.includes('onPress') ||
139
+ bestFrame.functionName?.includes('touchable') ||
140
+ bestFrame.functionName?.includes('Effect'))) {
141
+ originType = 'ui';
142
+ }
143
+ else if (stackLower.includes('listener') || stackLower.includes('subscribe')) {
144
+ originType = 'listener';
145
+ }
146
+ return {
147
+ originType,
148
+ caller: bestFrame?.copyableLocation || bestFrame?.raw || undefined,
149
+ callerFile: bestFrame?.fullPath || bestFrame?.fileName || undefined,
150
+ callerLine: bestFrame?.lineNumber ? Number(bestFrame.lineNumber) : undefined,
151
+ callerCol: bestFrame?.columnNumber ? Number(bestFrame.columnNumber) : undefined,
152
+ };
153
+ }
154
+ function extractSliceName(actionType, affectedSlices) {
155
+ if (affectedSlices && affectedSlices.length > 0) {
156
+ return affectedSlices[0];
157
+ }
158
+ // If Redux Toolkit action format: e.g. "users/fetchById" -> "users"
159
+ const match = actionType.match(/^([a-zA-Z0-9_\-]+)\//);
160
+ if (match && match[1] && !match[1].startsWith('@@')) {
161
+ return match[1];
162
+ }
163
+ return undefined;
164
+ }
165
+ function recordAction(action, prevState, nextState, rawStack) {
166
+ const stack = rawStack || new Error().stack || '';
67
167
  const type = actionTypeOf(action);
68
168
  const payload = action && typeof action === 'object' && action.payload !== undefined
69
169
  ? action.payload
@@ -71,13 +171,26 @@ function recordAction(action, prevState, nextState) {
71
171
  const now = Date.now();
72
172
  const timestamp = new Date(now).toLocaleTimeString();
73
173
  const affectedSlices = [];
174
+ const origin = detectActionOrigin(stack, type, action);
175
+ const sliceName = extractSliceName(type, affectedSlices);
74
176
  if (prevState &&
75
177
  nextState &&
76
178
  typeof prevState === 'object' &&
77
179
  typeof nextState === 'object') {
78
180
  Object.keys(nextState).forEach(key => {
79
181
  if (prevState[key] !== nextState[key]) {
80
- lastActionForReducer[key] = { type, payload, timestamp, updatedAt: now };
182
+ lastActionForReducer[key] = {
183
+ type,
184
+ payload,
185
+ timestamp,
186
+ updatedAt: now,
187
+ originType: origin.originType,
188
+ callerFile: origin.callerFile,
189
+ callerLine: origin.callerLine,
190
+ callerCol: origin.callerCol,
191
+ caller: origin.caller,
192
+ stack,
193
+ };
81
194
  affectedSlices.push(key);
82
195
  }
83
196
  });
@@ -91,6 +204,13 @@ function recordAction(action, prevState, nextState) {
91
204
  affectedSlices,
92
205
  prevState,
93
206
  nextState,
207
+ stack,
208
+ caller: origin.caller,
209
+ callerFile: origin.callerFile,
210
+ callerLine: origin.callerLine,
211
+ callerCol: origin.callerCol,
212
+ originType: origin.originType,
213
+ sliceName,
94
214
  });
95
215
  if (actionHistory.length > MAX_HISTORY) {
96
216
  actionHistory.length = MAX_HISTORY;
@@ -116,6 +236,7 @@ function recordAction(action, prevState, nextState) {
116
236
  */
117
237
  export const inspectorReduxMiddleware = (storeApi) => (next) => (action) => {
118
238
  middlewareAttached = true;
239
+ const capturedStack = new Error().stack || '';
119
240
  // Thunks are functions — let them run; their inner plain-action dispatches
120
241
  // pass back through this same middleware, so nothing is lost.
121
242
  if (typeof action === 'function') {
@@ -126,7 +247,7 @@ export const inspectorReduxMiddleware = (storeApi) => (next) => (action) => {
126
247
  const nextState = storeApi.getState();
127
248
  if (currentReduxState == null)
128
249
  currentReduxState = nextState;
129
- recordAction(action, prevState, nextState);
250
+ recordAction(action, prevState, nextState, capturedStack);
130
251
  return result;
131
252
  };
132
253
  export const connectReduxStore = (store) => {
@@ -148,6 +269,7 @@ export const connectReduxStore = (store) => {
148
269
  const originalDispatch = store.dispatch.bind(store);
149
270
  let inWrappedDispatch = false;
150
271
  store.dispatch = (action) => {
272
+ const capturedStack = new Error().stack || '';
151
273
  if (middlewareAttached || typeof action === 'function') {
152
274
  // Middleware handles recording, or it's a thunk whose inner dispatches
153
275
  // will be picked up individually.
@@ -168,7 +290,7 @@ export const connectReduxStore = (store) => {
168
290
  finally {
169
291
  inWrappedDispatch = false;
170
292
  }
171
- recordAction(action, prevState, store.getState());
293
+ recordAction(action, prevState, store.getState(), capturedStack);
172
294
  return result;
173
295
  };
174
296
  setReduxState(store.getState());
@@ -192,6 +314,7 @@ export const connectReduxStore = (store) => {
192
314
  }
193
315
  // Change arrived outside the wrapped dispatch — record it so the
194
316
  // timeline stays consistent, even without the original action type.
195
- recordAction({ type: '@@inspector/EXTERNAL_STATE_CHANGE' }, prevState, nextState);
317
+ const subStack = new Error().stack || '';
318
+ recordAction({ type: '@@inspector/EXTERNAL_STATE_CHANGE' }, prevState, nextState, subStack);
196
319
  });
197
320
  };
@@ -1,6 +1,7 @@
1
1
  import { NetworkLog, RouteInfo, DiffResult, JsonContent, StackFrameType } from '../types';
2
2
  export declare const getDomainColor: (domain: string) => string;
3
3
  export declare const formatDateTime: (timestamp: number) => string;
4
+ export declare const formatTimestamp: (timestamp: number) => string;
4
5
  export declare const getStatusColor: (status: number | null) => string;
5
6
  export declare const getDurationColor: (duration: number | null) => string;
6
7
  export declare const getSize: (data: unknown) => string;
@@ -44,6 +45,7 @@ export interface ParsedStackFrame {
44
45
  frameType: StackFrameType | 'app' | 'dependency' | 'runtime' | 'native';
45
46
  isUserCode: boolean;
46
47
  isRuntimeNoise: boolean;
48
+ rawFilePath?: string;
47
49
  lineNumber?: string;
48
50
  columnNumber?: string;
49
51
  isOrigin?: boolean;
@@ -51,6 +53,8 @@ export interface ParsedStackFrame {
51
53
  }
52
54
  /** Parses a stack trace line to extract function name, file name, extension (.tsx/.jsx/.ts), line, and column numbers */
53
55
  export declare const parseStackLine: (rawLine: string, isOrigin?: boolean) => ParsedStackFrame;
56
+ /** Opens a file and line number directly in VS Code / system editor */
57
+ export declare const openInVSCode: (filePath: string, lineNumber?: string | number, columnNumber?: string | number) => void;
54
58
  export declare const ANALYTICS_EVENT_PALETTE: string[];
55
59
  export declare const getEventColor: (name: string) => string;
56
60
  export { getEventCategory, registerGAPlugin, type GAEventCategory, type GAPlugin, } from './gaAnalyticsRegistry';