@rozenite/network-activity-plugin 1.0.0-alpha.8 → 1.0.0-alpha.9
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/App.html +2 -2
- package/dist/assets/{App-lNMijPJ4.js → App-CA1Fbh0I.js} +11995 -10804
- package/dist/assets/{App-R2ZMH9wJ.css → App-DoHQsY5s.css} +46 -0
- package/dist/event-source.cjs +22 -0
- package/dist/event-source.js +23 -0
- package/dist/rozenite.json +1 -1
- package/dist/src/react-native/{network-inspector.d.ts → http/network-inspector.d.ts} +1 -1
- package/dist/src/react-native/sse/event-source.d.ts +2 -0
- package/dist/src/react-native/sse/sse-inspector.d.ts +9 -0
- package/dist/src/react-native/sse/sse-interceptor.d.ts +36 -0
- package/dist/src/react-native/sse/types.d.ts +6 -0
- package/dist/src/react-native/utils.d.ts +6 -0
- package/dist/src/react-native/websocket/websocket-inspector.d.ts +9 -0
- package/dist/src/react-native/websocket/websocket-interceptor.d.ts +74 -0
- package/dist/src/shared/client.d.ts +5 -2
- package/dist/src/shared/sse-events.d.ts +35 -0
- package/dist/src/shared/websocket-events.d.ts +60 -0
- package/dist/src/ui/components/Badge.d.ts +1 -1
- package/dist/src/ui/components/Button.d.ts +1 -1
- package/dist/src/ui/components/JsonTreeCopyableItem.d.ts +7 -0
- package/dist/src/ui/components/RequestList.d.ts +6 -26
- package/dist/src/ui/components/SidePanel.d.ts +1 -0
- package/dist/src/ui/components/Toolbar.d.ts +1 -0
- package/dist/src/ui/hooks/useCopyToClipboard.d.ts +4 -0
- package/dist/src/ui/state/derived.d.ts +5 -0
- package/dist/src/ui/state/hooks.d.ts +17 -0
- package/dist/src/ui/state/model.d.ts +98 -0
- package/dist/src/ui/state/store.d.ts +24 -0
- package/dist/src/ui/tabs/CookiesTab.d.ts +3 -6
- package/dist/src/ui/tabs/HeadersTab.d.ts +3 -15
- package/dist/src/ui/tabs/MessagesTab.d.ts +5 -0
- package/dist/src/ui/tabs/RequestTab.d.ts +2 -7
- package/dist/src/ui/tabs/ResponseTab.d.ts +2 -8
- package/dist/src/ui/tabs/SSEMessagesTab.d.ts +5 -0
- package/dist/src/ui/tabs/TimingTab.d.ts +3 -5
- package/dist/src/ui/types.d.ts +4 -1
- package/dist/src/ui/utils/assert.d.ts +1 -0
- package/dist/src/ui/utils/copyToClipboard.d.ts +1 -0
- package/dist/src/ui/utils/getId.d.ts +1 -0
- package/dist/src/ui/utils/getStatusColor.d.ts +1 -0
- package/dist/useNetworkActivityDevTools.cjs +423 -34
- package/dist/useNetworkActivityDevTools.js +421 -34
- package/package.json +19 -8
- package/src/react-native/{network-inspector.ts → http/network-inspector.ts} +13 -34
- package/src/react-native/{xml-request.d.ts → http/xml-request.d.ts} +1 -0
- package/src/react-native/sse/event-source.ts +25 -0
- package/src/react-native/sse/sse-inspector.ts +117 -0
- package/src/react-native/sse/sse-interceptor.ts +162 -0
- package/src/react-native/sse/types.ts +9 -0
- package/src/react-native/useNetworkActivityDevTools.ts +75 -1
- package/src/react-native/utils.ts +43 -0
- package/src/react-native/websocket/websocket-inspector.ts +180 -0
- package/src/react-native/websocket/websocket-interceptor.d.ts +4 -0
- package/src/react-native/websocket/websocket-interceptor.ts +166 -0
- package/src/shared/client.ts +6 -2
- package/src/shared/sse-events.ts +44 -0
- package/src/shared/websocket-events.ts +79 -0
- package/src/ui/components/JsonTree.tsx +13 -0
- package/src/ui/components/JsonTreeCopyableItem.tsx +33 -0
- package/src/ui/components/RequestList.tsx +42 -124
- package/src/ui/components/SidePanel.tsx +323 -0
- package/src/ui/components/Tabs.tsx +1 -1
- package/src/ui/components/Toolbar.tsx +45 -0
- package/src/ui/hooks/useCopyToClipboard.ts +28 -0
- package/src/ui/state/derived.ts +112 -0
- package/src/ui/state/hooks.ts +44 -0
- package/src/ui/state/model.ts +129 -0
- package/src/ui/state/store.ts +559 -0
- package/src/ui/tabs/CookiesTab.tsx +162 -176
- package/src/ui/tabs/HeadersTab.tsx +23 -30
- package/src/ui/tabs/MessagesTab.tsx +276 -0
- package/src/ui/tabs/RequestTab.tsx +8 -13
- package/src/ui/tabs/ResponseTab.tsx +6 -10
- package/src/ui/tabs/SSEMessagesTab.tsx +213 -0
- package/src/ui/tabs/TimingTab.tsx +30 -43
- package/src/ui/types.ts +4 -1
- package/src/ui/utils/assert.ts +5 -0
- package/src/ui/utils/copyToClipboard.ts +3 -0
- package/src/ui/utils/getId.ts +10 -0
- package/src/ui/utils/getStatusColor.ts +15 -0
- package/src/ui/views/InspectorView.tsx +24 -320
- package/tailwind.config.ts +3 -0
- package/vite.config.ts +12 -0
- /package/dist/src/react-native/{network-requests-registry.d.ts → http/network-requests-registry.d.ts} +0 -0
- /package/dist/src/react-native/{xhr-interceptor.d.ts → http/xhr-interceptor.d.ts} +0 -0
- /package/src/react-native/{network-requests-registry.ts → http/network-requests-registry.ts} +0 -0
- /package/src/react-native/{xhr-interceptor.ts → http/xhr-interceptor.ts} +0 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
import { createStore } from 'zustand';
|
|
2
|
+
import {
|
|
3
|
+
NetworkActivityDevToolsClient,
|
|
4
|
+
NetworkActivityEventMap,
|
|
5
|
+
RequestId,
|
|
6
|
+
} from '../../shared/client';
|
|
7
|
+
import {
|
|
8
|
+
NetworkEntry,
|
|
9
|
+
HttpNetworkEntry,
|
|
10
|
+
WebSocketNetworkEntry,
|
|
11
|
+
WebSocketMessage,
|
|
12
|
+
SSENetworkEntry,
|
|
13
|
+
SSEMessage,
|
|
14
|
+
} from './model';
|
|
15
|
+
import { getHttpHeaderValue } from '../utils/getHttpHeaderValue';
|
|
16
|
+
import { getId } from '../utils/getId';
|
|
17
|
+
import { assert } from '../utils/assert';
|
|
18
|
+
|
|
19
|
+
const MAX_WEBSOCKET_MESSAGES_PER_CONNECTION = 32;
|
|
20
|
+
const MAX_SSE_MESSAGES_PER_CONNECTION = 32;
|
|
21
|
+
|
|
22
|
+
export interface NetworkActivityState {
|
|
23
|
+
// State
|
|
24
|
+
isRecording: boolean;
|
|
25
|
+
selectedRequestId: RequestId | null;
|
|
26
|
+
networkEntries: Map<RequestId, NetworkEntry>;
|
|
27
|
+
websocketMessages: Map<RequestId, WebSocketMessage[]>;
|
|
28
|
+
|
|
29
|
+
// Internal state (not exposed in interface)
|
|
30
|
+
_unsubscribeFunctions?: Array<{ remove: () => void }>;
|
|
31
|
+
_client?: NetworkActivityDevToolsClient;
|
|
32
|
+
|
|
33
|
+
// Actions
|
|
34
|
+
actions: {
|
|
35
|
+
setRecording: (isRecording: boolean) => void;
|
|
36
|
+
setSelectedRequest: (requestId: RequestId | null) => void;
|
|
37
|
+
clearRequests: () => void;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Event handling
|
|
41
|
+
handleEvent: <K extends keyof NetworkActivityEventMap>(
|
|
42
|
+
eventType: K,
|
|
43
|
+
data: NetworkActivityEventMap[K]
|
|
44
|
+
) => void;
|
|
45
|
+
|
|
46
|
+
// Client management
|
|
47
|
+
client: {
|
|
48
|
+
setupClient: (client: NetworkActivityDevToolsClient) => void;
|
|
49
|
+
cleanupClient: () => void;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const createNetworkActivityStore = () =>
|
|
54
|
+
createStore<NetworkActivityState>((set, get) => ({
|
|
55
|
+
// Initial state
|
|
56
|
+
isRecording: false,
|
|
57
|
+
selectedRequestId: null,
|
|
58
|
+
networkEntries: new Map(),
|
|
59
|
+
websocketMessages: new Map(),
|
|
60
|
+
|
|
61
|
+
// Actions
|
|
62
|
+
actions: {
|
|
63
|
+
setRecording: (isRecording: boolean) => {
|
|
64
|
+
const { _client } = get();
|
|
65
|
+
assert(!!_client, 'Client is not set');
|
|
66
|
+
|
|
67
|
+
_client.send(isRecording ? 'network-enable' : 'network-disable', {});
|
|
68
|
+
set({ isRecording });
|
|
69
|
+
},
|
|
70
|
+
setSelectedRequest: (requestId: RequestId | null) =>
|
|
71
|
+
set({ selectedRequestId: requestId }),
|
|
72
|
+
clearRequests: () =>
|
|
73
|
+
set({
|
|
74
|
+
networkEntries: new Map(),
|
|
75
|
+
websocketMessages: new Map(),
|
|
76
|
+
selectedRequestId: null,
|
|
77
|
+
}),
|
|
78
|
+
},
|
|
79
|
+
// Event handling
|
|
80
|
+
handleEvent: <K extends keyof NetworkActivityEventMap>(
|
|
81
|
+
eventType: K,
|
|
82
|
+
data: NetworkActivityEventMap[K]
|
|
83
|
+
) => {
|
|
84
|
+
switch (eventType) {
|
|
85
|
+
case 'request-sent': {
|
|
86
|
+
const eventData = data as NetworkActivityEventMap['request-sent'];
|
|
87
|
+
set((state) => {
|
|
88
|
+
const entry: HttpNetworkEntry = {
|
|
89
|
+
id: eventData.requestId,
|
|
90
|
+
type: 'http',
|
|
91
|
+
timestamp: eventData.timestamp,
|
|
92
|
+
request: {
|
|
93
|
+
url: eventData.request.url,
|
|
94
|
+
method: eventData.request.method,
|
|
95
|
+
headers: eventData.request.headers,
|
|
96
|
+
body: eventData.request.postData
|
|
97
|
+
? {
|
|
98
|
+
type:
|
|
99
|
+
getHttpHeaderValue(
|
|
100
|
+
eventData.request.headers,
|
|
101
|
+
'content-type'
|
|
102
|
+
)?.split(';')[0] || 'text/plain',
|
|
103
|
+
data: eventData.request.postData,
|
|
104
|
+
}
|
|
105
|
+
: undefined,
|
|
106
|
+
},
|
|
107
|
+
status: 'pending',
|
|
108
|
+
initiator: eventData.initiator,
|
|
109
|
+
resourceType: eventData.type,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const newEntries = new Map(state.networkEntries);
|
|
113
|
+
newEntries.set(eventData.requestId, entry);
|
|
114
|
+
return { networkEntries: newEntries };
|
|
115
|
+
});
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
case 'response-received': {
|
|
120
|
+
const eventData =
|
|
121
|
+
data as NetworkActivityEventMap['response-received'];
|
|
122
|
+
set((state) => {
|
|
123
|
+
const entry = state.networkEntries.get(eventData.requestId);
|
|
124
|
+
if (!entry || entry.type !== 'http') return state;
|
|
125
|
+
|
|
126
|
+
const httpEntry = entry as HttpNetworkEntry;
|
|
127
|
+
const updatedEntry: HttpNetworkEntry = {
|
|
128
|
+
...httpEntry,
|
|
129
|
+
status: 'loading',
|
|
130
|
+
response: eventData.response,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const newEntries = new Map(state.networkEntries);
|
|
134
|
+
newEntries.set(eventData.requestId, updatedEntry);
|
|
135
|
+
return { networkEntries: newEntries };
|
|
136
|
+
});
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
case 'request-completed': {
|
|
141
|
+
const eventData =
|
|
142
|
+
data as NetworkActivityEventMap['request-completed'];
|
|
143
|
+
set((state) => {
|
|
144
|
+
const entry = state.networkEntries.get(eventData.requestId);
|
|
145
|
+
if (!entry || entry.type !== 'http') return state;
|
|
146
|
+
|
|
147
|
+
const httpEntry = entry as HttpNetworkEntry;
|
|
148
|
+
const updatedEntry: HttpNetworkEntry = {
|
|
149
|
+
...httpEntry,
|
|
150
|
+
status: 'finished',
|
|
151
|
+
duration: eventData.duration,
|
|
152
|
+
size: eventData.size,
|
|
153
|
+
ttfb: eventData.ttfb,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const newEntries = new Map(state.networkEntries);
|
|
157
|
+
newEntries.set(eventData.requestId, updatedEntry);
|
|
158
|
+
return { networkEntries: newEntries };
|
|
159
|
+
});
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
case 'request-failed': {
|
|
164
|
+
const eventData = data as NetworkActivityEventMap['request-failed'];
|
|
165
|
+
set((state) => {
|
|
166
|
+
const entry = state.networkEntries.get(eventData.requestId);
|
|
167
|
+
if (!entry || entry.type !== 'http') return state;
|
|
168
|
+
|
|
169
|
+
const httpEntry = entry as HttpNetworkEntry;
|
|
170
|
+
const updatedEntry: HttpNetworkEntry = {
|
|
171
|
+
...httpEntry,
|
|
172
|
+
status: 'failed',
|
|
173
|
+
error: eventData.error,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const newEntries = new Map(state.networkEntries);
|
|
177
|
+
newEntries.set(eventData.requestId, updatedEntry);
|
|
178
|
+
return { networkEntries: newEntries };
|
|
179
|
+
});
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
case 'response-body': {
|
|
184
|
+
const eventData = data as NetworkActivityEventMap['response-body'];
|
|
185
|
+
set((state) => {
|
|
186
|
+
const entry = state.networkEntries.get(eventData.requestId);
|
|
187
|
+
if (!entry || entry.type !== 'http') return state;
|
|
188
|
+
|
|
189
|
+
const httpEntry = entry as HttpNetworkEntry;
|
|
190
|
+
const updatedEntry: HttpNetworkEntry = {
|
|
191
|
+
...httpEntry,
|
|
192
|
+
response: httpEntry.response
|
|
193
|
+
? {
|
|
194
|
+
...httpEntry.response,
|
|
195
|
+
body: eventData.body
|
|
196
|
+
? {
|
|
197
|
+
type:
|
|
198
|
+
getHttpHeaderValue(
|
|
199
|
+
httpEntry.response?.headers ?? {},
|
|
200
|
+
'content-type'
|
|
201
|
+
)?.split(';')[0] || 'text/plain',
|
|
202
|
+
data: eventData.body,
|
|
203
|
+
}
|
|
204
|
+
: undefined,
|
|
205
|
+
}
|
|
206
|
+
: undefined,
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const newEntries = new Map(state.networkEntries);
|
|
210
|
+
newEntries.set(eventData.requestId, updatedEntry);
|
|
211
|
+
return { networkEntries: newEntries };
|
|
212
|
+
});
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
case 'websocket-connect': {
|
|
217
|
+
const eventData =
|
|
218
|
+
data as NetworkActivityEventMap['websocket-connect'];
|
|
219
|
+
set((state) => {
|
|
220
|
+
const entry: WebSocketNetworkEntry = {
|
|
221
|
+
id: `ws-${eventData.socketId}`,
|
|
222
|
+
type: 'websocket',
|
|
223
|
+
timestamp: eventData.timestamp,
|
|
224
|
+
connection: {
|
|
225
|
+
url: eventData.url,
|
|
226
|
+
socketId: eventData.socketId,
|
|
227
|
+
protocols: eventData.protocols || undefined,
|
|
228
|
+
options: eventData.options,
|
|
229
|
+
},
|
|
230
|
+
status: 'connecting',
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const newEntries = new Map(state.networkEntries);
|
|
234
|
+
newEntries.set(entry.id, entry);
|
|
235
|
+
|
|
236
|
+
const newMessages = new Map(state.websocketMessages);
|
|
237
|
+
newMessages.set(entry.id, []);
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
networkEntries: newEntries,
|
|
241
|
+
websocketMessages: newMessages,
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
case 'websocket-open': {
|
|
248
|
+
const eventData = data as NetworkActivityEventMap['websocket-open'];
|
|
249
|
+
set((state) => {
|
|
250
|
+
const entry = state.networkEntries.get(`ws-${eventData.socketId}`);
|
|
251
|
+
if (!entry || entry.type !== 'websocket') return state;
|
|
252
|
+
|
|
253
|
+
const wsEntry = entry as WebSocketNetworkEntry;
|
|
254
|
+
const updatedEntry: WebSocketNetworkEntry = {
|
|
255
|
+
...wsEntry,
|
|
256
|
+
status: 'open',
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const newEntries = new Map(state.networkEntries);
|
|
260
|
+
newEntries.set(entry.id, updatedEntry);
|
|
261
|
+
return { networkEntries: newEntries };
|
|
262
|
+
});
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
case 'websocket-close': {
|
|
267
|
+
const eventData = data as NetworkActivityEventMap['websocket-close'];
|
|
268
|
+
set((state) => {
|
|
269
|
+
const entry = state.networkEntries.get(`ws-${eventData.socketId}`);
|
|
270
|
+
if (!entry || entry.type !== 'websocket') return state;
|
|
271
|
+
|
|
272
|
+
const wsEntry = entry as WebSocketNetworkEntry;
|
|
273
|
+
const updatedEntry: WebSocketNetworkEntry = {
|
|
274
|
+
...wsEntry,
|
|
275
|
+
status: 'closed',
|
|
276
|
+
closeCode: eventData.code,
|
|
277
|
+
closeReason: eventData.reason,
|
|
278
|
+
duration: eventData.timestamp - wsEntry.timestamp,
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const newEntries = new Map(state.networkEntries);
|
|
282
|
+
newEntries.set(entry.id, updatedEntry);
|
|
283
|
+
return { networkEntries: newEntries };
|
|
284
|
+
});
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
case 'websocket-message-sent': {
|
|
289
|
+
const eventData =
|
|
290
|
+
data as NetworkActivityEventMap['websocket-message-sent'];
|
|
291
|
+
set((state) => {
|
|
292
|
+
const socketId = `ws-${eventData.socketId}`;
|
|
293
|
+
const currentMessages = state.websocketMessages.get(socketId) || [];
|
|
294
|
+
|
|
295
|
+
const message: WebSocketMessage = {
|
|
296
|
+
id: getId(`${socketId}-message`),
|
|
297
|
+
direction: 'sent',
|
|
298
|
+
data: eventData.data,
|
|
299
|
+
messageType: eventData.messageType,
|
|
300
|
+
timestamp: eventData.timestamp,
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const newMessages = new Map(state.websocketMessages);
|
|
304
|
+
newMessages.set(
|
|
305
|
+
socketId,
|
|
306
|
+
[...currentMessages, message].slice(
|
|
307
|
+
-MAX_WEBSOCKET_MESSAGES_PER_CONNECTION
|
|
308
|
+
)
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
return { websocketMessages: newMessages };
|
|
312
|
+
});
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
case 'websocket-message-received': {
|
|
317
|
+
const eventData =
|
|
318
|
+
data as NetworkActivityEventMap['websocket-message-received'];
|
|
319
|
+
set((state) => {
|
|
320
|
+
const socketId = `ws-${eventData.socketId}`;
|
|
321
|
+
const currentMessages = state.websocketMessages.get(socketId) || [];
|
|
322
|
+
|
|
323
|
+
const message: WebSocketMessage = {
|
|
324
|
+
id: getId(`${socketId}-message`),
|
|
325
|
+
direction: 'received',
|
|
326
|
+
data: eventData.data,
|
|
327
|
+
messageType: eventData.messageType,
|
|
328
|
+
timestamp: eventData.timestamp,
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const newMessages = new Map(state.websocketMessages);
|
|
332
|
+
newMessages.set(
|
|
333
|
+
socketId,
|
|
334
|
+
[...currentMessages, message].slice(
|
|
335
|
+
-MAX_WEBSOCKET_MESSAGES_PER_CONNECTION
|
|
336
|
+
)
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
return { websocketMessages: newMessages };
|
|
340
|
+
});
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
case 'websocket-error': {
|
|
345
|
+
const eventData = data as NetworkActivityEventMap['websocket-error'];
|
|
346
|
+
set((state) => {
|
|
347
|
+
const entry = state.networkEntries.get(`ws-${eventData.socketId}`);
|
|
348
|
+
if (!entry || entry.type !== 'websocket') return state;
|
|
349
|
+
|
|
350
|
+
const wsEntry = entry as WebSocketNetworkEntry;
|
|
351
|
+
const updatedEntry: WebSocketNetworkEntry = {
|
|
352
|
+
...wsEntry,
|
|
353
|
+
status: 'error',
|
|
354
|
+
error: eventData.error,
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const newEntries = new Map(state.networkEntries);
|
|
358
|
+
newEntries.set(entry.id, updatedEntry);
|
|
359
|
+
return { networkEntries: newEntries };
|
|
360
|
+
});
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
case 'websocket-connection-status-changed': {
|
|
365
|
+
const eventData =
|
|
366
|
+
data as NetworkActivityEventMap['websocket-connection-status-changed'];
|
|
367
|
+
set((state) => {
|
|
368
|
+
const entry = state.networkEntries.get(`ws-${eventData.socketId}`);
|
|
369
|
+
if (!entry || entry.type !== 'websocket') return state;
|
|
370
|
+
|
|
371
|
+
const wsEntry = entry as WebSocketNetworkEntry;
|
|
372
|
+
const updatedEntry: WebSocketNetworkEntry = {
|
|
373
|
+
...wsEntry,
|
|
374
|
+
status: eventData.status,
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const newEntries = new Map(state.networkEntries);
|
|
378
|
+
newEntries.set(entry.id, updatedEntry);
|
|
379
|
+
return { networkEntries: newEntries };
|
|
380
|
+
});
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
case 'sse-open': {
|
|
385
|
+
const eventData = data as NetworkActivityEventMap['sse-open'];
|
|
386
|
+
set((state) => {
|
|
387
|
+
const entry = state.networkEntries.get(eventData.requestId);
|
|
388
|
+
if (!entry || entry.type !== 'http') return state;
|
|
389
|
+
|
|
390
|
+
// Transform the existing HTTP entry to SSE
|
|
391
|
+
const httpEntry = entry as HttpNetworkEntry;
|
|
392
|
+
const sseEntry: SSENetworkEntry = {
|
|
393
|
+
...httpEntry,
|
|
394
|
+
type: 'sse', // Change type from 'http' to 'sse'
|
|
395
|
+
status: 'open', // Update status
|
|
396
|
+
messages: [], // Add SSE-specific field
|
|
397
|
+
response: eventData.response,
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const newEntries = new Map(state.networkEntries);
|
|
401
|
+
newEntries.set(eventData.requestId, sseEntry);
|
|
402
|
+
return { networkEntries: newEntries };
|
|
403
|
+
});
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
case 'sse-message': {
|
|
408
|
+
const eventData = data as NetworkActivityEventMap['sse-message'];
|
|
409
|
+
set((state) => {
|
|
410
|
+
const entry = state.networkEntries.get(eventData.requestId);
|
|
411
|
+
if (!entry || entry.type !== 'sse') return state;
|
|
412
|
+
|
|
413
|
+
const sseEntry = entry as SSENetworkEntry;
|
|
414
|
+
const newMessage: SSEMessage = {
|
|
415
|
+
id: getId(`${eventData.requestId}-message`),
|
|
416
|
+
data: eventData.data,
|
|
417
|
+
timestamp: eventData.timestamp,
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
const updatedEntry: SSENetworkEntry = {
|
|
421
|
+
...sseEntry,
|
|
422
|
+
messages: [...sseEntry.messages, newMessage].slice(
|
|
423
|
+
-MAX_SSE_MESSAGES_PER_CONNECTION
|
|
424
|
+
),
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const newEntries = new Map(state.networkEntries);
|
|
428
|
+
newEntries.set(eventData.requestId, updatedEntry);
|
|
429
|
+
return { networkEntries: newEntries };
|
|
430
|
+
});
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
case 'sse-error': {
|
|
435
|
+
const eventData = data as NetworkActivityEventMap['sse-error'];
|
|
436
|
+
set((state) => {
|
|
437
|
+
const entry = state.networkEntries.get(eventData.requestId);
|
|
438
|
+
if (!entry || entry.type !== 'sse') return state;
|
|
439
|
+
|
|
440
|
+
const sseEntry = entry as SSENetworkEntry;
|
|
441
|
+
const updatedEntry: SSENetworkEntry = {
|
|
442
|
+
...sseEntry,
|
|
443
|
+
status: 'error',
|
|
444
|
+
error: eventData.error.message,
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
const newEntries = new Map(state.networkEntries);
|
|
448
|
+
newEntries.set(eventData.requestId, updatedEntry);
|
|
449
|
+
return { networkEntries: newEntries };
|
|
450
|
+
});
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
case 'sse-close': {
|
|
455
|
+
const eventData = data as NetworkActivityEventMap['sse-close'];
|
|
456
|
+
set((state) => {
|
|
457
|
+
const entry = state.networkEntries.get(eventData.requestId);
|
|
458
|
+
if (!entry || entry.type !== 'sse') return state;
|
|
459
|
+
|
|
460
|
+
const sseEntry = entry as SSENetworkEntry;
|
|
461
|
+
const updatedEntry: SSENetworkEntry = {
|
|
462
|
+
...sseEntry,
|
|
463
|
+
status: 'closed',
|
|
464
|
+
duration: eventData.timestamp - sseEntry.timestamp,
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
const newEntries = new Map(state.networkEntries);
|
|
468
|
+
newEntries.set(eventData.requestId, updatedEntry);
|
|
469
|
+
return { networkEntries: newEntries };
|
|
470
|
+
});
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
// Client management
|
|
477
|
+
client: {
|
|
478
|
+
setupClient: (client: NetworkActivityDevToolsClient) => {
|
|
479
|
+
const { handleEvent } = get();
|
|
480
|
+
|
|
481
|
+
// Subscribe to all events using the unified handler
|
|
482
|
+
const unsubscribeFunctions = [
|
|
483
|
+
client.onMessage('request-sent', (data) =>
|
|
484
|
+
handleEvent('request-sent', data)
|
|
485
|
+
),
|
|
486
|
+
client.onMessage('response-received', (data) =>
|
|
487
|
+
handleEvent('response-received', data)
|
|
488
|
+
),
|
|
489
|
+
client.onMessage('request-completed', (data) =>
|
|
490
|
+
handleEvent('request-completed', data)
|
|
491
|
+
),
|
|
492
|
+
client.onMessage('request-failed', (data) =>
|
|
493
|
+
handleEvent('request-failed', data)
|
|
494
|
+
),
|
|
495
|
+
client.onMessage('response-body', (data) =>
|
|
496
|
+
handleEvent('response-body', data)
|
|
497
|
+
),
|
|
498
|
+
client.onMessage('websocket-connect', (data) =>
|
|
499
|
+
handleEvent('websocket-connect', data)
|
|
500
|
+
),
|
|
501
|
+
client.onMessage('websocket-open', (data) =>
|
|
502
|
+
handleEvent('websocket-open', data)
|
|
503
|
+
),
|
|
504
|
+
client.onMessage('websocket-close', (data) =>
|
|
505
|
+
handleEvent('websocket-close', data)
|
|
506
|
+
),
|
|
507
|
+
client.onMessage('websocket-message-sent', (data) =>
|
|
508
|
+
handleEvent('websocket-message-sent', data)
|
|
509
|
+
),
|
|
510
|
+
client.onMessage('websocket-message-received', (data) =>
|
|
511
|
+
handleEvent('websocket-message-received', data)
|
|
512
|
+
),
|
|
513
|
+
client.onMessage('websocket-error', (data) =>
|
|
514
|
+
handleEvent('websocket-error', data)
|
|
515
|
+
),
|
|
516
|
+
client.onMessage('websocket-connection-status-changed', (data) =>
|
|
517
|
+
handleEvent('websocket-connection-status-changed', data)
|
|
518
|
+
),
|
|
519
|
+
client.onMessage('sse-open', (data) => handleEvent('sse-open', data)),
|
|
520
|
+
client.onMessage('sse-message', (data) =>
|
|
521
|
+
handleEvent('sse-message', data)
|
|
522
|
+
),
|
|
523
|
+
client.onMessage('sse-error', (data) =>
|
|
524
|
+
handleEvent('sse-error', data)
|
|
525
|
+
),
|
|
526
|
+
client.onMessage('sse-close', (data) =>
|
|
527
|
+
handleEvent('sse-close', data)
|
|
528
|
+
),
|
|
529
|
+
];
|
|
530
|
+
|
|
531
|
+
// Store unsubscribe functions in the state for cleanup
|
|
532
|
+
set({
|
|
533
|
+
_unsubscribeFunctions: unsubscribeFunctions,
|
|
534
|
+
_client: client,
|
|
535
|
+
});
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
cleanupClient: () => {
|
|
539
|
+
const { _unsubscribeFunctions, _client } = get();
|
|
540
|
+
|
|
541
|
+
if (_unsubscribeFunctions) {
|
|
542
|
+
_unsubscribeFunctions.forEach((unsubscribe: { remove: () => void }) =>
|
|
543
|
+
unsubscribe.remove()
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (_client) {
|
|
548
|
+
_client.send('network-disable', {});
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
set({
|
|
552
|
+
_unsubscribeFunctions: undefined,
|
|
553
|
+
_client: undefined,
|
|
554
|
+
});
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
}));
|
|
558
|
+
|
|
559
|
+
export const store = createNetworkActivityStore();
|