@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.
Files changed (87) hide show
  1. package/dist/App.html +2 -2
  2. package/dist/assets/{App-lNMijPJ4.js → App-CA1Fbh0I.js} +11995 -10804
  3. package/dist/assets/{App-R2ZMH9wJ.css → App-DoHQsY5s.css} +46 -0
  4. package/dist/event-source.cjs +22 -0
  5. package/dist/event-source.js +23 -0
  6. package/dist/rozenite.json +1 -1
  7. package/dist/src/react-native/{network-inspector.d.ts → http/network-inspector.d.ts} +1 -1
  8. package/dist/src/react-native/sse/event-source.d.ts +2 -0
  9. package/dist/src/react-native/sse/sse-inspector.d.ts +9 -0
  10. package/dist/src/react-native/sse/sse-interceptor.d.ts +36 -0
  11. package/dist/src/react-native/sse/types.d.ts +6 -0
  12. package/dist/src/react-native/utils.d.ts +6 -0
  13. package/dist/src/react-native/websocket/websocket-inspector.d.ts +9 -0
  14. package/dist/src/react-native/websocket/websocket-interceptor.d.ts +74 -0
  15. package/dist/src/shared/client.d.ts +5 -2
  16. package/dist/src/shared/sse-events.d.ts +35 -0
  17. package/dist/src/shared/websocket-events.d.ts +60 -0
  18. package/dist/src/ui/components/Badge.d.ts +1 -1
  19. package/dist/src/ui/components/Button.d.ts +1 -1
  20. package/dist/src/ui/components/JsonTreeCopyableItem.d.ts +7 -0
  21. package/dist/src/ui/components/RequestList.d.ts +6 -26
  22. package/dist/src/ui/components/SidePanel.d.ts +1 -0
  23. package/dist/src/ui/components/Toolbar.d.ts +1 -0
  24. package/dist/src/ui/hooks/useCopyToClipboard.d.ts +4 -0
  25. package/dist/src/ui/state/derived.d.ts +5 -0
  26. package/dist/src/ui/state/hooks.d.ts +17 -0
  27. package/dist/src/ui/state/model.d.ts +98 -0
  28. package/dist/src/ui/state/store.d.ts +24 -0
  29. package/dist/src/ui/tabs/CookiesTab.d.ts +3 -6
  30. package/dist/src/ui/tabs/HeadersTab.d.ts +3 -15
  31. package/dist/src/ui/tabs/MessagesTab.d.ts +5 -0
  32. package/dist/src/ui/tabs/RequestTab.d.ts +2 -7
  33. package/dist/src/ui/tabs/ResponseTab.d.ts +2 -8
  34. package/dist/src/ui/tabs/SSEMessagesTab.d.ts +5 -0
  35. package/dist/src/ui/tabs/TimingTab.d.ts +3 -5
  36. package/dist/src/ui/types.d.ts +4 -1
  37. package/dist/src/ui/utils/assert.d.ts +1 -0
  38. package/dist/src/ui/utils/copyToClipboard.d.ts +1 -0
  39. package/dist/src/ui/utils/getId.d.ts +1 -0
  40. package/dist/src/ui/utils/getStatusColor.d.ts +1 -0
  41. package/dist/useNetworkActivityDevTools.cjs +423 -34
  42. package/dist/useNetworkActivityDevTools.js +421 -34
  43. package/package.json +19 -8
  44. package/src/react-native/{network-inspector.ts → http/network-inspector.ts} +13 -34
  45. package/src/react-native/{xml-request.d.ts → http/xml-request.d.ts} +1 -0
  46. package/src/react-native/sse/event-source.ts +25 -0
  47. package/src/react-native/sse/sse-inspector.ts +117 -0
  48. package/src/react-native/sse/sse-interceptor.ts +162 -0
  49. package/src/react-native/sse/types.ts +9 -0
  50. package/src/react-native/useNetworkActivityDevTools.ts +75 -1
  51. package/src/react-native/utils.ts +43 -0
  52. package/src/react-native/websocket/websocket-inspector.ts +180 -0
  53. package/src/react-native/websocket/websocket-interceptor.d.ts +4 -0
  54. package/src/react-native/websocket/websocket-interceptor.ts +166 -0
  55. package/src/shared/client.ts +6 -2
  56. package/src/shared/sse-events.ts +44 -0
  57. package/src/shared/websocket-events.ts +79 -0
  58. package/src/ui/components/JsonTree.tsx +13 -0
  59. package/src/ui/components/JsonTreeCopyableItem.tsx +33 -0
  60. package/src/ui/components/RequestList.tsx +42 -124
  61. package/src/ui/components/SidePanel.tsx +323 -0
  62. package/src/ui/components/Tabs.tsx +1 -1
  63. package/src/ui/components/Toolbar.tsx +45 -0
  64. package/src/ui/hooks/useCopyToClipboard.ts +28 -0
  65. package/src/ui/state/derived.ts +112 -0
  66. package/src/ui/state/hooks.ts +44 -0
  67. package/src/ui/state/model.ts +129 -0
  68. package/src/ui/state/store.ts +559 -0
  69. package/src/ui/tabs/CookiesTab.tsx +162 -176
  70. package/src/ui/tabs/HeadersTab.tsx +23 -30
  71. package/src/ui/tabs/MessagesTab.tsx +276 -0
  72. package/src/ui/tabs/RequestTab.tsx +8 -13
  73. package/src/ui/tabs/ResponseTab.tsx +6 -10
  74. package/src/ui/tabs/SSEMessagesTab.tsx +213 -0
  75. package/src/ui/tabs/TimingTab.tsx +30 -43
  76. package/src/ui/types.ts +4 -1
  77. package/src/ui/utils/assert.ts +5 -0
  78. package/src/ui/utils/copyToClipboard.ts +3 -0
  79. package/src/ui/utils/getId.ts +10 -0
  80. package/src/ui/utils/getStatusColor.ts +15 -0
  81. package/src/ui/views/InspectorView.tsx +24 -320
  82. package/tailwind.config.ts +3 -0
  83. package/vite.config.ts +12 -0
  84. /package/dist/src/react-native/{network-requests-registry.d.ts → http/network-requests-registry.d.ts} +0 -0
  85. /package/dist/src/react-native/{xhr-interceptor.d.ts → http/xhr-interceptor.d.ts} +0 -0
  86. /package/src/react-native/{network-requests-registry.ts → http/network-requests-registry.ts} +0 -0
  87. /package/src/react-native/{xhr-interceptor.ts → http/xhr-interceptor.ts} +0 -0
