@rozenite/network-activity-plugin 1.7.0 → 1.8.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +24 -0
  3. package/dist/devtools/App.html +2 -2
  4. package/dist/devtools/assets/{App-pokLiGYV.js → App-B3xlUjs6.js} +163 -115
  5. package/dist/devtools/assets/{App-BrSkOkws.css → App-m6xge0az.css} +13 -0
  6. package/dist/react-native/chunks/boot-recording.cjs +307 -28
  7. package/dist/react-native/chunks/boot-recording.js +310 -31
  8. package/dist/react-native/chunks/useNetworkActivityDevTools.require.cjs +192 -141
  9. package/dist/react-native/chunks/useNetworkActivityDevTools.require.js +193 -142
  10. package/dist/react-native/index.d.ts +24 -7
  11. package/dist/rozenite.json +1 -1
  12. package/dist/sdk/index.cjs +127 -0
  13. package/dist/sdk/index.d.ts +1243 -0
  14. package/dist/sdk/index.js +127 -0
  15. package/package.json +17 -6
  16. package/sdk.ts +59 -0
  17. package/src/react-native/__tests__/events-listener.test.ts +35 -0
  18. package/src/react-native/agent/__tests__/network-activity-agent-state.test.ts +21 -9
  19. package/src/react-native/agent/state.ts +13 -13
  20. package/src/react-native/agent/tools.ts +20 -146
  21. package/src/react-native/agent/use-network-activity-agent-tools.ts +73 -64
  22. package/src/react-native/events-listener.ts +12 -3
  23. package/src/react-native/http/http-inspector.ts +19 -5
  24. package/src/react-native/network-inspector.ts +46 -8
  25. package/src/react-native/nitro-fetch/__tests__/nitro-network-inspector.test.ts +198 -0
  26. package/src/react-native/nitro-fetch/nitro-network-inspector.ts +403 -0
  27. package/src/react-native/useHttpInspector.ts +9 -19
  28. package/src/react-native/useNetworkActivityDevTools.ts +13 -1
  29. package/src/react-native/websocket/__tests__/websocket-inspector.test.ts +69 -0
  30. package/src/react-native/websocket/websocket-inspector.ts +32 -17
  31. package/src/shared/agent-tools.ts +230 -0
  32. package/src/shared/client.ts +3 -0
  33. package/src/shared/http-events.ts +6 -0
  34. package/src/shared/websocket-events.ts +16 -7
  35. package/src/ui/components/RequestList.tsx +21 -0
  36. package/src/ui/components/SidePanel.tsx +12 -9
  37. package/src/ui/state/derived.ts +4 -0
  38. package/src/ui/state/model.ts +6 -1
  39. package/src/ui/state/store.ts +52 -36
  40. package/src/ui/tabs/HeadersTab.tsx +18 -4
  41. package/src/ui/tabs/ResponseTab.tsx +5 -3
  42. package/tsconfig.json +4 -1
  43. package/vite.config.ts +7 -0
