@buoy-gg/network 2.1.2 → 2.1.3

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.
@@ -49,8 +49,7 @@ function NetworkModalInner({
49
49
  filter,
50
50
  setFilter,
51
51
  clearEvents,
52
- isEnabled,
53
- toggleInterception,
52
+ isCapturing,
54
53
  hasLockedEvents,
55
54
  lockedEventCount,
56
55
  isEventLocked,
@@ -58,6 +57,14 @@ function NetworkModalInner({
58
57
  } = (0, _useNetworkEvents.useNetworkEvents)({
59
58
  isPro
60
59
  });
60
+
61
+ // Local UI state for display toggle (doesn't affect global event capture)
62
+ // This allows events tool to continue receiving events while this UI is "paused"
63
+ const [isDisplayPaused, setIsDisplayPaused] = (0, _react.useState)(false);
64
+ const isEnabled = isCapturing && !isDisplayPaused;
65
+ const toggleInterception = (0, _react.useCallback)(() => {
66
+ setIsDisplayPaused(prev => !prev);
67
+ }, []);
61
68
  const handleModeChange = (0, _react.useCallback)(_mode => {
62
69
  // Mode changes handled by JsModal
63
70
  }, []);
@@ -7,11 +7,12 @@ exports.FREE_TIER_REQUEST_LIMIT = void 0;
7
7
  exports.useNetworkEvents = useNetworkEvents;
8
8
  var _react = require("react");
9
9
  var _networkEventStore = require("../utils/networkEventStore");
10
- var _networkListener = require("../utils/networkListener");
11
10
  var _formatGraphQLVariables = require("../utils/formatGraphQLVariables");
12
11
  /**
13
12
  * Hook for accessing network events and controls
14
- * Uses Reactotron-style listener pattern
13
+ *
14
+ * Uses self-managing Subscribable pattern - the network listener automatically
15
+ * starts when this hook mounts and stops when all subscribers unmount.
15
16
  */
16
17
 
17
18
  /** Free tier limit for network requests */
@@ -19,13 +20,15 @@ const FREE_TIER_REQUEST_LIMIT = exports.FREE_TIER_REQUEST_LIMIT = 25;
19
20
 
20
21
  /**
21
22
  * Custom hook for accessing network events and controls
22
- *
23
+ *
23
24
  * This hook provides a complete interface for network monitoring, including
24
- * event filtering, statistics calculation, and interception control. It uses
25
- * the Reactotron-style listener pattern for network event handling.
26
- *
25
+ * event filtering, statistics calculation, and interception control.
26
+ *
27
+ * The network listener automatically starts when this hook mounts and stops
28
+ * when all components using it unmount (Subscribable pattern from TanStack Query).
29
+ *
27
30
  * @returns Object containing filtered events, statistics, controls, and utilities
28
- *
31
+ *
29
32
  * @example
30
33
  * ```typescript
31
34
  * function NetworkMonitor() {
@@ -35,22 +38,19 @@ const FREE_TIER_REQUEST_LIMIT = exports.FREE_TIER_REQUEST_LIMIT = 25;
35
38
  * filter,
36
39
  * setFilter,
37
40
  * clearEvents,
38
- * toggleInterception,
39
- * isEnabled
41
+ * isCapturing
40
42
  * } = useNetworkEvents();
41
- *
43
+ *
42
44
  * return (
43
45
  * <div>
44
46
  * <p>Total requests: {stats.totalRequests}</p>
45
47
  * <p>Success rate: {stats.successfulRequests}/{stats.totalRequests}</p>
46
- * <button onClick={toggleInterception}>
47
- * {isEnabled ? 'Stop' : 'Start'} Monitoring
48
- * </button>
48
+ * <p>Capturing: {isCapturing ? 'Yes' : 'No'}</p>
49
49
  * </div>
50
50
  * );
51
51
  * }
52
52
  * ```
53
- *
53
+ *
54
54
  * @performance Uses memoization for expensive filtering and statistics calculations
55
55
  * @performance Optimizes string operations and array processing for large datasets
56
56
  * @performance Includes Set-based lookups for O(1) filter matching
@@ -63,36 +63,17 @@ function useNetworkEvents(options = {}) {
63
63
  } = options;
64
64
  const [events, setEvents] = (0, _react.useState)([]);
65
65
  const [filter, setFilter] = (0, _react.useState)({});
66
- const [isEnabled, setIsEnabled] = (0, _react.useState)(false);
67
66
 
68
- // Subscribe to event store changes
67
+ // Subscribe to event store - this automatically starts/stops the network listener
68
+ // based on subscriber count (Subscribable pattern from TanStack Query)
69
69
  (0, _react.useEffect)(() => {
70
- // Subscribe to store changes
71
- const unsubscribeStore = _networkEventStore.networkEventStore.subscribe(setEvents);
72
-
73
- // Add listener to network events
74
- const unsubscribeListener = (0, _networkListener.addNetworkListener)(event => {
75
- // Only log in development and for non-ignored URLs
76
- if (__DEV__ && !event.request.url.includes("symbolicate") && !event.request.url.includes(":8081")) {
77
- // Network event processed: [event.type] [method] [url] - available for debugging if needed
78
- }
79
- _networkEventStore.networkEventStore.processNetworkEvent(event);
80
- });
81
-
82
- // Check if already listening
83
- setIsEnabled((0, _networkListener.networkListener)().isActive);
84
-
85
- // Start listening if not already
86
- if (!(0, _networkListener.networkListener)().isActive) {
87
- (0, _networkListener.startNetworkListener)();
88
- setIsEnabled(true);
89
- }
90
-
91
- // Load initial events
92
- setEvents(_networkEventStore.networkEventStore.getEvents());
70
+ // subscribeToEvents handles everything:
71
+ // - Starts network listener when first subscriber joins
72
+ // - Stops network listener when last subscriber leaves
73
+ // - Immediately provides current events
74
+ const unsubscribe = _networkEventStore.networkEventStore.subscribeToEvents(setEvents);
93
75
  return () => {
94
- unsubscribeStore();
95
- unsubscribeListener();
76
+ unsubscribe();
96
77
  };
97
78
  }, []);
98
79
 
@@ -101,16 +82,8 @@ function useNetworkEvents(options = {}) {
101
82
  _networkEventStore.networkEventStore.clearEvents();
102
83
  }, []);
103
84
 
104
- // Toggle interception
105
- const toggleInterception = (0, _react.useCallback)(() => {
106
- if (isEnabled) {
107
- (0, _networkListener.stopNetworkListener)();
108
- setIsEnabled(false);
109
- } else {
110
- (0, _networkListener.startNetworkListener)();
111
- setIsEnabled(true);
112
- }
113
- }, [isEnabled]);
85
+ // Check if the store is currently capturing (has subscribers)
86
+ const isCapturing = _networkEventStore.networkEventStore.isCapturing();
114
87
 
115
88
  // Memoize search text processing to avoid repeated toLowerCase calls
116
89
  // Performance: Expensive string operations repeated for every event on every filter
@@ -304,8 +277,8 @@ function useNetworkEvents(options = {}) {
304
277
  filter,
305
278
  setFilter,
306
279
  clearEvents,
307
- isEnabled,
308
- toggleInterception,
280
+ /** Whether the store has subscribers (listener is running) */
281
+ isCapturing,
309
282
  hosts,
310
283
  methods,
311
284
  /** Whether there are locked events due to free tier limit */
@@ -315,6 +288,8 @@ function useNetworkEvents(options = {}) {
315
288
  /** Check if a specific event is locked (by ID) */
316
289
  isEventLocked,
317
290
  /** Free tier request limit */
318
- requestLimit: FREE_TIER_REQUEST_LIMIT
291
+ requestLimit: FREE_TIER_REQUEST_LIMIT,
292
+ /** Get subscriber counts for debugging */
293
+ getSubscriberCounts: () => _networkEventStore.networkEventStore.getSubscriberCounts()
319
294
  };
320
295
  }
@@ -4,18 +4,91 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.networkEventStore = void 0;
7
+ var _sharedUi = require("@buoy-gg/shared-ui");
7
8
  var _extractOperationName = require("./extractOperationName");
9
+ var _networkListener = require("./networkListener");
8
10
  /**
9
- * Network event store for managing captured network requests
10
- * Works with the Reactotron-style network listener
11
+ * Network Event Store
12
+ *
13
+ * Store for managing captured network requests. Uses BaseEventStore pattern -
14
+ * the network listener automatically starts when the first subscriber joins
15
+ * and stops when the last subscriber leaves.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { networkEventStore } from '@buoy-gg/network';
20
+ *
21
+ * // Subscribe to network events - automatically starts capturing!
22
+ * const unsubscribe = networkEventStore.subscribeToEvents((events) => {
23
+ * console.log('Network events:', events);
24
+ * });
25
+ *
26
+ * // Later, clean up - automatically stops if no other subscribers
27
+ * unsubscribe();
28
+ * ```
11
29
  */
12
30
 
13
- class NetworkEventStore {
14
- events = [];
31
+ /**
32
+ * Callback type for individual event notifications
33
+ */
34
+
35
+ /**
36
+ * Callback type for full events array notifications
37
+ */
38
+
39
+ class NetworkEventStore extends _sharedUi.BaseEventStore {
15
40
  pendingRequests = new Map();
16
- listeners = new Set();
17
- maxEvents = 500; // Configurable max events to prevent memory issues
18
- recentRequests = new Map(); // Track recent requests to detect duplicates
41
+ recentRequests = new Map();
42
+ rawListenerUnsubscribe = null;
43
+ constructor() {
44
+ super({
45
+ storeName: "network",
46
+ maxEvents: 500
47
+ });
48
+ }
49
+
50
+ /**
51
+ * Start capturing network events
52
+ */
53
+ startCapturing() {
54
+ const listener = (0, _networkListener.networkListener)();
55
+
56
+ // Start the network interceptor if not already active
57
+ if (!listener.isActive) {
58
+ listener.startListening();
59
+ }
60
+
61
+ // Add our listener to push raw events into the store
62
+ if (!this.rawListenerUnsubscribe) {
63
+ this.rawListenerUnsubscribe = (0, _networkListener.addNetworkListener)(event => {
64
+ this.processNetworkEvent(event);
65
+ });
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Stop capturing network events
71
+ */
72
+ stopCapturing() {
73
+ // Remove our raw listener
74
+ if (this.rawListenerUnsubscribe) {
75
+ this.rawListenerUnsubscribe();
76
+ this.rawListenerUnsubscribe = null;
77
+ }
78
+
79
+ // If no other listeners exist, stop the network interceptor
80
+ const listener = (0, _networkListener.networkListener)();
81
+ if (listener.listenerCount === 0) {
82
+ listener.stopListening();
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Check if the store is actively capturing network events
88
+ */
89
+ isCapturing() {
90
+ return this.rawListenerUnsubscribe !== null;
91
+ }
19
92
 
20
93
  /**
21
94
  * Process a network listener event
@@ -32,13 +105,13 @@ class NetworkEventStore {
32
105
 
33
106
  // If same request within 50ms, likely a duplicate from XHR/fetch dual interception
34
107
  if (lastRequestTime && now - lastRequestTime < 50) {
35
- return; // Skip duplicate
108
+ return;
36
109
  }
37
110
  this.recentRequests.set(requestKey, now);
38
111
 
39
112
  // Clean up old entries to prevent memory leak
40
113
  if (this.recentRequests.size > 100) {
41
- const cutoff = now - 5000; // Remove entries older than 5 seconds
114
+ const cutoff = now - 5000;
42
115
  for (const [key, time] of this.recentRequests.entries()) {
43
116
  if (time < cutoff) {
44
117
  this.recentRequests.delete(key);
@@ -46,7 +119,6 @@ class NetworkEventStore {
46
119
  }
47
120
  }
48
121
 
49
- // Create new network event for request
50
122
  // Build full URL with query params if present
51
123
  const queryString = request.params ? `?${new URLSearchParams(request.params).toString()}` : "";
52
124
  const fullUrl = `${request.url}${queryString}`;
@@ -54,12 +126,10 @@ class NetworkEventStore {
54
126
  // Extract GraphQL operation name and variables for searchability
55
127
  let operationName;
56
128
  let graphqlVariables;
57
- if (request.client === 'graphql') {
129
+ if (request.client === "graphql") {
58
130
  const extracted = (0, _extractOperationName.extractOperationName)(request.data);
59
131
  operationName = extracted || undefined;
60
-
61
- // Extract variables from GraphQL request
62
- if (request.data && typeof request.data === 'object' && 'variables' in request.data && request.data.variables && typeof request.data.variables === 'object') {
132
+ if (request.data && typeof request.data === "object" && "variables" in request.data && request.data.variables && typeof request.data.variables === "object") {
63
133
  graphqlVariables = request.data.variables;
64
134
  }
65
135
  }
@@ -67,7 +137,6 @@ class NetworkEventStore {
67
137
  id: request.id,
68
138
  method: request.method,
69
139
  url: fullUrl,
70
- // Store FULL URL with query params
71
140
  host: this.extractHost(request.url),
72
141
  path: this.extractPath(request.url),
73
142
  query: queryString,
@@ -78,18 +147,13 @@ class NetworkEventStore {
78
147
  responseHeaders: {},
79
148
  requestClient: request.client,
80
149
  operationName,
81
- // GraphQL operation name for search/filter
82
- graphqlVariables // GraphQL variables for display and search
150
+ graphqlVariables
83
151
  };
84
-
85
- // Store as pending
86
152
  this.pendingRequests.set(request.id, networkEvent);
87
-
88
- // Add to events list
89
153
  this.events = [networkEvent, ...this.events].slice(0, this.maxEvents);
90
- this.notifyListeners();
154
+ this.notify(networkEvent);
155
+ this.notifyArrayListeners();
91
156
  } else if (event.type === "response" || event.type === "error") {
92
- // Find and update the pending request
93
157
  const index = this.events.findIndex(e => e.id === request.id);
94
158
  if (index !== -1) {
95
159
  const updatedEvent = {
@@ -110,14 +174,11 @@ class NetworkEventStore {
110
174
  }
111
175
  this.events[index] = updatedEvent;
112
176
  this.pendingRequests.delete(request.id);
113
- this.notifyListeners();
177
+ this.notify(updatedEvent);
178
+ this.notifyArrayListeners();
114
179
  }
115
180
  }
116
181
  }
117
-
118
- /**
119
- * Extract host from URL
120
- */
121
182
  extractHost(url) {
122
183
  try {
123
184
  const urlObj = new URL(url);
@@ -127,10 +188,6 @@ class NetworkEventStore {
127
188
  return "";
128
189
  }
129
190
  }
130
-
131
- /**
132
- * Extract path from URL
133
- */
134
191
  extractPath(url) {
135
192
  try {
136
193
  const urlObj = new URL(url);
@@ -140,10 +197,6 @@ class NetworkEventStore {
140
197
  return url;
141
198
  }
142
199
  }
143
-
144
- /**
145
- * Get size of data
146
- */
147
200
  getDataSize(data) {
148
201
  if (!data) return 0;
149
202
  if (typeof data === "string") return data.length;
@@ -153,63 +206,38 @@ class NetworkEventStore {
153
206
  return 0;
154
207
  }
155
208
  }
156
-
157
- /**
158
- * Get all events
159
- */
160
- getEvents() {
161
- return [...this.events];
162
- }
163
-
164
- /**
165
- * Get event by ID
166
- */
167
209
  getEventById(id) {
168
210
  return this.events.find(e => e.id === id);
169
211
  }
170
212
 
171
213
  /**
172
- * Clear all events
214
+ * Override clearEvents to also clear pending requests
173
215
  */
174
216
  clearEvents() {
175
- this.events = [];
217
+ super.clearEvents();
176
218
  this.pendingRequests.clear();
177
219
  this.recentRequests.clear();
178
- this.notifyListeners();
179
- }
180
-
181
- /**
182
- * Subscribe to event changes
183
- */
184
- subscribe(listener) {
185
- this.listeners.add(listener);
186
- return () => {
187
- this.listeners.delete(listener);
188
- };
189
220
  }
190
221
 
191
222
  /**
192
- * Notify all listeners of changes
223
+ * Subscribe to individual events with optional replay of existing events
193
224
  */
194
- notifyListeners() {
195
- const events = this.getEvents();
196
- this.listeners.forEach(listener => listener(events));
197
- }
225
+ onEvent(callback, replayExisting = true) {
226
+ const unsubscribe = this.subscribe(callback);
198
227
 
199
- /**
200
- * Set maximum number of events to store
201
- */
202
- setMaxEvents(max) {
203
- this.maxEvents = max;
204
- if (this.events.length > max) {
205
- this.events = this.events.slice(0, max);
206
- this.notifyListeners();
228
+ // Replay existing events if requested
229
+ if (replayExisting && this.events.length > 0) {
230
+ const existingEvents = [...this.events].reverse();
231
+ for (const event of existingEvents) {
232
+ try {
233
+ callback(event);
234
+ } catch {
235
+ // Ignore callback errors
236
+ }
237
+ }
207
238
  }
239
+ return unsubscribe;
208
240
  }
209
-
210
- /**
211
- * Get statistics about network events
212
- */
213
241
  getStats() {
214
242
  const total = this.events.length;
215
243
  const successful = this.events.filter(e => e.status && e.status >= 200 && e.status < 300).length;
@@ -229,10 +257,6 @@ class NetworkEventStore {
229
257
  averageDuration: Math.round(avgDuration)
230
258
  };
231
259
  }
232
-
233
- /**
234
- * Filter events by criteria
235
- */
236
260
  filterEvents(filter) {
237
261
  let filtered = [...this.events];
238
262
  if (filter.method) {
@@ -263,7 +287,7 @@ class NetworkEventStore {
263
287
  }
264
288
 
265
289
  /**
266
- * Singleton store that aggregates captured network traffic. Components and hooks consume this
267
- * store to render histories, derive stats, and subscribe to real-time updates.
290
+ * Singleton store that aggregates captured network traffic.
291
+ * Automatically starts/stops capturing based on subscriber count.
268
292
  */
269
293
  const networkEventStore = exports.networkEventStore = new NetworkEventStore();
@@ -45,8 +45,7 @@ function NetworkModalInner({
45
45
  filter,
46
46
  setFilter,
47
47
  clearEvents,
48
- isEnabled,
49
- toggleInterception,
48
+ isCapturing,
50
49
  hasLockedEvents,
51
50
  lockedEventCount,
52
51
  isEventLocked,
@@ -54,6 +53,14 @@ function NetworkModalInner({
54
53
  } = useNetworkEvents({
55
54
  isPro
56
55
  });
56
+
57
+ // Local UI state for display toggle (doesn't affect global event capture)
58
+ // This allows events tool to continue receiving events while this UI is "paused"
59
+ const [isDisplayPaused, setIsDisplayPaused] = useState(false);
60
+ const isEnabled = isCapturing && !isDisplayPaused;
61
+ const toggleInterception = useCallback(() => {
62
+ setIsDisplayPaused(prev => !prev);
63
+ }, []);
57
64
  const handleModeChange = useCallback(_mode => {
58
65
  // Mode changes handled by JsModal
59
66
  }, []);
@@ -2,12 +2,13 @@
2
2
 
3
3
  /**
4
4
  * Hook for accessing network events and controls
5
- * Uses Reactotron-style listener pattern
5
+ *
6
+ * Uses self-managing Subscribable pattern - the network listener automatically
7
+ * starts when this hook mounts and stops when all subscribers unmount.
6
8
  */
7
9
 
8
10
  import { useState, useEffect, useCallback, useMemo } from "react";
9
11
  import { networkEventStore } from "../utils/networkEventStore";
10
- import { networkListener, startNetworkListener, stopNetworkListener, addNetworkListener } from "../utils/networkListener";
11
12
  import { searchGraphQLVariables } from "../utils/formatGraphQLVariables";
12
13
 
13
14
  /** Free tier limit for network requests */
@@ -15,13 +16,15 @@ export const FREE_TIER_REQUEST_LIMIT = 25;
15
16
 
16
17
  /**
17
18
  * Custom hook for accessing network events and controls
18
- *
19
+ *
19
20
  * This hook provides a complete interface for network monitoring, including
20
- * event filtering, statistics calculation, and interception control. It uses
21
- * the Reactotron-style listener pattern for network event handling.
22
- *
21
+ * event filtering, statistics calculation, and interception control.
22
+ *
23
+ * The network listener automatically starts when this hook mounts and stops
24
+ * when all components using it unmount (Subscribable pattern from TanStack Query).
25
+ *
23
26
  * @returns Object containing filtered events, statistics, controls, and utilities
24
- *
27
+ *
25
28
  * @example
26
29
  * ```typescript
27
30
  * function NetworkMonitor() {
@@ -31,22 +34,19 @@ export const FREE_TIER_REQUEST_LIMIT = 25;
31
34
  * filter,
32
35
  * setFilter,
33
36
  * clearEvents,
34
- * toggleInterception,
35
- * isEnabled
37
+ * isCapturing
36
38
  * } = useNetworkEvents();
37
- *
39
+ *
38
40
  * return (
39
41
  * <div>
40
42
  * <p>Total requests: {stats.totalRequests}</p>
41
43
  * <p>Success rate: {stats.successfulRequests}/{stats.totalRequests}</p>
42
- * <button onClick={toggleInterception}>
43
- * {isEnabled ? 'Stop' : 'Start'} Monitoring
44
- * </button>
44
+ * <p>Capturing: {isCapturing ? 'Yes' : 'No'}</p>
45
45
  * </div>
46
46
  * );
47
47
  * }
48
48
  * ```
49
- *
49
+ *
50
50
  * @performance Uses memoization for expensive filtering and statistics calculations
51
51
  * @performance Optimizes string operations and array processing for large datasets
52
52
  * @performance Includes Set-based lookups for O(1) filter matching
@@ -59,36 +59,17 @@ export function useNetworkEvents(options = {}) {
59
59
  } = options;
60
60
  const [events, setEvents] = useState([]);
61
61
  const [filter, setFilter] = useState({});
62
- const [isEnabled, setIsEnabled] = useState(false);
63
62
 
64
- // Subscribe to event store changes
63
+ // Subscribe to event store - this automatically starts/stops the network listener
64
+ // based on subscriber count (Subscribable pattern from TanStack Query)
65
65
  useEffect(() => {
66
- // Subscribe to store changes
67
- const unsubscribeStore = networkEventStore.subscribe(setEvents);
68
-
69
- // Add listener to network events
70
- const unsubscribeListener = addNetworkListener(event => {
71
- // Only log in development and for non-ignored URLs
72
- if (__DEV__ && !event.request.url.includes("symbolicate") && !event.request.url.includes(":8081")) {
73
- // Network event processed: [event.type] [method] [url] - available for debugging if needed
74
- }
75
- networkEventStore.processNetworkEvent(event);
76
- });
77
-
78
- // Check if already listening
79
- setIsEnabled(networkListener().isActive);
80
-
81
- // Start listening if not already
82
- if (!networkListener().isActive) {
83
- startNetworkListener();
84
- setIsEnabled(true);
85
- }
86
-
87
- // Load initial events
88
- setEvents(networkEventStore.getEvents());
66
+ // subscribeToEvents handles everything:
67
+ // - Starts network listener when first subscriber joins
68
+ // - Stops network listener when last subscriber leaves
69
+ // - Immediately provides current events
70
+ const unsubscribe = networkEventStore.subscribeToEvents(setEvents);
89
71
  return () => {
90
- unsubscribeStore();
91
- unsubscribeListener();
72
+ unsubscribe();
92
73
  };
93
74
  }, []);
94
75
 
@@ -97,16 +78,8 @@ export function useNetworkEvents(options = {}) {
97
78
  networkEventStore.clearEvents();
98
79
  }, []);
99
80
 
100
- // Toggle interception
101
- const toggleInterception = useCallback(() => {
102
- if (isEnabled) {
103
- stopNetworkListener();
104
- setIsEnabled(false);
105
- } else {
106
- startNetworkListener();
107
- setIsEnabled(true);
108
- }
109
- }, [isEnabled]);
81
+ // Check if the store is currently capturing (has subscribers)
82
+ const isCapturing = networkEventStore.isCapturing();
110
83
 
111
84
  // Memoize search text processing to avoid repeated toLowerCase calls
112
85
  // Performance: Expensive string operations repeated for every event on every filter
@@ -300,8 +273,8 @@ export function useNetworkEvents(options = {}) {
300
273
  filter,
301
274
  setFilter,
302
275
  clearEvents,
303
- isEnabled,
304
- toggleInterception,
276
+ /** Whether the store has subscribers (listener is running) */
277
+ isCapturing,
305
278
  hosts,
306
279
  methods,
307
280
  /** Whether there are locked events due to free tier limit */
@@ -311,6 +284,8 @@ export function useNetworkEvents(options = {}) {
311
284
  /** Check if a specific event is locked (by ID) */
312
285
  isEventLocked,
313
286
  /** Free tier request limit */
314
- requestLimit: FREE_TIER_REQUEST_LIMIT
287
+ requestLimit: FREE_TIER_REQUEST_LIMIT,
288
+ /** Get subscriber counts for debugging */
289
+ getSubscriberCounts: () => networkEventStore.getSubscriberCounts()
315
290
  };
316
291
  }
@@ -1,17 +1,91 @@
1
1
  "use strict";
2
2
 
3
3
  /**
4
- * Network event store for managing captured network requests
5
- * Works with the Reactotron-style network listener
4
+ * Network Event Store
5
+ *
6
+ * Store for managing captured network requests. Uses BaseEventStore pattern -
7
+ * the network listener automatically starts when the first subscriber joins
8
+ * and stops when the last subscriber leaves.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { networkEventStore } from '@buoy-gg/network';
13
+ *
14
+ * // Subscribe to network events - automatically starts capturing!
15
+ * const unsubscribe = networkEventStore.subscribeToEvents((events) => {
16
+ * console.log('Network events:', events);
17
+ * });
18
+ *
19
+ * // Later, clean up - automatically stops if no other subscribers
20
+ * unsubscribe();
21
+ * ```
6
22
  */
7
23
 
24
+ import { BaseEventStore } from "@buoy-gg/shared-ui";
8
25
  import { extractOperationName } from "./extractOperationName";
9
- class NetworkEventStore {
10
- events = [];
26
+ import { networkListener, addNetworkListener } from "./networkListener";
27
+
28
+ /**
29
+ * Callback type for individual event notifications
30
+ */
31
+
32
+ /**
33
+ * Callback type for full events array notifications
34
+ */
35
+
36
+ class NetworkEventStore extends BaseEventStore {
11
37
  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
38
+ recentRequests = new Map();
39
+ rawListenerUnsubscribe = null;
40
+ constructor() {
41
+ super({
42
+ storeName: "network",
43
+ maxEvents: 500
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Start capturing network events
49
+ */
50
+ startCapturing() {
51
+ const listener = networkListener();
52
+
53
+ // Start the network interceptor if not already active
54
+ if (!listener.isActive) {
55
+ listener.startListening();
56
+ }
57
+
58
+ // Add our listener to push raw events into the store
59
+ if (!this.rawListenerUnsubscribe) {
60
+ this.rawListenerUnsubscribe = addNetworkListener(event => {
61
+ this.processNetworkEvent(event);
62
+ });
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Stop capturing network events
68
+ */
69
+ stopCapturing() {
70
+ // Remove our raw listener
71
+ if (this.rawListenerUnsubscribe) {
72
+ this.rawListenerUnsubscribe();
73
+ this.rawListenerUnsubscribe = null;
74
+ }
75
+
76
+ // If no other listeners exist, stop the network interceptor
77
+ const listener = networkListener();
78
+ if (listener.listenerCount === 0) {
79
+ listener.stopListening();
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Check if the store is actively capturing network events
85
+ */
86
+ isCapturing() {
87
+ return this.rawListenerUnsubscribe !== null;
88
+ }
15
89
 
16
90
  /**
17
91
  * Process a network listener event
@@ -28,13 +102,13 @@ class NetworkEventStore {
28
102
 
29
103
  // If same request within 50ms, likely a duplicate from XHR/fetch dual interception
30
104
  if (lastRequestTime && now - lastRequestTime < 50) {
31
- return; // Skip duplicate
105
+ return;
32
106
  }
33
107
  this.recentRequests.set(requestKey, now);
34
108
 
35
109
  // Clean up old entries to prevent memory leak
36
110
  if (this.recentRequests.size > 100) {
37
- const cutoff = now - 5000; // Remove entries older than 5 seconds
111
+ const cutoff = now - 5000;
38
112
  for (const [key, time] of this.recentRequests.entries()) {
39
113
  if (time < cutoff) {
40
114
  this.recentRequests.delete(key);
@@ -42,7 +116,6 @@ class NetworkEventStore {
42
116
  }
43
117
  }
44
118
 
45
- // Create new network event for request
46
119
  // Build full URL with query params if present
47
120
  const queryString = request.params ? `?${new URLSearchParams(request.params).toString()}` : "";
48
121
  const fullUrl = `${request.url}${queryString}`;
@@ -50,12 +123,10 @@ class NetworkEventStore {
50
123
  // Extract GraphQL operation name and variables for searchability
51
124
  let operationName;
52
125
  let graphqlVariables;
53
- if (request.client === 'graphql') {
126
+ if (request.client === "graphql") {
54
127
  const extracted = extractOperationName(request.data);
55
128
  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') {
129
+ if (request.data && typeof request.data === "object" && "variables" in request.data && request.data.variables && typeof request.data.variables === "object") {
59
130
  graphqlVariables = request.data.variables;
60
131
  }
61
132
  }
@@ -63,7 +134,6 @@ class NetworkEventStore {
63
134
  id: request.id,
64
135
  method: request.method,
65
136
  url: fullUrl,
66
- // Store FULL URL with query params
67
137
  host: this.extractHost(request.url),
68
138
  path: this.extractPath(request.url),
69
139
  query: queryString,
@@ -74,18 +144,13 @@ class NetworkEventStore {
74
144
  responseHeaders: {},
75
145
  requestClient: request.client,
76
146
  operationName,
77
- // GraphQL operation name for search/filter
78
- graphqlVariables // GraphQL variables for display and search
147
+ graphqlVariables
79
148
  };
80
-
81
- // Store as pending
82
149
  this.pendingRequests.set(request.id, networkEvent);
83
-
84
- // Add to events list
85
150
  this.events = [networkEvent, ...this.events].slice(0, this.maxEvents);
86
- this.notifyListeners();
151
+ this.notify(networkEvent);
152
+ this.notifyArrayListeners();
87
153
  } else if (event.type === "response" || event.type === "error") {
88
- // Find and update the pending request
89
154
  const index = this.events.findIndex(e => e.id === request.id);
90
155
  if (index !== -1) {
91
156
  const updatedEvent = {
@@ -106,14 +171,11 @@ class NetworkEventStore {
106
171
  }
107
172
  this.events[index] = updatedEvent;
108
173
  this.pendingRequests.delete(request.id);
109
- this.notifyListeners();
174
+ this.notify(updatedEvent);
175
+ this.notifyArrayListeners();
110
176
  }
111
177
  }
112
178
  }
113
-
114
- /**
115
- * Extract host from URL
116
- */
117
179
  extractHost(url) {
118
180
  try {
119
181
  const urlObj = new URL(url);
@@ -123,10 +185,6 @@ class NetworkEventStore {
123
185
  return "";
124
186
  }
125
187
  }
126
-
127
- /**
128
- * Extract path from URL
129
- */
130
188
  extractPath(url) {
131
189
  try {
132
190
  const urlObj = new URL(url);
@@ -136,10 +194,6 @@ class NetworkEventStore {
136
194
  return url;
137
195
  }
138
196
  }
139
-
140
- /**
141
- * Get size of data
142
- */
143
197
  getDataSize(data) {
144
198
  if (!data) return 0;
145
199
  if (typeof data === "string") return data.length;
@@ -149,63 +203,38 @@ class NetworkEventStore {
149
203
  return 0;
150
204
  }
151
205
  }
152
-
153
- /**
154
- * Get all events
155
- */
156
- getEvents() {
157
- return [...this.events];
158
- }
159
-
160
- /**
161
- * Get event by ID
162
- */
163
206
  getEventById(id) {
164
207
  return this.events.find(e => e.id === id);
165
208
  }
166
209
 
167
210
  /**
168
- * Clear all events
211
+ * Override clearEvents to also clear pending requests
169
212
  */
170
213
  clearEvents() {
171
- this.events = [];
214
+ super.clearEvents();
172
215
  this.pendingRequests.clear();
173
216
  this.recentRequests.clear();
174
- this.notifyListeners();
175
217
  }
176
218
 
177
219
  /**
178
- * Subscribe to event changes
220
+ * Subscribe to individual events with optional replay of existing events
179
221
  */
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
- }
222
+ onEvent(callback, replayExisting = true) {
223
+ const unsubscribe = this.subscribe(callback);
194
224
 
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();
225
+ // Replay existing events if requested
226
+ if (replayExisting && this.events.length > 0) {
227
+ const existingEvents = [...this.events].reverse();
228
+ for (const event of existingEvents) {
229
+ try {
230
+ callback(event);
231
+ } catch {
232
+ // Ignore callback errors
233
+ }
234
+ }
203
235
  }
236
+ return unsubscribe;
204
237
  }
205
-
206
- /**
207
- * Get statistics about network events
208
- */
209
238
  getStats() {
210
239
  const total = this.events.length;
211
240
  const successful = this.events.filter(e => e.status && e.status >= 200 && e.status < 300).length;
@@ -225,10 +254,6 @@ class NetworkEventStore {
225
254
  averageDuration: Math.round(avgDuration)
226
255
  };
227
256
  }
228
-
229
- /**
230
- * Filter events by criteria
231
- */
232
257
  filterEvents(filter) {
233
258
  let filtered = [...this.events];
234
259
  if (filter.method) {
@@ -259,7 +284,7 @@ class NetworkEventStore {
259
284
  }
260
285
 
261
286
  /**
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.
287
+ * Singleton store that aggregates captured network traffic.
288
+ * Automatically starts/stops capturing based on subscriber count.
264
289
  */
265
290
  export const networkEventStore = new NetworkEventStore();
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkModal.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkModal.tsx"],"names":[],"mappings":"AA4CA,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AA2gCD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAMpD"}
1
+ {"version":3,"file":"NetworkModal.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkModal.tsx"],"names":[],"mappings":"AA4CA,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAkhCD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAMpD"}
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Hook for accessing network events and controls
3
- * Uses Reactotron-style listener pattern
3
+ *
4
+ * Uses self-managing Subscribable pattern - the network listener automatically
5
+ * starts when this hook mounts and stops when all subscribers unmount.
4
6
  */
5
7
  import type { NetworkEvent, NetworkStats, NetworkFilter } from "../types";
6
8
  /** Free tier limit for network requests */
@@ -9,8 +11,10 @@ export declare const FREE_TIER_REQUEST_LIMIT = 25;
9
11
  * Custom hook for accessing network events and controls
10
12
  *
11
13
  * This hook provides a complete interface for network monitoring, including
12
- * event filtering, statistics calculation, and interception control. It uses
13
- * the Reactotron-style listener pattern for network event handling.
14
+ * event filtering, statistics calculation, and interception control.
15
+ *
16
+ * The network listener automatically starts when this hook mounts and stops
17
+ * when all components using it unmount (Subscribable pattern from TanStack Query).
14
18
  *
15
19
  * @returns Object containing filtered events, statistics, controls, and utilities
16
20
  *
@@ -23,17 +27,14 @@ export declare const FREE_TIER_REQUEST_LIMIT = 25;
23
27
  * filter,
24
28
  * setFilter,
25
29
  * clearEvents,
26
- * toggleInterception,
27
- * isEnabled
30
+ * isCapturing
28
31
  * } = useNetworkEvents();
29
32
  *
30
33
  * return (
31
34
  * <div>
32
35
  * <p>Total requests: {stats.totalRequests}</p>
33
36
  * <p>Success rate: {stats.successfulRequests}/{stats.totalRequests}</p>
34
- * <button onClick={toggleInterception}>
35
- * {isEnabled ? 'Stop' : 'Start'} Monitoring
36
- * </button>
37
+ * <p>Capturing: {isCapturing ? 'Yes' : 'No'}</p>
37
38
  * </div>
38
39
  * );
39
40
  * }
@@ -56,8 +57,8 @@ export declare function useNetworkEvents(options?: {
56
57
  filter: NetworkFilter;
57
58
  setFilter: import("react").Dispatch<import("react").SetStateAction<NetworkFilter>>;
58
59
  clearEvents: () => void;
59
- isEnabled: boolean;
60
- toggleInterception: () => void;
60
+ /** Whether the store has subscribers (listener is running) */
61
+ isCapturing: boolean;
61
62
  hosts: string[];
62
63
  methods: string[];
63
64
  /** Whether there are locked events due to free tier limit */
@@ -68,5 +69,11 @@ export declare function useNetworkEvents(options?: {
68
69
  isEventLocked: (eventId: string) => boolean;
69
70
  /** Free tier request limit */
70
71
  requestLimit: number;
72
+ /** Get subscriber counts for debugging */
73
+ getSubscriberCounts: () => {
74
+ eventCallbacks: number;
75
+ arrayListeners: number;
76
+ total: number;
77
+ };
71
78
  };
72
79
  //# sourceMappingURL=useNetworkEvents.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useNetworkEvents.d.ts","sourceRoot":"","sources":["../../../../src/network/hooks/useNetworkEvents.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG1E,2CAA2C;AAC3C,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO;IAuR9D,yEAAyE;;IAEzE,8CAA8C;;;;;;;;;;IAU9C,6DAA6D;;IAE7D,qDAAqD;;IAErD,kDAAkD;6BAzFR,MAAM;IA2FhD,8BAA8B;;EAGjC"}
1
+ {"version":3,"file":"useNetworkEvents.d.ts","sourceRoot":"","sources":["../../../../src/network/hooks/useNetworkEvents.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG1E,2CAA2C;AAC3C,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO;IAwP9D,yEAAyE;;IAEzE,8CAA8C;;;;;;IAM9C,8DAA8D;;;;IAI9D,6DAA6D;;IAE7D,qDAAqD;;IAErD,kDAAkD;6BAzFR,MAAM;IA2FhD,8BAA8B;;IAE9B,0CAA0C;;;;;;EAG7C"}
@@ -7,7 +7,7 @@ export { NetworkEventItemCompact } from "./components/NetworkEventItemCompact";
7
7
  export { useNetworkEvents } from "./hooks/useNetworkEvents";
8
8
  export { TickProvider, useTickEveryMinute } from "./hooks/useTickEveryMinute";
9
9
  export { networkListener, startNetworkListener, stopNetworkListener, addNetworkListener, removeAllNetworkListeners, isNetworkListening, getNetworkListenerCount, } from "./utils/networkListener";
10
- export { networkEventStore } from "./utils/networkEventStore";
10
+ export { networkEventStore, type NetworkEventCallback } from "./utils/networkEventStore";
11
11
  export { formatBytes, formatDuration, formatHttpStatus, } from "./utils/formatting";
12
12
  export type { NetworkEvent, NetworkStats, NetworkFilter, NetworkEventStatus, NetworkInsight, } from "./types";
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/network/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAG/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAG9E,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EACL,WAAW,EACX,cAAc,EACd,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,cAAc,GACf,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/network/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAG/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAG9E,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,KAAK,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACzF,OAAO,EACL,WAAW,EACX,cAAc,EACd,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,cAAc,GACf,MAAM,SAAS,CAAC"}
@@ -1,58 +1,67 @@
1
1
  /**
2
- * Network event store for managing captured network requests
3
- * Works with the Reactotron-style network listener
2
+ * Network Event Store
3
+ *
4
+ * Store for managing captured network requests. Uses BaseEventStore pattern -
5
+ * the network listener automatically starts when the first subscriber joins
6
+ * and stops when the last subscriber leaves.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { networkEventStore } from '@buoy-gg/network';
11
+ *
12
+ * // Subscribe to network events - automatically starts capturing!
13
+ * const unsubscribe = networkEventStore.subscribeToEvents((events) => {
14
+ * console.log('Network events:', events);
15
+ * });
16
+ *
17
+ * // Later, clean up - automatically stops if no other subscribers
18
+ * unsubscribe();
19
+ * ```
4
20
  */
21
+ import { BaseEventStore } from "@buoy-gg/shared-ui";
5
22
  import type { NetworkEvent } from "../types";
6
23
  import type { NetworkingEvent } from "./networkListener";
7
- declare class NetworkEventStore {
8
- private events;
24
+ /**
25
+ * Callback type for individual event notifications
26
+ */
27
+ export type NetworkEventCallback = (event: NetworkEvent) => void;
28
+ /**
29
+ * Callback type for full events array notifications
30
+ */
31
+ export type NetworkEventsArrayCallback = (events: NetworkEvent[]) => void;
32
+ declare class NetworkEventStore extends BaseEventStore<NetworkEvent> {
9
33
  private pendingRequests;
10
- private listeners;
11
- private maxEvents;
12
34
  private recentRequests;
35
+ private rawListenerUnsubscribe;
36
+ constructor();
13
37
  /**
14
- * Process a network listener event
38
+ * Start capturing network events
15
39
  */
16
- processNetworkEvent(event: NetworkingEvent): void;
40
+ protected startCapturing(): void;
17
41
  /**
18
- * Extract host from URL
42
+ * Stop capturing network events
19
43
  */
20
- private extractHost;
44
+ protected stopCapturing(): void;
21
45
  /**
22
- * Extract path from URL
46
+ * Check if the store is actively capturing network events
23
47
  */
24
- private extractPath;
48
+ isCapturing(): boolean;
25
49
  /**
26
- * Get size of data
50
+ * Process a network listener event
27
51
  */
52
+ processNetworkEvent(event: NetworkingEvent): void;
53
+ private extractHost;
54
+ private extractPath;
28
55
  private getDataSize;
29
- /**
30
- * Get all events
31
- */
32
- getEvents(): NetworkEvent[];
33
- /**
34
- * Get event by ID
35
- */
36
56
  getEventById(id: string): NetworkEvent | undefined;
37
57
  /**
38
- * Clear all events
58
+ * Override clearEvents to also clear pending requests
39
59
  */
40
60
  clearEvents(): void;
41
61
  /**
42
- * Subscribe to event changes
43
- */
44
- subscribe(listener: (events: NetworkEvent[]) => void): () => void;
45
- /**
46
- * Notify all listeners of changes
47
- */
48
- private notifyListeners;
49
- /**
50
- * Set maximum number of events to store
51
- */
52
- setMaxEvents(max: number): void;
53
- /**
54
- * Get statistics about network events
62
+ * Subscribe to individual events with optional replay of existing events
55
63
  */
64
+ onEvent(callback: NetworkEventCallback, replayExisting?: boolean): () => void;
56
65
  getStats(): {
57
66
  totalRequests: number;
58
67
  successfulRequests: number;
@@ -62,9 +71,6 @@ declare class NetworkEventStore {
62
71
  totalDataReceived: number;
63
72
  averageDuration: number;
64
73
  };
65
- /**
66
- * Filter events by criteria
67
- */
68
74
  filterEvents(filter: {
69
75
  method?: string;
70
76
  status?: "success" | "error" | "pending";
@@ -73,8 +79,8 @@ declare class NetworkEventStore {
73
79
  }): NetworkEvent[];
74
80
  }
75
81
  /**
76
- * Singleton store that aggregates captured network traffic. Components and hooks consume this
77
- * store to render histories, derive stats, and subscribe to real-time updates.
82
+ * Singleton store that aggregates captured network traffic.
83
+ * Automatically starts/stops capturing based on subscriber count.
78
84
  */
79
85
  export declare const networkEventStore: NetworkEventStore;
80
86
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"networkEventStore.d.ts","sourceRoot":"","sources":["../../../../src/network/utils/networkEventStore.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD,cAAM,iBAAiB;IACrB,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,eAAe,CAAwC;IAC/D,OAAO,CAAC,SAAS,CAAoD;IACrE,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,cAAc,CAAkC;IAExD;;OAEG;IACH,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IA0GjD;;OAEG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;IACH,SAAS,IAAI,YAAY,EAAE;IAI3B;;OAEG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIlD;;OAEG;IACH,WAAW,IAAI,IAAI;IAOnB;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI;IAOjE;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB;;OAEG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQ/B;;OAEG;IACH,QAAQ;;;;;;;;;IAuCR;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;QACzC,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,YAAY,EAAE;CAyCnB;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
1
+ {"version":3,"file":"networkEventStore.d.ts","sourceRoot":"","sources":["../../../../src/network/utils/networkEventStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAOzD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;AAE1E,cAAM,iBAAkB,SAAQ,cAAc,CAAC,YAAY,CAAC;IAC1D,OAAO,CAAC,eAAe,CAAwC;IAC/D,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,sBAAsB,CAA6B;;IAS3D;;OAEG;IACH,SAAS,CAAC,cAAc,IAAI,IAAI;IAgBhC;;OAEG;IACH,SAAS,CAAC,aAAa,IAAI,IAAI;IAc/B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,mBAAmB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAsGjD,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,WAAW;IAUnB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIlD;;OAEG;IACH,WAAW,IAAI,IAAI;IAMnB;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,oBAAoB,EAAE,cAAc,UAAO,GAAG,MAAM,IAAI;IAkB1E,QAAQ;;;;;;;;;IAuCR,YAAY,CAAC,MAAM,EAAE;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;QACzC,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,YAAY,EAAE;CAyCnB;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,mBAA0B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buoy-gg/network",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
4
4
  "description": "network package",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -26,7 +26,7 @@
26
26
  ],
27
27
  "sideEffects": false,
28
28
  "dependencies": {
29
- "@buoy-gg/shared-ui": "2.1.2"
29
+ "@buoy-gg/shared-ui": "2.1.3"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": "*",