react-native-inapp-inspector 1.0.13 → 1.0.15
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/README.md +203 -44
- package/dist/commonjs/customHooks/analyticsLogger.d.ts +1 -0
- package/dist/commonjs/customHooks/analyticsLogger.js +57 -17
- package/dist/commonjs/customHooks/networkLogger.d.ts +1 -1
- package/dist/commonjs/customHooks/networkLogger.js +66 -54
- package/dist/commonjs/index.d.ts +1 -1
- package/dist/commonjs/index.js +1501 -793
- package/dist/esm/customHooks/analyticsLogger.d.ts +1 -0
- package/dist/esm/customHooks/analyticsLogger.js +55 -16
- package/dist/esm/customHooks/networkLogger.d.ts +1 -1
- package/dist/esm/customHooks/networkLogger.js +64 -55
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1501 -794
- package/example/App.tsx +5 -3
- package/package.json +3 -1
|
@@ -19,3 +19,4 @@ export declare const logAnalyticsEvent: (name: string, params?: Record<string, a
|
|
|
19
19
|
* setupAnalyticsLogger(analytics());
|
|
20
20
|
*/
|
|
21
21
|
export declare const setupAnalyticsLogger: (analyticsInstance: any) => void;
|
|
22
|
+
export declare const autoSetupAnalyticsLogger: () => boolean;
|
|
@@ -30,7 +30,7 @@ let currentUserId;
|
|
|
30
30
|
// ─── Core helpers ─────────────────────────────────────────────────────────────
|
|
31
31
|
const notify = () => {
|
|
32
32
|
const snapshot = [...events];
|
|
33
|
-
listeners.forEach(
|
|
33
|
+
listeners.forEach(cb => cb(snapshot));
|
|
34
34
|
};
|
|
35
35
|
const addEvent = (event) => {
|
|
36
36
|
events.unshift(event);
|
|
@@ -42,7 +42,7 @@ export const subscribeAnalyticsEvents = (callback) => {
|
|
|
42
42
|
listeners.push(callback);
|
|
43
43
|
callback([...events]);
|
|
44
44
|
return () => {
|
|
45
|
-
listeners = listeners.filter(
|
|
45
|
+
listeners = listeners.filter(l => l !== callback);
|
|
46
46
|
};
|
|
47
47
|
};
|
|
48
48
|
export const clearAnalyticsEvents = () => {
|
|
@@ -62,10 +62,10 @@ export const logAnalyticsEvent = (name, params = {}, userProperties = {}) => {
|
|
|
62
62
|
params,
|
|
63
63
|
userProperties: { ...currentUserProperties, ...userProperties },
|
|
64
64
|
timestamp: Date.now(),
|
|
65
|
-
source:
|
|
66
|
-
userId: currentUserId ??
|
|
67
|
-
screenName:
|
|
68
|
-
screenClass:
|
|
65
|
+
source: 'manual',
|
|
66
|
+
userId: currentUserId ?? '',
|
|
67
|
+
screenName: '',
|
|
68
|
+
screenClass: '',
|
|
69
69
|
});
|
|
70
70
|
};
|
|
71
71
|
// ─── Firebase Analytics instance patcher ─────────────────────────────────────
|
|
@@ -82,7 +82,7 @@ export const logAnalyticsEvent = (name, params = {}, userProperties = {}) => {
|
|
|
82
82
|
*/
|
|
83
83
|
export const setupAnalyticsLogger = (analyticsInstance) => {
|
|
84
84
|
if (!analyticsInstance) {
|
|
85
|
-
console.warn(
|
|
85
|
+
console.warn('[AnalyticsLogger] No analytics instance provided — skipping setup.');
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
88
|
// Guard against double-patching the same instance
|
|
@@ -98,10 +98,10 @@ export const setupAnalyticsLogger = (analyticsInstance) => {
|
|
|
98
98
|
params: params ?? {},
|
|
99
99
|
userProperties: { ...currentUserProperties },
|
|
100
100
|
timestamp: Date.now(),
|
|
101
|
-
source:
|
|
102
|
-
userId: currentUserId ??
|
|
103
|
-
screenName:
|
|
104
|
-
screenClass:
|
|
101
|
+
source: 'firebase',
|
|
102
|
+
userId: currentUserId ?? '',
|
|
103
|
+
screenName: '',
|
|
104
|
+
screenClass: '',
|
|
105
105
|
});
|
|
106
106
|
return originalLogEvent(name, params);
|
|
107
107
|
};
|
|
@@ -110,14 +110,14 @@ export const setupAnalyticsLogger = (analyticsInstance) => {
|
|
|
110
110
|
analyticsInstance.logScreenView = async (params) => {
|
|
111
111
|
addEvent({
|
|
112
112
|
id: counter++,
|
|
113
|
-
name:
|
|
113
|
+
name: 'screen_view',
|
|
114
114
|
params: params ?? {},
|
|
115
115
|
userProperties: { ...currentUserProperties },
|
|
116
116
|
timestamp: Date.now(),
|
|
117
|
-
source:
|
|
118
|
-
screenName: params?.screen_name ??
|
|
119
|
-
screenClass: params?.screen_class ??
|
|
120
|
-
userId: currentUserId ??
|
|
117
|
+
source: 'firebase',
|
|
118
|
+
screenName: params?.screen_name ?? '',
|
|
119
|
+
screenClass: params?.screen_class ?? '',
|
|
120
|
+
userId: currentUserId ?? '',
|
|
121
121
|
});
|
|
122
122
|
return originalLogScreenView(params);
|
|
123
123
|
};
|
|
@@ -150,3 +150,42 @@ export const setupAnalyticsLogger = (analyticsInstance) => {
|
|
|
150
150
|
return originalSetUserId(id);
|
|
151
151
|
};
|
|
152
152
|
};
|
|
153
|
+
export const autoSetupAnalyticsLogger = () => {
|
|
154
|
+
if (globalThis.__INSPECTOR_ANALYTICS_AUTOSETUP__)
|
|
155
|
+
return true;
|
|
156
|
+
let mod;
|
|
157
|
+
try {
|
|
158
|
+
mod = require('@react-native-firebase/analytics'); // optional dep
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return false; // GA not installed — silent no-op
|
|
162
|
+
}
|
|
163
|
+
const accessor = mod?.default ?? mod;
|
|
164
|
+
if (typeof accessor !== 'function')
|
|
165
|
+
return false;
|
|
166
|
+
try {
|
|
167
|
+
setupAnalyticsLogger(accessor());
|
|
168
|
+
}
|
|
169
|
+
catch { } // patch default-app instance
|
|
170
|
+
if (!accessor.__INSPECTOR_WRAPPED__) {
|
|
171
|
+
// patch future/named instances
|
|
172
|
+
const wrapped = function (...args) {
|
|
173
|
+
const instance = accessor.apply(this, args);
|
|
174
|
+
try {
|
|
175
|
+
setupAnalyticsLogger(instance);
|
|
176
|
+
}
|
|
177
|
+
catch { }
|
|
178
|
+
return instance;
|
|
179
|
+
};
|
|
180
|
+
Object.setPrototypeOf(wrapped, accessor);
|
|
181
|
+
Object.assign(wrapped, accessor);
|
|
182
|
+
wrapped.__INSPECTOR_WRAPPED__ = true;
|
|
183
|
+
try {
|
|
184
|
+
if (mod.default !== undefined)
|
|
185
|
+
mod.default = wrapped;
|
|
186
|
+
}
|
|
187
|
+
catch { }
|
|
188
|
+
}
|
|
189
|
+
globalThis.__INSPECTOR_ANALYTICS_AUTOSETUP__ = true;
|
|
190
|
+
return true;
|
|
191
|
+
};
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "./webViewLogger";
|
|
2
|
+
import axios from "axios";
|
|
2
3
|
let logs = [];
|
|
3
4
|
let listeners = [];
|
|
4
5
|
let counter = 0;
|
|
5
|
-
const ALLOWED_METHODS = [
|
|
6
|
+
const ALLOWED_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
7
|
+
const IGNORED_URL_PATTERNS = [/\/symbolicate(?:[/?#]|$)/];
|
|
8
|
+
function shouldIgnoreUrl(url) {
|
|
9
|
+
if (!url)
|
|
10
|
+
return false;
|
|
11
|
+
return IGNORED_URL_PATTERNS.some((re) => re.test(url));
|
|
12
|
+
}
|
|
6
13
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
7
14
|
function normaliseHeaders(raw) {
|
|
8
15
|
if (!raw)
|
|
9
16
|
return undefined;
|
|
10
|
-
if (typeof raw.forEach ===
|
|
17
|
+
if (typeof raw.forEach === "function") {
|
|
11
18
|
const result = {};
|
|
12
19
|
raw.forEach((value, key) => {
|
|
13
20
|
result[key] = value;
|
|
@@ -39,11 +46,11 @@ function parseRequestData(data) {
|
|
|
39
46
|
data._parts.forEach((part) => {
|
|
40
47
|
const key = part[0];
|
|
41
48
|
const value = part[1];
|
|
42
|
-
if (value && typeof value ===
|
|
49
|
+
if (value && typeof value === "object" && value.uri) {
|
|
43
50
|
parsedFormData[key] = {
|
|
44
51
|
_isFile: true,
|
|
45
|
-
name: value.name ||
|
|
46
|
-
type: value.type ||
|
|
52
|
+
name: value.name || "unknown",
|
|
53
|
+
type: value.type || "unknown",
|
|
47
54
|
uri: value.uri,
|
|
48
55
|
};
|
|
49
56
|
}
|
|
@@ -60,29 +67,29 @@ function getCallerFromStack() {
|
|
|
60
67
|
try {
|
|
61
68
|
const stack = new Error().stack;
|
|
62
69
|
if (!stack)
|
|
63
|
-
return
|
|
64
|
-
const lines = stack.split(
|
|
70
|
+
return "Unknown";
|
|
71
|
+
const lines = stack.split("\n");
|
|
65
72
|
for (let i = 0; i < lines.length; i++) {
|
|
66
73
|
const line = lines[i];
|
|
67
74
|
// Skip internal react-native network modules and the logger itself
|
|
68
|
-
if (line.includes(
|
|
69
|
-
line.includes(
|
|
70
|
-
line.includes(
|
|
71
|
-
line.includes(
|
|
75
|
+
if (line.includes("networkLogger") ||
|
|
76
|
+
line.includes("node_modules") ||
|
|
77
|
+
line.includes("Error") ||
|
|
78
|
+
line.includes("regeneratorRuntime")) {
|
|
72
79
|
continue;
|
|
73
80
|
}
|
|
74
|
-
return line.trim().replace(/^at /,
|
|
81
|
+
return line.trim().replace(/^at /, "");
|
|
75
82
|
}
|
|
76
83
|
}
|
|
77
84
|
catch (e) { }
|
|
78
|
-
return
|
|
85
|
+
return "Unknown";
|
|
79
86
|
}
|
|
80
87
|
// ─── Subscribe ────────────────────────────────────────────────────────────────
|
|
81
88
|
export const subscribeNetworkLogs = (callback) => {
|
|
82
89
|
listeners.push(callback);
|
|
83
90
|
callback([...logs]);
|
|
84
91
|
return () => {
|
|
85
|
-
listeners = listeners.filter(l => l !== callback);
|
|
92
|
+
listeners = listeners.filter((l) => l !== callback);
|
|
86
93
|
};
|
|
87
94
|
};
|
|
88
95
|
export const clearNetworkLogs = () => {
|
|
@@ -93,13 +100,15 @@ export const getNetworkLogs = () => [...logs];
|
|
|
93
100
|
// ─── Internal ─────────────────────────────────────────────────────────────────
|
|
94
101
|
const notify = () => {
|
|
95
102
|
const snapshot = [...logs];
|
|
96
|
-
listeners.forEach(cb => cb(snapshot));
|
|
103
|
+
listeners.forEach((cb) => cb(snapshot));
|
|
97
104
|
};
|
|
98
105
|
const addOrUpdateLog = (log) => {
|
|
99
106
|
const method = log.method?.toUpperCase();
|
|
100
107
|
if (!ALLOWED_METHODS.includes(method))
|
|
101
108
|
return;
|
|
102
|
-
|
|
109
|
+
if (shouldIgnoreUrl(log.url))
|
|
110
|
+
return;
|
|
111
|
+
const index = logs.findIndex((l) => l.id === log.id);
|
|
103
112
|
if (index >= 0) {
|
|
104
113
|
logs[index] = { ...logs[index], ...log };
|
|
105
114
|
}
|
|
@@ -117,12 +126,14 @@ export const setupNetworkLogger = () => {
|
|
|
117
126
|
const originalFetch = globalThis.fetch;
|
|
118
127
|
if (originalFetch) {
|
|
119
128
|
globalThis.fetch = async (url, options = {}) => {
|
|
120
|
-
const method = (options?.method ||
|
|
129
|
+
const method = (options?.method || "GET").toUpperCase();
|
|
121
130
|
if (!ALLOWED_METHODS.includes(method))
|
|
122
131
|
return originalFetch(url, options);
|
|
123
132
|
const id = counter++;
|
|
124
133
|
const start = Date.now();
|
|
125
|
-
const finalUrl = typeof url ===
|
|
134
|
+
const finalUrl = typeof url === "string" ? url : url?.url;
|
|
135
|
+
if (shouldIgnoreUrl(finalUrl))
|
|
136
|
+
return originalFetch(url, options);
|
|
126
137
|
const requestHeaders = normaliseHeaders(options?.headers);
|
|
127
138
|
const caller = getCallerFromStack(); // ✅ Capture call line
|
|
128
139
|
addOrUpdateLog({
|
|
@@ -131,7 +142,7 @@ export const setupNetworkLogger = () => {
|
|
|
131
142
|
method,
|
|
132
143
|
startTime: start,
|
|
133
144
|
caller,
|
|
134
|
-
request: method ===
|
|
145
|
+
request: method === "GET" ? undefined : parseRequestData(options?.body),
|
|
135
146
|
requestHeaders,
|
|
136
147
|
});
|
|
137
148
|
try {
|
|
@@ -139,11 +150,11 @@ export const setupNetworkLogger = () => {
|
|
|
139
150
|
const duration = Date.now() - start;
|
|
140
151
|
const responseHeaders = normaliseHeaders(response.headers);
|
|
141
152
|
let data = null;
|
|
142
|
-
const contentType = responseHeaders?.[
|
|
143
|
-
responseHeaders?.[
|
|
144
|
-
|
|
145
|
-
if (contentType.includes(
|
|
146
|
-
data =
|
|
153
|
+
const contentType = responseHeaders?.["content-type"] ||
|
|
154
|
+
responseHeaders?.["Content-Type"] ||
|
|
155
|
+
"";
|
|
156
|
+
if (contentType.includes("image/")) {
|
|
157
|
+
data = "[Image Data]";
|
|
147
158
|
}
|
|
148
159
|
else {
|
|
149
160
|
try {
|
|
@@ -186,52 +197,50 @@ export const setupNetworkLogger = () => {
|
|
|
186
197
|
}
|
|
187
198
|
};
|
|
188
199
|
}
|
|
189
|
-
//
|
|
200
|
+
// Hook Axios — patches both the default instance and any future axios.create() instances
|
|
190
201
|
try {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const instance = originalCreate.apply(this, args);
|
|
201
|
-
addAxiosInterceptors(instance);
|
|
202
|
-
return instance;
|
|
203
|
-
};
|
|
204
|
-
}
|
|
202
|
+
if (axios) {
|
|
203
|
+
addAxiosInterceptors(axios);
|
|
204
|
+
const originalCreate = axios.create;
|
|
205
|
+
if (typeof originalCreate === "function") {
|
|
206
|
+
axios.create = function (...args) {
|
|
207
|
+
const instance = originalCreate.apply(this, args);
|
|
208
|
+
addAxiosInterceptors(instance);
|
|
209
|
+
return instance;
|
|
210
|
+
};
|
|
205
211
|
}
|
|
206
212
|
}
|
|
207
213
|
}
|
|
208
|
-
catch (
|
|
209
|
-
//
|
|
214
|
+
catch (_e) {
|
|
215
|
+
// Axios not available — fetch-only mode
|
|
210
216
|
}
|
|
211
217
|
globalThis.__NETWORK_LOGGER_INITIALIZED__ = true;
|
|
212
218
|
};
|
|
213
219
|
// ─── Axios interceptor helper ─────────────────────────────────────────────────
|
|
214
220
|
export const addAxiosInterceptors = (axiosInstance) => {
|
|
215
221
|
axiosInstance.interceptors.request.use(async (config) => {
|
|
216
|
-
const method = (config.method ||
|
|
222
|
+
const method = (config.method || "GET").toUpperCase();
|
|
217
223
|
if (!ALLOWED_METHODS.includes(method))
|
|
218
224
|
return config;
|
|
219
225
|
const id = counter++;
|
|
220
226
|
const start = Date.now();
|
|
221
227
|
const caller = getCallerFromStack(); // ✅ Capture call line
|
|
228
|
+
let url = config.url ?? "";
|
|
229
|
+
if (!url.startsWith("http"))
|
|
230
|
+
url = `${config.baseURL ?? ""}${url}`;
|
|
231
|
+
// ✅ Leave config untagged so the response interceptor skips it too.
|
|
232
|
+
if (shouldIgnoreUrl(url))
|
|
233
|
+
return config;
|
|
222
234
|
config.__logId = id;
|
|
223
235
|
config.__logStart = start;
|
|
224
236
|
config.__logCaller = caller;
|
|
225
|
-
let url = config.url ?? '';
|
|
226
|
-
if (!url.startsWith('http'))
|
|
227
|
-
url = `${config.baseURL ?? ''}${url}`;
|
|
228
237
|
addOrUpdateLog({
|
|
229
238
|
id,
|
|
230
239
|
url,
|
|
231
240
|
method,
|
|
232
241
|
startTime: start,
|
|
233
242
|
caller,
|
|
234
|
-
request: method ===
|
|
243
|
+
request: method === "GET" ? undefined : parseRequestData(config.data),
|
|
235
244
|
requestHeaders: normaliseHeaders(config.headers),
|
|
236
245
|
});
|
|
237
246
|
return config;
|
|
@@ -241,12 +250,12 @@ export const addAxiosInterceptors = (axiosInstance) => {
|
|
|
241
250
|
const id = config.__logId;
|
|
242
251
|
const start = config.__logStart;
|
|
243
252
|
const caller = config.__logCaller;
|
|
244
|
-
const method = (config.method ||
|
|
253
|
+
const method = (config.method || "GET").toUpperCase();
|
|
245
254
|
if (id == null || !ALLOWED_METHODS.includes(method))
|
|
246
255
|
return response;
|
|
247
|
-
let url = config.url ??
|
|
248
|
-
if (!url.startsWith(
|
|
249
|
-
url = `${config.baseURL ??
|
|
256
|
+
let url = config.url ?? "";
|
|
257
|
+
if (!url.startsWith("http"))
|
|
258
|
+
url = `${config.baseURL ?? ""}${url}`;
|
|
250
259
|
addOrUpdateLog({
|
|
251
260
|
id,
|
|
252
261
|
url,
|
|
@@ -264,11 +273,11 @@ export const addAxiosInterceptors = (axiosInstance) => {
|
|
|
264
273
|
const id = config.__logId;
|
|
265
274
|
const start = config.__logStart;
|
|
266
275
|
const caller = config.__logCaller;
|
|
267
|
-
const method = (config.method ||
|
|
276
|
+
const method = (config.method || "GET").toUpperCase();
|
|
268
277
|
if (id != null && ALLOWED_METHODS.includes(method)) {
|
|
269
|
-
let url = config.url ??
|
|
270
|
-
if (!url.startsWith(
|
|
271
|
-
url = `${config.baseURL ??
|
|
278
|
+
let url = config.url ?? "";
|
|
279
|
+
if (!url.startsWith("http"))
|
|
280
|
+
url = `${config.baseURL ?? ""}${url}`;
|
|
272
281
|
addOrUpdateLog({
|
|
273
282
|
id,
|
|
274
283
|
url,
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
declare const NetworkInspectorWrapper: (props: any) => React.JSX.Element;
|
|
3
3
|
export default NetworkInspectorWrapper;
|
|
4
|
-
export { setupNetworkLogger, clearNetworkLogs, subscribeNetworkLogs, } from './customHooks/networkLogger';
|
|
4
|
+
export { setupNetworkLogger, clearNetworkLogs, subscribeNetworkLogs, addAxiosInterceptors, } from './customHooks/networkLogger';
|
|
5
5
|
export { setupConsoleLogger, clearConsoleLogs, subscribeConsoleLogs, } from './customHooks/consoleLogger';
|
|
6
6
|
export { setupAnalyticsLogger, logAnalyticsEvent, subscribeAnalyticsEvents, clearAnalyticsEvents, } from './customHooks/analyticsLogger';
|
|
7
7
|
export { WebView, getWebViewLogs, getWebViewNavHistory, getWebViewHtml, getWebViewCss, getWebViewJs, getWebViewHtmlUrl, clearWebViewData, subscribeWebView, } from './customHooks/webViewLogger';
|