@rozenite/network-activity-plugin 1.0.0-alpha.15 → 1.0.0-alpha.16

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 (30) hide show
  1. package/dist/App.html +2 -2
  2. package/dist/assets/{App-CfJuBHc_.css → App-BrSkOkws.css} +21 -0
  3. package/dist/assets/{App-CZPlDq_M.js → App-CM3Ub2ZA.js} +911 -479
  4. package/dist/rozenite.json +1 -1
  5. package/dist/src/react-native/http/overrides-registry.d.ts +6 -0
  6. package/dist/src/react-native/http/xhr-interceptor.d.ts +5 -0
  7. package/dist/src/shared/client.d.ts +7 -0
  8. package/dist/src/ui/components/CodeEditor.d.ts +5 -0
  9. package/dist/src/ui/components/OverrideResponse.d.ts +8 -0
  10. package/dist/src/ui/components/RequestList.d.ts +3 -2
  11. package/dist/src/ui/components/Section.d.ts +2 -1
  12. package/dist/src/ui/state/hooks.d.ts +3 -0
  13. package/dist/src/ui/state/store.d.ts +26 -3
  14. package/dist/useNetworkActivityDevTools.cjs +72 -0
  15. package/dist/useNetworkActivityDevTools.js +72 -0
  16. package/package.json +4 -4
  17. package/src/react-native/http/network-inspector.ts +50 -0
  18. package/src/react-native/http/overrides-registry.ts +32 -0
  19. package/src/react-native/http/xhr-interceptor.ts +14 -0
  20. package/src/react-native/useNetworkActivityDevTools.ts +6 -0
  21. package/src/shared/client.ts +9 -0
  22. package/src/ui/components/CodeEditor.tsx +26 -0
  23. package/src/ui/components/OverrideResponse.tsx +132 -0
  24. package/src/ui/components/RequestList.tsx +15 -8
  25. package/src/ui/components/Section.tsx +5 -1
  26. package/src/ui/components/SidePanel.tsx +8 -0
  27. package/src/ui/state/hooks.ts +4 -0
  28. package/src/ui/state/store.ts +585 -502
  29. package/src/ui/tabs/ResponseTab.tsx +60 -12
  30. package/src/ui/views/InspectorView.tsx +7 -1
@@ -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
+ };
@@ -9,9 +9,10 @@ import {
9
9
  useReactTable,
10
10
  } from '@tanstack/react-table';
11
11
  import { ProcessedRequest } from '../state/model';
12
- import { RequestId } from '../../shared/client';
12
+ import { RequestId, RequestOverride } from '../../shared/client';
13
13
  import {
14
14
  useNetworkActivityActions,
15
+ useOverrides,
15
16
  useProcessedRequests,
16
17
  useSelectedRequestId,
17
18
  } from '../state/hooks';
@@ -30,6 +31,7 @@ type NetworkRequest = {
30
31
  time: string;
31
32
  type: string;
32
33
  startTime: string;
34
+ hasOverride: boolean;
33
35
  };
34
36
 
