@rozenite/network-activity-plugin 1.0.0-alpha.8 → 1.0.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.
Files changed (160) hide show
  1. package/README.md +2 -0
  2. package/dist/App.html +2 -2
  3. package/dist/assets/{App-R2ZMH9wJ.css → App-BrSkOkws.css} +269 -2
  4. package/dist/assets/{App-lNMijPJ4.js → App-C6wCDVkW.js} +17485 -10814
  5. package/dist/event-source.cjs +22 -0
  6. package/dist/event-source.js +23 -0
  7. package/dist/react-native.cjs +4 -1
  8. package/dist/react-native.js +4 -1
  9. package/dist/rozenite.json +1 -1
  10. package/dist/src/react-native/config.d.ts +20 -0
  11. package/dist/src/react-native/{network-inspector.d.ts → http/network-inspector.d.ts} +1 -1
  12. package/dist/src/react-native/http/overrides-registry.d.ts +6 -0
  13. package/dist/src/react-native/{xhr-interceptor.d.ts → http/xhr-interceptor.d.ts} +7 -1
  14. package/dist/src/react-native/sse/event-source.d.ts +2 -0
  15. package/dist/src/react-native/sse/sse-inspector.d.ts +9 -0
  16. package/dist/src/react-native/sse/sse-interceptor.d.ts +36 -0
  17. package/dist/src/react-native/sse/types.d.ts +6 -0
  18. package/dist/src/react-native/useNetworkActivityDevTools.d.ts +2 -1
  19. package/dist/src/react-native/utils/getBlobName.d.ts +35 -0
  20. package/dist/src/react-native/utils/getFormDataEntries.d.ts +18 -0
  21. package/dist/src/react-native/utils.d.ts +6 -0
  22. package/dist/src/react-native/websocket/websocket-inspector.d.ts +9 -0
  23. package/dist/src/react-native/websocket/websocket-interceptor.d.ts +74 -0
  24. package/dist/src/shared/client.d.ts +53 -6
  25. package/dist/src/shared/sse-events.d.ts +38 -0
  26. package/dist/src/shared/websocket-events.d.ts +60 -0
  27. package/dist/src/ui/components/Badge.d.ts +1 -1
  28. package/dist/src/ui/components/Button.d.ts +2 -2
  29. package/dist/src/ui/components/CodeBlock.d.ts +3 -0
  30. package/dist/src/ui/components/CodeEditor.d.ts +5 -0
  31. package/dist/src/ui/components/CookieCard.d.ts +7 -0
  32. package/dist/src/ui/components/CopyRequestDropdown.d.ts +7 -0
  33. package/dist/src/ui/components/DropdownMenu.d.ts +27 -0
  34. package/dist/src/ui/components/FilterBar.d.ts +10 -0
  35. package/dist/src/ui/components/JsonTreeCopyableItem.d.ts +7 -0
  36. package/dist/src/ui/components/KeyValueGrid.d.ts +13 -0
  37. package/dist/src/ui/components/OverrideResponse.d.ts +8 -0
  38. package/dist/src/ui/components/RequestBody.d.ts +6 -0
  39. package/dist/src/ui/components/RequestList.d.ts +13 -28
  40. package/dist/src/ui/components/ScrollArea.d.ts +3 -2
  41. package/dist/src/ui/components/Section.d.ts +8 -0
  42. package/dist/src/ui/components/Separator.d.ts +2 -1
  43. package/dist/src/ui/components/SidePanel.d.ts +1 -0
  44. package/dist/src/ui/components/Tabs.d.ts +7 -0
  45. package/dist/src/ui/components/Toolbar.d.ts +1 -0
  46. package/dist/src/ui/hooks/useCopyToClipboard.d.ts +4 -0
  47. package/dist/src/ui/state/derived.d.ts +5 -0
  48. package/dist/src/ui/state/hooks.d.ts +21 -0
  49. package/dist/src/ui/state/model.d.ts +103 -0
  50. package/dist/src/ui/state/store.d.ts +48 -0
  51. package/dist/src/ui/tabs/CookiesTab.d.ts +3 -6
  52. package/dist/src/ui/tabs/HeadersTab.d.ts +3 -15
  53. package/dist/src/ui/tabs/MessagesTab.d.ts +5 -0
  54. package/dist/src/ui/tabs/RequestTab.d.ts +2 -7
  55. package/dist/src/ui/tabs/ResponseTab.d.ts +2 -8
  56. package/dist/src/ui/tabs/SSEMessagesTab.d.ts +5 -0
  57. package/dist/src/ui/tabs/TimingTab.d.ts +3 -5
  58. package/dist/src/ui/types.d.ts +4 -1
  59. package/dist/src/ui/utils/assert.d.ts +1 -0
  60. package/dist/src/ui/utils/checkRequestBodyBinary.d.ts +2 -0
  61. package/dist/src/ui/utils/copyToClipboard.d.ts +1 -0
  62. package/dist/src/ui/utils/escapeShellArg.d.ts +1 -0
  63. package/dist/src/ui/utils/generateCurlCommand.d.ts +2 -0
  64. package/dist/src/ui/utils/generateFetchCall.d.ts +2 -0
  65. package/dist/src/ui/utils/generateMultipartBody.d.ts +4 -0
  66. package/dist/src/ui/utils/getId.d.ts +1 -0
  67. package/dist/src/ui/utils/getStatusColor.d.ts +1 -0
  68. package/dist/src/utils/applyReactNativeRequestHeadersLogic.d.ts +7 -0
  69. package/dist/src/utils/applyReactNativeResponseHeadersLogic.d.ts +9 -0
  70. package/dist/src/utils/cookieParser.d.ts +6 -0
  71. package/dist/src/utils/getContentTypeMimeType.d.ts +2 -0
  72. package/dist/src/utils/getHttpHeader.d.ts +5 -0
  73. package/dist/src/utils/getHttpHeaderValueAsString.d.ts +11 -0
  74. package/dist/src/utils/getStringSizeInBytes.d.ts +1 -0
  75. package/dist/src/utils/inferContentTypeFromPostData.d.ts +2 -0
  76. package/dist/src/utils/safeStringify.d.ts +1 -0
  77. package/dist/src/utils/typeChecks.d.ts +9 -0
  78. package/dist/useNetworkActivityDevTools.cjs +724 -40
  79. package/dist/useNetworkActivityDevTools.js +723 -41
  80. package/package.json +22 -8
  81. package/react-native.ts +6 -1
  82. package/src/react-native/config.ts +43 -0
  83. package/src/react-native/http/network-inspector.ts +388 -0
  84. package/src/react-native/http/overrides-registry.ts +32 -0
  85. package/src/react-native/{xhr-interceptor.ts → http/xhr-interceptor.ts} +19 -2
  86. package/src/react-native/{xml-request.d.ts → http/xml-request.d.ts} +1 -0
  87. package/src/react-native/sse/event-source.ts +25 -0
  88. package/src/react-native/sse/sse-inspector.ts +139 -0
  89. package/src/react-native/sse/sse-interceptor.ts +180 -0
  90. package/src/react-native/sse/types.ts +9 -0
  91. package/src/react-native/useNetworkActivityDevTools.ts +156 -4
  92. package/src/react-native/utils/getBlobName.ts +45 -0
  93. package/src/react-native/utils/getFormDataEntries.ts +32 -0
  94. package/src/react-native/utils.ts +43 -0
  95. package/src/react-native/websocket/websocket-inspector.ts +180 -0
  96. package/src/react-native/websocket/websocket-interceptor.d.ts +4 -0
  97. package/src/react-native/websocket/websocket-interceptor.ts +166 -0
  98. package/src/shared/client.ts +79 -6
  99. package/src/shared/sse-events.ts +47 -0
  100. package/src/shared/websocket-events.ts +79 -0
  101. package/src/ui/components/Button.tsx +1 -0
  102. package/src/ui/components/CodeBlock.tsx +19 -0
  103. package/src/ui/components/CodeEditor.tsx +26 -0
  104. package/src/ui/components/CookieCard.tsx +64 -0
  105. package/src/ui/components/CopyRequestDropdown.tsx +95 -0
  106. package/src/ui/components/DropdownMenu.tsx +206 -0
  107. package/src/ui/components/FilterBar.tsx +117 -0
  108. package/src/ui/components/Input.tsx +1 -1
  109. package/src/ui/components/JsonTree.tsx +20 -0
  110. package/src/ui/components/JsonTreeCopyableItem.tsx +37 -0
  111. package/src/ui/components/KeyValueGrid.tsx +51 -0
  112. package/src/ui/components/OverrideResponse.tsx +132 -0
  113. package/src/ui/components/RequestBody.tsx +86 -0
  114. package/src/ui/components/RequestList.tsx +101 -131
  115. package/src/ui/components/ScrollArea.tsx +1 -0
  116. package/src/ui/components/Section.tsx +46 -0
  117. package/src/ui/components/SidePanel.tsx +333 -0
  118. package/src/ui/components/Tabs.tsx +1 -1
  119. package/src/ui/components/Toolbar.tsx +45 -0
  120. package/src/ui/globals.css +4 -0
  121. package/src/ui/hooks/useCopyToClipboard.ts +28 -0
  122. package/src/ui/state/derived.ts +112 -0
  123. package/src/ui/state/hooks.ts +52 -0
  124. package/src/ui/state/model.ts +140 -0
  125. package/src/ui/state/store.ts +669 -0
  126. package/src/ui/tabs/CookiesTab.tsx +61 -278
  127. package/src/ui/tabs/HeadersTab.tsx +85 -103
  128. package/src/ui/tabs/MessagesTab.tsx +276 -0
  129. package/src/ui/tabs/RequestTab.tsx +58 -51
  130. package/src/ui/tabs/ResponseTab.tsx +101 -74
  131. package/src/ui/tabs/SSEMessagesTab.tsx +224 -0
  132. package/src/ui/tabs/TimingTab.tsx +30 -43
  133. package/src/ui/types.ts +4 -1
  134. package/src/ui/utils/assert.ts +5 -0
  135. package/src/ui/utils/checkRequestBodyBinary.ts +7 -0
  136. package/src/ui/utils/copyToClipboard.ts +3 -0
  137. package/src/ui/utils/escapeShellArg.ts +12 -0
  138. package/src/ui/utils/generateCurlCommand.ts +83 -0
  139. package/src/ui/utils/generateFetchCall.ts +64 -0
  140. package/src/ui/utils/generateMultipartBody.ts +19 -0
  141. package/src/ui/utils/getId.ts +10 -0
  142. package/src/ui/utils/getStatusColor.ts +15 -0
  143. package/src/ui/views/InspectorView.tsx +35 -319
  144. package/src/utils/applyReactNativeRequestHeadersLogic.ts +30 -0
  145. package/src/utils/applyReactNativeResponseHeadersLogic.ts +28 -0
  146. package/src/utils/cookieParser.ts +126 -0
  147. package/src/utils/getContentTypeMimeType.ts +17 -0
  148. package/src/utils/getHttpHeader.ts +17 -0
  149. package/src/utils/getHttpHeaderValueAsString.ts +13 -0
  150. package/src/utils/getStringSizeInBytes.ts +3 -0
  151. package/src/utils/inferContentTypeFromPostData.ts +9 -0
  152. package/src/utils/safeStringify.ts +7 -0
  153. package/src/utils/typeChecks.ts +27 -0
  154. package/tailwind.config.ts +3 -0
  155. package/vite.config.ts +12 -0
  156. package/dist/src/ui/utils/getHttpHeaderValue.d.ts +0 -2
  157. package/src/react-native/network-inspector.ts +0 -247
  158. package/src/ui/utils/getHttpHeaderValue.ts +0 -14
  159. /package/dist/src/react-native/{network-requests-registry.d.ts → http/network-requests-registry.d.ts} +0 -0
  160. /package/src/react-native/{network-requests-registry.ts → http/network-requests-registry.ts} +0 -0
