@luciq/react-native 19.3.0-40271-SNAPSHOT → 19.4.0
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/CHANGELOG.md +15 -0
- package/android/native.gradle +1 -1
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqAPMModule.java +0 -9
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqNetworkLoggerModule.java +7 -29
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqReactnativeModule.java +52 -31
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqReactnativePackage.java +2 -0
- package/android/src/main/java/ai/luciq/reactlibrary/utils/EventEmitterModule.java +0 -7
- package/android/src/main/java/ai/luciq/reactlibrary/utils/ReportUtil.java +0 -7
- package/dist/modules/Luciq.d.ts +15 -0
- package/dist/modules/Luciq.js +22 -2
- package/dist/modules/NetworkLogger.d.ts +5 -0
- package/dist/modules/NetworkLogger.js +1 -9
- package/dist/native/NativeLuciq.d.ts +3 -0
- package/dist/utils/FeatureFlags.d.ts +0 -6
- package/dist/utils/FeatureFlags.js +0 -35
- package/dist/utils/LuciqUtils.js +0 -6
- package/dist/utils/XhrNetworkInterceptor.js +53 -85
- package/ios/RNLuciq/LuciqReactBridge.m +12 -0
- package/ios/native.rb +1 -1
- package/package.json +2 -1
- package/src/modules/Luciq.ts +25 -3
- package/src/modules/NetworkLogger.ts +1 -26
- package/src/native/NativeLuciq.ts +5 -0
- package/src/utils/FeatureFlags.ts +0 -44
- package/src/utils/LuciqUtils.ts +0 -15
- package/src/utils/XhrNetworkInterceptor.ts +55 -128
package/dist/utils/LuciqUtils.js
CHANGED
|
@@ -3,7 +3,6 @@ import parseErrorStackLib from 'react-native/Libraries/Core/Devtools/parseErrorS
|
|
|
3
3
|
import { NativeCrashReporting } from '../native/NativeCrashReporting';
|
|
4
4
|
import { NativeLuciq } from '../native/NativeLuciq';
|
|
5
5
|
import { NativeAPM } from '../native/NativeAPM';
|
|
6
|
-
import { Logger } from './logger';
|
|
7
6
|
import * as NetworkLogger from '../modules/NetworkLogger';
|
|
8
7
|
import { NativeNetworkLogger, NativeNetworkLoggerEvent, NetworkListenerType, NetworkLoggerEmitter, } from '../native/NativeNetworkLogger';
|
|
9
8
|
let apmFlags = {
|
|
@@ -182,12 +181,10 @@ export const reportNetworkLog = (network) => {
|
|
|
182
181
|
if (Platform.OS === 'android') {
|
|
183
182
|
const requestHeaders = JSON.stringify(network.requestHeaders);
|
|
184
183
|
const responseHeaders = JSON.stringify(network.responseHeaders);
|
|
185
|
-
Logger.debug('LCQ-RN-NET:', `[reportNetworkLog] Sending to NativeLuciq.networkLogAndroid: ${network.method} ${network.url}, status=${network.responseCode}, duration=${network.duration}ms, error=${network.errorDomain || 'none'}`);
|
|
186
184
|
NativeLuciq.networkLogAndroid(network.url, network.requestBody, network.responseBody, network.method, network.responseCode, requestHeaders, responseHeaders, network.duration);
|
|
187
185
|
if (!apmFlags.isNativeInterceptionFeatureEnabled ||
|
|
188
186
|
!apmFlags.hasAPMNetworkPlugin ||
|
|
189
187
|
!apmFlags.shouldEnableNativeInterception) {
|
|
190
|
-
Logger.debug('LCQ-RN-NET:', `[reportNetworkLog] Also sending to NativeAPM.networkLogAndroid (native interception disabled): ${network.method} ${network.url}`);
|
|
191
188
|
NativeAPM.networkLogAndroid(network.startTime, network.duration, requestHeaders, network.requestBody, network.requestBodySize, network.method, network.url, network.requestContentType, responseHeaders, network.responseBody, network.responseBodySize, network.responseCode, network.contentType, network.errorDomain, {
|
|
192
189
|
isW3cHeaderFound: network.isW3cHeaderFound,
|
|
193
190
|
partialId: network.partialId,
|
|
@@ -196,9 +193,6 @@ export const reportNetworkLog = (network) => {
|
|
|
196
193
|
w3cCaughtHeader: network.w3cCaughtHeader,
|
|
197
194
|
}, network.gqlQueryName, network.serverErrorMessage);
|
|
198
195
|
}
|
|
199
|
-
else {
|
|
200
|
-
Logger.debug('LCQ-RN-NET:', `[reportNetworkLog] Skipping NativeAPM.networkLogAndroid (native interception enabled): nativeFeature=${apmFlags.isNativeInterceptionFeatureEnabled}, hasPlugin=${apmFlags.hasAPMNetworkPlugin}, shouldEnable=${apmFlags.shouldEnableNativeInterception}`);
|
|
201
|
-
}
|
|
202
196
|
}
|
|
203
197
|
else {
|
|
204
198
|
NativeLuciq.networkLogIOS(network.url, network.method, network.requestBody, network.requestBodySize, network.responseBody, network.responseBodySize, network.responseCode, network.requestHeaders, network.responseHeaders, network.contentType, network.errorDomain, network.errorCode, network.startTime, network.duration, network.gqlQueryName, network.serverErrorMessage, {
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import LuciqConstants from './LuciqConstants';
|
|
2
2
|
import { stringifyIfNotString, generateW3CHeader } from './LuciqUtils';
|
|
3
|
-
import {
|
|
4
|
-
import { Logger } from './logger';
|
|
5
|
-
const TAG = 'LCQ-RN-NET:';
|
|
3
|
+
import { FeatureFlags } from '../utils/FeatureFlags';
|
|
6
4
|
const XMLHttpRequest = global.XMLHttpRequest;
|
|
7
5
|
let originalXHROpen = XMLHttpRequest.prototype.open;
|
|
8
6
|
let originalXHRSend = XMLHttpRequest.prototype.send;
|
|
@@ -10,34 +8,40 @@ let originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
|
10
8
|
let onProgressCallback;
|
|
11
9
|
let onDoneCallback;
|
|
12
10
|
let isInterceptorEnabled = false;
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
11
|
+
let network;
|
|
12
|
+
const _reset = () => {
|
|
13
|
+
network = {
|
|
14
|
+
id: '',
|
|
15
|
+
url: '',
|
|
16
|
+
method: '',
|
|
17
|
+
requestBody: '',
|
|
18
|
+
requestBodySize: 0,
|
|
19
|
+
responseBody: '',
|
|
20
|
+
responseBodySize: 0,
|
|
21
|
+
responseCode: 0,
|
|
22
|
+
requestHeaders: {},
|
|
23
|
+
responseHeaders: {},
|
|
24
|
+
contentType: '',
|
|
25
|
+
errorDomain: '',
|
|
26
|
+
errorCode: 0,
|
|
27
|
+
startTime: 0,
|
|
28
|
+
duration: 0,
|
|
29
|
+
gqlQueryName: '',
|
|
30
|
+
serverErrorMessage: '',
|
|
31
|
+
requestContentType: '',
|
|
32
|
+
isW3cHeaderFound: null,
|
|
33
|
+
partialId: null,
|
|
34
|
+
networkStartTimeInSeconds: null,
|
|
35
|
+
w3cGeneratedHeader: null,
|
|
36
|
+
w3cCaughtHeader: null,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const getTraceparentHeader = async (networkData) => {
|
|
40
|
+
const [isW3cExternalTraceIDEnabled, isW3cExternalGeneratedHeaderEnabled, isW3cCaughtHeaderEnabled,] = await Promise.all([
|
|
41
|
+
FeatureFlags.isW3ExternalTraceID(),
|
|
42
|
+
FeatureFlags.isW3ExternalGeneratedHeader(),
|
|
43
|
+
FeatureFlags.isW3CaughtHeader(),
|
|
44
|
+
]);
|
|
41
45
|
return injectHeaders(networkData, {
|
|
42
46
|
isW3cExternalTraceIDEnabled,
|
|
43
47
|
isW3cExternalGeneratedHeaderEnabled,
|
|
@@ -81,57 +85,39 @@ export default {
|
|
|
81
85
|
onProgressCallback = callback;
|
|
82
86
|
},
|
|
83
87
|
enableInterception() {
|
|
88
|
+
// Prevents infinite calls to XMLHttpRequest.open when enabling interception multiple times
|
|
84
89
|
if (isInterceptorEnabled) {
|
|
85
|
-
Logger.debug(TAG, 'enableInterception called but already enabled, skipping');
|
|
86
90
|
return;
|
|
87
91
|
}
|
|
88
|
-
Logger.debug(TAG, 'Enabling XHR network interception');
|
|
89
92
|
originalXHROpen = XMLHttpRequest.prototype.open;
|
|
90
93
|
originalXHRSend = XMLHttpRequest.prototype.send;
|
|
91
94
|
originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
92
95
|
// An error code that signifies an issue with the RN client.
|
|
93
96
|
const clientErrorCode = 9876;
|
|
94
97
|
XMLHttpRequest.prototype.open = function (method, url, ...args) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
networkMap.set(this, networkData);
|
|
99
|
-
Logger.debug(TAG, `[open] ${method} ${url}`);
|
|
98
|
+
_reset();
|
|
99
|
+
network.url = url;
|
|
100
|
+
network.method = method;
|
|
100
101
|
originalXHROpen.apply(this, [method, url, ...args]);
|
|
101
102
|
};
|
|
102
103
|
XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
|
|
104
|
+
// According to the HTTP RFC, headers are case-insensitive, so we convert
|
|
105
|
+
// them to lower-case to make accessing headers predictable.
|
|
106
|
+
// This avoid issues like failing to get the Content-Type header for a request
|
|
107
|
+
// because the header is set as 'Content-Type' instead of 'content-type'.
|
|
103
108
|
const key = header.toLowerCase();
|
|
104
|
-
|
|
105
|
-
if (networkData) {
|
|
106
|
-
networkData.requestHeaders[key] = stringifyIfNotString(value);
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
Logger.debug(TAG, `[setRequestHeader] No networkData found in WeakMap for header "${key}" — request may have been GC'd or open() was not called`);
|
|
110
|
-
}
|
|
109
|
+
network.requestHeaders[key] = stringifyIfNotString(value);
|
|
111
110
|
originalXHRSetRequestHeader.apply(this, [header, value]);
|
|
112
111
|
};
|
|
113
|
-
XMLHttpRequest.prototype.send = function (data) {
|
|
114
|
-
const
|
|
115
|
-
if (!networkData) {
|
|
116
|
-
Logger.debug(TAG, '[send] No networkData found in WeakMap — falling back to original send (open() was not intercepted)');
|
|
117
|
-
originalXHRSend.apply(this, [data]);
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
Logger.debug(TAG, `[send] ${networkData.method} ${networkData.url}`);
|
|
121
|
-
const cloneNetwork = JSON.parse(JSON.stringify(networkData));
|
|
112
|
+
XMLHttpRequest.prototype.send = async function (data) {
|
|
113
|
+
const cloneNetwork = JSON.parse(JSON.stringify(network));
|
|
122
114
|
cloneNetwork.requestBody = data ? data : '';
|
|
123
115
|
if (typeof cloneNetwork.requestBody !== 'string') {
|
|
124
116
|
cloneNetwork.requestBody = JSON.stringify(cloneNetwork.requestBody);
|
|
125
117
|
}
|
|
126
|
-
let isReported = false;
|
|
127
118
|
if (this.addEventListener) {
|
|
128
119
|
this.addEventListener('readystatechange', async () => {
|
|
129
120
|
if (!isInterceptorEnabled) {
|
|
130
|
-
Logger.debug(TAG, `[readystatechange] Interceptor disabled, ignoring state=${this.readyState} for ${cloneNetwork.url}`);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
if (isReported) {
|
|
134
|
-
Logger.debug(TAG, `[readystatechange] Already reported, ignoring state=${this.readyState} for ${cloneNetwork.url}`);
|
|
135
121
|
return;
|
|
136
122
|
}
|
|
137
123
|
if (this.readyState === this.HEADERS_RECEIVED) {
|
|
@@ -160,7 +146,6 @@ export default {
|
|
|
160
146
|
cloneNetwork.requestContentType =
|
|
161
147
|
cloneNetwork.requestHeaders['content-type'].split(';')[0];
|
|
162
148
|
}
|
|
163
|
-
Logger.debug(TAG, `[readystatechange] HEADERS_RECEIVED for ${cloneNetwork.url}, contentType=${cloneNetwork.contentType}`);
|
|
164
149
|
}
|
|
165
150
|
if (this.readyState === this.DONE) {
|
|
166
151
|
cloneNetwork.duration = Date.now() - cloneNetwork.startTime;
|
|
@@ -181,11 +166,11 @@ export default {
|
|
|
181
166
|
cloneNetwork.requestBody =
|
|
182
167
|
typeof _response === 'string' ? _response : JSON.stringify(_response);
|
|
183
168
|
cloneNetwork.responseBody = '';
|
|
169
|
+
// Detect a more descriptive error message.
|
|
184
170
|
if (typeof _response === 'string' && _response.length > 0) {
|
|
185
171
|
cloneNetwork.errorDomain = _response;
|
|
186
172
|
}
|
|
187
173
|
cloneNetwork.responseBody = `ERROR: ${cloneNetwork.errorDomain}`;
|
|
188
|
-
Logger.debug(TAG, `[readystatechange] DONE with client error for ${cloneNetwork.url}, errorDomain=${cloneNetwork.errorDomain}`);
|
|
189
174
|
// @ts-ignore
|
|
190
175
|
}
|
|
191
176
|
else if (this._timedOut) {
|
|
@@ -194,7 +179,6 @@ export default {
|
|
|
194
179
|
cloneNetwork.responseCode = 0;
|
|
195
180
|
cloneNetwork.contentType = 'text/plain';
|
|
196
181
|
cloneNetwork.responseBody = `ERROR: ${cloneNetwork.errorDomain}`;
|
|
197
|
-
Logger.debug(TAG, `[readystatechange] DONE with timeout for ${cloneNetwork.url}`);
|
|
198
182
|
}
|
|
199
183
|
// Only set response body if not already set by error handlers
|
|
200
184
|
if (!cloneNetwork.errorDomain) {
|
|
@@ -241,20 +225,16 @@ export default {
|
|
|
241
225
|
else {
|
|
242
226
|
delete cloneNetwork.gqlQueryName;
|
|
243
227
|
}
|
|
244
|
-
isReported = true;
|
|
245
|
-
Logger.debug(TAG, `[readystatechange] DONE for ${cloneNetwork.method} ${cloneNetwork.url} — status=${cloneNetwork.responseCode}, duration=${cloneNetwork.duration}ms, hasCallback=${!!onDoneCallback}`);
|
|
246
228
|
if (onDoneCallback) {
|
|
247
229
|
onDoneCallback(cloneNetwork);
|
|
248
230
|
}
|
|
249
|
-
else {
|
|
250
|
-
Logger.debug(TAG, `[readystatechange] WARNING: onDoneCallback is null, network log for ${cloneNetwork.url} will be LOST`);
|
|
251
|
-
}
|
|
252
231
|
}
|
|
253
232
|
});
|
|
254
233
|
const downloadUploadProgressCallback = (event) => {
|
|
255
234
|
if (!isInterceptorEnabled) {
|
|
256
235
|
return;
|
|
257
236
|
}
|
|
237
|
+
// check if will be able to compute progress
|
|
258
238
|
if (event.lengthComputable && onProgressCallback) {
|
|
259
239
|
const totalBytesSent = event.loaded;
|
|
260
240
|
const totalBytesExpectedToSend = event.total - event.loaded;
|
|
@@ -263,43 +243,31 @@ export default {
|
|
|
263
243
|
};
|
|
264
244
|
this.addEventListener('progress', downloadUploadProgressCallback);
|
|
265
245
|
this.upload.addEventListener('progress', downloadUploadProgressCallback);
|
|
246
|
+
// Handler for abort events (works with fetch, Axios, and any XHR-based requests)
|
|
266
247
|
this.addEventListener('abort', () => {
|
|
267
248
|
if (!isInterceptorEnabled) {
|
|
268
|
-
Logger.debug(TAG, `[abort] Interceptor disabled, ignoring abort for ${cloneNetwork.url}`);
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
if (isReported) {
|
|
272
|
-
Logger.debug(TAG, `[abort] Already reported via readystatechange DONE, ignoring duplicate abort for ${cloneNetwork.url}`);
|
|
273
249
|
return;
|
|
274
250
|
}
|
|
275
|
-
isReported = true;
|
|
276
251
|
cloneNetwork.duration = Date.now() - cloneNetwork.startTime;
|
|
277
252
|
cloneNetwork.responseCode = 0;
|
|
278
253
|
cloneNetwork.errorCode = clientErrorCode;
|
|
279
254
|
cloneNetwork.errorDomain = 'cancelled';
|
|
280
255
|
cloneNetwork.responseBody = `ERROR: ${cloneNetwork.errorDomain}`;
|
|
281
|
-
Logger.debug(TAG, `[abort] Request cancelled: ${cloneNetwork.method} ${cloneNetwork.url}, duration=${cloneNetwork.duration}ms, hasCallback=${!!onDoneCallback}`);
|
|
282
|
-
if (onDoneCallback) {
|
|
283
|
-
onDoneCallback(cloneNetwork);
|
|
284
|
-
}
|
|
285
|
-
else {
|
|
286
|
-
Logger.debug(TAG, `[abort] WARNING: onDoneCallback is null, cancelled log for ${cloneNetwork.url} will be LOST`);
|
|
287
|
-
}
|
|
288
256
|
});
|
|
289
257
|
}
|
|
290
258
|
cloneNetwork.startTime = Date.now();
|
|
291
|
-
const traceparent = getTraceparentHeader(cloneNetwork);
|
|
259
|
+
const traceparent = await getTraceparentHeader(cloneNetwork);
|
|
292
260
|
if (traceparent) {
|
|
293
261
|
this.setRequestHeader('Traceparent', traceparent);
|
|
294
|
-
|
|
262
|
+
}
|
|
263
|
+
if (this.readyState === this.UNSENT) {
|
|
264
|
+
return; // Prevent sending the request if not opened
|
|
295
265
|
}
|
|
296
266
|
originalXHRSend.apply(this, [data]);
|
|
297
267
|
};
|
|
298
268
|
isInterceptorEnabled = true;
|
|
299
|
-
Logger.debug(TAG, 'XHR network interception enabled');
|
|
300
269
|
},
|
|
301
270
|
disableInterception() {
|
|
302
|
-
Logger.debug(TAG, 'Disabling XHR network interception');
|
|
303
271
|
isInterceptorEnabled = false;
|
|
304
272
|
XMLHttpRequest.prototype.send = originalXHRSend;
|
|
305
273
|
XMLHttpRequest.prototype.open = originalXHROpen;
|
|
@@ -98,6 +98,18 @@ RCT_EXPORT_METHOD(setTrackUserSteps:(BOOL)isEnabled) {
|
|
|
98
98
|
[Luciq setTrackUserSteps:isEnabled];
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
RCT_EXPORT_METHOD(setWebViewMonitoringEnabled:(BOOL)isEnabled) {
|
|
102
|
+
[Luciq setWebViewMonitoringEnabled:isEnabled];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
RCT_EXPORT_METHOD(setWebViewNetworkTrackingEnabled:(BOOL)isEnabled) {
|
|
106
|
+
[Luciq setWebViewNetworkTrackingEnabled:isEnabled];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
RCT_EXPORT_METHOD(setWebViewUserInteractionsTrackingEnabled:(BOOL)isEnabled) {
|
|
110
|
+
[Luciq setWebViewUserInteractionsTrackingEnabled:isEnabled];
|
|
111
|
+
}
|
|
112
|
+
|
|
101
113
|
LCQReport *currentReport = nil;
|
|
102
114
|
RCT_EXPORT_METHOD(setPreSendingHandler:(RCTResponseSenderBlock)callBack) {
|
|
103
115
|
if (callBack != nil) {
|
package/ios/native.rb
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luciq/react-native",
|
|
3
3
|
"description": "Luciq is the Agentic Observability Platform built for Mobile.",
|
|
4
|
-
"version": "19.
|
|
4
|
+
"version": "19.4.0",
|
|
5
5
|
"author": "Luciq (https://luciq.ai)",
|
|
6
6
|
"repository": "github:luciqai/luciq-reactnative-sdk",
|
|
7
7
|
"homepage": "https://www.luciq.ai/platforms/react-native",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@apollo/client": "^3.7.0",
|
|
46
|
+
"@instabug/danger-plugin-coverage": "Instabug/danger-plugin-coverage",
|
|
46
47
|
"@react-native-community/eslint-config": "^3.1.0",
|
|
47
48
|
"@react-navigation/native": "^6.1.7",
|
|
48
49
|
"@rollup/plugin-commonjs": "^25.0.3",
|
package/src/modules/Luciq.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { NavigationAction, NavigationState as NavigationStateV4 } from 'rea
|
|
|
10
10
|
import type { LuciqConfig } from '../models/LuciqConfig';
|
|
11
11
|
import Report from '../models/Report';
|
|
12
12
|
import { emitter, NativeEvents, NativeLuciq } from '../native/NativeLuciq';
|
|
13
|
-
import { registerFeatureFlagsListener
|
|
13
|
+
import { registerFeatureFlagsListener } from '../utils/FeatureFlags';
|
|
14
14
|
import {
|
|
15
15
|
AutoMaskingType,
|
|
16
16
|
ColorTheme,
|
|
@@ -81,8 +81,6 @@ function reportCurrentViewForAndroid(screenName: string | null) {
|
|
|
81
81
|
* @param config SDK configurations. See {@link LuciqConfig} for more info.
|
|
82
82
|
*/
|
|
83
83
|
export const init = (config: LuciqConfig) => {
|
|
84
|
-
initFeatureFlagsCache();
|
|
85
|
-
|
|
86
84
|
if (Platform.OS === 'android') {
|
|
87
85
|
// Add android feature flags listener for android
|
|
88
86
|
registerFeatureFlagsListener();
|
|
@@ -132,6 +130,30 @@ export const setAppVariant = (appVariant: string) => {
|
|
|
132
130
|
NativeLuciq.setAppVariant(appVariant);
|
|
133
131
|
};
|
|
134
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Enables or disables WebView monitoring.
|
|
135
|
+
* @param isEnabled A boolean to enable/disable WebView monitoring.
|
|
136
|
+
*/
|
|
137
|
+
export const setWebViewMonitoringEnabled = (isEnabled: boolean) => {
|
|
138
|
+
NativeLuciq.setWebViewMonitoringEnabled(isEnabled);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Enables or disables WebView network tracking.
|
|
143
|
+
* @param isEnabled A boolean to enable/disable WebView network tracking.
|
|
144
|
+
*/
|
|
145
|
+
export const setWebViewNetworkTrackingEnabled = (isEnabled: boolean) => {
|
|
146
|
+
NativeLuciq.setWebViewNetworkTrackingEnabled(isEnabled);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Enables or disables WebView user interactions tracking.
|
|
151
|
+
* @param isEnabled A boolean to enable/disable WebView user interactions tracking.
|
|
152
|
+
*/
|
|
153
|
+
export const setWebViewUserInteractionsTrackingEnabled = (isEnabled: boolean) => {
|
|
154
|
+
NativeLuciq.setWebViewUserInteractionsTrackingEnabled(isEnabled);
|
|
155
|
+
};
|
|
156
|
+
|
|
135
157
|
/**
|
|
136
158
|
* Handles app state changes and updates APM network flags if necessary.
|
|
137
159
|
*/
|
|
@@ -39,17 +39,10 @@ function getPortFromUrl(url: string) {
|
|
|
39
39
|
* It is enabled by default.
|
|
40
40
|
* @param isEnabled
|
|
41
41
|
*/
|
|
42
|
-
const NET_TAG = 'LCQ-RN-NET:';
|
|
43
|
-
|
|
44
42
|
export const setEnabled = (isEnabled: boolean) => {
|
|
45
43
|
if (isEnabled) {
|
|
46
44
|
xhr.enableInterception();
|
|
47
45
|
xhr.setOnDoneCallback(async (network) => {
|
|
48
|
-
Logger.debug(
|
|
49
|
-
NET_TAG,
|
|
50
|
-
`[NetworkLogger] onDoneCallback received: ${network.method} ${network.url}, status=${network.responseCode}`,
|
|
51
|
-
);
|
|
52
|
-
|
|
53
46
|
// eslint-disable-next-line no-new-func
|
|
54
47
|
const predicate = Function('network', 'return ' + _requestFilterExpression);
|
|
55
48
|
|
|
@@ -57,17 +50,12 @@ export const setEnabled = (isEnabled: boolean) => {
|
|
|
57
50
|
const MAX_NETWORK_BODY_SIZE_IN_BYTES = await NativeLuciq.getNetworkBodyMaxSize();
|
|
58
51
|
try {
|
|
59
52
|
if (_networkDataObfuscationHandler) {
|
|
60
|
-
Logger.debug(NET_TAG, `[NetworkLogger] Running obfuscation handler for ${network.url}`);
|
|
61
53
|
network = await _networkDataObfuscationHandler(network);
|
|
62
54
|
}
|
|
63
55
|
|
|
64
56
|
if (__DEV__) {
|
|
65
57
|
const urlPort = getPortFromUrl(network.url);
|
|
66
58
|
if (urlPort === LuciqRNConfig.metroDevServerPort) {
|
|
67
|
-
Logger.debug(
|
|
68
|
-
NET_TAG,
|
|
69
|
-
`[NetworkLogger] Skipping Metro dev server request: ${network.url}`,
|
|
70
|
-
);
|
|
71
59
|
return;
|
|
72
60
|
}
|
|
73
61
|
}
|
|
@@ -109,23 +97,10 @@ export const setEnabled = (isEnabled: boolean) => {
|
|
|
109
97
|
);
|
|
110
98
|
}
|
|
111
99
|
|
|
112
|
-
Logger.debug(
|
|
113
|
-
NET_TAG,
|
|
114
|
-
`[NetworkLogger] Reporting network log to native: ${network.method} ${network.url}`,
|
|
115
|
-
);
|
|
116
100
|
reportNetworkLog(network);
|
|
117
101
|
} catch (e) {
|
|
118
|
-
Logger.error(
|
|
119
|
-
NET_TAG,
|
|
120
|
-
`[NetworkLogger] Error processing network log for ${network.url}:`,
|
|
121
|
-
e,
|
|
122
|
-
);
|
|
102
|
+
Logger.error(e);
|
|
123
103
|
}
|
|
124
|
-
} else {
|
|
125
|
-
Logger.debug(
|
|
126
|
-
NET_TAG,
|
|
127
|
-
`[NetworkLogger] Request filtered out by predicate: ${network.method} ${network.url}, expression="${_requestFilterExpression}"`,
|
|
128
|
-
);
|
|
129
104
|
}
|
|
130
105
|
});
|
|
131
106
|
} else {
|
|
@@ -167,6 +167,11 @@ export interface LuciqNativeModule extends NativeModule {
|
|
|
167
167
|
|
|
168
168
|
setTheme(theme: ThemeConfig): void;
|
|
169
169
|
setFullscreen(isEnabled: boolean): void;
|
|
170
|
+
|
|
171
|
+
// WebView APIs //
|
|
172
|
+
setWebViewMonitoringEnabled(isEnabled: boolean): void;
|
|
173
|
+
setWebViewNetworkTrackingEnabled(isEnabled: boolean): void;
|
|
174
|
+
setWebViewUserInteractionsTrackingEnabled(isEnabled: boolean): void;
|
|
170
175
|
}
|
|
171
176
|
|
|
172
177
|
export const NativeLuciq = NativeModules.Luciq as LuciqNativeModule;
|
|
@@ -1,40 +1,5 @@
|
|
|
1
1
|
import { NativeLuciq } from '../native/NativeLuciq';
|
|
2
2
|
import { _registerFeatureFlagsChangeListener } from '../modules/Luciq';
|
|
3
|
-
import { Logger } from './logger';
|
|
4
|
-
|
|
5
|
-
const TAG = 'LCQ-RN-NET:';
|
|
6
|
-
|
|
7
|
-
let cachedW3cFlags = {
|
|
8
|
-
isW3cExternalTraceIDEnabled: false,
|
|
9
|
-
isW3cExternalGeneratedHeaderEnabled: false,
|
|
10
|
-
isW3cCaughtHeaderEnabled: false,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export async function initFeatureFlagsCache() {
|
|
14
|
-
Logger.debug(TAG, '[FeatureFlags] Initializing W3C feature flags cache from native bridge...');
|
|
15
|
-
try {
|
|
16
|
-
const [traceID, generatedHeader, caughtHeader] = await Promise.all([
|
|
17
|
-
NativeLuciq.isW3ExternalTraceIDEnabled(),
|
|
18
|
-
NativeLuciq.isW3ExternalGeneratedHeaderEnabled(),
|
|
19
|
-
NativeLuciq.isW3CaughtHeaderEnabled(),
|
|
20
|
-
]);
|
|
21
|
-
cachedW3cFlags = {
|
|
22
|
-
isW3cExternalTraceIDEnabled: traceID,
|
|
23
|
-
isW3cExternalGeneratedHeaderEnabled: generatedHeader,
|
|
24
|
-
isW3cCaughtHeaderEnabled: caughtHeader,
|
|
25
|
-
};
|
|
26
|
-
Logger.debug(
|
|
27
|
-
TAG,
|
|
28
|
-
`[FeatureFlags] Cache initialized: traceID=${traceID}, generatedHeader=${generatedHeader}, caughtHeader=${caughtHeader}`,
|
|
29
|
-
);
|
|
30
|
-
} catch (e) {
|
|
31
|
-
Logger.debug(TAG, '[FeatureFlags] Failed to initialize cache, using defaults (all false):', e);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function getCachedW3cFlags() {
|
|
36
|
-
return cachedW3cFlags;
|
|
37
|
-
}
|
|
38
3
|
|
|
39
4
|
export const FeatureFlags = {
|
|
40
5
|
isW3ExternalTraceID: () => NativeLuciq.isW3ExternalTraceIDEnabled(),
|
|
@@ -51,15 +16,6 @@ export const registerFeatureFlagsListener = () => {
|
|
|
51
16
|
isW3CaughtHeaderEnabled: boolean;
|
|
52
17
|
networkBodyLimit: number;
|
|
53
18
|
}) => {
|
|
54
|
-
Logger.debug(
|
|
55
|
-
TAG,
|
|
56
|
-
`[FeatureFlags] Flags updated from native listener: traceID=${res.isW3ExternalTraceIDEnabled}, generatedHeader=${res.isW3ExternalGeneratedHeaderEnabled}, caughtHeader=${res.isW3CaughtHeaderEnabled}, bodyLimit=${res.networkBodyLimit}`,
|
|
57
|
-
);
|
|
58
|
-
cachedW3cFlags = {
|
|
59
|
-
isW3cExternalTraceIDEnabled: res.isW3ExternalTraceIDEnabled,
|
|
60
|
-
isW3cExternalGeneratedHeaderEnabled: res.isW3ExternalGeneratedHeaderEnabled,
|
|
61
|
-
isW3cCaughtHeaderEnabled: res.isW3CaughtHeaderEnabled,
|
|
62
|
-
};
|
|
63
19
|
FeatureFlags.isW3ExternalTraceID = async () => {
|
|
64
20
|
return res.isW3ExternalTraceIDEnabled;
|
|
65
21
|
};
|
package/src/utils/LuciqUtils.ts
CHANGED
|
@@ -12,7 +12,6 @@ import { NativeCrashReporting } from '../native/NativeCrashReporting';
|
|
|
12
12
|
import type { NetworkData } from './XhrNetworkInterceptor';
|
|
13
13
|
import { NativeLuciq } from '../native/NativeLuciq';
|
|
14
14
|
import { NativeAPM } from '../native/NativeAPM';
|
|
15
|
-
import { Logger } from './logger';
|
|
16
15
|
import * as NetworkLogger from '../modules/NetworkLogger';
|
|
17
16
|
import {
|
|
18
17
|
NativeNetworkLogger,
|
|
@@ -232,11 +231,6 @@ export const reportNetworkLog = (network: NetworkData) => {
|
|
|
232
231
|
const requestHeaders = JSON.stringify(network.requestHeaders);
|
|
233
232
|
const responseHeaders = JSON.stringify(network.responseHeaders);
|
|
234
233
|
|
|
235
|
-
Logger.debug(
|
|
236
|
-
'LCQ-RN-NET:',
|
|
237
|
-
`[reportNetworkLog] Sending to NativeLuciq.networkLogAndroid: ${network.method} ${network.url}, status=${network.responseCode}, duration=${network.duration}ms, error=${network.errorDomain || 'none'}`,
|
|
238
|
-
);
|
|
239
|
-
|
|
240
234
|
NativeLuciq.networkLogAndroid(
|
|
241
235
|
network.url,
|
|
242
236
|
network.requestBody,
|
|
@@ -252,10 +246,6 @@ export const reportNetworkLog = (network: NetworkData) => {
|
|
|
252
246
|
!apmFlags.hasAPMNetworkPlugin ||
|
|
253
247
|
!apmFlags.shouldEnableNativeInterception
|
|
254
248
|
) {
|
|
255
|
-
Logger.debug(
|
|
256
|
-
'LCQ-RN-NET:',
|
|
257
|
-
`[reportNetworkLog] Also sending to NativeAPM.networkLogAndroid (native interception disabled): ${network.method} ${network.url}`,
|
|
258
|
-
);
|
|
259
249
|
NativeAPM.networkLogAndroid(
|
|
260
250
|
network.startTime,
|
|
261
251
|
network.duration,
|
|
@@ -281,11 +271,6 @@ export const reportNetworkLog = (network: NetworkData) => {
|
|
|
281
271
|
network.gqlQueryName,
|
|
282
272
|
network.serverErrorMessage,
|
|
283
273
|
);
|
|
284
|
-
} else {
|
|
285
|
-
Logger.debug(
|
|
286
|
-
'LCQ-RN-NET:',
|
|
287
|
-
`[reportNetworkLog] Skipping NativeAPM.networkLogAndroid (native interception enabled): nativeFeature=${apmFlags.isNativeInterceptionFeatureEnabled}, hasPlugin=${apmFlags.hasAPMNetworkPlugin}, shouldEnable=${apmFlags.shouldEnableNativeInterception}`,
|
|
288
|
-
);
|
|
289
274
|
}
|
|
290
275
|
} else {
|
|
291
276
|
NativeLuciq.networkLogIOS(
|