@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.
@@ -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}
@@ -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,5 @@
1
+ import { useNetworkActivityDevTools as useNetworkActivityDevTools_2 } from './src/react-native/useNetworkActivityDevTools';
2
+
3
+ export declare let useNetworkActivityDevTools: useNetworkActivityDevTools_2;
4
+
5
+ export { }
@@ -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
+ }
@@ -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,8 @@
1
+ export default {
2
+ panels: [
3
+ {
4
+ name: 'Network Activity',
5
+ source: './src/ui/panel.tsx',
6
+ }
7
+ ],
8
+ };
@@ -0,0 +1,4 @@
1
+ declare module '*.module.css' {
2
+ const classes: { [key: string]: string };
3
+ export default classes;
4
+ }
@@ -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
+ }