react-native-inapp-inspector 2.2.2 → 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.
Files changed (159) hide show
  1. package/dist/commonjs/components/AnalyticsDetail.js +5 -1
  2. package/dist/commonjs/components/AnalyticsEventCard.js +2 -1
  3. package/dist/commonjs/components/DiffViewer.js +1 -1
  4. package/dist/commonjs/components/Inspector/BundleTab.js +22 -0
  5. package/dist/commonjs/components/Inspector/DeviceInfoTab.d.ts +3 -0
  6. package/dist/commonjs/components/Inspector/DeviceInfoTab.js +835 -0
  7. package/dist/commonjs/components/Inspector/InspectorHeader.js +12 -14
  8. package/dist/commonjs/components/Inspector/LogDetail.js +6 -3
  9. package/dist/commonjs/components/Inspector/MainScreen.js +7 -3
  10. package/dist/commonjs/components/Inspector/NetworkTab.js +92 -15
  11. package/dist/commonjs/components/Inspector/PerformanceTab.js +38 -12
  12. package/dist/commonjs/components/Inspector/SettingsPanel.js +440 -229
  13. package/dist/commonjs/components/Inspector/StorageTab.d.ts +3 -0
  14. package/dist/commonjs/components/Inspector/StorageTab.js +892 -0
  15. package/dist/commonjs/components/Inspector/TabBar.js +14 -0
  16. package/dist/commonjs/components/Inspector/TelemetryConsentModal.js +11 -2
  17. package/dist/commonjs/components/Inspector/UpdateAvailableModal.js +4 -1
  18. package/dist/commonjs/components/NetworkIcons.d.ts +6 -0
  19. package/dist/commonjs/components/NetworkIcons.js +30 -1
  20. package/dist/commonjs/constants/version.d.ts +1 -1
  21. package/dist/commonjs/constants/version.js +1 -1
  22. package/dist/commonjs/customHooks/bundleAnalyzer.js +44 -22
  23. package/dist/commonjs/customHooks/storageInspector.d.ts +67 -0
  24. package/dist/commonjs/customHooks/storageInspector.js +486 -0
  25. package/dist/commonjs/i18n/locales/en.json +8 -2
  26. package/dist/commonjs/index.d.ts +1 -0
  27. package/dist/commonjs/index.js +16 -1
  28. package/dist/commonjs/storage.d.ts +5 -0
  29. package/dist/commonjs/storage.js +18 -0
  30. package/dist/commonjs/types/enums.d.ts +5 -0
  31. package/dist/commonjs/types/enums.js +5 -0
  32. package/dist/esm/components/AnalyticsDetail.js +6 -2
  33. package/dist/esm/components/AnalyticsEventCard.js +2 -1
  34. package/dist/esm/components/DiffViewer.js +1 -1
  35. package/dist/esm/components/Inspector/BundleTab.js +22 -0
  36. package/dist/esm/components/Inspector/DeviceInfoTab.d.ts +3 -0
  37. package/dist/esm/components/Inspector/DeviceInfoTab.js +796 -0
  38. package/dist/esm/components/Inspector/InspectorHeader.js +13 -15
  39. package/dist/esm/components/Inspector/LogDetail.js +7 -4
  40. package/dist/esm/components/Inspector/MainScreen.js +7 -3
  41. package/dist/esm/components/Inspector/NetworkTab.js +93 -16
  42. package/dist/esm/components/Inspector/PerformanceTab.js +39 -13
  43. package/dist/esm/components/Inspector/SettingsPanel.js +441 -230
  44. package/dist/esm/components/Inspector/StorageTab.d.ts +3 -0
  45. package/dist/esm/components/Inspector/StorageTab.js +853 -0
  46. package/dist/esm/components/Inspector/TabBar.js +15 -1
  47. package/dist/esm/components/Inspector/TelemetryConsentModal.js +11 -2
  48. package/dist/esm/components/Inspector/UpdateAvailableModal.js +4 -1
  49. package/dist/esm/components/NetworkIcons.d.ts +6 -0
  50. package/dist/esm/components/NetworkIcons.js +23 -0
  51. package/dist/esm/constants/version.d.ts +1 -1
  52. package/dist/esm/constants/version.js +1 -1
  53. package/dist/esm/customHooks/bundleAnalyzer.js +44 -22
  54. package/dist/esm/customHooks/storageInspector.d.ts +67 -0
  55. package/dist/esm/customHooks/storageInspector.js +469 -0
  56. package/dist/esm/i18n/locales/en.json +8 -2
  57. package/dist/esm/index.d.ts +1 -0
  58. package/dist/esm/index.js +5 -0
  59. package/dist/esm/storage.d.ts +5 -0
  60. package/dist/esm/storage.js +5 -0
  61. package/dist/esm/types/enums.d.ts +5 -0
  62. package/dist/esm/types/enums.js +5 -0
  63. package/package.json +9 -1
  64. package/src/analytics.ts +35 -0
  65. package/src/bundle.ts +25 -0
  66. package/src/components/AnalyticsDetail.tsx +870 -0
  67. package/src/components/AnalyticsEventCard.tsx +442 -0
  68. package/src/components/AnalyticsGraph.tsx +583 -0
  69. package/src/components/AnimatedEntrance.tsx +64 -0
  70. package/src/components/AppHeaderLogo.tsx +109 -0
  71. package/src/components/BrandCircleIcon.tsx +144 -0
  72. package/src/components/BrandSquareIcon.tsx +144 -0
  73. package/src/components/CodeSnippet.tsx +725 -0
  74. package/src/components/ConsoleLogCard.tsx +628 -0
  75. package/src/components/CopyButton.tsx +87 -0
  76. package/src/components/DiffViewer.tsx +82 -0
  77. package/src/components/DomainHeader.tsx +237 -0
  78. package/src/components/EmptyState.tsx +73 -0
  79. package/src/components/EndOfListFooter.tsx +100 -0
  80. package/src/components/ErrorBoundary.tsx +688 -0
  81. package/src/components/HeadersSection.tsx +192 -0
  82. package/src/components/HighlightText.tsx +100 -0
  83. package/src/components/Inspector/AnalyticsFilterModal.tsx +1250 -0
  84. package/src/components/Inspector/AnalyticsTab.tsx +1336 -0
  85. package/src/components/Inspector/BundleTab.tsx +4760 -0
  86. package/src/components/Inspector/ConsoleTab.tsx +702 -0
  87. package/src/components/Inspector/CrashDetail.tsx +941 -0
  88. package/src/components/Inspector/CrashFilterModal.tsx +721 -0
  89. package/src/components/Inspector/CrashTab.tsx +871 -0
  90. package/src/components/Inspector/DeviceInfoTab.tsx +1204 -0
  91. package/src/components/Inspector/FabLauncher.tsx +80 -0
  92. package/src/components/Inspector/InspectorContext.tsx +28 -0
  93. package/src/components/Inspector/InspectorHeader.tsx +972 -0
  94. package/src/components/Inspector/LogDetail.tsx +1431 -0
  95. package/src/components/Inspector/MainScreen.tsx +262 -0
  96. package/src/components/Inspector/NavigationTracker.tsx +13 -0
  97. package/src/components/Inspector/NetworkDetail.tsx +794 -0
  98. package/src/components/Inspector/NetworkTab.tsx +1183 -0
  99. package/src/components/Inspector/NpmUpdateToast.tsx +392 -0
  100. package/src/components/Inspector/PerformanceTab.tsx +1928 -0
  101. package/src/components/Inspector/ReduxDetail.tsx +1651 -0
  102. package/src/components/Inspector/ReduxTab.tsx +954 -0
  103. package/src/components/Inspector/SettingsPanel.tsx +3447 -0
  104. package/src/components/Inspector/StorageTab.tsx +1048 -0
  105. package/src/components/Inspector/TabBar.tsx +207 -0
  106. package/src/components/Inspector/TelemetryConsentModal.tsx +416 -0
  107. package/src/components/Inspector/UpdateAvailableModal.tsx +557 -0
  108. package/src/components/JsonViewer.tsx +486 -0
  109. package/src/components/LogCard.tsx +491 -0
  110. package/src/components/LogSyntaxHighlighter.tsx +178 -0
  111. package/src/components/MetaAccordion.tsx +340 -0
  112. package/src/components/MiniBarChart.tsx +42 -0
  113. package/src/components/MiniLineChart.tsx +33 -0
  114. package/src/components/NetworkIcons.tsx +2221 -0
  115. package/src/components/SectionHeader.tsx +113 -0
  116. package/src/components/SegmentedTabs.tsx +83 -0
  117. package/src/components/Slider.tsx +300 -0
  118. package/src/components/SourcePageCard.tsx +148 -0
  119. package/src/components/Toast.tsx +131 -0
  120. package/src/components/TouchableScale.tsx +91 -0
  121. package/src/components/TreeNode.tsx +186 -0
  122. package/src/console.ts +24 -0
  123. package/src/constants/index.ts +38 -0
  124. package/src/constants/version.ts +3 -0
  125. package/src/crash.ts +32 -0
  126. package/src/customHooks/analyticsLogger.ts +336 -0
  127. package/src/customHooks/bundleAnalyzer.ts +1256 -0
  128. package/src/customHooks/consoleLogger.ts +497 -0
  129. package/src/customHooks/crashHandler.ts +944 -0
  130. package/src/customHooks/logFilters.ts +32 -0
  131. package/src/customHooks/networkLogger.ts +419 -0
  132. package/src/customHooks/performanceTracker.ts +1014 -0
  133. package/src/customHooks/reduxLogger.ts +406 -0
  134. package/src/customHooks/storageInspector.ts +509 -0
  135. package/src/customHooks/useAccordion.tsx +60 -0
  136. package/src/decorators/index.ts +184 -0
  137. package/src/helpers/gaAnalyticsRegistry.ts +204 -0
  138. package/src/helpers/index.ts +860 -0
  139. package/src/helpers/memoryManager.ts +138 -0
  140. package/src/helpers/searchQueryParser.ts +283 -0
  141. package/src/helpers/settingsStore.ts +171 -0
  142. package/src/helpers/telemetry.ts +505 -0
  143. package/src/helpers/toast.ts +17 -0
  144. package/src/i18n/index.ts +69 -0
  145. package/src/i18n/locales/en.json +1058 -0
  146. package/src/index.tsx +2356 -0
  147. package/src/native/NativeInspector.ts +397 -0
  148. package/src/native/NativeNetworkInspector.ts +24 -0
  149. package/src/network.ts +22 -0
  150. package/src/performance.ts +38 -0
  151. package/src/redux.ts +23 -0
  152. package/src/storage.ts +18 -0
  153. package/src/styles/AppColors.ts +408 -0
  154. package/src/styles/AppFonts.ts +8 -0
  155. package/src/styles/common.ts +209 -0
  156. package/src/styles/index.ts +1570 -0
  157. package/src/types/enums.ts +199 -0
  158. package/src/types/index.ts +34 -0
  159. package/src/types/interfaces.ts +515 -0