@@ -0,0 +1,5 @@
1
+ export function assert(condition: boolean, message: string): asserts condition {
2
+ if (!condition) {
3
+ throw new Error(message);
4
+ }
5
+ }
@@ -0,0 +1,3 @@
1
+ export async function copyToClipboard(text: string) {
2
+ return navigator.clipboard.writeText(text);
3
+ }
@@ -0,0 +1,10 @@
1
+ const idMap = new Map<string, number>();
2
+
3
+ export const getId = (namespace: string) => {
4
+ if (!idMap.has(namespace)) {
5
+ idMap.set(namespace, 0);
6
+ }
7
+ const id = idMap.get(namespace) ?? 0;
8
+ idMap.set(namespace, id + 1);
9
+ return `${namespace}-${id}`;
10
+ };
@@ -0,0 +1,15 @@
1
+ export const getStatusColor = (status: number | string): string => {
2
+ if (typeof status === 'string') {
3
+ // Handle WebSocket statuses
4
+ if (status === 'open') return 'text-green-400';
5
+ if (status === 'connecting') return 'text-yellow-400';
6
+ if (status === 'closed' || status === 'error') return 'text-red-400';
7
+ return 'text-gray-400';
8
+ }
9
+
10
+ // Handle HTTP status codes
11
+ if (status >= 200 && status < 300) return 'text-green-400';
12
+ if (status >= 300 && status < 400) return 'text-yellow-400';
13
+ if (status >= 400) return 'text-red-400';
14
+ return 'text-gray-400';
15
+ };
@@ -1,348 +1,52 @@
1
- import { useState, useMemo, useEffect } from 'react';
2
- import { Badge } from '../components/Badge';
3
- import { Button } from '../components/Button';
4
- import { Tabs, TabsContent, TabsList, TabsTrigger } from '../components/Tabs';
5
- import { HeadersTab } from '../tabs/HeadersTab';
6
- import { RequestTab } from '../tabs/RequestTab';
7
- import { ResponseTab } from '../tabs/ResponseTab';
8
- import { CookiesTab } from '../tabs/CookiesTab';
9
- import { TimingTab } from '../tabs/TimingTab';
1
+ import { useEffect } from 'react';
2
+ import { Toolbar } from '../components/Toolbar';
3
+ import { RequestList } from '../components/RequestList';
4
+ import { SidePanel } from '../components/SidePanel';
5
+ import { NetworkActivityDevToolsClient } from '../../shared/client';
10
6
  import {
11
- RequestList,
12
- processNetworkEntries,
13
- getTypeColor,
14
- getStatusColor,
15
- } from '../components/RequestList';
16
- import { Circle, Square, Trash2, X } from 'lucide-react';
17
- import {
18
- NetworkActivityDevToolsClient,
19
- NetworkActivityEventMap,
20
- RequestId,
21
- } from '../../shared/client';
22
- import { NetworkEntry } from '../types';
7
+ useNetworkActivityClientManagement,
8
+ useHasSelectedRequest,
9
+ useNetworkActivityActions,
10
+ } from '../state/hooks';
23
11
 
