react-native-inapp-inspector 1.1.24 → 1.1.26
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/android/build.gradle +36 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +114 -0
- package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +28 -0
- package/dist/commonjs/components/AnalyticsEventCard.js +94 -88
- package/dist/commonjs/components/ConsoleLogCard.js +49 -25
- package/dist/commonjs/components/ErrorBoundary.d.ts +1 -1
- package/dist/commonjs/components/ErrorBoundary.js +9 -145
- package/dist/commonjs/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
- package/dist/commonjs/components/Inspector/AnalyticsFilterModal.js +1002 -0
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +637 -10
- package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -1
- package/dist/commonjs/components/Inspector/BundleTab.js +826 -406
- package/dist/commonjs/components/Inspector/CrashDetail.d.ts +3 -0
- package/dist/commonjs/components/Inspector/CrashDetail.js +796 -0
- package/dist/commonjs/components/Inspector/CrashFilterModal.d.ts +20 -0
- package/dist/commonjs/components/Inspector/CrashFilterModal.js +597 -0
- package/dist/commonjs/components/Inspector/CrashTab.d.ts +3 -0
- package/dist/commonjs/components/Inspector/CrashTab.js +727 -0
- package/dist/commonjs/components/Inspector/InspectorContext.js +6 -1
- package/dist/commonjs/components/Inspector/InspectorHeader.js +65 -28
- package/dist/commonjs/components/Inspector/LogDetail.js +227 -114
- package/dist/commonjs/components/Inspector/MainScreen.js +13 -4
- package/dist/commonjs/components/Inspector/NetworkDetail.js +131 -81
- package/dist/commonjs/components/Inspector/PerformanceTab.js +20 -10
- package/dist/commonjs/components/Inspector/SettingsPanel.js +90 -1
- package/dist/commonjs/components/Inspector/TabBar.js +15 -3
- package/dist/commonjs/components/NetworkIcons.d.ts +25 -0
- package/dist/commonjs/components/NetworkIcons.js +188 -1
- package/dist/commonjs/components/SegmentedTabs.js +33 -23
- package/dist/commonjs/components/TreeNode.js +13 -12
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/analyticsLogger.js +70 -44
- package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +5 -1
- package/dist/commonjs/customHooks/bundleAnalyzer.js +497 -126
- package/dist/commonjs/customHooks/consoleLogger.d.ts +1 -0
- package/dist/commonjs/customHooks/consoleLogger.js +153 -51
- package/dist/commonjs/customHooks/crashHandler.d.ts +64 -0
- package/dist/commonjs/customHooks/crashHandler.js +681 -0
- package/dist/commonjs/customHooks/networkLogger.js +74 -49
- package/dist/commonjs/customHooks/performanceTracker.d.ts +8 -0
- package/dist/commonjs/customHooks/performanceTracker.js +44 -3
- package/dist/commonjs/decorators/index.d.ts +49 -0
- package/dist/commonjs/decorators/index.js +142 -0
- package/dist/commonjs/helpers/gaAnalyticsRegistry.d.ts +18 -0
- package/dist/commonjs/helpers/gaAnalyticsRegistry.js +174 -0
- package/dist/commonjs/helpers/index.d.ts +7 -4
- package/dist/commonjs/helpers/index.js +102 -64
- package/dist/commonjs/i18n/locales/en.json +182 -4
- package/dist/commonjs/index.d.ts +6 -0
- package/dist/commonjs/index.js +310 -8
- package/dist/commonjs/styles/index.d.ts +4 -1
- package/dist/commonjs/styles/index.js +22 -19
- package/dist/commonjs/types/enums.d.ts +156 -9
- package/dist/commonjs/types/enums.js +139 -2
- package/dist/commonjs/types/interfaces.d.ts +70 -1
- package/dist/esm/components/AnalyticsEventCard.js +95 -89
- package/dist/esm/components/ConsoleLogCard.js +49 -25
- package/dist/esm/components/ErrorBoundary.d.ts +1 -1
- package/dist/esm/components/ErrorBoundary.js +10 -113
- package/dist/esm/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
- package/dist/esm/components/Inspector/AnalyticsFilterModal.js +964 -0
- package/dist/esm/components/Inspector/AnalyticsTab.js +639 -12
- package/dist/esm/components/Inspector/BundleTab.d.ts +1 -1
- package/dist/esm/components/Inspector/BundleTab.js +827 -407
- package/dist/esm/components/Inspector/CrashDetail.d.ts +3 -0
- package/dist/esm/components/Inspector/CrashDetail.js +758 -0
- package/dist/esm/components/Inspector/CrashFilterModal.d.ts +20 -0
- package/dist/esm/components/Inspector/CrashFilterModal.js +557 -0
- package/dist/esm/components/Inspector/CrashTab.d.ts +3 -0
- package/dist/esm/components/Inspector/CrashTab.js +689 -0
- package/dist/esm/components/Inspector/InspectorContext.js +1 -0
- package/dist/esm/components/Inspector/InspectorHeader.js +66 -29
- package/dist/esm/components/Inspector/LogDetail.js +227 -114
- package/dist/esm/components/Inspector/MainScreen.js +14 -5
- package/dist/esm/components/Inspector/NetworkDetail.js +132 -49
- package/dist/esm/components/Inspector/PerformanceTab.js +20 -10
- package/dist/esm/components/Inspector/SettingsPanel.js +91 -2
- package/dist/esm/components/Inspector/TabBar.js +16 -4
- package/dist/esm/components/NetworkIcons.d.ts +25 -0
- package/dist/esm/components/NetworkIcons.js +161 -0
- package/dist/esm/components/SegmentedTabs.js +33 -23
- package/dist/esm/components/TreeNode.js +13 -12
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/analyticsLogger.js +70 -44
- package/dist/esm/customHooks/bundleAnalyzer.d.ts +5 -1
- package/dist/esm/customHooks/bundleAnalyzer.js +493 -125
- package/dist/esm/customHooks/consoleLogger.d.ts +1 -0
- package/dist/esm/customHooks/consoleLogger.js +151 -50
- package/dist/esm/customHooks/crashHandler.d.ts +64 -0
- package/dist/esm/customHooks/crashHandler.js +659 -0
- package/dist/esm/customHooks/networkLogger.js +74 -49
- package/dist/esm/customHooks/performanceTracker.d.ts +8 -0
- package/dist/esm/customHooks/performanceTracker.js +38 -1
- package/dist/esm/decorators/index.d.ts +49 -0
- package/dist/esm/decorators/index.js +137 -0
- package/dist/esm/helpers/gaAnalyticsRegistry.d.ts +18 -0
- package/dist/esm/helpers/gaAnalyticsRegistry.js +169 -0
- package/dist/esm/helpers/index.d.ts +7 -4
- package/dist/esm/helpers/index.js +100 -63
- package/dist/esm/i18n/locales/en.json +182 -4
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +262 -8
- package/dist/esm/styles/index.d.ts +4 -1
- package/dist/esm/styles/index.js +22 -19
- package/dist/esm/types/enums.d.ts +156 -9
- package/dist/esm/types/enums.js +138 -3
- package/dist/esm/types/interfaces.d.ts +70 -1
- package/ios/NetworkInspectorModule.h +6 -0
- package/ios/NetworkInspectorModule.m +125 -0
- package/package.json +5 -2
- package/react-native-inapp-inspector.podspec +18 -0
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
|
+
import { setupGlobalCrashHandler } from "./crashHandler";
|
|
2
3
|
let logs = [];
|
|
3
4
|
let listeners = [];
|
|
4
5
|
let counter = 0;
|
|
5
6
|
const ALLOWED_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
6
|
-
const IGNORED_URL_PATTERNS = [
|
|
7
|
+
const IGNORED_URL_PATTERNS = [
|
|
8
|
+
/\/symbolicate(?:[/?#]|$)/i,
|
|
9
|
+
/\/index\.bundle(?:\?|$)/i,
|
|
10
|
+
/\/hot(?:\?|$)/i,
|
|
11
|
+
/\/message(?:\?|$)/i,
|
|
12
|
+
/\/open-debugger(?:\?|$)/i,
|
|
13
|
+
/\/status(?:\?|$)/i,
|
|
14
|
+
/127\.0\.0\.1:(?:8081|8082|8083|19000|19001|19002|3000|8000)/i,
|
|
15
|
+
/localhost:(?:8081|8082|8083|19000|19001|19002|3000|8000)\/index\.bundle/i,
|
|
16
|
+
];
|
|
7
17
|
function shouldIgnoreUrl(url) {
|
|
8
18
|
if (!url)
|
|
9
19
|
return false;
|
|
@@ -133,65 +143,76 @@ export const setupNetworkLogger = () => {
|
|
|
133
143
|
const finalUrl = typeof url === "string" ? url : url?.url;
|
|
134
144
|
if (shouldIgnoreUrl(finalUrl))
|
|
135
145
|
return originalFetch(url, options);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
addOrUpdateLog({
|
|
139
|
-
id,
|
|
140
|
-
url: finalUrl,
|
|
141
|
-
method,
|
|
142
|
-
startTime: start,
|
|
143
|
-
caller,
|
|
144
|
-
request: method === "GET" ? undefined : parseRequestData(options?.body),
|
|
145
|
-
requestHeaders,
|
|
146
|
-
});
|
|
146
|
+
let caller = "Unknown";
|
|
147
|
+
let requestHeaders;
|
|
147
148
|
try {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const responseHeaders = normaliseHeaders(response.headers);
|
|
151
|
-
let data = null;
|
|
152
|
-
const contentType = responseHeaders?.["content-type"] ||
|
|
153
|
-
responseHeaders?.["Content-Type"] ||
|
|
154
|
-
"";
|
|
155
|
-
if (contentType.includes("image/")) {
|
|
156
|
-
data = "[Image Data]";
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
try {
|
|
160
|
-
const clone = response.clone();
|
|
161
|
-
const text = await clone.text();
|
|
162
|
-
try {
|
|
163
|
-
data = JSON.parse(text);
|
|
164
|
-
}
|
|
165
|
-
catch {
|
|
166
|
-
data = text;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
catch { }
|
|
170
|
-
}
|
|
149
|
+
requestHeaders = normaliseHeaders(options?.headers);
|
|
150
|
+
caller = getCallerFromStack(); // ✅ Capture call line
|
|
171
151
|
addOrUpdateLog({
|
|
172
152
|
id,
|
|
173
153
|
url: finalUrl,
|
|
174
154
|
method,
|
|
175
|
-
status: response.status,
|
|
176
|
-
response: data,
|
|
177
|
-
duration,
|
|
178
155
|
startTime: start,
|
|
179
156
|
caller,
|
|
180
|
-
|
|
157
|
+
request: method === "GET" ? undefined : parseRequestData(options?.body),
|
|
158
|
+
requestHeaders,
|
|
181
159
|
});
|
|
160
|
+
}
|
|
161
|
+
catch { }
|
|
162
|
+
try {
|
|
163
|
+
const response = await originalFetch(url, options);
|
|
164
|
+
const duration = Date.now() - start;
|
|
165
|
+
try {
|
|
166
|
+
const responseHeaders = normaliseHeaders(response.headers);
|
|
167
|
+
let data = null;
|
|
168
|
+
const contentType = responseHeaders?.["content-type"] ||
|
|
169
|
+
responseHeaders?.["Content-Type"] ||
|
|
170
|
+
"";
|
|
171
|
+
if (contentType.includes("image/")) {
|
|
172
|
+
data = "[Image Data]";
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
try {
|
|
176
|
+
const clone = response.clone();
|
|
177
|
+
const text = await clone.text();
|
|
178
|
+
try {
|
|
179
|
+
data = JSON.parse(text);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
data = text;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch { }
|
|
186
|
+
}
|
|
187
|
+
addOrUpdateLog({
|
|
188
|
+
id,
|
|
189
|
+
url: finalUrl,
|
|
190
|
+
method,
|
|
191
|
+
status: response.status,
|
|
192
|
+
response: data,
|
|
193
|
+
duration,
|
|
194
|
+
startTime: start,
|
|
195
|
+
caller,
|
|
196
|
+
responseHeaders,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch { }
|
|
182
200
|
return response;
|
|
183
201
|
}
|
|
184
202
|
catch (error) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
203
|
+
try {
|
|
204
|
+
addOrUpdateLog({
|
|
205
|
+
id,
|
|
206
|
+
url: finalUrl,
|
|
207
|
+
method,
|
|
208
|
+
status: 0,
|
|
209
|
+
startTime: start,
|
|
210
|
+
response: error,
|
|
211
|
+
caller,
|
|
212
|
+
duration: Date.now() - start,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
catch { }
|
|
195
216
|
throw error;
|
|
196
217
|
}
|
|
197
218
|
};
|
|
@@ -213,6 +234,10 @@ export const setupNetworkLogger = () => {
|
|
|
213
234
|
catch (_e) {
|
|
214
235
|
// Axios not available — fetch-only mode
|
|
215
236
|
}
|
|
237
|
+
try {
|
|
238
|
+
setupGlobalCrashHandler();
|
|
239
|
+
}
|
|
240
|
+
catch { }
|
|
216
241
|
globalThis.__NETWORK_LOGGER_INITIALIZED__ = true;
|
|
217
242
|
};
|
|
218
243
|
// ─── Axios interceptor helper ─────────────────────────────────────────────────
|
|
@@ -54,6 +54,7 @@ export interface ComponentRenderProfile {
|
|
|
54
54
|
fixKeys: PerformanceFixKey[];
|
|
55
55
|
severity: 'optimal' | 'warning' | 'critical';
|
|
56
56
|
}
|
|
57
|
+
export declare const INITIAL_EVENTS: PerformanceEvent[];
|
|
57
58
|
export declare const usePerformanceTracker: () => {
|
|
58
59
|
isRecording: boolean;
|
|
59
60
|
setIsRecording: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
@@ -82,3 +83,10 @@ export declare const usePerformanceTracker: () => {
|
|
|
82
83
|
clearEvents: () => void;
|
|
83
84
|
triggerGc: () => void;
|
|
84
85
|
};
|
|
86
|
+
export declare const logPerformanceEvent: (event: Omit<PerformanceEvent, "id" | "timestamp"> & {
|
|
87
|
+
id?: string;
|
|
88
|
+
timestamp?: number;
|
|
89
|
+
}) => PerformanceEvent;
|
|
90
|
+
export declare const clearPerformanceEvents: () => void;
|
|
91
|
+
export declare const subscribePerformanceEvents: (listener: (events: PerformanceEvent[]) => void) => () => void;
|
|
92
|
+
export declare const getPerformanceEvents: () => PerformanceEvent[];
|
|
@@ -170,7 +170,7 @@ const INITIAL_RENDER_PROFILES = [
|
|
|
170
170
|
severity: 'optimal',
|
|
171
171
|
},
|
|
172
172
|
];
|
|
173
|
-
const INITIAL_EVENTS = [
|
|
173
|
+
export const INITIAL_EVENTS = [
|
|
174
174
|
{
|
|
175
175
|
id: 'perf-1',
|
|
176
176
|
timestamp: Date.now() - 48000,
|
|
@@ -535,3 +535,40 @@ export const usePerformanceTracker = () => {
|
|
|
535
535
|
triggerGc,
|
|
536
536
|
};
|
|
537
537
|
};
|
|
538
|
+
const globalPerformanceEvents = [...INITIAL_EVENTS];
|
|
539
|
+
const performanceListeners = new Set();
|
|
540
|
+
export const logPerformanceEvent = (event) => {
|
|
541
|
+
const fullEvent = {
|
|
542
|
+
id: event.id || `perf-${Date.now()}-${Math.random().toString(36).substring(2, 6)}`,
|
|
543
|
+
timestamp: event.timestamp || Date.now(),
|
|
544
|
+
...event,
|
|
545
|
+
};
|
|
546
|
+
globalPerformanceEvents.unshift(fullEvent);
|
|
547
|
+
if (globalPerformanceEvents.length > 100) {
|
|
548
|
+
globalPerformanceEvents.pop();
|
|
549
|
+
}
|
|
550
|
+
performanceListeners.forEach(listener => {
|
|
551
|
+
try {
|
|
552
|
+
listener([...globalPerformanceEvents]);
|
|
553
|
+
}
|
|
554
|
+
catch { }
|
|
555
|
+
});
|
|
556
|
+
return fullEvent;
|
|
557
|
+
};
|
|
558
|
+
export const clearPerformanceEvents = () => {
|
|
559
|
+
globalPerformanceEvents.length = 0;
|
|
560
|
+
performanceListeners.forEach(listener => {
|
|
561
|
+
try {
|
|
562
|
+
listener([]);
|
|
563
|
+
}
|
|
564
|
+
catch { }
|
|
565
|
+
});
|
|
566
|
+
};
|
|
567
|
+
export const subscribePerformanceEvents = (listener) => {
|
|
568
|
+
performanceListeners.add(listener);
|
|
569
|
+
listener([...globalPerformanceEvents]);
|
|
570
|
+
return () => {
|
|
571
|
+
performanceListeners.delete(listener);
|
|
572
|
+
};
|
|
573
|
+
};
|
|
574
|
+
export const getPerformanceEvents = () => [...globalPerformanceEvents];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface InspectLogOptions {
|
|
2
|
+
tag?: string;
|
|
3
|
+
logArgs?: boolean;
|
|
4
|
+
logResult?: boolean;
|
|
5
|
+
logDuration?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* `@InspectLog`: Automatically logs method execution, arguments, duration, and return values
|
|
9
|
+
* to the In-App Inspector Console.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* class UserService {
|
|
14
|
+
* @InspectLog({ tag: 'API', logArgs: true, logDuration: true })
|
|
15
|
+
* async fetchUser(id: string) {
|
|
16
|
+
* return api.get(`/users/${id}`);
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function InspectLog(options?: InspectLogOptions): (_target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
22
|
+
/**
|
|
23
|
+
* `@InspectTrackTime`: Measures and logs execution duration for critical functions.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* class FeedManager {
|
|
28
|
+
* @InspectTrackTime('Feed Data Processing')
|
|
29
|
+
* processFeedItems(items: FeedItem[]) { ... }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function InspectTrackTime(label?: string): (_target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
34
|
+
/**
|
|
35
|
+
* `@InspectCatch`: Safely catches unhandled exceptions thrown inside the method,
|
|
36
|
+
* logs them to the In-App Inspector, and returns a fallback value.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* class AuthService {
|
|
41
|
+
* @InspectCatch({ fallback: null, tag: 'AUTH' })
|
|
42
|
+
* async getCachedToken() { ... }
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function InspectCatch(options?: {
|
|
47
|
+
fallback?: any;
|
|
48
|
+
tag?: string;
|
|
49
|
+
}): (_target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// ─── Method & Class Decorators for React Native In-App Inspector ─────────────
|
|
2
|
+
//
|
|
3
|
+
// Clean TypeScript decorators to seamlessly annotate methods, asynchronous actions,
|
|
4
|
+
// and API services with automatic inspection, logging, timing, and error boundaries.
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
/**
|
|
7
|
+
* `@InspectLog`: Automatically logs method execution, arguments, duration, and return values
|
|
8
|
+
* to the In-App Inspector Console.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* class UserService {
|
|
13
|
+
* @InspectLog({ tag: 'API', logArgs: true, logDuration: true })
|
|
14
|
+
* async fetchUser(id: string) {
|
|
15
|
+
* return api.get(`/users/${id}`);
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function InspectLog(options = {}) {
|
|
21
|
+
const { tag = 'Inspect', logArgs = true, logResult = true, logDuration = true, } = options;
|
|
22
|
+
return function (_target, propertyKey, descriptor) {
|
|
23
|
+
const originalMethod = descriptor.value;
|
|
24
|
+
descriptor.value = function (...args) {
|
|
25
|
+
const start = Date.now();
|
|
26
|
+
const prefix = `[${tag}] ${propertyKey}`;
|
|
27
|
+
if (logArgs && args.length > 0) {
|
|
28
|
+
console.log(`${prefix} called with arguments:`, ...args);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.log(`${prefix} invoked`);
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const result = originalMethod.apply(this, args);
|
|
35
|
+
if (result && typeof result.then === 'function') {
|
|
36
|
+
return result
|
|
37
|
+
.then((asyncResult) => {
|
|
38
|
+
const elapsed = Date.now() - start;
|
|
39
|
+
if (logResult) {
|
|
40
|
+
console.log(`${prefix} resolved${logDuration ? ` in ${elapsed}ms` : ''}:`, asyncResult);
|
|
41
|
+
}
|
|
42
|
+
return asyncResult;
|
|
43
|
+
})
|
|
44
|
+
.catch((err) => {
|
|
45
|
+
const elapsed = Date.now() - start;
|
|
46
|
+
console.error(`${prefix} rejected${logDuration ? ` in ${elapsed}ms` : ''}:`, err);
|
|
47
|
+
throw err;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const elapsed = Date.now() - start;
|
|
51
|
+
if (logResult) {
|
|
52
|
+
console.log(`${prefix} returned${logDuration ? ` in ${elapsed}ms` : ''}:`, result);
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const elapsed = Date.now() - start;
|
|
58
|
+
console.error(`${prefix} failed${logDuration ? ` in ${elapsed}ms` : ''}:`, error);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return descriptor;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* `@InspectTrackTime`: Measures and logs execution duration for critical functions.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* class FeedManager {
|
|
71
|
+
* @InspectTrackTime('Feed Data Processing')
|
|
72
|
+
* processFeedItems(items: FeedItem[]) { ... }
|
|
73
|
+
* }
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function InspectTrackTime(label) {
|
|
77
|
+
return function (_target, propertyKey, descriptor) {
|
|
78
|
+
const originalMethod = descriptor.value;
|
|
79
|
+
const metricLabel = label || propertyKey;
|
|
80
|
+
descriptor.value = function (...args) {
|
|
81
|
+
const start = Date.now();
|
|
82
|
+
try {
|
|
83
|
+
const result = originalMethod.apply(this, args);
|
|
84
|
+
if (result && typeof result.then === 'function') {
|
|
85
|
+
return result.finally(() => {
|
|
86
|
+
const duration = Date.now() - start;
|
|
87
|
+
console.log(`[PERF] ⚡ ${metricLabel} took ${duration}ms`);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const duration = Date.now() - start;
|
|
91
|
+
console.log(`[PERF] ⚡ ${metricLabel} took ${duration}ms`);
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
const duration = Date.now() - start;
|
|
96
|
+
console.error(`[PERF] ⚡ ${metricLabel} failed after ${duration}ms:`, err);
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
return descriptor;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* `@InspectCatch`: Safely catches unhandled exceptions thrown inside the method,
|
|
105
|
+
* logs them to the In-App Inspector, and returns a fallback value.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* class AuthService {
|
|
110
|
+
* @InspectCatch({ fallback: null, tag: 'AUTH' })
|
|
111
|
+
* async getCachedToken() { ... }
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export function InspectCatch(options = {}) {
|
|
116
|
+
const { fallback = undefined, tag = 'Catch' } = options;
|
|
117
|
+
return function (_target, propertyKey, descriptor) {
|
|
118
|
+
const originalMethod = descriptor.value;
|
|
119
|
+
descriptor.value = function (...args) {
|
|
120
|
+
try {
|
|
121
|
+
const result = originalMethod.apply(this, args);
|
|
122
|
+
if (result && typeof result.then === 'function') {
|
|
123
|
+
return result.catch((err) => {
|
|
124
|
+
console.error(`[${tag}] Error caught in ${propertyKey}:`, err);
|
|
125
|
+
return fallback;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
console.error(`[${tag}] Error caught in ${propertyKey}:`, err);
|
|
132
|
+
return fallback;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
return descriptor;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { GAEventCategory } from '../types/enums';
|
|
2
|
+
export { GAEventCategory };
|
|
3
|
+
export interface GAPlugin {
|
|
4
|
+
name: string;
|
|
5
|
+
resolveCategory?: (eventName: string, params?: Record<string, any>) => GAEventCategory | string | null | undefined;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Register a dynamic Google Analytics / Firebase plugin or custom resolver.
|
|
9
|
+
*/
|
|
10
|
+
export declare const registerGAPlugin: (plugin: GAPlugin) => void;
|
|
11
|
+
/**
|
|
12
|
+
* Dynamically resolves the GA4 event category using:
|
|
13
|
+
* 1. Registered dynamic GA plugins
|
|
14
|
+
* 2. Runtime Firebase Analytics inspection
|
|
15
|
+
* 3. GA4 standard event taxonomies (O(1) Set lookup)
|
|
16
|
+
* 4. Semantic pattern & prefix analysis
|
|
17
|
+
*/
|
|
18
|
+
export declare const getEventCategory: (name: string, params?: Record<string, any>) => GAEventCategory;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// ─── Google Analytics (GA4) & Firebase Analytics Dynamic Registry ─────────────
|
|
2
|
+
//
|
|
3
|
+
// Dynamically inspects GA4 / Firebase Analytics runtime schemas, auto-detects
|
|
4
|
+
// standard GA4 event taxonomies, and supports dynamic GA plugins.
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
import { GAEventCategory } from '../types/enums';
|
|
7
|
+
export { GAEventCategory };
|
|
8
|
+
// ─── Official GA4 & Firebase Standard Event Sets ─────────────────────────────
|
|
9
|
+
const GA4_PAGE_VIEW_EVENTS = new Set([
|
|
10
|
+
'screen_view',
|
|
11
|
+
'page_view',
|
|
12
|
+
'screen_active',
|
|
13
|
+
'screen_inactive',
|
|
14
|
+
'view_search_results',
|
|
15
|
+
'search',
|
|
16
|
+
]);
|
|
17
|
+
const GA4_ECOMMERCE_EVENTS = new Set([
|
|
18
|
+
'purchase',
|
|
19
|
+
'refund',
|
|
20
|
+
'add_to_cart',
|
|
21
|
+
'remove_from_cart',
|
|
22
|
+
'view_cart',
|
|
23
|
+
'begin_checkout',
|
|
24
|
+
'add_shipping_info',
|
|
25
|
+
'add_payment_info',
|
|
26
|
+
'view_item',
|
|
27
|
+
'view_item_list',
|
|
28
|
+
'select_item',
|
|
29
|
+
'select_promotion',
|
|
30
|
+
'view_promotion',
|
|
31
|
+
'add_to_wishlist',
|
|
32
|
+
'generate_lead',
|
|
33
|
+
'in_app_purchase',
|
|
34
|
+
'earn_virtual_currency',
|
|
35
|
+
'spend_virtual_currency',
|
|
36
|
+
'join_group',
|
|
37
|
+
'level_up',
|
|
38
|
+
'level_start',
|
|
39
|
+
'level_end',
|
|
40
|
+
'post_score',
|
|
41
|
+
'select_content',
|
|
42
|
+
'tutorial_begin',
|
|
43
|
+
'tutorial_complete',
|
|
44
|
+
'unlock_achievement',
|
|
45
|
+
]);
|
|
46
|
+
const GA4_SYSTEM_EVENTS = new Set([
|
|
47
|
+
'first_open',
|
|
48
|
+
'first_visit',
|
|
49
|
+
'session_start',
|
|
50
|
+
'user_engagement',
|
|
51
|
+
'app_clear_data',
|
|
52
|
+
'app_exception',
|
|
53
|
+
'app_remove',
|
|
54
|
+
'app_store_refund',
|
|
55
|
+
'app_store_subscription_cancel',
|
|
56
|
+
'app_store_subscription_convert',
|
|
57
|
+
'app_store_subscription_renew',
|
|
58
|
+
'app_update',
|
|
59
|
+
'os_update',
|
|
60
|
+
'notification_receive',
|
|
61
|
+
'notification_open',
|
|
62
|
+
'notification_dismiss',
|
|
63
|
+
'notification_foreground',
|
|
64
|
+
'dynamic_link_first_open',
|
|
65
|
+
'dynamic_link_app_open',
|
|
66
|
+
'dynamic_link_app_update',
|
|
67
|
+
'ad_impression',
|
|
68
|
+
'ad_click',
|
|
69
|
+
'ad_reward',
|
|
70
|
+
'ad_query',
|
|
71
|
+
'ad_exposure',
|
|
72
|
+
]);
|
|
73
|
+
// ─── Registered Dynamic Plugins ──────────────────────────────────────────────
|
|
74
|
+
const registeredPlugins = [];
|
|
75
|
+
/**
|
|
76
|
+
* Register a dynamic Google Analytics / Firebase plugin or custom resolver.
|
|
77
|
+
*/
|
|
78
|
+
export const registerGAPlugin = (plugin) => {
|
|
79
|
+
if (!registeredPlugins.some(p => p.name === plugin.name)) {
|
|
80
|
+
registeredPlugins.push(plugin);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Attempt dynamic inspection of host runtime Firebase / Google Analytics modules.
|
|
85
|
+
*/
|
|
86
|
+
const tryInspectRuntimeFirebaseAnalytics = () => {
|
|
87
|
+
try {
|
|
88
|
+
const firebaseAnalytics = require('@react-native-firebase/analytics');
|
|
89
|
+
const mod = firebaseAnalytics?.default || firebaseAnalytics;
|
|
90
|
+
if (mod?.EventNames) {
|
|
91
|
+
Object.keys(mod.EventNames).forEach(k => {
|
|
92
|
+
const val = mod.EventNames[k]?.toLowerCase();
|
|
93
|
+
if (val) {
|
|
94
|
+
if (val.includes('cart') ||
|
|
95
|
+
val.includes('checkout') ||
|
|
96
|
+
val.includes('purchase') ||
|
|
97
|
+
val.includes('item')) {
|
|
98
|
+
GA4_ECOMMERCE_EVENTS.add(val);
|
|
99
|
+
}
|
|
100
|
+
else if (val.includes('screen') || val.includes('page')) {
|
|
101
|
+
GA4_PAGE_VIEW_EVENTS.add(val);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Optional dependency - safe fallback to built-in GA4 taxonomy
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
// Initialize dynamic runtime inspection once
|
|
112
|
+
tryInspectRuntimeFirebaseAnalytics();
|
|
113
|
+
/**
|
|
114
|
+
* Dynamically resolves the GA4 event category using:
|
|
115
|
+
* 1. Registered dynamic GA plugins
|
|
116
|
+
* 2. Runtime Firebase Analytics inspection
|
|
117
|
+
* 3. GA4 standard event taxonomies (O(1) Set lookup)
|
|
118
|
+
* 4. Semantic pattern & prefix analysis
|
|
119
|
+
*/
|
|
120
|
+
export const getEventCategory = (name, params) => {
|
|
121
|
+
if (!name || typeof name !== 'string')
|
|
122
|
+
return 'custom';
|
|
123
|
+
const cleanName = name.trim().toLowerCase();
|
|
124
|
+
// 1. Run through registered dynamic plugins
|
|
125
|
+
for (const plugin of registeredPlugins) {
|
|
126
|
+
if (plugin.resolveCategory) {
|
|
127
|
+
const category = plugin.resolveCategory(cleanName, params);
|
|
128
|
+
if (category)
|
|
129
|
+
return category;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// 2. Exact GA4 taxonomy matching (O(1))
|
|
133
|
+
if (GA4_PAGE_VIEW_EVENTS.has(cleanName)) {
|
|
134
|
+
return 'page_view';
|
|
135
|
+
}
|
|
136
|
+
if (GA4_ECOMMERCE_EVENTS.has(cleanName)) {
|
|
137
|
+
return 'ecommerce';
|
|
138
|
+
}
|
|
139
|
+
if (GA4_SYSTEM_EVENTS.has(cleanName)) {
|
|
140
|
+
return 'system';
|
|
141
|
+
}
|
|
142
|
+
// 3. Prefix & namespace detection
|
|
143
|
+
if (cleanName.startsWith('firebase_') ||
|
|
144
|
+
cleanName.startsWith('ga_') ||
|
|
145
|
+
cleanName.startsWith('gtag_') ||
|
|
146
|
+
cleanName.startsWith('_') ||
|
|
147
|
+
cleanName.startsWith('google_')) {
|
|
148
|
+
return 'system';
|
|
149
|
+
}
|
|
150
|
+
// 4. Semantic pattern matching
|
|
151
|
+
if (cleanName.endsWith('_view') ||
|
|
152
|
+
cleanName.endsWith('_screen') ||
|
|
153
|
+
cleanName.startsWith('screen_') ||
|
|
154
|
+
cleanName.startsWith('page_') ||
|
|
155
|
+
cleanName.includes('screen_view') ||
|
|
156
|
+
cleanName.includes('page_view')) {
|
|
157
|
+
return 'page_view';
|
|
158
|
+
}
|
|
159
|
+
if (cleanName.includes('cart') ||
|
|
160
|
+
cleanName.includes('checkout') ||
|
|
161
|
+
cleanName.includes('purchase') ||
|
|
162
|
+
cleanName.includes('payment') ||
|
|
163
|
+
cleanName.includes('order') ||
|
|
164
|
+
cleanName.includes('refund') ||
|
|
165
|
+
cleanName.includes('shipping')) {
|
|
166
|
+
return 'ecommerce';
|
|
167
|
+
}
|
|
168
|
+
return 'custom';
|
|
169
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NetworkLog, RouteInfo, DiffResult, JsonContent } from '../types';
|
|
1
|
+
import { NetworkLog, RouteInfo, DiffResult, JsonContent, StackFrameType } from '../types';
|
|
2
2
|
export declare const getDomainColor: (domain: string) => string;
|
|
3
3
|
export declare const formatDateTime: (timestamp: number) => string;
|
|
4
4
|
export declare const getStatusColor: (status: number | null) => string;
|
|
@@ -40,17 +40,20 @@ export interface ParsedStackFrame {
|
|
|
40
40
|
functionName: string;
|
|
41
41
|
fileName: string;
|
|
42
42
|
fullPath: string;
|
|
43
|
-
fileExt
|
|
44
|
-
|
|
43
|
+
fileExt: string;
|
|
44
|
+
frameType: StackFrameType | 'app' | 'dependency' | 'runtime' | 'native';
|
|
45
|
+
isUserCode: boolean;
|
|
46
|
+
isRuntimeNoise: boolean;
|
|
45
47
|
lineNumber?: string;
|
|
46
48
|
columnNumber?: string;
|
|
47
49
|
isOrigin?: boolean;
|
|
50
|
+
copyableLocation: string;
|
|
48
51
|
}
|
|
49
52
|
/** Parses a stack trace line to extract function name, file name, extension (.tsx/.jsx/.ts), line, and column numbers */
|
|
50
53
|
export declare const parseStackLine: (rawLine: string, isOrigin?: boolean) => ParsedStackFrame;
|
|
51
54
|
export declare const ANALYTICS_EVENT_PALETTE: string[];
|
|
52
55
|
export declare const getEventColor: (name: string) => string;
|
|
53
|
-
export
|
|
56
|
+
export { getEventCategory, registerGAPlugin, type GAEventCategory, type GAPlugin, } from './gaAnalyticsRegistry';
|
|
54
57
|
export declare const getCategoryColors: (category: string) => {
|
|
55
58
|
bg: string;
|
|
56
59
|
border: string;
|