@@ -0,0 +1,132 @@
1
+ import { useRef, useState } from 'react';
2
+ import { HttpNetworkEntry } from '../state/model';
3
+ import { Section } from '../components/Section';
4
+ import { KeyValueGrid } from '../components/KeyValueGrid';
5
+ import { useNetworkActivityActions } from '../state/hooks';
6
+ import { CodeEditor } from '../components/CodeEditor';
7
+ import { RequestOverride } from '../../shared/client';
8
+ import { Button } from './Button';
9
+ import { Check, CircleSlash2 } from 'lucide-react';
10
+
11
+ export type OverrideResponseProps = {
12
+ selectedRequest: HttpNetworkEntry;
13
+ initialOverride: RequestOverride | undefined;
14
+ onClear: () => void;
15
+ };
16
+
17
+ export const OverrideResponse = ({
18
+ selectedRequest,
19
+ initialOverride,
20
+ onClear,
21
+ }: OverrideResponseProps) => {
22
+ const actions = useNetworkActivityActions();
23
+ const [savedOverride, setSavedOverride] = useState<
24
+ RequestOverride | undefined
25
+ >(initialOverride);
26
+ const [editedBody, setEditedBody] = useState<string | undefined>(
27
+ initialOverride?.body
28
+ );
29
+ const [editedStatus, setEditedStatus] = useState<number | undefined>(
30
+ initialOverride?.status
31
+ );
32
+ const responseEditorRef = useRef<HTMLPreElement>(null);
33
+ const responseBody = selectedRequest.response?.body;
34
+
35
+ const saveOverride = () => {
36
+ if (editedBody === undefined && editedStatus === undefined) return;
37
+
38
+ const newOverrideData = {
39
+ body: editedBody,
40
+ status: editedStatus,
41
+ };
42
+
43
+ setSavedOverride(newOverrideData);
44
+ actions.addOverride(selectedRequest.request.url, newOverrideData);
45
+ };
46
+
47
+ const clearOverride = () => {
48
+ setSavedOverride(undefined);
49
+ setEditedBody(undefined);
50
+ actions.clearOverride(selectedRequest.request.url);
51
+ onClear();
52
+ };
53
+
54
+ if (!responseBody || responseBody.data === null) {
55
+ return (
56
+ <div className="text-sm text-gray-400">
57
+ No response body available for this request
58
+ </div>
59
+ );
60
+ }
61
+
62
+ const { type } = responseBody;
63
+
64
+ const hasChanges =
65
+ editedBody !== savedOverride?.body ||
66
+ editedStatus !== savedOverride?.status;
67
+
68
+ const overrideActions = (
69
+ <>
70
+ <Button
71
+ variant="ghost"
72
+ size="xs"
73
+ className="text-violet-300 hover:text-violet-300 ms-2"
74
+ onClick={clearOverride}
75
+ >
76
+ <CircleSlash2 className="h-2 w-2" />
77
+ Clear override
78
+ </Button>
79
+
80
+ <Button
81
+ variant="ghost"
82
+ size="xs"
83
+ className="text-violet-300 hover:text-violet-300"
84
+ onClick={saveOverride}
85
+ disabled={!hasChanges}
86
+ >
87
+ <Check className="h-2 w-2" />
88
+ {hasChanges ? 'Save override' : 'Saved'}
89
+ </Button>
90
+ </>
91
+ );
92
+
93
+ if (savedOverride !== undefined) {
94
+ return (
95
+ <Section
96
+ title="Response Body"
97
+ collapsible={false}
98
+ action={overrideActions}
99
+ >
100
+ <div className="space-y-4">
101
+ <KeyValueGrid
102
+ items={[
103
+ {
104
+ key: 'Content-Type',
105
+ value: type,
106
+ valueClassName: 'text-blue-400',
107
+ },
108
+ ]}
109
+ />
110
+
111
+ <div className="grid grid-cols-[minmax(7rem,25%)_minmax(3rem,1fr)] gap-x-2 gap-y-2 text-sm">
112
+ <span className={'text-gray-400 wrap-anywhere'}>Status Code</span>
113
+ <input
114
+ type="number"
115
+ value={editedStatus}
116
+ onChange={(e) => {
117
+ setEditedStatus(parseInt(e.target.value));
118
+ }}
119
+ className="max-w-24 font-mono text-gray-300 whitespace-pre-wrap bg-gray-800 p-1 rounded-md border border-gray-700 overflow-x-auto wrap-anywhere ring-offset-blue-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
120
+ />
121
+ </div>
122
+
123
+ <CodeEditor
124
+ data={savedOverride?.body}
125
+ ref={responseEditorRef}
126
+ onInput={(e) => setEditedBody(e.currentTarget.innerText)}
127
+ />
128
+ </div>
129
+ </Section>
130
+ );
131
+ }
132
+ };
@@ -0,0 +1,86 @@
1
+ import { HttpRequestData } from '../state/model';
2
+ import { KeyValueGrid, KeyValueItem } from './KeyValueGrid';
3
+ import { CodeBlock } from './CodeBlock';
4
+ import { JsonTree } from './JsonTree';
5
+ import {
6
+ RequestBinaryPostData,
7
+ RequestFormDataPostData,
8
+ } from '../../shared/client';
9
+
10
+ type RequestBodyProps = {
11
+ data: HttpRequestData['data'];
12
+ };
13
+
14
+ const getFormDataBinaryEntries = (
15
+ key: string,
16
+ value: RequestBinaryPostData['value']
17
+ ): KeyValueItem[] => {
18
+ return [
19
+ {
20
+ key,
21
+ value: <span className="text-blue-400">[binary]</span>,
22
+ },
23
+ ...getBinaryEntries(value).map((item) => ({
24
+ ...item,
25
+ key: ` └─ ${item.key}`,
26
+ keyClassName: 'whitespace-pre',
27
+ })),
28
+ ];
29
+ };
30
+
31
+ const getBinaryEntries = (
32
+ value: RequestBinaryPostData['value']
33
+ ): KeyValueItem[] => {
34
+ const { size, type, name } = value;
35
+
36
+ const items: KeyValueItem[] = [];
37
+
38
+ if (name) {
39
+ items.push({ key: 'Name', value: name });
40
+ }
41
+
42
+ if (type) {
43
+ items.push({ key: 'Type', value: type });
44
+ }
45
+
46
+ items.push({ key: 'Size', value: `${size} bytes` });
47
+
48
+ return items;
49
+ };
50
+
51
+ const getFormDataEntries = (value: RequestFormDataPostData['value']) =>
52
+ Object.entries(value).flatMap(([key, { value, type }]) => {
53
+ if (type === 'binary') {
54
+ return getFormDataBinaryEntries(key, value);
55
+ }
56
+
57
+ return [{ key, value }];
58
+ });
59
+
60
+ export const RequestBody = ({ data }: RequestBodyProps) => {
61
+ const { type: dataType, value } = data;
62
+
63
+ if (dataType === 'text') {
64
+ try {
65
+ const jsonData = JSON.parse(value);
66
+
67
+ return (
68
+ <CodeBlock>
69
+ <JsonTree data={jsonData} />
70
+ </CodeBlock>
71
+ );
72
+ } catch {
73
+ return <CodeBlock>{value}</CodeBlock>;
74
+ }
75
+ }
76
+
77
+ if (dataType === 'form-data') {
78
+ return <KeyValueGrid items={getFormDataEntries(value)} />;
79
+ }
80
+
81
+ if (dataType === 'binary') {
82
+ return <KeyValueGrid items={getBinaryEntries(value)} />;
83
+ }
84
+
85
+ return null;
86
+ };
@@ -1,42 +1,38 @@
1
- import * as React from 'react';
1
+ import { useMemo, useState } from 'react';
2
2
  import {
3
3
  createColumnHelper,
4
4
  flexRender,
5
5
  getCoreRowModel,
6
6
  getSortedRowModel,
7
+ SortingFn,
7
8
  SortingState,
8
9
  useReactTable,
9
10
  } from '@tanstack/react-table';
