react-native-inapp-inspector 2.3.14 → 2.3.16

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 (125) hide show
  1. package/README.md +1 -0
  2. package/dist/commonjs/components/AnimatedEntrance.js +3 -3
  3. package/dist/commonjs/components/ConsoleLogCard.js +174 -100
  4. package/dist/commonjs/components/Inspector/AnalyticsTab.js +0 -22
  5. package/dist/commonjs/components/Inspector/ConsoleTab.js +25 -38
  6. package/dist/commonjs/components/Inspector/CrashTab.js +19 -28
  7. package/dist/commonjs/components/Inspector/DeviceInfoTab.js +198 -98
  8. package/dist/commonjs/components/Inspector/FeedbackModal.d.ts +7 -0
  9. package/dist/commonjs/components/Inspector/FeedbackModal.js +552 -0
  10. package/dist/commonjs/components/Inspector/InspectorHeader.js +58 -6
  11. package/dist/commonjs/components/Inspector/MainScreen.js +100 -58
  12. package/dist/commonjs/components/Inspector/NetworkTab.js +26 -371
  13. package/dist/commonjs/components/Inspector/NpmStarPrompt.d.ts +3 -0
  14. package/dist/commonjs/components/Inspector/NpmStarPrompt.js +494 -0
  15. package/dist/commonjs/components/Inspector/PerformanceTab.js +40 -7
  16. package/dist/commonjs/components/Inspector/ReduxTab.js +3 -23
  17. package/dist/commonjs/components/Inspector/SettingsPanel.js +1035 -173
  18. package/dist/commonjs/components/Inspector/StorageTab.js +23 -28
  19. package/dist/commonjs/components/Inspector/TabBar.js +50 -15
  20. package/dist/commonjs/components/JsonViewer.js +14 -2
  21. package/dist/commonjs/components/LogCard.js +428 -227
  22. package/dist/commonjs/components/NetworkIcons.d.ts +5 -0
  23. package/dist/commonjs/components/NetworkIcons.js +33 -2
  24. package/dist/commonjs/constants/version.d.ts +1 -1
  25. package/dist/commonjs/constants/version.js +1 -1
  26. package/dist/commonjs/customHooks/analyticsLogger.js +1 -1
  27. package/dist/commonjs/customHooks/consoleLogger.js +34 -2
  28. package/dist/commonjs/customHooks/crashHandler.js +1 -1
  29. package/dist/commonjs/customHooks/networkLogger.js +184 -26
  30. package/dist/commonjs/helpers/index.d.ts +1 -0
  31. package/dist/commonjs/helpers/index.js +1 -0
  32. package/dist/commonjs/helpers/remoteConfig.d.ts +4 -0
  33. package/dist/commonjs/helpers/remoteConfig.js +165 -0
  34. package/dist/commonjs/helpers/searchQueryParser.d.ts +1 -1
  35. package/dist/commonjs/helpers/searchQueryParser.js +39 -291
  36. package/dist/commonjs/helpers/telemetry.d.ts +5 -0
  37. package/dist/commonjs/helpers/telemetry.js +339 -8
  38. package/dist/commonjs/index.d.ts +1 -0
  39. package/dist/commonjs/index.js +173 -29
  40. package/dist/commonjs/styles/AppFonts.d.ts +2 -0
  41. package/dist/commonjs/styles/AppFonts.js +1 -0
  42. package/dist/commonjs/styles/index.d.ts +22 -0
  43. package/dist/commonjs/styles/index.js +19 -0
  44. package/dist/commonjs/types/enums.d.ts +1 -0
  45. package/dist/commonjs/types/enums.js +1 -0
  46. package/dist/commonjs/types/interfaces.d.ts +12 -0
  47. package/dist/esm/components/AnimatedEntrance.js +3 -3
  48. package/dist/esm/components/ConsoleLogCard.js +141 -100
  49. package/dist/esm/components/Inspector/AnalyticsTab.js +1 -23
  50. package/dist/esm/components/Inspector/ConsoleTab.js +26 -39
  51. package/dist/esm/components/Inspector/CrashTab.js +20 -29
  52. package/dist/esm/components/Inspector/DeviceInfoTab.js +198 -98
  53. package/dist/esm/components/Inspector/FeedbackModal.d.ts +7 -0
  54. package/dist/esm/components/Inspector/FeedbackModal.js +515 -0
  55. package/dist/esm/components/Inspector/InspectorHeader.js +60 -8
  56. package/dist/esm/components/Inspector/MainScreen.js +101 -59
  57. package/dist/esm/components/Inspector/NetworkTab.js +27 -372
  58. package/dist/esm/components/Inspector/NpmStarPrompt.d.ts +3 -0
  59. package/dist/esm/components/Inspector/NpmStarPrompt.js +454 -0
  60. package/dist/esm/components/Inspector/PerformanceTab.js +40 -7
  61. package/dist/esm/components/Inspector/ReduxTab.js +4 -24
  62. package/dist/esm/components/Inspector/SettingsPanel.js +1038 -176
  63. package/dist/esm/components/Inspector/StorageTab.js +23 -28
  64. package/dist/esm/components/Inspector/TabBar.js +50 -15
  65. package/dist/esm/components/JsonViewer.js +14 -2
  66. package/dist/esm/components/LogCard.js +422 -221
  67. package/dist/esm/components/NetworkIcons.d.ts +5 -0
  68. package/dist/esm/components/NetworkIcons.js +26 -0
  69. package/dist/esm/constants/version.d.ts +1 -1
  70. package/dist/esm/constants/version.js +1 -1
  71. package/dist/esm/customHooks/analyticsLogger.js +1 -1
  72. package/dist/esm/customHooks/consoleLogger.js +34 -2
  73. package/dist/esm/customHooks/crashHandler.js +1 -1
  74. package/dist/esm/customHooks/networkLogger.js +184 -26
  75. package/dist/esm/helpers/index.d.ts +1 -0
  76. package/dist/esm/helpers/index.js +1 -0
  77. package/dist/esm/helpers/remoteConfig.d.ts +4 -0
  78. package/dist/esm/helpers/remoteConfig.js +160 -0
  79. package/dist/esm/helpers/searchQueryParser.d.ts +1 -1
  80. package/dist/esm/helpers/searchQueryParser.js +39 -291
  81. package/dist/esm/helpers/telemetry.d.ts +5 -0
  82. package/dist/esm/helpers/telemetry.js +332 -9
  83. package/dist/esm/index.d.ts +1 -0
  84. package/dist/esm/index.js +171 -29
  85. package/dist/esm/styles/AppFonts.d.ts +2 -0
  86. package/dist/esm/styles/AppFonts.js +1 -0
  87. package/dist/esm/styles/index.d.ts +22 -0
  88. package/dist/esm/styles/index.js +19 -0
  89. package/dist/esm/types/enums.d.ts +1 -0
  90. package/dist/esm/types/enums.js +1 -0
  91. package/dist/esm/types/interfaces.d.ts +12 -0
  92. package/package.json +1 -1
  93. package/src/components/AnimatedEntrance.tsx +3 -3
  94. package/src/components/ConsoleLogCard.tsx +154 -103
  95. package/src/components/Inspector/AnalyticsTab.tsx +0 -23
  96. package/src/components/Inspector/ConsoleTab.tsx +27 -41
  97. package/src/components/Inspector/CrashTab.tsx +19 -28
  98. package/src/components/Inspector/DeviceInfoTab.tsx +224 -102
  99. package/src/components/Inspector/FeedbackModal.tsx +626 -0
  100. package/src/components/Inspector/InspectorHeader.tsx +83 -5
  101. package/src/components/Inspector/MainScreen.tsx +61 -6
  102. package/src/components/Inspector/NetworkTab.tsx +70 -480
  103. package/src/components/Inspector/NpmStarPrompt.tsx +532 -0
  104. package/src/components/Inspector/PerformanceTab.tsx +44 -8
  105. package/src/components/Inspector/ReduxTab.tsx +9 -24
  106. package/src/components/Inspector/SettingsPanel.tsx +1162 -197
  107. package/src/components/Inspector/StorageTab.tsx +27 -30
  108. package/src/components/Inspector/TabBar.tsx +57 -20
  109. package/src/components/JsonViewer.tsx +15 -2
  110. package/src/components/LogCard.tsx +528 -339
  111. package/src/components/NetworkIcons.tsx +92 -0
  112. package/src/constants/version.ts +1 -1
  113. package/src/customHooks/analyticsLogger.ts +1 -1
  114. package/src/customHooks/consoleLogger.ts +36 -3
  115. package/src/customHooks/crashHandler.ts +1 -1
  116. package/src/customHooks/networkLogger.ts +214 -26
  117. package/src/helpers/index.ts +1 -0
  118. package/src/helpers/remoteConfig.ts +212 -0
  119. package/src/helpers/searchQueryParser.ts +42 -284
  120. package/src/helpers/telemetry.ts +381 -10
  121. package/src/index.tsx +374 -211
  122. package/src/styles/AppFonts.ts +2 -0
  123. package/src/styles/index.ts +20 -1
  124. package/src/types/enums.ts +1 -0
  125. package/src/types/interfaces.ts +12 -0