24
12
  export type InspectorViewProps = {
25
13
  client: NetworkActivityDevToolsClient;
26
14
  };
27
15
 
28
16
  export const InspectorView = ({ client }: InspectorViewProps) => {
29
- const [isRecording, setIsRecording] = useState(true);
30
- const [selectedRequestId, setSelectedRequestId] = useState<RequestId | null>(
31
- null
32
- );
33
- const [networkEntries, setNetworkEntries] = useState<
34
- Map<RequestId, NetworkEntry>
35
- >(new Map());
36
-
37
- const selectedRequest = useMemo(() => {
38
- if (!selectedRequestId) return null;
39
- const processedRequests = processNetworkEntries(networkEntries);
40
- return (
41
- processedRequests.find((request) => request.id === selectedRequestId) ||
42
- null
43
- );
44
- }, [selectedRequestId, networkEntries]);
17
+ const actions = useNetworkActivityActions();
18
+ const clientManagement = useNetworkActivityClientManagement();
19
+ const hasSelectedRequest = useHasSelectedRequest();
45
20
 
46
21
  useEffect(() => {
47
- const handleRequestSent = (
48
- data: NetworkActivityEventMap['request-sent']
49
- ) => {
50
- const entry: NetworkEntry = {
51
- requestId: data.requestId,
52
- url: data.request.url,
53
- method: data.request.method,
54
- headers: data.request.headers,
55
- postData: data.request.postData,
56
- status: 'pending',
57
- startTime: data.timestamp,
58
- type: data.type,
59
- initiator: data.initiator,
60
- request: data.request,
61
- };
62
-
63
- setNetworkEntries((prev) => new Map(prev).set(data.requestId, entry));
64
- };
65
-
66
- const handleResponseReceived = (
67
- data: NetworkActivityEventMap['response-received']
68
- ) => {
69
- setNetworkEntries((prev) => {
70
- const entry = prev.get(data.requestId);
71
- if (!entry) return prev;
72
-
73
- const updatedEntry: NetworkEntry = {
74
- ...entry,
75
- status: 'loading',
76
- response: data.response,
77
- };
78
-
79
- return new Map(prev).set(data.requestId, updatedEntry);
80
- });
81
- };
82
-
83
- const handleRequestCompleted = (
84
- data: NetworkActivityEventMap['request-completed']
85
- ) => {
86
- setNetworkEntries((prev) => {
87
- const entry = prev.get(data.requestId);
88
- if (!entry) return prev;
89
-
90
- const updatedEntry: NetworkEntry = {
91
- ...entry,
92
- status: 'finished',
93
- endTime: data.timestamp,
94
- duration: data.duration,
95
- ttfb: data.ttfb,
96
- size: data.size,
97
- };
98
-
99
- return new Map(prev).set(data.requestId, updatedEntry);
100
- });
101
- };
102
-
103
- const handleRequestFailed = (
104
- data: NetworkActivityEventMap['request-failed']
105
- ) => {
106
- setNetworkEntries((prev) => {
107
- const entry = prev.get(data.requestId);
108
- if (!entry) return prev;
109
-
110
- const updatedEntry: NetworkEntry = {
111
- ...entry,
112
- status: 'failed',
113
- error: data.error,
114
- canceled: data.canceled,
115
- };
116
-
117
- return new Map(prev).set(data.requestId, updatedEntry);
118
- });
119
- };
120
-
121
- // Subscribe to network events
122
- const unsubscribeRequestSent = client.onMessage(
123
- 'request-sent',
124
- handleRequestSent
125
- );
126
- const unsubscribeResponseReceived = client.onMessage(
127
- 'response-received',
128
- handleResponseReceived
129
- );
130
- const unsubscribeRequestCompleted = client.onMessage(
131
- 'request-completed',
132
- handleRequestCompleted
133
- );
134
- const unsubscribeRequestFailed = client.onMessage(
135
- 'request-failed',
136
- handleRequestFailed
137
- );
138
-
139
- const handleResponseBody = (
140
- data: NetworkActivityEventMap['response-body']
141
- ) => {
142
- setNetworkEntries((prev) => {
143
- const entry = prev.get(data.requestId);
144
- if (!entry) return prev;
145
-
146
- const updatedEntry: NetworkEntry = {
147
- ...entry,
148
- responseBody: {
149
- ...entry.responseBody,
150
- body: data.body,
151
- },
152
- };
153
-
154
- console.log(updatedEntry);
155
- return new Map(prev).set(data.requestId, updatedEntry);
156
- });
157
- };
22
+ if (!client) {
23
+ return;
24
+ }
158
25
 
159
- const unsubscribeResponseBody = client.onMessage(
160
- 'response-body',
161
- handleResponseBody
162
- );
26
+ clientManagement.setupClient(client);
27
+ actions.setRecording(true);
163
28
 
164
29
  return () => {
165
- unsubscribeRequestSent.remove();
166
- unsubscribeResponseReceived.remove();
167
- unsubscribeRequestCompleted.remove();
168
- unsubscribeRequestFailed.remove();
169
- unsubscribeResponseBody.remove();
30
+ actions.setRecording(false);
31
+ clientManagement.cleanupClient();
170
32
  };
171
- }, [client]);
172
-
173
- useEffect(() => {
174
- if (isRecording) {
175
- client.send('network-enable', {});
176
-
177
- return () => {
178
- client.send('network-disable', {});
179
- };
180
- }
181
- }, [isRecording, client]);
182
-
183
- const clearRequests = () => {
184
- setNetworkEntries(new Map());
185
- setSelectedRequestId(null);
186
- };
187
-
188
- const closeSidePanel = () => {
189
- setSelectedRequestId(null);
190
- };
33
+ }, [client, clientManagement, actions]);
191
34
 