@@ -0,0 +1,69 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+
3
+ vi.mock('../websocket-interceptor', () => ({
4
+ getWebSocketInterceptor: vi.fn(),
5
+ }));
6
+
7
+ import { createWebSocketInspector } from '../websocket-inspector';
8
+ import type { WebSocketInterceptor } from '../websocket-interceptor';
9
+
10
+ const createInterceptor = () => {
11
+ const callbacks: Partial<{
12
+ connect: Parameters<WebSocketInterceptor['setConnectCallback']>[0];
13
+ open: Parameters<WebSocketInterceptor['setOnOpenCallback']>[0];
14
+ message: Parameters<WebSocketInterceptor['setOnMessageCallback']>[0];
15
+ }> = {};
16
+
17
+ const interceptor: WebSocketInterceptor = {
18
+ setCloseCallback: vi.fn(),
19
+ setSendCallback: vi.fn(),
20
+ setConnectCallback: vi.fn((callback) => {
21
+ callbacks.connect = callback;
22
+ }),
23
+ setOnOpenCallback: vi.fn((callback) => {
24
+ callbacks.open = callback;
25
+ }),
26
+ setOnMessageCallback: vi.fn((callback) => {
27
+ callbacks.message = callback;
28
+ }),
29
+ setOnErrorCallback: vi.fn(),
30
+ setOnCloseCallback: vi.fn(),
31
+ isInterceptorEnabled: vi.fn(() => true),
32
+ enableInterception: vi.fn(),
33
+ disableInterception: vi.fn(),
34
+ };
35
+
36
+ return {
37
+ callbacks,
38
+ interceptor,
39
+ };
40
+ };
41
+
42
+ describe('websocket inspector', () => {
43
+ it('stringifies numeric interceptor ids before emitting events', () => {
44
+ const { callbacks, interceptor } = createInterceptor();
45
+ const inspector = createWebSocketInspector(interceptor);
46
+ const events: Array<{ type: string; socketId: string }> = [];
47
+
48
+ inspector.on('websocket-connect', (event) => {
49
+ events.push({ type: event.type, socketId: event.socketId });
50
+ });
51
+ inspector.on('websocket-open', (event) => {
52
+ events.push({ type: event.type, socketId: event.socketId });
53
+ });
54
+ inspector.on('websocket-message-received', (event) => {
55
+ events.push({ type: event.type, socketId: event.socketId });
56
+ });
57
+
58
+ inspector.enable();
59
+ callbacks.connect?.('wss://example.com/socket', ['chat'], [], 42);
60
+ callbacks.open?.(42);
61
+ callbacks.message?.('hello', 42);
62
+
63
+ expect(events).toEqual([
64
+ { type: 'websocket-connect', socketId: '42' },
65
+ { type: 'websocket-open', socketId: '42' },
66
+ { type: 'websocket-message-received', socketId: '42' },
67
+ ]);
68
+ });
69
+ });
@@ -5,6 +5,7 @@ import {
5
5
  WebSocketEventMap,
6
6
  } from '../../shared/websocket-events';
7
7
  import type { Inspector } from '../inspector';
8
+ import type { WebSocketInterceptor } from './websocket-interceptor';
8
9
 