@@ -0,0 +1,212 @@
1
+ import {ActiveTab} from '../types';
2
+
3
+ /**
4
+ * Supported Remote Config keys for each module:
5
+ *
6
+ * 1. JSON Configuration:
7
+ * - `inspector_modules` (e.g. `{"apis": true, "logs": true, "analytics": true, ...}`)
8
+ * - `inapp_inspector_modules`
9
+ *
10
+ * 2. Individual Boolean Keys:
11
+ * - `inspector_module_apis` / `inapp_inspector_apis` / `inspector_apis`
12
+ * - `inspector_module_logs` / `inapp_inspector_logs` / `inspector_logs`
13
+ * - `inspector_module_analytics` / `inapp_inspector_analytics` / `inspector_analytics`
14
+ * - `inspector_module_redux` / `inapp_inspector_redux` / `inspector_redux`
15
+ * - `inspector_module_storage` / `inapp_inspector_storage` / `inspector_storage`
16
+ * - `inspector_module_device` / `inapp_inspector_device` / `inspector_device`
17
+ * - `inspector_module_crash` / `inapp_inspector_crash` / `inspector_crash`
18
+ * - `inspector_module_bundle` / `inapp_inspector_bundle` / `inspector_bundle`
19
+ * - `inspector_module_performance` / `inapp_inspector_performance` / `inspector_performance`
20
+ * - `inspector_module_debugging` / `inapp_inspector_debugging` / `inspector_debugging`
21
+ */
22
+
23
+ const MODULE_KEYS: ActiveTab[] = [
24
+ 'apis',
25
+ 'logs',
26
+ 'analytics',
27
+ 'redux',
28
+ 'storage',
29
+ 'device',
30
+ 'crash',
31
+ 'bundle',
32
+ 'performance',
33
+ 'debugging',
34
+ ];
35
+
36
+ let cachedRemoteConfigInstance: any = null;
37
+
38
+ /**
39
+ * Dynamically resolves Firebase Remote Config instance if installed.
40
+ */
41
+ export function getFirebaseRemoteConfig(customInstance?: any): any {
42
+ if (customInstance && typeof customInstance === 'object') {
43
+ cachedRemoteConfigInstance = customInstance;
44
+ return customInstance;
45
+ }
46
+ if (cachedRemoteConfigInstance) {
47
+ return cachedRemoteConfigInstance;
48
+ }
49
+ try {
50
+ const rcModule = require('@react-native-firebase/remote-config');
51
+ const instance =
52
+ typeof rcModule.default === 'function'
53
+ ? rcModule.default()
54
+ : typeof rcModule === 'function'
55
+ ? rcModule()
56
+ : rcModule;
57
+ cachedRemoteConfigInstance = instance;
58
+ return instance;
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Checks if @react-native-firebase/remote-config is available in the environment.
66
+ */
67
+ export function isFirebaseRemoteConfigAvailable(customInstance?: any): boolean {
68
+ return Boolean(getFirebaseRemoteConfig(customInstance));
69
+ }
70
+
71
+ /**
72
+ * Helper to extract a boolean or JSON value from a Firebase Remote Config parameter.
73
+ */
74
+ function extractValue(val: any): any {
75
+ if (val === null || val === undefined) return undefined;
76
+ if (typeof val === 'boolean') return val;
77
+ if (typeof val === 'number') return val !== 0;
78
+ if (typeof val === 'string') {
79
+ const lower = val.trim().toLowerCase();
80
+ if (lower === 'true' || lower === '1' || lower === 'enabled' || lower === 'on') return true;
81
+ if (lower === 'false' || lower === '0' || lower === 'disabled' || lower === 'off') return false;
82
+ try {
83
+ return JSON.parse(val);
84
+ } catch {
85
+ return undefined;
86
+ }
87
+ }
88
+ // Firebase Remote Config Value object with .asBoolean() or .asString()
89
+ if (typeof val.asBoolean === 'function') {
90
+ return val.asBoolean();
91
+ }
92
+ if (typeof val.asString === 'function') {
93
+ const str = val.asString();
94
+ const lower = str.trim().toLowerCase();
95
+ if (lower === 'true' || lower === '1' || lower === 'enabled' || lower === 'on') return true;
96
+ if (lower === 'false' || lower === '0' || lower === 'disabled' || lower === 'off') return false;
97
+ try {
98
+ return JSON.parse(str);
99
+ } catch {
100
+ return undefined;
101
+ }
102
+ }
103
+ return undefined;
104
+ }
105
+
106
+ /**
107
+ * Fetches and activates latest values from Firebase Remote Config, then parses module enable/disable statuses.
108
+ * Returns a partial or complete mapping of module visibility flags, or null if Remote Config is unavailable.
109
+ *
110
+ * @param customInstance Optional custom remoteConfig instance from consumer app
111
+ * @param fetchTimeoutMs Timeout for fetch operation in ms (default: 8000)
112
+ */
113
+ export async function fetchRemoteConfigModuleStatus(
114
+ customInstance?: any,
115
+ fetchTimeoutMs = 8000,
116
+ ): Promise<Partial<Record<ActiveTab, boolean>> | null> {
117
+ const rc = getFirebaseRemoteConfig(customInstance);
118
+ if (!rc) {
119
+ return null;
120
+ }
121
+
122
+ try {
123
+ // 1. Fetch & Activate with timeout protection
124
+ const fetchPromise = (async () => {
125
+ if (typeof rc.fetchAndActivate === 'function') {
126
+ await rc.fetchAndActivate();
127
+ } else {
128
+ if (typeof rc.fetch === 'function') {
129
+ await rc.fetch();
130
+ }
131
+ if (typeof rc.activate === 'function') {
132
+ await rc.activate();
133
+ }
134
+ }
135
+ })();
136
+
137
+ const timeoutPromise = new Promise((_, reject) =>
138
+ setTimeout(() => reject(new Error('Remote Config fetch timeout')), fetchTimeoutMs),
139
+ );
140
+
141
+ await Promise.race([fetchPromise, timeoutPromise]).catch(() => {
142
+ // If network fetch fails/times out, we still read cached/activated values
143
+ });
144
+
145
+ // 2. Read values from Remote Config
146
+ const result: Partial<Record<ActiveTab, boolean>> = {};
147
+
148
+ // Check JSON bundle key first: `inspector_modules` or `inapp_inspector_modules`
149
+ const jsonKeys = [
150
+ 'inspector_modules',
151
+ 'inapp_inspector_modules',
152
+ 'react_native_inapp_inspector_modules',
153
+ ];
154
+
155
+ for (const jKey of jsonKeys) {
156
+ let rawVal: any;
157
+ if (typeof rc.getValue === 'function') {
158
+ try {
159
+ rawVal = rc.getValue(jKey);
160
+ } catch {}
161
+ } else if (rc[jKey] !== undefined) {
162
+ rawVal = rc[jKey];
163
+ }
164
+
165
+ const parsed = extractValue(rawVal);
166
+ if (parsed && typeof parsed === 'object') {
167
+ for (const modKey of MODULE_KEYS) {
168
+ if (typeof parsed[modKey] === 'boolean') {
169
+ result[modKey] = parsed[modKey];
170
+ }
171
+ }
172
+ }
173
+ }
174
+
175
+ // Check individual module boolean flags (overrides JSON if explicitly defined)
176
+ for (const modKey of MODULE_KEYS) {
177
+ const paramKeys = [
178
+ `inspector_module_${modKey}`,
179
+ `inapp_inspector_${modKey}`,
180
+ `inspector_${modKey}`,
181
+ `inapp_inspector_module_${modKey}`,
182
+ `rn_inspector_${modKey}`,
183
+ ];
184
+
185
+ for (const pKey of paramKeys) {
186
+ let rawVal: any;
187
+ if (typeof rc.getValue === 'function') {
188
+ try {
189
+ rawVal = rc.getValue(pKey);
190
+ } catch {}
191
+ } else if (rc[pKey] !== undefined) {
192
+ rawVal = rc[pKey];
193
+ }
194
+
195
+ const boolVal = extractValue(rawVal);
196
+ if (typeof boolVal === 'boolean') {
197
+ result[modKey] = boolVal;
198
+ break;
199
+ }
200
+ }
201
+ }
202
+
203
+ // APIs module is fundamental core, keep true unless explicitly disabled via remote config
204
+ if (result.apis === undefined) {
205
+ result.apis = true;
206
+ }
207
+
208
+ return Object.keys(result).length > 0 ? result : null;
209
+ } catch (error) {
210
+ return null;
211
+ }
212
+ }
@@ -1,4 +1,5 @@
1
1
  import {NetworkLog, SearchScope} from '../types';
2
+ import {markSearchFilterUsed} from './telemetry';
2
3
 
3
4
  export interface SearchQueryOptions {
4
5
  scope?: SearchScope;
@@ -7,320 +8,77 @@ export interface SearchQueryOptions {
7
8
  }
8
9
 
9
10
  /**
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.
11
+ * Universal substring search for NetworkLog.
12
+ * Checks whether a given NetworkLog matches all query keywords across all log fields.
12
13
  */
13
14
  export function matchNetworkLogQuery(
14
15
  log: NetworkLog,
15
16
  searchQuery: string,
16
17
  routePath?: string,
17
- options?: SearchQueryOptions,
18
+ _options?: SearchQueryOptions,
18
19
  ): boolean {
19
20
  if (!searchQuery || searchQuery.trim().length === 0) return true;
20
21
 
21
- // Split tokens by spaces or commas while respecting quotes
22
- const rawTokens =
23
- searchQuery.trim().match(/(?:[^\s,"']+|"[^"]*"|'[^']*')+/g) || [];
22
+ markSearchFilterUsed();
24
23
 
24
+ const rawTokens = searchQuery.trim().toLowerCase().split(/\s+/).filter(Boolean);
25
25
  if (rawTokens.length === 0) return true;
26
26
 
27
- const isCaseSensitive = Boolean(options?.isCaseSensitive);
28
- const isRegex = Boolean(options?.isRegex);
29
- const scope = options?.scope || 'all';
30
-
31
- const methodRaw = log.method || '';
32
- const urlRaw = log.url || '';
27
+ const methodRaw = (log.method || '').toLowerCase();
28
+ const urlRaw = (log.url || '').toLowerCase();
29
+ const statusStr = log.status != null ? String(log.status).toLowerCase() : 'pending';
33
30
  const statusNum =
34
31
  typeof log.status === 'number'
35
32
  ? log.status
36
33
  : log.status != null
37
34
  ? parseInt(String(log.status), 10)
38
35
  : null;
39
- const statusStr = log.status != null ? String(log.status) : 'pending';
40
- const durationNum = log.duration || 0;
41
- const clientRaw = log.client || '';
42
- const callerRaw = log.caller || '';
36
+ const clientRaw = (log.client || '').toLowerCase();
37
+ const callerRaw = (log.caller || '').toLowerCase();
38
+ const pathRaw = (routePath || (log as any)?.routeInfo?.path || '').toLowerCase();
39
+
43
40
  const reqRaw =
44
41
  typeof log.request === 'string'
45
- ? log.request
42
+ ? log.request.toLowerCase()
46
43
  : log.request != null
47
- ? JSON.stringify(log.request)
44
+ ? JSON.stringify(log.request).toLowerCase()
48
45
  : '';
49
46
  const resRaw =
50
47
  typeof log.response === 'string'
51
- ? log.response
48
+ ? log.response.toLowerCase()
52
49
  : log.response != null
53
- ? JSON.stringify(log.response)
50
+ ? JSON.stringify(log.response).toLowerCase()
54
51
  : '';
55
52
  const reqHeadersRaw = log.requestHeaders
56
- ? JSON.stringify(log.requestHeaders)
53
+ ? JSON.stringify(log.requestHeaders).toLowerCase()
57
54
  : '';
58
55
  const resHeadersRaw = log.responseHeaders
59
- ? JSON.stringify(log.responseHeaders)
56
+ ? JSON.stringify(log.responseHeaders).toLowerCase()
60
57
  : '';
61
- const pathRaw = routePath || (log as any)?.routeInfo?.path || '';
62
-
63
- // Construct search corpus based on active scope
64
- const getScopedCorpus = (caseSens: boolean): string => {
65
- let parts: string[] = [];
66
- if (scope === 'url') {
67
- parts = [methodRaw, urlRaw, pathRaw, callerRaw];
68
- } else if (scope === 'reqBody') {
69
- parts = [reqRaw];
70
- } else if (scope === 'resBody') {
71
- parts = [resRaw];
72
- } else if (scope === 'headers') {
73
- parts = [reqHeadersRaw, resHeadersRaw];
74
- } else {
75
- parts = [
76
- methodRaw,
77
- urlRaw,
78
- statusStr,
79
- pathRaw,
80
- callerRaw,
81
- clientRaw,
82
- reqRaw,
83
- resRaw,
84
- reqHeadersRaw,
85
- resHeadersRaw,
86
- ];
87
- }
88
- const combined = parts.join(' ');
89
- return caseSens ? combined : combined.toLowerCase();
90
- };
91
-
92
- const targetCorpus = getScopedCorpus(isCaseSensitive);
93
-
94
- const methodStr = methodRaw.toLowerCase();
95
- const urlStr = urlRaw.toLowerCase();
96
- const clientStr = clientRaw.toLowerCase();
97
- const callerStr = callerRaw.toLowerCase();
98
- const reqStr = reqRaw.toLowerCase();
99
- const resStr = resRaw.toLowerCase();
100
- const reqHeadersStr = reqHeadersRaw.toLowerCase();
101
- const resHeadersStr = resHeadersRaw.toLowerCase();
102
- const pathStr = pathRaw.toLowerCase();
103
-
104
- for (const rawToken of rawTokens) {
105
- const token = rawToken.replace(/^["']|["']$/g, '').trim();
106
- if (!token) continue;
107
-
108
- const lowerToken = token.toLowerCase();
109
58
 
110
- // 1. method:<VAL> or m:<VAL>
111
- if (lowerToken.startsWith('method:') || lowerToken.startsWith('m:')) {
112
- const targetMethod = lowerToken.replace(/^(method:|m:)/, '').trim();
113
- if (targetMethod.startsWith('!')) {
114
- if (methodStr === targetMethod.slice(1)) return false;
115
- } else {
116
- if (methodStr !== targetMethod) return false;
117
- }
118
- continue;
59
+ const corpus = [
60
+ methodRaw,
61
+ urlRaw,
62
+ statusStr,
63
+ pathRaw,
64
+ callerRaw,
65
+ clientRaw,
66
+ reqRaw,
67
+ resRaw,
68
+ reqHeadersRaw,
69
+ resHeadersRaw,
70
+ ].join(' ');
71
+
72
+ return rawTokens.every(token => {
73
+ if (corpus.includes(token)) return true;
74
+ // Error status keywords
75
+ if (token === 'error' || token === 'failed' || token === 'err' || token === 'fail') {
76
+ return log.status === 0 || (statusNum !== null && statusNum >= 400);
119
77
  }
120
-
121
- // 2. status:<VAL> or s:<VAL>
122
- if (lowerToken.startsWith('status:') || lowerToken.startsWith('s:')) {
123
- const val = lowerToken.replace(/^(status:|s:)/, '').trim();
124
- if (val === 'pending' || val === 'loading') {
125
- if (log.status != null) return false;
126
- } else if (val.startsWith('>=')) {
127
- const threshold = parseInt(val.slice(2), 10);
128
- if (isNaN(threshold) || statusNum === null || statusNum < threshold) return false;
129
- } else if (val.startsWith('>')) {
130
- const threshold = parseInt(val.slice(1), 10);
131
- if (isNaN(threshold) || statusNum === null || statusNum <= threshold) return false;
132
- } else if (val.startsWith('<=')) {
133
- const threshold = parseInt(val.slice(2), 10);
134
- if (isNaN(threshold) || statusNum === null || statusNum > threshold) return false;
135
- } else if (val.startsWith('<')) {
136
- const threshold = parseInt(val.slice(1), 10);
137
- if (isNaN(threshold) || statusNum === null || statusNum >= threshold) return false;
138
- } else if (val.endsWith('xx') || val.endsWith('x')) {
139
- const prefix = val.replace(/x/g, '');
140
- if (!statusStr.startsWith(prefix)) return false;
141
- } else {
142
- const targetCode = parseInt(val, 10);
143
- if (!isNaN(targetCode)) {
144
- if (statusNum !== targetCode) return false;
145
- } else if (!statusStr.includes(val)) {
146
- return false;
147
- }
148
- }
149
- continue;
150
- }
151
-
152
- // 3. is:<FLAG>
153
- if (lowerToken.startsWith('is:')) {
154
- const flag = lowerToken.slice(3).trim();
155
- if (
156
- flag === 'error' ||
157
- flag === 'failed' ||
158
- flag === 'err' ||
159
- flag === 'fail'
160
- ) {
161
- const isErr =
162
- log.status === 0 || (statusNum !== null && statusNum >= 400);
163
- if (!isErr) return false;
164
- } else if (flag === 'success' || flag === 'ok' || flag === '2xx') {
165
- const isSuccess = statusNum !== null && statusNum >= 200 && statusNum < 300;
166
- if (!isSuccess) return false;
167
- } else if (flag === 'pending' || flag === 'loading') {
168
- if (log.status != null) return false;
169
- } else if (flag === 'slow') {
170
- if (durationNum < 1000) return false;
171
- } else if (flag === 'fast') {
172
- if (durationNum >= 200) return false;
173
- } else if (flag === 'graphql' || flag === 'gql') {
174
- const isGql =
175
- urlStr.includes('graphql') ||
176
- clientStr === 'graphql' ||
177
- clientStr === 'apollo';
178
- if (!isGql) return false;
179
- } else if (flag === 'https') {
180
- if (!urlStr.startsWith('https')) return false;
181
- } else if (flag === 'http') {
182
- if (urlStr.startsWith('https') || !urlStr.startsWith('http'))
183
- return false;
184
- } else if (flag === 'json') {
185
- const isJson =
186
- reqHeadersStr.includes('application/json') ||
187
- resHeadersStr.includes('application/json') ||
188
- urlStr.includes('.json');
189
- if (!isJson) return false;
190
- } else if (flag === 'image') {
191
- const isImg =
192
- resHeadersStr.includes('image/') ||
193
- /\.(png|jpe?g|gif|webp|svg)/i.test(urlStr);
194
- if (!isImg) return false;
195
- }
196
- continue;
197
- }
198
-
199
- // 4. slow:<DURATION> or dur:<DURATION> or duration:<DURATION>
200
- if (
201
- lowerToken.startsWith('slow:') ||
202
- lowerToken.startsWith('dur:') ||
203
- lowerToken.startsWith('duration:')
204
- ) {
205
- const val = lowerToken.replace(/^(slow:|dur:|duration:)/, '').trim();
206
- let thresholdMs = 0;
207
- let isGreater = true;
208
-
209
- let cleanVal = val;
210
- if (val.startsWith('>=')) {
211
- isGreater = true;
212
- cleanVal = val.slice(2);
213
- } else if (val.startsWith('>')) {
214
- isGreater = true;
215
- cleanVal = val.slice(1);
216
- } else if (val.startsWith('<=')) {
217
- isGreater = false;
218
- cleanVal = val.slice(2);
219
- } else if (val.startsWith('<')) {
220
- isGreater = false;
221
- cleanVal = val.slice(1);
222
- }
223
-
224
- if (cleanVal.endsWith('s') && !cleanVal.endsWith('ms')) {
225
- thresholdMs = parseFloat(cleanVal.slice(0, -1)) * 1000;
226
- } else if (cleanVal.endsWith('ms')) {
227
- thresholdMs = parseFloat(cleanVal.slice(0, -2));
228
- } else {
229
- thresholdMs = parseFloat(cleanVal);
230
- }
231
-
232
- if (!isNaN(thresholdMs)) {
233
- if (isGreater) {
234
- if (durationNum < thresholdMs) return false;
235
- } else {
236
- if (durationNum > thresholdMs) return false;
237
- }
238
- }
239
- continue;
78
+ // Success status keywords
79
+ if (token === 'success' || token === 'ok' || token === '2xx') {
80
+ return statusNum !== null && statusNum >= 200 && statusNum < 300;
240
81
  }
241
-
242
- // 5. client:<CLIENT> or c:<CLIENT>
243
- if (lowerToken.startsWith('client:') || lowerToken.startsWith('c:')) {
244
- const targetClient = lowerToken.replace(/^(client:|c:)/, '').trim();
245
- if (!clientStr.includes(targetClient)) return false;
246
- continue;
247
- }
248
-
249
- // 6. domain:<DOMAIN> or d:<DOMAIN>
250
- if (lowerToken.startsWith('domain:') || lowerToken.startsWith('d:')) {
251
- const targetDomain = lowerToken.replace(/^(domain:|d:)/, '').trim();
252
- if (!urlStr.includes(targetDomain)) return false;
253
- continue;
254
- }
255
-
256
- // 7. path:<PATH> or p:<PATH> or page:<PAGE>
257
- if (
258
- lowerToken.startsWith('path:') ||
259
- lowerToken.startsWith('p:') ||
260
- lowerToken.startsWith('page:')
261
- ) {
262
- const targetPath = lowerToken
263
- .replace(/^(path:|p:|page:)/, '')
264
- .trim();
265
- if (
266
- !urlStr.includes(targetPath) &&
267
- !pathStr.includes(targetPath) &&
268
- !callerStr.includes(targetPath)
269
- ) {
270
- return false;
271
- }
272
- continue;
273
- }
274
-
275
- // 8. req:<TERM> or body:<TERM>
276
- if (lowerToken.startsWith('req:') || lowerToken.startsWith('body:')) {
277
- const target = lowerToken.replace(/^(req:|body:)/, '').trim();
278
- if (!reqStr.includes(target)) return false;
279
- continue;
280
- }
281
-
282
- // 9. res:<TERM>
283
- if (lowerToken.startsWith('res:')) {
284
- const target = lowerToken.slice(4).trim();
285
- if (!resStr.includes(target)) return false;
286
- continue;
287
- }
288
-
289
- // 10. header:<TERM> or h:<TERM>
290
- if (lowerToken.startsWith('header:') || lowerToken.startsWith('h:')) {
291
- const target = lowerToken.replace(/^(header:|h:)/, '').trim();
292
- if (!reqHeadersStr.includes(target) && !resHeadersStr.includes(target)) {
293
- return false;
294
- }
295
- continue;
296
- }
297
-
298
- // 11. Generic Match (handles Regex, Case-sensitivity, and Scoped corpus)
299
- if (isRegex) {
300
- try {
301
- const re = new RegExp(token, isCaseSensitive ? '' : 'i');
302
- if (!re.test(targetCorpus)) return false;
303
- } catch {
304
- const queryTerm = isCaseSensitive ? token : lowerToken;
305
- if (!targetCorpus.includes(queryTerm)) return false;
306
- }
307
- } else {
308
- const queryTerm = isCaseSensitive ? token : lowerToken;
309
- if (!targetCorpus.includes(queryTerm)) {
310
- // Special case: if user typed "error" / "failed", check error status
311
- if (
312
- queryTerm === 'error' ||
313
- queryTerm === 'failed' ||
314
- queryTerm === 'fail'
315
- ) {
316
- const isErr =
317
- log.status === 0 || (statusNum !== null && statusNum >= 400);
318
- if (isErr) continue;
319
- }
320
- return false;
321
- }
322
- }
323
- }
324
-
325
- return true;
82
+ return false;
83
+ });
326
84
  }