@buoy-gg/network 1.7.2
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/README.md +381 -0
- package/lib/commonjs/index.js +34 -0
- package/lib/commonjs/network/components/NetworkCopySettingsView.js +867 -0
- package/lib/commonjs/network/components/NetworkEventDetailView.js +837 -0
- package/lib/commonjs/network/components/NetworkEventItemCompact.js +323 -0
- package/lib/commonjs/network/components/NetworkFilterViewV3.js +297 -0
- package/lib/commonjs/network/components/NetworkModal.js +937 -0
- package/lib/commonjs/network/hooks/useNetworkEvents.js +320 -0
- package/lib/commonjs/network/hooks/useTickEveryMinute.js +34 -0
- package/lib/commonjs/network/index.js +102 -0
- package/lib/commonjs/network/types/index.js +1 -0
- package/lib/commonjs/network/utils/extractOperationName.js +80 -0
- package/lib/commonjs/network/utils/formatGraphQLVariables.js +219 -0
- package/lib/commonjs/network/utils/formatting.js +30 -0
- package/lib/commonjs/network/utils/networkEventStore.js +269 -0
- package/lib/commonjs/network/utils/networkListener.js +801 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/preset.js +83 -0
- package/lib/module/index.js +7 -0
- package/lib/module/network/components/NetworkCopySettingsView.js +862 -0
- package/lib/module/network/components/NetworkEventDetailView.js +834 -0
- package/lib/module/network/components/NetworkEventItemCompact.js +320 -0
- package/lib/module/network/components/NetworkFilterViewV3.js +293 -0
- package/lib/module/network/components/NetworkModal.js +933 -0
- package/lib/module/network/hooks/useNetworkEvents.js +316 -0
- package/lib/module/network/hooks/useTickEveryMinute.js +29 -0
- package/lib/module/network/index.js +20 -0
- package/lib/module/network/types/index.js +1 -0
- package/lib/module/network/utils/extractOperationName.js +76 -0
- package/lib/module/network/utils/formatGraphQLVariables.js +213 -0
- package/lib/module/network/utils/formatting.js +9 -0
- package/lib/module/network/utils/networkEventStore.js +265 -0
- package/lib/module/network/utils/networkListener.js +791 -0
- package/lib/module/preset.js +79 -0
- package/lib/typescript/index.d.ts +3 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkCopySettingsView.d.ts +26 -0
- package/lib/typescript/network/components/NetworkCopySettingsView.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts +13 -0
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkEventItemCompact.d.ts +12 -0
- package/lib/typescript/network/components/NetworkEventItemCompact.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkFilterViewV3.d.ts +22 -0
- package/lib/typescript/network/components/NetworkFilterViewV3.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkModal.d.ts +14 -0
- package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -0
- package/lib/typescript/network/hooks/useNetworkEvents.d.ts +72 -0
- package/lib/typescript/network/hooks/useNetworkEvents.d.ts.map +1 -0
- package/lib/typescript/network/hooks/useTickEveryMinute.d.ts +9 -0
- package/lib/typescript/network/hooks/useTickEveryMinute.d.ts.map +1 -0
- package/lib/typescript/network/index.d.ts +12 -0
- package/lib/typescript/network/index.d.ts.map +1 -0
- package/lib/typescript/network/types/index.d.ts +88 -0
- package/lib/typescript/network/types/index.d.ts.map +1 -0
- package/lib/typescript/network/utils/extractOperationName.d.ts +41 -0
- package/lib/typescript/network/utils/extractOperationName.d.ts.map +1 -0
- package/lib/typescript/network/utils/formatGraphQLVariables.d.ts +79 -0
- package/lib/typescript/network/utils/formatGraphQLVariables.d.ts.map +1 -0
- package/lib/typescript/network/utils/formatting.d.ts +6 -0
- package/lib/typescript/network/utils/formatting.d.ts.map +1 -0
- package/lib/typescript/network/utils/networkEventStore.d.ts +81 -0
- package/lib/typescript/network/utils/networkEventStore.d.ts.map +1 -0
- package/lib/typescript/network/utils/networkListener.d.ts +191 -0
- package/lib/typescript/network/utils/networkListener.d.ts.map +1 -0
- package/lib/typescript/preset.d.ts +76 -0
- package/lib/typescript/preset.d.ts.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Network event store for managing captured network requests
|
|
5
|
+
* Works with the Reactotron-style network listener
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { extractOperationName } from "./extractOperationName";
|
|
9
|
+
class NetworkEventStore {
|
|
10
|
+
events = [];
|
|
11
|
+
pendingRequests = new Map();
|
|
12
|
+
listeners = new Set();
|
|
13
|
+
maxEvents = 500; // Configurable max events to prevent memory issues
|
|
14
|
+
recentRequests = new Map(); // Track recent requests to detect duplicates
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Process a network listener event
|
|
18
|
+
*/
|
|
19
|
+
processNetworkEvent(event) {
|
|
20
|
+
const {
|
|
21
|
+
request
|
|
22
|
+
} = event;
|
|
23
|
+
if (event.type === "request") {
|
|
24
|
+
// Check for duplicate request based on URL, method, and timing
|
|
25
|
+
const requestKey = `${request.method}:${request.url}`;
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
const lastRequestTime = this.recentRequests.get(requestKey);
|
|
28
|
+
|
|
29
|
+
// If same request within 50ms, likely a duplicate from XHR/fetch dual interception
|
|
30
|
+
if (lastRequestTime && now - lastRequestTime < 50) {
|
|
31
|
+
return; // Skip duplicate
|
|
32
|
+
}
|
|
33
|
+
this.recentRequests.set(requestKey, now);
|
|
34
|
+
|
|
35
|
+
// Clean up old entries to prevent memory leak
|
|
36
|
+
if (this.recentRequests.size > 100) {
|
|
37
|
+
const cutoff = now - 5000; // Remove entries older than 5 seconds
|
|
38
|
+
for (const [key, time] of this.recentRequests.entries()) {
|
|
39
|
+
if (time < cutoff) {
|
|
40
|
+
this.recentRequests.delete(key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Create new network event for request
|
|
46
|
+
// Build full URL with query params if present
|
|
47
|
+
const queryString = request.params ? `?${new URLSearchParams(request.params).toString()}` : "";
|
|
48
|
+
const fullUrl = `${request.url}${queryString}`;
|
|
49
|
+
|
|
50
|
+
// Extract GraphQL operation name and variables for searchability
|
|
51
|
+
let operationName;
|
|
52
|
+
let graphqlVariables;
|
|
53
|
+
if (request.client === 'graphql') {
|
|
54
|
+
const extracted = extractOperationName(request.data);
|
|
55
|
+
operationName = extracted || undefined;
|
|
56
|
+
|
|
57
|
+
// Extract variables from GraphQL request
|
|
58
|
+
if (request.data && typeof request.data === 'object' && 'variables' in request.data && request.data.variables && typeof request.data.variables === 'object') {
|
|
59
|
+
graphqlVariables = request.data.variables;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const networkEvent = {
|
|
63
|
+
id: request.id,
|
|
64
|
+
method: request.method,
|
|
65
|
+
url: fullUrl,
|
|
66
|
+
// Store FULL URL with query params
|
|
67
|
+
host: this.extractHost(request.url),
|
|
68
|
+
path: this.extractPath(request.url),
|
|
69
|
+
query: queryString,
|
|
70
|
+
timestamp: event.timestamp.getTime(),
|
|
71
|
+
requestHeaders: request.headers || {},
|
|
72
|
+
requestData: request.data,
|
|
73
|
+
requestSize: this.getDataSize(request.data),
|
|
74
|
+
responseHeaders: {},
|
|
75
|
+
requestClient: request.client,
|
|
76
|
+
operationName,
|
|
77
|
+
// GraphQL operation name for search/filter
|
|
78
|
+
graphqlVariables // GraphQL variables for display and search
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Store as pending
|
|
82
|
+
this.pendingRequests.set(request.id, networkEvent);
|
|
83
|
+
|
|
84
|
+
// Add to events list
|
|
85
|
+
this.events = [networkEvent, ...this.events].slice(0, this.maxEvents);
|
|
86
|
+
this.notifyListeners();
|
|
87
|
+
} else if (event.type === "response" || event.type === "error") {
|
|
88
|
+
// Find and update the pending request
|
|
89
|
+
const index = this.events.findIndex(e => e.id === request.id);
|
|
90
|
+
if (index !== -1) {
|
|
91
|
+
const updatedEvent = {
|
|
92
|
+
...this.events[index],
|
|
93
|
+
duration: event.duration
|
|
94
|
+
};
|
|
95
|
+
if (event.response) {
|
|
96
|
+
updatedEvent.status = event.response.status;
|
|
97
|
+
updatedEvent.statusText = event.response.statusText;
|
|
98
|
+
updatedEvent.responseHeaders = event.response.headers || {};
|
|
99
|
+
updatedEvent.responseData = event.response.body;
|
|
100
|
+
updatedEvent.responseSize = event.response.size || 0;
|
|
101
|
+
updatedEvent.responseType = event.response.headers?.["content-type"];
|
|
102
|
+
}
|
|
103
|
+
if (event.error) {
|
|
104
|
+
updatedEvent.error = event.error.message;
|
|
105
|
+
updatedEvent.status = updatedEvent.status || 0;
|
|
106
|
+
}
|
|
107
|
+
this.events[index] = updatedEvent;
|
|
108
|
+
this.pendingRequests.delete(request.id);
|
|
109
|
+
this.notifyListeners();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Extract host from URL
|
|
116
|
+
*/
|
|
117
|
+
extractHost(url) {
|
|
118
|
+
try {
|
|
119
|
+
const urlObj = new URL(url);
|
|
120
|
+
// @ts-ignore - this does exist on native
|
|
121
|
+
return urlObj.hostname;
|
|
122
|
+
} catch {
|
|
123
|
+
return "";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Extract path from URL
|
|
129
|
+
*/
|
|
130
|
+
extractPath(url) {
|
|
131
|
+
try {
|
|
132
|
+
const urlObj = new URL(url);
|
|
133
|
+
// @ts-ignore - this does exist on native
|
|
134
|
+
return urlObj.pathname;
|
|
135
|
+
} catch {
|
|
136
|
+
return url;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get size of data
|
|
142
|
+
*/
|
|
143
|
+
getDataSize(data) {
|
|
144
|
+
if (!data) return 0;
|
|
145
|
+
if (typeof data === "string") return data.length;
|
|
146
|
+
try {
|
|
147
|
+
return JSON.stringify(data).length;
|
|
148
|
+
} catch {
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get all events
|
|
155
|
+
*/
|
|
156
|
+
getEvents() {
|
|
157
|
+
return [...this.events];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get event by ID
|
|
162
|
+
*/
|
|
163
|
+
getEventById(id) {
|
|
164
|
+
return this.events.find(e => e.id === id);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Clear all events
|
|
169
|
+
*/
|
|
170
|
+
clearEvents() {
|
|
171
|
+
this.events = [];
|
|
172
|
+
this.pendingRequests.clear();
|
|
173
|
+
this.recentRequests.clear();
|
|
174
|
+
this.notifyListeners();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Subscribe to event changes
|
|
179
|
+
*/
|
|
180
|
+
subscribe(listener) {
|
|
181
|
+
this.listeners.add(listener);
|
|
182
|
+
return () => {
|
|
183
|
+
this.listeners.delete(listener);
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Notify all listeners of changes
|
|
189
|
+
*/
|
|
190
|
+
notifyListeners() {
|
|
191
|
+
const events = this.getEvents();
|
|
192
|
+
this.listeners.forEach(listener => listener(events));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Set maximum number of events to store
|
|
197
|
+
*/
|
|
198
|
+
setMaxEvents(max) {
|
|
199
|
+
this.maxEvents = max;
|
|
200
|
+
if (this.events.length > max) {
|
|
201
|
+
this.events = this.events.slice(0, max);
|
|
202
|
+
this.notifyListeners();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get statistics about network events
|
|
208
|
+
*/
|
|
209
|
+
getStats() {
|
|
210
|
+
const total = this.events.length;
|
|
211
|
+
const successful = this.events.filter(e => e.status && e.status >= 200 && e.status < 300).length;
|
|
212
|
+
const failed = this.events.filter(e => e.error || e.status && e.status >= 400).length;
|
|
213
|
+
const pending = this.events.filter(e => !e.status && !e.error).length;
|
|
214
|
+
const durations = this.events.filter(e => e.duration).map(e => e.duration);
|
|
215
|
+
const avgDuration = durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0;
|
|
216
|
+
const totalSent = this.events.reduce((sum, e) => sum + (e.requestSize || 0), 0);
|
|
217
|
+
const totalReceived = this.events.reduce((sum, e) => sum + (e.responseSize || 0), 0);
|
|
218
|
+
return {
|
|
219
|
+
totalRequests: total,
|
|
220
|
+
successfulRequests: successful,
|
|
221
|
+
failedRequests: failed,
|
|
222
|
+
pendingRequests: pending,
|
|
223
|
+
totalDataSent: totalSent,
|
|
224
|
+
totalDataReceived: totalReceived,
|
|
225
|
+
averageDuration: Math.round(avgDuration)
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Filter events by criteria
|
|
231
|
+
*/
|
|
232
|
+
filterEvents(filter) {
|
|
233
|
+
let filtered = [...this.events];
|
|
234
|
+
if (filter.method) {
|
|
235
|
+
filtered = filtered.filter(e => e.method === filter.method);
|
|
236
|
+
}
|
|
237
|
+
if (filter.status) {
|
|
238
|
+
switch (filter.status) {
|
|
239
|
+
case "success":
|
|
240
|
+
filtered = filtered.filter(e => e.status && e.status >= 200 && e.status < 300);
|
|
241
|
+
break;
|
|
242
|
+
case "error":
|
|
243
|
+
filtered = filtered.filter(e => e.error || e.status && e.status >= 400);
|
|
244
|
+
break;
|
|
245
|
+
case "pending":
|
|
246
|
+
filtered = filtered.filter(e => !e.status && !e.error);
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (filter.searchText) {
|
|
251
|
+
const search = filter.searchText.toLowerCase();
|
|
252
|
+
filtered = filtered.filter(e => e.url.toLowerCase().includes(search) || e.method.toLowerCase().includes(search) || e.error && e.error.toLowerCase().includes(search));
|
|
253
|
+
}
|
|
254
|
+
if (filter.host) {
|
|
255
|
+
filtered = filtered.filter(e => e.host === filter.host);
|
|
256
|
+
}
|
|
257
|
+
return filtered;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Singleton store that aggregates captured network traffic. Components and hooks consume this
|
|
263
|
+
* store to render histories, derive stats, and subscribe to real-time updates.
|
|
264
|
+
*/
|
|
265
|
+
export const networkEventStore = new NetworkEventStore();
|