10
- import { NetworkEntry } from '../types';
11
- import { RequestId } from '../../shared/client';
12
- import { getHttpHeaderValue } from '../utils/getHttpHeaderValue';
11
+ import { ProcessedRequest } from '../state/model';
12
+ import { RequestId, RequestOverride } from '../../shared/client';
13
+ import {
14
+ useNetworkActivityActions,
15
+ useOverrides,
16
+ useProcessedRequests,
17
+ useSelectedRequestId,
18
+ useClientUISettings,
19
+ } from '../state/hooks';
20
+ import { getStatusColor } from '../utils/getStatusColor';
21
+ import { FilterState } from './FilterBar';
22
+ import { isNumber } from '../../utils/typeChecks';
13
23
 
14
24
  type NetworkRequest = {
15
- id: string;
25
+ id: RequestId;
16
26
  name: string;
17
- status: number;
27
+ status: string | number;
18
28
  method: string;
19
29
  domain: string;
20
30
  path: string;
21
31
  size: string;
22
32
  time: string;
23
33
  type: string;
24
- initiator: string;
25
34
  startTime: string;
26
- requestBody?: {
27
- type: string;
28
- data: string;
29
- };
30
- responseBody?: {
31
- type: string;
32
- data: string | null;
33
- };
34
- };
35
-
36
- type RequestListProps = {
37
- networkEntries: Map<RequestId, NetworkEntry>;
38
- selectedRequestId: RequestId | null;
39
- onRequestSelect: (requestId: RequestId) => void;
35
+ hasOverride: boolean;
40
36
  };
