react-native-inapp-inspector 2.2.8 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +22 -7
- 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/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 +19 -3
- package/dist/commonjs/components/JsonViewer.js +2 -0
- package/dist/commonjs/components/MetaAccordion.js +15 -3
- 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/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 +80 -11
- package/dist/commonjs/native/NativeInspector.js +13 -0
- package/dist/commonjs/native/NativeNetworkInspector.d.ts +1 -1
- 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 +1 -0
- 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/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 +19 -3
- package/dist/esm/components/JsonViewer.js +2 -0
- package/dist/esm/components/MetaAccordion.js +15 -3
- 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/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 +68 -12
- package/dist/esm/native/NativeInspector.js +13 -0
- package/dist/esm/native/NativeNetworkInspector.d.ts +1 -1
- 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 +1 -0
- package/ios/NetworkInspectorModule.mm +11 -1
- 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/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 +21 -3
- package/src/components/JsonViewer.tsx +2 -0
- package/src/components/MetaAccordion.tsx +18 -3
- package/src/constants/version.ts +1 -1
- package/src/customHooks/networkLogger.ts +15 -0
- 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 +89 -11
- package/src/native/NativeInspector.ts +13 -0
- package/src/native/NativeNetworkInspector.ts +1 -1
- package/src/styles/AppColors.ts +9 -0
- package/src/styles/AppFonts.ts +16 -0
- package/src/types/interfaces.ts +1 -0
|
@@ -245,9 +245,25 @@ export const StorageTab = React.memo(() => {
|
|
|
245
245
|
<View style={styles.searchBar}>
|
|
246
246
|
<SearchIcon size={14} color={AppColors.grayTextWeak}/>
|
|
247
247
|
<TextInput value={search} onChangeText={setSearch} placeholder={`Search ${entries.length} keys & values...`} placeholderTextColor={AppColors.grayTextWeak} style={styles.searchInput} clearButtonMode="while-editing"/>
|
|
248
|
-
{search.length > 0 && (<
|
|
249
|
-
<
|
|
250
|
-
|
|
248
|
+
{search.length > 0 && (<View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
|
|
249
|
+
<View style={{
|
|
250
|
+
backgroundColor: `${AppColors.purple}20`,
|
|
251
|
+
borderRadius: 10,
|
|
252
|
+
paddingHorizontal: 6,
|
|
253
|
+
paddingVertical: 1.5,
|
|
254
|
+
}}>
|
|
255
|
+
<Text style={{
|
|
256
|
+
color: AppColors.purple,
|
|
257
|
+
fontSize: 9.5,
|
|
258
|
+
fontFamily: AppFonts.interBold,
|
|
259
|
+
}}>
|
|
260
|
+
{filteredEntries.length}
|
|
261
|
+
</Text>
|
|
262
|
+
</View>
|
|
263
|
+
<TouchableScale onPress={() => setSearch('')} hitSlop={8}>
|
|
264
|
+
<ClearIcon size={13} color={AppColors.grayTextWeak}/>
|
|
265
|
+
</TouchableScale>
|
|
266
|
+
</View>)}
|
|
251
267
|
</View>
|
|
252
268
|
|
|
253
269
|
{/* Action Buttons: Add Key & Refresh */}
|
|
@@ -5,6 +5,7 @@ import { useTranslation } from '../i18n';
|
|
|
5
5
|
import TreeNode from './TreeNode';
|
|
6
6
|
import SegmentedTabs from './SegmentedTabs';
|
|
7
7
|
import HighlightText from './HighlightText';
|
|
8
|
+
import CopyButton from './CopyButton';
|
|
8
9
|
import { PrettyIcon, RawIcon, TableIcon } from './NetworkIcons';
|
|
9
10
|
// Styles
|
|
10
11
|
import { AppColors } from '../styles/AppColors';
|
|
@@ -151,6 +152,7 @@ const JsonViewer = React.memo(({ data, search, forceOpen, defaultExpandDepth = 1
|
|
|
151
152
|
icon: (isActive) => (<TableIcon color={isActive ? AppColors.white : AppColors.slate400} size={12}/>),
|
|
152
153
|
},
|
|
153
154
|
]} activeKey={mode} onChange={key => setMode(key)}/>
|
|
155
|
+
<CopyButton value={data} label="JSON"/>
|
|
154
156
|
</View>)}
|
|
155
157
|
|
|
156
158
|
{/* ── Main View Content Area (Royal Monospace Theme) ── */}
|
|
@@ -11,6 +11,7 @@ import { getDurationColor, handleOpenExternalLink } from '../helpers';
|
|
|
11
11
|
import { ChevronIcon, CalendarIcon, StatusIcon, ClockIcon, SizeIcon, TerminalIcon, GlobeIcon, } from './NetworkIcons';
|
|
12
12
|
// Components
|
|
13
13
|
import TouchableScale from './TouchableScale';
|
|
14
|
+
import CopyButton from './CopyButton';
|
|
14
15
|
// Stylesheet
|
|
15
16
|
import { AppColors } from '../styles/AppColors';
|
|
16
17
|
import { AppFonts } from '../styles/AppFonts';
|
|
@@ -60,9 +61,20 @@ const MetaAccordion = ({ status, statusColor, duration, size, triggeredAt, metho
|
|
|
60
61
|
<Pressable onPress={toggleOpen} hitSlop={12}>
|
|
61
62
|
<View style={styles.metaHeader}>
|
|
62
63
|
<Text style={styles.metaTitle}>Metadata</Text>
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
64
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
65
|
+
<CopyButton value={{
|
|
66
|
+
status: status ?? 0,
|
|
67
|
+
duration: duration != null ? `${duration} ms` : '—',
|
|
68
|
+
size: size || '—',
|
|
69
|
+
triggeredAt: triggeredAt || '',
|
|
70
|
+
method: method || 'GET',
|
|
71
|
+
contentType: contentType || 'application/json',
|
|
72
|
+
url: url || '',
|
|
73
|
+
}} label="Metadata"/>
|
|
74
|
+
<Animated.View style={chevronStyle}>
|
|
75
|
+
<ChevronIcon color={AppColors.grayTextWeak} size={14}/>
|
|
76
|
+
</Animated.View>
|
|
77
|
+
</View>
|
|
66
78
|
</View>
|
|
67
79
|
</Pressable>
|
|
68
80
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const LIB_VERSION = "2.
|
|
1
|
+
export declare const LIB_VERSION = "2.3.0";
|
|
@@ -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
|
});
|
|
@@ -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;
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
export function matchNetworkLogQuery(log, searchQuery, routePath, options) {
|
|
6
6
|
if (!searchQuery || searchQuery.trim().length === 0)
|
|
7
7
|
return true;
|
|
8
|
-
|
|
8
|
+
// Split tokens by spaces or commas while respecting quotes
|
|
9
|
+
const rawTokens = searchQuery.trim().match(/(?:[^\s,"']+|"[^"]*"|'[^']*')+/g) || [];
|
|
9
10
|
if (rawTokens.length === 0)
|
|
10
11
|
return true;
|
|
11
12
|
const isCaseSensitive = Boolean(options?.isCaseSensitive);
|
|
@@ -15,24 +16,35 @@ export function matchNetworkLogQuery(log, searchQuery, routePath, options) {
|
|
|
15
16
|
const urlRaw = log.url || '';
|
|
16
17
|
const statusNum = typeof log.status === 'number'
|
|
17
18
|
? log.status
|
|
18
|
-
:
|
|
19
|
-
|
|
19
|
+
: log.status != null
|
|
20
|
+
? parseInt(String(log.status), 10)
|
|
21
|
+
: null;
|
|
22
|
+
const statusStr = log.status != null ? String(log.status) : 'pending';
|
|
20
23
|
const durationNum = log.duration || 0;
|
|
21
24
|
const clientRaw = log.client || '';
|
|
25
|
+
const callerRaw = log.caller || '';
|
|
22
26
|
const reqRaw = typeof log.request === 'string'
|
|
23
27
|
? log.request
|
|
24
|
-
:
|
|
28
|
+
: log.request != null
|
|
29
|
+
? JSON.stringify(log.request)
|
|
30
|
+
: '';
|
|
25
31
|
const resRaw = typeof log.response === 'string'
|
|
26
32
|
? log.response
|
|
27
|
-
:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
33
|
+
: log.response != null
|
|
34
|
+
? JSON.stringify(log.response)
|
|
35
|
+
: '';
|
|
36
|
+
const reqHeadersRaw = log.requestHeaders
|
|
37
|
+
? JSON.stringify(log.requestHeaders)
|
|
38
|
+
: '';
|
|
39
|
+
const resHeadersRaw = log.responseHeaders
|
|
40
|
+
? JSON.stringify(log.responseHeaders)
|
|
41
|
+
: '';
|
|
42
|
+
const pathRaw = routePath || log?.routeInfo?.path || '';
|
|
31
43
|
// Construct search corpus based on active scope
|
|
32
44
|
const getScopedCorpus = (caseSens) => {
|
|
33
45
|
let parts = [];
|
|
34
46
|
if (scope === 'url') {
|
|
35
|
-
parts = [methodRaw, urlRaw, pathRaw];
|
|
47
|
+
parts = [methodRaw, urlRaw, pathRaw, callerRaw];
|
|
36
48
|
}
|
|
37
49
|
else if (scope === 'reqBody') {
|
|
38
50
|
parts = [reqRaw];
|
|
@@ -49,6 +61,7 @@ export function matchNetworkLogQuery(log, searchQuery, routePath, options) {
|
|
|
49
61
|
urlRaw,
|
|
50
62
|
statusStr,
|
|
51
63
|
pathRaw,
|
|
64
|
+
callerRaw,
|
|
52
65
|
clientRaw,
|
|
53
66
|
reqRaw,
|
|
54
67
|
resRaw,
|
|
@@ -63,6 +76,7 @@ export function matchNetworkLogQuery(log, searchQuery, routePath, options) {
|
|
|
63
76
|
const methodStr = methodRaw.toLowerCase();
|
|
64
77
|
const urlStr = urlRaw.toLowerCase();
|
|
65
78
|
const clientStr = clientRaw.toLowerCase();
|
|
79
|
+
const callerStr = callerRaw.toLowerCase();
|
|
66
80
|
const reqStr = reqRaw.toLowerCase();
|
|
67
81
|
const resStr = resRaw.toLowerCase();
|
|
68
82
|
const reqHeadersStr = reqHeadersRaw.toLowerCase();
|
|
@@ -89,24 +103,28 @@ export function matchNetworkLogQuery(log, searchQuery, routePath, options) {
|
|
|
89
103
|
// 2. status:<VAL> or s:<VAL>
|
|
90
104
|
if (lowerToken.startsWith('status:') || lowerToken.startsWith('s:')) {
|
|
91
105
|
const val = lowerToken.replace(/^(status:|s:)/, '').trim();
|
|
92
|
-
if (val
|
|
106
|
+
if (val === 'pending' || val === 'loading') {
|
|
107
|
+
if (log.status != null)
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
else if (val.startsWith('>=')) {
|
|
93
111
|
const threshold = parseInt(val.slice(2), 10);
|
|
94
|
-
if (isNaN(threshold) || statusNum < threshold)
|
|
112
|
+
if (isNaN(threshold) || statusNum === null || statusNum < threshold)
|
|
95
113
|
return false;
|
|
96
114
|
}
|
|
97
115
|
else if (val.startsWith('>')) {
|
|
98
116
|
const threshold = parseInt(val.slice(1), 10);
|
|
99
|
-
if (isNaN(threshold) || statusNum <= threshold)
|
|
117
|
+
if (isNaN(threshold) || statusNum === null || statusNum <= threshold)
|
|
100
118
|
return false;
|
|
101
119
|
}
|
|
102
120
|
else if (val.startsWith('<=')) {
|
|
103
121
|
const threshold = parseInt(val.slice(2), 10);
|
|
104
|
-
if (isNaN(threshold) || statusNum > threshold)
|
|
122
|
+
if (isNaN(threshold) || statusNum === null || statusNum > threshold)
|
|
105
123
|
return false;
|
|
106
124
|
}
|
|
107
125
|
else if (val.startsWith('<')) {
|
|
108
126
|
const threshold = parseInt(val.slice(1), 10);
|
|
109
|
-
if (isNaN(threshold) || statusNum >= threshold)
|
|
127
|
+
if (isNaN(threshold) || statusNum === null || statusNum >= threshold)
|
|
110
128
|
return false;
|
|
111
129
|
}
|
|
112
130
|
else if (val.endsWith('xx') || val.endsWith('x')) {
|
|
@@ -133,15 +151,19 @@ export function matchNetworkLogQuery(log, searchQuery, routePath, options) {
|
|
|
133
151
|
flag === 'failed' ||
|
|
134
152
|
flag === 'err' ||
|
|
135
153
|
flag === 'fail') {
|
|
136
|
-
const isErr = log.status === 0 ||
|
|
154
|
+
const isErr = log.status === 0 || (statusNum !== null && statusNum >= 400);
|
|
137
155
|
if (!isErr)
|
|
138
156
|
return false;
|
|
139
157
|
}
|
|
140
158
|
else if (flag === 'success' || flag === 'ok' || flag === '2xx') {
|
|
141
|
-
const isSuccess = statusNum >= 200 && statusNum < 300;
|
|
159
|
+
const isSuccess = statusNum !== null && statusNum >= 200 && statusNum < 300;
|
|
142
160
|
if (!isSuccess)
|
|
143
161
|
return false;
|
|
144
162
|
}
|
|
163
|
+
else if (flag === 'pending' || flag === 'loading') {
|
|
164
|
+
if (log.status != null)
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
145
167
|
else if (flag === 'slow') {
|
|
146
168
|
if (durationNum < 1000)
|
|
147
169
|
return false;
|
|
@@ -172,6 +194,12 @@ export function matchNetworkLogQuery(log, searchQuery, routePath, options) {
|
|
|
172
194
|
if (!isJson)
|
|
173
195
|
return false;
|
|
174
196
|
}
|
|
197
|
+
else if (flag === 'image') {
|
|
198
|
+
const isImg = resHeadersStr.includes('image/') ||
|
|
199
|
+
/\.(png|jpe?g|gif|webp|svg)/i.test(urlStr);
|
|
200
|
+
if (!isImg)
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
175
203
|
continue;
|
|
176
204
|
}
|
|
177
205
|
// 4. slow:<DURATION> or dur:<DURATION> or duration:<DURATION>
|
|
@@ -233,10 +261,16 @@ export function matchNetworkLogQuery(log, searchQuery, routePath, options) {
|
|
|
233
261
|
return false;
|
|
234
262
|
continue;
|
|
235
263
|
}
|
|
236
|
-
// 7. path:<PATH> or p:<PATH>
|
|
237
|
-
if (lowerToken.startsWith('path:') ||
|
|
238
|
-
|
|
239
|
-
|
|
264
|
+
// 7. path:<PATH> or p:<PATH> or page:<PAGE>
|
|
265
|
+
if (lowerToken.startsWith('path:') ||
|
|
266
|
+
lowerToken.startsWith('p:') ||
|
|
267
|
+
lowerToken.startsWith('page:')) {
|
|
268
|
+
const targetPath = lowerToken
|
|
269
|
+
.replace(/^(path:|p:|page:)/, '')
|
|
270
|
+
.trim();
|
|
271
|
+
if (!urlStr.includes(targetPath) &&
|
|
272
|
+
!pathStr.includes(targetPath) &&
|
|
273
|
+
!callerStr.includes(targetPath)) {
|
|
240
274
|
return false;
|
|
241
275
|
}
|
|
242
276
|
continue;
|
|
@@ -279,6 +313,14 @@ export function matchNetworkLogQuery(log, searchQuery, routePath, options) {
|
|
|
279
313
|
else {
|
|
280
314
|
const queryTerm = isCaseSensitive ? token : lowerToken;
|
|
281
315
|
if (!targetCorpus.includes(queryTerm)) {
|
|
316
|
+
// Special case: if user typed "error" / "failed", check error status
|
|
317
|
+
if (queryTerm === 'error' ||
|
|
318
|
+
queryTerm === 'failed' ||
|
|
319
|
+
queryTerm === 'fail') {
|
|
320
|
+
const isErr = log.status === 0 || (statusNum !== null && statusNum >= 400);
|
|
321
|
+
if (isErr)
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
282
324
|
return false;
|
|
283
325
|
}
|
|
284
326
|
}
|
package/dist/esm/i18n/index.d.ts
CHANGED
|
@@ -4,9 +4,17 @@ type TranslationParams = Record<string, any>;
|
|
|
4
4
|
* Standalone zero-dependency translation function
|
|
5
5
|
*/
|
|
6
6
|
export declare function t(key: string, optionsOrFallback?: TranslationParams | string): string;
|
|
7
|
+
export declare function setLanguage(lang: string): void;
|
|
8
|
+
export declare function addTranslations(lang: string, translations: Record<string, any>): void;
|
|
9
|
+
export declare function setTranslations(translations: Record<string, any>, lang?: string): void;
|
|
7
10
|
export declare const i18n: {
|
|
8
11
|
t: typeof t;
|
|
9
12
|
language: string;
|
|
13
|
+
changeLanguage: typeof setLanguage;
|
|
14
|
+
addResourceBundle: (lng: string, _ns: string, resources: any) => void;
|
|
15
|
+
setLanguage: typeof setLanguage;
|
|
16
|
+
addTranslations: typeof addTranslations;
|
|
17
|
+
setTranslations: typeof setTranslations;
|
|
10
18
|
};
|
|
11
19
|
/**
|
|
12
20
|
* React hook compatible with react-i18next useTranslation()
|
|
@@ -16,6 +24,11 @@ export declare function useTranslation(): {
|
|
|
16
24
|
i18n: {
|
|
17
25
|
t: typeof t;
|
|
18
26
|
language: string;
|
|
27
|
+
changeLanguage: typeof setLanguage;
|
|
28
|
+
addResourceBundle: (lng: string, _ns: string, resources: any) => void;
|
|
29
|
+
setLanguage: typeof setLanguage;
|
|
30
|
+
addTranslations: typeof addTranslations;
|
|
31
|
+
setTranslations: typeof setTranslations;
|
|
19
32
|
};
|
|
20
33
|
};
|
|
21
34
|
/**
|
package/dist/esm/i18n/index.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
2
|
+
import enLocale from './locales/en.json';
|
|
3
|
+
const localeRegistry = {
|
|
4
|
+
en: enLocale,
|
|
5
|
+
};
|
|
6
|
+
let currentLanguage = 'en';
|
|
3
7
|
/**
|
|
4
8
|
* Lightweight nested lookup for translation keys (e.g. "network.headers")
|
|
5
9
|
*/
|
|
@@ -29,7 +33,11 @@ function interpolate(template, params) {
|
|
|
29
33
|
* Standalone zero-dependency translation function
|
|
30
34
|
*/
|
|
31
35
|
export function t(key, optionsOrFallback) {
|
|
32
|
-
const
|
|
36
|
+
const activeBundle = localeRegistry[currentLanguage] || localeRegistry.en;
|
|
37
|
+
let raw = lookupKey(activeBundle, key);
|
|
38
|
+
if (raw === undefined && currentLanguage !== 'en') {
|
|
39
|
+
raw = lookupKey(localeRegistry.en, key);
|
|
40
|
+
}
|
|
33
41
|
if (typeof raw === 'string') {
|
|
34
42
|
if (typeof optionsOrFallback === 'object' && optionsOrFallback !== null) {
|
|
35
43
|
return interpolate(raw, optionsOrFallback);
|
|
@@ -41,9 +49,31 @@ export function t(key, optionsOrFallback) {
|
|
|
41
49
|
}
|
|
42
50
|
return key;
|
|
43
51
|
}
|
|
52
|
+
export function setLanguage(lang) {
|
|
53
|
+
if (typeof lang === 'string' && lang.trim()) {
|
|
54
|
+
currentLanguage = lang.trim();
|
|
55
|
+
i18n.language = currentLanguage;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function addTranslations(lang, translations) {
|
|
59
|
+
if (!lang || !translations || typeof translations !== 'object')
|
|
60
|
+
return;
|
|
61
|
+
localeRegistry[lang] = {
|
|
62
|
+
...(localeRegistry[lang] || {}),
|
|
63
|
+
...translations,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function setTranslations(translations, lang = 'en') {
|
|
67
|
+
addTranslations(lang, translations);
|
|
68
|
+
}
|
|
44
69
|
export const i18n = {
|
|
45
70
|
t,
|
|
46
|
-
language:
|
|
71
|
+
language: currentLanguage,
|
|
72
|
+
changeLanguage: setLanguage,
|
|
73
|
+
addResourceBundle: (lng, _ns, resources) => addTranslations(lng, resources),
|
|
74
|
+
setLanguage,
|
|
75
|
+
addTranslations,
|
|
76
|
+
setTranslations,
|
|
47
77
|
};
|
|
48
78
|
/**
|
|
49
79
|
* React hook compatible with react-i18next useTranslation()
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -23,3 +23,6 @@ export { setMaxPerformanceEventsLimit, getMaxPerformanceEventsLimit, prunePerfor
|
|
|
23
23
|
export { BrandSquareIcon, BrandCircleIcon, } from './components/NetworkIcons';
|
|
24
24
|
export { connectAsyncStorage, connectMMKV, isAsyncStorageConnected, isMMKVConnected, getRegisteredMMKVInstanceIds, fetchStorageEntries, setStorageEntry, removeStorageEntry, clearStorageDriver, subscribeToStorageChanges, type StorageEntry, type StorageDriver, } from './customHooks/storageInspector';
|
|
25
25
|
export { ActiveTab, Method, StatusFilter, SortOrder, LocalFilter, ModalAnimationType, SettingsPage, SettingsSubTab, LogFilter, ConsoleLogType, AnalyticsEventSource, GAEventCategory, StackFrameType, DiffResultType, BundleSubTab, PerformanceSubTab, CrashType, CrashExportFormat, CrashDetailSubTab, CrashFilterType, BreadcrumbType, } from './types';
|
|
26
|
+
export { AppFonts, setAppFonts, type AppFontConfig, } from './styles/AppFonts';
|
|
27
|
+
export { AppColors, setAppColors, getThemeColors, updateAppColorsTheme, } from './styles/AppColors';
|
|
28
|
+
export { t, i18n, useTranslation, setLanguage, addTranslations, setTranslations, I18nextProvider, } from './i18n';
|