react-native-inapp-inspector 1.1.35 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +123 -101
  2. package/android/build.gradle +13 -1
  3. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +890 -0
  4. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.kt +17 -0
  5. package/dist/commonjs/components/ConsoleLogCard.js +5 -5
  6. package/dist/commonjs/components/CopyButton.js +3 -0
  7. package/dist/commonjs/components/EndOfListFooter.js +1 -1
  8. package/dist/commonjs/components/ErrorBoundary.js +3 -0
  9. package/dist/commonjs/components/Inspector/AnalyticsTab.js +25 -73
  10. package/dist/commonjs/components/Inspector/BundleTab.js +671 -189
  11. package/dist/commonjs/components/Inspector/ConsoleTab.js +110 -99
  12. package/dist/commonjs/components/Inspector/CrashTab.js +99 -91
  13. package/dist/commonjs/components/Inspector/InspectorHeader.js +107 -20
  14. package/dist/commonjs/components/Inspector/MainScreen.js +111 -68
  15. package/dist/commonjs/components/Inspector/NetworkTab.js +151 -30
  16. package/dist/commonjs/components/Inspector/PerformanceTab.js +96 -3
  17. package/dist/commonjs/components/Inspector/ReduxTab.js +18 -10
  18. package/dist/commonjs/components/Inspector/SettingsPanel.js +1218 -1238
  19. package/dist/commonjs/components/Inspector/TabBar.js +4 -2
  20. package/dist/commonjs/components/JsonViewer.js +9 -4
  21. package/dist/commonjs/components/LogCard.js +31 -1
  22. package/dist/commonjs/components/LogSyntaxHighlighter.d.ts +16 -0
  23. package/dist/commonjs/components/LogSyntaxHighlighter.js +130 -0
  24. package/dist/commonjs/components/NetworkIcons.d.ts +3 -0
  25. package/dist/commonjs/components/NetworkIcons.js +15 -1
  26. package/dist/commonjs/components/Slider.d.ts +13 -0
  27. package/dist/commonjs/components/Slider.js +261 -0
  28. package/dist/commonjs/components/TouchableScale.d.ts +1 -0
  29. package/dist/commonjs/components/TouchableScale.js +30 -32
  30. package/dist/commonjs/constants/index.d.ts +1 -2
  31. package/dist/commonjs/constants/index.js +4 -0
  32. package/dist/commonjs/constants/version.d.ts +1 -1
  33. package/dist/commonjs/constants/version.js +1 -1
  34. package/dist/commonjs/customHooks/analyticsLogger.d.ts +4 -0
  35. package/dist/commonjs/customHooks/analyticsLogger.js +8 -6
  36. package/dist/commonjs/customHooks/crashHandler.js +8 -1
  37. package/dist/commonjs/helpers/index.d.ts +1 -0
  38. package/dist/commonjs/helpers/index.js +25 -1
  39. package/dist/commonjs/helpers/searchQueryParser.d.ts +6 -0
  40. package/dist/commonjs/helpers/searchQueryParser.js +236 -0
  41. package/dist/commonjs/helpers/settingsStore.d.ts +9 -0
  42. package/dist/commonjs/helpers/settingsStore.js +59 -3
  43. package/dist/commonjs/index.d.ts +1 -0
  44. package/dist/commonjs/index.js +220 -43
  45. package/dist/commonjs/native/NativeInspector.d.ts +125 -0
  46. package/dist/commonjs/native/NativeInspector.js +313 -0
  47. package/dist/commonjs/native/NativeNetworkInspector.d.ts +22 -0
  48. package/dist/commonjs/native/NativeNetworkInspector.js +4 -0
  49. package/dist/commonjs/styles/index.js +41 -41
  50. package/dist/commonjs/types/enums.d.ts +1 -1
  51. package/dist/commonjs/types/interfaces.d.ts +9 -0
  52. package/dist/esm/components/ConsoleLogCard.js +5 -5
  53. package/dist/esm/components/CopyButton.js +3 -0
  54. package/dist/esm/components/EndOfListFooter.js +2 -2
  55. package/dist/esm/components/ErrorBoundary.js +3 -0
  56. package/dist/esm/components/Inspector/AnalyticsTab.js +27 -75
  57. package/dist/esm/components/Inspector/BundleTab.js +671 -189
  58. package/dist/esm/components/Inspector/ConsoleTab.js +111 -100
  59. package/dist/esm/components/Inspector/CrashTab.js +99 -91
  60. package/dist/esm/components/Inspector/InspectorHeader.js +108 -21
  61. package/dist/esm/components/Inspector/MainScreen.js +113 -70
  62. package/dist/esm/components/Inspector/NetworkTab.js +152 -31
  63. package/dist/esm/components/Inspector/PerformanceTab.js +96 -3
  64. package/dist/esm/components/Inspector/ReduxTab.js +18 -10
  65. package/dist/esm/components/Inspector/SettingsPanel.js +1222 -1242
  66. package/dist/esm/components/Inspector/TabBar.js +4 -2
  67. package/dist/esm/components/JsonViewer.js +9 -4
  68. package/dist/esm/components/LogCard.js +32 -2
  69. package/dist/esm/components/LogSyntaxHighlighter.d.ts +16 -0
  70. package/dist/esm/components/LogSyntaxHighlighter.js +123 -0
  71. package/dist/esm/components/NetworkIcons.d.ts +3 -0
  72. package/dist/esm/components/NetworkIcons.js +11 -0
  73. package/dist/esm/components/Slider.d.ts +13 -0
  74. package/dist/esm/components/Slider.js +222 -0
  75. package/dist/esm/components/TouchableScale.d.ts +1 -0
  76. package/dist/esm/components/TouchableScale.js +31 -33
  77. package/dist/esm/constants/index.d.ts +1 -2
  78. package/dist/esm/constants/index.js +4 -0
  79. package/dist/esm/constants/version.d.ts +1 -1
  80. package/dist/esm/constants/version.js +1 -1
  81. package/dist/esm/customHooks/analyticsLogger.d.ts +4 -0
  82. package/dist/esm/customHooks/analyticsLogger.js +6 -5
  83. package/dist/esm/customHooks/crashHandler.js +8 -1
  84. package/dist/esm/helpers/index.d.ts +1 -0
  85. package/dist/esm/helpers/index.js +11 -1
  86. package/dist/esm/helpers/searchQueryParser.d.ts +6 -0
  87. package/dist/esm/helpers/searchQueryParser.js +233 -0
  88. package/dist/esm/helpers/settingsStore.d.ts +9 -0
  89. package/dist/esm/helpers/settingsStore.js +58 -3
  90. package/dist/esm/index.d.ts +1 -0
  91. package/dist/esm/index.js +207 -44
  92. package/dist/esm/native/NativeInspector.d.ts +125 -0
  93. package/dist/esm/native/NativeInspector.js +289 -0
  94. package/dist/esm/native/NativeNetworkInspector.d.ts +22 -0
  95. package/dist/esm/native/NativeNetworkInspector.js +2 -0
  96. package/dist/esm/styles/index.js +41 -41
  97. package/dist/esm/types/enums.d.ts +1 -1
  98. package/dist/esm/types/interfaces.d.ts +9 -0
  99. package/ios/NetworkInspectorModule.h +6 -0
  100. package/ios/NetworkInspectorModule.m +760 -5
  101. package/package.json +13 -2
  102. package/react-native-inapp-inspector.podspec +7 -2
  103. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +0 -97
  104. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +0 -28
