@rozenite/network-activity-plugin 1.0.0-alpha.5 → 1.0.0-alpha.7
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/dist/{panel.html → App.html} +3 -3
- package/dist/assets/App-CIflVb88.js +24164 -0
- package/dist/assets/App-Czu6Vt2P.css +1233 -0
- package/dist/react-native.cjs +1 -1
- package/dist/react-native.d.ts +1 -90
- package/dist/rozenite.config.d.ts +7 -0
- package/dist/rozenite.json +1 -1
- package/dist/src/react-native/network-inspector.d.ts +8 -0
- package/dist/src/react-native/network-requests-registry.d.ts +6 -0
- package/dist/src/react-native/useNetworkActivityDevTools.d.ts +2 -0
- package/dist/src/react-native/xhr-interceptor.d.ts +38 -0
- package/dist/src/shared/client.d.ts +64 -0
- package/dist/src/ui/App.d.ts +1 -0
- package/dist/src/ui/components/Badge.d.ts +9 -0
- package/dist/src/ui/components/Button.d.ts +11 -0
- package/dist/src/ui/components/Input.d.ts +3 -0
- package/dist/src/ui/components/JsonTree.d.ts +5 -0
- package/dist/src/ui/components/RequestList.d.ts +45 -0
- package/dist/src/ui/components/ScrollArea.d.ts +4 -0
- package/dist/src/ui/components/Separator.d.ts +3 -0
- package/dist/src/ui/tabs/CookiesTab.d.ts +8 -0
- package/dist/src/ui/tabs/HeadersTab.d.ts +17 -0
- package/dist/src/ui/tabs/RequestTab.d.ts +10 -0
- package/dist/src/ui/tabs/ResponseTab.d.ts +12 -0
- package/dist/src/ui/tabs/TimingTab.d.ts +7 -0
- package/dist/src/ui/types.d.ts +23 -0
- package/dist/src/ui/utils.d.ts +2 -0
- package/dist/src/ui/views/InspectorView.d.ts +5 -0
- package/dist/src/ui/views/LoadingView.d.ts +1 -0
- package/dist/useNetworkActivityDevTools.cjs +360 -0
- package/dist/useNetworkActivityDevTools.js +108 -236
- package/package.json +23 -16
- package/postcss.config.js +6 -0
- package/rozenite.config.ts +1 -1
- package/src/react-native/network-inspector.ts +113 -260
- package/src/react-native/network-requests-registry.ts +7 -77
- package/src/react-native/useNetworkActivityDevTools.ts +1 -1
- package/src/react-native/xhr-interceptor.ts +2 -2
- package/src/react-native/xml-request.d.ts +11 -1
- package/src/shared/client.ts +80 -0
- package/src/ui/App.tsx +19 -0
- package/src/ui/components/Badge.tsx +36 -0
- package/src/ui/components/Button.tsx +56 -0
- package/src/ui/components/Input.tsx +22 -0
- package/src/ui/components/JsonTree.tsx +37 -0
- package/src/ui/components/RequestList.tsx +376 -0
- package/src/ui/components/ScrollArea.tsx +48 -0
- package/src/ui/components/Separator.tsx +31 -0
- package/src/ui/components/Tabs.tsx +55 -0
- package/src/ui/globals.css +90 -0
- package/src/ui/tabs/CookiesTab.tsx +290 -0
- package/src/ui/tabs/HeadersTab.tsx +117 -0
- package/src/ui/tabs/RequestTab.tsx +72 -0
- package/src/ui/tabs/ResponseTab.tsx +140 -0
- package/src/ui/tabs/TimingTab.tsx +71 -0
- package/src/ui/types.ts +30 -0
- package/src/ui/utils.ts +5 -97
- package/src/ui/views/InspectorView.tsx +349 -0
- package/src/ui/views/LoadingView.tsx +19 -0
- package/tailwind.config.ts +93 -0
- package/dist/assets/panel-BNxB_KsS.js +0 -16663
- package/dist/assets/panel-DXGMsavf.css +0 -555
- package/src/types/client.ts +0 -111
- package/src/types/network.ts +0 -32
- package/src/ui/components.module.css +0 -158
- package/src/ui/components.tsx +0 -241
- package/src/ui/network-details.module.css +0 -197
- package/src/ui/network-details.tsx +0 -345
- package/src/ui/network-list.module.css +0 -128
- package/src/ui/network-list.tsx +0 -240
- package/src/ui/network-toolbar.module.css +0 -9
- package/src/ui/network-toolbar.tsx +0 -34
- package/src/ui/panel.module.css +0 -67
- package/src/ui/panel.tsx +0 -318
- package/src/ui/tanstack-query.tsx +0 -204
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import { NetworkActivityDevToolsClient } from '../
|
|
1
|
+
import { NetworkActivityDevToolsClient } from '../shared/client';
|
|
2
2
|
import { getNetworkRequestsRegistry } from './network-requests-registry';
|
|
3
3
|
import { XHRInterceptor } from './xhr-interceptor';
|
|
4
4
|
|
|
5
5
|
const networkRequestsRegistry = getNetworkRequestsRegistry();
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const getContentType = (request: XMLHttpRequest): string => {
|
|
8
|
+
const responseHeaders = request.responseHeaders;
|
|
9
|
+
const responseType = request.responseType;
|
|
10
|
+
|
|
11
|
+
if (responseHeaders?.['content-type']) {
|
|
12
|
+
return responseHeaders['content-type'].split(';')[0].trim();
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
switch (responseType) {
|
|
9
16
|
case 'arraybuffer':
|
|
10
17
|
case 'blob':
|
|
11
|
-
case 'base64':
|
|
12
18
|
return 'application/octet-stream';
|
|
13
19
|
case 'text':
|
|
14
20
|
case '':
|
|
@@ -18,81 +24,45 @@ const mimeTypeFromResponseType = (responseType: string): string | undefined => {
|
|
|
18
24
|
case 'document':
|
|
19
25
|
return 'text/html';
|
|
20
26
|
}
|
|
21
|
-
|
|
22
|
-
return undefined;
|
|
23
27
|
};
|
|
24
28
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const lines = headersString.split('\r\n');
|
|
30
|
-
for (const line of lines) {
|
|
31
|
-
const colonIndex = line.indexOf(':');
|
|
32
|
-
if (colonIndex > 0) {
|
|
33
|
-
const key = line.substring(0, colonIndex).trim();
|
|
34
|
-
const value = line.substring(colonIndex + 1).trim();
|
|
35
|
-
headers[key] = value;
|
|
36
|
-
}
|
|
29
|
+
const getResponseSize = (request: XMLHttpRequest): number => {
|
|
30
|
+
if (typeof request.response === 'object') {
|
|
31
|
+
return request.response.size;
|
|
37
32
|
}
|
|
38
|
-
|
|
33
|
+
|
|
34
|
+
return request.response.length || 0;
|
|
39
35
|
};
|
|
40
36
|
|
|
41
37
|
const getResponseBody = async (
|
|
42
38
|
request: XMLHttpRequest
|
|
43
|
-
): Promise<
|
|
44
|
-
|
|
45
|
-
if (request.responseType === 'arraybuffer') {
|
|
46
|
-
const arrayBuffer = request.response as ArrayBuffer;
|
|
47
|
-
return {
|
|
48
|
-
body: btoa(String.fromCharCode(...new Uint8Array(arrayBuffer))),
|
|
49
|
-
base64Encoded: true,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
39
|
+
): Promise<string | null> => {
|
|
40
|
+
const responseType = request.responseType;
|
|
52
41
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
contentType.startsWith('text/') ||
|
|
58
|
-
contentType.startsWith('application/json')
|
|
59
|
-
) {
|
|
60
|
-
return new Promise((resolve) => {
|
|
61
|
-
const reader = new FileReader();
|
|
62
|
-
reader.onload = () => {
|
|
63
|
-
resolve({
|
|
64
|
-
body: reader.result as string,
|
|
65
|
-
base64Encoded: false,
|
|
66
|
-
});
|
|
67
|
-
};
|
|
68
|
-
reader.readAsText(request.response);
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
42
|
+
if (responseType === 'text') {
|
|
43
|
+
return request.responseText as string;
|
|
44
|
+
}
|
|
72
45
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
46
|
+
if (responseType === 'blob') {
|
|
47
|
+
// This may be a text blob.
|
|
48
|
+
const contentType = request.getResponseHeader('Content-Type') || '';
|
|
49
|
+
|
|
50
|
+
if (
|
|
51
|
+
contentType.startsWith('text/') ||
|
|
52
|
+
contentType.startsWith('application/json')
|
|
53
|
+
) {
|
|
54
|
+
// It looks like a text blob, let's read it and forward it to the client.
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
const reader = new FileReader();
|
|
57
|
+
reader.onload = () => {
|
|
58
|
+
resolve(reader.result as string);
|
|
59
|
+
};
|
|
60
|
+
reader.readAsText(request.response);
|
|
61
|
+
});
|
|
78
62
|
}
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
body: request.responseText || request.response || '',
|
|
82
|
-
base64Encoded: false,
|
|
83
|
-
};
|
|
84
|
-
} catch (error) {
|
|
85
|
-
return {
|
|
86
|
-
body: `[Error reading response: ${error}]`,
|
|
87
|
-
base64Encoded: false,
|
|
88
|
-
};
|
|
89
63
|
}
|
|
90
|
-
};
|
|
91
64
|
|
|
92
|
-
|
|
93
|
-
const allRequests = networkRequestsRegistry.getAllEntries();
|
|
94
|
-
const entry = allRequests.find(({ request: req }) => req === request);
|
|
95
|
-
return entry?.id ?? null;
|
|
65
|
+
return null;
|
|
96
66
|
};
|
|
97
67
|
|
|
98
68
|
const getInitiatorFromStack = (): {
|
|
@@ -131,6 +101,8 @@ export type NetworkInspector = {
|
|
|
131
101
|
dispose: () => void;
|
|
132
102
|
};
|
|
133
103
|
|
|
104
|
+
const READY_STATE_HEADERS_RECEIVED = 2;
|
|
105
|
+
|
|
134
106
|
export const getNetworkInspector = (
|
|
135
107
|
pluginClient: NetworkActivityDevToolsClient
|
|
136
108
|
): NetworkInspector => {
|
|
@@ -138,202 +110,86 @@ export const getNetworkInspector = (
|
|
|
138
110
|
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
139
111
|
};
|
|
140
112
|
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
};
|
|
113
|
+
const handleRequestSend = (data: string, request: XMLHttpRequest): void => {
|
|
114
|
+
const sendTime = Date.now();
|
|
144
115
|
|
|
145
|
-
|
|
146
|
-
|
|
116
|
+
const requestId = generateRequestId();
|
|
117
|
+
const initiator = getInitiatorFromStack();
|
|
147
118
|
|
|
148
|
-
|
|
149
|
-
(method: string, url: string, request: XMLHttpRequest) => {
|
|
150
|
-
const requestId = generateRequestId();
|
|
151
|
-
const loaderId = generateLoaderId();
|
|
152
|
-
const startTime = Date.now();
|
|
153
|
-
const initiator = getInitiatorFromStack();
|
|
154
|
-
|
|
155
|
-
// Store request in registry with metadata
|
|
156
|
-
networkRequestsRegistry.addEntry(requestId, request, {
|
|
157
|
-
id: requestId,
|
|
158
|
-
loaderId,
|
|
159
|
-
documentURL:
|
|
160
|
-
typeof document !== 'undefined' ? document.URL : undefined,
|
|
161
|
-
method,
|
|
162
|
-
url,
|
|
163
|
-
headers: request?._headers || {},
|
|
164
|
-
startTime,
|
|
165
|
-
status: 'pending',
|
|
166
|
-
type: 'XHR',
|
|
167
|
-
initiator,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
);
|
|
119
|
+
networkRequestsRegistry.addEntry(requestId, request);
|
|
171
120
|
|
|
172
|
-
|
|
173
|
-
const requestId = findRequestId(request);
|
|
174
|
-
if (!requestId) return;
|
|
121
|
+
let ttfb = 0;
|
|
175
122
|
|
|
176
|
-
|
|
177
|
-
|
|
123
|
+
pluginClient.send('request-sent', {
|
|
124
|
+
requestId: requestId,
|
|
125
|
+
timestamp: sendTime / 1000,
|
|
126
|
+
request: {
|
|
127
|
+
url: request._url as string,
|
|
128
|
+
method: request._method as string,
|
|
129
|
+
headers: request._headers,
|
|
130
|
+
postData: data,
|
|
131
|
+
},
|
|
132
|
+
type: 'XHR',
|
|
133
|
+
initiator,
|
|
134
|
+
});
|
|
178
135
|
|
|
179
|
-
|
|
136
|
+
request.addEventListener('readystatechange', () => {
|
|
137
|
+
if (request.readyState === READY_STATE_HEADERS_RECEIVED) {
|
|
138
|
+
ttfb = Date.now() - sendTime;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
180
141
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
142
|
+
request.addEventListener('load', () => {
|
|
143
|
+
pluginClient.send('response-received', {
|
|
144
|
+
requestId: requestId,
|
|
145
|
+
timestamp: Date.now() / 1000,
|
|
146
|
+
type: 'XHR',
|
|
147
|
+
response: {
|
|
148
|
+
url: request._url as string,
|
|
149
|
+
status: request.status,
|
|
150
|
+
statusText: request.statusText,
|
|
151
|
+
headers: request.responseHeaders as Record<string, string>,
|
|
152
|
+
contentType: getContentType(request),
|
|
153
|
+
size: getResponseSize(request),
|
|
154
|
+
responseTime: Date.now() / 1000,
|
|
155
|
+
},
|
|
186
156
|
});
|
|
157
|
+
});
|
|
187
158
|
|
|
188
|
-
|
|
189
|
-
pluginClient.send('
|
|
190
|
-
requestId,
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
method: metadata.method,
|
|
196
|
-
headers: metadata.headers,
|
|
197
|
-
postData: data,
|
|
198
|
-
hasPostData: !!data,
|
|
199
|
-
},
|
|
200
|
-
timestamp: metadata.startTime,
|
|
201
|
-
wallTime: metadata.startTime,
|
|
202
|
-
initiator: metadata.initiator || { type: 'other' },
|
|
203
|
-
type: metadata.type,
|
|
159
|
+
request.addEventListener('loadend', () => {
|
|
160
|
+
pluginClient.send('request-completed', {
|
|
161
|
+
requestId: requestId,
|
|
162
|
+
timestamp: Date.now() / 1000,
|
|
163
|
+
duration: Date.now() - sendTime,
|
|
164
|
+
size: getResponseSize(request),
|
|
165
|
+
ttfb,
|
|
204
166
|
});
|
|
205
167
|
});
|
|
206
168
|
|
|
207
|
-
|
|
208
|
-
(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// Update metadata with response info
|
|
228
|
-
networkRequestsRegistry.updateEntry(requestId, {
|
|
229
|
-
status: 'loading',
|
|
230
|
-
response: {
|
|
231
|
-
url: metadata.url,
|
|
232
|
-
status: request.status,
|
|
233
|
-
statusText: request.statusText,
|
|
234
|
-
headers,
|
|
235
|
-
mimeType,
|
|
236
|
-
encodedDataLength: responseSize || 0,
|
|
237
|
-
responseTime: Date.now(),
|
|
238
|
-
},
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
// Send Network.responseReceived event
|
|
242
|
-
pluginClient.send('Network.responseReceived', {
|
|
243
|
-
requestId,
|
|
244
|
-
loaderId: metadata.loaderId || '',
|
|
245
|
-
timestamp: Date.now(),
|
|
246
|
-
type: metadata.type || 'Other',
|
|
247
|
-
response: {
|
|
248
|
-
url: metadata.url,
|
|
249
|
-
status: request.status,
|
|
250
|
-
statusText: request.statusText,
|
|
251
|
-
headers,
|
|
252
|
-
mimeType,
|
|
253
|
-
encodedDataLength: responseSize || 0,
|
|
254
|
-
responseTime: Date.now(),
|
|
255
|
-
},
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
XHRInterceptor.setResponseCallback(
|
|
261
|
-
(
|
|
262
|
-
status: number,
|
|
263
|
-
timeout: number,
|
|
264
|
-
response: string,
|
|
265
|
-
responseURL: string,
|
|
266
|
-
responseType: string,
|
|
267
|
-
request: XMLHttpRequest
|
|
268
|
-
) => {
|
|
269
|
-
const requestId = findRequestId(request);
|
|
270
|
-
if (!requestId) return;
|
|
271
|
-
|
|
272
|
-
const entry = networkRequestsRegistry.getEntry(requestId);
|
|
273
|
-
if (!entry) return;
|
|
274
|
-
|
|
275
|
-
const metadata = entry.metadata;
|
|
276
|
-
if (!metadata) return;
|
|
277
|
-
|
|
278
|
-
const endTime = Date.now();
|
|
279
|
-
const duration = endTime - metadata.startTime;
|
|
280
|
-
const dataLength = response ? response.length : 0;
|
|
281
|
-
|
|
282
|
-
// Update metadata with final data
|
|
283
|
-
networkRequestsRegistry.updateEntry(requestId, {
|
|
284
|
-
endTime,
|
|
285
|
-
duration,
|
|
286
|
-
dataLength,
|
|
287
|
-
encodedDataLength: dataLength,
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
// Check if request failed
|
|
291
|
-
if (status >= 400 || request.readyState === 0) {
|
|
292
|
-
const errorText = request.statusText || 'Request failed';
|
|
293
|
-
const canceled = request.readyState === 0;
|
|
294
|
-
|
|
295
|
-
// Update metadata
|
|
296
|
-
networkRequestsRegistry.updateEntry(requestId, {
|
|
297
|
-
status: 'failed',
|
|
298
|
-
errorText,
|
|
299
|
-
canceled,
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
// Send Network.loadingFailed event
|
|
303
|
-
pluginClient.send('Network.loadingFailed', {
|
|
304
|
-
requestId,
|
|
305
|
-
timestamp: endTime,
|
|
306
|
-
type: metadata.type || 'Other',
|
|
307
|
-
errorText,
|
|
308
|
-
canceled,
|
|
309
|
-
});
|
|
310
|
-
} else {
|
|
311
|
-
// Update metadata
|
|
312
|
-
networkRequestsRegistry.updateEntry(requestId, {
|
|
313
|
-
status: 'finished',
|
|
314
|
-
encodedDataLength: dataLength,
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
// Send Network.dataReceived event if there's data
|
|
318
|
-
if (dataLength > 0) {
|
|
319
|
-
pluginClient.send('Network.dataReceived', {
|
|
320
|
-
requestId,
|
|
321
|
-
timestamp: endTime,
|
|
322
|
-
dataLength,
|
|
323
|
-
encodedDataLength: dataLength,
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Send Network.loadingFinished event
|
|
328
|
-
pluginClient.send('Network.loadingFinished', {
|
|
329
|
-
requestId,
|
|
330
|
-
timestamp: endTime,
|
|
331
|
-
encodedDataLength: dataLength,
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
);
|
|
169
|
+
request.addEventListener('error', () => {
|
|
170
|
+
pluginClient.send('request-failed', {
|
|
171
|
+
requestId: requestId,
|
|
172
|
+
timestamp: Date.now() / 1000,
|
|
173
|
+
type: 'XHR',
|
|
174
|
+
error: 'Failed',
|
|
175
|
+
canceled: false,
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
request.addEventListener('abort', () => {
|
|
180
|
+
pluginClient.send('request-failed', {
|
|
181
|
+
requestId: requestId,
|
|
182
|
+
timestamp: Date.now() / 1000,
|
|
183
|
+
type: 'XHR',
|
|
184
|
+
error: 'Aborted',
|
|
185
|
+
canceled: true,
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
};
|
|
336
189
|
|
|
190
|
+
const enable = () => {
|
|
191
|
+
XHRInterceptor.disableInterception();
|
|
192
|
+
XHRInterceptor.setSendCallback(handleRequestSend);
|
|
337
193
|
XHRInterceptor.enableInterception();
|
|
338
194
|
};
|
|
339
195
|
|
|
@@ -355,22 +211,19 @@ export const getNetworkInspector = (
|
|
|
355
211
|
});
|
|
356
212
|
|
|
357
213
|
const handleBodySubscription = pluginClient.onMessage(
|
|
358
|
-
'
|
|
359
|
-
async (
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
if (!
|
|
214
|
+
'get-response-body',
|
|
215
|
+
async ({ requestId }) => {
|
|
216
|
+
const request = networkRequestsRegistry.getEntry(requestId);
|
|
217
|
+
|
|
218
|
+
if (!request) {
|
|
363
219
|
return;
|
|
364
220
|
}
|
|
365
221
|
|
|
366
|
-
const
|
|
367
|
-
const { body, base64Encoded } = await getResponseBody(request);
|
|
222
|
+
const body = await getResponseBody(request);
|
|
368
223
|
|
|
369
|
-
|
|
370
|
-
pluginClient.send('Network.responseBodyReceived', {
|
|
224
|
+
pluginClient.send('response-body', {
|
|
371
225
|
requestId,
|
|
372
226
|
body,
|
|
373
|
-
base64Encoded,
|
|
374
227
|
});
|
|
375
228
|
}
|
|
376
229
|
);
|
|
@@ -1,50 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
NetworkRequestId,
|
|
3
|
-
NetworkLoaderId,
|
|
4
|
-
NetworkResourceType,
|
|
5
|
-
NetworkRequest,
|
|
6
|
-
NetworkResponse,
|
|
7
|
-
NetworkInitiator,
|
|
8
|
-
} from '../types/client';
|
|
9
|
-
|
|
10
|
-
export type NetworkRequestMetadata = {
|
|
11
|
-
id: NetworkRequestId;
|
|
12
|
-
loaderId?: NetworkLoaderId;
|
|
13
|
-
documentURL?: string;
|
|
14
|
-
method: string;
|
|
15
|
-
url: string;
|
|
16
|
-
headers: Record<string, string>;
|
|
17
|
-
postData?: string;
|
|
18
|
-
hasPostData?: boolean;
|
|
19
|
-
type?: NetworkResourceType;
|
|
20
|
-
initiator?: NetworkInitiator;
|
|
21
|
-
startTime: number;
|
|
22
|
-
endTime?: number;
|
|
23
|
-
duration?: number;
|
|
24
|
-
status: 'pending' | 'loading' | 'finished' | 'failed';
|
|
25
|
-
response?: NetworkResponse;
|
|
26
|
-
errorText?: string;
|
|
27
|
-
canceled?: boolean;
|
|
28
|
-
encodedDataLength?: number;
|
|
29
|
-
dataLength?: number;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export type NetworkRegistryEntry = {
|
|
1
|
+
type NetworkRegistryEntry = {
|
|
33
2
|
id: string;
|
|
34
|
-
request: XMLHttpRequest;
|
|
35
|
-
metadata: NetworkRequestMetadata;
|
|
36
3
|
sentAt: number;
|
|
4
|
+
request: XMLHttpRequest;
|
|
37
5
|
};
|
|
38
6
|
|
|
39
7
|
export type NetworkRequestRegistry = {
|
|
40
|
-
addEntry: (
|
|
41
|
-
|
|
42
|
-
request: XMLHttpRequest,
|
|
43
|
-
metadata: Partial<NetworkRequestMetadata>
|
|
44
|
-
) => void;
|
|
45
|
-
getEntry: (id: string) => NetworkRegistryEntry | null;
|
|
46
|
-
updateEntry: (id: string, updates: Partial<NetworkRequestMetadata>) => void;
|
|
47
|
-
getAllEntries: () => Array<NetworkRegistryEntry>;
|
|
8
|
+
addEntry: (id: string, request: XMLHttpRequest) => void;
|
|
9
|
+
getEntry: (id: string) => XMLHttpRequest | null;
|
|
48
10
|
clear: () => void;
|
|
49
11
|
};
|
|
50
12
|
|
|
@@ -65,47 +27,17 @@ export const getNetworkRequestsRegistry = (): NetworkRequestRegistry => {
|
|
|
65
27
|
});
|
|
66
28
|
};
|
|
67
29
|
|
|
68
|
-
const addEntry = (
|
|
69
|
-
id: string,
|
|
70
|
-
request: XMLHttpRequest,
|
|
71
|
-
metadata: Partial<NetworkRequestMetadata>
|
|
72
|
-
) => {
|
|
30
|
+
const addEntry = (id: string, request: XMLHttpRequest): void => {
|
|
73
31
|
trimRegistry();
|
|
74
|
-
|
|
75
|
-
const fullMetadata: NetworkRequestMetadata = {
|
|
76
|
-
id,
|
|
77
|
-
method: metadata.method || 'GET',
|
|
78
|
-
url: metadata.url || '',
|
|
79
|
-
headers: metadata.headers || {},
|
|
80
|
-
startTime: metadata.startTime || Date.now(),
|
|
81
|
-
status: metadata.status || 'pending',
|
|
82
|
-
...metadata,
|
|
83
|
-
};
|
|
84
|
-
|
|
85
32
|
registry.set(id, {
|
|
86
33
|
id,
|
|
87
34
|
request,
|
|
88
|
-
metadata: fullMetadata,
|
|
89
35
|
sentAt: Date.now(),
|
|
90
36
|
});
|
|
91
37
|
};
|
|
92
38
|
|
|
93
|
-
const getEntry = (id: string) => {
|
|
94
|
-
return registry.get(id) ?? null;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const updateEntry = (
|
|
98
|
-
id: string,
|
|
99
|
-
updates: Partial<NetworkRequestMetadata>
|
|
100
|
-
) => {
|
|
101
|
-
const entry = registry.get(id);
|
|
102
|
-
if (entry) {
|
|
103
|
-
entry.metadata = { ...entry.metadata, ...updates };
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const getAllEntries = () => {
|
|
108
|
-
return Array.from(registry.values());
|
|
39
|
+
const getEntry = (id: string): XMLHttpRequest | null => {
|
|
40
|
+
return registry.get(id)?.request ?? null;
|
|
109
41
|
};
|
|
110
42
|
|
|
111
43
|
const clear = () => {
|
|
@@ -115,8 +47,6 @@ export const getNetworkRequestsRegistry = (): NetworkRequestRegistry => {
|
|
|
115
47
|
return {
|
|
116
48
|
addEntry,
|
|
117
49
|
getEntry,
|
|
118
|
-
updateEntry,
|
|
119
|
-
getAllEntries,
|
|
120
50
|
clear,
|
|
121
51
|
};
|
|
122
52
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
import { useRozeniteDevToolsClient } from '@rozenite/plugin-bridge';
|
|
3
3
|
import { getNetworkInspector } from './network-inspector';
|
|
4
|
-
import { NetworkActivityEventMap } from '../
|
|
4
|
+
import { NetworkActivityEventMap } from '../shared/client';
|
|
5
5
|
|
|
6
6
|
export const useNetworkActivityDevTools = () => {
|
|
7
7
|
const client = useRozeniteDevToolsClient<NetworkActivityEventMap>({
|
|
@@ -31,8 +31,8 @@ type XHRInterceptorRequestHeaderCallback = (
|
|
|
31
31
|
) => void;
|
|
32
32
|
|
|
33
33
|
type XHRInterceptorHeaderReceivedCallback = (
|
|
34
|
-
responseContentType: string |
|
|
35
|
-
responseSize: number |
|
|
34
|
+
responseContentType: string | undefined,
|
|
35
|
+
responseSize: number | undefined,
|
|
36
36
|
allHeaders: string,
|
|
37
37
|
request: XMLHttpRequest
|
|
38
38
|
) => void;
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
declare type BlobData = {
|
|
2
|
+
blobId: string;
|
|
3
|
+
lastModified?: number;
|
|
4
|
+
name?: string;
|
|
5
|
+
offset: number;
|
|
6
|
+
size: number;
|
|
7
|
+
type?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
1
10
|
declare global {
|
|
2
11
|
interface XMLHttpRequest {
|
|
3
12
|
_requestId?: number;
|
|
@@ -10,13 +19,14 @@ declare global {
|
|
|
10
19
|
_method?: string | null;
|
|
11
20
|
_perfKey?: string | null;
|
|
12
21
|
_responseType: ResponseType;
|
|
13
|
-
_response: string;
|
|
22
|
+
_response: string | BlobData;
|
|
14
23
|
_sent: boolean;
|
|
15
24
|
_url?: string | null;
|
|
16
25
|
_timedOut: boolean;
|
|
17
26
|
_trackingName?: string;
|
|
18
27
|
_incrementalEvents: boolean;
|
|
19
28
|
_startTime?: number | null;
|
|
29
|
+
responseHeaders?: { [key: string]: string };
|
|
20
30
|
}
|
|
21
31
|
}
|
|
22
32
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { RozeniteDevToolsClient } from '@rozenite/plugin-bridge';
|
|
2
|
+
|
|
3
|
+
export type RequestId = string;
|
|
4
|
+
export type Timestamp = number;
|
|
5
|
+
|
|
6
|
+
export type Request = {
|
|
7
|
+
url: string;
|
|
8
|
+
method: string;
|
|
9
|
+
headers: Record<string, string>;
|
|
10
|
+
postData?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type Response = {
|
|
14
|
+
url: string;
|
|
15
|
+
status: number;
|
|
16
|
+
statusText: string;
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
contentType: string;
|
|
19
|
+
size: number;
|
|
20
|
+
responseTime: Timestamp;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type Initiator = {
|
|
24
|
+
type: string;
|
|
25
|
+
url?: string;
|
|
26
|
+
lineNumber?: number;
|
|
27
|
+
columnNumber?: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type ResourceType = 'XHR' | 'Fetch' | 'Other';
|
|
31
|
+
|
|
32
|
+
export type NetworkActivityEventMap = {
|
|
33
|
+
// Control events
|
|
34
|
+
'network-enable': unknown;
|
|
35
|
+
'network-disable': unknown;
|
|
36
|
+
|
|
37
|
+
// Network request events
|
|
38
|
+
'request-sent': {
|
|
39
|
+
requestId: RequestId;
|
|
40
|
+
request: Request;
|
|
41
|
+
timestamp: Timestamp;
|
|
42
|
+
initiator: Initiator;
|
|
43
|
+
type: ResourceType;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
'response-received': {
|
|
47
|
+
requestId: RequestId;
|
|
48
|
+
timestamp: Timestamp;
|
|
49
|
+
type: ResourceType;
|
|
50
|
+
response: Response;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
'request-completed': {
|
|
54
|
+
requestId: RequestId;
|
|
55
|
+
timestamp: Timestamp;
|
|
56
|
+
duration: number;
|
|
57
|
+
size: number;
|
|
58
|
+
ttfb: number;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
'request-failed': {
|
|
62
|
+
requestId: RequestId;
|
|
63
|
+
timestamp: Timestamp;
|
|
64
|
+
type: ResourceType;
|
|
65
|
+
error: string;
|
|
66
|
+
canceled: boolean;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
'get-response-body': {
|
|
70
|
+
requestId: RequestId;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
'response-body': {
|
|
74
|
+
requestId: RequestId;
|
|
75
|
+
body: string | null;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export type NetworkActivityDevToolsClient =
|
|
80
|
+
RozeniteDevToolsClient<NetworkActivityEventMap>;
|