react-native-inapp-inspector 2.2.3 → 2.2.4
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/AnalyticsDetail.js +5 -1
- package/dist/commonjs/components/AnalyticsEventCard.js +2 -1
- package/dist/commonjs/components/DiffViewer.js +1 -1
- package/dist/commonjs/components/Inspector/BundleTab.js +22 -0
- package/dist/commonjs/components/Inspector/DeviceInfoTab.d.ts +3 -0
- package/dist/commonjs/components/Inspector/DeviceInfoTab.js +835 -0
- package/dist/commonjs/components/Inspector/InspectorHeader.js +12 -14
- package/dist/commonjs/components/Inspector/LogDetail.js +6 -3
- package/dist/commonjs/components/Inspector/MainScreen.js +7 -3
- package/dist/commonjs/components/Inspector/NetworkTab.js +92 -15
- package/dist/commonjs/components/Inspector/PerformanceTab.js +38 -12
- package/dist/commonjs/components/Inspector/SettingsPanel.js +440 -229
- package/dist/commonjs/components/Inspector/StorageTab.d.ts +3 -0
- package/dist/commonjs/components/Inspector/StorageTab.js +892 -0
- package/dist/commonjs/components/Inspector/TabBar.js +14 -0
- package/dist/commonjs/components/Inspector/TelemetryConsentModal.js +11 -2
- package/dist/commonjs/components/Inspector/UpdateAvailableModal.js +4 -1
- package/dist/commonjs/components/NetworkIcons.d.ts +6 -0
- package/dist/commonjs/components/NetworkIcons.js +30 -1
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/bundleAnalyzer.js +44 -22
- package/dist/commonjs/customHooks/storageInspector.d.ts +67 -0
- package/dist/commonjs/customHooks/storageInspector.js +486 -0
- package/dist/commonjs/i18n/locales/en.json +8 -2
- package/dist/commonjs/index.d.ts +1 -0
- package/dist/commonjs/index.js +16 -1
- package/dist/commonjs/storage.d.ts +5 -0
- package/dist/commonjs/storage.js +18 -0
- package/dist/commonjs/types/enums.d.ts +5 -0
- package/dist/commonjs/types/enums.js +5 -0
- package/dist/esm/components/AnalyticsDetail.js +6 -2
- package/dist/esm/components/AnalyticsEventCard.js +2 -1
- package/dist/esm/components/DiffViewer.js +1 -1
- package/dist/esm/components/Inspector/BundleTab.js +22 -0
- package/dist/esm/components/Inspector/DeviceInfoTab.d.ts +3 -0
- package/dist/esm/components/Inspector/DeviceInfoTab.js +796 -0
- package/dist/esm/components/Inspector/InspectorHeader.js +13 -15
- package/dist/esm/components/Inspector/LogDetail.js +7 -4
- package/dist/esm/components/Inspector/MainScreen.js +7 -3
- package/dist/esm/components/Inspector/NetworkTab.js +93 -16
- package/dist/esm/components/Inspector/PerformanceTab.js +39 -13
- package/dist/esm/components/Inspector/SettingsPanel.js +441 -230
- package/dist/esm/components/Inspector/StorageTab.d.ts +3 -0
- package/dist/esm/components/Inspector/StorageTab.js +853 -0
- package/dist/esm/components/Inspector/TabBar.js +15 -1
- package/dist/esm/components/Inspector/TelemetryConsentModal.js +11 -2
- package/dist/esm/components/Inspector/UpdateAvailableModal.js +4 -1
- package/dist/esm/components/NetworkIcons.d.ts +6 -0
- package/dist/esm/components/NetworkIcons.js +23 -0
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/bundleAnalyzer.js +44 -22
- package/dist/esm/customHooks/storageInspector.d.ts +67 -0
- package/dist/esm/customHooks/storageInspector.js +469 -0
- package/dist/esm/i18n/locales/en.json +8 -2
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/storage.d.ts +5 -0
- package/dist/esm/storage.js +5 -0
- package/dist/esm/types/enums.d.ts +5 -0
- package/dist/esm/types/enums.js +5 -0
- package/package.json +8 -1
- package/src/components/AnalyticsDetail.tsx +6 -1
- package/src/components/AnalyticsEventCard.tsx +2 -1
- package/src/components/DiffViewer.tsx +1 -1
- package/src/components/Inspector/BundleTab.tsx +29 -0
- package/src/components/Inspector/DeviceInfoTab.tsx +1204 -0
- package/src/components/Inspector/InspectorHeader.tsx +13 -14
- package/src/components/Inspector/LogDetail.tsx +15 -11
- package/src/components/Inspector/MainScreen.tsx +7 -3
- package/src/components/Inspector/NetworkTab.tsx +104 -16
- package/src/components/Inspector/PerformanceTab.tsx +46 -12
- package/src/components/Inspector/SettingsPanel.tsx +570 -304
- package/src/components/Inspector/StorageTab.tsx +1048 -0
- package/src/components/Inspector/TabBar.tsx +20 -0
- package/src/components/Inspector/TelemetryConsentModal.tsx +26 -2
- package/src/components/Inspector/UpdateAvailableModal.tsx +13 -1
- package/src/components/NetworkIcons.tsx +103 -0
- package/src/constants/version.ts +1 -1
- package/src/customHooks/bundleAnalyzer.ts +47 -22
- package/src/customHooks/storageInspector.ts +509 -0
- package/src/i18n/locales/en.json +8 -2
- package/src/index.tsx +21 -1
- package/src/storage.ts +18 -0
- package/src/types/enums.ts +5 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
// Universal In-App Storage Inspector Adapter for AsyncStorage & MMKV
|
|
2
|
+
// Supports automatic module resolution, NativeModules SQLite fallback, custom instances, and full CRUD operations.
|
|
3
|
+
|
|
4
|
+
import {NativeModules} from 'react-native';
|
|
5
|
+
|
|
6
|
+
export interface StorageEntry {
|
|
7
|
+
key: string;
|
|
8
|
+
value: string;
|
|
9
|
+
parsedValue?: any;
|
|
10
|
+
type: 'json' | 'string' | 'number' | 'boolean' | 'null';
|
|
11
|
+
byteSize: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type StorageDriver = 'asyncStorage' | 'mmkv';
|
|
15
|
+
|
|
16
|
+
// In-memory registered storage references
|
|
17
|
+
let customAsyncStorage: any = null;
|
|
18
|
+
const customMMKVInstances = new Map<string, any>();
|
|
19
|
+
|
|
20
|
+
// Listeners for storage mutations
|
|
21
|
+
const changeListeners = new Set<() => void>();
|
|
22
|
+
|
|
23
|
+
const notifyChange = () => {
|
|
24
|
+
changeListeners.forEach(listener => {
|
|
25
|
+
try {
|
|
26
|
+
listener();
|
|
27
|
+
} catch {}
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Register an AsyncStorage instance with the inspector (optional override).
|
|
33
|
+
*/
|
|
34
|
+
export const connectAsyncStorage = (storageInstance: any) => {
|
|
35
|
+
if (storageInstance) {
|
|
36
|
+
customAsyncStorage = storageInstance;
|
|
37
|
+
notifyChange();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Register an MMKV instance with the inspector (optional override).
|
|
43
|
+
*/
|
|
44
|
+
export const connectMMKV = (mmkvInstance: any, id: string = 'default') => {
|
|
45
|
+
if (mmkvInstance) {
|
|
46
|
+
customMMKVInstances.set(id, mmkvInstance);
|
|
47
|
+
notifyChange();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Native Bridge Fallback for AsyncStorage:
|
|
53
|
+
* Directly interfaces with NativeModules.RNCAsyncStorage without needing JS package imports.
|
|
54
|
+
*/
|
|
55
|
+
const getNativeAsyncStorageBridge = (): any => {
|
|
56
|
+
const nativeModule =
|
|
57
|
+
NativeModules.RNCAsyncStorage ||
|
|
58
|
+
NativeModules.RNC_AsyncSQLiteDBStorage ||
|
|
59
|
+
NativeModules.AsyncSQLiteDBStorage;
|
|
60
|
+
|
|
61
|
+
if (nativeModule) {
|
|
62
|
+
return {
|
|
63
|
+
getAllKeys: (): Promise<string[]> => {
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
if (typeof nativeModule.getAllKeys !== 'function') {
|
|
66
|
+
resolve([]);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
nativeModule.getAllKeys((err: any, keys: string[]) => {
|
|
70
|
+
if (err) reject(err);
|
|
71
|
+
else resolve(keys || []);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
multiGet: (keys: string[]): Promise<[string, string | null][]> => {
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
if (typeof nativeModule.multiGet !== 'function') {
|
|
78
|
+
resolve([]);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
nativeModule.multiGet(keys, (err: any, result: [string, string | null][]) => {
|
|
82
|
+
if (err) reject(err);
|
|
83
|
+
else resolve(result || []);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
getItem: (key: string): Promise<string | null> => {
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
if (typeof nativeModule.multiGet !== 'function') {
|
|
90
|
+
resolve(null);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
nativeModule.multiGet([key], (err: any, result: [string, string | null][]) => {
|
|
94
|
+
if (err) reject(err);
|
|
95
|
+
else resolve(result?.[0]?.[1] ?? null);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
setItem: (key: string, value: string): Promise<void> => {
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
if (typeof nativeModule.multiSet !== 'function') {
|
|
102
|
+
resolve();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
nativeModule.multiSet([[key, value]], (err: any) => {
|
|
106
|
+
if (err) reject(err);
|
|
107
|
+
else resolve();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
removeItem: (key: string): Promise<void> => {
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
if (typeof nativeModule.multiRemove !== 'function') {
|
|
114
|
+
resolve();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
nativeModule.multiRemove([key], (err: any) => {
|
|
118
|
+
if (err) reject(err);
|
|
119
|
+
else resolve();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
clear: (): Promise<void> => {
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
if (typeof nativeModule.clear !== 'function') {
|
|
126
|
+
resolve();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
nativeModule.clear((err: any) => {
|
|
130
|
+
if (err) reject(err);
|
|
131
|
+
else resolve();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Auto-detect AsyncStorage:
|
|
142
|
+
* 1. Explicit connection
|
|
143
|
+
* 2. Global instance
|
|
144
|
+
* 3. Dynamic require '@react-native-async-storage/async-storage'
|
|
145
|
+
* 4. Native Bridge Direct interface
|
|
146
|
+
*/
|
|
147
|
+
export const getResolvedAsyncStorage = (): any => {
|
|
148
|
+
if (customAsyncStorage) return customAsyncStorage;
|
|
149
|
+
|
|
150
|
+
// 1. Global instances
|
|
151
|
+
try {
|
|
152
|
+
const maybeReq =
|
|
153
|
+
(globalThis as any)?.__inAppAsyncStorage ||
|
|
154
|
+
(globalThis as any)?.AsyncStorage ||
|
|
155
|
+
(global as any)?.AsyncStorage;
|
|
156
|
+
if (maybeReq && typeof maybeReq.getAllKeys === 'function') {
|
|
157
|
+
return maybeReq;
|
|
158
|
+
}
|
|
159
|
+
} catch {}
|
|
160
|
+
|
|
161
|
+
// 2. Dynamic runtime require
|
|
162
|
+
try {
|
|
163
|
+
const req =
|
|
164
|
+
typeof (globalThis as any)?.require === 'function'
|
|
165
|
+
? (globalThis as any).require
|
|
166
|
+
: typeof require === 'function'
|
|
167
|
+
? require
|
|
168
|
+
: null;
|
|
169
|
+
|
|
170
|
+
if (req) {
|
|
171
|
+
try {
|
|
172
|
+
const mod = req('@react-native-async-storage/async-storage');
|
|
173
|
+
const resolved = mod?.default || mod;
|
|
174
|
+
if (resolved && typeof resolved.getAllKeys === 'function') {
|
|
175
|
+
return resolved;
|
|
176
|
+
}
|
|
177
|
+
} catch {}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const legacyMod = req('@react-native-community/async-storage');
|
|
181
|
+
const resolved = legacyMod?.default || legacyMod;
|
|
182
|
+
if (resolved && typeof resolved.getAllKeys === 'function') {
|
|
183
|
+
return resolved;
|
|
184
|
+
}
|
|
185
|
+
} catch {}
|
|
186
|
+
}
|
|
187
|
+
} catch {}
|
|
188
|
+
|
|
189
|
+
// 3. Native Bridge Fallback (Direct SQLite)
|
|
190
|
+
const nativeBridge = getNativeAsyncStorageBridge();
|
|
191
|
+
if (nativeBridge) {
|
|
192
|
+
return nativeBridge;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return null;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Auto-detect MMKV:
|
|
200
|
+
* 1. Explicit connection
|
|
201
|
+
* 2. Dynamic require 'react-native-mmkv' & instantiate default instance
|
|
202
|
+
* 3. Global MMKV / JSI instances
|
|
203
|
+
*/
|
|
204
|
+
export const getResolvedMMKV = (id: string = 'default'): any => {
|
|
205
|
+
if (customMMKVInstances.has(id)) {
|
|
206
|
+
return customMMKVInstances.get(id);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 1. Dynamic require
|
|
210
|
+
try {
|
|
211
|
+
const req =
|
|
212
|
+
typeof (globalThis as any)?.require === 'function'
|
|
213
|
+
? (globalThis as any).require
|
|
214
|
+
: typeof require === 'function'
|
|
215
|
+
? require
|
|
216
|
+
: null;
|
|
217
|
+
|
|
218
|
+
if (req) {
|
|
219
|
+
const mmkvMod = req('react-native-mmkv');
|
|
220
|
+
const MMKVClass = mmkvMod?.MMKV || mmkvMod?.default?.MMKV;
|
|
221
|
+
if (MMKVClass && typeof MMKVClass === 'function') {
|
|
222
|
+
if (!customMMKVInstances.has('default')) {
|
|
223
|
+
try {
|
|
224
|
+
const defaultInst = new MMKVClass();
|
|
225
|
+
customMMKVInstances.set('default', defaultInst);
|
|
226
|
+
} catch {}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
} catch {}
|
|
231
|
+
|
|
232
|
+
// 2. Global references
|
|
233
|
+
try {
|
|
234
|
+
const globalMMKV =
|
|
235
|
+
(globalThis as any)?.mmkv ||
|
|
236
|
+
(global as any)?.mmkv ||
|
|
237
|
+
(globalThis as any)?.__MMKV__;
|
|
238
|
+
if (globalMMKV && typeof globalMMKV.getAllKeys === 'function') {
|
|
239
|
+
if (!customMMKVInstances.has('default')) {
|
|
240
|
+
customMMKVInstances.set('default', globalMMKV);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
} catch {}
|
|
244
|
+
|
|
245
|
+
if (customMMKVInstances.has(id)) {
|
|
246
|
+
return customMMKVInstances.get(id);
|
|
247
|
+
}
|
|
248
|
+
if (customMMKVInstances.size > 0) {
|
|
249
|
+
return customMMKVInstances.values().next().value;
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
export const isAsyncStorageConnected = (): boolean => {
|
|
255
|
+
return Boolean(getResolvedAsyncStorage());
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
export const isMMKVConnected = (): boolean => {
|
|
259
|
+
return Boolean(getResolvedMMKV());
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
export const getRegisteredMMKVInstanceIds = (): string[] => {
|
|
263
|
+
getResolvedMMKV(); // trigger auto-discovery
|
|
264
|
+
return Array.from(customMMKVInstances.keys());
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Calculate approximate byte size of a UTF-8 string
|
|
269
|
+
*/
|
|
270
|
+
export const calculateByteSize = (str: string): number => {
|
|
271
|
+
if (!str) return 0;
|
|
272
|
+
return str.length * 2;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Determine type and format value safely
|
|
277
|
+
*/
|
|
278
|
+
export const analyzeStorageValue = (
|
|
279
|
+
rawVal: any,
|
|
280
|
+
): {
|
|
281
|
+
strVal: string;
|
|
282
|
+
parsedVal?: any;
|
|
283
|
+
type: 'json' | 'string' | 'number' | 'boolean' | 'null';
|
|
284
|
+
byteSize: number;
|
|
285
|
+
} => {
|
|
286
|
+
if (rawVal === null || rawVal === undefined) {
|
|
287
|
+
return {strVal: 'null', type: 'null', byteSize: 4};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const strVal = typeof rawVal === 'string' ? rawVal : String(rawVal);
|
|
291
|
+
const byteSize = calculateByteSize(strVal);
|
|
292
|
+
|
|
293
|
+
if (typeof rawVal === 'number') {
|
|
294
|
+
return {strVal, parsedVal: rawVal, type: 'number', byteSize};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (typeof rawVal === 'boolean') {
|
|
298
|
+
return {strVal, parsedVal: rawVal, type: 'boolean', byteSize};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const trimmed = strVal.trim();
|
|
302
|
+
if (
|
|
303
|
+
(trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
304
|
+
(trimmed.startsWith('[') && trimmed.endsWith(']'))
|
|
305
|
+
) {
|
|
306
|
+
try {
|
|
307
|
+
const parsed = JSON.parse(trimmed);
|
|
308
|
+
return {strVal, parsedVal: parsed, type: 'json', byteSize};
|
|
309
|
+
} catch {}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (trimmed === 'true' || trimmed === 'false') {
|
|
313
|
+
return {strVal, parsedVal: trimmed === 'true', type: 'boolean', byteSize};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (!isNaN(Number(trimmed)) && trimmed !== '') {
|
|
317
|
+
return {strVal, parsedVal: Number(trimmed), type: 'number', byteSize};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {strVal, type: 'string', byteSize};
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Fetch all storage entries for given driver
|
|
325
|
+
*/
|
|
326
|
+
export const fetchStorageEntries = async (
|
|
327
|
+
driver: StorageDriver,
|
|
328
|
+
instanceId?: string,
|
|
329
|
+
): Promise<StorageEntry[]> => {
|
|
330
|
+
const entries: StorageEntry[] = [];
|
|
331
|
+
|
|
332
|
+
if (driver === 'asyncStorage') {
|
|
333
|
+
const storage = getResolvedAsyncStorage();
|
|
334
|
+
if (!storage || typeof storage.getAllKeys !== 'function') {
|
|
335
|
+
return [];
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
const keys: string[] = await storage.getAllKeys();
|
|
340
|
+
if (!keys || keys.length === 0) return [];
|
|
341
|
+
|
|
342
|
+
if (typeof storage.multiGet === 'function') {
|
|
343
|
+
const pairs: [string, string | null][] = await storage.multiGet(keys);
|
|
344
|
+
for (const [k, v] of pairs) {
|
|
345
|
+
const analysis = analyzeStorageValue(v);
|
|
346
|
+
entries.push({
|
|
347
|
+
key: k,
|
|
348
|
+
value: analysis.strVal,
|
|
349
|
+
parsedValue: analysis.parsedVal,
|
|
350
|
+
type: analysis.type,
|
|
351
|
+
byteSize: analysis.byteSize,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
for (const k of keys) {
|
|
356
|
+
const v = await storage.getItem(k);
|
|
357
|
+
const analysis = analyzeStorageValue(v);
|
|
358
|
+
entries.push({
|
|
359
|
+
key: k,
|
|
360
|
+
value: analysis.strVal,
|
|
361
|
+
parsedValue: analysis.parsedVal,
|
|
362
|
+
type: analysis.type,
|
|
363
|
+
byteSize: analysis.byteSize,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
} catch (err) {
|
|
368
|
+
console.warn('[StorageInspector] Error reading AsyncStorage keys:', err);
|
|
369
|
+
}
|
|
370
|
+
} else if (driver === 'mmkv') {
|
|
371
|
+
const mmkv = getResolvedMMKV(instanceId);
|
|
372
|
+
if (!mmkv || typeof mmkv.getAllKeys !== 'function') {
|
|
373
|
+
return [];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
try {
|
|
377
|
+
const keys: string[] = mmkv.getAllKeys();
|
|
378
|
+
for (const k of keys) {
|
|
379
|
+
let rawVal: any = null;
|
|
380
|
+
if (typeof mmkv.getString === 'function') {
|
|
381
|
+
rawVal = mmkv.getString(k);
|
|
382
|
+
}
|
|
383
|
+
if (rawVal === undefined || rawVal === null) {
|
|
384
|
+
if (typeof mmkv.getNumber === 'function') {
|
|
385
|
+
const num = mmkv.getNumber(k);
|
|
386
|
+
if (num !== undefined && !isNaN(num)) rawVal = num;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (rawVal === undefined || rawVal === null) {
|
|
390
|
+
if (typeof mmkv.getBoolean === 'function') {
|
|
391
|
+
const bool = mmkv.getBoolean(k);
|
|
392
|
+
if (bool !== undefined) rawVal = bool;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const analysis = analyzeStorageValue(rawVal);
|
|
397
|
+
entries.push({
|
|
398
|
+
key: k,
|
|
399
|
+
value: analysis.strVal,
|
|
400
|
+
parsedValue: analysis.parsedVal,
|
|
401
|
+
type: analysis.type,
|
|
402
|
+
byteSize: analysis.byteSize,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
} catch (err) {
|
|
406
|
+
console.warn('[StorageInspector] Error reading MMKV keys:', err);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Sort alphabetically by key
|
|
411
|
+
entries.sort((a, b) => a.key.localeCompare(b.key));
|
|
412
|
+
return entries;
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Create or Update a key-value entry (CRUD: Create / Update)
|
|
417
|
+
*/
|
|
418
|
+
export const setStorageEntry = async (
|
|
419
|
+
driver: StorageDriver,
|
|
420
|
+
key: string,
|
|
421
|
+
value: string,
|
|
422
|
+
instanceId?: string,
|
|
423
|
+
): Promise<boolean> => {
|
|
424
|
+
if (!key.trim()) return false;
|
|
425
|
+
|
|
426
|
+
try {
|
|
427
|
+
if (driver === 'asyncStorage') {
|
|
428
|
+
const storage = getResolvedAsyncStorage();
|
|
429
|
+
if (!storage || typeof storage.setItem !== 'function') return false;
|
|
430
|
+
await storage.setItem(key.trim(), value);
|
|
431
|
+
notifyChange();
|
|
432
|
+
return true;
|
|
433
|
+
} else if (driver === 'mmkv') {
|
|
434
|
+
const mmkv = getResolvedMMKV(instanceId);
|
|
435
|
+
if (!mmkv || typeof mmkv.set !== 'function') return false;
|
|
436
|
+
mmkv.set(key.trim(), value);
|
|
437
|
+
notifyChange();
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
} catch (err) {
|
|
441
|
+
console.warn('[StorageInspector] Error setting storage item:', err);
|
|
442
|
+
}
|
|
443
|
+
return false;
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Delete a single key (CRUD: Delete)
|
|
448
|
+
*/
|
|
449
|
+
export const removeStorageEntry = async (
|
|
450
|
+
driver: StorageDriver,
|
|
451
|
+
key: string,
|
|
452
|
+
instanceId?: string,
|
|
453
|
+
): Promise<boolean> => {
|
|
454
|
+
try {
|
|
455
|
+
if (driver === 'asyncStorage') {
|
|
456
|
+
const storage = getResolvedAsyncStorage();
|
|
457
|
+
if (!storage || typeof storage.removeItem !== 'function') return false;
|
|
458
|
+
await storage.removeItem(key);
|
|
459
|
+
notifyChange();
|
|
460
|
+
return true;
|
|
461
|
+
} else if (driver === 'mmkv') {
|
|
462
|
+
const mmkv = getResolvedMMKV(instanceId);
|
|
463
|
+
if (!mmkv || typeof mmkv.delete !== 'function') return false;
|
|
464
|
+
mmkv.delete(key);
|
|
465
|
+
notifyChange();
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
} catch (err) {
|
|
469
|
+
console.warn('[StorageInspector] Error removing storage item:', err);
|
|
470
|
+
}
|
|
471
|
+
return false;
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Wipe all keys for given storage (CRUD: Clear All)
|
|
476
|
+
*/
|
|
477
|
+
export const clearStorageDriver = async (
|
|
478
|
+
driver: StorageDriver,
|
|
479
|
+
instanceId?: string,
|
|
480
|
+
): Promise<boolean> => {
|
|
481
|
+
try {
|
|
482
|
+
if (driver === 'asyncStorage') {
|
|
483
|
+
const storage = getResolvedAsyncStorage();
|
|
484
|
+
if (!storage || typeof storage.clear !== 'function') return false;
|
|
485
|
+
await storage.clear();
|
|
486
|
+
notifyChange();
|
|
487
|
+
return true;
|
|
488
|
+
} else if (driver === 'mmkv') {
|
|
489
|
+
const mmkv = getResolvedMMKV(instanceId);
|
|
490
|
+
if (!mmkv || typeof mmkv.clearAll !== 'function') return false;
|
|
491
|
+
mmkv.clearAll();
|
|
492
|
+
notifyChange();
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
} catch (err) {
|
|
496
|
+
console.warn('[StorageInspector] Error clearing storage:', err);
|
|
497
|
+
}
|
|
498
|
+
return false;
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Subscribe to storage mutations
|
|
503
|
+
*/
|
|
504
|
+
export const subscribeToStorageChanges = (listener: () => void): (() => void) => {
|
|
505
|
+
changeListeners.add(listener);
|
|
506
|
+
return () => {
|
|
507
|
+
changeListeners.delete(listener);
|
|
508
|
+
};
|
|
509
|
+
};
|
package/src/i18n/locales/en.json
CHANGED
|
@@ -36,7 +36,12 @@
|
|
|
36
36
|
"apis": "APIs",
|
|
37
37
|
"logs": "Logs",
|
|
38
38
|
"analytics": "Analytics",
|
|
39
|
-
"redux": "Redux"
|
|
39
|
+
"redux": "Redux",
|
|
40
|
+
"bundle": "Bundle",
|
|
41
|
+
"performance": "Performance",
|
|
42
|
+
"crash": "Crash",
|
|
43
|
+
"device": "Device",
|
|
44
|
+
"storage": "Storage"
|
|
40
45
|
},
|
|
41
46
|
"settings": {
|
|
42
47
|
"mainTitle": "Settings & Modules",
|
|
@@ -51,7 +56,8 @@
|
|
|
51
56
|
"dormantBadge": "DORMANT",
|
|
52
57
|
"protectedBadge": "PROTECTED",
|
|
53
58
|
"modulesAndTools": "Modules & Tools",
|
|
54
|
-
"uiPreferences": "UI
|
|
59
|
+
"uiPreferences": "UI & Display",
|
|
60
|
+
"ramLimits": "RAM & Limits",
|
|
55
61
|
"zeroOverheadTitle": "Zero Background Overhead",
|
|
56
62
|
"zeroOverheadDesc": "Check the modules you wish to activate. Disabled modules consume 0% CPU & memory until saved.",
|
|
57
63
|
"unsavedPending": "● Unsaved selections pending",
|
package/src/index.tsx
CHANGED
|
@@ -133,6 +133,7 @@ import {
|
|
|
133
133
|
SortOrder,
|
|
134
134
|
GroupedListItem,
|
|
135
135
|
ActiveTab,
|
|
136
|
+
SettingsSubTab,
|
|
136
137
|
ConsoleLog,
|
|
137
138
|
Method,
|
|
138
139
|
AnalyticsEvent,
|
|
@@ -352,7 +353,7 @@ const NetworkInspector = ({
|
|
|
352
353
|
| 'redux'
|
|
353
354
|
| null
|
|
354
355
|
>(null);
|
|
355
|
-
const [settingsActiveSubTab, setSettingsActiveSubTab] = useState<
|
|
356
|
+
const [settingsActiveSubTab, setSettingsActiveSubTab] = useState<SettingsSubTab>('module');
|
|
356
357
|
const [tabVisibility, setTabVisibility] = useState<
|
|
357
358
|
Record<ActiveTab, boolean>
|
|
358
359
|
>({
|
|
@@ -363,6 +364,8 @@ const NetworkInspector = ({
|
|
|
363
364
|
bundle: false,
|
|
364
365
|
performance: false,
|
|
365
366
|
crash: false,
|
|
367
|
+
device: true,
|
|
368
|
+
storage: true,
|
|
366
369
|
});
|
|
367
370
|
|
|
368
371
|
const [maxNetworkLogs, setMaxNetworkLogs] = useState<number>(100);
|
|
@@ -423,6 +426,8 @@ const NetworkInspector = ({
|
|
|
423
426
|
bundle: false,
|
|
424
427
|
performance: false,
|
|
425
428
|
crash: false,
|
|
429
|
+
device: true,
|
|
430
|
+
storage: true,
|
|
426
431
|
});
|
|
427
432
|
setDefaultTab('apis');
|
|
428
433
|
setIsAutoRamLimitEnabled(true);
|
|
@@ -2311,6 +2316,21 @@ export {
|
|
|
2311
2316
|
BrandCircleIcon,
|
|
2312
2317
|
} from './components/NetworkIcons';
|
|
2313
2318
|
|
|
2319
|
+
export {
|
|
2320
|
+
connectAsyncStorage,
|
|
2321
|
+
connectMMKV,
|
|
2322
|
+
isAsyncStorageConnected,
|
|
2323
|
+
isMMKVConnected,
|
|
2324
|
+
getRegisteredMMKVInstanceIds,
|
|
2325
|
+
fetchStorageEntries,
|
|
2326
|
+
setStorageEntry,
|
|
2327
|
+
removeStorageEntry,
|
|
2328
|
+
clearStorageDriver,
|
|
2329
|
+
subscribeToStorageChanges,
|
|
2330
|
+
type StorageEntry,
|
|
2331
|
+
type StorageDriver,
|
|
2332
|
+
} from './customHooks/storageInspector';
|
|
2333
|
+
|
|
2314
2334
|
export {
|
|
2315
2335
|
ActiveTab,
|
|
2316
2336
|
Method,
|
package/src/storage.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sub-module entry point: react-native-inapp-inspector/storage
|
|
3
|
+
* Universal AsyncStorage and MMKV live inspector with full CRUD operations.
|
|
4
|
+
*/
|
|
5
|
+
export {
|
|
6
|
+
connectAsyncStorage,
|
|
7
|
+
connectMMKV,
|
|
8
|
+
isAsyncStorageConnected,
|
|
9
|
+
isMMKVConnected,
|
|
10
|
+
getRegisteredMMKVInstanceIds,
|
|
11
|
+
fetchStorageEntries,
|
|
12
|
+
setStorageEntry,
|
|
13
|
+
removeStorageEntry,
|
|
14
|
+
clearStorageDriver,
|
|
15
|
+
subscribeToStorageChanges,
|
|
16
|
+
type StorageEntry,
|
|
17
|
+
type StorageDriver,
|
|
18
|
+
} from './customHooks/storageInspector';
|
package/src/types/enums.ts
CHANGED
|
@@ -10,6 +10,8 @@ export const ActiveTab = {
|
|
|
10
10
|
Bundle: 'bundle',
|
|
11
11
|
Performance: 'performance',
|
|
12
12
|
Crash: 'crash',
|
|
13
|
+
Device: 'device',
|
|
14
|
+
Storage: 'storage',
|
|
13
15
|
} as const;
|
|
14
16
|
export type ActiveTab = (typeof ActiveTab)[keyof typeof ActiveTab];
|
|
15
17
|
|
|
@@ -63,6 +65,8 @@ export const SettingsPage = {
|
|
|
63
65
|
Bundle: 'bundle',
|
|
64
66
|
Performance: 'performance',
|
|
65
67
|
Crash: 'crash',
|
|
68
|
+
Device: 'device',
|
|
69
|
+
Storage: 'storage',
|
|
66
70
|
} as const;
|
|
67
71
|
export type SettingsPage =
|
|
68
72
|
| (typeof SettingsPage)[keyof typeof SettingsPage]
|
|
@@ -71,6 +75,7 @@ export type SettingsPage =
|
|
|
71
75
|
export const SettingsSubTab = {
|
|
72
76
|
Module: 'module',
|
|
73
77
|
Ui: 'ui',
|
|
78
|
+
Limits: 'limits',
|
|
74
79
|
} as const;
|
|
75
80
|
export type SettingsSubTab =
|
|
76
81
|
(typeof SettingsSubTab)[keyof typeof SettingsSubTab];
|