@rozenite/network-activity-plugin 1.9.0 → 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 +43 -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-hSoryVpJ.js → App-DsimzJvx.js} +6827 -970
- package/dist/react-native/chunks/boot-recording.cjs +138 -14
- package/dist/react-native/chunks/boot-recording.js +138 -14
- package/dist/react-native/chunks/get-nitro-module.cjs +4 -1
- package/dist/react-native/chunks/get-nitro-module.js +4 -1
- 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 +37 -1
- 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 +208 -25
- package/src/react-native/network-inspector.ts +2 -2
- package/src/react-native/nitro-fetch/get-nitro-module.ts +5 -1
- package/src/react-native/nitro-fetch/nitro-network-inspector.ts +8 -2
- 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 -97
- 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 +34 -0
- package/src/utils/getContentTypeMimeType.ts +14 -0
- package/vite.config.ts +5 -1
- package/vitest.setup.ts +31 -0
|
@@ -213,6 +213,13 @@ function isJsonContentType(contentType) {
|
|
|
213
213
|
const mimeType = normalizeContentType(contentType);
|
|
214
214
|
return mimeType === "application/json" || mimeType.endsWith("+json");
|
|
215
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
|
+
}
|
|
216
223
|
const isBlob = (value) => value instanceof Blob;
|
|
217
224
|
const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
218
225
|
const isFormData = (value) => value instanceof FormData;
|
|
@@ -327,6 +334,30 @@ const getResponseSize = (request) => {
|
|
|
327
334
|
return null;
|
|
328
335
|
}
|
|
329
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
|
+
};
|
|
330
361
|
const getResponseBody = async (request) => {
|
|
331
362
|
const responseType = request.responseType;
|
|
332
363
|
if (responseType === "" || responseType === "text") {
|
|
@@ -334,35 +365,128 @@ const getResponseBody = async (request) => {
|
|
|
334
365
|
}
|
|
335
366
|
if (responseType === "blob") {
|
|
336
367
|
const contentType = request.getResponseHeader("Content-Type") || "";
|
|
337
|
-
if (contentType.startsWith("text/") || isJsonContentType(contentType)) {
|
|
338
|
-
return
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
368
|
+
if (contentType.startsWith("text/") || isJsonContentType(contentType) || isXmlContentType(contentType)) {
|
|
369
|
+
return readBlobAsText(request.response);
|
|
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;
|
|
345
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) };
|
|
346
386
|
}
|
|
347
387
|
if (responseType === "json") {
|
|
348
388
|
return safeStringify(request.response);
|
|
349
389
|
}
|
|
350
390
|
return null;
|
|
351
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
|
+
};
|
|
352
463
|
const getInitiatorFromStack = () => {
|
|
353
464
|
try {
|
|
354
465
|
const stack = new Error().stack;
|
|
355
466
|
if (!stack) {
|
|
356
467
|
return { type: "other" };
|
|
357
468
|
}
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
|
|
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) {
|
|
361
474
|
return {
|
|
362
475
|
type: "script",
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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"
|
|
366
490
|
};
|
|
367
491
|
}
|
|
368
492
|
} catch {
|
|
@@ -210,6 +210,13 @@ function isJsonContentType(contentType) {
|
|
|
210
210
|
const mimeType = normalizeContentType(contentType);
|
|
211
211
|
return mimeType === "application/json" || mimeType.endsWith("+json");
|
|
212
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
|
+
}
|
|
213
220
|
const isBlob = (value) => value instanceof Blob;
|
|
214
221
|
const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
215
222
|
const isFormData = (value) => value instanceof FormData;
|
|
@@ -324,6 +331,30 @@ const getResponseSize = (request) => {
|
|
|
324
331
|
return null;
|
|
325
332
|
}
|
|
326
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
|
+
};
|
|
327
358
|
const getResponseBody = async (request) => {
|
|
328
359
|
const responseType = request.responseType;
|
|
329
360
|
if (responseType === "" || responseType === "text") {
|
|
@@ -331,35 +362,128 @@ const getResponseBody = async (request) => {
|
|
|
331
362
|
}
|
|
332
363
|
if (responseType === "blob") {
|
|
333
364
|
const contentType = request.getResponseHeader("Content-Type") || "";
|
|
334
|
-
if (contentType.startsWith("text/") || isJsonContentType(contentType)) {
|
|
335
|
-
return
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
365
|
+
if (contentType.startsWith("text/") || isJsonContentType(contentType) || isXmlContentType(contentType)) {
|
|
366
|
+
return readBlobAsText(request.response);
|
|
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;
|
|
342
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) };
|
|
343
383
|
}
|
|
344
384
|
if (responseType === "json") {
|
|
345
385
|
return safeStringify(request.response);
|
|
346
386
|
}
|
|
347
387
|
return null;
|
|
348
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
|
+
};
|
|
349
460
|
const getInitiatorFromStack = () => {
|
|
350
461
|
try {
|
|
351
462
|
const stack = new Error().stack;
|
|
352
463
|
if (!stack) {
|
|
353
464
|
return { type: "other" };
|
|
354
465
|
}
|
|
355
|
-
const
|
|
356
|
-
const
|
|
357
|
-
|
|
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) {
|
|
358
471
|
return {
|
|
359
472
|
type: "script",
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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"
|
|
363
487
|
};
|
|
364
488
|
}
|
|
365
489
|
} catch {
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const
|
|
2
|
+
const nitroModule = (() => {
|
|
3
3
|
try {
|
|
4
4
|
return require("react-native-nitro-fetch");
|
|
5
5
|
} catch {
|
|
6
6
|
return null;
|
|
7
7
|
}
|
|
8
|
+
})();
|
|
9
|
+
const getNitroModule = () => {
|
|
10
|
+
return nitroModule;
|
|
8
11
|
};
|
|
9
12
|
exports.getNitroModule = getNitroModule;
|
|
@@ -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,
|
|
@@ -63,7 +63,7 @@ declare type HttpEventMap = {
|
|
|
63
63
|
};
|
|
64
64
|
'response-body': {
|
|
65
65
|
requestId: RequestId;
|
|
66
|
-
body:
|
|
66
|
+
body: ResponseBody;
|
|
67
67
|
};
|
|
68
68
|
'set-overrides': {
|
|
69
69
|
overrides: [string, RequestOverride][];
|
|
@@ -76,9 +76,37 @@ declare type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD';
|
|
|
76
76
|
|
|
77
77
|
declare type Initiator = {
|
|
78
78
|
type: string;
|
|
79
|
+
symbolicationStatus?: 'pending' | 'complete' | 'failed' | 'unavailable';
|
|
80
|
+
symbolicationError?: string;
|
|
81
|
+
functionName?: string;
|
|
79
82
|
url?: string;
|
|
80
83
|
lineNumber?: number;
|
|
81
84
|
columnNumber?: number;
|
|
85
|
+
generatedUrl?: string;
|
|
86
|
+
generatedLineNumber?: number;
|
|
87
|
+
generatedColumnNumber?: number;
|
|
88
|
+
codeFrame?: InitiatorCodeFrame | null;
|
|
89
|
+
stack?: InitiatorStackFrame[];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
declare type InitiatorCodeFrame = {
|
|
93
|
+
content: string;
|
|
94
|
+
fileName: string;
|
|
95
|
+
location?: {
|
|
96
|
+
row: number;
|
|
97
|
+
column: number;
|
|
98
|
+
} | null;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
declare type InitiatorStackFrame = {
|
|
102
|
+
functionName?: string;
|
|
103
|
+
url?: string;
|
|
104
|
+
lineNumber?: number;
|
|
105
|
+
columnNumber?: number;
|
|
106
|
+
generatedUrl?: string;
|
|
107
|
+
generatedLineNumber?: number;
|
|
108
|
+
generatedColumnNumber?: number;
|
|
109
|
+
isCollapsed?: boolean;
|
|
82
110
|
};
|
|
83
111
|
|
|
84
112
|
declare type InspectorsConfig = {
|
|
@@ -171,6 +199,14 @@ declare type Response_2 = {
|
|
|
171
199
|
responseTime: Timestamp;
|
|
172
200
|
};
|
|
173
201
|
|
|
202
|
+
declare type ResponseBody = string | {
|
|
203
|
+
kind: 'binary';
|
|
204
|
+
base64: string;
|
|
205
|
+
} | {
|
|
206
|
+
kind: 'binary-too-large';
|
|
207
|
+
size: number;
|
|
208
|
+
} | null;
|
|
209
|
+
|
|
174
210
|
declare type SSECloseEvent = {
|
|
175
211
|
type: 'sse-close';
|
|
176
212
|
requestId: SSERequestId;
|
package/dist/rozenite.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@rozenite/network-activity-plugin","version":"1.
|
|
1
|
+
{"name":"@rozenite/network-activity-plugin","version":"1.10.0","description":"Network Activity for Rozenite.","panels":[{"name":"Network Activity","source":"/devtools/App.html"}]}
|
package/dist/sdk/index.d.ts
CHANGED
|
@@ -402,7 +402,7 @@ declare type HttpEventMap = {
|
|
|
402
402
|
};
|
|
403
403
|
'response-body': {
|
|
404
404
|
requestId: RequestId;
|
|
405
|
-
body:
|
|
405
|
+
body: ResponseBody;
|
|
406
406
|
};
|
|
407
407
|
'set-overrides': {
|
|
408
408
|
overrides: [string, RequestOverride][];
|
|
@@ -415,9 +415,37 @@ export declare type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | '
|
|
|
415
415
|
|
|
416
416
|
export declare type Initiator = {
|
|
417
417
|
type: string;
|
|
418
|
+
symbolicationStatus?: 'pending' | 'complete' | 'failed' | 'unavailable';
|
|
419
|
+
symbolicationError?: string;
|
|
420
|
+
functionName?: string;
|
|
418
421
|
url?: string;
|
|
419
422
|
lineNumber?: number;
|
|
420
423
|
columnNumber?: number;
|
|
424
|
+
generatedUrl?: string;
|
|
425
|
+
generatedLineNumber?: number;
|
|
426
|
+
generatedColumnNumber?: number;
|
|
427
|
+
codeFrame?: InitiatorCodeFrame | null;
|
|
428
|
+
stack?: InitiatorStackFrame[];
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
declare type InitiatorCodeFrame = {
|
|
432
|
+
content: string;
|
|
433
|
+
fileName: string;
|
|
434
|
+
location?: {
|
|
435
|
+
row: number;
|
|
436
|
+
column: number;
|
|
437
|
+
} | null;
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
declare type InitiatorStackFrame = {
|
|
441
|
+
functionName?: string;
|
|
442
|
+
url?: string;
|
|
443
|
+
lineNumber?: number;
|
|
444
|
+
columnNumber?: number;
|
|
445
|
+
generatedUrl?: string;
|
|
446
|
+
generatedLineNumber?: number;
|
|
447
|
+
generatedColumnNumber?: number;
|
|
448
|
+
isCollapsed?: boolean;
|
|
421
449
|
};
|
|
422
450
|
|
|
423
451
|
export declare const NETWORK_ACTIVITY_AGENT_PLUGIN_ID = "@rozenite/network-activity-plugin";
|
|
@@ -1070,6 +1098,14 @@ declare type Response_2 = {
|
|
|
1070
1098
|
};
|
|
1071
1099
|
export { Response_2 as Response }
|
|
1072
1100
|
|
|
1101
|
+
declare type ResponseBody = string | {
|
|
1102
|
+
kind: 'binary';
|
|
1103
|
+
base64: string;
|
|
1104
|
+
} | {
|
|
1105
|
+
kind: 'binary-too-large';
|
|
1106
|
+
size: number;
|
|
1107
|
+
} | null;
|
|
1108
|
+
|
|
1073
1109
|
declare type SSEAgentMessage = {
|
|
1074
1110
|
id: string;
|
|
1075
1111
|
type: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rozenite/network-activity-plugin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Network Activity for Rozenite.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/react-native/index.cjs",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"nanoevents": "^9.1.0",
|
|
19
|
-
"@rozenite/agent-shared": "1.
|
|
20
|
-
"@rozenite/agent-bridge": "1.
|
|
21
|
-
"@rozenite/plugin-bridge": "1.
|
|
19
|
+
"@rozenite/agent-shared": "1.10.0",
|
|
20
|
+
"@rozenite/agent-bridge": "1.10.0",
|
|
21
|
+
"@rozenite/plugin-bridge": "1.10.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@floating-ui/react": "^0.26.0",
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
"@radix-ui/react-tabs": "^1.1.12",
|
|
30
30
|
"@tanstack/react-table": "^8.21.3",
|
|
31
31
|
"@tanstack/react-virtual": "^3.0.0",
|
|
32
|
+
"@testing-library/dom": "^10.4.0",
|
|
33
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
34
|
+
"@testing-library/react": "^16.1.0",
|
|
32
35
|
"@types/react": "~19.2.2",
|
|
33
36
|
"@types/react-dom": "~19.1.7",
|
|
34
37
|
"autoprefixer": "^10.4.21",
|
|
@@ -43,14 +46,15 @@
|
|
|
43
46
|
"react-native": "0.83.1",
|
|
44
47
|
"react-native-sse": "^1.2.1",
|
|
45
48
|
"react-native-web": "^0.21.2",
|
|
49
|
+
"react-virtuoso": "^4.6.0",
|
|
46
50
|
"tailwind-merge": "^3.3.1",
|
|
47
51
|
"tailwindcss": "^3.4.17",
|
|
48
52
|
"tailwindcss-animate": "^1.0.7",
|
|
49
53
|
"typescript": "~5.9.3",
|
|
50
54
|
"vite": "^7.3.1",
|
|
51
55
|
"zustand": "^5.0.6",
|
|
52
|
-
"@rozenite/vite-plugin": "1.
|
|
53
|
-
"rozenite": "1.
|
|
56
|
+
"@rozenite/vite-plugin": "1.10.0",
|
|
57
|
+
"rozenite": "1.10.0"
|
|
54
58
|
},
|
|
55
59
|
"peerDependencies": {
|
|
56
60
|
"react-native-nitro-fetch": "*",
|
|
@@ -86,6 +90,7 @@
|
|
|
86
90
|
"build": "rozenite build",
|
|
87
91
|
"dev": "rozenite dev",
|
|
88
92
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
89
|
-
"lint": "eslint ."
|
|
93
|
+
"lint": "eslint .",
|
|
94
|
+
"test": "vitest --run --passWithNoTests"
|
|
90
95
|
}
|
|
91
96
|
}
|