@rozenite/network-activity-plugin 1.0.0-alpha.5 → 1.0.0-alpha.7
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/{panel.html → App.html} +3 -3
- package/dist/assets/App-CIflVb88.js +24164 -0
- package/dist/assets/App-Czu6Vt2P.css +1233 -0
- package/dist/react-native.cjs +1 -1
- package/dist/react-native.d.ts +1 -90
- package/dist/rozenite.config.d.ts +7 -0
- package/dist/rozenite.json +1 -1
- package/dist/src/react-native/network-inspector.d.ts +8 -0
- package/dist/src/react-native/network-requests-registry.d.ts +6 -0
- package/dist/src/react-native/useNetworkActivityDevTools.d.ts +2 -0
- package/dist/src/react-native/xhr-interceptor.d.ts +38 -0
- package/dist/src/shared/client.d.ts +64 -0
- package/dist/src/ui/App.d.ts +1 -0
- package/dist/src/ui/components/Badge.d.ts +9 -0
- package/dist/src/ui/components/Button.d.ts +11 -0
- package/dist/src/ui/components/Input.d.ts +3 -0
- package/dist/src/ui/components/JsonTree.d.ts +5 -0
- package/dist/src/ui/components/RequestList.d.ts +45 -0
- package/dist/src/ui/components/ScrollArea.d.ts +4 -0
- package/dist/src/ui/components/Separator.d.ts +3 -0
- package/dist/src/ui/tabs/CookiesTab.d.ts +8 -0
- package/dist/src/ui/tabs/HeadersTab.d.ts +17 -0
- package/dist/src/ui/tabs/RequestTab.d.ts +10 -0
- package/dist/src/ui/tabs/ResponseTab.d.ts +12 -0
- package/dist/src/ui/tabs/TimingTab.d.ts +7 -0
- package/dist/src/ui/types.d.ts +23 -0
- package/dist/src/ui/utils.d.ts +2 -0
- package/dist/src/ui/views/InspectorView.d.ts +5 -0
- package/dist/src/ui/views/LoadingView.d.ts +1 -0
- package/dist/useNetworkActivityDevTools.cjs +360 -0
- package/dist/useNetworkActivityDevTools.js +108 -236
- package/package.json +23 -16
- package/postcss.config.js +6 -0
- package/rozenite.config.ts +1 -1
- package/src/react-native/network-inspector.ts +113 -260
- package/src/react-native/network-requests-registry.ts +7 -77
- package/src/react-native/useNetworkActivityDevTools.ts +1 -1
- package/src/react-native/xhr-interceptor.ts +2 -2
- package/src/react-native/xml-request.d.ts +11 -1
- package/src/shared/client.ts +80 -0
- package/src/ui/App.tsx +19 -0
- package/src/ui/components/Badge.tsx +36 -0
- package/src/ui/components/Button.tsx +56 -0
- package/src/ui/components/Input.tsx +22 -0
- package/src/ui/components/JsonTree.tsx +37 -0
- package/src/ui/components/RequestList.tsx +376 -0
- package/src/ui/components/ScrollArea.tsx +48 -0
- package/src/ui/components/Separator.tsx +31 -0
- package/src/ui/components/Tabs.tsx +55 -0
- package/src/ui/globals.css +90 -0
- package/src/ui/tabs/CookiesTab.tsx +290 -0
- package/src/ui/tabs/HeadersTab.tsx +117 -0
- package/src/ui/tabs/RequestTab.tsx +72 -0
- package/src/ui/tabs/ResponseTab.tsx +140 -0
- package/src/ui/tabs/TimingTab.tsx +71 -0
- package/src/ui/types.ts +30 -0
- package/src/ui/utils.ts +5 -97
- package/src/ui/views/InspectorView.tsx +349 -0
- package/src/ui/views/LoadingView.tsx +19 -0
- package/tailwind.config.ts +93 -0
- package/dist/assets/panel-BNxB_KsS.js +0 -16663
- package/dist/assets/panel-DXGMsavf.css +0 -555
- package/src/types/client.ts +0 -111
- package/src/types/network.ts +0 -32
- package/src/ui/components.module.css +0 -158
- package/src/ui/components.tsx +0 -241
- package/src/ui/network-details.module.css +0 -197
- package/src/ui/network-details.tsx +0 -345
- package/src/ui/network-list.module.css +0 -128
- package/src/ui/network-list.tsx +0 -240
- package/src/ui/network-toolbar.module.css +0 -9
- package/src/ui/network-toolbar.tsx +0 -34
- package/src/ui/panel.module.css +0 -67
- package/src/ui/panel.tsx +0 -318
- package/src/ui/tanstack-query.tsx +0 -204
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Button, Toolbar } from './components';
|
|
3
|
-
import styles from './network-toolbar.module.css';
|
|
4
|
-
|
|
5
|
-
interface NetworkToolbarProps {
|
|
6
|
-
isRecording: boolean;
|
|
7
|
-
onToggleRecording: () => void;
|
|
8
|
-
onClear: () => void;
|
|
9
|
-
requestCount: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const NetworkToolbar: React.FC<NetworkToolbarProps> = ({
|
|
13
|
-
isRecording,
|
|
14
|
-
onToggleRecording,
|
|
15
|
-
onClear,
|
|
16
|
-
requestCount,
|
|
17
|
-
}) => {
|
|
18
|
-
return (
|
|
19
|
-
<Toolbar>
|
|
20
|
-
<Button
|
|
21
|
-
onClick={onToggleRecording}
|
|
22
|
-
variant={isRecording ? 'danger' : 'success'}
|
|
23
|
-
size="small"
|
|
24
|
-
className={styles.recordingButton}
|
|
25
|
-
>
|
|
26
|
-
{isRecording ? 'Stop' : 'Start'} Recording
|
|
27
|
-
</Button>
|
|
28
|
-
<Button onClick={onClear} variant="secondary" size="small">
|
|
29
|
-
Clear
|
|
30
|
-
</Button>
|
|
31
|
-
<div className={styles.requestCount}>{requestCount} requests</div>
|
|
32
|
-
</Toolbar>
|
|
33
|
-
);
|
|
34
|
-
};
|
package/src/ui/panel.module.css
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
.container {
|
|
2
|
-
height: 100vh;
|
|
3
|
-
display: flex;
|
|
4
|
-
flex-direction: column;
|
|
5
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.mainContent {
|
|
9
|
-
flex: 1;
|
|
10
|
-
display: flex;
|
|
11
|
-
overflow: hidden;
|
|
12
|
-
min-height: 0;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.networkListContainer {
|
|
16
|
-
width: 60%;
|
|
17
|
-
border-right: 1px solid #e0e0e0;
|
|
18
|
-
display: flex;
|
|
19
|
-
flex-direction: column;
|
|
20
|
-
min-width: 0;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
.listContent {
|
|
24
|
-
flex: 1;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.detailsContainer {
|
|
28
|
-
width: 40%;
|
|
29
|
-
display: flex;
|
|
30
|
-
flex-direction: column;
|
|
31
|
-
min-width: 0;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.headerStatus {
|
|
35
|
-
width: 60px;
|
|
36
|
-
text-align: center;
|
|
37
|
-
flex-shrink: 0;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.headerMethod {
|
|
41
|
-
width: 80px;
|
|
42
|
-
text-align: center;
|
|
43
|
-
flex-shrink: 0;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.headerName {
|
|
47
|
-
flex: 1;
|
|
48
|
-
min-width: 0;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.headerType {
|
|
52
|
-
width: 100px;
|
|
53
|
-
text-align: center;
|
|
54
|
-
flex-shrink: 0;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.headerTime {
|
|
58
|
-
width: 80px;
|
|
59
|
-
text-align: right;
|
|
60
|
-
flex-shrink: 0;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.headerSize {
|
|
64
|
-
width: 80px;
|
|
65
|
-
text-align: right;
|
|
66
|
-
flex-shrink: 0;
|
|
67
|
-
}
|
package/src/ui/panel.tsx
DELETED
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useMemo } from 'react';
|
|
2
|
-
import { useRozeniteDevToolsClient } from '@rozenite/plugin-bridge';
|
|
3
|
-
import {
|
|
4
|
-
NetworkActivityEventMap,
|
|
5
|
-
NetworkRequestId,
|
|
6
|
-
NetworkResourceType,
|
|
7
|
-
} from '../types/client';
|
|
8
|
-
import { NetworkEntry } from '../types/network';
|
|
9
|
-
import styles from './panel.module.css';
|
|
10
|
-
import { NetworkToolbar } from './network-toolbar';
|
|
11
|
-
import { PanelHeader } from './components';
|
|
12
|
-
import { NetworkList } from './network-list';
|
|
13
|
-
import { NetworkDetails } from './network-details';
|
|
14
|
-
|
|
15
|
-
// Enhanced network entry with CDP data
|
|
16
|
-
type EnhancedNetworkEntry = NetworkEntry & {
|
|
17
|
-
// CDP specific fields
|
|
18
|
-
loaderId?: string;
|
|
19
|
-
documentURL?: string;
|
|
20
|
-
type?: NetworkResourceType;
|
|
21
|
-
initiator?: {
|
|
22
|
-
type: string;
|
|
23
|
-
url?: string;
|
|
24
|
-
lineNumber?: number;
|
|
25
|
-
columnNumber?: number;
|
|
26
|
-
};
|
|
27
|
-
// Request details
|
|
28
|
-
request?: {
|
|
29
|
-
url: string;
|
|
30
|
-
method: string;
|
|
31
|
-
headers: Record<string, string>;
|
|
32
|
-
postData?: string;
|
|
33
|
-
hasPostData?: boolean;
|
|
34
|
-
};
|
|
35
|
-
// Response details
|
|
36
|
-
response?: {
|
|
37
|
-
url: string;
|
|
38
|
-
status: number;
|
|
39
|
-
statusText: string;
|
|
40
|
-
headers: Record<string, string>;
|
|
41
|
-
mimeType: string;
|
|
42
|
-
encodedDataLength: number;
|
|
43
|
-
responseTime: number;
|
|
44
|
-
};
|
|
45
|
-
// Response body
|
|
46
|
-
responseBody?: {
|
|
47
|
-
body: string;
|
|
48
|
-
base64Encoded: boolean;
|
|
49
|
-
};
|
|
50
|
-
// Timing and data
|
|
51
|
-
dataLength?: number;
|
|
52
|
-
// Error details
|
|
53
|
-
blockedReason?: string;
|
|
54
|
-
corsErrorStatus?: any;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export default function NetworkActivityPanel() {
|
|
58
|
-
const client = useRozeniteDevToolsClient<NetworkActivityEventMap>({
|
|
59
|
-
pluginId: '@rozenite/network-activity-plugin',
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const [networkEntries, setNetworkEntries] = useState<
|
|
63
|
-
Map<string, EnhancedNetworkEntry>
|
|
64
|
-
>(new Map());
|
|
65
|
-
const [selectedRequestId, setSelectedRequestId] = useState<string | null>(
|
|
66
|
-
null
|
|
67
|
-
);
|
|
68
|
-
const [isRecording, setIsRecording] = useState(true);
|
|
69
|
-
const [containerHeight, setContainerHeight] = useState(0);
|
|
70
|
-
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
71
|
-
|
|
72
|
-
// Convert Map to sorted array for rendering
|
|
73
|
-
const sortedEntries = useMemo(() => {
|
|
74
|
-
return Array.from(networkEntries.values()).sort(
|
|
75
|
-
(a, b) => b.startTime - a.startTime
|
|
76
|
-
);
|
|
77
|
-
}, [networkEntries]);
|
|
78
|
-
|
|
79
|
-
const selectedEntry = selectedRequestId
|
|
80
|
-
? networkEntries.get(selectedRequestId) || null
|
|
81
|
-
: null;
|
|
82
|
-
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
if (!client) {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (isRecording) {
|
|
89
|
-
client.send('network-enable', {});
|
|
90
|
-
} else {
|
|
91
|
-
client.send('network-disable', {});
|
|
92
|
-
}
|
|
93
|
-
}, [client, isRecording]);
|
|
94
|
-
|
|
95
|
-
useEffect(() => {
|
|
96
|
-
if (!client) return;
|
|
97
|
-
|
|
98
|
-
const subscriptions: Array<{ remove: () => void }> = [];
|
|
99
|
-
|
|
100
|
-
// Subscribe to CDP Network events
|
|
101
|
-
subscriptions.push(
|
|
102
|
-
client.onMessage('Network.requestWillBeSent', (payload) => {
|
|
103
|
-
setNetworkEntries((prev) => {
|
|
104
|
-
const newMap = new Map(prev);
|
|
105
|
-
const existing = newMap.get(payload.requestId);
|
|
106
|
-
|
|
107
|
-
newMap.set(payload.requestId, {
|
|
108
|
-
requestId: payload.requestId,
|
|
109
|
-
loaderId: payload.loaderId,
|
|
110
|
-
documentURL: payload.documentURL,
|
|
111
|
-
url: payload.request.url,
|
|
112
|
-
method: payload.request.method,
|
|
113
|
-
headers: payload.request.headers,
|
|
114
|
-
postData: payload.request.postData,
|
|
115
|
-
hasPostData: payload.request.hasPostData,
|
|
116
|
-
status: 'pending',
|
|
117
|
-
startTime: payload.timestamp,
|
|
118
|
-
type: payload.type,
|
|
119
|
-
initiator: payload.initiator,
|
|
120
|
-
request: payload.request,
|
|
121
|
-
// Preserve existing response data if this is a redirect
|
|
122
|
-
...(existing && {
|
|
123
|
-
response: existing.response,
|
|
124
|
-
status: existing.status,
|
|
125
|
-
endTime: existing.endTime,
|
|
126
|
-
duration: existing.duration,
|
|
127
|
-
encodedDataLength: existing.encodedDataLength,
|
|
128
|
-
dataLength: existing.dataLength,
|
|
129
|
-
errorText: existing.errorText,
|
|
130
|
-
canceled: existing.canceled,
|
|
131
|
-
}),
|
|
132
|
-
});
|
|
133
|
-
return newMap;
|
|
134
|
-
});
|
|
135
|
-
})
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
subscriptions.push(
|
|
139
|
-
client.onMessage('Network.responseReceived', (payload) => {
|
|
140
|
-
setNetworkEntries((prev) => {
|
|
141
|
-
const newMap = new Map(prev);
|
|
142
|
-
const entry = newMap.get(payload.requestId);
|
|
143
|
-
if (entry) {
|
|
144
|
-
newMap.set(payload.requestId, {
|
|
145
|
-
...entry,
|
|
146
|
-
response: payload.response,
|
|
147
|
-
status: 'loading',
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
return newMap;
|
|
151
|
-
});
|
|
152
|
-
})
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
subscriptions.push(
|
|
156
|
-
client.onMessage('Network.dataReceived', (payload) => {
|
|
157
|
-
setNetworkEntries((prev) => {
|
|
158
|
-
const newMap = new Map(prev);
|
|
159
|
-
const entry = newMap.get(payload.requestId);
|
|
160
|
-
if (entry) {
|
|
161
|
-
newMap.set(payload.requestId, {
|
|
162
|
-
...entry,
|
|
163
|
-
dataLength: payload.dataLength,
|
|
164
|
-
encodedDataLength: payload.encodedDataLength,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
return newMap;
|
|
168
|
-
});
|
|
169
|
-
})
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
subscriptions.push(
|
|
173
|
-
client.onMessage('Network.loadingFinished', (payload) => {
|
|
174
|
-
setNetworkEntries((prev) => {
|
|
175
|
-
const newMap = new Map(prev);
|
|
176
|
-
const entry = newMap.get(payload.requestId);
|
|
177
|
-
if (entry) {
|
|
178
|
-
const endTime = payload.timestamp;
|
|
179
|
-
const duration = endTime - entry.startTime;
|
|
180
|
-
|
|
181
|
-
newMap.set(payload.requestId, {
|
|
182
|
-
...entry,
|
|
183
|
-
status: 'finished',
|
|
184
|
-
endTime,
|
|
185
|
-
duration,
|
|
186
|
-
encodedDataLength: payload.encodedDataLength,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
return newMap;
|
|
190
|
-
});
|
|
191
|
-
})
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
subscriptions.push(
|
|
195
|
-
client.onMessage('Network.loadingFailed', (payload) => {
|
|
196
|
-
setNetworkEntries((prev) => {
|
|
197
|
-
const newMap = new Map(prev);
|
|
198
|
-
const entry = newMap.get(payload.requestId);
|
|
199
|
-
if (entry) {
|
|
200
|
-
const endTime = payload.timestamp;
|
|
201
|
-
const duration = endTime - entry.startTime;
|
|
202
|
-
|
|
203
|
-
newMap.set(payload.requestId, {
|
|
204
|
-
...entry,
|
|
205
|
-
status: 'failed',
|
|
206
|
-
endTime,
|
|
207
|
-
duration,
|
|
208
|
-
errorText: payload.errorText,
|
|
209
|
-
canceled: payload.canceled,
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
return newMap;
|
|
213
|
-
});
|
|
214
|
-
})
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
subscriptions.push(
|
|
218
|
-
client.onMessage('Network.responseBodyReceived', (payload) => {
|
|
219
|
-
setNetworkEntries((prev) => {
|
|
220
|
-
const newMap = new Map(prev);
|
|
221
|
-
const entry = newMap.get(payload.requestId);
|
|
222
|
-
if (entry) {
|
|
223
|
-
newMap.set(payload.requestId, {
|
|
224
|
-
...entry,
|
|
225
|
-
responseBody: {
|
|
226
|
-
body: payload.body,
|
|
227
|
-
base64Encoded: payload.base64Encoded,
|
|
228
|
-
},
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
return newMap;
|
|
232
|
-
});
|
|
233
|
-
})
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
return () => {
|
|
237
|
-
subscriptions.forEach((sub) => sub.remove());
|
|
238
|
-
};
|
|
239
|
-
}, [client, isRecording]);
|
|
240
|
-
|
|
241
|
-
const clearNetworkLog = () => {
|
|
242
|
-
setNetworkEntries(new Map());
|
|
243
|
-
setSelectedRequestId(null);
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
const toggleRecording = () => {
|
|
247
|
-
setIsRecording(!isRecording);
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
const handleSelectRequest = (requestId: string) => {
|
|
251
|
-
setSelectedRequestId(requestId);
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
const requestResponseBody = (requestId: string) => {
|
|
255
|
-
if (client) {
|
|
256
|
-
client.send('Network.getResponseBody', { requestId });
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
// Update container height on mount and resize
|
|
261
|
-
useEffect(() => {
|
|
262
|
-
const updateHeight = () => {
|
|
263
|
-
if (containerRef.current) {
|
|
264
|
-
const rect = containerRef.current.getBoundingClientRect();
|
|
265
|
-
setContainerHeight(rect.height - 120); // Subtract toolbar and header height
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
updateHeight();
|
|
270
|
-
window.addEventListener('resize', updateHeight);
|
|
271
|
-
return () => window.removeEventListener('resize', updateHeight);
|
|
272
|
-
}, []);
|
|
273
|
-
|
|
274
|
-
return (
|
|
275
|
-
<div ref={containerRef} className={styles.container}>
|
|
276
|
-
{/* Toolbar */}
|
|
277
|
-
<NetworkToolbar
|
|
278
|
-
isRecording={isRecording}
|
|
279
|
-
onToggleRecording={toggleRecording}
|
|
280
|
-
onClear={clearNetworkLog}
|
|
281
|
-
requestCount={sortedEntries.length}
|
|
282
|
-
/>
|
|
283
|
-
|
|
284
|
-
{/* Main Content */}
|
|
285
|
-
<div className={styles.mainContent}>
|
|
286
|
-
{/* Network List */}
|
|
287
|
-
<div className={styles.networkListContainer}>
|
|
288
|
-
{/* List Header */}
|
|
289
|
-
<PanelHeader>
|
|
290
|
-
<div className={styles.headerStatus}>Status</div>
|
|
291
|
-
<div className={styles.headerMethod}>Method</div>
|
|
292
|
-
<div className={styles.headerName}>Name</div>
|
|
293
|
-
<div className={styles.headerType}>Type</div>
|
|
294
|
-
<div className={styles.headerTime}>Time</div>
|
|
295
|
-
<div className={styles.headerSize}>Size</div>
|
|
296
|
-
</PanelHeader>
|
|
297
|
-
|
|
298
|
-
<div className={styles.listContent}>
|
|
299
|
-
<NetworkList
|
|
300
|
-
entries={sortedEntries}
|
|
301
|
-
selectedRequestId={selectedRequestId}
|
|
302
|
-
onSelect={handleSelectRequest}
|
|
303
|
-
height={containerHeight}
|
|
304
|
-
/>
|
|
305
|
-
</div>
|
|
306
|
-
</div>
|
|
307
|
-
|
|
308
|
-
{/* Details Panel */}
|
|
309
|
-
<div className={styles.detailsContainer}>
|
|
310
|
-
<NetworkDetails
|
|
311
|
-
entry={selectedEntry}
|
|
312
|
-
onRequestResponseBody={requestResponseBody}
|
|
313
|
-
/>
|
|
314
|
-
</div>
|
|
315
|
-
</div>
|
|
316
|
-
</div>
|
|
317
|
-
);
|
|
318
|
-
}
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
QueryCacheNotifyEvent,
|
|
3
|
-
MutationCacheNotifyEvent,
|
|
4
|
-
QueryClient,
|
|
5
|
-
QueryClientProvider,
|
|
6
|
-
Query,
|
|
7
|
-
Mutation,
|
|
8
|
-
} from '@tanstack/react-query';
|
|
9
|
-
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools';
|
|
10
|
-
import { useRozeniteDevToolsClient } from '@rozenite/plugin-bridge';
|
|
11
|
-
import { useEffect, useRef } from 'react';
|
|
12
|
-
|
|
13
|
-
const queryClient = new QueryClient({
|
|
14
|
-
defaultOptions: {
|
|
15
|
-
queries: {
|
|
16
|
-
queryFn: async () => {
|
|
17
|
-
// Prevent refetch from throwing an error
|
|
18
|
-
return Promise.resolve(null);
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
type DevToolsEventMap = {
|
|
25
|
-
DEVTOOLS_TO_DEVICE: unknown;
|
|
26
|
-
DEVICE_TO_DEVTOOLS: QueryCacheNotifyEvent | MutationCacheNotifyEvent;
|
|
27
|
-
DEVICE_TO_DEVTOOLS_ACK: { requestId: string; success: boolean };
|
|
28
|
-
DEVICE_TO_DEVTOOLS_INITIAL_DATA: { queries: Query[]; mutations: Mutation[] };
|
|
29
|
-
DEVTOOLS_TO_DEVICE_INITIAL_DATA_REQUEST: unknown;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const Wrapped = () => {
|
|
33
|
-
const client = useRozeniteDevToolsClient<DevToolsEventMap>({
|
|
34
|
-
pluginId: '@rozenite/tanstack-query-plugin',
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// Track pending acknowledgments to prevent feedback loops
|
|
38
|
-
const pendingAcknowledgment = useRef<Set<string>>(new Set());
|
|
39
|
-
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
if (!client) return;
|
|
42
|
-
client.send('DEVTOOLS_TO_DEVICE_INITIAL_DATA_REQUEST', null);
|
|
43
|
-
}, [client]);
|
|
44
|
-
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
if (!client) return;
|
|
47
|
-
|
|
48
|
-
const handleEvent = (event: Event) => {
|
|
49
|
-
const detail = (event as CustomEvent).detail;
|
|
50
|
-
|
|
51
|
-
// Generate a unique request ID for this DevTools action
|
|
52
|
-
const requestId = `devtools-${Date.now()}-${Math.random()
|
|
53
|
-
.toString(36)
|
|
54
|
-
.substr(2, 9)}`;
|
|
55
|
-
|
|
56
|
-
// Mark that we're waiting for acknowledgment
|
|
57
|
-
pendingAcknowledgment.current.add(requestId);
|
|
58
|
-
|
|
59
|
-
// Add the request ID to the event detail
|
|
60
|
-
const eventWithRequestId = {
|
|
61
|
-
...detail,
|
|
62
|
-
requestId,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
client.send('DEVTOOLS_TO_DEVICE', eventWithRequestId);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
window.addEventListener('@tanstack/query-devtools-event', handleEvent);
|
|
69
|
-
return () =>
|
|
70
|
-
window.removeEventListener('@tanstack/query-devtools-event', handleEvent);
|
|
71
|
-
}, [client]);
|
|
72
|
-
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
if (!client) return;
|
|
75
|
-
|
|
76
|
-
const ackSubscription = client.onMessage(
|
|
77
|
-
'DEVICE_TO_DEVTOOLS_ACK',
|
|
78
|
-
(ack) => {
|
|
79
|
-
// Remove the request from pending acknowledgments
|
|
80
|
-
pendingAcknowledgment.current.delete(ack.requestId);
|
|
81
|
-
}
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
const subscription = client.onMessage('DEVICE_TO_DEVTOOLS', (event) => {
|
|
85
|
-
// Don't reflect events if we're waiting for acknowledgments
|
|
86
|
-
if (pendingAcknowledgment.current.size > 0) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if ('query' in event) {
|
|
91
|
-
const { query, type } = event as QueryCacheNotifyEvent;
|
|
92
|
-
const queryCache = queryClient.getQueryCache();
|
|
93
|
-
|
|
94
|
-
if (type === 'updated') {
|
|
95
|
-
const existingQuery = queryCache.get(query.queryHash);
|
|
96
|
-
if (existingQuery) {
|
|
97
|
-
existingQuery.setState(query.state);
|
|
98
|
-
} else {
|
|
99
|
-
queryCache.build(
|
|
100
|
-
queryClient,
|
|
101
|
-
{
|
|
102
|
-
queryKey: query.queryKey,
|
|
103
|
-
queryHash: query.queryHash,
|
|
104
|
-
},
|
|
105
|
-
query.state
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
} else if (type === 'added') {
|
|
109
|
-
const existingQuery = queryCache.get(query.queryHash);
|
|
110
|
-
if (!existingQuery) {
|
|
111
|
-
// Only add if it doesn't already exist
|
|
112
|
-
queryCache.build(
|
|
113
|
-
queryClient,
|
|
114
|
-
{
|
|
115
|
-
queryKey: query.queryKey,
|
|
116
|
-
queryHash: query.queryHash,
|
|
117
|
-
},
|
|
118
|
-
query.state
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
} else if (type === 'removed') {
|
|
122
|
-
const existingQuery = queryCache.get(query.queryHash);
|
|
123
|
-
if (existingQuery) {
|
|
124
|
-
queryCache.remove(existingQuery);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
} else if ('mutation' in event) {
|
|
128
|
-
const { mutation, type } = event as MutationCacheNotifyEvent;
|
|
129
|
-
const mutationCache = queryClient.getMutationCache();
|
|
130
|
-
|
|
131
|
-
if (type === 'added') {
|
|
132
|
-
const existingMutation = mutationCache.find({
|
|
133
|
-
mutationKey: mutation.options.mutationKey,
|
|
134
|
-
});
|
|
135
|
-
if (existingMutation) {
|
|
136
|
-
mutationCache.remove(existingMutation);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
mutationCache.build(queryClient, mutation.options, mutation.state);
|
|
140
|
-
} else if (type === 'removed') {
|
|
141
|
-
const existingMutation = mutationCache.find({
|
|
142
|
-
mutationKey: mutation.options.mutationKey,
|
|
143
|
-
});
|
|
144
|
-
if (existingMutation) {
|
|
145
|
-
mutationCache.remove(existingMutation);
|
|
146
|
-
}
|
|
147
|
-
} else if (type === 'updated') {
|
|
148
|
-
const existingMutation = mutationCache.find({
|
|
149
|
-
mutationKey: mutation.options.mutationKey,
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
if (existingMutation) {
|
|
153
|
-
mutationCache.remove(existingMutation);
|
|
154
|
-
mutationCache.build(queryClient, mutation.options, mutation.state);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
const initialDataSubscription = client.onMessage(
|
|
161
|
-
'DEVICE_TO_DEVTOOLS_INITIAL_DATA',
|
|
162
|
-
(event) => {
|
|
163
|
-
// Clear existing data first
|
|
164
|
-
queryClient.clear();
|
|
165
|
-
queryClient.getMutationCache().clear();
|
|
166
|
-
|
|
167
|
-
// Restore queries
|
|
168
|
-
const queryCache = queryClient.getQueryCache();
|
|
169
|
-
event.queries.forEach((query) => {
|
|
170
|
-
queryCache.build(
|
|
171
|
-
queryClient,
|
|
172
|
-
{
|
|
173
|
-
queryKey: query.queryKey,
|
|
174
|
-
queryHash: query.queryHash,
|
|
175
|
-
},
|
|
176
|
-
query.state
|
|
177
|
-
);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Restore mutations
|
|
181
|
-
const mutationCache = queryClient.getMutationCache();
|
|
182
|
-
event.mutations.forEach((mutation) => {
|
|
183
|
-
mutationCache.build(queryClient, mutation.options, mutation.state);
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
return () => {
|
|
189
|
-
subscription.remove();
|
|
190
|
-
ackSubscription.remove();
|
|
191
|
-
initialDataSubscription.remove();
|
|
192
|
-
};
|
|
193
|
-
}, [client, queryClient]);
|
|
194
|
-
|
|
195
|
-
return <ReactQueryDevtoolsPanel />;
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
export default function TanStackQueryPanel() {
|
|
199
|
-
return (
|
|
200
|
-
<QueryClientProvider client={queryClient}>
|
|
201
|
-
<Wrapped />
|
|
202
|
-
</QueryClientProvider>
|
|
203
|
-
);
|
|
204
|
-
}
|