@rozenite/network-activity-plugin 1.0.0-alpha.9 → 1.0.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/README.md +2 -0
- package/dist/App.html +2 -2
- package/dist/assets/{App-DoHQsY5s.css → App-BrSkOkws.css} +223 -2
- package/dist/assets/{App-CA1Fbh0I.js → App-C6wCDVkW.js} +8157 -2677
- package/dist/react-native.cjs +4 -1
- package/dist/react-native.js +4 -1
- package/dist/rozenite.json +1 -1
- package/dist/src/react-native/config.d.ts +20 -0
- package/dist/src/react-native/http/overrides-registry.d.ts +6 -0
- package/dist/src/react-native/http/xhr-interceptor.d.ts +7 -1
- package/dist/src/react-native/sse/sse-interceptor.d.ts +2 -2
- package/dist/src/react-native/useNetworkActivityDevTools.d.ts +2 -1
- package/dist/src/react-native/utils/getBlobName.d.ts +35 -0
- package/dist/src/react-native/utils/getFormDataEntries.d.ts +18 -0
- package/dist/src/shared/client.d.ts +48 -4
- package/dist/src/shared/sse-events.d.ts +4 -1
- package/dist/src/ui/components/Button.d.ts +2 -2
- package/dist/src/ui/components/CodeBlock.d.ts +3 -0
- package/dist/src/ui/components/CodeEditor.d.ts +5 -0
- package/dist/src/ui/components/CookieCard.d.ts +7 -0
- package/dist/src/ui/components/CopyRequestDropdown.d.ts +7 -0
- package/dist/src/ui/components/DropdownMenu.d.ts +27 -0
- package/dist/src/ui/components/FilterBar.d.ts +10 -0
- package/dist/src/ui/components/JsonTreeCopyableItem.d.ts +1 -1
- package/dist/src/ui/components/KeyValueGrid.d.ts +13 -0
- package/dist/src/ui/components/OverrideResponse.d.ts +8 -0
- package/dist/src/ui/components/RequestBody.d.ts +6 -0
- package/dist/src/ui/components/RequestList.d.ts +9 -4
- package/dist/src/ui/components/ScrollArea.d.ts +3 -2
- package/dist/src/ui/components/Section.d.ts +8 -0
- package/dist/src/ui/components/Separator.d.ts +2 -1
- package/dist/src/ui/components/Tabs.d.ts +7 -0
- package/dist/src/ui/state/hooks.d.ts +4 -0
- package/dist/src/ui/state/model.d.ts +12 -7
- package/dist/src/ui/state/store.d.ts +27 -3
- package/dist/src/ui/utils/checkRequestBodyBinary.d.ts +2 -0
- package/dist/src/ui/utils/escapeShellArg.d.ts +1 -0
- package/dist/src/ui/utils/generateCurlCommand.d.ts +2 -0
- package/dist/src/ui/utils/generateFetchCall.d.ts +2 -0
- package/dist/src/ui/utils/generateMultipartBody.d.ts +4 -0
- package/dist/src/utils/applyReactNativeRequestHeadersLogic.d.ts +7 -0
- package/dist/src/utils/applyReactNativeResponseHeadersLogic.d.ts +9 -0
- package/dist/src/utils/cookieParser.d.ts +6 -0
- package/dist/src/utils/getContentTypeMimeType.d.ts +2 -0
- package/dist/src/utils/getHttpHeader.d.ts +5 -0
- package/dist/src/utils/getHttpHeaderValueAsString.d.ts +11 -0
- package/dist/src/utils/getStringSizeInBytes.d.ts +1 -0
- package/dist/src/utils/inferContentTypeFromPostData.d.ts +2 -0
- package/dist/src/utils/safeStringify.d.ts +1 -0
- package/dist/src/utils/typeChecks.d.ts +9 -0
- package/dist/useNetworkActivityDevTools.cjs +319 -24
- package/dist/useNetworkActivityDevTools.js +320 -25
- package/package.json +7 -4
- package/react-native.ts +6 -1
- package/src/react-native/config.ts +43 -0
- package/src/react-native/http/network-inspector.ts +170 -8
- package/src/react-native/http/overrides-registry.ts +32 -0
- package/src/react-native/http/xhr-interceptor.ts +19 -2
- package/src/react-native/sse/sse-inspector.ts +27 -5
- package/src/react-native/sse/sse-interceptor.ts +26 -8
- package/src/react-native/useNetworkActivityDevTools.ts +86 -8
- package/src/react-native/utils/getBlobName.ts +45 -0
- package/src/react-native/utils/getFormDataEntries.ts +32 -0
- package/src/react-native/utils.ts +3 -3
- package/src/shared/client.ts +73 -4
- package/src/shared/sse-events.ts +4 -1
- package/src/ui/components/Button.tsx +1 -0
- package/src/ui/components/CodeBlock.tsx +19 -0
- package/src/ui/components/CodeEditor.tsx +26 -0
- package/src/ui/components/CookieCard.tsx +64 -0
- package/src/ui/components/CopyRequestDropdown.tsx +95 -0
- package/src/ui/components/DropdownMenu.tsx +206 -0
- package/src/ui/components/FilterBar.tsx +117 -0
- package/src/ui/components/Input.tsx +1 -1
- package/src/ui/components/JsonTree.tsx +10 -3
- package/src/ui/components/JsonTreeCopyableItem.tsx +14 -10
- package/src/ui/components/KeyValueGrid.tsx +51 -0
- package/src/ui/components/OverrideResponse.tsx +132 -0
- package/src/ui/components/RequestBody.tsx +86 -0
- package/src/ui/components/RequestList.tsx +65 -13
- package/src/ui/components/ScrollArea.tsx +1 -0
- package/src/ui/components/Section.tsx +46 -0
- package/src/ui/components/SidePanel.tsx +15 -5
- package/src/ui/globals.css +4 -0
- package/src/ui/hooks/useCopyToClipboard.ts +2 -2
- package/src/ui/state/hooks.ts +8 -0
- package/src/ui/state/model.ts +18 -7
- package/src/ui/state/store.ts +610 -500
- package/src/ui/tabs/CookiesTab.tsx +60 -263
- package/src/ui/tabs/HeadersTab.tsx +78 -89
- package/src/ui/tabs/RequestTab.tsx +58 -46
- package/src/ui/tabs/ResponseTab.tsx +98 -67
- package/src/ui/tabs/SSEMessagesTab.tsx +50 -39
- package/src/ui/utils/checkRequestBodyBinary.ts +7 -0
- package/src/ui/utils/escapeShellArg.ts +12 -0
- package/src/ui/utils/generateCurlCommand.ts +83 -0
- package/src/ui/utils/generateFetchCall.ts +64 -0
- package/src/ui/utils/generateMultipartBody.ts +19 -0
- package/src/ui/views/InspectorView.tsx +15 -3
- package/src/utils/applyReactNativeRequestHeadersLogic.ts +30 -0
- package/src/utils/applyReactNativeResponseHeadersLogic.ts +28 -0
- package/src/utils/cookieParser.ts +126 -0
- package/src/utils/getContentTypeMimeType.ts +17 -0
- package/src/utils/getHttpHeader.ts +17 -0
- package/src/utils/getHttpHeaderValueAsString.ts +13 -0
- package/src/utils/getStringSizeInBytes.ts +3 -0
- package/src/utils/inferContentTypeFromPostData.ts +9 -0
- package/src/utils/safeStringify.ts +7 -0
- package/src/utils/typeChecks.ts +27 -0
- package/dist/src/ui/utils/getHttpHeaderValue.d.ts +0 -2
- package/src/ui/utils/getHttpHeaderValue.ts +0 -14
|
@@ -1,24 +1,40 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
1
|
+
import { useRef, useEffect } from "react";
|
|
2
2
|
import { useRozeniteDevToolsClient } from "@rozenite/plugin-bridge";
|
|
3
3
|
import { createNanoEvents } from "nanoevents";
|
|
4
4
|
import { Platform } from "react-native";
|
|
5
5
|
import WebSocketInterceptor from "react-native/Libraries/WebSocket/WebSocketInterceptor";
|
|
6
6
|
import { g as getEventSource } from "./event-source.js";
|
|
7
|
-
function
|
|
7
|
+
function safeStringify(data) {
|
|
8
|
+
try {
|
|
9
|
+
return typeof data === "string" ? data : JSON.stringify(data);
|
|
10
|
+
} catch {
|
|
11
|
+
return String(data);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function getHttpHeader(headers, name) {
|
|
8
15
|
const lowerName = name.toLowerCase();
|
|
9
16
|
for (const key in headers) {
|
|
10
17
|
if (key.toLowerCase() === lowerName) {
|
|
11
|
-
return headers[key];
|
|
18
|
+
return { value: headers[key], originalKey: key };
|
|
12
19
|
}
|
|
13
20
|
}
|
|
14
21
|
return void 0;
|
|
15
22
|
}
|
|
23
|
+
function getContentTypeMime(headers) {
|
|
24
|
+
const contentType = getHttpHeader(headers, "content-type");
|
|
25
|
+
if (!contentType) {
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
28
|
+
const { value } = contentType;
|
|
29
|
+
const actualValue = Array.isArray(value) ? value[0] : value;
|
|
30
|
+
return actualValue.split(";")[0].trim();
|
|
31
|
+
}
|
|
16
32
|
const getContentType = (request) => {
|
|
17
33
|
const responseHeaders = request.responseHeaders;
|
|
18
34
|
const responseType = request.responseType;
|
|
19
|
-
const contentType =
|
|
35
|
+
const contentType = getContentTypeMime(responseHeaders || {});
|
|
20
36
|
if (contentType) {
|
|
21
|
-
return contentType
|
|
37
|
+
return contentType;
|
|
22
38
|
}
|
|
23
39
|
switch (responseType) {
|
|
24
40
|
case "arraybuffer":
|
|
@@ -66,6 +82,28 @@ const getNetworkRequestsRegistry = () => {
|
|
|
66
82
|
clear
|
|
67
83
|
};
|
|
68
84
|
};
|
|
85
|
+
function getBlobName(blob) {
|
|
86
|
+
if (typeof (blob == null ? void 0 : blob.name) === "string") {
|
|
87
|
+
return blob.name;
|
|
88
|
+
}
|
|
89
|
+
if ((blob == null ? void 0 : blob.data) && typeof blob.data.name === "string") {
|
|
90
|
+
return blob.data.name;
|
|
91
|
+
}
|
|
92
|
+
return void 0;
|
|
93
|
+
}
|
|
94
|
+
function getFormDataEntries(formData) {
|
|
95
|
+
if (!formData || typeof formData !== "object") {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
if (typeof formData.entries === "function") {
|
|
99
|
+
return formData.entries();
|
|
100
|
+
}
|
|
101
|
+
if (Array.isArray(formData._parts)) {
|
|
102
|
+
return formData._parts;
|
|
103
|
+
}
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
const XMLHttpRequest = global.XMLHttpRequest || window.XMLHttpRequest;
|
|
69
107
|
const originalXHROpen = XMLHttpRequest.prototype.open;
|
|
70
108
|
const originalXHRSend = XMLHttpRequest.prototype.send;
|
|
71
109
|
const originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
@@ -74,6 +112,7 @@ let sendCallback;
|
|
|
74
112
|
let requestHeaderCallback;
|
|
75
113
|
let headerReceivedCallback;
|
|
76
114
|
let responseCallback;
|
|
115
|
+
let overrideCallback;
|
|
77
116
|
let isInterceptorEnabled$1 = false;
|
|
78
117
|
const XHRInterceptor = {
|
|
79
118
|
/**
|
|
@@ -106,6 +145,12 @@ const XHRInterceptor = {
|
|
|
106
145
|
setRequestHeaderCallback(callback) {
|
|
107
146
|
requestHeaderCallback = callback;
|
|
108
147
|
},
|
|
148
|
+
/**
|
|
149
|
+
* Invoked before XMLHttpRequest.send(...) is called.
|
|
150
|
+
*/
|
|
151
|
+
setOverrideCallback(callback) {
|
|
152
|
+
overrideCallback = callback;
|
|
153
|
+
},
|
|
109
154
|
isInterceptorEnabled() {
|
|
110
155
|
return isInterceptorEnabled$1;
|
|
111
156
|
},
|
|
@@ -129,6 +174,9 @@ const XHRInterceptor = {
|
|
|
129
174
|
if (sendCallback) {
|
|
130
175
|
sendCallback(data, this);
|
|
131
176
|
}
|
|
177
|
+
if (overrideCallback) {
|
|
178
|
+
overrideCallback(this);
|
|
179
|
+
}
|
|
132
180
|
if (this.addEventListener) {
|
|
133
181
|
this.addEventListener(
|
|
134
182
|
"readystatechange",
|
|
@@ -189,14 +237,128 @@ const XHRInterceptor = {
|
|
|
189
237
|
sendCallback = null;
|
|
190
238
|
headerReceivedCallback = null;
|
|
191
239
|
requestHeaderCallback = null;
|
|
240
|
+
overrideCallback = null;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
const getStringSizeInBytes = (value) => {
|
|
244
|
+
return new TextEncoder().encode(value).length;
|
|
245
|
+
};
|
|
246
|
+
const splitSetCookieHeaderByComma = (header) => {
|
|
247
|
+
const regex = /(?:^|,\s)([^=;,]+=[^;]+(?:;[^,]*)*)/g;
|
|
248
|
+
const matches = [];
|
|
249
|
+
let match;
|
|
250
|
+
while ((match = regex.exec(header)) !== null) {
|
|
251
|
+
matches.push(match[1].trim());
|
|
252
|
+
}
|
|
253
|
+
return matches;
|
|
254
|
+
};
|
|
255
|
+
const applyReactNativeResponseHeadersLogic = (headers) => {
|
|
256
|
+
const parsedHeaders = { ...headers };
|
|
257
|
+
const setCookieHeader = getHttpHeader(headers, "set-cookie");
|
|
258
|
+
if (setCookieHeader) {
|
|
259
|
+
const { value, originalKey } = setCookieHeader;
|
|
260
|
+
const cookies = splitSetCookieHeaderByComma(value);
|
|
261
|
+
parsedHeaders[originalKey] = cookies.length > 0 ? cookies : value;
|
|
262
|
+
}
|
|
263
|
+
return parsedHeaders;
|
|
264
|
+
};
|
|
265
|
+
const isBlob = (value) => value instanceof Blob;
|
|
266
|
+
const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
267
|
+
const isFormData = (value) => value instanceof FormData;
|
|
268
|
+
const isNullOrUndefined = (value) => value === null || value === void 0;
|
|
269
|
+
const createOverridesRegistry = () => {
|
|
270
|
+
let overrides = /* @__PURE__ */ new Map();
|
|
271
|
+
const setOverrides = (newOverrides) => {
|
|
272
|
+
overrides = new Map(newOverrides);
|
|
273
|
+
};
|
|
274
|
+
const getOverrideForUrl = (url) => {
|
|
275
|
+
return overrides.get(url);
|
|
276
|
+
};
|
|
277
|
+
return {
|
|
278
|
+
setOverrides,
|
|
279
|
+
getOverrideForUrl
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
let registryInstance = null;
|
|
283
|
+
const getOverridesRegistry = () => {
|
|
284
|
+
if (!registryInstance) {
|
|
285
|
+
registryInstance = createOverridesRegistry();
|
|
192
286
|
}
|
|
287
|
+
return registryInstance;
|
|
193
288
|
};
|
|
194
289
|
const networkRequestsRegistry = getNetworkRequestsRegistry();
|
|
290
|
+
const overridesRegistry$1 = getOverridesRegistry();
|
|
291
|
+
const getBinaryPostData = (body) => ({
|
|
292
|
+
type: "binary",
|
|
293
|
+
value: {
|
|
294
|
+
size: body.size,
|
|
295
|
+
type: body.type,
|
|
296
|
+
name: getBlobName(body)
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
const getArrayBufferPostData = (body) => ({
|
|
300
|
+
type: "binary",
|
|
301
|
+
value: {
|
|
302
|
+
size: body.byteLength
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
const getTextPostData = (body) => ({
|
|
306
|
+
type: "text",
|
|
307
|
+
value: safeStringify(body)
|
|
308
|
+
});
|
|
309
|
+
const getFormDataPostData = (body) => ({
|
|
310
|
+
type: "form-data",
|
|
311
|
+
value: getFormDataEntries(body).reduce(
|
|
312
|
+
(acc, [key, value]) => {
|
|
313
|
+
if (isBlob(value)) {
|
|
314
|
+
acc[key] = getBinaryPostData(value);
|
|
315
|
+
} else if (isArrayBuffer(value)) {
|
|
316
|
+
acc[key] = getArrayBufferPostData(value);
|
|
317
|
+
} else {
|
|
318
|
+
acc[key] = getTextPostData(value);
|
|
319
|
+
}
|
|
320
|
+
return acc;
|
|
321
|
+
},
|
|
322
|
+
{}
|
|
323
|
+
)
|
|
324
|
+
});
|
|
325
|
+
const getRequestBody = (body) => {
|
|
326
|
+
if (isNullOrUndefined(body)) {
|
|
327
|
+
return body;
|
|
328
|
+
}
|
|
329
|
+
if (isBlob(body)) {
|
|
330
|
+
return getBinaryPostData(body);
|
|
331
|
+
}
|
|
332
|
+
if (isArrayBuffer(body)) {
|
|
333
|
+
return getArrayBufferPostData(body);
|
|
334
|
+
}
|
|
335
|
+
if (isFormData(body)) {
|
|
336
|
+
return getFormDataPostData(body);
|
|
337
|
+
}
|
|
338
|
+
return getTextPostData(body);
|
|
339
|
+
};
|
|
195
340
|
const getResponseSize = (request) => {
|
|
196
|
-
|
|
197
|
-
|
|
341
|
+
try {
|
|
342
|
+
const { responseType, response } = request;
|
|
343
|
+
if (response === null) {
|
|
344
|
+
return 0;
|
|
345
|
+
}
|
|
346
|
+
if (responseType === "" || responseType === "text") {
|
|
347
|
+
return getStringSizeInBytes(request.responseText);
|
|
348
|
+
}
|
|
349
|
+
if (responseType === "json") {
|
|
350
|
+
return getStringSizeInBytes(safeStringify(response));
|
|
351
|
+
}
|
|
352
|
+
if (responseType === "blob") {
|
|
353
|
+
return response.size;
|
|
354
|
+
}
|
|
355
|
+
if (responseType === "arraybuffer") {
|
|
356
|
+
return response.byteLength;
|
|
357
|
+
}
|
|
358
|
+
return 0;
|
|
359
|
+
} catch {
|
|
360
|
+
return null;
|
|
198
361
|
}
|
|
199
|
-
return request.response.length || 0;
|
|
200
362
|
};
|
|
201
363
|
const getResponseBody = async (request) => {
|
|
202
364
|
const responseType = request.responseType;
|
|
@@ -215,6 +377,9 @@ const getResponseBody = async (request) => {
|
|
|
215
377
|
});
|
|
216
378
|
}
|
|
217
379
|
}
|
|
380
|
+
if (responseType === "json") {
|
|
381
|
+
return safeStringify(request.response);
|
|
382
|
+
}
|
|
218
383
|
return null;
|
|
219
384
|
};
|
|
220
385
|
const getInitiatorFromStack = () => {
|
|
@@ -256,7 +421,7 @@ const getNetworkInspector = (pluginClient) => {
|
|
|
256
421
|
url: request._url,
|
|
257
422
|
method: request._method,
|
|
258
423
|
headers: request._headers,
|
|
259
|
-
postData: data
|
|
424
|
+
postData: getRequestBody(data)
|
|
260
425
|
},
|
|
261
426
|
type: "XHR",
|
|
262
427
|
initiator
|
|
@@ -275,7 +440,9 @@ const getNetworkInspector = (pluginClient) => {
|
|
|
275
440
|
url: request._url,
|
|
276
441
|
status: request.status,
|
|
277
442
|
statusText: request.statusText,
|
|
278
|
-
headers:
|
|
443
|
+
headers: applyReactNativeResponseHeadersLogic(
|
|
444
|
+
request.responseHeaders || {}
|
|
445
|
+
),
|
|
279
446
|
contentType: getContentType(request),
|
|
280
447
|
size: getResponseSize(request),
|
|
281
448
|
responseTime: Date.now()
|
|
@@ -310,9 +477,45 @@ const getNetworkInspector = (pluginClient) => {
|
|
|
310
477
|
});
|
|
311
478
|
});
|
|
312
479
|
};
|
|
480
|
+
const handleRequestOverride = (request) => {
|
|
481
|
+
const override = overridesRegistry$1.getOverrideForUrl(
|
|
482
|
+
request._url
|
|
483
|
+
);
|
|
484
|
+
if (!override) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
request.addEventListener("readystatechange", () => {
|
|
488
|
+
if (override.body !== void 0) {
|
|
489
|
+
Object.defineProperty(request, "responseType", {
|
|
490
|
+
writable: true
|
|
491
|
+
});
|
|
492
|
+
Object.defineProperty(request, "response", {
|
|
493
|
+
writable: true
|
|
494
|
+
});
|
|
495
|
+
Object.defineProperty(request, "responseText", {
|
|
496
|
+
writable: true
|
|
497
|
+
});
|
|
498
|
+
const contentType = getContentType(request);
|
|
499
|
+
if (contentType === "application/json") {
|
|
500
|
+
request.responseType = "json";
|
|
501
|
+
} else if (contentType === "text/plain") {
|
|
502
|
+
request.responseType = "text";
|
|
503
|
+
}
|
|
504
|
+
request.response = override.body;
|
|
505
|
+
request.responseText = override.body;
|
|
506
|
+
}
|
|
507
|
+
if (override.status !== void 0) {
|
|
508
|
+
Object.defineProperty(request, "status", {
|
|
509
|
+
writable: true
|
|
510
|
+
});
|
|
511
|
+
request.status = override.status;
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
};
|
|
313
515
|
const enable = () => {
|
|
314
516
|
XHRInterceptor.disableInterception();
|
|
315
517
|
XHRInterceptor.setSendCallback(handleRequestSend);
|
|
518
|
+
XHRInterceptor.setOverrideCallback(handleRequestOverride);
|
|
316
519
|
XHRInterceptor.enableInterception();
|
|
317
520
|
};
|
|
318
521
|
const disable = () => {
|
|
@@ -518,6 +721,8 @@ let closeCallback;
|
|
|
518
721
|
let isInterceptorEnabled = false;
|
|
519
722
|
const eventSourceClass = getEventSource();
|
|
520
723
|
const originalOpen = eventSourceClass.prototype.open;
|
|
724
|
+
const originalDispatch = eventSourceClass.prototype.dispatch;
|
|
725
|
+
const BUILT_IN_EVENT_TYPES = /* @__PURE__ */ new Set(["open", "error", "close", "done"]);
|
|
521
726
|
const SSEInterceptor = {
|
|
522
727
|
/**
|
|
523
728
|
* Invoked when EventSource.open() is called (connection attempt starting).
|
|
@@ -565,11 +770,6 @@ const SSEInterceptor = {
|
|
|
565
770
|
openEventCallback(event, this);
|
|
566
771
|
}
|
|
567
772
|
});
|
|
568
|
-
this.addEventListener("message", (event) => {
|
|
569
|
-
if (messageCallback) {
|
|
570
|
-
messageCallback(event, this);
|
|
571
|
-
}
|
|
572
|
-
});
|
|
573
773
|
this.addEventListener(
|
|
574
774
|
"error",
|
|
575
775
|
(event) => {
|
|
@@ -585,6 +785,14 @@ const SSEInterceptor = {
|
|
|
585
785
|
});
|
|
586
786
|
return originalOpen.call(this);
|
|
587
787
|
};
|
|
788
|
+
eventSourceClass.prototype.dispatch = function(eventType, data) {
|
|
789
|
+
if (!BUILT_IN_EVENT_TYPES.has(eventType)) {
|
|
790
|
+
if (messageCallback) {
|
|
791
|
+
messageCallback(data, this);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
return originalDispatch.call(this, eventType, data);
|
|
795
|
+
};
|
|
588
796
|
isInterceptorEnabled = true;
|
|
589
797
|
},
|
|
590
798
|
// Unpatch EventSource open method and remove the callbacks.
|
|
@@ -594,6 +802,7 @@ const SSEInterceptor = {
|
|
|
594
802
|
}
|
|
595
803
|
isInterceptorEnabled = false;
|
|
596
804
|
eventSourceClass.prototype.open = originalOpen;
|
|
805
|
+
eventSourceClass.prototype.dispatch = originalDispatch;
|
|
597
806
|
connectCallback = null;
|
|
598
807
|
messageCallback = null;
|
|
599
808
|
errorCallback = null;
|
|
@@ -607,9 +816,7 @@ const getSSEInspector = () => {
|
|
|
607
816
|
var _a;
|
|
608
817
|
const requestId = (_a = eventSource._xhr) == null ? void 0 : _a._rozeniteRequestId;
|
|
609
818
|
if (!requestId) {
|
|
610
|
-
|
|
611
|
-
"No request ID found for EventSource. This should never happen!"
|
|
612
|
-
);
|
|
819
|
+
return null;
|
|
613
820
|
}
|
|
614
821
|
return requestId;
|
|
615
822
|
};
|
|
@@ -618,6 +825,9 @@ const getSSEInspector = () => {
|
|
|
618
825
|
SSEInterceptor.setOpenEventCallback((_, eventSource) => {
|
|
619
826
|
const sseEventSource = eventSource;
|
|
620
827
|
const requestId = getRequestId(sseEventSource);
|
|
828
|
+
if (!requestId) {
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
621
831
|
const sseXhr = sseEventSource._xhr;
|
|
622
832
|
const event = {
|
|
623
833
|
type: "sse-open",
|
|
@@ -638,17 +848,26 @@ const getSSEInspector = () => {
|
|
|
638
848
|
SSEInterceptor.setMessageCallback((messageEvent, eventSource) => {
|
|
639
849
|
const sseEventSource = eventSource;
|
|
640
850
|
const requestId = getRequestId(sseEventSource);
|
|
851
|
+
if (!requestId) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
641
854
|
const event = {
|
|
642
855
|
type: "sse-message",
|
|
643
856
|
requestId,
|
|
644
857
|
timestamp: Date.now(),
|
|
645
|
-
|
|
858
|
+
payload: {
|
|
859
|
+
type: messageEvent.type,
|
|
860
|
+
data: messageEvent.data || ""
|
|
861
|
+
}
|
|
646
862
|
};
|
|
647
863
|
eventEmitter.emit("sse-message", event);
|
|
648
864
|
});
|
|
649
865
|
SSEInterceptor.setErrorCallback((errorEvent, eventSource) => {
|
|
650
866
|
const sseEventSource = eventSource;
|
|
651
867
|
const requestId = getRequestId(sseEventSource);
|
|
868
|
+
if (!requestId) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
652
871
|
const event = {
|
|
653
872
|
type: "sse-error",
|
|
654
873
|
requestId,
|
|
@@ -663,6 +882,9 @@ const getSSEInspector = () => {
|
|
|
663
882
|
SSEInterceptor.setCloseCallback((_, eventSource) => {
|
|
664
883
|
const sseEventSource = eventSource;
|
|
665
884
|
const requestId = getRequestId(sseEventSource);
|
|
885
|
+
if (!requestId) {
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
666
888
|
const event = {
|
|
667
889
|
type: "sse-close",
|
|
668
890
|
requestId,
|
|
@@ -677,26 +899,93 @@ const getSSEInspector = () => {
|
|
|
677
899
|
},
|
|
678
900
|
isEnabled: () => SSEInterceptor.isInterceptorEnabled(),
|
|
679
901
|
dispose: () => {
|
|
902
|
+
SSEInterceptor.disableInterception();
|
|
680
903
|
eventEmitter.events = {};
|
|
681
904
|
},
|
|
682
905
|
on: (event, callback) => eventEmitter.on(event, callback)
|
|
683
906
|
};
|
|
684
907
|
};
|
|
685
|
-
const
|
|
908
|
+
const DEFAULT_CONFIG = {
|
|
909
|
+
inspectors: {
|
|
910
|
+
http: true,
|
|
911
|
+
websocket: true,
|
|
912
|
+
sse: true
|
|
913
|
+
},
|
|
914
|
+
clientUISettings: {
|
|
915
|
+
showUrlAsName: false
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
const validateConfig = (config) => {
|
|
919
|
+
const inspectors = config.inspectors;
|
|
920
|
+
if (!inspectors) {
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
if (inspectors.sse && !inspectors.http) {
|
|
924
|
+
throw new Error("SSE inspector requires HTTP inspector to be enabled.");
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
const overridesRegistry = getOverridesRegistry();
|
|
928
|
+
const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
|
|
929
|
+
var _a, _b, _c, _d;
|
|
930
|
+
const isRecordingEnabledRef = useRef(false);
|
|
686
931
|
const client = useRozeniteDevToolsClient({
|
|
687
932
|
pluginId: "@rozenite/network-activity-plugin"
|
|
688
933
|
});
|
|
934
|
+
const isHttpInspectorEnabled = ((_a = config.inspectors) == null ? void 0 : _a.http) ?? true;
|
|
935
|
+
const isWebSocketInspectorEnabled = ((_b = config.inspectors) == null ? void 0 : _b.websocket) ?? true;
|
|
936
|
+
const isSSEInspectorEnabled = ((_c = config.inspectors) == null ? void 0 : _c.sse) ?? true;
|
|
937
|
+
const showUrlAsName = (_d = config.clientUISettings) == null ? void 0 : _d.showUrlAsName;
|
|
689
938
|
useEffect(() => {
|
|
690
939
|
if (!client) {
|
|
691
940
|
return;
|
|
692
941
|
}
|
|
942
|
+
validateConfig(config);
|
|
943
|
+
}, [config]);
|
|
944
|
+
useEffect(() => {
|
|
945
|
+
if (!client) {
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
const sendClientUISettings = () => {
|
|
949
|
+
var _a2;
|
|
950
|
+
client.send("client-ui-settings", {
|
|
951
|
+
settings: {
|
|
952
|
+
showUrlAsName: showUrlAsName ?? ((_a2 = DEFAULT_CONFIG.clientUISettings) == null ? void 0 : _a2.showUrlAsName)
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
};
|
|
956
|
+
const subscriptions = [
|
|
957
|
+
client.onMessage("network-enable", () => {
|
|
958
|
+
isRecordingEnabledRef.current = true;
|
|
959
|
+
}),
|
|
960
|
+
client.onMessage("network-disable", () => {
|
|
961
|
+
isRecordingEnabledRef.current = false;
|
|
962
|
+
}),
|
|
963
|
+
client.onMessage("set-overrides", (data) => {
|
|
964
|
+
overridesRegistry.setOverrides(data.overrides);
|
|
965
|
+
}),
|
|
966
|
+
client.onMessage("get-client-ui-settings", () => {
|
|
967
|
+
sendClientUISettings();
|
|
968
|
+
})
|
|
969
|
+
];
|
|
970
|
+
sendClientUISettings();
|
|
971
|
+
return () => {
|
|
972
|
+
subscriptions.forEach((subscription) => subscription.remove());
|
|
973
|
+
};
|
|
974
|
+
}, [client, showUrlAsName]);
|
|
975
|
+
useEffect(() => {
|
|
976
|
+
if (!client || !isHttpInspectorEnabled) {
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
693
979
|
const networkInspector = getNetworkInspector(client);
|
|
980
|
+
if (isRecordingEnabledRef.current) {
|
|
981
|
+
networkInspector.enable();
|
|
982
|
+
}
|
|
694
983
|
return () => {
|
|
695
984
|
networkInspector.dispose();
|
|
696
985
|
};
|
|
697
|
-
}, [client]);
|
|
986
|
+
}, [client, isHttpInspectorEnabled]);
|
|
698
987
|
useEffect(() => {
|
|
699
|
-
if (!client) {
|
|
988
|
+
if (!client || !isWebSocketInspectorEnabled) {
|
|
700
989
|
return;
|
|
701
990
|
}
|
|
702
991
|
const eventsToForward = [
|
|
@@ -720,12 +1009,15 @@ const useNetworkActivityDevTools = () => {
|
|
|
720
1009
|
client.onMessage("network-disable", () => {
|
|
721
1010
|
websocketInspector.disable();
|
|
722
1011
|
});
|
|
1012
|
+
if (isRecordingEnabledRef.current) {
|
|
1013
|
+
websocketInspector.enable();
|
|
1014
|
+
}
|
|
723
1015
|
return () => {
|
|
724
1016
|
websocketInspector.dispose();
|
|
725
1017
|
};
|
|
726
|
-
}, [client]);
|
|
1018
|
+
}, [client, isWebSocketInspectorEnabled]);
|
|
727
1019
|
useEffect(() => {
|
|
728
|
-
if (!client) {
|
|
1020
|
+
if (!client || !isSSEInspectorEnabled) {
|
|
729
1021
|
return;
|
|
730
1022
|
}
|
|
731
1023
|
const eventsToForward = [
|
|
@@ -746,10 +1038,13 @@ const useNetworkActivityDevTools = () => {
|
|
|
746
1038
|
client.onMessage("network-disable", () => {
|
|
747
1039
|
sseInspector.disable();
|
|
748
1040
|
});
|
|
1041
|
+
if (isRecordingEnabledRef.current) {
|
|
1042
|
+
sseInspector.enable();
|
|
1043
|
+
}
|
|
749
1044
|
return () => {
|
|
750
1045
|
sseInspector.dispose();
|
|
751
1046
|
};
|
|
752
|
-
}, [client]);
|
|
1047
|
+
}, [client, isSSEInspectorEnabled]);
|
|
753
1048
|
return client;
|
|
754
1049
|
};
|
|
755
1050
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rozenite/network-activity-plugin",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Network Activity for Rozenite.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/react-native.cjs",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"types": "./dist/react-native.d.ts",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"nanoevents": "^9.1.0",
|
|
11
|
-
"@rozenite/plugin-bridge": "1.0.0
|
|
11
|
+
"@rozenite/plugin-bridge": "1.0.0"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@floating-ui/react": "^0.26.0",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"@radix-ui/react-separator": "^1.1.7",
|
|
17
17
|
"@radix-ui/react-slot": "^1.2.3",
|
|
18
18
|
"@radix-ui/react-tabs": "^1.1.12",
|
|
19
|
+
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
|
19
20
|
"@tanstack/react-table": "^8.21.3",
|
|
20
21
|
"@tanstack/react-virtual": "^3.0.0",
|
|
21
22
|
"autoprefixer": "^10.4.21",
|
|
@@ -33,8 +34,10 @@
|
|
|
33
34
|
"typescript": "^5.7.3",
|
|
34
35
|
"vite": "^6.0.0",
|
|
35
36
|
"zustand": "^5.0.6",
|
|
36
|
-
"@
|
|
37
|
-
"
|
|
37
|
+
"@types/react": "~18.3.23",
|
|
38
|
+
"@types/react-dom": "~18.3.1",
|
|
39
|
+
"@rozenite/vite-plugin": "1.0.0",
|
|
40
|
+
"rozenite": "1.0.0"
|
|
38
41
|
},
|
|
39
42
|
"peerDependencies": {
|
|
40
43
|
"react-native-sse": "*"
|
package/react-native.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export let useNetworkActivityDevTools: typeof import('./src/react-native/useNetworkActivityDevTools').useNetworkActivityDevTools;
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const isWeb =
|
|
4
|
+
typeof window !== 'undefined' && window.navigator.product !== 'ReactNative';
|
|
5
|
+
const isDev = process.env.NODE_ENV !== 'production';
|
|
6
|
+
const isServer = typeof window === 'undefined';
|
|
7
|
+
|
|
8
|
+
if (isDev && !isWeb && !isServer) {
|
|
4
9
|
useNetworkActivityDevTools =
|
|
5
10
|
require('./src/react-native/useNetworkActivityDevTools').useNetworkActivityDevTools;
|
|
6
11
|
} else {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type InspectorType = 'http' | 'websocket' | 'sse';
|
|
2
|
+
|
|
3
|
+
export type NetworkActivityDevToolsConfig = {
|
|
4
|
+
/**
|
|
5
|
+
* Specifies which network inspectors are enabled.
|
|
6
|
+
* Set to `false` to disable monitoring for a specific type of network traffic.
|
|
7
|
+
* @default { http: true, websocket: true, sse: true }
|
|
8
|
+
*/
|
|
9
|
+
inspectors?: {
|
|
10
|
+
[key in InspectorType]?: boolean;
|
|
11
|
+
};
|
|
12
|
+
clientUISettings?: {
|
|
13
|
+
/**
|
|
14
|
+
* If true, display the entire relative URL as the request name in the UI instead of only the last path segment.
|
|
15
|
+
* @default false
|
|
16
|
+
*/
|
|
17
|
+
showUrlAsName?: boolean;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const DEFAULT_CONFIG: NetworkActivityDevToolsConfig = {
|
|
22
|
+
inspectors: {
|
|
23
|
+
http: true,
|
|
24
|
+
websocket: true,
|
|
25
|
+
sse: true,
|
|
26
|
+
},
|
|
27
|
+
clientUISettings: {
|
|
28
|
+
showUrlAsName: false,
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const validateConfig = (config: NetworkActivityDevToolsConfig): void => {
|
|
33
|
+
const inspectors = config.inspectors;
|
|
34
|
+
|
|
35
|
+
if (!inspectors) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// For SSE, HTTP must be enabled
|
|
40
|
+
if (inspectors.sse && !inspectors.http) {
|
|
41
|
+
throw new Error('SSE inspector requires HTTP inspector to be enabled.');
|
|
42
|
+
}
|
|
43
|
+
};
|