@rozenite/network-activity-plugin 1.8.1 → 1.10.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 +56 -0
- package/dist/devtools/App.html +2 -2
- package/dist/devtools/assets/{App-m6xge0az.css → App-CUXU0mup.css} +152 -2
- package/dist/devtools/assets/{App-B3xlUjs6.js → App-DsimzJvx.js} +6833 -966
- package/dist/react-native/chunks/boot-recording.cjs +156 -28
- package/dist/react-native/chunks/boot-recording.js +156 -28
- package/dist/react-native/chunks/get-nitro-module.cjs +12 -0
- package/dist/react-native/chunks/get-nitro-module.js +13 -0
- package/dist/react-native/chunks/useNetworkActivityDevTools.require.cjs +20 -1
- package/dist/react-native/chunks/useNetworkActivityDevTools.require.js +20 -1
- package/dist/react-native/index.d.ts +39 -3
- package/dist/rozenite.json +1 -1
- package/dist/sdk/index.d.ts +37 -1
- package/package.json +12 -7
- package/src/react-native/agent/use-network-activity-agent-tools.ts +22 -4
- package/src/react-native/http/__tests__/http-utils.test.ts +228 -0
- package/src/react-native/http/http-utils.ts +209 -25
- package/src/react-native/network-inspector.ts +2 -2
- package/src/react-native/nitro-fetch/get-nitro-module.ts +13 -0
- package/src/react-native/nitro-fetch/nitro-network-inspector.ts +10 -11
- package/src/shared/http-events.ts +40 -1
- package/src/ui/components/CodeBlock.tsx +45 -1
- package/src/ui/components/FilterBar.tsx +366 -58
- package/src/ui/components/HexView.tsx +54 -0
- package/src/ui/components/MetadataCard.tsx +95 -0
- package/src/ui/components/RequestList.tsx +192 -34
- package/src/ui/components/SidePanel.tsx +42 -1
- package/src/ui/components/ViewToggle.tsx +44 -0
- package/src/ui/components/XmlTree.tsx +160 -0
- package/src/ui/components/__tests__/CodeBlock.test.tsx +89 -0
- package/src/ui/components/__tests__/HexView.test.tsx +41 -0
- package/src/ui/components/__tests__/MetadataCard.test.tsx +107 -0
- package/src/ui/components/__tests__/ViewToggle.test.tsx +80 -0
- package/src/ui/components/__tests__/XmlTree.test.tsx +149 -0
- package/src/ui/response-renderers/__tests__/binary-too-large.test.tsx +56 -0
- package/src/ui/response-renderers/__tests__/binary.test.tsx +96 -0
- package/src/ui/response-renderers/__tests__/dispatch.test.ts +124 -0
- package/src/ui/response-renderers/__tests__/html.test.tsx +101 -0
- package/src/ui/response-renderers/__tests__/image.test.tsx +73 -0
- package/src/ui/response-renderers/__tests__/json.test.tsx +95 -0
- package/src/ui/response-renderers/__tests__/svg.test.tsx +46 -0
- package/src/ui/response-renderers/__tests__/xml.test.tsx +100 -0
- package/src/ui/response-renderers/binary-too-large.tsx +36 -0
- package/src/ui/response-renderers/binary.tsx +31 -0
- package/src/ui/response-renderers/empty.tsx +14 -0
- package/src/ui/response-renderers/html.tsx +36 -0
- package/src/ui/response-renderers/image.tsx +37 -0
- package/src/ui/response-renderers/index.ts +55 -0
- package/src/ui/response-renderers/json.tsx +40 -0
- package/src/ui/response-renderers/svg.tsx +27 -0
- package/src/ui/response-renderers/text-fallback.tsx +14 -0
- package/src/ui/response-renderers/types.ts +38 -0
- package/src/ui/response-renderers/unknown.tsx +18 -0
- package/src/ui/response-renderers/xml.tsx +46 -0
- package/src/ui/state/derived.ts +12 -0
- package/src/ui/state/model.ts +6 -1
- package/src/ui/state/store.ts +39 -2
- package/src/ui/tabs/InitiatorTab.tsx +230 -0
- package/src/ui/tabs/ResponseTab.tsx +80 -96
- package/src/ui/tabs/__tests__/ResponseTab.test.tsx +102 -0
- package/src/ui/utils/__tests__/download.test.ts +115 -0
- package/src/ui/utils/__tests__/hex.test.ts +84 -0
- package/src/ui/utils/__tests__/symbolication.test.ts +207 -0
- package/src/ui/utils/download.ts +154 -0
- package/src/ui/utils/hex.ts +59 -0
- package/src/ui/utils/initiator.ts +136 -0
- package/src/ui/utils/symbolication.ts +248 -0
- package/src/ui/views/InspectorView.tsx +8 -5
- package/src/utils/__tests__/getContentTypeMimeType.test.ts +65 -0
- package/src/utils/getContentTypeMimeType.ts +28 -0
- package/vite.config.ts +9 -1
- package/vitest.setup.ts +31 -0
|
@@ -3,6 +3,7 @@ const nanoevents = require("nanoevents");
|
|
|
3
3
|
const eventSource = require("./event-source.cjs");
|
|
4
4
|
const reactNative = require("react-native");
|
|
5
5
|
const WebSocketInterceptor = require("react-native/Libraries/WebSocket/WebSocketInterceptor");
|
|
6
|
+
const getNitroModule = require("./get-nitro-module.cjs");
|
|
6
7
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
7
8
|
const WebSocketInterceptor__default = /* @__PURE__ */ _interopDefault(WebSocketInterceptor);
|
|
8
9
|
const REQUEST_TTL = 1e3 * 60 * 5;
|
|
@@ -184,10 +185,6 @@ function safeStringify(data) {
|
|
|
184
185
|
const getStringSizeInBytes = (value) => {
|
|
185
186
|
return new TextEncoder().encode(value).length;
|
|
186
187
|
};
|
|
187
|
-
const isBlob = (value) => value instanceof Blob;
|
|
188
|
-
const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
189
|
-
const isFormData = (value) => value instanceof FormData;
|
|
190
|
-
const isNullOrUndefined = (value) => value === null || value === void 0;
|
|
191
188
|
function getHttpHeader(headers, name) {
|
|
192
189
|
const lowerName = name.toLowerCase();
|
|
193
190
|
for (const key in headers) {
|
|
@@ -197,6 +194,9 @@ function getHttpHeader(headers, name) {
|
|
|
197
194
|
}
|
|
198
195
|
return void 0;
|
|
199
196
|
}
|
|
197
|
+
function normalizeContentType(contentType) {
|
|
198
|
+
return contentType.split(";")[0].trim().toLowerCase();
|
|
199
|
+
}
|
|
200
200
|
function getContentTypeMime(headers) {
|
|
201
201
|
const contentType = getHttpHeader(headers, "content-type");
|
|
202
202
|
if (!contentType) {
|
|
@@ -206,6 +206,24 @@ function getContentTypeMime(headers) {
|
|
|
206
206
|
const actualValue = Array.isArray(value) ? value[0] : value;
|
|
207
207
|
return actualValue.split(";")[0].trim();
|
|
208
208
|
}
|
|
209
|
+
function isJsonContentType(contentType) {
|
|
210
|
+
if (!contentType) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
const mimeType = normalizeContentType(contentType);
|
|
214
|
+
return mimeType === "application/json" || mimeType.endsWith("+json");
|
|
215
|
+
}
|
|
216
|
+
function isXmlContentType(contentType) {
|
|
217
|
+
if (!contentType) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
const mimeType = normalizeContentType(contentType);
|
|
221
|
+
return mimeType === "application/xml" || mimeType === "text/xml" || mimeType.endsWith("+xml");
|
|
222
|
+
}
|
|
223
|
+
const isBlob = (value) => value instanceof Blob;
|
|
224
|
+
const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
225
|
+
const isFormData = (value) => value instanceof FormData;
|
|
226
|
+
const isNullOrUndefined = (value) => value === null || value === void 0;
|
|
209
227
|
const getContentType$1 = (request) => {
|
|
210
228
|
const responseHeaders = request.responseHeaders;
|
|
211
229
|
const responseType = request.responseType;
|
|
@@ -316,6 +334,30 @@ const getResponseSize = (request) => {
|
|
|
316
334
|
return null;
|
|
317
335
|
}
|
|
318
336
|
};
|
|
337
|
+
const BINARY_CAPTURE_SIZE_CAP = 5 * 1024 * 1024;
|
|
338
|
+
const readBlobAsText = (blob) => new Promise((resolve) => {
|
|
339
|
+
const reader = new FileReader();
|
|
340
|
+
reader.onload = () => resolve(reader.result);
|
|
341
|
+
reader.readAsText(blob);
|
|
342
|
+
});
|
|
343
|
+
const readBlobAsBase64 = (blob) => new Promise((resolve) => {
|
|
344
|
+
const reader = new FileReader();
|
|
345
|
+
reader.onload = () => {
|
|
346
|
+
const dataUrl = reader.result;
|
|
347
|
+
resolve(dataUrl.substring(dataUrl.indexOf(",") + 1));
|
|
348
|
+
};
|
|
349
|
+
reader.readAsDataURL(blob);
|
|
350
|
+
});
|
|
351
|
+
const ARRAY_BUFFER_BASE64_CHUNK = 32768;
|
|
352
|
+
const arrayBufferToBase64 = (buffer) => {
|
|
353
|
+
const bytes = new Uint8Array(buffer);
|
|
354
|
+
let binary = "";
|
|
355
|
+
for (let i = 0; i < bytes.length; i += ARRAY_BUFFER_BASE64_CHUNK) {
|
|
356
|
+
const chunk = bytes.subarray(i, i + ARRAY_BUFFER_BASE64_CHUNK);
|
|
357
|
+
binary += String.fromCharCode.apply(null, chunk);
|
|
358
|
+
}
|
|
359
|
+
return btoa(binary);
|
|
360
|
+
};
|
|
319
361
|
const getResponseBody = async (request) => {
|
|
320
362
|
const responseType = request.responseType;
|
|
321
363
|
if (responseType === "" || responseType === "text") {
|
|
@@ -323,35 +365,128 @@ const getResponseBody = async (request) => {
|
|
|
323
365
|
}
|
|
324
366
|
if (responseType === "blob") {
|
|
325
367
|
const contentType = request.getResponseHeader("Content-Type") || "";
|
|
326
|
-
if (contentType.startsWith("text/") || contentType
|
|
327
|
-
return
|
|
328
|
-
const reader = new FileReader();
|
|
329
|
-
reader.onload = () => {
|
|
330
|
-
resolve(reader.result);
|
|
331
|
-
};
|
|
332
|
-
reader.readAsText(request.response);
|
|
333
|
-
});
|
|
368
|
+
if (contentType.startsWith("text/") || isJsonContentType(contentType) || isXmlContentType(contentType)) {
|
|
369
|
+
return readBlobAsText(request.response);
|
|
334
370
|
}
|
|
371
|
+
const blob = request.response;
|
|
372
|
+
if (blob.size > BINARY_CAPTURE_SIZE_CAP) {
|
|
373
|
+
return { kind: "binary-too-large", size: blob.size };
|
|
374
|
+
}
|
|
375
|
+
return { kind: "binary", base64: await readBlobAsBase64(blob) };
|
|
376
|
+
}
|
|
377
|
+
if (responseType === "arraybuffer") {
|
|
378
|
+
const buffer = request.response;
|
|
379
|
+
if (!buffer || buffer.byteLength === 0) {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
if (buffer.byteLength > BINARY_CAPTURE_SIZE_CAP) {
|
|
383
|
+
return { kind: "binary-too-large", size: buffer.byteLength };
|
|
384
|
+
}
|
|
385
|
+
return { kind: "binary", base64: arrayBufferToBase64(buffer) };
|
|
335
386
|
}
|
|
336
387
|
if (responseType === "json") {
|
|
337
388
|
return safeStringify(request.response);
|
|
338
389
|
}
|
|
339
390
|
return null;
|
|
340
391
|
};
|
|
392
|
+
const STACK_PREVIEW_FRAME_LIMIT = 8;
|
|
393
|
+
const INITIATOR_STACK_FRAME_OFFSET = 3;
|
|
394
|
+
const parseStackLocation = (location) => {
|
|
395
|
+
const match = location.match(/^(.*):(\d+):(\d+)$/);
|
|
396
|
+
if (!match) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
url: match[1],
|
|
401
|
+
lineNumber: Number.parseInt(match[2], 10),
|
|
402
|
+
columnNumber: Number.parseInt(match[3], 10)
|
|
403
|
+
};
|
|
404
|
+
};
|
|
405
|
+
const normalizeFunctionName = (functionName) => {
|
|
406
|
+
const trimmedFunctionName = functionName?.trim();
|
|
407
|
+
return trimmedFunctionName && trimmedFunctionName !== "<anonymous>" && trimmedFunctionName !== "anonymous" && trimmedFunctionName !== "<unknown>" ? trimmedFunctionName : void 0;
|
|
408
|
+
};
|
|
409
|
+
const parseStackFrame = (line) => {
|
|
410
|
+
const trimmedLine = line.trim();
|
|
411
|
+
if (!trimmedLine) {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
let functionName;
|
|
415
|
+
let location;
|
|
416
|
+
const v8FunctionFrame = trimmedLine.match(/^at\s+(.*?)\s+\((.*)\)$/);
|
|
417
|
+
if (v8FunctionFrame) {
|
|
418
|
+
functionName = v8FunctionFrame[1];
|
|
419
|
+
location = v8FunctionFrame[2];
|
|
420
|
+
} else {
|
|
421
|
+
const v8LocationFrame = trimmedLine.match(/^at\s+(.*)$/);
|
|
422
|
+
const jscFrame = trimmedLine.match(/^(.*?)@(.*)$/);
|
|
423
|
+
if (v8LocationFrame) {
|
|
424
|
+
location = v8LocationFrame[1];
|
|
425
|
+
} else if (jscFrame) {
|
|
426
|
+
functionName = jscFrame[1];
|
|
427
|
+
location = jscFrame[2];
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (!location) {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
const parsedLocation = parseStackLocation(location);
|
|
434
|
+
if (!parsedLocation) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
return {
|
|
438
|
+
functionName: normalizeFunctionName(functionName),
|
|
439
|
+
...parsedLocation
|
|
440
|
+
};
|
|
441
|
+
};
|
|
442
|
+
const toGeneratedStackFrame = (frame) => ({
|
|
443
|
+
functionName: frame.functionName,
|
|
444
|
+
generatedUrl: frame.url,
|
|
445
|
+
generatedLineNumber: frame.lineNumber,
|
|
446
|
+
generatedColumnNumber: frame.columnNumber
|
|
447
|
+
});
|
|
448
|
+
const getGeneratedFrameLocation = (frame) => ({
|
|
449
|
+
url: frame.generatedUrl ?? frame.url,
|
|
450
|
+
lineNumber: frame.generatedLineNumber ?? frame.lineNumber,
|
|
451
|
+
columnNumber: frame.generatedColumnNumber ?? frame.columnNumber
|
|
452
|
+
});
|
|
453
|
+
const canSymbolicateStack = (stack) => stack?.some(
|
|
454
|
+
(frame) => getGeneratedFrameLocation(frame).url?.startsWith("http")
|
|
455
|
+
) ?? false;
|
|
456
|
+
const getStackPreview = (frames) => {
|
|
457
|
+
const callerFrames = frames.slice(INITIATOR_STACK_FRAME_OFFSET);
|
|
458
|
+
return (callerFrames.length > 0 ? callerFrames : frames).slice(
|
|
459
|
+
0,
|
|
460
|
+
STACK_PREVIEW_FRAME_LIMIT
|
|
461
|
+
);
|
|
462
|
+
};
|
|
341
463
|
const getInitiatorFromStack = () => {
|
|
342
464
|
try {
|
|
343
465
|
const stack = new Error().stack;
|
|
344
466
|
if (!stack) {
|
|
345
467
|
return { type: "other" };
|
|
346
468
|
}
|
|
347
|
-
const
|
|
348
|
-
const
|
|
349
|
-
|
|
469
|
+
const parsedFrames = stack.split("\n").map(parseStackFrame).filter((frame) => frame !== null);
|
|
470
|
+
const stackPreview = getStackPreview(parsedFrames);
|
|
471
|
+
const initiatorFrame = stackPreview[0];
|
|
472
|
+
const generatedStackPreview = stackPreview.map(toGeneratedStackFrame);
|
|
473
|
+
if (initiatorFrame?.url) {
|
|
350
474
|
return {
|
|
351
475
|
type: "script",
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
476
|
+
functionName: initiatorFrame.functionName,
|
|
477
|
+
generatedUrl: initiatorFrame.url,
|
|
478
|
+
generatedLineNumber: initiatorFrame.lineNumber,
|
|
479
|
+
generatedColumnNumber: initiatorFrame.columnNumber,
|
|
480
|
+
stack: generatedStackPreview,
|
|
481
|
+
symbolicationStatus: canSymbolicateStack(generatedStackPreview) ? "pending" : "unavailable"
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
if (parsedFrames.length > 0) {
|
|
485
|
+
const fallbackStack = stackPreview.map(toGeneratedStackFrame);
|
|
486
|
+
return {
|
|
487
|
+
type: "other",
|
|
488
|
+
stack: fallbackStack,
|
|
489
|
+
symbolicationStatus: canSymbolicateStack(fallbackStack) ? "pending" : "unavailable"
|
|
355
490
|
};
|
|
356
491
|
}
|
|
357
492
|
} catch {
|
|
@@ -367,7 +502,7 @@ const setupRequestOverride = (overridesRegistry, request) => {
|
|
|
367
502
|
Object.defineProperty(request, "response", { writable: true });
|
|
368
503
|
Object.defineProperty(request, "responseText", { writable: true });
|
|
369
504
|
const contentType = getContentType$1(request);
|
|
370
|
-
if (contentType
|
|
505
|
+
if (isJsonContentType(contentType)) {
|
|
371
506
|
request.responseType = "json";
|
|
372
507
|
} else if (contentType === "text/plain") {
|
|
373
508
|
request.responseType = "text";
|
|
@@ -1026,13 +1161,6 @@ const NITRO_NETWORK_EVENTS = [
|
|
|
1026
1161
|
"websocket-message-received",
|
|
1027
1162
|
"websocket-error"
|
|
1028
1163
|
];
|
|
1029
|
-
const loadNitroModule = () => {
|
|
1030
|
-
try {
|
|
1031
|
-
return require("react-native-nitro-fetch");
|
|
1032
|
-
} catch {
|
|
1033
|
-
return null;
|
|
1034
|
-
}
|
|
1035
|
-
};
|
|
1036
1164
|
const timestampOrigin = typeof performance !== "undefined" && typeof performance.timeOrigin === "number" ? performance.timeOrigin : Date.now() - performance.now();
|
|
1037
1165
|
const toEpochTime = (timestamp) => Math.round(timestampOrigin + timestamp);
|
|
1038
1166
|
const toHeaders = (headers) => {
|
|
@@ -1062,7 +1190,7 @@ const getContentType = (headers) => {
|
|
|
1062
1190
|
return headers.find((header) => header.key.toLowerCase() === "content-type")?.value ?? "text/plain";
|
|
1063
1191
|
};
|
|
1064
1192
|
const normalizeReadyState = (readyState) => readyState.toUpperCase();
|
|
1065
|
-
const createNitroNetworkInspector = (getNitroModule =
|
|
1193
|
+
const createNitroNetworkInspector = (getNitroModule$1 = getNitroModule.getNitroModule) => {
|
|
1066
1194
|
const eventEmitter = nanoevents.createNanoEvents();
|
|
1067
1195
|
const previousEntries = /* @__PURE__ */ new Map();
|
|
1068
1196
|
const responseBodies = /* @__PURE__ */ new Map();
|
|
@@ -1209,7 +1337,7 @@ const createNitroNetworkInspector = (getNitroModule = loadNitroModule) => {
|
|
|
1209
1337
|
if (unsubscribe) {
|
|
1210
1338
|
return;
|
|
1211
1339
|
}
|
|
1212
|
-
nitroModule = getNitroModule();
|
|
1340
|
+
nitroModule = getNitroModule$1();
|
|
1213
1341
|
if (!nitroModule) {
|
|
1214
1342
|
return;
|
|
1215
1343
|
}
|
|
@@ -2,6 +2,7 @@ import { createNanoEvents } from "nanoevents";
|
|
|
2
2
|
import { g as getEventSource } from "./event-source.js";
|
|
3
3
|
import { Platform } from "react-native";
|
|
4
4
|
import WebSocketInterceptor from "react-native/Libraries/WebSocket/WebSocketInterceptor";
|
|
5
|
+
import { g as getNitroModule } from "./get-nitro-module.js";
|
|
5
6
|
const REQUEST_TTL = 1e3 * 60 * 5;
|
|
6
7
|
const getNetworkRequestsRegistry = () => {
|
|
7
8
|
const registry = /* @__PURE__ */ new Map();
|
|
@@ -181,10 +182,6 @@ function safeStringify(data) {
|
|
|
181
182
|
const getStringSizeInBytes = (value) => {
|
|
182
183
|
return new TextEncoder().encode(value).length;
|
|
183
184
|
};
|
|
184
|
-
const isBlob = (value) => value instanceof Blob;
|
|
185
|
-
const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
186
|
-
const isFormData = (value) => value instanceof FormData;
|
|
187
|
-
const isNullOrUndefined = (value) => value === null || value === void 0;
|
|
188
185
|
function getHttpHeader(headers, name) {
|
|
189
186
|
const lowerName = name.toLowerCase();
|
|
190
187
|
for (const key in headers) {
|
|
@@ -194,6 +191,9 @@ function getHttpHeader(headers, name) {
|
|
|
194
191
|
}
|
|
195
192
|
return void 0;
|
|
196
193
|
}
|
|
194
|
+
function normalizeContentType(contentType) {
|
|
195
|
+
return contentType.split(";")[0].trim().toLowerCase();
|
|
196
|
+
}
|
|
197
197
|
function getContentTypeMime(headers) {
|
|
198
198
|
const contentType = getHttpHeader(headers, "content-type");
|
|
199
199
|
if (!contentType) {
|
|
@@ -203,6 +203,24 @@ function getContentTypeMime(headers) {
|
|
|
203
203
|
const actualValue = Array.isArray(value) ? value[0] : value;
|
|
204
204
|
return actualValue.split(";")[0].trim();
|
|
205
205
|
}
|
|
206
|
+
function isJsonContentType(contentType) {
|
|
207
|
+
if (!contentType) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
const mimeType = normalizeContentType(contentType);
|
|
211
|
+
return mimeType === "application/json" || mimeType.endsWith("+json");
|
|
212
|
+
}
|
|
213
|
+
function isXmlContentType(contentType) {
|
|
214
|
+
if (!contentType) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
const mimeType = normalizeContentType(contentType);
|
|
218
|
+
return mimeType === "application/xml" || mimeType === "text/xml" || mimeType.endsWith("+xml");
|
|
219
|
+
}
|
|
220
|
+
const isBlob = (value) => value instanceof Blob;
|
|
221
|
+
const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
222
|
+
const isFormData = (value) => value instanceof FormData;
|
|
223
|
+
const isNullOrUndefined = (value) => value === null || value === void 0;
|
|
206
224
|
const getContentType$1 = (request) => {
|
|
207
225
|
const responseHeaders = request.responseHeaders;
|
|
208
226
|
const responseType = request.responseType;
|
|
@@ -313,6 +331,30 @@ const getResponseSize = (request) => {
|
|
|
313
331
|
return null;
|
|
314
332
|
}
|
|
315
333
|
};
|
|
334
|
+
const BINARY_CAPTURE_SIZE_CAP = 5 * 1024 * 1024;
|
|
335
|
+
const readBlobAsText = (blob) => new Promise((resolve) => {
|
|
336
|
+
const reader = new FileReader();
|
|
337
|
+
reader.onload = () => resolve(reader.result);
|
|
338
|
+
reader.readAsText(blob);
|
|
339
|
+
});
|
|
340
|
+
const readBlobAsBase64 = (blob) => new Promise((resolve) => {
|
|
341
|
+
const reader = new FileReader();
|
|
342
|
+
reader.onload = () => {
|
|
343
|
+
const dataUrl = reader.result;
|
|
344
|
+
resolve(dataUrl.substring(dataUrl.indexOf(",") + 1));
|
|
345
|
+
};
|
|
346
|
+
reader.readAsDataURL(blob);
|
|
347
|
+
});
|
|
348
|
+
const ARRAY_BUFFER_BASE64_CHUNK = 32768;
|
|
349
|
+
const arrayBufferToBase64 = (buffer) => {
|
|
350
|
+
const bytes = new Uint8Array(buffer);
|
|
351
|
+
let binary = "";
|
|
352
|
+
for (let i = 0; i < bytes.length; i += ARRAY_BUFFER_BASE64_CHUNK) {
|
|
353
|
+
const chunk = bytes.subarray(i, i + ARRAY_BUFFER_BASE64_CHUNK);
|
|
354
|
+
binary += String.fromCharCode.apply(null, chunk);
|
|
355
|
+
}
|
|
356
|
+
return btoa(binary);
|
|
357
|
+
};
|
|
316
358
|
const getResponseBody = async (request) => {
|
|
317
359
|
const responseType = request.responseType;
|
|
318
360
|
if (responseType === "" || responseType === "text") {
|
|
@@ -320,35 +362,128 @@ const getResponseBody = async (request) => {
|
|
|
320
362
|
}
|
|
321
363
|
if (responseType === "blob") {
|
|
322
364
|
const contentType = request.getResponseHeader("Content-Type") || "";
|
|
323
|
-
if (contentType.startsWith("text/") || contentType
|
|
324
|
-
return
|
|
325
|
-
const reader = new FileReader();
|
|
326
|
-
reader.onload = () => {
|
|
327
|
-
resolve(reader.result);
|
|
328
|
-
};
|
|
329
|
-
reader.readAsText(request.response);
|
|
330
|
-
});
|
|
365
|
+
if (contentType.startsWith("text/") || isJsonContentType(contentType) || isXmlContentType(contentType)) {
|
|
366
|
+
return readBlobAsText(request.response);
|
|
331
367
|
}
|
|
368
|
+
const blob = request.response;
|
|
369
|
+
if (blob.size > BINARY_CAPTURE_SIZE_CAP) {
|
|
370
|
+
return { kind: "binary-too-large", size: blob.size };
|
|
371
|
+
}
|
|
372
|
+
return { kind: "binary", base64: await readBlobAsBase64(blob) };
|
|
373
|
+
}
|
|
374
|
+
if (responseType === "arraybuffer") {
|
|
375
|
+
const buffer = request.response;
|
|
376
|
+
if (!buffer || buffer.byteLength === 0) {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
if (buffer.byteLength > BINARY_CAPTURE_SIZE_CAP) {
|
|
380
|
+
return { kind: "binary-too-large", size: buffer.byteLength };
|
|
381
|
+
}
|
|
382
|
+
return { kind: "binary", base64: arrayBufferToBase64(buffer) };
|
|
332
383
|
}
|
|
333
384
|
if (responseType === "json") {
|
|
334
385
|
return safeStringify(request.response);
|
|
335
386
|
}
|
|
336
387
|
return null;
|
|
337
388
|
};
|
|
389
|
+
const STACK_PREVIEW_FRAME_LIMIT = 8;
|
|
390
|
+
const INITIATOR_STACK_FRAME_OFFSET = 3;
|
|
391
|
+
const parseStackLocation = (location) => {
|
|
392
|
+
const match = location.match(/^(.*):(\d+):(\d+)$/);
|
|
393
|
+
if (!match) {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
url: match[1],
|
|
398
|
+
lineNumber: Number.parseInt(match[2], 10),
|
|
399
|
+
columnNumber: Number.parseInt(match[3], 10)
|
|
400
|
+
};
|
|
401
|
+
};
|
|
402
|
+
const normalizeFunctionName = (functionName) => {
|
|
403
|
+
const trimmedFunctionName = functionName?.trim();
|
|
404
|
+
return trimmedFunctionName && trimmedFunctionName !== "<anonymous>" && trimmedFunctionName !== "anonymous" && trimmedFunctionName !== "<unknown>" ? trimmedFunctionName : void 0;
|
|
405
|
+
};
|
|
406
|
+
const parseStackFrame = (line) => {
|
|
407
|
+
const trimmedLine = line.trim();
|
|
408
|
+
if (!trimmedLine) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
let functionName;
|
|
412
|
+
let location;
|
|
413
|
+
const v8FunctionFrame = trimmedLine.match(/^at\s+(.*?)\s+\((.*)\)$/);
|
|
414
|
+
if (v8FunctionFrame) {
|
|
415
|
+
functionName = v8FunctionFrame[1];
|
|
416
|
+
location = v8FunctionFrame[2];
|
|
417
|
+
} else {
|
|
418
|
+
const v8LocationFrame = trimmedLine.match(/^at\s+(.*)$/);
|
|
419
|
+
const jscFrame = trimmedLine.match(/^(.*?)@(.*)$/);
|
|
420
|
+
if (v8LocationFrame) {
|
|
421
|
+
location = v8LocationFrame[1];
|
|
422
|
+
} else if (jscFrame) {
|
|
423
|
+
functionName = jscFrame[1];
|
|
424
|
+
location = jscFrame[2];
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (!location) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
const parsedLocation = parseStackLocation(location);
|
|
431
|
+
if (!parsedLocation) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
functionName: normalizeFunctionName(functionName),
|
|
436
|
+
...parsedLocation
|
|
437
|
+
};
|
|
438
|
+
};
|
|
439
|
+
const toGeneratedStackFrame = (frame) => ({
|
|
440
|
+
functionName: frame.functionName,
|
|
441
|
+
generatedUrl: frame.url,
|
|
442
|
+
generatedLineNumber: frame.lineNumber,
|
|
443
|
+
generatedColumnNumber: frame.columnNumber
|
|
444
|
+
});
|
|
445
|
+
const getGeneratedFrameLocation = (frame) => ({
|
|
446
|
+
url: frame.generatedUrl ?? frame.url,
|
|
447
|
+
lineNumber: frame.generatedLineNumber ?? frame.lineNumber,
|
|
448
|
+
columnNumber: frame.generatedColumnNumber ?? frame.columnNumber
|
|
449
|
+
});
|
|
450
|
+
const canSymbolicateStack = (stack) => stack?.some(
|
|
451
|
+
(frame) => getGeneratedFrameLocation(frame).url?.startsWith("http")
|
|
452
|
+
) ?? false;
|
|
453
|
+
const getStackPreview = (frames) => {
|
|
454
|
+
const callerFrames = frames.slice(INITIATOR_STACK_FRAME_OFFSET);
|
|
455
|
+
return (callerFrames.length > 0 ? callerFrames : frames).slice(
|
|
456
|
+
0,
|
|
457
|
+
STACK_PREVIEW_FRAME_LIMIT
|
|
458
|
+
);
|
|
459
|
+
};
|
|
338
460
|
const getInitiatorFromStack = () => {
|
|
339
461
|
try {
|
|
340
462
|
const stack = new Error().stack;
|
|
341
463
|
if (!stack) {
|
|
342
464
|
return { type: "other" };
|
|
343
465
|
}
|
|
344
|
-
const
|
|
345
|
-
const
|
|
346
|
-
|
|
466
|
+
const parsedFrames = stack.split("\n").map(parseStackFrame).filter((frame) => frame !== null);
|
|
467
|
+
const stackPreview = getStackPreview(parsedFrames);
|
|
468
|
+
const initiatorFrame = stackPreview[0];
|
|
469
|
+
const generatedStackPreview = stackPreview.map(toGeneratedStackFrame);
|
|
470
|
+
if (initiatorFrame?.url) {
|
|
347
471
|
return {
|
|
348
472
|
type: "script",
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
473
|
+
functionName: initiatorFrame.functionName,
|
|
474
|
+
generatedUrl: initiatorFrame.url,
|
|
475
|
+
generatedLineNumber: initiatorFrame.lineNumber,
|
|
476
|
+
generatedColumnNumber: initiatorFrame.columnNumber,
|
|
477
|
+
stack: generatedStackPreview,
|
|
478
|
+
symbolicationStatus: canSymbolicateStack(generatedStackPreview) ? "pending" : "unavailable"
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
if (parsedFrames.length > 0) {
|
|
482
|
+
const fallbackStack = stackPreview.map(toGeneratedStackFrame);
|
|
483
|
+
return {
|
|
484
|
+
type: "other",
|
|
485
|
+
stack: fallbackStack,
|
|
486
|
+
symbolicationStatus: canSymbolicateStack(fallbackStack) ? "pending" : "unavailable"
|
|
352
487
|
};
|
|
353
488
|
}
|
|
354
489
|
} catch {
|
|
@@ -364,7 +499,7 @@ const setupRequestOverride = (overridesRegistry, request) => {
|
|
|
364
499
|
Object.defineProperty(request, "response", { writable: true });
|
|
365
500
|
Object.defineProperty(request, "responseText", { writable: true });
|
|
366
501
|
const contentType = getContentType$1(request);
|
|
367
|
-
if (contentType
|
|
502
|
+
if (isJsonContentType(contentType)) {
|
|
368
503
|
request.responseType = "json";
|
|
369
504
|
} else if (contentType === "text/plain") {
|
|
370
505
|
request.responseType = "text";
|
|
@@ -1023,13 +1158,6 @@ const NITRO_NETWORK_EVENTS = [
|
|
|
1023
1158
|
"websocket-message-received",
|
|
1024
1159
|
"websocket-error"
|
|
1025
1160
|
];
|
|
1026
|
-
const loadNitroModule = () => {
|
|
1027
|
-
try {
|
|
1028
|
-
return require("react-native-nitro-fetch");
|
|
1029
|
-
} catch {
|
|
1030
|
-
return null;
|
|
1031
|
-
}
|
|
1032
|
-
};
|
|
1033
1161
|
const timestampOrigin = typeof performance !== "undefined" && typeof performance.timeOrigin === "number" ? performance.timeOrigin : Date.now() - performance.now();
|
|
1034
1162
|
const toEpochTime = (timestamp) => Math.round(timestampOrigin + timestamp);
|
|
1035
1163
|
const toHeaders = (headers) => {
|
|
@@ -1059,7 +1187,7 @@ const getContentType = (headers) => {
|
|
|
1059
1187
|
return headers.find((header) => header.key.toLowerCase() === "content-type")?.value ?? "text/plain";
|
|
1060
1188
|
};
|
|
1061
1189
|
const normalizeReadyState = (readyState) => readyState.toUpperCase();
|
|
1062
|
-
const createNitroNetworkInspector = (getNitroModule =
|
|
1190
|
+
const createNitroNetworkInspector = (getNitroModule$1 = getNitroModule) => {
|
|
1063
1191
|
const eventEmitter = createNanoEvents();
|
|
1064
1192
|
const previousEntries = /* @__PURE__ */ new Map();
|
|
1065
1193
|
const responseBodies = /* @__PURE__ */ new Map();
|
|
@@ -1206,7 +1334,7 @@ const createNitroNetworkInspector = (getNitroModule = loadNitroModule) => {
|
|
|
1206
1334
|
if (unsubscribe) {
|
|
1207
1335
|
return;
|
|
1208
1336
|
}
|
|
1209
|
-
nitroModule = getNitroModule();
|
|
1337
|
+
nitroModule = getNitroModule$1();
|
|
1210
1338
|
if (!nitroModule) {
|
|
1211
1339
|
return;
|
|
1212
1340
|
}
|
|
@@ -924,7 +924,9 @@ const useNetworkActivityAgentTools = ({
|
|
|
924
924
|
agentBridge.useRozenitePluginAgentTool({
|
|
925
925
|
pluginId: NETWORK_ACTIVITY_AGENT_PLUGIN_ID,
|
|
926
926
|
tool: getResponseBodyTool,
|
|
927
|
-
handler: async ({
|
|
927
|
+
handler: async ({
|
|
928
|
+
requestId
|
|
929
|
+
}) => {
|
|
928
930
|
const record = state.getHttpRecord(requestId);
|
|
929
931
|
if (!record) {
|
|
930
932
|
throw new Error(`Unknown request "${requestId}"`);
|
|
@@ -951,6 +953,23 @@ const useNetworkActivityAgentTools = ({
|
|
|
951
953
|
reason: "The plugin could not extract a text response body for this request."
|
|
952
954
|
};
|
|
953
955
|
}
|
|
956
|
+
if (typeof body !== "string") {
|
|
957
|
+
if (body.kind === "binary-too-large") {
|
|
958
|
+
return {
|
|
959
|
+
requestId,
|
|
960
|
+
available: false,
|
|
961
|
+
reason: `Response body exceeded the in-capture size cap (${body.size} bytes).`
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
return {
|
|
965
|
+
requestId,
|
|
966
|
+
available: true,
|
|
967
|
+
body: body.base64,
|
|
968
|
+
base64Encoded: true,
|
|
969
|
+
decoded: false,
|
|
970
|
+
mimeType: record.response?.contentType
|
|
971
|
+
};
|
|
972
|
+
}
|
|
954
973
|
return {
|
|
955
974
|
requestId,
|
|
956
975
|
available: true,
|
|
@@ -922,7 +922,9 @@ const useNetworkActivityAgentTools = ({
|
|
|
922
922
|
useRozenitePluginAgentTool({
|
|
923
923
|
pluginId: NETWORK_ACTIVITY_AGENT_PLUGIN_ID,
|
|
924
924
|
tool: getResponseBodyTool,
|
|
925
|
-
handler: async ({
|
|
925
|
+
handler: async ({
|
|
926
|
+
requestId
|
|
927
|
+
}) => {
|
|
926
928
|
const record = state.getHttpRecord(requestId);
|
|
927
929
|
if (!record) {
|
|
928
930
|
throw new Error(`Unknown request "${requestId}"`);
|
|
@@ -949,6 +951,23 @@ const useNetworkActivityAgentTools = ({
|
|
|
949
951
|
reason: "The plugin could not extract a text response body for this request."
|
|
950
952
|
};
|
|
951
953
|
}
|
|
954
|
+
if (typeof body !== "string") {
|
|
955
|
+
if (body.kind === "binary-too-large") {
|
|
956
|
+
return {
|
|
957
|
+
requestId,
|
|
958
|
+
available: false,
|
|
959
|
+
reason: `Response body exceeded the in-capture size cap (${body.size} bytes).`
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
return {
|
|
963
|
+
requestId,
|
|
964
|
+
available: true,
|
|
965
|
+
body: body.base64,
|
|
966
|
+
base64Encoded: true,
|
|
967
|
+
decoded: false,
|
|
968
|
+
mimeType: record.response?.contentType
|
|
969
|
+
};
|
|
970
|
+
}
|
|
952
971
|
return {
|
|
953
972
|
requestId,
|
|
954
973
|
available: true,
|