@rozenite/network-activity-plugin 1.0.0 → 1.2.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 +9 -0
- package/dist/App.html +1 -1
- package/dist/assets/{App-C6wCDVkW.js → App-o_iVtD-5.js} +50 -7
- package/dist/boot-recording.cjs +1092 -0
- package/dist/boot-recording.js +1091 -0
- package/dist/react-native.cjs +3 -0
- package/dist/react-native.d.ts +3 -0
- package/dist/react-native.js +5 -1
- package/dist/rozenite.json +1 -1
- package/dist/src/react-native/boot-recording.d.ts +41 -0
- package/dist/src/react-native/config.d.ts +7 -4
- package/dist/src/react-native/events-listener.d.ts +44 -0
- package/dist/src/react-native/http/http-inspector.d.ts +10 -0
- package/dist/src/react-native/http/http-utils.d.ts +15 -0
- package/dist/src/react-native/inspector.d.ts +7 -0
- package/dist/src/react-native/network-inspector.d.ts +16 -0
- package/dist/src/react-native/sse/sse-inspector.d.ts +4 -7
- package/dist/src/react-native/useHttpInspector.d.ts +3 -0
- package/dist/src/react-native/useSSEInspector.d.ts +3 -0
- package/dist/src/react-native/useWebSocketInspector.d.ts +3 -0
- package/dist/src/react-native/websocket/websocket-inspector.d.ts +4 -7
- package/dist/src/shared/client.d.ts +3 -98
- package/dist/src/shared/http-events.d.ts +106 -0
- package/dist/src/shared/sse-events.d.ts +1 -1
- package/dist/src/ui/state/hooks.d.ts +3 -3
- package/dist/src/ui/state/model.d.ts +10 -0
- package/dist/useNetworkActivityDevTools.cjs +112 -993
- package/dist/useNetworkActivityDevTools.js +110 -989
- package/package.json +4 -4
- package/react-native.ts +8 -0
- package/src/react-native/boot-recording.ts +90 -0
- package/src/react-native/config.ts +9 -4
- package/src/react-native/events-listener.ts +102 -0
- package/src/react-native/http/http-inspector.ts +174 -0
- package/src/react-native/http/http-utils.ts +217 -0
- package/src/react-native/inspector.ts +10 -0
- package/src/react-native/network-inspector.ts +78 -0
- package/src/react-native/sse/sse-inspector.ts +12 -10
- package/src/react-native/useHttpInspector.ts +59 -0
- package/src/react-native/useNetworkActivityDevTools.ts +60 -115
- package/src/react-native/useSSEInspector.ts +35 -0
- package/src/react-native/useWebSocketInspector.ts +35 -0
- package/src/react-native/websocket/websocket-inspector.ts +18 -10
- package/src/shared/client.ts +4 -132
- package/src/shared/http-events.ts +140 -0
- package/src/shared/sse-events.ts +1 -1
- package/src/ui/components/RequestList.tsx +18 -6
- package/src/ui/components/Toolbar.tsx +3 -2
- package/src/ui/state/derived.ts +9 -3
- package/src/ui/state/model.ts +10 -0
- package/src/ui/state/store.ts +34 -3
- package/dist/src/react-native/http/network-inspector.d.ts +0 -8
- package/src/react-native/http/network-inspector.ts +0 -388
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
export type HttpHeaders = Record<string, string | string[]>;
|
|
2
|
+
export type XHRHeaders = NonNullable<XMLHttpRequest['responseHeaders']>;
|
|
3
|
+
|
|
4
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD';
|
|
5
|
+
|
|
6
|
+
export type RequestId = string;
|
|
7
|
+
export type Timestamp = number;
|
|
8
|
+
|
|
9
|
+
export type XHRPostData =
|
|
10
|
+
| string
|
|
11
|
+
| Blob
|
|
12
|
+
| FormData
|
|
13
|
+
| ArrayBuffer
|
|
14
|
+
| ArrayBufferView
|
|
15
|
+
| unknown
|
|
16
|
+
| null
|
|
17
|
+
| undefined;
|
|
18
|
+
|
|
19
|
+
export type RequestTextPostData = {
|
|
20
|
+
type: 'text';
|
|
21
|
+
value: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type RequestBinaryPostData = {
|
|
25
|
+
type: 'binary';
|
|
26
|
+
value: {
|
|
27
|
+
size: number;
|
|
28
|
+
type?: string;
|
|
29
|
+
name?: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type RequestFormDataPostData = {
|
|
34
|
+
type: 'form-data';
|
|
35
|
+
value: Record<string, RequestTextPostData | RequestBinaryPostData>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type RequestPostData =
|
|
39
|
+
| RequestTextPostData
|
|
40
|
+
| RequestFormDataPostData
|
|
41
|
+
| RequestBinaryPostData
|
|
42
|
+
| null
|
|
43
|
+
| undefined;
|
|
44
|
+
|
|
45
|
+
export type Cookie = {
|
|
46
|
+
name: string;
|
|
47
|
+
value: string;
|
|
48
|
+
domain?: string;
|
|
49
|
+
path?: string;
|
|
50
|
+
expires?: string;
|
|
51
|
+
maxAge?: string;
|
|
52
|
+
secure?: boolean;
|
|
53
|
+
httpOnly?: boolean;
|
|
54
|
+
sameSite?: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type Request = {
|
|
58
|
+
url: string;
|
|
59
|
+
method: HttpMethod;
|
|
60
|
+
headers: HttpHeaders;
|
|
61
|
+
postData?: RequestPostData;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type Response = {
|
|
65
|
+
url: string;
|
|
66
|
+
status: number;
|
|
67
|
+
statusText: string;
|
|
68
|
+
headers: HttpHeaders;
|
|
69
|
+
contentType: string;
|
|
70
|
+
size: number | null;
|
|
71
|
+
responseTime: Timestamp;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export type Initiator = {
|
|
75
|
+
type: string;
|
|
76
|
+
url?: string;
|
|
77
|
+
lineNumber?: number;
|
|
78
|
+
columnNumber?: number;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type ResourceType = 'XHR' | 'Fetch' | 'Other';
|
|
82
|
+
|
|
83
|
+
export type RequestOverride = {
|
|
84
|
+
status?: number;
|
|
85
|
+
body?: string;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export type HttpEventMap = {
|
|
89
|
+
'request-sent': {
|
|
90
|
+
requestId: RequestId;
|
|
91
|
+
request: Request;
|
|
92
|
+
timestamp: Timestamp;
|
|
93
|
+
initiator: Initiator;
|
|
94
|
+
type: ResourceType;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
'response-received': {
|
|
98
|
+
requestId: RequestId;
|
|
99
|
+
timestamp: Timestamp;
|
|
100
|
+
type: ResourceType;
|
|
101
|
+
response: Response;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
'request-completed': {
|
|
105
|
+
requestId: RequestId;
|
|
106
|
+
timestamp: Timestamp;
|
|
107
|
+
duration: number;
|
|
108
|
+
size: number | null;
|
|
109
|
+
ttfb: number;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
'request-failed': {
|
|
113
|
+
requestId: RequestId;
|
|
114
|
+
timestamp: Timestamp;
|
|
115
|
+
type: ResourceType;
|
|
116
|
+
error: string;
|
|
117
|
+
canceled: boolean;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
'request-progress': {
|
|
121
|
+
requestId: RequestId;
|
|
122
|
+
timestamp: Timestamp;
|
|
123
|
+
loaded: number;
|
|
124
|
+
total: number;
|
|
125
|
+
lengthComputable: boolean;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
'get-response-body': {
|
|
129
|
+
requestId: RequestId;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
'response-body': {
|
|
133
|
+
requestId: RequestId;
|
|
134
|
+
body: string | null;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
'set-overrides': {
|
|
138
|
+
overrides: [string, RequestOverride][];
|
|
139
|
+
};
|
|
140
|
+
};
|
package/src/shared/sse-events.ts
CHANGED
|
@@ -61,7 +61,7 @@ const formatStartTime = (startTime: number): string => {
|
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
const extractDomainAndPath = (
|
|
64
|
-
url: string
|
|
64
|
+
url: string,
|
|
65
65
|
): { domain: string; path: string } => {
|
|
66
66
|
try {
|
|
67
67
|
const { hostname, pathname, search, hash, port } = new URL(url);
|
|
@@ -80,7 +80,7 @@ const generateName = (url: string, showEntirePathName = false): string => {
|
|
|
80
80
|
const urlObj = new URL(url);
|
|
81
81
|
const pathname = urlObj.pathname;
|
|
82
82
|
const filename = showEntirePathName ? undefined : pathname.split('/').pop();
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
return filename || pathname || urlObj.hostname;
|
|
85
85
|
} catch {
|
|
86
86
|
return url;
|
|
@@ -128,17 +128,25 @@ const sortTime: SortingFn<NetworkRequest> = (rowA, rowB, columnId) => {
|
|
|
128
128
|
const processNetworkRequests = (
|
|
129
129
|
processedRequests: ProcessedRequest[],
|
|
130
130
|
overrides: Map<string, RequestOverride>,
|
|
131
|
-
showEntirePathAsName = false
|
|
131
|
+
showEntirePathAsName = false,
|
|
132
132
|
): NetworkRequest[] => {
|
|
133
133
|
return processedRequests.map((request): NetworkRequest => {
|
|
134
134
|
const { domain, path } = extractDomainAndPath(request.name);
|
|
135
135
|
const duration = request.duration || 0;
|
|
136
136
|
const hasOverride = overrides.has(request.name);
|
|
137
137
|
|
|
138
|
+
let statusDisplay: string | number = request.httpStatus || request.status;
|
|
139
|
+
if (request.status === 'loading' && request.progress?.lengthComputable) {
|
|
140
|
+
const percentage = Math.round(
|
|
141
|
+
(request.progress.loaded / request.progress.total) * 100,
|
|
142
|
+
);
|
|
143
|
+
statusDisplay = `${percentage}%`;
|
|
144
|
+
}
|
|
145
|
+
|
|
138
146
|
return {
|
|
139
147
|
id: request.id,
|
|
140
148
|
name: generateName(request.name, showEntirePathAsName),
|
|
141
|
-
status:
|
|
149
|
+
status: statusDisplay,
|
|
142
150
|
method: request.method,
|
|
143
151
|
domain,
|
|
144
152
|
path,
|
|
@@ -254,7 +262,11 @@ export const RequestList = ({ filter }: RequestListProps) => {
|
|
|
254
262
|
}, [processedRequests, filter]);
|
|
255
263
|
|
|
256
264
|
const requests = useMemo(() => {
|
|
257
|
-
return processNetworkRequests(
|
|
265
|
+
return processNetworkRequests(
|
|
266
|
+
filteredRequests,
|
|
267
|
+
overrides,
|
|
268
|
+
clientUISettings?.showUrlAsName,
|
|
269
|
+
);
|
|
258
270
|
}, [filteredRequests, overrides, clientUISettings?.showUrlAsName]);
|
|
259
271
|
|
|
260
272
|
const table = useReactTable({
|
|
@@ -294,7 +306,7 @@ export const RequestList = ({ filter }: RequestListProps) => {
|
|
|
294
306
|
? null
|
|
295
307
|
: flexRender(
|
|
296
308
|
header.column.columnDef.header,
|
|
297
|
-
header.getContext()
|
|
309
|
+
header.getContext(),
|
|
298
310
|
)}
|
|
299
311
|
{header.column.getCanSort() && (
|
|
300
312
|
<span className="text-gray-500">
|
|
@@ -25,11 +25,12 @@ export const Toolbar = () => {
|
|
|
25
25
|
? 'text-red-400 hover:text-red-300'
|
|
26
26
|
: 'text-gray-400 hover:text-blue-400'
|
|
27
27
|
}`}
|
|
28
|
+
title={isRecording ? 'Stop recording' : 'Start recording'}
|
|
28
29
|
>
|
|
29
30
|
{isRecording ? (
|
|
30
|
-
<
|
|
31
|
+
<Square className="h-4 w-4 fill-current" />
|
|
31
32
|
) : (
|
|
32
|
-
<
|
|
33
|
+
<Circle className="h-4 w-4 fill-current" />
|
|
33
34
|
)}
|
|
34
35
|
</Button>
|
|
35
36
|
<Button
|
package/src/ui/state/derived.ts
CHANGED
|
@@ -21,9 +21,10 @@ export const getProcessedRequests = memoize((state: NetworkActivityState) => {
|
|
|
21
21
|
status: httpEntry.status,
|
|
22
22
|
timestamp: httpEntry.timestamp,
|
|
23
23
|
duration: httpEntry.duration,
|
|
24
|
-
size: httpEntry.size,
|
|
24
|
+
size: httpEntry.size ?? null,
|
|
25
25
|
method: httpEntry.request.method,
|
|
26
26
|
httpStatus: httpEntry.response?.status,
|
|
27
|
+
progress: httpEntry.progress,
|
|
27
28
|
});
|
|
28
29
|
} else if (entry.type === 'websocket') {
|
|
29
30
|
const wsEntry = entry as WebSocketNetworkEntry;
|
|
@@ -34,6 +35,7 @@ export const getProcessedRequests = memoize((state: NetworkActivityState) => {
|
|
|
34
35
|
status: wsEntry.status,
|
|
35
36
|
timestamp: wsEntry.timestamp,
|
|
36
37
|
duration: wsEntry.duration,
|
|
38
|
+
size: null,
|
|
37
39
|
method: 'WS',
|
|
38
40
|
httpStatus: 0,
|
|
39
41
|
});
|
|
@@ -46,6 +48,7 @@ export const getProcessedRequests = memoize((state: NetworkActivityState) => {
|
|
|
46
48
|
status: sseEntry.status,
|
|
47
49
|
timestamp: sseEntry.timestamp,
|
|
48
50
|
duration: sseEntry.duration,
|
|
51
|
+
size: null,
|
|
49
52
|
method: 'SSE',
|
|
50
53
|
httpStatus: 0,
|
|
51
54
|
});
|
|
@@ -62,7 +65,7 @@ export const getSelectedRequest = memoize((state: NetworkActivityState) => {
|
|
|
62
65
|
});
|
|
63
66
|
|
|
64
67
|
export const getRequestSummary = (
|
|
65
|
-
requestId: string
|
|
68
|
+
requestId: string,
|
|
66
69
|
): ((state: NetworkActivityState) => ProcessedRequest | null) =>
|
|
67
70
|
memoize((state: NetworkActivityState) => {
|
|
68
71
|
const { networkEntries } = state;
|
|
@@ -78,9 +81,10 @@ export const getRequestSummary = (
|
|
|
78
81
|
status: httpEntry.status,
|
|
79
82
|
timestamp: httpEntry.timestamp,
|
|
80
83
|
duration: httpEntry.duration,
|
|
81
|
-
size: httpEntry.size,
|
|
84
|
+
size: httpEntry.size ?? null,
|
|
82
85
|
method: httpEntry.request.method,
|
|
83
86
|
httpStatus: httpEntry.response?.status || 0,
|
|
87
|
+
progress: httpEntry.progress,
|
|
84
88
|
};
|
|
85
89
|
} else if (entry.type === 'websocket') {
|
|
86
90
|
const wsEntry = entry as WebSocketNetworkEntry;
|
|
@@ -91,6 +95,7 @@ export const getRequestSummary = (
|
|
|
91
95
|
status: wsEntry.status,
|
|
92
96
|
timestamp: wsEntry.timestamp,
|
|
93
97
|
duration: wsEntry.duration,
|
|
98
|
+
size: null,
|
|
94
99
|
method: 'WS',
|
|
95
100
|
httpStatus: 0,
|
|
96
101
|
};
|
|
@@ -103,6 +108,7 @@ export const getRequestSummary = (
|
|
|
103
108
|
status: sseEntry.status,
|
|
104
109
|
timestamp: sseEntry.timestamp,
|
|
105
110
|
duration: sseEntry.duration,
|
|
111
|
+
size: null,
|
|
106
112
|
method: 'SSE',
|
|
107
113
|
httpStatus: 0,
|
|
108
114
|
};
|
package/src/ui/state/model.ts
CHANGED
|
@@ -58,6 +58,11 @@ export type HttpNetworkEntry = {
|
|
|
58
58
|
size?: number;
|
|
59
59
|
initiator?: Initiator;
|
|
60
60
|
resourceType?: ResourceType;
|
|
61
|
+
progress?: {
|
|
62
|
+
loaded: number;
|
|
63
|
+
total: number;
|
|
64
|
+
lengthComputable: boolean;
|
|
65
|
+
};
|
|
61
66
|
};
|
|
62
67
|
|
|
63
68
|
/* SSE */
|
|
@@ -137,4 +142,9 @@ export type ProcessedRequest = {
|
|
|
137
142
|
size: number | null;
|
|
138
143
|
method: HttpMethod | 'WS' | 'SSE';
|
|
139
144
|
httpStatus?: number;
|
|
145
|
+
progress?: {
|
|
146
|
+
loaded: number;
|
|
147
|
+
total: number;
|
|
148
|
+
lengthComputable: boolean;
|
|
149
|
+
};
|
|
140
150
|
};
|
package/src/ui/state/store.ts
CHANGED
|
@@ -65,7 +65,7 @@ export const createNetworkActivityStore = () =>
|
|
|
65
65
|
persist(
|
|
66
66
|
(set, get) => ({
|
|
67
67
|
// Initial state
|
|
68
|
-
isRecording:
|
|
68
|
+
isRecording: true,
|
|
69
69
|
selectedRequestId: null,
|
|
70
70
|
networkEntries: new Map(),
|
|
71
71
|
websocketMessages: new Map(),
|
|
@@ -168,6 +168,33 @@ export const createNetworkActivityStore = () =>
|
|
|
168
168
|
break;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
case 'request-progress': {
|
|
172
|
+
const eventData =
|
|
173
|
+
data as NetworkActivityEventMap['request-progress'];
|
|
174
|
+
set((state) => {
|
|
175
|
+
const entry = state.networkEntries.get(eventData.requestId);
|
|
176
|
+
if (!entry || entry.type !== 'http') {
|
|
177
|
+
return state;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const httpEntry = entry as HttpNetworkEntry;
|
|
181
|
+
const updatedEntry: HttpNetworkEntry = {
|
|
182
|
+
...httpEntry,
|
|
183
|
+
status: 'loading',
|
|
184
|
+
progress: {
|
|
185
|
+
loaded: eventData.loaded,
|
|
186
|
+
total: eventData.total,
|
|
187
|
+
lengthComputable: eventData.lengthComputable,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const newEntries = new Map(state.networkEntries);
|
|
192
|
+
newEntries.set(eventData.requestId, updatedEntry);
|
|
193
|
+
return { networkEntries: newEntries };
|
|
194
|
+
});
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
|
|
171
198
|
case 'response-received': {
|
|
172
199
|
const eventData =
|
|
173
200
|
data as NetworkActivityEventMap['response-received'];
|
|
@@ -559,6 +586,9 @@ export const createNetworkActivityStore = () =>
|
|
|
559
586
|
client.onMessage('request-sent', (data) =>
|
|
560
587
|
handleEvent('request-sent', data)
|
|
561
588
|
),
|
|
589
|
+
client.onMessage('request-progress', (data) =>
|
|
590
|
+
handleEvent('request-progress', data)
|
|
591
|
+
),
|
|
562
592
|
client.onMessage('response-received', (data) =>
|
|
563
593
|
handleEvent('response-received', data)
|
|
564
594
|
),
|
|
@@ -654,9 +684,10 @@ export const createNetworkActivityStore = () =>
|
|
|
654
684
|
typeof value === 'object' &&
|
|
655
685
|
value !== null &&
|
|
656
686
|
'_type' in value &&
|
|
657
|
-
value._type === 'map'
|
|
687
|
+
value._type === 'map' &&
|
|
688
|
+
'value' in value
|
|
658
689
|
) {
|
|
659
|
-
return new Map(value.value);
|
|
690
|
+
return new Map(value.value as [string, RequestOverride][]);
|
|
660
691
|
}
|
|
661
692
|
return value;
|
|
662
693
|
},
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { NetworkActivityDevToolsClient } from '../../shared/client';
|
|
2
|
-
export type NetworkInspector = {
|
|
3
|
-
enable: () => void;
|
|
4
|
-
disable: () => void;
|
|
5
|
-
isEnabled: () => boolean;
|
|
6
|
-
dispose: () => void;
|
|
7
|
-
};
|
|
8
|
-
export declare const getNetworkInspector: (pluginClient: NetworkActivityDevToolsClient) => NetworkInspector;
|