41
37
 
42
38
  const formatSize = (bytes: number): string => {
@@ -68,85 +64,30 @@ const extractDomainAndPath = (
68
64
  url: string
69
65
  ): { domain: string; path: string } => {
70
66
  try {
71
- const urlObj = new URL(url);
67
+ const { hostname, pathname, search, hash, port } = new URL(url);
68
+
72
69
  return {
73
- domain: urlObj.hostname,
74
- path: urlObj.pathname + urlObj.search + urlObj.hash,
70
+ domain: `${hostname}${port ? `:${port}` : ''}`,
71
+ path: `${pathname}${search}${hash}`,
75
72
  };
76
73
  } catch {
77
74
  return { domain: 'unknown', path: url };
78
75
  }
79
76
  };
80
77
 
81
- const generateName = (url: string): string => {
78
+ const generateName = (url: string, showEntirePathName = false): string => {
82
79
  try {
83
80
  const urlObj = new URL(url);
84
81
  const pathname = urlObj.pathname;
85
- const filename = pathname.split('/').pop();
82
+ const filename = showEntirePathName ? undefined : pathname.split('/').pop();
83
+
86
84
  return filename || pathname || urlObj.hostname;
87
85
  } catch {
88
86
  return url;
89
87
  }
90
88
  };
91
89
 
92
- const formatInitiator = (initiator: any): string => {
93
- if (!initiator) return 'Other';
94
- if (initiator.type === 'script' && initiator.url) {
95
- try {
96
- const url = new URL(initiator.url);
97
- const filename = url.pathname.split('/').pop() || url.hostname;
98
- const line = initiator.lineNumber ? `:${initiator.lineNumber}` : '';
99
- return `${filename}${line}`;
100
- } catch {
101
- return 'Script';
102
- }
103
- }
104
- return initiator.type || 'Other';
105
- };
106
-
107
- const mapResourceType = (type: string): string => {
108
- const typeMap: Record<string, string> = {
109
- Document: 'document',
110
- Stylesheet: 'stylesheet',
111
- Image: 'img',
112
- Media: 'media',
113
- Font: 'font',
114
- Script: 'script',
115
- XHR: 'xhr',
116
- Fetch: 'xhr',
117
- EventSource: 'eventsource',
118
- WebSocket: 'websocket',
119
- Manifest: 'manifest',
120
- Other: 'other',
121
- Ping: 'ping',
122
- CSPViolationReport: 'csp',
123
- Preflight: 'preflight',
124
- Subresource: 'subresource',
125
- };
126
- return typeMap[type] || 'other';
127
- };
128
-
129
- const getTypeColor = (type: string) => {
130
- const colors: Record<string, string> = {
131
- document: 'bg-blue-600',
132
- script: 'bg-yellow-600',
133
- stylesheet: 'bg-purple-600',
134
- xhr: 'bg-green-600',
135
- img: 'bg-pink-600',
136
- font: 'bg-orange-600',
137
- };
138
- return colors[type] || 'bg-gray-600';
139
- };
140
-
141
- const getStatusColor = (status: number) => {
142
- if (status >= 200 && status < 300) return 'text-green-400';
143
- if (status >= 300 && status < 400) return 'text-yellow-400';
144
- if (status >= 400) return 'text-red-400';
145
- return 'text-gray-400';
146
- };
147
-
148
- // Custom sorting functions
149
- const sortSize = (rowA: any, rowB: any, columnId: string) => {
90
+ const sortSize: SortingFn<NetworkRequest> = (rowA, rowB, columnId) => {
150
91
  const a = rowA.getValue(columnId) as string;
151
92
  const b = rowB.getValue(columnId) as string;
152
93
 
@@ -168,7 +109,7 @@ const sortSize = (rowA: any, rowB: any, columnId: string) => {
168
109
  return getNumericValue(a) - getNumericValue(b);
169
110
  };
170
111
 
171
- const sortTime = (rowA: any, rowB: any, columnId: string) => {
112
+ const sortTime: SortingFn<NetworkRequest> = (rowA, rowB, columnId) => {
172
113
  const a = rowA.getValue(columnId) as string;
173
114
  const b = rowB.getValue(columnId) as string;
174
115
 
@@ -184,38 +125,28 @@ const sortTime = (rowA: any, rowB: any, columnId: string) => {
184
125
  return getNumericValue(a) - getNumericValue(b);
185
126
  };
186
127
 
187
- // Convert NetworkEntry to NetworkRequest for UI display
188
- const processNetworkEntries = (
189
- networkEntries: Map<RequestId, NetworkEntry>
128
+ const processNetworkRequests = (
129
+ processedRequests: ProcessedRequest[],
130
+ overrides: Map<string, RequestOverride>,
131
+ showEntirePathAsName = false
190
132
  ): NetworkRequest[] => {
191
- return Array.from(networkEntries.values()).map((entry): NetworkRequest => {
192
- const { domain, path } = extractDomainAndPath(entry.url);
193
- const duration = entry.duration || 0;
133
+ return processedRequests.map((request): NetworkRequest => {
134
+ const { domain, path } = extractDomainAndPath(request.name);
135
+ const duration = request.duration || 0;
136
+ const hasOverride = overrides.has(request.name);
194
137
 
195
138
  return {
196
- id: entry.requestId,
197
- name: generateName(entry.url),
198
- status: entry.response?.status || 0,
199
- method: entry.request?.method || 'GET',
139
+ id: request.id,
140
+ name: generateName(request.name, showEntirePathAsName),
141
+ status: request.httpStatus || request.status,
142
+ method: request.method,
200
143
  domain,
201
144
  path,
202
- size: formatSize(entry.size || 0),
145
+ size: isNumber(request.size) ? formatSize(request.size) : '—',
203
146
  time: formatDuration(duration),
204
- type: mapResourceType(entry.type || 'Other'),
205
- initiator: formatInitiator(entry.initiator),
206
- startTime: formatStartTime(entry.startTime || 0),
207
- requestBody: entry.request?.postData
208
- ? {
209
- type: getHttpHeaderValue(entry.request.headers, 'content-type') || 'text/plain',
210
- data: entry.request.postData,
211
- }
212
- : undefined,
213
- responseBody: entry.responseBody
214
- ? {
215
- type: entry.response?.contentType || 'application/octet-stream',
216
- data: entry.responseBody.body,
217
- }
218
- : undefined,
147
+ type: request.type,
148
+ startTime: formatStartTime(request.timestamp),
149
+ hasOverride: hasOverride,
219
150
  };
220
151
  });
221
152
  };
@@ -231,8 +162,14 @@ const columns = [
231
162
  }),
232
163
  columnHelper.accessor('name', {
233
164
  header: 'Name',
234
- cell: ({ getValue }) => (
235
- <div className="flex-1 min-w-0 truncate">{getValue()}</div>
165
+ cell: ({ row, getValue }) => (
166
+ <div className="flex-1 min-w-0 truncate" title={row.original.path}>
167
+ {getValue()}
168
+
169
+ {row.original.hasOverride && (
170
+ <span className="w-2 h-2 rounded-full bg-violet-300 ms-2 inline-block"></span>
171
+ )}
172
+ </div>
236
173
  ),
237
174
  sortingFn: 'alphanumeric',
238
175
  }),
@@ -262,28 +199,63 @@ const columns = [
262
199
  }),
263
200
  columnHelper.accessor('size', {
264
201
  header: 'Size',
265
- cell: ({ getValue }) => <div className="text-gray-300">{getValue()}</div>,
202
+ cell: ({ getValue }) => (
203
+ <div className="text-gray-300 whitespace-nowrap">{getValue()}</div>
204
+ ),
266
205
  size: 80,
267
206
  sortingFn: sortSize,
268
207
  }),
269
208
  columnHelper.accessor('time', {
270
209
  header: 'Time',
271
- cell: ({ getValue }) => <div className="text-gray-300">{getValue()}</div>,
210
+ cell: ({ getValue }) => (
211
+ <div className="text-gray-300 whitespace-nowrap">{getValue()}</div>
212
+ ),
272
213
  size: 80,
273
214
  sortingFn: sortTime,
274
215
  }),
275
216
  ];
276
217
 
277
- export const RequestList: React.FC<RequestListProps> = ({
278
- networkEntries,
279
- selectedRequestId,
280
- onRequestSelect,
281
- }) => {
282
- const [sorting, setSorting] = React.useState<SortingState>([]);
218
+ export type RequestListProps = {
219
+ filter: FilterState;
220
+ };
283
221
 
284
- const requests = React.useMemo(() => {
285
- return processNetworkEntries(networkEntries);
286
- }, [networkEntries]);
222
+ export const RequestList = ({ filter }: RequestListProps) => {
223
+ const actions = useNetworkActivityActions();
224
+ const processedRequests = useProcessedRequests();
225
+ const selectedRequestId = useSelectedRequestId();
226
+ const [sorting, setSorting] = useState<SortingState>([]);
227
+ const overrides = useOverrides();
228
+ const clientUISettings = useClientUISettings();
229
+
230
+ // Filter requests based on current filter state
231
+ const filteredRequests = useMemo(() => {
232
+ return processedRequests.filter((request) => {
233
+ // Type filter
234
+ if (!filter.types.has(request.type)) {
235
+ return false;
236
+ }
237
+
238
+ // Text filter
239
+ if (filter.text) {
240
+ const searchText = filter.text.toLowerCase();
241
+ const searchableFields = [
242
+ request.name,
243
+ request.method,
244
+ request.status.toString(),
245
+ ]
246
+ .join(' ')
247
+ .toLowerCase();
248
+
249
+ return searchableFields.includes(searchText);
250
+ }
251
+
252
+ return true;
253
+ });
254
+ }, [processedRequests, filter]);
255
+
256
+ const requests = useMemo(() => {
257
+ return processNetworkRequests(filteredRequests, overrides, clientUISettings?.showUrlAsName);
258
+ }, [filteredRequests, overrides, clientUISettings?.showUrlAsName]);
287
259
 
288
260
  const table = useReactTable({
289
261
  data: requests,
@@ -296,6 +268,10 @@ export const RequestList: React.FC<RequestListProps> = ({
296
268
  },
297
269
  });
298
270
 
271
+ const onRequestSelect = (requestId: RequestId): void => {
272
+ actions.setSelectedRequest(requestId);
273
+ };
274
+
299
275
  return (
300
276
  <div className="flex-1 overflow-auto">
301
277
  <table className="w-full">
@@ -367,11 +343,5 @@ export {
367
343
  formatStartTime,
368
344
  extractDomainAndPath,
369
345
  generateName,
370
- formatInitiator,
371
- mapResourceType,
372
- getTypeColor,
373
- getStatusColor,
374
- processNetworkEntries,
346
+ processNetworkRequests,
375
347
  };
376
-
377
- export type { NetworkRequest };
@@ -18,6 +18,7 @@ const ScrollArea = React.forwardRef<
18
18
  {children}
19
19
  </ScrollAreaPrimitive.Viewport>
20
20
  <ScrollBar />
21
+ <ScrollBar orientation="horizontal" />
21
22
  <ScrollAreaPrimitive.Corner />
22
23
  </ScrollAreaPrimitive.Root>
23
24
  ));
@@ -0,0 +1,46 @@
1
+ import React, { useState } from 'react';
2
+ import { cn } from '../utils/cn';
3
+
4
+ export type SectionProps = {
5
+ title: string;
6
+ children: React.ReactNode;
7
+ collapsible?: boolean;
8
+ action?: React.ReactNode;
9
+ };
10
+
11
+ export const Section = ({
12
+ title,
13
+ children,
14
+ collapsible = true,
15
+ action,
16
+ }: SectionProps) => {
17
+ const [isCollapsed, setIsCollapsed] = useState(false);
18
+
19
+ const isChildrenVisible = !collapsible || !isCollapsed;
20
+
21
+ const handleCollapseSection = () => {
22
+ setIsCollapsed((prevState) => !prevState);
23
+ };
24
+
25
+ const headerClassName = `flex items-center w-full text-left text-sm text-gray-300 mb-2 ${
26
+ collapsible ? 'hover:text-white' : 'cursor-default'
27
+ }`;
28
+
29
+ return (
30
+ <div>
31
+ <button
32
+ onClick={collapsible ? handleCollapseSection : undefined}
33
+ className={headerClassName}
34
+ tabIndex={collapsible ? 0 : -1}
35
+ >
36
+ {collapsible && (
37
+ <span className={cn('mr-2', { 'rotate-90': !isCollapsed })}>▶</span>
38
+ )}
39
+ <span className="font-medium me-auto">{title}</span>
40
+
41
+ {action}
42
+ </button>
43
+ {isChildrenVisible && children}
44
+ </div>
45
+ );
46
+ };