192
35
  return (
193
36
  <div className="h-screen bg-gray-900 text-gray-100 flex flex-col">
194
- {/* Toolbar */}
195
- <div className="flex items-center gap-2 p-2 border-b border-gray-700 bg-gray-800">
196
- <Button
197
- variant="ghost"
198
- size="sm"
199
- onClick={() => setIsRecording(!isRecording)}
200
- className={`h-8 w-8 p-0 ${
201
- isRecording
202
- ? 'text-red-400 hover:text-red-300'
203
- : 'text-gray-400 hover:text-blue-400'
204
- }`}
205
- >
206
- {isRecording ? (
207
- <Circle className="h-4 w-4 fill-current" />
208
- ) : (
209
- <Square className="h-4 w-4" />
210
- )}
211
- </Button>
212
- <Button
213
- variant="ghost"
214
- size="sm"
215
- onClick={clearRequests}
216
- className="h-8 w-8 p-0 text-gray-400 hover:text-blue-400"
217
- >
218
- <Trash2 className="h-4 w-4" />
219
- </Button>
220
- </div>
37
+ <Toolbar />
221
38
 
222
39
  <div className="flex flex-1 overflow-hidden">
223
40
  {/* Request List */}
224
41
  <div
225
42
  className={`flex flex-col ${
226
- selectedRequest ? 'w-1/2' : 'w-full'
43
+ hasSelectedRequest ? 'w-1/2' : 'w-full'
227
44
  } border-r border-gray-700 overflow-hidden`}
228
45
  >
229
- <RequestList
230
- networkEntries={networkEntries}
231
- selectedRequestId={selectedRequestId}
232
- onRequestSelect={setSelectedRequestId}
233
- />
46
+ <RequestList />
234
47
  </div>
235
48
 
236
- {/* Side Panel */}
237
- {selectedRequest && (
238
- <div className="w-1/2 flex flex-col bg-gray-900">
239
- {/* Side Panel Header */}
240
- <div className="flex items-center justify-between p-3 border-b border-gray-700 bg-gray-800">
241
- <div className="flex items-center gap-2">
242
- <div
243
- className={`w-3 h-3 rounded-full ${getTypeColor(
244
- selectedRequest.type
245
- )}`}
246
- ></div>
247
- <span className="font-medium">{selectedRequest.name}</span>
248
- <Badge
249
- variant="outline"
250
- className={`${getStatusColor(
251
- selectedRequest.status
252
- )} border-current`}
253
- >
254
- {selectedRequest.status}
255
- </Badge>
256
- </div>
257
- <Button
258
- variant="ghost"
259
- size="sm"
260
- onClick={closeSidePanel}
261
- className="h-6 w-6 p-0 text-gray-400 hover:text-blue-400"
262
- >
263
- <X className="h-4 w-4" />
264
- </Button>
265
- </div>
266
-
267
- {/* Side Panel Content */}
268
- <div className="flex-1 overflow-hidden">
269
- <Tabs defaultValue="headers" className="h-full flex flex-col">
270
- <TabsList className="grid w-full grid-cols-5 bg-gray-800 rounded-none border-b border-gray-700">
271
- <TabsTrigger
272
- value="headers"
273
- className="data-[state=active]:bg-gray-700"
274
- >
275
- Headers
276
- </TabsTrigger>
277
- <TabsTrigger
278
- value="request"
279
- className="data-[state=active]:bg-gray-700"
280
- >
281
- Request
282
- </TabsTrigger>
283
- <TabsTrigger
284
- value="response"
285
- className="data-[state=active]:bg-gray-700"
286
- >
287
- Response
288
- </TabsTrigger>
289
- <TabsTrigger
290
- value="cookies"
291
- className="data-[state=active]:bg-gray-700"
292
- >
293
- Cookies
294
- </TabsTrigger>
295
- <TabsTrigger
296
- value="timing"
297
- className="data-[state=active]:bg-gray-700"
298
- >
299
- Timing
300
- </TabsTrigger>
301
- </TabsList>
302
-
303
- <TabsContent
304
- value="headers"
305
- className="flex-1 m-0 overflow-hidden"
306
- >
307
- <HeadersTab
308
- selectedRequest={selectedRequest}
309
- networkEntries={networkEntries}
310
- getStatusColor={getStatusColor}
311
- />
312
- </TabsContent>
313
-
314
- <TabsContent value="request" className="flex-1 m-0 overflow-hidden">
315
- <RequestTab selectedRequest={selectedRequest} />
316
- </TabsContent>
317
-
318
- <TabsContent value="response" className="flex-1 m-0 overflow-hidden">
319
- <ResponseTab
320
- selectedRequest={selectedRequest}
321
- onRequestResponseBody={(requestId) => {
322
- client.send('get-response-body', {
323
- requestId,
324
- });
325
- }}
326
- />
327
- </TabsContent>
328
-
329
- <TabsContent value="cookies" className="flex-1 m-0 overflow-hidden">
330
- <CookiesTab
331
- selectedRequest={selectedRequest}
332
- networkEntries={networkEntries}
333
- />
334
- </TabsContent>
335
-
336
- <TabsContent value="timing" className="flex-1 m-0 overflow-hidden">
337
- <TimingTab
338
- selectedRequest={selectedRequest}
339
- networkEntries={networkEntries}
340
- />
341
- </TabsContent>
342
- </Tabs>
343
- </div>
344
- </div>
345
- )}
49
+ {hasSelectedRequest && <SidePanel />}
346
50
  </div>
347
51
  </div>
348
52
  );
@@ -7,6 +7,9 @@ const config: Config = {
7
7
  content: ['./src/ui/**/*.{js,ts,jsx,tsx,mdx}'],
8
8
  theme: {
9
9
  extend: {
10
+ translate: {
11
+ '0.75': '0.1875rem',
12
+ },
10
13
  colors: {
11
14
  background: 'hsl(var(--background))',
12
15
  foreground: 'hsl(var(--foreground))',
package/vite.config.ts CHANGED
@@ -12,6 +12,18 @@ export default defineConfig({
12
12
  reportCompressedSize: false,
13
13
  minify: false,
14
14
  sourcemap: false,
15
+ rollupOptions: {
16
+ output: {
17
+ manualChunks: (id) => {
18
+ // Mitigate https://github.com/facebook/metro/issues/836
19
+ if (id.includes('event-source.ts')) {
20
+ return 'event-source';
21
+ }
22
+
23
+ return undefined;
24
+ },
25
+ },
26
+ },
15
27
  },
16
28
  server: {
17
29
  port: 3000,