35
37
  const formatSize = (bytes: number): string => {
@@ -122,11 +124,13 @@ const sortTime: SortingFn<NetworkRequest> = (rowA, rowB, columnId) => {
122
124
  };
123
125
 
124
126
  const processNetworkRequests = (
125
- processedRequests: ProcessedRequest[]
127
+ processedRequests: ProcessedRequest[],
128
+ overrides: Map<string, RequestOverride>
126
129
  ): NetworkRequest[] => {
127
130
  return processedRequests.map((request): NetworkRequest => {
128
131
  const { domain, path } = extractDomainAndPath(request.name);
129
132
  const duration = request.duration || 0;
133
+ const hasOverride = overrides.has(request.name);
130
134
 
131
135
  return {
132
136
  id: request.id,
@@ -139,6 +143,7 @@ const processNetworkRequests = (
139
143
  time: formatDuration(duration),
140
144
  type: request.type,
141
145
  startTime: formatStartTime(request.timestamp),
146
+ hasOverride: hasOverride,
142
147
  };
143
148
  });
144
149
  };
@@ -155,11 +160,12 @@ const columns = [
155
160
  columnHelper.accessor('name', {
156
161
  header: 'Name',
157
162
  cell: ({ row, getValue }) => (
158
- <div
159
- className="flex-1 min-w-0 truncate"
160
- title={row.original.path}
161
- >
163
+ <div className="flex-1 min-w-0 truncate" title={row.original.path}>
162
164
  {getValue()}
165
+
166
+ {row.original.hasOverride && (
167
+ <span className="w-2 h-2 rounded-full bg-violet-300 ms-2 inline-block"></span>
168
+ )}
163
169
  </div>
164
170
  ),
165
171
  sortingFn: 'alphanumeric',
@@ -215,6 +221,7 @@ export const RequestList = ({ filter }: RequestListProps) => {
215
221
  const processedRequests = useProcessedRequests();
216
222
  const selectedRequestId = useSelectedRequestId();
217
223
  const [sorting, setSorting] = useState<SortingState>([]);
224
+ const overrides = useOverrides();
218
225
 
219
226
  // Filter requests based on current filter state
220
227
  const filteredRequests = useMemo(() => {
@@ -243,8 +250,8 @@ export const RequestList = ({ filter }: RequestListProps) => {
243
250
  }, [processedRequests, filter]);
244
251
 
245
252
  const requests = useMemo(() => {
246
- return processNetworkRequests(filteredRequests);
247
- }, [filteredRequests]);
253
+ return processNetworkRequests(filteredRequests, overrides);
254
+ }, [filteredRequests, overrides]);
248
255
 
249
256
  const table = useReactTable({
250
257
  data: requests,
@@ -5,12 +5,14 @@ export type SectionProps = {
5
5
  title: string;
6
6
  children: React.ReactNode;
7
7
  collapsible?: boolean;
8
+ action?: React.ReactNode;
8
9
  };
9
10
 
10
11
  export const Section = ({
11
12
  title,
12
13
  children,
13
14
  collapsible = true,
15
+ action,
14
16
  }: SectionProps) => {
15
17
  const [isCollapsed, setIsCollapsed] = useState(false);
16
18
 
@@ -34,7 +36,9 @@ export const Section = ({
34
36
  {collapsible && (
35
37
  <span className={cn('mr-2', { 'rotate-90': !isCollapsed })}>▶</span>
36
38
  )}
37
- <span className="font-medium">{title}</span>
39
+ <span className="font-medium me-auto">{title}</span>
40
+
41
+ {action}
38
42
  </button>
39
43
  {isChildrenVisible && children}
40
44
  </div>
@@ -10,6 +10,7 @@ import { X } from 'lucide-react';
10
10
  import {
11
11
  useNetworkActivityActions,
12
12
  useNetworkActivityStore,
13
+ useOverrides,
13
14
  useSelectedRequest,
14
15
  } from '../state/hooks';
15
16
  import { NetworkEntry as OldNetworkEntry } from '../types';
@@ -85,6 +86,7 @@ export const SidePanel = () => {
85
86
  const actions = useNetworkActivityActions();
86
87
  const selectedRequest = useSelectedRequest();
87
88
  const client = useNetworkActivityStore((state) => state._client);
89
+ const overrides = useOverrides();
88
90
 
89
91
  const onClose = (): void => {
90
92
  actions.setSelectedRequest(null);
@@ -128,6 +130,9 @@ export const SidePanel = () => {
128
130
  legacyNetworkEntries.set(legacyEntry.requestId, legacyEntry);
129
131
  }
130
132
 
133
+ const override = legacyEntry !== null ? overrides.get(legacyEntry.url) : null;
134
+ const hasResponseOverride = override && override.body ? true : false;
135
+
131
136
  const getTabsListTriggers = () => {
132
137
  if (httpDetails) {
133
138
  return (
@@ -149,6 +154,9 @@ export const SidePanel = () => {
149
154
  className="data-[state=active]:bg-gray-700"
150
155
  >
151
156
  Response
157
+ {hasResponseOverride && (
158
+ <span className="w-2 h-2 rounded-full bg-violet-300 ms-2 inline-block"></span>
159
+ )}
152
160
  </TabsTrigger>
153
161
  <TabsTrigger
154
162
  value="cookies"
@@ -42,3 +42,7 @@ export const useWebSocketMessages = (requestId: string) => {
42
42
  (state) => state.websocketMessages.get(requestId) || []
43
43
  );
44
44
  };
45
+
46
+ export const useOverrides = () => {
47
+ return useNetworkActivityStore((state) => state.overrides);
48
+ };