@@ -43,7 +43,7 @@ const addEvent = (event) => {
43
43
  if (!isAnalyticsModuleEnabled)
44
44
  return;
45
45
  events.unshift(event);
46
- events = events.slice(0, 200);
46
+ events = events.slice(0, 75);
47
47
  notify();
48
48
  };
49
49
  // ─── Public subscribe / query API ────────────────────────────────────────────
@@ -94,10 +94,11 @@ export const logAnalyticsEvent = (name, params = {}, userProperties = {}) => {
94
94
  * setupAnalyticsLogger(analytics());
95
95
  */
96
96
  let isAnalyticsPatched = false;
97
- export const isAnalyticsConnected = () => isAnalyticsPatched ||
98
- events.length > 0 ||
99
- Object.keys(currentUserProperties).length > 0 ||
100
- currentUserId != null;
97
+ /**
98
+ * Returns true if analytics logging is active (always ready for custom and Firebase events).
99
+ */
100
+ export const isAnalyticsConnected = () => true;
101
+ export const isFirebaseAnalyticsPatched = () => isAnalyticsPatched;
101
102
  export const setupAnalyticsLogger = (analyticsInstance) => {
102
103
  if (!analyticsInstance) {
103
104
  console.warn('[AnalyticsLogger] No analytics instance provided — skipping setup.');
@@ -1,6 +1,7 @@
1
1
  import { LogBox, NativeModules, TurboModuleRegistry, NativeEventEmitter, Platform, AppState, Dimensions, } from 'react-native';
2
2
  import { CrashExportFormat, CrashType } from '../types/enums';
3
3
  import { addLogFromCrash } from './consoleLogger';
4
+ import { showNativeFloatingButton, setNativeFloatingButtonBadge, } from '../native/NativeInspector';
4
5
  import { t } from '../i18n';
5
6
  export { CrashExportFormat };
6
7
  let crashListeners = [];
@@ -9,7 +10,7 @@ let lastHandledErrorTimestamp = 0;
9
10
  let crashRecordsStore = [];
10
11
  let breadcrumbsStore = [];
11
12
  const MAX_BREADCRUMBS = 50;
12
- let maxStoredCrashes = 100;
13
+ let maxStoredCrashes = 50;
13
14
  const appStartTime = Date.now();
14
15
  let isCrashModuleEnabled = false;
15
16
  export const setCrashModuleEnabled = (enabled) => {
@@ -365,6 +366,12 @@ export const handleInterceptedCrash = (errorOrTitle, rawStack, isFatal = false,
365
366
  logId: log?.id,
366
367
  crashRecord,
367
368
  });
369
+ // Ensure native floating icon is visible & badged even if React UI is broken
370
+ try {
371
+ showNativeFloatingButton();
372
+ setNativeFloatingButtonBadge(true);
373
+ }
374
+ catch { }
368
375
  return crashRecord;
369
376
  }
370
377
  catch (err) {
@@ -1,4 +1,5 @@
1
1
  import { NetworkLog, RouteInfo, DiffResult, JsonContent, StackFrameType } from '../types';
2
+ export * from './searchQueryParser';
2
3
  export declare const getDomainColor: (domain: string) => string;
3
4
  export declare const formatDateTime: (timestamp: number) => string;
4
5
  export declare const formatTimestamp: (timestamp: number) => string;
@@ -5,6 +5,7 @@ import { showToast } from './toast';
5
5
  import { AppColors } from '../styles/AppColors';
6
6
  // Constants
7
7
  import { DOMAIN_COLORS, DURATION_FAST_MS, DURATION_SLOW_MS } from '../constants';
8
+ export * from './searchQueryParser';
8
9
  export const getDomainColor = (domain) => {
9
10
  if (!domain)
10
11
  return DOMAIN_COLORS[0];
@@ -156,9 +157,18 @@ export const getFetchCommand = (log) => {
156
157
  export const deduplicateLogs = (raw) => {
157
158
  const map = new Map();
158
159
  raw.forEach(entry => {
159
- if (!map.has(entry.id) || entry.status != null) {
160
+ if (!map.has(entry.id)) {
160
161
  map.set(entry.id, entry);
161
162
  }
163
+ else {
164
+ const existing = map.get(entry.id);
165
+ if (existing.status == null && entry.status != null) {
166
+ map.set(entry.id, entry);
167
+ }
168
+ else if ((entry.startTime ?? 0) >= (existing.startTime ?? 0)) {
169
+ map.set(entry.id, entry);
170
+ }
171
+ }
162
172
  });
163
173
  return Array.from(map.values()).sort((a, b) => b.id - a.id);
164
174
  };
@@ -0,0 +1,6 @@
1
+ import { NetworkLog } from '../types';
2
+ /**
3
+ * Parses query strings like `method:POST is:error slow:>1s status:200 auth`
4
+ * and checks whether a given NetworkLog matches all tokens.
5
+ */
6
+ export declare function matchNetworkLogQuery(log: NetworkLog, searchQuery: string, routePath?: string): boolean;
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Parses query strings like `method:POST is:error slow:>1s status:200 auth`
3
+ * and checks whether a given NetworkLog matches all tokens.
4
+ */
5
+ export function matchNetworkLogQuery(log, searchQuery, routePath) {
6
+ if (!searchQuery || searchQuery.trim().length === 0)
7
+ return true;
8
+ const rawTokens = searchQuery
9
+ .trim()
10
+ .match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [];
11
+ if (rawTokens.length === 0)
12
+ return true;
13
+ const methodStr = (log.method || '').toLowerCase();
14
+ const urlStr = (log.url || '').toLowerCase();
15
+ const statusNum = typeof log.status === 'number' ? log.status : parseInt(String(log.status || 0), 10);
16
+ const statusStr = String(log.status ?? '');
17
+ const durationNum = log.duration || 0;
18
+ const clientStr = (log.client || '').toLowerCase();
19
+ const reqStr = typeof log.request === 'string' ? log.request : JSON.stringify(log.request || '');
20
+ const resStr = typeof log.response === 'string' ? log.response : JSON.stringify(log.response || '');
21
+ const reqHeadersStr = JSON.stringify(log.requestHeaders || '').toLowerCase();
22
+ const resHeadersStr = JSON.stringify(log.responseHeaders || '').toLowerCase();
23
+ const pathStr = routePath ? routePath.toLowerCase() : '';
24
+ const fullSearchCorpus = [
25
+ methodStr,
26
+ urlStr,
27
+ statusStr,
28
+ pathStr,
29
+ clientStr,
30
+ reqStr.toLowerCase(),
31
+ resStr.toLowerCase(),
32
+ reqHeadersStr,
33
+ resHeadersStr,
34
+ ].join(' ');
35
+ for (const rawToken of rawTokens) {
36
+ const token = rawToken.replace(/^["']|["']$/g, '').trim();
37
+ if (!token)
38
+ continue;
39
+ const lowerToken = token.toLowerCase();
40
+ // 1. method:<VAL> or m:<VAL>
41
+ if (lowerToken.startsWith('method:') || lowerToken.startsWith('m:')) {
42
+ const targetMethod = lowerToken.replace(/^(method:|m:)/, '').trim();
43
+ if (targetMethod.startsWith('!')) {
44
+ if (methodStr === targetMethod.slice(1))
45
+ return false;
46
+ }
47
+ else {
48
+ if (methodStr !== targetMethod)
49
+ return false;
50
+ }
51
+ continue;
52
+ }
53
+ // 2. status:<VAL> or s:<VAL>
54
+ if (lowerToken.startsWith('status:') || lowerToken.startsWith('s:')) {
55
+ const val = lowerToken.replace(/^(status:|s:)/, '').trim();
56
+ if (val.startsWith('>=')) {
57
+ const threshold = parseInt(val.slice(2), 10);
58
+ if (isNaN(threshold) || statusNum < threshold)
59
+ return false;
60
+ }
61
+ else if (val.startsWith('>')) {
62
+ const threshold = parseInt(val.slice(1), 10);
63
+ if (isNaN(threshold) || statusNum <= threshold)
64
+ return false;
65
+ }
66
+ else if (val.startsWith('<=')) {
67
+ const threshold = parseInt(val.slice(2), 10);
68
+ if (isNaN(threshold) || statusNum > threshold)
69
+ return false;
70
+ }
71
+ else if (val.startsWith('<')) {
72
+ const threshold = parseInt(val.slice(1), 10);
73
+ if (isNaN(threshold) || statusNum >= threshold)
74
+ return false;
75
+ }
76
+ else if (val.endsWith('xx') || val.endsWith('x')) {
77
+ const prefix = val.replace(/x/g, '');
78
+ if (!statusStr.startsWith(prefix))
79
+ return false;
80
+ }
81
+ else {
82
+ const targetCode = parseInt(val, 10);
83
+ if (!isNaN(targetCode)) {
84
+ if (statusNum !== targetCode)
85
+ return false;
86
+ }
87
+ else if (!statusStr.includes(val)) {
88
+ return false;
89
+ }
90
+ }
91
+ continue;
92
+ }
93
+ // 3. is:<FLAG>
94
+ if (lowerToken.startsWith('is:')) {
95
+ const flag = lowerToken.slice(3).trim();
96
+ if (flag === 'error' || flag === 'failed' || flag === 'err' || flag === 'fail') {
97
+ const isErr = log.status === 0 || log.status == null || statusNum >= 400;
98
+ if (!isErr)
99
+ return false;
100
+ }
101
+ else if (flag === 'success' || flag === 'ok' || flag === '2xx') {
102
+ const isSuccess = statusNum >= 200 && statusNum < 300;
103
+ if (!isSuccess)
104
+ return false;
105
+ }
106
+ else if (flag === 'slow') {
107
+ if (durationNum < 1000)
108
+ return false;
109
+ }
110
+ else if (flag === 'fast') {
111
+ if (durationNum >= 200)
112
+ return false;
113
+ }
114
+ else if (flag === 'graphql' || flag === 'gql') {
115
+ const isGql = urlStr.includes('graphql') ||
116
+ clientStr === 'graphql' ||
117
+ clientStr === 'apollo';
118
+ if (!isGql)
119
+ return false;
120
+ }
121
+ else if (flag === 'https') {
122
+ if (!urlStr.startsWith('https'))
123
+ return false;
124
+ }
125
+ else if (flag === 'http') {
126
+ if (urlStr.startsWith('https') || !urlStr.startsWith('http'))
127
+ return false;
128
+ }
129
+ else if (flag === 'json') {
130
+ const isJson = reqHeadersStr.includes('application/json') ||
131
+ resHeadersStr.includes('application/json') ||
132
+ urlStr.includes('.json');
133
+ if (!isJson)
134
+ return false;
135
+ }
136
+ continue;
137
+ }
138
+ // 4. slow:<DURATION> or dur:<DURATION> or duration:<DURATION>
139
+ if (lowerToken.startsWith('slow:') ||
140
+ lowerToken.startsWith('dur:') ||
141
+ lowerToken.startsWith('duration:')) {
142
+ const val = lowerToken.replace(/^(slow:|dur:|duration:)/, '').trim();
143
+ let thresholdMs = 0;
144
+ let isGreater = true;
145
+ let cleanVal = val;
146
+ if (val.startsWith('>=')) {
147
+ isGreater = true;
148
+ cleanVal = val.slice(2);
149
+ }
150
+ else if (val.startsWith('>')) {
151
+ isGreater = true;
152
+ cleanVal = val.slice(1);
153
+ }
154
+ else if (val.startsWith('<=')) {
155
+ isGreater = false;
156
+ cleanVal = val.slice(2);
157
+ }
158
+ else if (val.startsWith('<')) {
159
+ isGreater = false;
160
+ cleanVal = val.slice(1);
161
+ }
162
+ if (cleanVal.endsWith('s') && !cleanVal.endsWith('ms')) {
163
+ thresholdMs = parseFloat(cleanVal.slice(0, -1)) * 1000;
164
+ }
165
+ else if (cleanVal.endsWith('ms')) {
166
+ thresholdMs = parseFloat(cleanVal.slice(0, -2));
167
+ }
168
+ else {
169
+ thresholdMs = parseFloat(cleanVal);
170
+ }
171
+ if (!isNaN(thresholdMs)) {
172
+ if (isGreater) {
173
+ if (durationNum < thresholdMs)
174
+ return false;
175
+ }
176
+ else {
177
+ if (durationNum > thresholdMs)
178
+ return false;
179
+ }
180
+ }
181
+ continue;
182
+ }
183
+ // 5. client:<CLIENT> or c:<CLIENT>
184
+ if (lowerToken.startsWith('client:') || lowerToken.startsWith('c:')) {
185
+ const targetClient = lowerToken.replace(/^(client:|c:)/, '').trim();
186
+ if (!clientStr.includes(targetClient))
187
+ return false;
188
+ continue;
189
+ }
190
+ // 6. domain:<DOMAIN> or d:<DOMAIN>
191
+ if (lowerToken.startsWith('domain:') || lowerToken.startsWith('d:')) {
192
+ const targetDomain = lowerToken.replace(/^(domain:|d:)/, '').trim();
193
+ if (!urlStr.includes(targetDomain))
194
+ return false;
195
+ continue;
196
+ }
197
+ // 7. path:<PATH> or p:<PATH>
198
+ if (lowerToken.startsWith('path:') || lowerToken.startsWith('p:')) {
199
+ const targetPath = lowerToken.replace(/^(path:|p:)/, '').trim();
200
+ if (!urlStr.includes(targetPath) && !pathStr.includes(targetPath)) {
201
+ return false;
202
+ }
203
+ continue;
204
+ }
205
+ // 8. req:<TERM> or body:<TERM>
206
+ if (lowerToken.startsWith('req:') || lowerToken.startsWith('body:')) {
207
+ const target = lowerToken.replace(/^(req:|body:)/, '').trim();
208
+ if (!reqStr.toLowerCase().includes(target))
209
+ return false;
210
+ continue;
211
+ }
212
+ // 9. res:<TERM>
213
+ if (lowerToken.startsWith('res:')) {
214
+ const target = lowerToken.slice(4).trim();
215
+ if (!resStr.toLowerCase().includes(target))
216
+ return false;
217
+ continue;
218
+ }
219
+ // 10. header:<TERM> or h:<TERM>
220
+ if (lowerToken.startsWith('header:') || lowerToken.startsWith('h:')) {
221
+ const target = lowerToken.replace(/^(header:|h:)/, '').trim();
222
+ if (!reqHeadersStr.includes(target) && !resHeadersStr.includes(target)) {
223
+ return false;
224
+ }
225
+ continue;
226
+ }
227
+ // 11. Generic Plain Substring Match
228
+ if (!fullSearchCorpus.includes(lowerToken)) {
229
+ return false;
230
+ }
231
+ }
232
+ return true;
233
+ }
@@ -6,3 +6,12 @@ export declare function saveSettings(settings: PersistedSettings): void;
6
6
  export declare function clearPersistedSettings(): Promise<void>;
7
7
  export declare const isPersistentStorageAvailable: () => boolean;
8
8
  export declare function getCustomStorage(): InspectorStorage | null;
9
+ export interface RamLimitsProfile {
10
+ maxNetworkLogs: number;
11
+ maxConsoleLogs: number;
12
+ maxAnalyticsEvents: number;
13
+ maxCrashRecords: number;
14
+ profileName: 'High-End' | 'Standard' | 'Compact' | 'Ultra-Light';
15
+ freeRamMb: number;
16
+ }
17
+ export declare function calculateRamBasedLimits(freeRamMb: number): RamLimitsProfile;
@@ -3,6 +3,7 @@
3
3
  // Backed by iOS Settings (NSUserDefaults) for iOS, or custom storage if passed (e.g. AsyncStorage/MMKV).
4
4
  // Contains an in-memory fallback for Android or Jest test environments.
5
5
  import { Platform, Settings } from 'react-native';
6
+ import { getNativeStorageItem, setNativeStorageItem, isNativeModuleAvailable, } from '../native/NativeInspector';
6
7
  // In-memory fallback (settings survive for the app session only).
7
8
  const memory = new Map();
8
9
  let customStorage = null;
@@ -19,6 +20,13 @@ export async function loadSettings() {
19
20
  const parsed = JSON.parse(raw);
20
21
  return parsed && typeof parsed === 'object' ? parsed : {};
21
22
  }
23
+ if (isNativeModuleAvailable()) {
24
+ const raw = await getNativeStorageItem(SETTINGS_KEY);
25
+ if (raw) {
26
+ const parsed = JSON.parse(raw);
27
+ return parsed && typeof parsed === 'object' ? parsed : {};
28
+ }
29
+ }
22
30
  if (Platform.OS === 'ios') {
23
31
  const raw = Settings.get(SETTINGS_KEY);
24
32
  if (!raw)
@@ -29,7 +37,7 @@ export async function loadSettings() {
29
37
  }
30
38
  return raw && typeof raw === 'object' ? raw : {};
31
39
  }
32
- // Android/fallback: memory
40
+ // In-memory fallback
33
41
  const raw = memory.get(SETTINGS_KEY);
34
42
  if (!raw)
35
43
  return {};
@@ -47,11 +55,13 @@ export function saveSettings(settings) {
47
55
  customStorage.setItem(SETTINGS_KEY, raw);
48
56
  return;
49
57
  }
58
+ if (isNativeModuleAvailable()) {
59
+ setNativeStorageItem(SETTINGS_KEY, raw);
60
+ }
50
61
  if (Platform.OS === 'ios') {
51
62
  Settings.set({ [SETTINGS_KEY]: raw });
52
63
  return;
53
64
  }
54
- // Android/fallback: memory
55
65
  memory.set(SETTINGS_KEY, raw);
56
66
  }
57
67
  catch {
@@ -69,6 +79,9 @@ export async function clearPersistedSettings() {
69
79
  }
70
80
  return;
71
81
  }
82
+ if (isNativeModuleAvailable()) {
83
+ await setNativeStorageItem(SETTINGS_KEY, null);
84
+ }
72
85
  if (Platform.OS === 'ios') {
73
86
  Settings.set({ [SETTINGS_KEY]: null });
74
87
  return;
@@ -80,8 +93,50 @@ export async function clearPersistedSettings() {
80
93
  }
81
94
  }
82
95
  export const isPersistentStorageAvailable = () => {
83
- return customStorage !== null || Platform.OS === 'ios';
96
+ return customStorage !== null || isNativeModuleAvailable() || Platform.OS === 'ios';
84
97
  };
85
98
  export function getCustomStorage() {
86
99
  return customStorage;
87
100
  }
101
+ export function calculateRamBasedLimits(freeRamMb) {
102
+ if (freeRamMb >= 3000) {
103
+ return {
104
+ maxNetworkLogs: 100,
105
+ maxConsoleLogs: 100,
106
+ maxAnalyticsEvents: 75,
107
+ maxCrashRecords: 50,
108
+ profileName: 'High-End',
109
+ freeRamMb,
110
+ };
111
+ }
112
+ else if (freeRamMb >= 1500) {
113
+ return {
114
+ maxNetworkLogs: 100,
115
+ maxConsoleLogs: 100,
116
+ maxAnalyticsEvents: 75,
117
+ maxCrashRecords: 50,
118
+ profileName: 'Standard',
119
+ freeRamMb,
120
+ };
121
+ }
122
+ else if (freeRamMb >= 600) {
123
+ return {
124
+ maxNetworkLogs: 100,
125
+ maxConsoleLogs: 100,
126
+ maxAnalyticsEvents: 50,
127
+ maxCrashRecords: 25,
128
+ profileName: 'Compact',
129
+ freeRamMb,
130
+ };
131
+ }
132
+ else {
133
+ return {
134
+ maxNetworkLogs: 50,
135
+ maxConsoleLogs: 50,
136
+ maxAnalyticsEvents: 25,
137
+ maxCrashRecords: 15,
138
+ profileName: 'Ultra-Light',
139
+ freeRamMb,
140
+ };
141
+ }
142
+ }
@@ -12,4 +12,5 @@ export { connectReduxStore, inspectorReduxMiddleware, getReduxState, subscribeRe
12
12
  export { getEventCategory, registerGAPlugin, type GAPlugin, } from './helpers/gaAnalyticsRegistry';
13
13
  export { usePerformanceTracker, useComponentProfiler, useNavigationProfiler, trackComponentRender, trackNavigationTransition, trackHeavyTask, measureAsync, getHermesMemoryStats, registerComponentProfile, subscribeRenderProfiles, getRenderProfiles, logPerformanceEvent, clearPerformanceEvents, subscribePerformanceEvents, getPerformanceEvents, getInitialRenderProfiles, getInitialPerformanceEvents, generateFixSnippet, } from './customHooks/performanceTracker';
14
14
  export { InspectLog, InspectTrackTime, InspectCatch, type InspectLogOptions, } from './decorators';
15
+ export { getNativeDeviceMetrics, enableNativeCrashProtection, subscribeNativeCrashes, showNativeFloatingButton, hideNativeFloatingButton, setNativeFloatingButtonBadge, subscribeNativeFloatingButtonPress, subscribeNativeDeviceShake, startNativeFpsMonitoring, stopNativeFpsMonitoring, getNativeFpsMetrics, getNativeStorageItem, setNativeStorageItem, isNativeModuleAvailable, type NativeDeviceMetrics, type NativeCrashEvent, type FloatingButtonOptions, type NativeFpsMetrics, } from './native/NativeInspector';
15
16
  export { ActiveTab, Method, StatusFilter, SortOrder, LocalFilter, ModalAnimationType, SettingsPage, SettingsSubTab, LogFilter, ConsoleLogType, AnalyticsEventSource, GAEventCategory, StackFrameType, DiffResultType, BundleSubTab, PerformanceSubTab, CrashType, CrashExportFormat, CrashDetailSubTab, CrashFilterType, BreadcrumbType, } from './types';