9
10
  type NanoEventsMap = {
10
11
  [K in keyof WebSocketEventMap]: (data: WebSocketEventMap[K]) => void;
@@ -23,15 +24,18 @@ export const WEBSOCKET_EVENTS: (keyof WebSocketEventMap)[] = [
23
24
  ];
24
25
 
25
26
  export const isWebSocketEvent = (
26
- type: string
27
+ type: string,
27
28
  ): type is keyof WebSocketEventMap => {
28
29
  return (WEBSOCKET_EVENTS as readonly string[]).includes(type);
29
30
  };
30
31
 
31
- export const getWebSocketInspector = (): WebSocketInspector => {
32
+ const toSocketId = (socketId: number) => String(socketId);
33
+
34
+ export const createWebSocketInspector = (
35
+ webSocketInterceptor: WebSocketInterceptor = getWebSocketInterceptor(),
36
+ ): WebSocketInspector => {
32
37
  const eventEmitter = createNanoEvents<NanoEventsMap>();
33
38
  const socketUrlMap = new Map<number, string>();
34
- const webSocketInterceptor = getWebSocketInterceptor();
35
39
 
36
40
  return {
37
41
  enable: () => {
@@ -40,19 +44,20 @@ export const getWebSocketInspector = (): WebSocketInspector => {
40
44
  url: string,
41
45
  protocols: string[] | null,
42
46
  options: string[],
43
- socketId: number
47
+ socketId: number,
44
48
  ) => {
45
49
  socketUrlMap.set(socketId, url);
46
50
  const event: WebSocketEvent = {
47
51
  type: 'websocket-connect',
48
52
  url,
49
- socketId,
53
+ socketId: toSocketId(socketId),
50
54
  timestamp: Date.now(),
51
55
  protocols,
52
56
  options,
57
+ source: 'builtin',
53
58
  };
54
59
  eventEmitter.emit('websocket-connect', event);
55
- }
60
+ },
56
61
  );
57
62
 
58
63
  webSocketInterceptor.setCloseCallback(
@@ -66,14 +71,15 @@ export const getWebSocketInspector = (): WebSocketInspector => {
66
71
  const event: WebSocketEvent = {
67
72
  type: 'websocket-close',
68
73
  url,
69
- socketId,
74
+ socketId: toSocketId(socketId),
70
75
  timestamp: Date.now(),
71
76
  code: code || 0,
72
77
  reason: reason || undefined,
78
+ source: 'builtin',
73
79
  };
74
80
  eventEmitter.emit('websocket-close', event);
75
81
  socketUrlMap.delete(socketId);
76
- }
82
+ },
77
83
  );
78
84
 
79
85
  webSocketInterceptor.setOnMessageCallback(
@@ -87,13 +93,14 @@ export const getWebSocketInspector = (): WebSocketInspector => {
87
93
  const event: WebSocketEvent = {
88
94
  type: 'websocket-message-received',
89
95
  url,
90
- socketId,
96
+ socketId: toSocketId(socketId),
91
97
  timestamp: Date.now(),
92
98
  data,
93
99
  messageType: typeof data === 'string' ? 'text' : 'binary',
100
+ source: 'builtin',
94
101
  };
95
102
  eventEmitter.emit('websocket-message-received', event);
96
- }
103
+ },
97
104
  );
98
105
 
99
106
  webSocketInterceptor.setOnErrorCallback(
@@ -107,12 +114,13 @@ export const getWebSocketInspector = (): WebSocketInspector => {
107
114
  const event: WebSocketEvent = {
108
115
  type: 'websocket-error',
109
116
  url,
110
- socketId,
117
+ socketId: toSocketId(socketId),
111
118
  timestamp: Date.now(),
112
119
  error,
120
+ source: 'builtin',
113
121
  };
114
122
  eventEmitter.emit('websocket-error', event);
115
- }
123
+ },
116
124
  );
117
125
 
118
126
  webSocketInterceptor.setSendCallback((data: string, socketId: number) => {
@@ -125,10 +133,11 @@ export const getWebSocketInspector = (): WebSocketInspector => {
125
133
  const event: WebSocketEvent = {
126
134
  type: 'websocket-message-sent',
127
135
  url,
128
- socketId,
136
+ socketId: toSocketId(socketId),
129
137
  timestamp: Date.now(),
130
138
  data,
131
139
  messageType: typeof data === 'string' ? 'text' : 'binary',
140
+ source: 'builtin',
132
141
  };
133
142
  eventEmitter.emit('websocket-message-sent', event);
134
143
  });
@@ -143,8 +152,9 @@ export const getWebSocketInspector = (): WebSocketInspector => {
143
152
  const event: WebSocketEvent = {
144
153
  type: 'websocket-open',
145
154
  url,
146
- socketId,
155
+ socketId: toSocketId(socketId),
147
156
  timestamp: Date.now(),
157
+ source: 'builtin',
148
158
  };
149
159
  eventEmitter.emit('websocket-open', event);
150
160
  });
@@ -160,14 +170,15 @@ export const getWebSocketInspector = (): WebSocketInspector => {
160
170
  const event: WebSocketEvent = {
161
171
  type: 'websocket-close',
162
172
  url,
163
- socketId,
173
+ socketId: toSocketId(socketId),
164
174
  timestamp: Date.now(),
165
175
  code: error.code,
166
176
  reason: error.reason,
177
+ source: 'builtin',
167
178
  };
168
179
  eventEmitter.emit('websocket-close', event);
169
180
  socketUrlMap.delete(socketId);
170
- }
181
+ },
171
182
  );
172
183
 
173
184
  webSocketInterceptor.enableInterception();
@@ -182,7 +193,11 @@ export const getWebSocketInspector = (): WebSocketInspector => {
182
193
  },
