@rozenite/network-activity-plugin 1.0.0-alpha.1
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/LICENSE +20 -0
- package/README.md +73 -0
- package/dist/assets/panel-C5YgUUj5.js +54 -0
- package/dist/assets/panel-NCVczPb1.css +1 -0
- package/dist/panel.html +31 -0
- package/dist/react-native.cjs +1 -0
- package/dist/react-native.d.ts +5 -0
- package/dist/react-native.js +174 -0
- package/dist/rozenite.json +1 -0
- package/package.json +31 -0
- package/react-native.ts +7 -0
- package/rozenite.config.ts +8 -0
- package/src/css-modules.d.ts +4 -0
- package/src/react-native/useNetworkActivityDevTools.ts +235 -0
- package/src/types/network.ts +153 -0
- package/src/ui/components.module.css +158 -0
- package/src/ui/components.tsx +219 -0
- package/src/ui/network-details.module.css +57 -0
- package/src/ui/network-details.tsx +134 -0
- package/src/ui/network-list.module.css +122 -0
- package/src/ui/network-list.tsx +145 -0
- package/src/ui/network-toolbar.module.css +9 -0
- package/src/ui/network-toolbar.tsx +40 -0
- package/src/ui/panel.module.css +61 -0
- package/src/ui/panel.tsx +201 -0
- package/src/ui/tanstack-query.tsx +197 -0
- package/src/ui/utils.ts +89 -0
- package/tsconfig.json +25 -0
- package/vite.config.ts +20 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
._container_1098q_1{height:100vh;display:flex;flex-direction:column;font-family:system-ui,-apple-system,sans-serif}._mainContent_1098q_8{flex:1;display:flex;overflow:hidden;min-height:0}._networkListContainer_1098q_15{width:60%;border-right:1px solid #e0e0e0;display:flex;flex-direction:column;min-width:0}._listContent_1098q_23{flex:1}._detailsContainer_1098q_27{width:40%;display:flex;flex-direction:column;min-width:0}._headerStatus_1098q_34{width:60px;text-align:center;flex-shrink:0}._headerMethod_1098q_40{width:80px;text-align:center;flex-shrink:0}._headerName_1098q_46{flex:1;min-width:0}._headerTime_1098q_51,._headerSize_1098q_57{width:80px;text-align:right;flex-shrink:0}._button_7nfhi_2{border:none;border-radius:4px;cursor:pointer;font-weight:500;transition:all .2s ease}._button_7nfhi_2:disabled{opacity:.6;cursor:not-allowed}._buttonSmall_7nfhi_15{font-size:12px;padding:4px 8px}._buttonMedium_7nfhi_20{font-size:14px;padding:8px 16px}._buttonLarge_7nfhi_25{font-size:16px;padding:12px 24px}._buttonPrimary_7nfhi_30{background-color:#007aff;color:#fff}._buttonSecondary_7nfhi_35{background-color:#6c757d;color:#fff}._buttonDanger_7nfhi_40{background-color:#dc3545;color:#fff}._buttonSuccess_7nfhi_45{background-color:#28a745;color:#fff}._card_7nfhi_51{background-color:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:16px;box-shadow:0 1px 3px #0000001a}._badge_7nfhi_60{background-color:#007aff;color:#fff;padding:2px 6px;border-radius:12px;font-size:11px;font-weight:500;display:inline-block}._toolbar_7nfhi_71{display:flex;align-items:center;padding:8px 12px;border-bottom:1px solid #e0e0e0;background-color:#f8f9fa}._panelHeader_7nfhi_80{display:flex;align-items:center;padding:8px 12px;border-bottom:1px solid #e0e0e0;background-color:#f8f9fa;font-size:12px;font-weight:700;color:#333}._emptyState_7nfhi_92{display:flex;align-items:center;justify-content:center;padding:40px 20px;color:#666;font-size:14px;text-align:center}._loadingSpinner_7nfhi_103{border:2px solid #e0e0e0;border-top:2px solid #007AFF;border-radius:50%;animation:_spin_7nfhi_1 1s linear infinite}._tooltipReference_7nfhi_111{display:inline-block}._tooltipFloating_7nfhi_115{padding:6px 10px;border-radius:6px;font-size:12px;white-space:nowrap;z-index:1000;max-width:300px;word-break:break-all;box-shadow:0 4px 12px #0000004d;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);font-family:system-ui,-apple-system,sans-serif;line-height:1.4}._tooltipDefault_7nfhi_130{background-color:#333;color:#fff;border:1px solid rgba(255,255,255,.1)}._tooltipInfo_7nfhi_136{background-color:#06c;color:#fff;border:1px solid rgba(255,255,255,.2)}._tooltipWarning_7nfhi_142{background-color:#ff8c00;color:#fff;border:1px solid rgba(255,255,255,.2)}._tooltipError_7nfhi_148{background-color:#dc3545;color:#fff;border:1px solid rgba(255,255,255,.2)}@keyframes _spin_7nfhi_1{0%{transform:rotate(0)}to{transform:rotate(360deg)}}._recordingButton_1dfw3_1{margin-right:8px}._requestCount_1dfw3_5{margin-left:auto;font-size:12px;color:#666}._container_1s7a9_1{height:100%;overflow:auto}._virtualContainer_1s7a9_6{height:100%;width:100%;position:relative}._virtualItem_1s7a9_12{position:absolute;top:0;left:0;width:100%;height:60px}._listItem_1s7a9_20{display:flex;align-items:center;padding:8px 12px;border-bottom:1px solid #e0e0e0;background-color:#fff;cursor:pointer;font-size:12px;font-family:monospace;height:60px;box-sizing:border-box;min-width:0}._listItemSelected_1s7a9_34{display:flex;align-items:center;padding:8px 12px;border-bottom:1px solid #e0e0e0;background-color:#f5f5f5;cursor:pointer;font-size:12px;font-family:monospace;height:60px;box-sizing:border-box;min-width:0}._statusColumn_1s7a9_48{width:60px;text-align:center;flex-shrink:0}._methodColumn_1s7a9_54{width:80px;text-align:center;flex-shrink:0}._urlColumn_1s7a9_60{flex:1;overflow:hidden;min-width:0}._domainText_1s7a9_66{font-weight:700;color:#333;margin-bottom:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}._pathText_1s7a9_75{color:#666;font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}._fullUrlText_1s7a9_83{color:#999;font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px}._durationColumn_1s7a9_92{width:80px;text-align:right;margin-right:8px;flex-shrink:0}._sizeColumn_1s7a9_99{width:80px;text-align:right;flex-shrink:0}._columnText_1s7a9_105{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}._emptyContainer_1s7a9_112{height:100%;display:flex;align-items:center;justify-content:center}._emptyText_1s7a9_119{color:#666;font-size:14px}._container_felnd_1{padding:16px;height:100%;overflow:auto}._card_felnd_7{margin-bottom:20px}._cardTitle_felnd_11{margin:0 0 8px;font-size:16px}._cardTitleError_felnd_16{margin:0 0 8px;font-size:16px;color:#f44336}._infoText_felnd_22{font-size:12px;font-family:monospace}._infoRow_felnd_27,._infoRowUrl_felnd_32{margin-bottom:4px;word-break:break-all}._urlText_felnd_37{cursor:help;border-bottom:1px dotted #666}._headersContainer_felnd_42{font-size:12px;font-family:monospace;background-color:#f5f5f5;padding:8px;border-radius:4px}._headerRow_felnd_50{margin-bottom:2px;word-break:break-all}._headerValue_felnd_55{word-break:break-all}
|
package/dist/panel.html
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>panel Panel</title>
|
|
7
|
+
<style>
|
|
8
|
+
html,
|
|
9
|
+
body {
|
|
10
|
+
height: 100%;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
body {
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#root {
|
|
18
|
+
display: flex;
|
|
19
|
+
height: 100%;
|
|
20
|
+
}
|
|
21
|
+
</style>
|
|
22
|
+
<script>
|
|
23
|
+
var __ROZENITE_PANEL__ = true;
|
|
24
|
+
</script>
|
|
25
|
+
<script type="module" crossorigin src="./assets/panel-C5YgUUj5.js"></script>
|
|
26
|
+
<link rel="stylesheet" crossorigin href="./assets/panel-NCVczPb1.css">
|
|
27
|
+
</head>
|
|
28
|
+
<body>
|
|
29
|
+
<div id="root"></div>
|
|
30
|
+
</body>
|
|
31
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=require("react"),k=require("@rozenite/plugin-bridge");let g=0;function S(){return(++g).toString()}function i(t){if(t instanceof Headers){const e={};return t.forEach((r,o)=>{e[o]=r}),e}else if(Array.isArray(t)){const e={};return t.forEach(([r,o])=>{e[r]=o}),e}return t}function E(t,e){let r,o,n,s;return typeof t=="string"?(r=t,o=(e==null?void 0:e.method)||"GET",n=i((e==null?void 0:e.headers)||{}),s=(e==null?void 0:e.body)||void 0):t instanceof Request?(r=t.url,o=t.method,n=i(t.headers),s=t.body||void 0):(r=t.toString(),o=(e==null?void 0:e.method)||"GET",n=i((e==null?void 0:e.headers)||{}),s=(e==null?void 0:e.body)||void 0),{url:r,method:o,headers:n,body:s}}const T=()=>{const t=k.useRozeniteDevToolsClient({pluginId:"@rozenite/network-activity-plugin"});return v.useEffect(()=>{if(!t)return;const e=window.fetch.bind(window),r=(o,n)=>{t.send(o,n)};return window.fetch=async function(o,n){const s=S(),c=E(o,n),d=Date.now()/1e3,w=d;r("Network.requestWillBeSent",{requestId:s,loaderId:s,documentURL:"",request:{url:c.url,method:c.method,headers:c.headers,postData:c.body,hasPostData:!!c.body},timestamp:d,wallTime:w,initiator:{type:"script",stack:{callFrames:[{functionName:"fetch",scriptId:"1",url:"",lineNumber:1,columnNumber:1}]}},redirectHasExtraInfo:!1,redirectResponse:null,referrerPolicy:"no-referrer",type:"Fetch",frameId:"1",hasUserGesture:!1}),r("Network.requestWillBeSentExtraInfo",{requestId:s,blockedCookies:[],headers:c.headers,connectTiming:{requestTime:d},clientSecurityState:{initiatorIsSecureContext:!1,initiatorIPAddressSpace:"Public",privateNetworkRequestPolicy:"Allow"},siteHasCookieInOtherPartition:!1});let a;try{a=await e(o,n);const l={};a.headers.forEach((y,m)=>{l[m]=y});const p=a.headers.get("content-type")||"",h=a.headers.get("content-length"),u=h?parseInt(h,10):0;let f=u;try{f=(await a.clone().arrayBuffer()).byteLength}catch{f=u}return r("Network.responseReceived",{requestId:s,loaderId:s,timestamp:Date.now()/1e3,type:"Fetch",response:{url:c.url,status:a.status,statusText:a.statusText,headers:l,mimeType:p,requestHeaders:c.headers,requestHeadersText:void 0,connectionReused:!1,connectionId:0,remoteIPAddress:void 0,remotePort:void 0,protocol:void 0,securityState:"unknown",encodedDataLength:u,timing:{requestTime:d,proxyStart:-1,proxyEnd:-1,dnsStart:-1,dnsEnd:-1,connectStart:-1,connectEnd:-1,sslStart:-1,sslEnd:-1,workerStart:-1,workerReadyStart:-1,workerReadyEnd:-1,sendStart:d,sendEnd:d,pushStart:-1,pushEnd:-1,receiveHeadersEnd:Date.now()/1e3},responseTime:Date.now()/1e3,fromDiskCache:!1,fromServiceWorker:!1,fromPrefetchCache:!1,encodedBodySize:u,decodedBodySize:f,headersText:void 0,serviceWorkerResponseSource:void 0,responseSource:"network",statusCode:a.status},hasExtraInfo:!1}),r("Network.loadingFinished",{requestId:s,timestamp:Date.now()/1e3,encodedDataLength:u,shouldReportCorbBlocking:!1}),a}catch(l){throw r("Network.loadingFailed",{requestId:s,timestamp:Date.now()/1e3,type:"Fetch",errorText:l instanceof Error?l.message:"Unknown error",canceled:!1,blockedReason:void 0}),l}},()=>{window.fetch=e}},[t]),t};exports.useNetworkActivityDevTools=void 0;process.env.NODE_ENV!=="production"?exports.useNetworkActivityDevTools=T:exports.useNetworkActivityDevTools=()=>null;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { useEffect as v } from "react";
|
|
2
|
+
import { useRozeniteDevToolsClient as S } from "@rozenite/plugin-bridge";
|
|
3
|
+
let E = 0;
|
|
4
|
+
function g() {
|
|
5
|
+
return (++E).toString();
|
|
6
|
+
}
|
|
7
|
+
function h(t) {
|
|
8
|
+
if (t instanceof Headers) {
|
|
9
|
+
const e = {};
|
|
10
|
+
return t.forEach((r, o) => {
|
|
11
|
+
e[o] = r;
|
|
12
|
+
}), e;
|
|
13
|
+
} else if (Array.isArray(t)) {
|
|
14
|
+
const e = {};
|
|
15
|
+
return t.forEach(([r, o]) => {
|
|
16
|
+
e[r] = o;
|
|
17
|
+
}), e;
|
|
18
|
+
}
|
|
19
|
+
return t;
|
|
20
|
+
}
|
|
21
|
+
function I(t, e) {
|
|
22
|
+
let r, o, n, s;
|
|
23
|
+
return typeof t == "string" ? (r = t, o = (e == null ? void 0 : e.method) || "GET", n = h((e == null ? void 0 : e.headers) || {}), s = (e == null ? void 0 : e.body) || void 0) : t instanceof Request ? (r = t.url, o = t.method, n = h(t.headers), s = t.body || void 0) : (r = t.toString(), o = (e == null ? void 0 : e.method) || "GET", n = h((e == null ? void 0 : e.headers) || {}), s = (e == null ? void 0 : e.body) || void 0), { url: r, method: o, headers: n, body: s };
|
|
24
|
+
}
|
|
25
|
+
const T = () => {
|
|
26
|
+
const t = S({
|
|
27
|
+
pluginId: "@rozenite/network-activity-plugin"
|
|
28
|
+
});
|
|
29
|
+
return v(() => {
|
|
30
|
+
if (!t)
|
|
31
|
+
return;
|
|
32
|
+
const e = window.fetch.bind(window), r = (o, n) => {
|
|
33
|
+
t.send(o, n);
|
|
34
|
+
};
|
|
35
|
+
return window.fetch = async function(o, n) {
|
|
36
|
+
const s = g(), d = I(o, n), c = Date.now() / 1e3, w = c;
|
|
37
|
+
r("Network.requestWillBeSent", {
|
|
38
|
+
requestId: s,
|
|
39
|
+
loaderId: s,
|
|
40
|
+
documentURL: "",
|
|
41
|
+
request: {
|
|
42
|
+
url: d.url,
|
|
43
|
+
method: d.method,
|
|
44
|
+
headers: d.headers,
|
|
45
|
+
postData: d.body,
|
|
46
|
+
hasPostData: !!d.body
|
|
47
|
+
},
|
|
48
|
+
timestamp: c,
|
|
49
|
+
wallTime: w,
|
|
50
|
+
initiator: {
|
|
51
|
+
type: "script",
|
|
52
|
+
stack: {
|
|
53
|
+
callFrames: [
|
|
54
|
+
{
|
|
55
|
+
functionName: "fetch",
|
|
56
|
+
scriptId: "1",
|
|
57
|
+
url: "",
|
|
58
|
+
lineNumber: 1,
|
|
59
|
+
columnNumber: 1
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
redirectHasExtraInfo: !1,
|
|
65
|
+
redirectResponse: null,
|
|
66
|
+
referrerPolicy: "no-referrer",
|
|
67
|
+
type: "Fetch",
|
|
68
|
+
frameId: "1",
|
|
69
|
+
hasUserGesture: !1
|
|
70
|
+
}), r("Network.requestWillBeSentExtraInfo", {
|
|
71
|
+
requestId: s,
|
|
72
|
+
blockedCookies: [],
|
|
73
|
+
headers: d.headers,
|
|
74
|
+
connectTiming: {
|
|
75
|
+
requestTime: c
|
|
76
|
+
},
|
|
77
|
+
clientSecurityState: {
|
|
78
|
+
initiatorIsSecureContext: !1,
|
|
79
|
+
initiatorIPAddressSpace: "Public",
|
|
80
|
+
privateNetworkRequestPolicy: "Allow"
|
|
81
|
+
},
|
|
82
|
+
siteHasCookieInOtherPartition: !1
|
|
83
|
+
});
|
|
84
|
+
let a;
|
|
85
|
+
try {
|
|
86
|
+
a = await e(o, n);
|
|
87
|
+
const l = {};
|
|
88
|
+
a.headers.forEach((m, y) => {
|
|
89
|
+
l[y] = m;
|
|
90
|
+
});
|
|
91
|
+
const k = a.headers.get("content-type") || "", i = a.headers.get("content-length"), u = i ? parseInt(i, 10) : 0;
|
|
92
|
+
let f = u;
|
|
93
|
+
try {
|
|
94
|
+
f = (await a.clone().arrayBuffer()).byteLength;
|
|
95
|
+
} catch {
|
|
96
|
+
f = u;
|
|
97
|
+
}
|
|
98
|
+
return r("Network.responseReceived", {
|
|
99
|
+
requestId: s,
|
|
100
|
+
loaderId: s,
|
|
101
|
+
timestamp: Date.now() / 1e3,
|
|
102
|
+
type: "Fetch",
|
|
103
|
+
response: {
|
|
104
|
+
url: d.url,
|
|
105
|
+
status: a.status,
|
|
106
|
+
statusText: a.statusText,
|
|
107
|
+
headers: l,
|
|
108
|
+
mimeType: k,
|
|
109
|
+
requestHeaders: d.headers,
|
|
110
|
+
requestHeadersText: void 0,
|
|
111
|
+
connectionReused: !1,
|
|
112
|
+
connectionId: 0,
|
|
113
|
+
remoteIPAddress: void 0,
|
|
114
|
+
remotePort: void 0,
|
|
115
|
+
protocol: void 0,
|
|
116
|
+
securityState: "unknown",
|
|
117
|
+
encodedDataLength: u,
|
|
118
|
+
timing: {
|
|
119
|
+
requestTime: c,
|
|
120
|
+
proxyStart: -1,
|
|
121
|
+
proxyEnd: -1,
|
|
122
|
+
dnsStart: -1,
|
|
123
|
+
dnsEnd: -1,
|
|
124
|
+
connectStart: -1,
|
|
125
|
+
connectEnd: -1,
|
|
126
|
+
sslStart: -1,
|
|
127
|
+
sslEnd: -1,
|
|
128
|
+
workerStart: -1,
|
|
129
|
+
workerReadyStart: -1,
|
|
130
|
+
workerReadyEnd: -1,
|
|
131
|
+
sendStart: c,
|
|
132
|
+
sendEnd: c,
|
|
133
|
+
pushStart: -1,
|
|
134
|
+
pushEnd: -1,
|
|
135
|
+
receiveHeadersEnd: Date.now() / 1e3
|
|
136
|
+
},
|
|
137
|
+
responseTime: Date.now() / 1e3,
|
|
138
|
+
fromDiskCache: !1,
|
|
139
|
+
fromServiceWorker: !1,
|
|
140
|
+
fromPrefetchCache: !1,
|
|
141
|
+
encodedBodySize: u,
|
|
142
|
+
decodedBodySize: f,
|
|
143
|
+
headersText: void 0,
|
|
144
|
+
serviceWorkerResponseSource: void 0,
|
|
145
|
+
responseSource: "network",
|
|
146
|
+
statusCode: a.status
|
|
147
|
+
},
|
|
148
|
+
hasExtraInfo: !1
|
|
149
|
+
}), r("Network.loadingFinished", {
|
|
150
|
+
requestId: s,
|
|
151
|
+
timestamp: Date.now() / 1e3,
|
|
152
|
+
encodedDataLength: u,
|
|
153
|
+
shouldReportCorbBlocking: !1
|
|
154
|
+
}), a;
|
|
155
|
+
} catch (l) {
|
|
156
|
+
throw r("Network.loadingFailed", {
|
|
157
|
+
requestId: s,
|
|
158
|
+
timestamp: Date.now() / 1e3,
|
|
159
|
+
type: "Fetch",
|
|
160
|
+
errorText: l instanceof Error ? l.message : "Unknown error",
|
|
161
|
+
canceled: !1,
|
|
162
|
+
blockedReason: void 0
|
|
163
|
+
}), l;
|
|
164
|
+
}
|
|
165
|
+
}, () => {
|
|
166
|
+
window.fetch = e;
|
|
167
|
+
};
|
|
168
|
+
}, [t]), t;
|
|
169
|
+
};
|
|
170
|
+
let p;
|
|
171
|
+
process.env.NODE_ENV !== "production" ? p = T : p = () => null;
|
|
172
|
+
export {
|
|
173
|
+
p as useNetworkActivityDevTools
|
|
174
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"@rozenite/network-activity-plugin","version":"1.0.0-alpha.1","description":"Network Activity for Rozenite.","panels":[{"name":"Network Activity","source":"/panel.html"}]}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rozenite/network-activity-plugin",
|
|
3
|
+
"version": "1.0.0-alpha.1",
|
|
4
|
+
"description": "Network Activity for Rozenite.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/react-native.cjs",
|
|
7
|
+
"module": "./dist/react-native.js",
|
|
8
|
+
"types": "./dist/react-native.d.ts",
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@tanstack/react-virtual": "^3.0.0",
|
|
11
|
+
"@floating-ui/react": "^0.26.0",
|
|
12
|
+
"@rozenite/plugin-bridge": "1.0.0-alpha.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"typescript": "^5.7.3",
|
|
16
|
+
"vite": "^6.0.0",
|
|
17
|
+
"react": "*",
|
|
18
|
+
"react-native": "*",
|
|
19
|
+
"rozenite": "1.0.0-alpha.2",
|
|
20
|
+
"@rozenite/vite-plugin": "1.0.0-alpha.1"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"react": ">= 17.0.0",
|
|
24
|
+
"react-native": ">= 0.74.0"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "rozenite build",
|
|
29
|
+
"dev": "rozenite dev"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/react-native.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export let useNetworkActivityDevTools: typeof import('./src/react-native/useNetworkActivityDevTools').useNetworkActivityDevTools;
|
|
2
|
+
|
|
3
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
4
|
+
useNetworkActivityDevTools = require('./src/react-native/useNetworkActivityDevTools').useNetworkActivityDevTools;
|
|
5
|
+
} else {
|
|
6
|
+
useNetworkActivityDevTools = () => null;
|
|
7
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useRozeniteDevToolsClient } from '@rozenite/plugin-bridge';
|
|
3
|
+
import { NetworkEventMap } from '../types/network';
|
|
4
|
+
|
|
5
|
+
let requestCounter = 0;
|
|
6
|
+
|
|
7
|
+
function generateRequestId() {
|
|
8
|
+
return (++requestCounter).toString();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Helper function to convert Headers object to plain object
|
|
12
|
+
function headersToObject(headers: HeadersInit | Record<string, string>): Record<string, string> {
|
|
13
|
+
if (headers instanceof Headers) {
|
|
14
|
+
const obj: Record<string, string> = {};
|
|
15
|
+
headers.forEach((value: string, key: string) => {
|
|
16
|
+
obj[key] = value;
|
|
17
|
+
});
|
|
18
|
+
return obj;
|
|
19
|
+
} else if (Array.isArray(headers)) {
|
|
20
|
+
const obj: Record<string, string> = {};
|
|
21
|
+
headers.forEach(([key, value]) => {
|
|
22
|
+
obj[key] = value;
|
|
23
|
+
});
|
|
24
|
+
return obj;
|
|
25
|
+
}
|
|
26
|
+
return headers as Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Helper function to get request info from fetch input
|
|
30
|
+
function getRequestInfo(input: RequestInfo | URL, init?: RequestInit) {
|
|
31
|
+
let url: string;
|
|
32
|
+
let method: string;
|
|
33
|
+
let headers: Record<string, string>;
|
|
34
|
+
let body: unknown;
|
|
35
|
+
|
|
36
|
+
if (typeof input === 'string') {
|
|
37
|
+
url = input;
|
|
38
|
+
method = init?.method || 'GET';
|
|
39
|
+
headers = headersToObject(init?.headers || {});
|
|
40
|
+
body = init?.body || undefined;
|
|
41
|
+
} else if (input instanceof Request) {
|
|
42
|
+
url = input.url;
|
|
43
|
+
method = input.method;
|
|
44
|
+
headers = headersToObject(input.headers);
|
|
45
|
+
body = input.body || undefined;
|
|
46
|
+
} else {
|
|
47
|
+
// URL object
|
|
48
|
+
url = input.toString();
|
|
49
|
+
method = init?.method || 'GET';
|
|
50
|
+
headers = headersToObject(init?.headers || {});
|
|
51
|
+
body = init?.body || undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { url, method, headers, body };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const useNetworkActivityDevTools = () => {
|
|
58
|
+
const client = useRozeniteDevToolsClient<NetworkEventMap>({
|
|
59
|
+
pluginId: '@rozenite/network-activity-plugin',
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (!client) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const originalFetch = window.fetch.bind(window);
|
|
68
|
+
|
|
69
|
+
const sendCdpEvent = (method: keyof NetworkEventMap, params: NetworkEventMap[keyof NetworkEventMap]) => {
|
|
70
|
+
client.send(method, params);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
window.fetch = async function(input: RequestInfo | URL, init?: RequestInit) {
|
|
74
|
+
const requestId = generateRequestId();
|
|
75
|
+
const requestInfo = getRequestInfo(input, init);
|
|
76
|
+
const timestamp = Date.now() / 1000;
|
|
77
|
+
const wallTime = timestamp;
|
|
78
|
+
|
|
79
|
+
sendCdpEvent('Network.requestWillBeSent', {
|
|
80
|
+
requestId,
|
|
81
|
+
loaderId: requestId,
|
|
82
|
+
documentURL: '',
|
|
83
|
+
request: {
|
|
84
|
+
url: requestInfo.url,
|
|
85
|
+
method: requestInfo.method,
|
|
86
|
+
headers: requestInfo.headers,
|
|
87
|
+
postData: requestInfo.body,
|
|
88
|
+
hasPostData: !!requestInfo.body
|
|
89
|
+
},
|
|
90
|
+
timestamp,
|
|
91
|
+
wallTime,
|
|
92
|
+
initiator: {
|
|
93
|
+
type: 'script',
|
|
94
|
+
stack: {
|
|
95
|
+
callFrames: [
|
|
96
|
+
{
|
|
97
|
+
functionName: 'fetch',
|
|
98
|
+
scriptId: '1',
|
|
99
|
+
url: '',
|
|
100
|
+
lineNumber: 1,
|
|
101
|
+
columnNumber: 1
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
redirectHasExtraInfo: false,
|
|
107
|
+
redirectResponse: null,
|
|
108
|
+
referrerPolicy: 'no-referrer',
|
|
109
|
+
type: 'Fetch',
|
|
110
|
+
frameId: '1',
|
|
111
|
+
hasUserGesture: false
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
sendCdpEvent('Network.requestWillBeSentExtraInfo', {
|
|
115
|
+
requestId,
|
|
116
|
+
blockedCookies: [],
|
|
117
|
+
headers: requestInfo.headers,
|
|
118
|
+
connectTiming: {
|
|
119
|
+
requestTime: timestamp
|
|
120
|
+
},
|
|
121
|
+
clientSecurityState: {
|
|
122
|
+
initiatorIsSecureContext: false,
|
|
123
|
+
initiatorIPAddressSpace: 'Public',
|
|
124
|
+
privateNetworkRequestPolicy: 'Allow'
|
|
125
|
+
},
|
|
126
|
+
siteHasCookieInOtherPartition: false
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
let response;
|
|
130
|
+
try {
|
|
131
|
+
response = await originalFetch(input, init);
|
|
132
|
+
|
|
133
|
+
// Get response headers
|
|
134
|
+
const responseHeaders: Record<string, string> = {};
|
|
135
|
+
response.headers.forEach((value: string, key: string) => {
|
|
136
|
+
responseHeaders[key] = value;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const contentType = response.headers.get('content-type') || '';
|
|
140
|
+
const contentLength = response.headers.get('content-length');
|
|
141
|
+
const encodedDataLength = contentLength ? parseInt(contentLength, 10) : 0;
|
|
142
|
+
|
|
143
|
+
// Get decoded body size (actual response size)
|
|
144
|
+
let decodedBodySize = encodedDataLength;
|
|
145
|
+
try {
|
|
146
|
+
const responseClone = response.clone();
|
|
147
|
+
const arrayBuffer = await responseClone.arrayBuffer();
|
|
148
|
+
decodedBodySize = arrayBuffer.byteLength;
|
|
149
|
+
} catch {
|
|
150
|
+
// Fallback to content-length if we can't read the response
|
|
151
|
+
decodedBodySize = encodedDataLength;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
sendCdpEvent('Network.responseReceived', {
|
|
155
|
+
requestId,
|
|
156
|
+
loaderId: requestId,
|
|
157
|
+
timestamp: Date.now() / 1000,
|
|
158
|
+
type: 'Fetch',
|
|
159
|
+
response: {
|
|
160
|
+
url: requestInfo.url,
|
|
161
|
+
status: response.status,
|
|
162
|
+
statusText: response.statusText,
|
|
163
|
+
headers: responseHeaders,
|
|
164
|
+
mimeType: contentType,
|
|
165
|
+
requestHeaders: requestInfo.headers,
|
|
166
|
+
requestHeadersText: undefined,
|
|
167
|
+
connectionReused: false,
|
|
168
|
+
connectionId: 0,
|
|
169
|
+
remoteIPAddress: undefined,
|
|
170
|
+
remotePort: undefined,
|
|
171
|
+
protocol: undefined,
|
|
172
|
+
securityState: 'unknown',
|
|
173
|
+
encodedDataLength,
|
|
174
|
+
timing: {
|
|
175
|
+
requestTime: timestamp,
|
|
176
|
+
proxyStart: -1,
|
|
177
|
+
proxyEnd: -1,
|
|
178
|
+
dnsStart: -1,
|
|
179
|
+
dnsEnd: -1,
|
|
180
|
+
connectStart: -1,
|
|
181
|
+
connectEnd: -1,
|
|
182
|
+
sslStart: -1,
|
|
183
|
+
sslEnd: -1,
|
|
184
|
+
workerStart: -1,
|
|
185
|
+
workerReadyStart: -1,
|
|
186
|
+
workerReadyEnd: -1,
|
|
187
|
+
sendStart: timestamp,
|
|
188
|
+
sendEnd: timestamp,
|
|
189
|
+
pushStart: -1,
|
|
190
|
+
pushEnd: -1,
|
|
191
|
+
receiveHeadersEnd: Date.now() / 1000
|
|
192
|
+
},
|
|
193
|
+
responseTime: Date.now() / 1000,
|
|
194
|
+
fromDiskCache: false,
|
|
195
|
+
fromServiceWorker: false,
|
|
196
|
+
fromPrefetchCache: false,
|
|
197
|
+
encodedBodySize: encodedDataLength,
|
|
198
|
+
decodedBodySize: decodedBodySize,
|
|
199
|
+
headersText: undefined,
|
|
200
|
+
serviceWorkerResponseSource: undefined,
|
|
201
|
+
responseSource: 'network',
|
|
202
|
+
statusCode: response.status
|
|
203
|
+
},
|
|
204
|
+
hasExtraInfo: false
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
sendCdpEvent('Network.loadingFinished', {
|
|
208
|
+
requestId,
|
|
209
|
+
timestamp: Date.now() / 1000,
|
|
210
|
+
encodedDataLength,
|
|
211
|
+
shouldReportCorbBlocking: false
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return response;
|
|
215
|
+
} catch (err) {
|
|
216
|
+
sendCdpEvent('Network.loadingFailed', {
|
|
217
|
+
requestId,
|
|
218
|
+
timestamp: Date.now() / 1000,
|
|
219
|
+
type: 'Fetch',
|
|
220
|
+
errorText: err instanceof Error ? err.message : 'Unknown error',
|
|
221
|
+
canceled: false,
|
|
222
|
+
blockedReason: undefined
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
throw err;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
return () => {
|
|
230
|
+
window.fetch = originalFetch;
|
|
231
|
+
};
|
|
232
|
+
}, [client]);
|
|
233
|
+
|
|
234
|
+
return client;
|
|
235
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Types for Chrome DevTools Protocol Network events
|
|
2
|
+
|
|
3
|
+
export interface NetworkRequest {
|
|
4
|
+
requestId: string;
|
|
5
|
+
loaderId: string;
|
|
6
|
+
documentURL: string;
|
|
7
|
+
request: {
|
|
8
|
+
url: string;
|
|
9
|
+
method: string;
|
|
10
|
+
headers: Record<string, string>;
|
|
11
|
+
postData?: string;
|
|
12
|
+
hasPostData?: boolean;
|
|
13
|
+
};
|
|
14
|
+
timestamp: number;
|
|
15
|
+
wallTime: number;
|
|
16
|
+
initiator: {
|
|
17
|
+
type: string;
|
|
18
|
+
stack?: {
|
|
19
|
+
callFrames: Array<{
|
|
20
|
+
functionName: string;
|
|
21
|
+
scriptId: string;
|
|
22
|
+
url: string;
|
|
23
|
+
lineNumber: number;
|
|
24
|
+
columnNumber: number;
|
|
25
|
+
}>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
redirectHasExtraInfo: boolean;
|
|
29
|
+
redirectResponse?: any;
|
|
30
|
+
referrerPolicy: string;
|
|
31
|
+
type: string;
|
|
32
|
+
frameId: string;
|
|
33
|
+
hasUserGesture: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface NetworkRequestExtraInfo {
|
|
37
|
+
requestId: string;
|
|
38
|
+
blockedCookies: Array<{
|
|
39
|
+
blockedReasons: string[];
|
|
40
|
+
cookie: {
|
|
41
|
+
name: string;
|
|
42
|
+
value: string;
|
|
43
|
+
domain: string;
|
|
44
|
+
path: string;
|
|
45
|
+
expires: number;
|
|
46
|
+
size: number;
|
|
47
|
+
httpOnly: boolean;
|
|
48
|
+
secure: boolean;
|
|
49
|
+
session: boolean;
|
|
50
|
+
sameSite: string;
|
|
51
|
+
};
|
|
52
|
+
}>;
|
|
53
|
+
headers: Record<string, string>;
|
|
54
|
+
connectTiming: {
|
|
55
|
+
requestTime: number;
|
|
56
|
+
};
|
|
57
|
+
clientSecurityState?: {
|
|
58
|
+
initiatorIsSecureContext: boolean;
|
|
59
|
+
initiatorIPAddressSpace: string;
|
|
60
|
+
privateNetworkRequestPolicy: string;
|
|
61
|
+
};
|
|
62
|
+
siteHasCookieInOtherPartition: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface NetworkResponse {
|
|
66
|
+
requestId: string;
|
|
67
|
+
loaderId: string;
|
|
68
|
+
timestamp: number;
|
|
69
|
+
type: string;
|
|
70
|
+
response: {
|
|
71
|
+
url: string;
|
|
72
|
+
status: number;
|
|
73
|
+
statusText: string;
|
|
74
|
+
headers: Record<string, string>;
|
|
75
|
+
mimeType: string;
|
|
76
|
+
requestHeaders: Record<string, string>;
|
|
77
|
+
requestHeadersText?: string;
|
|
78
|
+
connectionReused: boolean;
|
|
79
|
+
connectionId: number;
|
|
80
|
+
remoteIPAddress?: string;
|
|
81
|
+
remotePort?: number;
|
|
82
|
+
protocol?: string;
|
|
83
|
+
securityState: string;
|
|
84
|
+
encodedDataLength: number;
|
|
85
|
+
timing?: {
|
|
86
|
+
requestTime: number;
|
|
87
|
+
proxyStart: number;
|
|
88
|
+
proxyEnd: number;
|
|
89
|
+
dnsStart: number;
|
|
90
|
+
dnsEnd: number;
|
|
91
|
+
connectStart: number;
|
|
92
|
+
connectEnd: number;
|
|
93
|
+
sslStart: number;
|
|
94
|
+
sslEnd: number;
|
|
95
|
+
workerStart: number;
|
|
96
|
+
workerReadyStart: number;
|
|
97
|
+
workerReadyEnd: number;
|
|
98
|
+
sendStart: number;
|
|
99
|
+
sendEnd: number;
|
|
100
|
+
pushStart: number;
|
|
101
|
+
pushEnd: number;
|
|
102
|
+
receiveHeadersEnd: number;
|
|
103
|
+
};
|
|
104
|
+
responseTime: number;
|
|
105
|
+
fromDiskCache: boolean;
|
|
106
|
+
fromServiceWorker: boolean;
|
|
107
|
+
fromPrefetchCache: boolean;
|
|
108
|
+
encodedBodySize: number;
|
|
109
|
+
decodedBodySize: number;
|
|
110
|
+
headersText?: string;
|
|
111
|
+
serviceWorkerResponseSource?: string;
|
|
112
|
+
responseSource: string;
|
|
113
|
+
statusCode: number;
|
|
114
|
+
};
|
|
115
|
+
hasExtraInfo: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface NetworkLoadingFinished {
|
|
119
|
+
requestId: string;
|
|
120
|
+
timestamp: number;
|
|
121
|
+
encodedDataLength: number;
|
|
122
|
+
shouldReportCorbBlocking: boolean;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface NetworkLoadingFailed {
|
|
126
|
+
requestId: string;
|
|
127
|
+
timestamp: number;
|
|
128
|
+
type: string;
|
|
129
|
+
errorText: string;
|
|
130
|
+
canceled?: boolean;
|
|
131
|
+
blockedReason?: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface NetworkEventMap extends Record<string, unknown> {
|
|
135
|
+
'Network.requestWillBeSent': NetworkRequest;
|
|
136
|
+
'Network.requestWillBeSentExtraInfo': NetworkRequestExtraInfo;
|
|
137
|
+
'Network.responseReceived': NetworkResponse;
|
|
138
|
+
'Network.loadingFinished': NetworkLoadingFinished;
|
|
139
|
+
'Network.loadingFailed': NetworkLoadingFailed;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface NetworkEntry {
|
|
143
|
+
requestId: string;
|
|
144
|
+
request: NetworkRequest;
|
|
145
|
+
extraInfo?: NetworkRequestExtraInfo;
|
|
146
|
+
response?: NetworkResponse;
|
|
147
|
+
loadingFinished?: NetworkLoadingFinished;
|
|
148
|
+
loadingFailed?: NetworkLoadingFailed;
|
|
149
|
+
status: 'pending' | 'loading' | 'finished' | 'failed';
|
|
150
|
+
startTime: number;
|
|
151
|
+
endTime?: number;
|
|
152
|
+
duration?: number;
|
|
153
|
+
}
|