react-native-inapp-inspector 1.1.2 → 1.1.3
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/README.md +14 -0
- package/dist/commonjs/components/ConsoleLogCard.js +18 -0
- package/dist/commonjs/components/LogCard.js +17 -0
- package/dist/commonjs/components/NetworkIcons.d.ts +9 -2
- package/dist/commonjs/components/NetworkIcons.js +59 -3
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/reduxLogger.d.ts +21 -7
- package/dist/commonjs/customHooks/reduxLogger.js +147 -48
- package/dist/commonjs/customHooks/webViewLogger.js +13 -8
- package/dist/commonjs/helpers/settingsStore.d.ts +24 -0
- package/dist/commonjs/helpers/settingsStore.js +74 -0
- package/dist/commonjs/index.d.ts +1 -1
- package/dist/commonjs/index.js +572 -46
- package/dist/commonjs/styles/index.d.ts +17 -1
- package/dist/commonjs/styles/index.js +25 -3
- package/dist/commonjs/types/index.d.ts +4 -0
- package/dist/esm/components/ConsoleLogCard.js +18 -0
- package/dist/esm/components/LogCard.js +17 -0
- package/dist/esm/components/NetworkIcons.d.ts +9 -2
- package/dist/esm/components/NetworkIcons.js +51 -2
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/reduxLogger.d.ts +21 -7
- package/dist/esm/customHooks/reduxLogger.js +145 -47
- package/dist/esm/customHooks/webViewLogger.js +13 -8
- package/dist/esm/helpers/settingsStore.d.ts +24 -0
- package/dist/esm/helpers/settingsStore.js +67 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +570 -47
- package/dist/esm/styles/index.d.ts +17 -1
- package/dist/esm/styles/index.js +25 -3
- package/dist/esm/types/index.d.ts +4 -0
- package/example/ios/example.xcodeproj/project.pbxproj +0 -8
- package/example/package-lock.json +4 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -185,6 +185,20 @@ import store from './store';
|
|
|
185
185
|
connectReduxStore(store);
|
|
186
186
|
```
|
|
187
187
|
|
|
188
|
+
**Recommended:** if you use thunks, sagas or RTK Query, also add the inspector middleware so actions dispatched from inside them are captured with full type/payload attribution (they bypass the wrapped outer dispatch otherwise):
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
import {inspectorReduxMiddleware} from 'react-native-inapp-inspector';
|
|
192
|
+
|
|
193
|
+
const store = configureStore({
|
|
194
|
+
reducer,
|
|
195
|
+
middleware: getDefaultMiddleware =>
|
|
196
|
+
getDefaultMiddleware().concat(inspectorReduxMiddleware),
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Using both together is safe — they de-duplicate automatically.
|
|
201
|
+
|
|
188
202
|
The Redux tab shows the latest state tree, recent dispatches, payloads, and changed top-level state slices. Action history is capped at 50 entries.
|
|
189
203
|
|
|
190
204
|
---
|
|
@@ -320,6 +320,24 @@ exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, se
|
|
|
320
320
|
: `Object{${Object.keys(jsonContent.data).length}}`}
|
|
321
321
|
</react_native_1.Text>
|
|
322
322
|
</react_native_1.View>)}
|
|
323
|
+
{/* #9 — collapsed duplicate counter */}
|
|
324
|
+
{'duplicateCount' in item &&
|
|
325
|
+
item.duplicateCount != null &&
|
|
326
|
+
item.duplicateCount > 1 && (<react_native_1.View style={[
|
|
327
|
+
styles.badge,
|
|
328
|
+
{
|
|
329
|
+
backgroundColor: 'rgba(104, 75, 155, 0.1)',
|
|
330
|
+
borderColor: 'rgba(104, 75, 155, 0.25)',
|
|
331
|
+
borderWidth: 1,
|
|
332
|
+
},
|
|
333
|
+
]}>
|
|
334
|
+
<react_native_1.Text style={[
|
|
335
|
+
styles.badgeText,
|
|
336
|
+
{ color: AppColors_1.AppColors.purple, fontWeight: '700' },
|
|
337
|
+
]}>
|
|
338
|
+
×{item.duplicateCount}
|
|
339
|
+
</react_native_1.Text>
|
|
340
|
+
</react_native_1.View>)}
|
|
323
341
|
{isAnalyticsError && (<react_native_1.View style={[
|
|
324
342
|
styles.badge,
|
|
325
343
|
{ backgroundColor: `${AppColors_1.AppColors.skyBlue}15` },
|
|
@@ -116,6 +116,23 @@ const LogCard = react_1.default.memo(function LogCard({ item, onPress, timelineM
|
|
|
116
116
|
.json
|
|
117
117
|
</react_native_1.Text>
|
|
118
118
|
</react_native_1.View>)}
|
|
119
|
+
|
|
120
|
+
{/* #9 — collapsed duplicate counter */}
|
|
121
|
+
{item.duplicateCount != null && item.duplicateCount > 1 && (<react_native_1.View style={[
|
|
122
|
+
styles_1.default.chip,
|
|
123
|
+
{
|
|
124
|
+
backgroundColor: `${AppColors_1.AppColors.purple}15`,
|
|
125
|
+
borderColor: `${AppColors_1.AppColors.purple}30`,
|
|
126
|
+
marginLeft: 6,
|
|
127
|
+
},
|
|
128
|
+
]}>
|
|
129
|
+
<react_native_1.Text style={[
|
|
130
|
+
styles_1.default.chipText,
|
|
131
|
+
{ color: AppColors_1.AppColors.purple, fontWeight: '700' },
|
|
132
|
+
]}>
|
|
133
|
+
×{item.duplicateCount}
|
|
134
|
+
</react_native_1.Text>
|
|
135
|
+
</react_native_1.View>)}
|
|
119
136
|
</react_native_1.View>
|
|
120
137
|
|
|
121
138
|
<react_native_1.View style={styles_1.default.cardBottomRow}>
|
|
@@ -40,5 +40,12 @@ export declare const HtmlIcon: ({ color, size }: any) => React.JSX.Element;
|
|
|
40
40
|
export declare const CssIcon: ({ color, size }: any) => React.JSX.Element;
|
|
41
41
|
export declare const JsIcon: ({ color, size }: any) => React.JSX.Element;
|
|
42
42
|
export declare const EyeIcon: ({ color, size }: any) => React.JSX.Element;
|
|
43
|
-
export declare const SettingsIcon: ({ color, size }: any) => React.JSX.Element;
|
|
44
|
-
export declare const FolderIcon: ({ color, size }: any) => React.JSX.Element;
|
|
43
|
+
export declare const SettingsIcon: ({ color, size, }: any) => React.JSX.Element;
|
|
44
|
+
export declare const FolderIcon: ({ color, size, }: any) => React.JSX.Element;
|
|
45
|
+
export declare const WipeIcon: ({ color, size }: any) => React.JSX.Element;
|
|
46
|
+
export declare const LayersIcon: ({ color, size, }: any) => React.JSX.Element;
|
|
47
|
+
export declare const UserIcon: ({ color, size }: any) => React.JSX.Element;
|
|
48
|
+
export declare const InfoCircleIcon: ({ color, size, }: any) => React.JSX.Element;
|
|
49
|
+
export declare const WarningTriangleIcon: ({ color, size, }: any) => React.JSX.Element;
|
|
50
|
+
export declare const ErrorCircleIcon: ({ color, size, }: any) => React.JSX.Element;
|
|
51
|
+
export declare const TrendingUpIcon: ({ color, size, }: any) => React.JSX.Element;
|
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.FolderIcon = exports.SettingsIcon = exports.EyeIcon = exports.JsIcon = exports.CssIcon = exports.HtmlIcon = exports.BrandSquareIcon = exports.BrandCircleIcon = exports.MoonIcon = exports.SunIcon = exports.DebugIcon = exports.InsightsIcon = exports.AnalyticsIcon = exports.WhiteBackNavigation = exports.CloseWhite = exports.DownloadIcon = exports.FilterIcon = exports.ChevronIcon = exports.SortArrowIcon = exports.GlobeIcon = exports.DiffIcon = exports.SignalIcon = exports.ExportIcon = exports.HeaderPauseIcon = exports.TrashIcon = exports.FailIcon = exports.CheckIcon = exports.TerminalIcon = exports.FetchIcon = exports.CopyIcon = exports.HeadersIcon = exports.ResponseIcon = exports.RequestIcon = exports.SizeIcon = exports.StatusIcon = exports.CalendarIcon = exports.ClockIcon = exports.ClearIcon = exports.SearchIcon = exports.ExpandCollapseIcon = exports.ScreenIcon = exports.MapPinIcon = exports.EmptyRadarIcon = void 0;
|
|
39
|
+
exports.TrendingUpIcon = exports.ErrorCircleIcon = exports.WarningTriangleIcon = exports.InfoCircleIcon = exports.UserIcon = exports.LayersIcon = exports.WipeIcon = exports.FolderIcon = exports.SettingsIcon = exports.EyeIcon = exports.JsIcon = exports.CssIcon = exports.HtmlIcon = exports.BrandSquareIcon = exports.BrandCircleIcon = exports.MoonIcon = exports.SunIcon = exports.DebugIcon = exports.InsightsIcon = exports.AnalyticsIcon = exports.WhiteBackNavigation = exports.CloseWhite = exports.DownloadIcon = exports.FilterIcon = exports.ChevronIcon = exports.SortArrowIcon = exports.GlobeIcon = exports.DiffIcon = exports.SignalIcon = exports.ExportIcon = exports.HeaderPauseIcon = exports.TrashIcon = exports.FailIcon = exports.CheckIcon = exports.TerminalIcon = exports.FetchIcon = exports.CopyIcon = exports.HeadersIcon = exports.ResponseIcon = exports.RequestIcon = exports.SizeIcon = exports.StatusIcon = exports.CalendarIcon = exports.ClockIcon = exports.ClearIcon = exports.SearchIcon = exports.ExpandCollapseIcon = exports.ScreenIcon = exports.MapPinIcon = exports.EmptyRadarIcon = void 0;
|
|
40
40
|
const react_1 = __importDefault(require("react"));
|
|
41
41
|
const react_native_svg_1 = __importStar(require("react-native-svg"));
|
|
42
42
|
// Stylesheet
|
|
@@ -317,16 +317,72 @@ const EyeIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 14 }) => {
|
|
|
317
317
|
</react_native_svg_1.default>);
|
|
318
318
|
};
|
|
319
319
|
exports.EyeIcon = EyeIcon;
|
|
320
|
-
const SettingsIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 14 }) => {
|
|
320
|
+
const SettingsIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 14, }) => {
|
|
321
321
|
return (<react_native_svg_1.default width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
322
322
|
<react_native_svg_1.Path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
323
323
|
<react_native_svg_1.Circle cx="12" cy="12" r="3" stroke={color} strokeWidth="2"/>
|
|
324
324
|
</react_native_svg_1.default>);
|
|
325
325
|
};
|
|
326
326
|
exports.SettingsIcon = SettingsIcon;
|
|
327
|
-
const FolderIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 14 }) => {
|
|
327
|
+
const FolderIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 14, }) => {
|
|
328
328
|
return (<react_native_svg_1.default width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
329
329
|
<react_native_svg_1.Path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
330
330
|
</react_native_svg_1.default>);
|
|
331
331
|
};
|
|
332
332
|
exports.FolderIcon = FolderIcon;
|
|
333
|
+
// #3 — Broom/sweep "wipe" icon used by the header Clear-Everything button.
|
|
334
|
+
const WipeIcon = ({ color = '#FFFFFF', size = 16 }) => {
|
|
335
|
+
return (<react_native_svg_1.default width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
336
|
+
{/* Handle */}
|
|
337
|
+
<react_native_svg_1.Path d="M19.5 3.5L12.7 10.3" stroke={color} strokeWidth="2" strokeLinecap="round"/>
|
|
338
|
+
{/* Brush head */}
|
|
339
|
+
<react_native_svg_1.Path d="M13.5 9.5l1.5 1.5c.8.8.8 2 0 2.8L10 19l-5-5 5.2-5.5c.8-.8 2-.8 2.8 0z" stroke={color} strokeWidth="2" strokeLinejoin="round"/>
|
|
340
|
+
{/* Bristle strokes */}
|
|
341
|
+
<react_native_svg_1.Path d="M7.5 16.5L5.5 18.5M10 19l-1.5 1.5" stroke={color} strokeWidth="2" strokeLinecap="round"/>
|
|
342
|
+
{/* Dust sparks */}
|
|
343
|
+
<react_native_svg_1.Path d="M3 11.5h.01M5.5 8.5h.01" stroke={color} strokeWidth="2.4" strokeLinecap="round"/>
|
|
344
|
+
</react_native_svg_1.default>);
|
|
345
|
+
};
|
|
346
|
+
exports.WipeIcon = WipeIcon;
|
|
347
|
+
// #7 — Icons for the inner filter chips / sub tabs.
|
|
348
|
+
const LayersIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 12, }) => {
|
|
349
|
+
return (<react_native_svg_1.default width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
350
|
+
<react_native_svg_1.Path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
351
|
+
</react_native_svg_1.default>);
|
|
352
|
+
};
|
|
353
|
+
exports.LayersIcon = LayersIcon;
|
|
354
|
+
const UserIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 12 }) => {
|
|
355
|
+
return (<react_native_svg_1.default width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
356
|
+
<react_native_svg_1.Path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
357
|
+
<react_native_svg_1.Circle cx="12" cy="7" r="4" stroke={color} strokeWidth="2"/>
|
|
358
|
+
</react_native_svg_1.default>);
|
|
359
|
+
};
|
|
360
|
+
exports.UserIcon = UserIcon;
|
|
361
|
+
const InfoCircleIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 12, }) => {
|
|
362
|
+
return (<react_native_svg_1.default width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
363
|
+
<react_native_svg_1.Circle cx="12" cy="12" r="10" stroke={color} strokeWidth="2"/>
|
|
364
|
+
<react_native_svg_1.Path d="M12 16v-4M12 8h.01" stroke={color} strokeWidth="2" strokeLinecap="round"/>
|
|
365
|
+
</react_native_svg_1.default>);
|
|
366
|
+
};
|
|
367
|
+
exports.InfoCircleIcon = InfoCircleIcon;
|
|
368
|
+
const WarningTriangleIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 12, }) => {
|
|
369
|
+
return (<react_native_svg_1.default width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
370
|
+
<react_native_svg_1.Path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
371
|
+
<react_native_svg_1.Path d="M12 9v4M12 17h.01" stroke={color} strokeWidth="2" strokeLinecap="round"/>
|
|
372
|
+
</react_native_svg_1.default>);
|
|
373
|
+
};
|
|
374
|
+
exports.WarningTriangleIcon = WarningTriangleIcon;
|
|
375
|
+
const ErrorCircleIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 12, }) => {
|
|
376
|
+
return (<react_native_svg_1.default width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
377
|
+
<react_native_svg_1.Circle cx="12" cy="12" r="10" stroke={color} strokeWidth="2"/>
|
|
378
|
+
<react_native_svg_1.Path d="M15 9l-6 6M9 9l6 6" stroke={color} strokeWidth="2" strokeLinecap="round"/>
|
|
379
|
+
</react_native_svg_1.default>);
|
|
380
|
+
};
|
|
381
|
+
exports.ErrorCircleIcon = ErrorCircleIcon;
|
|
382
|
+
const TrendingUpIcon = ({ color = AppColors_1.AppColors.grayTextWeak, size = 12, }) => {
|
|
383
|
+
return (<react_native_svg_1.default width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
384
|
+
<react_native_svg_1.Path d="M23 6l-9.5 9.5-5-5L1 18" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
385
|
+
<react_native_svg_1.Path d="M17 6h6v6" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
386
|
+
</react_native_svg_1.default>);
|
|
387
|
+
};
|
|
388
|
+
exports.TrendingUpIcon = TrendingUpIcon;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const LIB_VERSION = "1.1.
|
|
1
|
+
export declare const LIB_VERSION = "1.1.3";
|
|
@@ -3,4 +3,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.LIB_VERSION = void 0;
|
|
4
4
|
// AUTO-GENERATED FILE — do not edit by hand.
|
|
5
5
|
// Regenerated from package.json on every build by scripts/gen-version.js.
|
|
6
|
-
exports.LIB_VERSION = '1.1.
|
|
6
|
+
exports.LIB_VERSION = '1.1.3';
|
|
@@ -1,16 +1,30 @@
|
|
|
1
|
-
export
|
|
2
|
-
export declare const setReduxAutoRefresh: (val: boolean) => void;
|
|
3
|
-
export declare const getReduxAutoRefresh: () => boolean;
|
|
4
|
-
export declare const getLastActionForReducer: () => Record<string, any>;
|
|
5
|
-
export declare const clearLastActionForReducer: () => void;
|
|
6
|
-
export declare const getActionHistory: () => {
|
|
1
|
+
export interface ReduxHistoryEntry {
|
|
7
2
|
id: number;
|
|
8
3
|
type: string;
|
|
9
4
|
payload: any;
|
|
10
5
|
timestamp: string;
|
|
11
6
|
affectedSlices: string[];
|
|
12
|
-
}
|
|
7
|
+
}
|
|
8
|
+
export declare const getReduxState: () => any;
|
|
9
|
+
export declare const setReduxAutoRefresh: (val: boolean) => void;
|
|
10
|
+
export declare const getReduxAutoRefresh: () => boolean;
|
|
11
|
+
export declare const getLastActionForReducer: () => Record<string, any>;
|
|
12
|
+
export declare const clearLastActionForReducer: () => void;
|
|
13
|
+
export declare const getActionHistory: () => ReduxHistoryEntry[];
|
|
13
14
|
export declare const clearActionHistory: () => void;
|
|
14
15
|
export declare const setReduxState: (state: any) => void;
|
|
15
16
|
export declare const subscribeReduxState: (cb: () => void) => () => void;
|
|
17
|
+
/**
|
|
18
|
+
* Standard Redux middleware — add it to your store to capture every action,
|
|
19
|
+
* including those dispatched from thunks, sagas and RTK Query:
|
|
20
|
+
*
|
|
21
|
+
* const store = configureStore({
|
|
22
|
+
* reducer,
|
|
23
|
+
* middleware: gDM => gDM().concat(inspectorReduxMiddleware),
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* Pair with `connectReduxStore(store)` (safe — they de-duplicate) or rely on
|
|
27
|
+
* the middleware alone; the initial snapshot is taken on the first action.
|
|
28
|
+
*/
|
|
29
|
+
export declare const inspectorReduxMiddleware: (storeApi: any) => (next: (action: any) => any) => (action: any) => any;
|
|
16
30
|
export declare const connectReduxStore: (store: any) => void;
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// #8 — Redux inspector module.
|
|
3
|
+
//
|
|
4
|
+
// Two integration paths, both feeding the same timeline/state snapshot:
|
|
5
|
+
//
|
|
6
|
+
// 1. `inspectorReduxMiddleware` (recommended) — a standard Redux middleware.
|
|
7
|
+
// Because it sits inside the middleware chain it sees EVERY action,
|
|
8
|
+
// including ones dispatched from thunks, sagas, listeners and RTK Query.
|
|
9
|
+
//
|
|
10
|
+
// 2. `connectReduxStore(store)` — zero-config fallback. It wraps the outer
|
|
11
|
+
// `store.dispatch` AND diffs state on `store.subscribe`, so even actions
|
|
12
|
+
// that bypass the wrapped dispatch (thunk/saga internals capture the raw
|
|
13
|
+
// dispatch reference at store-creation time) still update the state tree
|
|
14
|
+
// and per-reducer "last action" consistently.
|
|
2
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.connectReduxStore = exports.subscribeReduxState = exports.setReduxState = exports.clearActionHistory = exports.getActionHistory = exports.clearLastActionForReducer = exports.getLastActionForReducer = exports.getReduxAutoRefresh = exports.setReduxAutoRefresh = exports.getReduxState = void 0;
|
|
16
|
+
exports.connectReduxStore = exports.inspectorReduxMiddleware = exports.subscribeReduxState = exports.setReduxState = exports.clearActionHistory = exports.getActionHistory = exports.clearLastActionForReducer = exports.getLastActionForReducer = exports.getReduxAutoRefresh = exports.setReduxAutoRefresh = exports.getReduxState = void 0;
|
|
4
17
|
let currentReduxState = null;
|
|
5
18
|
const listeners = new Set();
|
|
6
19
|
let globalReduxAutoRefresh = true;
|
|
7
20
|
let lastActionForReducer = {};
|
|
8
21
|
let actionHistory = [];
|
|
22
|
+
const MAX_HISTORY = 50;
|
|
23
|
+
let historyIdSeq = 0;
|
|
24
|
+
// Guards against double-instrumentation (e.g. connectReduxStore called twice,
|
|
25
|
+
// or middleware + connect used together) which previously produced duplicate
|
|
26
|
+
// timeline entries and inconsistent counts.
|
|
27
|
+
const connectedStores = new WeakSet();
|
|
28
|
+
let middlewareAttached = false;
|
|
9
29
|
const getReduxState = () => currentReduxState;
|
|
10
30
|
exports.getReduxState = getReduxState;
|
|
11
31
|
const setReduxAutoRefresh = (val) => {
|
|
@@ -18,19 +38,19 @@ const getLastActionForReducer = () => lastActionForReducer;
|
|
|
18
38
|
exports.getLastActionForReducer = getLastActionForReducer;
|
|
19
39
|
const clearLastActionForReducer = () => {
|
|
20
40
|
lastActionForReducer = {};
|
|
21
|
-
|
|
41
|
+
notify();
|
|
22
42
|
};
|
|
23
43
|
exports.clearLastActionForReducer = clearLastActionForReducer;
|
|
24
44
|
const getActionHistory = () => actionHistory;
|
|
25
45
|
exports.getActionHistory = getActionHistory;
|
|
26
46
|
const clearActionHistory = () => {
|
|
27
47
|
actionHistory = [];
|
|
28
|
-
|
|
48
|
+
notify();
|
|
29
49
|
};
|
|
30
50
|
exports.clearActionHistory = clearActionHistory;
|
|
31
51
|
const setReduxState = (state) => {
|
|
32
52
|
currentReduxState = state;
|
|
33
|
-
|
|
53
|
+
notify();
|
|
34
54
|
};
|
|
35
55
|
exports.setReduxState = setReduxState;
|
|
36
56
|
const subscribeReduxState = (cb) => {
|
|
@@ -40,6 +60,83 @@ const subscribeReduxState = (cb) => {
|
|
|
40
60
|
};
|
|
41
61
|
};
|
|
42
62
|
exports.subscribeReduxState = subscribeReduxState;
|
|
63
|
+
function notify() {
|
|
64
|
+
listeners.forEach(cb => cb());
|
|
65
|
+
}
|
|
66
|
+
function actionTypeOf(action) {
|
|
67
|
+
if (typeof action === 'string')
|
|
68
|
+
return action;
|
|
69
|
+
if (action && typeof action === 'object' && action.type != null) {
|
|
70
|
+
return String(action.type);
|
|
71
|
+
}
|
|
72
|
+
if (typeof action === 'function') {
|
|
73
|
+
return action.name ? `thunk: ${action.name}` : 'thunk';
|
|
74
|
+
}
|
|
75
|
+
return 'UNKNOWN_ACTION';
|
|
76
|
+
}
|
|
77
|
+
function recordAction(action, prevState, nextState) {
|
|
78
|
+
const type = actionTypeOf(action);
|
|
79
|
+
const payload = action && typeof action === 'object' && action.payload !== undefined
|
|
80
|
+
? action.payload
|
|
81
|
+
: null;
|
|
82
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
83
|
+
const affectedSlices = [];
|
|
84
|
+
if (prevState &&
|
|
85
|
+
nextState &&
|
|
86
|
+
typeof prevState === 'object' &&
|
|
87
|
+
typeof nextState === 'object') {
|
|
88
|
+
Object.keys(nextState).forEach(key => {
|
|
89
|
+
if (prevState[key] !== nextState[key]) {
|
|
90
|
+
lastActionForReducer[key] = { type, payload, timestamp };
|
|
91
|
+
affectedSlices.push(key);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
actionHistory.unshift({
|
|
96
|
+
id: ++historyIdSeq,
|
|
97
|
+
type,
|
|
98
|
+
payload,
|
|
99
|
+
timestamp,
|
|
100
|
+
affectedSlices,
|
|
101
|
+
});
|
|
102
|
+
if (actionHistory.length > MAX_HISTORY) {
|
|
103
|
+
actionHistory.length = MAX_HISTORY;
|
|
104
|
+
}
|
|
105
|
+
if (globalReduxAutoRefresh) {
|
|
106
|
+
currentReduxState = nextState;
|
|
107
|
+
}
|
|
108
|
+
// Timeline / last-action map always changed — notify even when the state
|
|
109
|
+
// tree snapshot is paused so those panels stay live.
|
|
110
|
+
notify();
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Standard Redux middleware — add it to your store to capture every action,
|
|
114
|
+
* including those dispatched from thunks, sagas and RTK Query:
|
|
115
|
+
*
|
|
116
|
+
* const store = configureStore({
|
|
117
|
+
* reducer,
|
|
118
|
+
* middleware: gDM => gDM().concat(inspectorReduxMiddleware),
|
|
119
|
+
* });
|
|
120
|
+
*
|
|
121
|
+
* Pair with `connectReduxStore(store)` (safe — they de-duplicate) or rely on
|
|
122
|
+
* the middleware alone; the initial snapshot is taken on the first action.
|
|
123
|
+
*/
|
|
124
|
+
const inspectorReduxMiddleware = (storeApi) => (next) => (action) => {
|
|
125
|
+
middlewareAttached = true;
|
|
126
|
+
// Thunks are functions — let them run; their inner plain-action dispatches
|
|
127
|
+
// pass back through this same middleware, so nothing is lost.
|
|
128
|
+
if (typeof action === 'function') {
|
|
129
|
+
return next(action);
|
|
130
|
+
}
|
|
131
|
+
const prevState = storeApi.getState();
|
|
132
|
+
const result = next(action);
|
|
133
|
+
const nextState = storeApi.getState();
|
|
134
|
+
if (currentReduxState == null)
|
|
135
|
+
currentReduxState = nextState;
|
|
136
|
+
recordAction(action, prevState, nextState);
|
|
137
|
+
return result;
|
|
138
|
+
};
|
|
139
|
+
exports.inspectorReduxMiddleware = inspectorReduxMiddleware;
|
|
43
140
|
const connectReduxStore = (store) => {
|
|
44
141
|
if (!store ||
|
|
45
142
|
typeof store.getState !== 'function' ||
|
|
@@ -47,61 +144,63 @@ const connectReduxStore = (store) => {
|
|
|
47
144
|
console.warn('[NetworkInspector] Invalid Redux store passed to connectReduxStore.');
|
|
48
145
|
return;
|
|
49
146
|
}
|
|
50
|
-
//
|
|
147
|
+
// Idempotent — connecting the same store twice must not double-wrap
|
|
148
|
+
// dispatch or double-record the timeline.
|
|
149
|
+
if (connectedStores.has(store)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
connectedStores.add(store);
|
|
153
|
+
// Wrap the outer dispatch so directly dispatched actions get full
|
|
154
|
+
// type/payload attribution. Skipped when the middleware is already
|
|
155
|
+
// attached, otherwise every direct dispatch would be recorded twice.
|
|
51
156
|
const originalDispatch = store.dispatch.bind(store);
|
|
157
|
+
let inWrappedDispatch = false;
|
|
52
158
|
store.dispatch = (action) => {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
action &&
|
|
63
|
-
typeof action === 'object') {
|
|
64
|
-
Object.keys(nextState).forEach(key => {
|
|
65
|
-
if (prevState[key] !== nextState[key]) {
|
|
66
|
-
const actionObj = {
|
|
67
|
-
type: action.type || 'UNKNOWN_ACTION',
|
|
68
|
-
payload: action.payload !== undefined ? action.payload : null,
|
|
69
|
-
timestamp: new Date().toLocaleTimeString(),
|
|
70
|
-
};
|
|
71
|
-
lastActionForReducer[key] = actionObj;
|
|
72
|
-
affectedSlices.push(key);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
// Push to history
|
|
76
|
-
actionHistory.unshift({
|
|
77
|
-
id: Date.now() + Math.random(),
|
|
78
|
-
type: action.type || 'UNKNOWN_ACTION',
|
|
79
|
-
payload: action.payload !== undefined ? action.payload : null,
|
|
80
|
-
timestamp: new Date().toLocaleTimeString(),
|
|
81
|
-
affectedSlices,
|
|
82
|
-
});
|
|
83
|
-
// Cap size at 50
|
|
84
|
-
if (actionHistory.length > 50) {
|
|
85
|
-
actionHistory.pop();
|
|
159
|
+
if (middlewareAttached || typeof action === 'function') {
|
|
160
|
+
// Middleware handles recording, or it's a thunk whose inner dispatches
|
|
161
|
+
// will be picked up individually.
|
|
162
|
+
inWrappedDispatch = true;
|
|
163
|
+
try {
|
|
164
|
+
return originalDispatch(action);
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
inWrappedDispatch = false;
|
|
86
168
|
}
|
|
87
169
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
170
|
+
const prevState = store.getState();
|
|
171
|
+
inWrappedDispatch = true;
|
|
172
|
+
let result;
|
|
173
|
+
try {
|
|
174
|
+
result = originalDispatch(action);
|
|
91
175
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// so notify subscribers to re-render those without moving the tree snapshot.
|
|
95
|
-
listeners.forEach(cb => cb());
|
|
176
|
+
finally {
|
|
177
|
+
inWrappedDispatch = false;
|
|
96
178
|
}
|
|
179
|
+
recordAction(action, prevState, store.getState());
|
|
97
180
|
return result;
|
|
98
181
|
};
|
|
99
182
|
(0, exports.setReduxState)(store.getState());
|
|
100
|
-
//
|
|
183
|
+
// Subscribe-diff fallback: catches state changes whose dispatch bypassed
|
|
184
|
+
// the wrapper above (thunk/saga internals hold the raw dispatch reference).
|
|
185
|
+
// Without this, the tree and per-reducer last-action drifted out of sync.
|
|
186
|
+
let lastSeenState = store.getState();
|
|
101
187
|
store.subscribe(() => {
|
|
102
|
-
|
|
103
|
-
|
|
188
|
+
const nextState = store.getState();
|
|
189
|
+
if (nextState === lastSeenState)
|
|
190
|
+
return;
|
|
191
|
+
const prevState = lastSeenState;
|
|
192
|
+
lastSeenState = nextState;
|
|
193
|
+
if (inWrappedDispatch || middlewareAttached) {
|
|
194
|
+
// Already recorded with proper attribution; just refresh the snapshot.
|
|
195
|
+
if (globalReduxAutoRefresh) {
|
|
196
|
+
currentReduxState = nextState;
|
|
197
|
+
notify();
|
|
198
|
+
}
|
|
199
|
+
return;
|
|
104
200
|
}
|
|
201
|
+
// Change arrived outside the wrapped dispatch — record it so the
|
|
202
|
+
// timeline stays consistent, even without the original action type.
|
|
203
|
+
recordAction({ type: '@@inspector/EXTERNAL_STATE_CHANGE' }, prevState, nextState);
|
|
105
204
|
});
|
|
106
205
|
};
|
|
107
206
|
exports.connectReduxStore = connectReduxStore;
|
|
@@ -323,14 +323,19 @@ exports.WebView = (0, react_1.forwardRef)((props, ref) => {
|
|
|
323
323
|
onNavigationStateChange: handleNavigationStateChange,
|
|
324
324
|
onLoadStart: handleLoadStart,
|
|
325
325
|
onLoadEnd: handleLoadEnd,
|
|
326
|
-
}), loading &&
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
326
|
+
}), loading &&
|
|
327
|
+
showLoader &&
|
|
328
|
+
react_1.default.createElement(react_native_1.View, {
|
|
329
|
+
style: {
|
|
330
|
+
...react_native_1.StyleSheet.absoluteFill,
|
|
331
|
+
justifyContent: 'center',
|
|
332
|
+
alignItems: 'center',
|
|
333
|
+
backgroundColor: 'rgba(255, 255, 255, 0.4)',
|
|
334
|
+
},
|
|
335
|
+
}, react_1.default.createElement(react_native_1.ActivityIndicator, {
|
|
336
|
+
size: 'large',
|
|
337
|
+
color: '#684B9B',
|
|
338
|
+
})));
|
|
334
339
|
});
|
|
335
340
|
// Perform monkey-patching to intercept react-native-webview exports globally
|
|
336
341
|
try {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface PersistedSettings {
|
|
2
|
+
isDark?: boolean;
|
|
3
|
+
modalHeightPercent?: number;
|
|
4
|
+
tabVisibility?: Record<string, boolean>;
|
|
5
|
+
defaultTab?: string;
|
|
6
|
+
maxNetworkLogs?: number;
|
|
7
|
+
maxConsoleLogs?: number;
|
|
8
|
+
showConsoleLevels?: {
|
|
9
|
+
info: boolean;
|
|
10
|
+
warn: boolean;
|
|
11
|
+
error: boolean;
|
|
12
|
+
};
|
|
13
|
+
webViewCaptureCssJs?: boolean;
|
|
14
|
+
reduxAutoRefresh?: boolean;
|
|
15
|
+
reduxExpandDepth?: number;
|
|
16
|
+
slowRequestThreshold?: number;
|
|
17
|
+
insightsShowConsoleAlerts?: boolean;
|
|
18
|
+
showDuplicateLogs?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function loadSettings(): Promise<PersistedSettings>;
|
|
21
|
+
/** Debounced save so rapid toggling doesn't hammer storage. */
|
|
22
|
+
export declare function saveSettings(settings: PersistedSettings): void;
|
|
23
|
+
export declare function clearPersistedSettings(): Promise<void>;
|
|
24
|
+
export declare const isPersistentStorageAvailable: () => boolean;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// #5 — Persistence layer for the inspector's settings selections.
|
|
3
|
+
//
|
|
4
|
+
// Backed by @react-native-async-storage/async-storage when the host app has it
|
|
5
|
+
// installed (most RN apps do), with a transparent in-memory fallback so this
|
|
6
|
+
// library never crashes and never forces a new native dependency.
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.isPersistentStorageAvailable = void 0;
|
|
9
|
+
exports.loadSettings = loadSettings;
|
|
10
|
+
exports.saveSettings = saveSettings;
|
|
11
|
+
exports.clearPersistedSettings = clearPersistedSettings;
|
|
12
|
+
let storage = null;
|
|
13
|
+
try {
|
|
14
|
+
// Optional dependency — resolved only if the host app already ships it.
|
|
15
|
+
const mod = require('@react-native-async-storage/async-storage');
|
|
16
|
+
storage = mod?.default ?? mod ?? null;
|
|
17
|
+
if (storage && typeof storage.getItem !== 'function')
|
|
18
|
+
storage = null;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
storage = null;
|
|
22
|
+
}
|
|
23
|
+
// In-memory fallback (settings survive for the app session only).
|
|
24
|
+
const memory = new Map();
|
|
25
|
+
const SETTINGS_KEY = 'rn-inapp-inspector.settings.v1';
|
|
26
|
+
async function loadSettings() {
|
|
27
|
+
try {
|
|
28
|
+
const raw = storage
|
|
29
|
+
? await storage.getItem(SETTINGS_KEY)
|
|
30
|
+
: memory.get(SETTINGS_KEY) ?? null;
|
|
31
|
+
if (!raw)
|
|
32
|
+
return {};
|
|
33
|
+
const parsed = JSON.parse(raw);
|
|
34
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
let saveTimer = null;
|
|
41
|
+
/** Debounced save so rapid toggling doesn't hammer storage. */
|
|
42
|
+
function saveSettings(settings) {
|
|
43
|
+
if (saveTimer)
|
|
44
|
+
clearTimeout(saveTimer);
|
|
45
|
+
saveTimer = setTimeout(async () => {
|
|
46
|
+
try {
|
|
47
|
+
const raw = JSON.stringify(settings);
|
|
48
|
+
if (storage) {
|
|
49
|
+
await storage.setItem(SETTINGS_KEY, raw);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
memory.set(SETTINGS_KEY, raw);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Persistence is best-effort — never crash the host app over it.
|
|
57
|
+
}
|
|
58
|
+
}, 250);
|
|
59
|
+
}
|
|
60
|
+
async function clearPersistedSettings() {
|
|
61
|
+
try {
|
|
62
|
+
if (storage) {
|
|
63
|
+
await storage.removeItem(SETTINGS_KEY);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
memory.delete(SETTINGS_KEY);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// ignore
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const isPersistentStorageAvailable = () => storage != null;
|
|
74
|
+
exports.isPersistentStorageAvailable = isPersistentStorageAvailable;
|
package/dist/commonjs/index.d.ts
CHANGED
|
@@ -6,4 +6,4 @@ export { setupConsoleLogger, clearConsoleLogs, subscribeConsoleLogs, } from './c
|
|
|
6
6
|
export { setupAnalyticsLogger, logAnalyticsEvent, subscribeAnalyticsEvents, clearAnalyticsEvents, } from './customHooks/analyticsLogger';
|
|
7
7
|
export { WebView, getWebViewLogs, getWebViewNavHistory, getWebViewHtml, getWebViewCss, getWebViewJs, getWebViewHtmlUrl, clearWebViewData, subscribeWebView, } from './customHooks/webViewLogger';
|
|
8
8
|
export { default as ErrorBoundary } from './components/ErrorBoundary';
|
|
9
|
-
export { connectReduxStore, getReduxState, subscribeReduxState, } from './customHooks/reduxLogger';
|
|
9
|
+
export { connectReduxStore, inspectorReduxMiddleware, getReduxState, subscribeReduxState, getActionHistory, clearActionHistory, } from './customHooks/reduxLogger';
|