183
194
  on: <TEventType extends keyof WebSocketEventMap>(
184
195
  event: TEventType,
185
- callback: (data: WebSocketEventMap[TEventType]) => void
196
+ callback: (data: WebSocketEventMap[TEventType]) => void,
186
197
  ) => eventEmitter.on(event, callback as NanoEventsMap[TEventType]),
187
198
  };
188
199
  };
200
+
201
+ export const getWebSocketInspector = (): WebSocketInspector => {
202
+ return createWebSocketInspector();
203
+ };
@@ -0,0 +1,230 @@
1
+ import {
2
+ defineAgentToolContract,
3
+ type AgentToolContract,
4
+ } from '@rozenite/agent-shared';
5
+ import type {
6
+ NetworkActivityAgentBodyResult,
7
+ NetworkActivityAgentState,
8
+ } from '../react-native/agent/state';
9
+
10
+ export const NETWORK_ACTIVITY_AGENT_PLUGIN_ID =
11
+ '@rozenite/network-activity-plugin';
12
+
13
+ export type NetworkActivityPaginationArgs = {
14
+ limit?: number;
15
+ cursor?: string;
16
+ };
17
+
18
+ export type NetworkActivityRequestIdArgs = {
19
+ requestId: string;
20
+ };
21
+
22
+ export type NetworkActivityStartRecordingArgs = undefined;
23
+
24
+ export type NetworkActivityGetRecordingStatusArgs = undefined;
25
+
26
+ export type NetworkActivityGetRecordingStatusResult = ReturnType<
27
+ NetworkActivityAgentState['getStatus']
28
+ >;
29
+
30
+ export type NetworkActivityStartRecordingResult =
31
+ NetworkActivityGetRecordingStatusResult & {
32
+ started: true;
33
+ };
34
+
35
+ export type NetworkActivityStopRecordingArgs = undefined;
36
+
37
+ export type NetworkActivityStopRecordingResult =
38
+ NetworkActivityGetRecordingStatusResult & {
39
+ stopped: true;
40
+ };
41
+
42
+ export type NetworkActivityListRequestsArgs = NetworkActivityPaginationArgs;
43
+
44
+ export type NetworkActivityListRequestsResult = ReturnType<
45
+ NetworkActivityAgentState['listRequests']
46
+ >;
47
+
48
+ export type NetworkActivityGetRequestDetailsArgs = NetworkActivityRequestIdArgs;
49
+
50
+ export type NetworkActivityGetRequestDetailsResult = ReturnType<
51
+ NetworkActivityAgentState['getRequestDetails']
52
+ >;
53
+
54
+ export type NetworkActivityGetRequestBodyArgs = NetworkActivityRequestIdArgs;
55
+
56
+ export type NetworkActivityGetRequestBodyResult = NetworkActivityAgentBodyResult;
57
+
58
+ export type NetworkActivityGetResponseBodyArgs = NetworkActivityRequestIdArgs;
59
+
60
+ export type NetworkActivityGetResponseBodyResult = NetworkActivityAgentBodyResult;
61
+
62
+ export type NetworkActivityListRealtimeConnectionsArgs =
63
+ NetworkActivityPaginationArgs;
64
+
65
+ export type NetworkActivityListRealtimeConnectionsResult = ReturnType<
66
+ NetworkActivityAgentState['listRealtimeConnections']
67
+ >;
68
+
69
+ export type NetworkActivityGetRealtimeConnectionDetailsArgs =
70
+ NetworkActivityRequestIdArgs;
71
+
72
+ export type NetworkActivityGetRealtimeConnectionDetailsResult = ReturnType<
73
+ NetworkActivityAgentState['getRealtimeConnectionDetails']
74
+ >;
75
+
76
+ export const networkActivityToolDefinitions = {
77
+ startRecording: defineAgentToolContract<
78
+ NetworkActivityStartRecordingArgs,
79
+ NetworkActivityStartRecordingResult
80
+ >({
81
+ name: 'startRecording',
82
+ description:
83
+ 'Start recording network activity in the fallback network activity plugin.',
84
+ inputSchema: {
85
+ type: 'object',
86
+ properties: {},
87
+ },
88
+ }),
89
+ stopRecording: defineAgentToolContract<
90
+ NetworkActivityStopRecordingArgs,
91
+ NetworkActivityStopRecordingResult
92
+ >({
93
+ name: 'stopRecording',
94
+ description:
95
+ 'Stop recording network activity without clearing the captured plugin buffer.',
96
+ inputSchema: {
97
+ type: 'object',
98
+ properties: {},
99
+ },
100
+ }),
101
+ getRecordingStatus: defineAgentToolContract<
102
+ NetworkActivityGetRecordingStatusArgs,
103
+ NetworkActivityGetRecordingStatusResult
104
+ >({
105
+ name: 'getRecordingStatus',
106
+ description:
107
+ 'Return network activity plugin recording state and buffer metadata.',
108
+ inputSchema: {
109
+ type: 'object',
110
+ properties: {},
111
+ },
112
+ }),
113
+ listRequests: defineAgentToolContract<
114
+ NetworkActivityListRequestsArgs,
115
+ NetworkActivityListRequestsResult
116
+ >({
117
+ name: 'listRequests',
118
+ description:
119
+ 'List captured HTTP request summaries with cursor pagination from the fallback plugin.',
120
+ inputSchema: {
121
+ type: 'object',
122
+ properties: {
123
+ limit: {
124
+ type: 'number',
125
+ description: 'Maximum number of requests to return. Defaults to 20.',
126
+ },
127
+ cursor: {
128
+ type: 'string',
129
+ description:
130
+ 'Opaque pagination cursor from a previous listRequests call.',
131
+ },
132
+ },
133
+ },
134
+ }),
135
+ getRequestDetails: defineAgentToolContract<
136
+ NetworkActivityGetRequestDetailsArgs,
137
+ NetworkActivityGetRequestDetailsResult
138
+ >({
139
+ name: 'getRequestDetails',
140
+ description:
141
+ 'Return detailed metadata for a captured HTTP request without fetching response body.',
142
+ inputSchema: {
143
+ type: 'object',
144
+ properties: {
145
+ requestId: {
146
+ type: 'string',
147
+ description: 'Captured plugin request ID to inspect.',
148
+ },
149
+ },
150
+ required: ['requestId'],
151
+ },
152
+ }),
153
+ getRequestBody: defineAgentToolContract<
154
+ NetworkActivityGetRequestBodyArgs,
155
+ NetworkActivityGetRequestBodyResult
156
+ >({
157
+ name: 'getRequestBody',
158
+ description:
159
+ 'Return the captured request body for a plugin-recorded HTTP request when available.',
160
+ inputSchema: {
161
+ type: 'object',
162
+ properties: {
163
+ requestId: {
164
+ type: 'string',
165
+ description: 'Captured plugin request ID to inspect.',
166
+ },
167
+ },
168
+ required: ['requestId'],
169
+ },
170
+ }),
171
+ getResponseBody: defineAgentToolContract<
172
+ NetworkActivityGetResponseBodyArgs,
173
+ NetworkActivityGetResponseBodyResult
174
+ >({
175
+ name: 'getResponseBody',
176
+ description:
177
+ 'Return the captured response body for a plugin-recorded HTTP request when available.',
178
+ inputSchema: {
179
+ type: 'object',
180
+ properties: {
181
+ requestId: {
182
+ type: 'string',
183
+ description: 'Captured plugin request ID to inspect.',
184
+ },
185
+ },
186
+ required: ['requestId'],
187
+ },
188
+ }),
189
+ listRealtimeConnections: defineAgentToolContract<
190
+ NetworkActivityListRealtimeConnectionsArgs,
191
+ NetworkActivityListRealtimeConnectionsResult
192
+ >({
193
+ name: 'listRealtimeConnections',
194
+ description:
195
+ 'List captured WebSocket and SSE connections with cursor pagination.',
196
+ inputSchema: {
197
+ type: 'object',
198
+ properties: {
199
+ limit: {
200
+ type: 'number',
201
+ description:
202
+ 'Maximum number of realtime connections to return. Defaults to 20.',
203
+ },
204
+ cursor: {
205
+ type: 'string',
206
+ description:
207
+ 'Opaque pagination cursor from a previous listRealtimeConnections call.',
208
+ },
209
+ },
210
+ },
211
+ }),
212
+ getRealtimeConnectionDetails: defineAgentToolContract<
213
+ NetworkActivityGetRealtimeConnectionDetailsArgs,
214
+ NetworkActivityGetRealtimeConnectionDetailsResult
215
+ >({
216
+ name: 'getRealtimeConnectionDetails',
217
+ description:
218
+ 'Return details for a captured WebSocket or SSE connection, including recent messages.',
219
+ inputSchema: {
220
+ type: 'object',
221
+ properties: {
222
+ requestId: {
223
+ type: 'string',
224
+ description: 'Captured realtime request ID to inspect.',
225
+ },
226
+ },
227
+ required: ['requestId'],
228
+ },
229
+ }),
230
+ } as const satisfies Record<string, AgentToolContract<unknown, unknown>>;
@@ -14,6 +14,9 @@ export type NetworkActivityEventMap = {
14
14
  'network-enable': unknown;
15
15
  'network-disable': unknown;
16
16
 
17
+ // Recording state sync (sent by React Native to inform DevTools UI of current state)
18
+ 'recording-state': { isRecording: boolean };
19
+
17
20
  // Client UI settings events
18
21
  'get-client-ui-settings': unknown;
19
22
  'client-ui-settings': {
@@ -5,6 +5,7 @@ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD';
5
5
 
6
6
  export type RequestId = string;
7
7
  export type Timestamp = number;
8
+ export type NetworkEventSource = 'builtin' | 'nitro';
8
9
 
9
10
  export type XHRPostData =
10
11
  | string
@@ -92,6 +93,7 @@ export type HttpEventMap = {
92
93
  timestamp: Timestamp;
93
94
  initiator: Initiator;
94
95
  type: ResourceType;
96
+ source?: NetworkEventSource;
95
97
  };
96
98
 
97
99
  'response-received': {
@@ -99,6 +101,7 @@ export type HttpEventMap = {
99
101
  timestamp: Timestamp;
100
102
  type: ResourceType;
101
103
  response: Response;
104
+ source?: NetworkEventSource;
102
105
  };
103
106
 
104
107
  'request-completed': {
@@ -107,6 +110,7 @@ export type HttpEventMap = {
107
110
  duration: number;
108
111
  size: number | null;
109
112
  ttfb: number;
113
+ source?: NetworkEventSource;
110
114
  };
111
115
 
112
116
  'request-failed': {
@@ -115,6 +119,7 @@ export type HttpEventMap = {
115
119
  type: ResourceType;
116
120
  error: string;
117
121
  canceled: boolean;
122
+ source?: NetworkEventSource;
118
123
  };
119
124
 
120
125
  'request-progress': {
@@ -123,6 +128,7 @@ export type HttpEventMap = {
123
128
  loaded: number;
124
129
  total: number;
125
130
  lengthComputable: boolean;
131
+ source?: NetworkEventSource;
126
132
  };
127
133
 
128
134
  'get-response-body': {
@@ -1,3 +1,5 @@
1
+ import type { NetworkEventSource } from './http-events';
2
+
1
3
  export type WebSocketMessageType = 'text' | 'binary';
2
4
 
3
5
  export type WebSocketConnectionStatus =
@@ -9,60 +11,67 @@ export type WebSocketConnectionStatus =
9
11
  export type WebSocketConnectEvent = {
10
12
  type: 'websocket-connect';
11
13
  url: string;
12
- socketId: number;
14
+ socketId: string;
13
15
  timestamp: number;
14
16
  protocols: string[] | null;
15
17
  options: string[];
18
+ source?: NetworkEventSource;
16
19
  };
17
20
 
18
21
  export type WebSocketOpenEvent = {
19
22
  type: 'websocket-open';
20
23
  url: string;
21
- socketId: number;
24
+ socketId: string;
22
25
  timestamp: number;
26
+ source?: NetworkEventSource;
23
27
  };
24
28
 
25
29
  export type WebSocketCloseEvent = {
26
30
  type: 'websocket-close';
27
31
  url: string;
28
- socketId: number;
32
+ socketId: string;
29
33
  timestamp: number;
30
34
  code: number;
31
35
  reason?: string;
36
+ source?: NetworkEventSource;
32
37
  };
33
38
 
34
39
  export type WebSocketMessageSentEvent = {
35
40
  type: 'websocket-message-sent';
36
41
  url: string;
37
- socketId: number;
42
+ socketId: string;
38
43
  timestamp: number;
39
44
  data: string;
40
45
  messageType: WebSocketMessageType;
46
+ source?: NetworkEventSource;
41
47
  };
42
48
 
43
49
  export type WebSocketMessageReceivedEvent = {
44
50
  type: 'websocket-message-received';
45
51
  url: string;
46
- socketId: number;
52
+ socketId: string;
47
53
  timestamp: number;
48
54
  data: string;
49
55
  messageType: WebSocketMessageType;
56
+ source?: NetworkEventSource;
50
57
  };
51
58
 
52
59
  export type WebSocketErrorEvent = {
53
60
  type: 'websocket-error';
54
61
  url: string;
55
- socketId: number;
62
+ socketId: string;
56
63
  timestamp: number;
57
64
  error: string;
65
+ source?: NetworkEventSource;
58
66
  };
59
67
 
60
68
  export type WebSocketConnectionStatusChangedEvent = {
61
69
  type: 'websocket-connection-status-changed';
62
70
  url: string;
63
- socketId: number;
71
+ socketId: string;
64
72
  timestamp: number;
65
73
  status: WebSocketConnectionStatus;
74
+ source?: NetworkEventSource;
66
75
  };
67
76
 
68
77
  export type WebSocketEvent =
@@ -20,6 +20,7 @@ import {
20
20
  import { getStatusColor } from '../utils/getStatusColor';
21
21
  import { FilterState } from './FilterBar';
22
22
  import { isNumber } from '../../utils/typeChecks';
23
+ import type { NetworkEventSource } from '../../shared/client';
23
24
 
24
25
  type NetworkRequest = {
25
26
  id: RequestId;
@@ -31,10 +32,23 @@ type NetworkRequest = {
31
32
  size: string;
32
33
  time: string;
33
34
  type: string;
35
+ source?: NetworkEventSource;
34
36
  startTime: string;
35
37
  hasOverride: boolean;
36
38
  };
37
39
 
40
+ const getSourceLabel = (source?: NetworkEventSource) => {
41
+ if (source === 'nitro') {
42
+ return 'Nitro';
43
+ }
44
+
45
+ if (source === 'builtin') {
46
+ return 'Built-in';
47
+ }
48
+
49
+ return null;
50
+ };
51
+
38
52
  const formatSize = (bytes: number): string => {
39
53
  if (bytes === 0) return '0 B';
40
54
  const k = 1024;
@@ -153,6 +167,7 @@ const processNetworkRequests = (
153
167
  size: isNumber(request.size) ? formatSize(request.size) : '—',
154
168
  time: formatDuration(duration),
155
169
  type: request.type,
170
+ source: request.source,
156
171
  startTime: formatStartTime(request.timestamp),
157
172
  hasOverride: hasOverride,
158
173
  };
@@ -177,6 +192,12 @@ const columns = [
177
192
  {row.original.hasOverride && (
178
193
  <span className="w-2 h-2 rounded-full bg-violet-300 ms-2 inline-block"></span>
179
194
  )}
195
+
196
+ {getSourceLabel(row.original.source) && (
197
+ <span className="ml-2 rounded border border-gray-700 px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-gray-400">
198
+ {getSourceLabel(row.original.source)}
199
+ </span>
200
+ )}
180
201
  </div>
181
202
  ),
182
203
  sortingFn: 'alphanumeric',