react-native-inapp-inspector 2.2.9 → 2.3.1
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.
- package/dist/commonjs/components/AppHeaderLogo.js +45 -12
- package/dist/commonjs/components/BrandSquareIcon.js +92 -49
- package/dist/commonjs/components/EmptyState.d.ts +7 -2
- package/dist/commonjs/components/EmptyState.js +11 -5
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +29 -18
- package/dist/commonjs/components/Inspector/ConsoleTab.js +6 -1
- package/dist/commonjs/components/Inspector/CrashDetail.js +43 -9
- package/dist/commonjs/components/Inspector/CrashTab.js +34 -3
- package/dist/commonjs/components/Inspector/DeviceInfoTab.js +80 -16
- package/dist/commonjs/components/Inspector/InspectorHeader.js +16 -3
- package/dist/commonjs/components/Inspector/MainScreen.js +7 -11
- package/dist/commonjs/components/Inspector/NetworkDetail.js +3 -2
- package/dist/commonjs/components/Inspector/NetworkTab.js +103 -4
- package/dist/commonjs/components/Inspector/ReduxTab.js +19 -3
- package/dist/commonjs/components/Inspector/SettingsPanel.js +4 -4
- package/dist/commonjs/components/Inspector/StorageTab.js +123 -92
- package/dist/commonjs/components/JsonViewer.js +2 -0
- package/dist/commonjs/components/MetaAccordion.js +15 -3
- package/dist/commonjs/components/SkeletonPlaceholder.d.ts +6 -0
- package/dist/commonjs/components/SkeletonPlaceholder.js +208 -0
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/networkLogger.d.ts +3 -0
- package/dist/commonjs/customHooks/networkLogger.js +10 -1
- package/dist/commonjs/customHooks/storageInspector.d.ts +2 -2
- package/dist/commonjs/customHooks/storageInspector.js +17 -13
- package/dist/commonjs/helpers/index.d.ts +6 -0
- package/dist/commonjs/helpers/index.js +196 -6
- package/dist/commonjs/helpers/searchQueryParser.js +62 -20
- package/dist/commonjs/i18n/index.d.ts +13 -0
- package/dist/commonjs/i18n/index.js +35 -2
- package/dist/commonjs/index.d.ts +3 -0
- package/dist/commonjs/index.js +108 -35
- package/dist/commonjs/styles/AppColors.d.ts +4 -0
- package/dist/commonjs/styles/AppColors.js +11 -1
- package/dist/commonjs/styles/AppFonts.d.ts +11 -0
- package/dist/commonjs/styles/AppFonts.js +11 -1
- package/dist/commonjs/types/interfaces.d.ts +2 -2
- package/dist/esm/components/AppHeaderLogo.js +45 -12
- package/dist/esm/components/BrandSquareIcon.js +92 -49
- package/dist/esm/components/EmptyState.d.ts +7 -2
- package/dist/esm/components/EmptyState.js +11 -5
- package/dist/esm/components/Inspector/AnalyticsTab.js +30 -19
- package/dist/esm/components/Inspector/ConsoleTab.js +6 -1
- package/dist/esm/components/Inspector/CrashDetail.js +43 -9
- package/dist/esm/components/Inspector/CrashTab.js +34 -3
- package/dist/esm/components/Inspector/DeviceInfoTab.js +80 -16
- package/dist/esm/components/Inspector/InspectorHeader.js +17 -4
- package/dist/esm/components/Inspector/MainScreen.js +8 -12
- package/dist/esm/components/Inspector/NetworkDetail.js +3 -2
- package/dist/esm/components/Inspector/NetworkTab.js +103 -4
- package/dist/esm/components/Inspector/ReduxTab.js +19 -3
- package/dist/esm/components/Inspector/SettingsPanel.js +4 -4
- package/dist/esm/components/Inspector/StorageTab.js +124 -93
- package/dist/esm/components/JsonViewer.js +2 -0
- package/dist/esm/components/MetaAccordion.js +15 -3
- package/dist/esm/components/SkeletonPlaceholder.d.ts +6 -0
- package/dist/esm/components/SkeletonPlaceholder.js +173 -0
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/networkLogger.d.ts +3 -0
- package/dist/esm/customHooks/networkLogger.js +8 -0
- package/dist/esm/customHooks/storageInspector.d.ts +2 -2
- package/dist/esm/customHooks/storageInspector.js +17 -13
- package/dist/esm/helpers/index.d.ts +6 -0
- package/dist/esm/helpers/index.js +193 -5
- package/dist/esm/helpers/searchQueryParser.js +62 -20
- package/dist/esm/i18n/index.d.ts +13 -0
- package/dist/esm/i18n/index.js +33 -3
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +97 -37
- package/dist/esm/styles/AppColors.d.ts +4 -0
- package/dist/esm/styles/AppColors.js +9 -0
- package/dist/esm/styles/AppFonts.d.ts +11 -0
- package/dist/esm/styles/AppFonts.js +9 -0
- package/dist/esm/types/interfaces.d.ts +2 -2
- package/package.json +1 -1
- package/src/components/AppHeaderLogo.tsx +46 -12
- package/src/components/BrandSquareIcon.tsx +127 -78
- package/src/components/EmptyState.tsx +27 -5
- package/src/components/Inspector/AnalyticsTab.tsx +48 -27
- package/src/components/Inspector/ConsoleTab.tsx +10 -0
- package/src/components/Inspector/CrashDetail.tsx +65 -9
- package/src/components/Inspector/CrashTab.tsx +41 -3
- package/src/components/Inspector/DeviceInfoTab.tsx +87 -16
- package/src/components/Inspector/InspectorHeader.tsx +24 -4
- package/src/components/Inspector/MainScreen.tsx +7 -11
- package/src/components/Inspector/NetworkDetail.tsx +4 -4
- package/src/components/Inspector/NetworkTab.tsx +129 -4
- package/src/components/Inspector/ReduxTab.tsx +26 -8
- package/src/components/Inspector/SettingsPanel.tsx +4 -4
- package/src/components/Inspector/StorageTab.tsx +191 -117
- package/src/components/JsonViewer.tsx +2 -0
- package/src/components/MetaAccordion.tsx +18 -3
- package/src/components/SkeletonPlaceholder.tsx +206 -0
- package/src/constants/version.ts +1 -1
- package/src/customHooks/networkLogger.ts +15 -0
- package/src/customHooks/storageInspector.ts +18 -14
- package/src/helpers/index.ts +213 -5
- package/src/helpers/searchQueryParser.ts +63 -20
- package/src/i18n/index.ts +38 -3
- package/src/index.tsx +124 -37
- package/src/styles/AppColors.ts +9 -0
- package/src/styles/AppFonts.ts +16 -0
- package/src/types/interfaces.ts +2 -2
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { Animated, StyleSheet, View } from 'react-native';
|
|
3
|
+
import { AppColors } from '../styles/AppColors';
|
|
4
|
+
const SkeletonPlaceholder = React.memo(function SkeletonPlaceholder({ cardCount = 4, }) {
|
|
5
|
+
const shimmerAnim = useRef(new Animated.Value(0.35)).current;
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const animation = Animated.loop(Animated.sequence([
|
|
8
|
+
Animated.timing(shimmerAnim, {
|
|
9
|
+
toValue: 0.85,
|
|
10
|
+
duration: 750,
|
|
11
|
+
useNativeDriver: true,
|
|
12
|
+
}),
|
|
13
|
+
Animated.timing(shimmerAnim, {
|
|
14
|
+
toValue: 0.35,
|
|
15
|
+
duration: 750,
|
|
16
|
+
useNativeDriver: true,
|
|
17
|
+
}),
|
|
18
|
+
]));
|
|
19
|
+
animation.start();
|
|
20
|
+
return () => animation.stop();
|
|
21
|
+
}, [shimmerAnim]);
|
|
22
|
+
return (<View style={skeletonStyles.container}>
|
|
23
|
+
{/* ─── Search & Scope Toolbar Skeleton ─── */}
|
|
24
|
+
<View style={skeletonStyles.toolbarSkeleton}>
|
|
25
|
+
<Animated.View style={[
|
|
26
|
+
skeletonStyles.searchBarSkeleton,
|
|
27
|
+
{ opacity: shimmerAnim },
|
|
28
|
+
]}/>
|
|
29
|
+
<View style={skeletonStyles.actionButtonsRow}>
|
|
30
|
+
<Animated.View style={[skeletonStyles.iconButtonSkeleton, { opacity: shimmerAnim }]}/>
|
|
31
|
+
<Animated.View style={[skeletonStyles.iconButtonSkeleton, { opacity: shimmerAnim }]}/>
|
|
32
|
+
</View>
|
|
33
|
+
</View>
|
|
34
|
+
|
|
35
|
+
{/* ─── Quick Filter Chips Skeleton Strip ─── */}
|
|
36
|
+
<View style={skeletonStyles.chipStripSkeleton}>
|
|
37
|
+
<Animated.View style={[skeletonStyles.chipSkeleton, { width: 48, opacity: shimmerAnim }]}/>
|
|
38
|
+
<Animated.View style={[skeletonStyles.chipSkeleton, { width: 68, opacity: shimmerAnim }]}/>
|
|
39
|
+
<Animated.View style={[skeletonStyles.chipSkeleton, { width: 76, opacity: shimmerAnim }]}/>
|
|
40
|
+
<Animated.View style={[skeletonStyles.chipSkeleton, { width: 58, opacity: shimmerAnim }]}/>
|
|
41
|
+
</View>
|
|
42
|
+
|
|
43
|
+
{/* ─── List Cards Skeleton ─── */}
|
|
44
|
+
{Array.from({ length: cardCount }).map((_, i) => (<Animated.View key={`skeleton_card_${i}`} style={[skeletonStyles.cardSkeleton, { opacity: shimmerAnim }]}>
|
|
45
|
+
{/* Top row: Status pill + Method + Time */}
|
|
46
|
+
<View style={skeletonStyles.cardTopRow}>
|
|
47
|
+
<View style={skeletonStyles.badgeGroup}>
|
|
48
|
+
<View style={skeletonStyles.statusBadgeSkeleton}/>
|
|
49
|
+
<View style={skeletonStyles.methodBadgeSkeleton}/>
|
|
50
|
+
</View>
|
|
51
|
+
<View style={skeletonStyles.timeSkeleton}/>
|
|
52
|
+
</View>
|
|
53
|
+
|
|
54
|
+
{/* Middle row: URL lines */}
|
|
55
|
+
<View style={skeletonStyles.urlLineLong}/>
|
|
56
|
+
<View style={skeletonStyles.urlLineShort}/>
|
|
57
|
+
|
|
58
|
+
{/* Bottom row: Latency & Size */}
|
|
59
|
+
<View style={skeletonStyles.cardBottomRow}>
|
|
60
|
+
<View style={skeletonStyles.metaPillSkeleton}/>
|
|
61
|
+
<View style={skeletonStyles.metaPillSkeleton}/>
|
|
62
|
+
</View>
|
|
63
|
+
</Animated.View>))}
|
|
64
|
+
</View>);
|
|
65
|
+
});
|
|
66
|
+
const skeletonStyles = StyleSheet.create({
|
|
67
|
+
container: {
|
|
68
|
+
flex: 1,
|
|
69
|
+
paddingHorizontal: 12,
|
|
70
|
+
paddingTop: 8,
|
|
71
|
+
},
|
|
72
|
+
toolbarSkeleton: {
|
|
73
|
+
flexDirection: 'row',
|
|
74
|
+
alignItems: 'center',
|
|
75
|
+
gap: 8,
|
|
76
|
+
marginBottom: 8,
|
|
77
|
+
},
|
|
78
|
+
searchBarSkeleton: {
|
|
79
|
+
flex: 1,
|
|
80
|
+
height: 36,
|
|
81
|
+
borderRadius: 8,
|
|
82
|
+
backgroundColor: AppColors.graySurface,
|
|
83
|
+
borderWidth: 1,
|
|
84
|
+
borderColor: AppColors.dividerColor,
|
|
85
|
+
},
|
|
86
|
+
actionButtonsRow: {
|
|
87
|
+
flexDirection: 'row',
|
|
88
|
+
gap: 6,
|
|
89
|
+
},
|
|
90
|
+
iconButtonSkeleton: {
|
|
91
|
+
width: 36,
|
|
92
|
+
height: 36,
|
|
93
|
+
borderRadius: 8,
|
|
94
|
+
backgroundColor: AppColors.graySurface,
|
|
95
|
+
borderWidth: 1,
|
|
96
|
+
borderColor: AppColors.dividerColor,
|
|
97
|
+
},
|
|
98
|
+
chipStripSkeleton: {
|
|
99
|
+
flexDirection: 'row',
|
|
100
|
+
gap: 6,
|
|
101
|
+
marginBottom: 10,
|
|
102
|
+
},
|
|
103
|
+
chipSkeleton: {
|
|
104
|
+
height: 24,
|
|
105
|
+
borderRadius: 6,
|
|
106
|
+
backgroundColor: AppColors.graySurface,
|
|
107
|
+
borderWidth: 1,
|
|
108
|
+
borderColor: AppColors.dividerColor,
|
|
109
|
+
},
|
|
110
|
+
cardSkeleton: {
|
|
111
|
+
backgroundColor: AppColors.primaryLight,
|
|
112
|
+
borderRadius: 10,
|
|
113
|
+
padding: 12,
|
|
114
|
+
marginBottom: 8,
|
|
115
|
+
borderWidth: 1,
|
|
116
|
+
borderColor: AppColors.dividerColor,
|
|
117
|
+
},
|
|
118
|
+
cardTopRow: {
|
|
119
|
+
flexDirection: 'row',
|
|
120
|
+
justifyContent: 'space-between',
|
|
121
|
+
alignItems: 'center',
|
|
122
|
+
marginBottom: 8,
|
|
123
|
+
},
|
|
124
|
+
badgeGroup: {
|
|
125
|
+
flexDirection: 'row',
|
|
126
|
+
alignItems: 'center',
|
|
127
|
+
gap: 6,
|
|
128
|
+
},
|
|
129
|
+
statusBadgeSkeleton: {
|
|
130
|
+
width: 38,
|
|
131
|
+
height: 18,
|
|
132
|
+
borderRadius: 4,
|
|
133
|
+
backgroundColor: AppColors.graySurface,
|
|
134
|
+
},
|
|
135
|
+
methodBadgeSkeleton: {
|
|
136
|
+
width: 44,
|
|
137
|
+
height: 18,
|
|
138
|
+
borderRadius: 4,
|
|
139
|
+
backgroundColor: AppColors.graySurface,
|
|
140
|
+
},
|
|
141
|
+
timeSkeleton: {
|
|
142
|
+
width: 48,
|
|
143
|
+
height: 12,
|
|
144
|
+
borderRadius: 4,
|
|
145
|
+
backgroundColor: AppColors.graySurface,
|
|
146
|
+
},
|
|
147
|
+
urlLineLong: {
|
|
148
|
+
height: 13,
|
|
149
|
+
borderRadius: 4,
|
|
150
|
+
backgroundColor: AppColors.graySurface,
|
|
151
|
+
marginBottom: 5,
|
|
152
|
+
width: '90%',
|
|
153
|
+
},
|
|
154
|
+
urlLineShort: {
|
|
155
|
+
height: 11,
|
|
156
|
+
borderRadius: 4,
|
|
157
|
+
backgroundColor: AppColors.graySurface,
|
|
158
|
+
marginBottom: 8,
|
|
159
|
+
width: '55%',
|
|
160
|
+
},
|
|
161
|
+
cardBottomRow: {
|
|
162
|
+
flexDirection: 'row',
|
|
163
|
+
gap: 8,
|
|
164
|
+
marginTop: 2,
|
|
165
|
+
},
|
|
166
|
+
metaPillSkeleton: {
|
|
167
|
+
width: 52,
|
|
168
|
+
height: 14,
|
|
169
|
+
borderRadius: 4,
|
|
170
|
+
backgroundColor: AppColors.graySurface,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
export default SkeletonPlaceholder;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const LIB_VERSION = "2.
|
|
1
|
+
export declare const LIB_VERSION = "2.3.1";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RouteInfo } from "../types";
|
|
1
2
|
type NetworkLog = {
|
|
2
3
|
id: number;
|
|
3
4
|
url: string;
|
|
@@ -11,7 +12,9 @@ type NetworkLog = {
|
|
|
11
12
|
client?: string;
|
|
12
13
|
requestHeaders?: Record<string, string>;
|
|
13
14
|
responseHeaders?: Record<string, string>;
|
|
15
|
+
routeInfo?: RouteInfo;
|
|
14
16
|
};
|
|
17
|
+
export declare const setRouteInfoProvider: (provider: (() => RouteInfo | null) | null) => void;
|
|
15
18
|
export declare const setNetworkModuleEnabled: (enabled: boolean) => void;
|
|
16
19
|
export declare const getNetworkModuleEnabled: () => boolean;
|
|
17
20
|
export declare const subscribeNetworkLogs: (callback: (logs: NetworkLog[]) => void) => () => void;
|
|
@@ -4,6 +4,10 @@ let logs = [];
|
|
|
4
4
|
let listeners = [];
|
|
5
5
|
let counter = 0;
|
|
6
6
|
let isNetworkModuleEnabled = true;
|
|
7
|
+
let currentRouteProvider = null;
|
|
8
|
+
export const setRouteInfoProvider = (provider) => {
|
|
9
|
+
currentRouteProvider = provider;
|
|
10
|
+
};
|
|
7
11
|
export const setNetworkModuleEnabled = (enabled) => {
|
|
8
12
|
isNetworkModuleEnabled = enabled;
|
|
9
13
|
};
|
|
@@ -185,6 +189,7 @@ export const setupNetworkLogger = () => {
|
|
|
185
189
|
else if (caller?.toLowerCase().includes('axios')) {
|
|
186
190
|
client = 'axios';
|
|
187
191
|
}
|
|
192
|
+
const currentRoute = currentRouteProvider ? currentRouteProvider() : null;
|
|
188
193
|
addOrUpdateLog({
|
|
189
194
|
id,
|
|
190
195
|
url: finalUrl,
|
|
@@ -192,6 +197,7 @@ export const setupNetworkLogger = () => {
|
|
|
192
197
|
startTime: start,
|
|
193
198
|
caller,
|
|
194
199
|
client,
|
|
200
|
+
routeInfo: currentRoute || undefined,
|
|
195
201
|
request: method === "GET" ? undefined : parseRequestData(options?.body),
|
|
196
202
|
requestHeaders,
|
|
197
203
|
});
|
|
@@ -298,6 +304,7 @@ export const addAxiosInterceptors = (axiosInstance) => {
|
|
|
298
304
|
config.__logId = id;
|
|
299
305
|
config.__logStart = start;
|
|
300
306
|
config.__logCaller = caller;
|
|
307
|
+
const currentRoute = currentRouteProvider ? currentRouteProvider() : null;
|
|
301
308
|
addOrUpdateLog({
|
|
302
309
|
id,
|
|
303
310
|
url,
|
|
@@ -305,6 +312,7 @@ export const addAxiosInterceptors = (axiosInstance) => {
|
|
|
305
312
|
startTime: start,
|
|
306
313
|
caller,
|
|
307
314
|
client: 'axios',
|
|
315
|
+
routeInfo: currentRoute || undefined,
|
|
308
316
|
request: method === "GET" ? undefined : parseRequestData(config.data),
|
|
309
317
|
requestHeaders: normaliseHeaders(config.headers),
|
|
310
318
|
});
|
|
@@ -37,11 +37,11 @@ export declare const getRegisteredMMKVInstanceIds: () => string[];
|
|
|
37
37
|
*/
|
|
38
38
|
export declare const calculateByteSize: (str: string) => number;
|
|
39
39
|
/**
|
|
40
|
-
* Determine type and format value safely
|
|
40
|
+
* Determine type and format value safely without blocking JS thread
|
|
41
41
|
*/
|
|
42
42
|
export declare const analyzeStorageValue: (rawVal: any) => {
|
|
43
43
|
strVal: string;
|
|
44
|
-
|
|
44
|
+
parsedValue?: any;
|
|
45
45
|
type: "json" | "string" | "number" | "boolean" | "null";
|
|
46
46
|
byteSize: number;
|
|
47
47
|
};
|
|
@@ -255,34 +255,38 @@ export const calculateByteSize = (str) => {
|
|
|
255
255
|
return str.length * 2;
|
|
256
256
|
};
|
|
257
257
|
/**
|
|
258
|
-
* Determine type and format value safely
|
|
258
|
+
* Determine type and format value safely without blocking JS thread
|
|
259
259
|
*/
|
|
260
260
|
export const analyzeStorageValue = (rawVal) => {
|
|
261
261
|
if (rawVal === null || rawVal === undefined) {
|
|
262
262
|
return { strVal: 'null', type: 'null', byteSize: 4 };
|
|
263
263
|
}
|
|
264
264
|
const strVal = typeof rawVal === 'string' ? rawVal : String(rawVal);
|
|
265
|
-
const byteSize =
|
|
265
|
+
const byteSize = strVal.length * 2;
|
|
266
266
|
if (typeof rawVal === 'number') {
|
|
267
|
-
return { strVal,
|
|
267
|
+
return { strVal, parsedValue: rawVal, type: 'number', byteSize };
|
|
268
268
|
}
|
|
269
269
|
if (typeof rawVal === 'boolean') {
|
|
270
|
-
return { strVal,
|
|
270
|
+
return { strVal, parsedValue: rawVal, type: 'boolean', byteSize };
|
|
271
271
|
}
|
|
272
272
|
const trimmed = strVal.trim();
|
|
273
273
|
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
274
274
|
(trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
275
|
+
// For small/medium JSON, parse eagerly; for huge payloads (>10KB), parse lazily on expand
|
|
276
|
+
if (trimmed.length < 10000) {
|
|
277
|
+
try {
|
|
278
|
+
const parsed = JSON.parse(trimmed);
|
|
279
|
+
return { strVal, parsedValue: parsed, type: 'json', byteSize };
|
|
280
|
+
}
|
|
281
|
+
catch { }
|
|
278
282
|
}
|
|
279
|
-
|
|
283
|
+
return { strVal, type: 'json', byteSize };
|
|
280
284
|
}
|
|
281
285
|
if (trimmed === 'true' || trimmed === 'false') {
|
|
282
|
-
return { strVal,
|
|
286
|
+
return { strVal, parsedValue: trimmed === 'true', type: 'boolean', byteSize };
|
|
283
287
|
}
|
|
284
288
|
if (!isNaN(Number(trimmed)) && trimmed !== '') {
|
|
285
|
-
return { strVal,
|
|
289
|
+
return { strVal, parsedValue: Number(trimmed), type: 'number', byteSize };
|
|
286
290
|
}
|
|
287
291
|
return { strVal, type: 'string', byteSize };
|
|
288
292
|
};
|
|
@@ -307,7 +311,7 @@ export const fetchStorageEntries = async (driver, instanceId) => {
|
|
|
307
311
|
entries.push({
|
|
308
312
|
key: k,
|
|
309
313
|
value: analysis.strVal,
|
|
310
|
-
parsedValue: analysis.
|
|
314
|
+
parsedValue: analysis.parsedValue,
|
|
311
315
|
type: analysis.type,
|
|
312
316
|
byteSize: analysis.byteSize,
|
|
313
317
|
});
|
|
@@ -320,7 +324,7 @@ export const fetchStorageEntries = async (driver, instanceId) => {
|
|
|
320
324
|
entries.push({
|
|
321
325
|
key: k,
|
|
322
326
|
value: analysis.strVal,
|
|
323
|
-
parsedValue: analysis.
|
|
327
|
+
parsedValue: analysis.parsedValue,
|
|
324
328
|
type: analysis.type,
|
|
325
329
|
byteSize: analysis.byteSize,
|
|
326
330
|
});
|
|
@@ -361,7 +365,7 @@ export const fetchStorageEntries = async (driver, instanceId) => {
|
|
|
361
365
|
entries.push({
|
|
362
366
|
key: k,
|
|
363
367
|
value: analysis.strVal,
|
|
364
|
-
parsedValue: analysis.
|
|
368
|
+
parsedValue: analysis.parsedValue,
|
|
365
369
|
type: analysis.type,
|
|
366
370
|
byteSize: analysis.byteSize,
|
|
367
371
|
});
|
|
@@ -15,6 +15,7 @@ export declare const getFetchCommand: (log: NetworkLog) => string;
|
|
|
15
15
|
export declare const deduplicateLogs: (raw: NetworkLog[]) => NetworkLog[];
|
|
16
16
|
export declare const escapeRegex: (str: string) => string;
|
|
17
17
|
export declare const getNavigationInfo: (state: any, path?: string[]) => RouteInfo;
|
|
18
|
+
export declare const getLogPageName: (log: NetworkLog, routeInfo?: RouteInfo | null) => string;
|
|
18
19
|
export declare const flattenObject: (obj: any, prefix?: string) => Record<string, any>;
|
|
19
20
|
export declare const getDiff: (oldObj: any, newObj: any) => DiffResult[];
|
|
20
21
|
export declare const formatDisplayUrl: (url: string) => string;
|
|
@@ -24,6 +25,11 @@ export declare const isAllValuesEmpty: (obj: Record<string, any>) => boolean;
|
|
|
24
25
|
export declare const formatDateTimeToAnalytics: (ts: number) => string;
|
|
25
26
|
export declare const getBundleIdentifier: () => string;
|
|
26
27
|
export declare const getAppName: () => string;
|
|
28
|
+
export declare const getAppVersionAndBuild: () => {
|
|
29
|
+
version: string;
|
|
30
|
+
build: string;
|
|
31
|
+
formatted: string;
|
|
32
|
+
};
|
|
27
33
|
export declare const handleOpenExternalLink: (url: string) => void;
|
|
28
34
|
/** Formats a timestamp as HH:MM:SS (no milliseconds). */
|
|
29
35
|
export declare const formatTimeShort: (ts: number) => string;
|
|
@@ -177,16 +177,33 @@ export const escapeRegex = (str) => {
|
|
|
177
177
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
178
178
|
};
|
|
179
179
|
export const getNavigationInfo = (state, path = []) => {
|
|
180
|
-
if (!state
|
|
180
|
+
if (!state) {
|
|
181
181
|
return {
|
|
182
|
-
path: path.length > 0 ? path.join(' ➔ ') : '
|
|
182
|
+
path: path.length > 0 ? path.join(' ➔ ') : '',
|
|
183
183
|
params: null,
|
|
184
184
|
};
|
|
185
185
|
}
|
|
186
|
-
|
|
186
|
+
if (state.name && !state.routes) {
|
|
187
|
+
const fullPath = path.length > 0 ? [...path, state.name].join(' ➔ ') : state.name;
|
|
188
|
+
return { path: fullPath, params: state.params || null };
|
|
189
|
+
}
|
|
190
|
+
if (!state.routes ||
|
|
191
|
+
!Array.isArray(state.routes) ||
|
|
192
|
+
state.routes.length === 0) {
|
|
193
|
+
return {
|
|
194
|
+
path: path.length > 0 ? path.join(' ➔ ') : '',
|
|
195
|
+
params: null,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const index = typeof state.index === 'number' &&
|
|
199
|
+
state.index >= 0 &&
|
|
200
|
+
state.index < state.routes.length
|
|
201
|
+
? state.index
|
|
202
|
+
: 0;
|
|
203
|
+
const route = state.routes[index];
|
|
187
204
|
if (!route) {
|
|
188
205
|
return {
|
|
189
|
-
path: path.length > 0 ? path.join(' ➔ ') : '
|
|
206
|
+
path: path.length > 0 ? path.join(' ➔ ') : '',
|
|
190
207
|
params: null,
|
|
191
208
|
};
|
|
192
209
|
}
|
|
@@ -194,9 +211,87 @@ export const getNavigationInfo = (state, path = []) => {
|
|
|
194
211
|
if (route?.state) {
|
|
195
212
|
return getNavigationInfo(route.state, currentPath);
|
|
196
213
|
}
|
|
197
|
-
const resolved = currentPath.length > 0 ? currentPath.join(' ➔ ') : '
|
|
214
|
+
const resolved = currentPath.length > 0 ? currentPath.join(' ➔ ') : route.name || '';
|
|
198
215
|
return { path: resolved, params: route.params || null };
|
|
199
216
|
};
|
|
217
|
+
export const getLogPageName = (log, routeInfo) => {
|
|
218
|
+
// 1. If routeInfo has a valid path, resolve the leaf screen name
|
|
219
|
+
const effectiveRoute = routeInfo || log?.routeInfo;
|
|
220
|
+
if (effectiveRoute &&
|
|
221
|
+
effectiveRoute.path &&
|
|
222
|
+
effectiveRoute.path !== 'Navigators') {
|
|
223
|
+
const parts = effectiveRoute.path
|
|
224
|
+
.split(' ➔ ')
|
|
225
|
+
.map((s) => s.trim())
|
|
226
|
+
.filter(Boolean);
|
|
227
|
+
if (parts.length > 0) {
|
|
228
|
+
return parts[parts.length - 1]; // Leaf screen name, e.g. "HomeScreen", "ProfileScreen"
|
|
229
|
+
}
|
|
230
|
+
return effectiveRoute.path;
|
|
231
|
+
}
|
|
232
|
+
// 2. Extract screen / component name from log.caller stack trace
|
|
233
|
+
if (log.caller && log.caller !== 'Unknown') {
|
|
234
|
+
try {
|
|
235
|
+
const match = log.caller.match(/([^/\\]+)\.(tsx|jsx|ts|js)/i);
|
|
236
|
+
if (match && match[1]) {
|
|
237
|
+
const file = match[1];
|
|
238
|
+
const ignored = [
|
|
239
|
+
'networklogger',
|
|
240
|
+
'fetch',
|
|
241
|
+
'axios',
|
|
242
|
+
'apiclient',
|
|
243
|
+
'request',
|
|
244
|
+
'http',
|
|
245
|
+
'index',
|
|
246
|
+
'nativeinspector',
|
|
247
|
+
'inappinspector',
|
|
248
|
+
'bundleanalyzer',
|
|
249
|
+
];
|
|
250
|
+
if (!ignored.includes(file.toLowerCase())) {
|
|
251
|
+
return file;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (e) { }
|
|
256
|
+
}
|
|
257
|
+
// 3. Fallback to API resource / endpoint category from URL
|
|
258
|
+
if (log.url) {
|
|
259
|
+
try {
|
|
260
|
+
const cleanUrl = log.url.split('?')[0].split('#')[0];
|
|
261
|
+
const withoutProto = cleanUrl.replace(/^https?:\/\//i, '');
|
|
262
|
+
const slashIdx = withoutProto.indexOf('/');
|
|
263
|
+
if (slashIdx !== -1) {
|
|
264
|
+
const pathSegments = withoutProto
|
|
265
|
+
.substring(slashIdx + 1)
|
|
266
|
+
.split('/')
|
|
267
|
+
.filter(Boolean);
|
|
268
|
+
const ignoredSegments = [
|
|
269
|
+
'api',
|
|
270
|
+
'v1',
|
|
271
|
+
'v2',
|
|
272
|
+
'v3',
|
|
273
|
+
'v4',
|
|
274
|
+
'rest',
|
|
275
|
+
'graphql',
|
|
276
|
+
'json',
|
|
277
|
+
'data',
|
|
278
|
+
'service',
|
|
279
|
+
'app',
|
|
280
|
+
];
|
|
281
|
+
const meaningful = pathSegments.filter(s => !ignoredSegments.includes(s.toLowerCase()));
|
|
282
|
+
if (meaningful.length > 0) {
|
|
283
|
+
const seg = meaningful[0];
|
|
284
|
+
return seg.charAt(0).toUpperCase() + seg.slice(1);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const host = withoutProto.split('/')[0];
|
|
288
|
+
if (host)
|
|
289
|
+
return host;
|
|
290
|
+
}
|
|
291
|
+
catch (e) { }
|
|
292
|
+
}
|
|
293
|
+
return 'General';
|
|
294
|
+
};
|
|
200
295
|
export const flattenObject = (obj, prefix = '') => {
|
|
201
296
|
let result = {};
|
|
202
297
|
if (typeof obj === 'object' && obj !== null) {
|
|
@@ -351,6 +446,99 @@ export const getAppName = () => {
|
|
|
351
446
|
}
|
|
352
447
|
return 'App';
|
|
353
448
|
};
|
|
449
|
+
export const getAppVersionAndBuild = () => {
|
|
450
|
+
let version = '';
|
|
451
|
+
let build = '';
|
|
452
|
+
// 1. Try ExpoApplication / ExponentConstants
|
|
453
|
+
const ExpoApplication = NativeModules.ExpoApplication;
|
|
454
|
+
if (ExpoApplication) {
|
|
455
|
+
if (typeof ExpoApplication.nativeAppVersion === 'string') {
|
|
456
|
+
version = ExpoApplication.nativeAppVersion;
|
|
457
|
+
}
|
|
458
|
+
if (typeof ExpoApplication.nativeBuildVersion === 'string') {
|
|
459
|
+
build = ExpoApplication.nativeBuildVersion;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
const ExponentConstants = NativeModules.ExponentConstants;
|
|
463
|
+
if (ExponentConstants) {
|
|
464
|
+
const manifest = ExponentConstants.manifest || ExponentConstants.expoConfig;
|
|
465
|
+
if (manifest) {
|
|
466
|
+
if (!version && manifest.version) {
|
|
467
|
+
version = String(manifest.version);
|
|
468
|
+
}
|
|
469
|
+
if (!build) {
|
|
470
|
+
if (manifest.ios?.buildNumber) {
|
|
471
|
+
build = String(manifest.ios.buildNumber);
|
|
472
|
+
}
|
|
473
|
+
else if (manifest.android?.versionCode) {
|
|
474
|
+
build = String(manifest.android.versionCode);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
// 2. Try react-native-device-info
|
|
480
|
+
const RNDeviceInfo = NativeModules.RNDeviceInfo;
|
|
481
|
+
if (RNDeviceInfo) {
|
|
482
|
+
if (!version) {
|
|
483
|
+
if (typeof RNDeviceInfo.appVersion === 'string') {
|
|
484
|
+
version = RNDeviceInfo.appVersion;
|
|
485
|
+
}
|
|
486
|
+
else if (typeof RNDeviceInfo.getVersion === 'function') {
|
|
487
|
+
try {
|
|
488
|
+
version = RNDeviceInfo.getVersion();
|
|
489
|
+
}
|
|
490
|
+
catch (e) { }
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (!build) {
|
|
494
|
+
if (typeof RNDeviceInfo.buildNumber === 'string') {
|
|
495
|
+
build = RNDeviceInfo.buildNumber;
|
|
496
|
+
}
|
|
497
|
+
else if (typeof RNDeviceInfo.getBuildNumber === 'function') {
|
|
498
|
+
try {
|
|
499
|
+
build = RNDeviceInfo.getBuildNumber();
|
|
500
|
+
}
|
|
501
|
+
catch (e) { }
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// 3. Try PlatformConstants / Platform.constants
|
|
506
|
+
const constants = Platform.constants || NativeModules.PlatformConstants;
|
|
507
|
+
if (constants) {
|
|
508
|
+
if (!version &&
|
|
509
|
+
constants.Version &&
|
|
510
|
+
typeof constants.Version === 'string' &&
|
|
511
|
+
constants.Version.includes('.')) {
|
|
512
|
+
version = constants.Version;
|
|
513
|
+
}
|
|
514
|
+
if (!version && constants.reactNativeVersion) {
|
|
515
|
+
const rnv = constants.reactNativeVersion;
|
|
516
|
+
if (rnv.major !== undefined) {
|
|
517
|
+
version = `${rnv.major}.${rnv.minor}.${rnv.patch}`;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// 4. Try Android AppInfo
|
|
522
|
+
const AppInfo = NativeModules.AppInfo;
|
|
523
|
+
if (AppInfo) {
|
|
524
|
+
if (!version && typeof AppInfo.versionName === 'string') {
|
|
525
|
+
version = AppInfo.versionName;
|
|
526
|
+
}
|
|
527
|
+
if (!build && typeof AppInfo.versionCode === 'string') {
|
|
528
|
+
build = AppInfo.versionCode;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
// Fallbacks
|
|
532
|
+
if (!version)
|
|
533
|
+
version = '1.0.0';
|
|
534
|
+
if (!build)
|
|
535
|
+
build = '1';
|
|
536
|
+
return {
|
|
537
|
+
version,
|
|
538
|
+
build,
|
|
539
|
+
formatted: `${version} (${build})`,
|
|
540
|
+
};
|
|
541
|
+
};
|
|
354
542
|
export const handleOpenExternalLink = (url) => {
|
|
355
543
|
if (!url)
|
|
356
544
|
return;
|