@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.
Files changed (67) hide show
  1. package/README.md +381 -0
  2. package/lib/commonjs/index.js +34 -0
  3. package/lib/commonjs/network/components/NetworkCopySettingsView.js +867 -0
  4. package/lib/commonjs/network/components/NetworkEventDetailView.js +837 -0
  5. package/lib/commonjs/network/components/NetworkEventItemCompact.js +323 -0
  6. package/lib/commonjs/network/components/NetworkFilterViewV3.js +297 -0
  7. package/lib/commonjs/network/components/NetworkModal.js +937 -0
  8. package/lib/commonjs/network/hooks/useNetworkEvents.js +320 -0
  9. package/lib/commonjs/network/hooks/useTickEveryMinute.js +34 -0
  10. package/lib/commonjs/network/index.js +102 -0
  11. package/lib/commonjs/network/types/index.js +1 -0
  12. package/lib/commonjs/network/utils/extractOperationName.js +80 -0
  13. package/lib/commonjs/network/utils/formatGraphQLVariables.js +219 -0
  14. package/lib/commonjs/network/utils/formatting.js +30 -0
  15. package/lib/commonjs/network/utils/networkEventStore.js +269 -0
  16. package/lib/commonjs/network/utils/networkListener.js +801 -0
  17. package/lib/commonjs/package.json +1 -0
  18. package/lib/commonjs/preset.js +83 -0
  19. package/lib/module/index.js +7 -0
  20. package/lib/module/network/components/NetworkCopySettingsView.js +862 -0
  21. package/lib/module/network/components/NetworkEventDetailView.js +834 -0
  22. package/lib/module/network/components/NetworkEventItemCompact.js +320 -0
  23. package/lib/module/network/components/NetworkFilterViewV3.js +293 -0
  24. package/lib/module/network/components/NetworkModal.js +933 -0
  25. package/lib/module/network/hooks/useNetworkEvents.js +316 -0
  26. package/lib/module/network/hooks/useTickEveryMinute.js +29 -0
  27. package/lib/module/network/index.js +20 -0
  28. package/lib/module/network/types/index.js +1 -0
  29. package/lib/module/network/utils/extractOperationName.js +76 -0
  30. package/lib/module/network/utils/formatGraphQLVariables.js +213 -0
  31. package/lib/module/network/utils/formatting.js +9 -0
  32. package/lib/module/network/utils/networkEventStore.js +265 -0
  33. package/lib/module/network/utils/networkListener.js +791 -0
  34. package/lib/module/preset.js +79 -0
  35. package/lib/typescript/index.d.ts +3 -0
  36. package/lib/typescript/index.d.ts.map +1 -0
  37. package/lib/typescript/network/components/NetworkCopySettingsView.d.ts +26 -0
  38. package/lib/typescript/network/components/NetworkCopySettingsView.d.ts.map +1 -0
  39. package/lib/typescript/network/components/NetworkEventDetailView.d.ts +13 -0
  40. package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +1 -0
  41. package/lib/typescript/network/components/NetworkEventItemCompact.d.ts +12 -0
  42. package/lib/typescript/network/components/NetworkEventItemCompact.d.ts.map +1 -0
  43. package/lib/typescript/network/components/NetworkFilterViewV3.d.ts +22 -0
  44. package/lib/typescript/network/components/NetworkFilterViewV3.d.ts.map +1 -0
  45. package/lib/typescript/network/components/NetworkModal.d.ts +14 -0
  46. package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -0
  47. package/lib/typescript/network/hooks/useNetworkEvents.d.ts +72 -0
  48. package/lib/typescript/network/hooks/useNetworkEvents.d.ts.map +1 -0
  49. package/lib/typescript/network/hooks/useTickEveryMinute.d.ts +9 -0
  50. package/lib/typescript/network/hooks/useTickEveryMinute.d.ts.map +1 -0
  51. package/lib/typescript/network/index.d.ts +12 -0
  52. package/lib/typescript/network/index.d.ts.map +1 -0
  53. package/lib/typescript/network/types/index.d.ts +88 -0
  54. package/lib/typescript/network/types/index.d.ts.map +1 -0
  55. package/lib/typescript/network/utils/extractOperationName.d.ts +41 -0
  56. package/lib/typescript/network/utils/extractOperationName.d.ts.map +1 -0
  57. package/lib/typescript/network/utils/formatGraphQLVariables.d.ts +79 -0
  58. package/lib/typescript/network/utils/formatGraphQLVariables.d.ts.map +1 -0
  59. package/lib/typescript/network/utils/formatting.d.ts +6 -0
  60. package/lib/typescript/network/utils/formatting.d.ts.map +1 -0
  61. package/lib/typescript/network/utils/networkEventStore.d.ts +81 -0
  62. package/lib/typescript/network/utils/networkEventStore.d.ts.map +1 -0
  63. package/lib/typescript/network/utils/networkListener.d.ts +191 -0
  64. package/lib/typescript/network/utils/networkListener.d.ts.map +1 -0
  65. package/lib/typescript/preset.d.ts +76 -0
  66. package/lib/typescript/preset.d.ts.map +1 -0
  67. 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();