@@ -0,0 +1,138 @@
1
+ import {AppState, NativeEventSubscription, Platform} from 'react-native';
2
+ import {pruneNetworkLogs} from '../customHooks/networkLogger';
3
+ import {pruneConsoleLogs} from '../customHooks/consoleLogger';
4
+ import {pruneReduxHistory} from '../customHooks/reduxLogger';
5
+ import {pruneAnalyticsLogs} from '../customHooks/analyticsLogger';
6
+ import {pruneCrashRecords, addCrashBreadcrumb} from '../customHooks/crashHandler';
7
+ import {prunePerformanceEvents} from '../customHooks/performanceTracker';
8
+ import {showToast} from './toast';
9
+
10
+ export interface MemoryPruneSummary {
11
+ timestamp: number;
12
+ prunedNetwork: number;
13
+ prunedConsole: number;
14
+ prunedRedux: number;
15
+ prunedAnalytics: number;
16
+ prunedCrashes: number;
17
+ prunedPerformance: number;
18
+ totalPruned: number;
19
+ triggeredBy: 'memory-warning' | 'manual' | 'ram-threshold';
20
+ }
21
+
22
+ type MemoryWarningListener = (summary: MemoryPruneSummary) => void;
23
+
24
+ const memoryListeners = new Set<MemoryWarningListener>();
25
+ let appStateSubscription: NativeEventSubscription | null = null;
26
+ let isMemoryWarningHandlerActive = false;
27
+ let lastPruneTimestamp = 0;
28
+
29
+ /**
30
+ * Prunes all in-memory log stores to prevent OOM / memory pressure.
31
+ * Keeps targetRetentionPct (default: 50%) of existing logs.
32
+ */
33
+ export const pruneAllLogs = (
34
+ triggeredBy: MemoryPruneSummary['triggeredBy'] = 'manual',
35
+ targetRetentionPct: number = 0.5,
36
+ ): MemoryPruneSummary => {
37
+ const targetPct = Math.max(0.1, Math.min(1.0, targetRetentionPct));
38
+
39
+ const prunedNetwork = pruneNetworkLogs(undefined);
40
+ const prunedConsole = pruneConsoleLogs(undefined);
41
+ const prunedRedux = pruneReduxHistory(undefined);
42
+ const prunedAnalytics = pruneAnalyticsLogs(undefined);
43
+ const prunedCrashes = pruneCrashRecords(undefined);
44
+ const prunedPerformance = prunePerformanceEvents(undefined);
45
+
46
+ const totalPruned =
47
+ prunedNetwork +
48
+ prunedConsole +
49
+ prunedRedux +
50
+ prunedAnalytics +
51
+ prunedCrashes +
52
+ prunedPerformance;
53
+
54
+ lastPruneTimestamp = Date.now();
55
+
56
+ // Trigger Hermes/V8 Garbage Collection if exposed
57
+ if (typeof (globalThis as any).gc === 'function') {
58
+ try {
59
+ (globalThis as any).gc();
60
+ } catch {}
61
+ }
62
+
63
+ const summary: MemoryPruneSummary = {
64
+ timestamp: lastPruneTimestamp,
65
+ prunedNetwork,
66
+ prunedConsole,
67
+ prunedRedux,
68
+ prunedAnalytics,
69
+ prunedCrashes,
70
+ prunedPerformance,
71
+ totalPruned,
72
+ triggeredBy,
73
+ };
74
+
75
+ try {
76
+ addCrashBreadcrumb(
77
+ 'system',
78
+ `[MemoryManager] Low memory auto-prune executed (${triggeredBy}). Freed ${totalPruned} log records.`,
79
+ summary,
80
+ );
81
+ } catch {}
82
+
83
+ memoryListeners.forEach(listener => {
84
+ try {
85
+ listener(summary);
86
+ } catch {}
87
+ });
88
+
89
+ return summary;
90
+ };
91
+
92
+ /**
93
+ * Starts listening to OS memory pressure warnings via AppState 'memoryWarning'.
94
+ * Automatically prunes logs and notifies subscribers when memory is constrained.
95
+ */
96
+ export const setupMemoryWarningHandler = (): (() => void) => {
97
+ if (isMemoryWarningHandlerActive) {
98
+ return () => {};
99
+ }
100
+
101
+ isMemoryWarningHandlerActive = true;
102
+
103
+ try {
104
+ appStateSubscription = AppState.addEventListener('memoryWarning' as any, (state: any) => {
105
+ const summary = pruneAllLogs('memory-warning', 0.5);
106
+ try {
107
+ showToast(`Memory Warning: Auto-pruned ${summary.totalPruned} inspector logs`);
108
+ } catch {}
109
+ });
110
+ } catch (e) {
111
+ // AppState memoryWarning might not be supported on all mock/test platforms
112
+ }
113
+
114
+ return () => {
115
+ if (appStateSubscription) {
116
+ try {
117
+ appStateSubscription.remove();
118
+ } catch {}
119
+ appStateSubscription = null;
120
+ }
121
+ isMemoryWarningHandlerActive = false;
122
+ };
123
+ };
124
+
125
+ /**
126
+ * Subscribes to memory prune events.
127
+ */
128
+ export const subscribeMemoryWarning = (
129
+ listener: MemoryWarningListener,
130
+ ): (() => void) => {
131
+ memoryListeners.add(listener);
132
+ return () => {
133
+ memoryListeners.delete(listener);
134
+ };
135
+ };
136
+
137
+ export const getLastPruneTimestamp = (): number => lastPruneTimestamp;
138
+ export const isMemoryHandlerActive = (): boolean => isMemoryWarningHandlerActive;
@@ -0,0 +1,283 @@
1
+ import {NetworkLog, SearchScope} from '../types';
2
+
3
+ export interface SearchQueryOptions {
4
+ scope?: SearchScope;
5
+ isRegex?: boolean;
6
+ isCaseSensitive?: boolean;
7
+ }
8
+
9
+ /**
10
+ * Parses query strings like `method:POST is:error slow:>1s status:200 auth`
11
+ * and checks whether a given NetworkLog matches all tokens against the specified scope.
12
+ */
13
+ export function matchNetworkLogQuery(
14
+ log: NetworkLog,
15
+ searchQuery: string,
16
+ routePath?: string,
17
+ options?: SearchQueryOptions,
18
+ ): boolean {
19
+ if (!searchQuery || searchQuery.trim().length === 0) return true;
20
+
21
+ const rawTokens =
22
+ searchQuery.trim().match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [];
23
+
24
+ if (rawTokens.length === 0) return true;
25
+
26
+ const isCaseSensitive = Boolean(options?.isCaseSensitive);
27
+ const isRegex = Boolean(options?.isRegex);
28
+ const scope = options?.scope || 'all';
29
+
30
+ const methodRaw = log.method || '';
31
+ const urlRaw = log.url || '';
32
+ const statusNum =
33
+ typeof log.status === 'number'
34
+ ? log.status
35
+ : parseInt(String(log.status || 0), 10);
36
+ const statusStr = String(log.status ?? '');
37
+ const durationNum = log.duration || 0;
38
+ const clientRaw = log.client || '';
39
+ const reqRaw =
40
+ typeof log.request === 'string'
41
+ ? log.request
42
+ : JSON.stringify(log.request || '');
43
+ const resRaw =
44
+ typeof log.response === 'string'
45
+ ? log.response
46
+ : JSON.stringify(log.response || '');
47
+ const reqHeadersRaw = JSON.stringify(log.requestHeaders || '');
48
+ const resHeadersRaw = JSON.stringify(log.responseHeaders || '');
49
+ const pathRaw = routePath || '';
50
+
51
+ // Construct search corpus based on active scope
52
+ const getScopedCorpus = (caseSens: boolean): string => {
53
+ let parts: string[] = [];
54
+ if (scope === 'url') {
55
+ parts = [methodRaw, urlRaw, pathRaw];
56
+ } else if (scope === 'reqBody') {
57
+ parts = [reqRaw];
58
+ } else if (scope === 'resBody') {
59
+ parts = [resRaw];
60
+ } else if (scope === 'headers') {
61
+ parts = [reqHeadersRaw, resHeadersRaw];
62
+ } else {
63
+ parts = [
64
+ methodRaw,
65
+ urlRaw,
66
+ statusStr,
67
+ pathRaw,
68
+ clientRaw,
69
+ reqRaw,
70
+ resRaw,
71
+ reqHeadersRaw,
72
+ resHeadersRaw,
73
+ ];
74
+ }
75
+ const combined = parts.join(' ');
76
+ return caseSens ? combined : combined.toLowerCase();
77
+ };
78
+
79
+ const targetCorpus = getScopedCorpus(isCaseSensitive);
80
+
81
+ const methodStr = methodRaw.toLowerCase();
82
+ const urlStr = urlRaw.toLowerCase();
83
+ const clientStr = clientRaw.toLowerCase();
84
+ const reqStr = reqRaw.toLowerCase();
85
+ const resStr = resRaw.toLowerCase();
86
+ const reqHeadersStr = reqHeadersRaw.toLowerCase();
87
+ const resHeadersStr = resHeadersRaw.toLowerCase();
88
+ const pathStr = pathRaw.toLowerCase();
89
+
90
+ for (const rawToken of rawTokens) {
91
+ const token = rawToken.replace(/^["']|["']$/g, '').trim();
92
+ if (!token) continue;
93
+
94
+ const lowerToken = token.toLowerCase();
95
+
96
+ // 1. method:<VAL> or m:<VAL>
97
+ if (lowerToken.startsWith('method:') || lowerToken.startsWith('m:')) {
98
+ const targetMethod = lowerToken.replace(/^(method:|m:)/, '').trim();
99
+ if (targetMethod.startsWith('!')) {
100
+ if (methodStr === targetMethod.slice(1)) return false;
101
+ } else {
102
+ if (methodStr !== targetMethod) return false;
103
+ }
104
+ continue;
105
+ }
106
+
107
+ // 2. status:<VAL> or s:<VAL>
108
+ if (lowerToken.startsWith('status:') || lowerToken.startsWith('s:')) {
109
+ const val = lowerToken.replace(/^(status:|s:)/, '').trim();
110
+ if (val.startsWith('>=')) {
111
+ const threshold = parseInt(val.slice(2), 10);
112
+ if (isNaN(threshold) || statusNum < threshold) return false;
113
+ } else if (val.startsWith('>')) {
114
+ const threshold = parseInt(val.slice(1), 10);
115
+ if (isNaN(threshold) || statusNum <= threshold) return false;
116
+ } else if (val.startsWith('<=')) {
117
+ const threshold = parseInt(val.slice(2), 10);
118
+ if (isNaN(threshold) || statusNum > threshold) return false;
119
+ } else if (val.startsWith('<')) {
120
+ const threshold = parseInt(val.slice(1), 10);
121
+ if (isNaN(threshold) || statusNum >= threshold) return false;
122
+ } else if (val.endsWith('xx') || val.endsWith('x')) {
123
+ const prefix = val.replace(/x/g, '');
124
+ if (!statusStr.startsWith(prefix)) return false;
125
+ } else {
126
+ const targetCode = parseInt(val, 10);
127
+ if (!isNaN(targetCode)) {
128
+ if (statusNum !== targetCode) return false;
129
+ } else if (!statusStr.includes(val)) {
130
+ return false;
131
+ }
132
+ }
133
+ continue;
134
+ }
135
+
136
+ // 3. is:<FLAG>
137
+ if (lowerToken.startsWith('is:')) {
138
+ const flag = lowerToken.slice(3).trim();
139
+ if (
140
+ flag === 'error' ||
141
+ flag === 'failed' ||
142
+ flag === 'err' ||
143
+ flag === 'fail'
144
+ ) {
145
+ const isErr =
146
+ log.status === 0 || log.status == null || statusNum >= 400;
147
+ if (!isErr) return false;
148
+ } else if (flag === 'success' || flag === 'ok' || flag === '2xx') {
149
+ const isSuccess = statusNum >= 200 && statusNum < 300;
150
+ if (!isSuccess) return false;
151
+ } else if (flag === 'slow') {
152
+ if (durationNum < 1000) return false;
153
+ } else if (flag === 'fast') {
154
+ if (durationNum >= 200) return false;
155
+ } else if (flag === 'graphql' || flag === 'gql') {
156
+ const isGql =
157
+ urlStr.includes('graphql') ||
158
+ clientStr === 'graphql' ||
159
+ clientStr === 'apollo';
160
+ if (!isGql) return false;
161
+ } else if (flag === 'https') {
162
+ if (!urlStr.startsWith('https')) return false;
163
+ } else if (flag === 'http') {
164
+ if (urlStr.startsWith('https') || !urlStr.startsWith('http'))
165
+ return false;
166
+ } else if (flag === 'json') {
167
+ const isJson =
168
+ reqHeadersStr.includes('application/json') ||
169
+ resHeadersStr.includes('application/json') ||
170
+ urlStr.includes('.json');
171
+ if (!isJson) return false;
172
+ }
173
+ continue;
174
+ }
175
+
176
+ // 4. slow:<DURATION> or dur:<DURATION> or duration:<DURATION>
177
+ if (
178
+ lowerToken.startsWith('slow:') ||
179
+ lowerToken.startsWith('dur:') ||
180
+ lowerToken.startsWith('duration:')
181
+ ) {
182
+ const val = lowerToken.replace(/^(slow:|dur:|duration:)/, '').trim();
183
+ let thresholdMs = 0;
184
+ let isGreater = true;
185
+
186
+ let cleanVal = val;
187
+ if (val.startsWith('>=')) {
188
+ isGreater = true;
189
+ cleanVal = val.slice(2);
190
+ } else if (val.startsWith('>')) {
191
+ isGreater = true;
192
+ cleanVal = val.slice(1);
193
+ } else if (val.startsWith('<=')) {
194
+ isGreater = false;
195
+ cleanVal = val.slice(2);
196
+ } else if (val.startsWith('<')) {
197
+ isGreater = false;
198
+ cleanVal = val.slice(1);
199
+ }
200
+
201
+ if (cleanVal.endsWith('s') && !cleanVal.endsWith('ms')) {
202
+ thresholdMs = parseFloat(cleanVal.slice(0, -1)) * 1000;
203
+ } else if (cleanVal.endsWith('ms')) {
204
+ thresholdMs = parseFloat(cleanVal.slice(0, -2));
205
+ } else {
206
+ thresholdMs = parseFloat(cleanVal);
207
+ }
208
+
209
+ if (!isNaN(thresholdMs)) {
210
+ if (isGreater) {
211
+ if (durationNum < thresholdMs) return false;
212
+ } else {
213
+ if (durationNum > thresholdMs) return false;
214
+ }
215
+ }
216
+ continue;
217
+ }
218
+
219
+ // 5. client:<CLIENT> or c:<CLIENT>
220
+ if (lowerToken.startsWith('client:') || lowerToken.startsWith('c:')) {
221
+ const targetClient = lowerToken.replace(/^(client:|c:)/, '').trim();
222
+ if (!clientStr.includes(targetClient)) return false;
223
+ continue;
224
+ }
225
+
226
+ // 6. domain:<DOMAIN> or d:<DOMAIN>
227
+ if (lowerToken.startsWith('domain:') || lowerToken.startsWith('d:')) {
228
+ const targetDomain = lowerToken.replace(/^(domain:|d:)/, '').trim();
229
+ if (!urlStr.includes(targetDomain)) return false;
230
+ continue;
231
+ }
232
+
233
+ // 7. path:<PATH> or p:<PATH>
234
+ if (lowerToken.startsWith('path:') || lowerToken.startsWith('p:')) {
235
+ const targetPath = lowerToken.replace(/^(path:|p:)/, '').trim();
236
+ if (!urlStr.includes(targetPath) && !pathStr.includes(targetPath)) {
237
+ return false;
238
+ }
239
+ continue;
240
+ }
241
+
242
+ // 8. req:<TERM> or body:<TERM>
243
+ if (lowerToken.startsWith('req:') || lowerToken.startsWith('body:')) {
244
+ const target = lowerToken.replace(/^(req:|body:)/, '').trim();
245
+ if (!reqStr.includes(target)) return false;
246
+ continue;
247
+ }
248
+
249
+ // 9. res:<TERM>
250
+ if (lowerToken.startsWith('res:')) {
251
+ const target = lowerToken.slice(4).trim();
252
+ if (!resStr.includes(target)) return false;
253
+ continue;
254
+ }
255
+
256
+ // 10. header:<TERM> or h:<TERM>
257
+ if (lowerToken.startsWith('header:') || lowerToken.startsWith('h:')) {
258
+ const target = lowerToken.replace(/^(header:|h:)/, '').trim();
259
+ if (!reqHeadersStr.includes(target) && !resHeadersStr.includes(target)) {
260
+ return false;
261
+ }
262
+ continue;
263
+ }
264
+
265
+ // 11. Generic Match (handles Regex, Case-sensitivity, and Scoped corpus)
266
+ if (isRegex) {
267
+ try {
268
+ const re = new RegExp(token, isCaseSensitive ? '' : 'i');
269
+ if (!re.test(targetCorpus)) return false;
270
+ } catch {
271
+ const queryTerm = isCaseSensitive ? token : lowerToken;
272
+ if (!targetCorpus.includes(queryTerm)) return false;
273
+ }
274
+ } else {
275
+ const queryTerm = isCaseSensitive ? token : lowerToken;
276
+ if (!targetCorpus.includes(queryTerm)) {
277
+ return false;
278
+ }
279
+ }
280
+ }
281
+
282
+ return true;
283
+ }
@@ -0,0 +1,171 @@
1
+ // #5 — Persistence layer for the inspector's settings selections.
2
+ //
3
+ // Backed by iOS Settings (NSUserDefaults) for iOS, or custom storage if passed (e.g. AsyncStorage/MMKV).
4
+ // Contains an in-memory fallback for Android or Jest test environments.
5
+
6
+ import { Platform, Settings } from 'react-native';
7
+ import {InspectorStorage, PersistedSettings} from '../types';
8
+ import {
9
+ getNativeStorageItem,
10
+ setNativeStorageItem,
11
+ isNativeModuleAvailable,
12
+ } from '../native/NativeInspector';
13
+
14
+ export {InspectorStorage, PersistedSettings};
15
+
16
+ // In-memory fallback (settings survive for the app session only).
17
+ const memory = new Map<string, string>();
18
+
19
+ let customStorage: InspectorStorage | null = null;
20
+
21
+ export function setCustomStorage(storage: InspectorStorage | null) {
22
+ customStorage = storage;
23
+ }
24
+
25
+ const SETTINGS_KEY = 'rn-inapp-inspector.settings.v1';
26
+
27
+ export async function loadSettings(): Promise<PersistedSettings> {
28
+ try {
29
+ if (customStorage) {
30
+ const raw = await customStorage.getItem(SETTINGS_KEY);
31
+ if (!raw) return {};
32
+ const parsed = JSON.parse(raw);
33
+ return parsed && typeof parsed === 'object' ? parsed : {};
34
+ }
35
+
36
+ if (isNativeModuleAvailable()) {
37
+ const raw = await getNativeStorageItem(SETTINGS_KEY);
38
+ if (raw) {
39
+ const parsed = JSON.parse(raw);
40
+ return parsed && typeof parsed === 'object' ? parsed : {};
41
+ }
42
+ }
43
+
44
+ if (Platform.OS === 'ios') {
45
+ const raw = Settings.get(SETTINGS_KEY);
46
+ if (!raw) return {};
47
+ if (typeof raw === 'string') {
48
+ const parsed = JSON.parse(raw);
49
+ return parsed && typeof parsed === 'object' ? parsed : {};
50
+ }
51
+ return raw && typeof raw === 'object' ? raw : {};
52
+ }
53
+
54
+ // In-memory fallback
55
+ const raw = memory.get(SETTINGS_KEY);
56
+ if (!raw) return {};
57
+ const parsed = JSON.parse(raw);
58
+ return parsed && typeof parsed === 'object' ? parsed : {};
59
+ } catch {
60
+ return {};
61
+ }
62
+ }
63
+
64
+ export function saveSettings(settings: PersistedSettings): void {
65
+ try {
66
+ const raw = JSON.stringify(settings);
67
+
68
+ if (customStorage) {
69
+ customStorage.setItem(SETTINGS_KEY, raw);
70
+ return;
71
+ }
72
+
73
+ if (isNativeModuleAvailable()) {
74
+ setNativeStorageItem(SETTINGS_KEY, raw);
75
+ }
76
+
77
+ if (Platform.OS === 'ios') {
78
+ Settings.set({ [SETTINGS_KEY]: raw });
79
+ return;
80
+ }
81
+
82
+ memory.set(SETTINGS_KEY, raw);
83
+ } catch {
84
+ // ignore
85
+ }
86
+ }
87
+
88
+ export async function clearPersistedSettings(): Promise<void> {
89
+ try {
90
+ if (customStorage) {
91
+ if (typeof (customStorage as any).removeItem === 'function') {
92
+ await (customStorage as any).removeItem(SETTINGS_KEY);
93
+ } else {
94
+ await customStorage.setItem(SETTINGS_KEY, '');
95
+ }
96
+ return;
97
+ }
98
+
99
+ if (isNativeModuleAvailable()) {
100
+ await setNativeStorageItem(SETTINGS_KEY, null);
101
+ }
102
+
103
+ if (Platform.OS === 'ios') {
104
+ Settings.set({ [SETTINGS_KEY]: null });
105
+ return;
106
+ }
107
+
108
+ memory.delete(SETTINGS_KEY);
109
+ } catch {
110
+ // ignore
111
+ }
112
+ }
113
+
114
+ export const isPersistentStorageAvailable = () => {
115
+ return customStorage !== null || isNativeModuleAvailable() || Platform.OS === 'ios';
116
+ };
117
+
118
+ export function getCustomStorage(): InspectorStorage | null {
119
+ return customStorage;
120
+ }
121
+
122
+ export interface RamLimitsProfile {
123
+ maxNetworkLogs: number;
124
+ maxConsoleLogs: number;
125
+ maxAnalyticsEvents: number;
126
+ maxCrashRecords: number;
127
+ profileName: 'High-End' | 'Standard' | 'Compact' | 'Ultra-Light';
128
+ freeRamMb: number;
129
+ }
130
+
131
+ export function calculateRamBasedLimits(freeRamMb: number): RamLimitsProfile {
132
+ if (freeRamMb >= 3000) {
133
+ return {
134
+ maxNetworkLogs: 100,
135
+ maxConsoleLogs: 100,
136
+ maxAnalyticsEvents: 75,
137
+ maxCrashRecords: 50,
138
+ profileName: 'High-End',
139
+ freeRamMb,
140
+ };
141
+ } else if (freeRamMb >= 1500) {
142
+ return {
143
+ maxNetworkLogs: 100,
144
+ maxConsoleLogs: 100,
145
+ maxAnalyticsEvents: 75,
146
+ maxCrashRecords: 50,
147
+ profileName: 'Standard',
148
+ freeRamMb,
149
+ };
150
+ } else if (freeRamMb >= 600) {
151
+ return {
152
+ maxNetworkLogs: 100,
153
+ maxConsoleLogs: 100,
154
+ maxAnalyticsEvents: 50,
155
+ maxCrashRecords: 25,
156
+ profileName: 'Compact',
157
+ freeRamMb,
158
+ };
159
+ } else {
160
+ return {
161
+ maxNetworkLogs: 50,
162
+ maxConsoleLogs: 50,
163
+ maxAnalyticsEvents: 25,
164
+ maxCrashRecords: 15,
165
+ profileName: 'Ultra-Light',
166
+ freeRamMb,
167
+ };
168
+ }
169
+ }
170
+
171
+