@buoy-gg/events 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.
- package/lib/commonjs/components/EventsCopySettingsView.js +39 -8
- package/lib/commonjs/components/UnifiedEventDetail.js +32 -1
- package/lib/commonjs/components/UnifiedEventFilters.js +50 -21
- package/lib/commonjs/components/UnifiedEventItem.js +6 -1
- package/lib/commonjs/hooks/useUnifiedEvents.js +137 -28
- package/lib/commonjs/index.js +6 -0
- package/lib/commonjs/stores/unifiedEventStore.js +61 -16
- package/lib/commonjs/types/copySettings.js +29 -0
- package/lib/commonjs/utils/autoDiscoverEventSources.js +261 -39
- package/lib/commonjs/utils/badgeSelectionStorage.js +32 -0
- package/lib/commonjs/utils/eventExportFormatter.js +778 -1
- package/lib/module/components/EventsCopySettingsView.js +40 -9
- package/lib/module/components/UnifiedEventDetail.js +32 -1
- package/lib/module/components/UnifiedEventFilters.js +50 -21
- package/lib/module/components/UnifiedEventItem.js +6 -1
- package/lib/module/hooks/useUnifiedEvents.js +140 -31
- package/lib/module/index.js +4 -1
- package/lib/module/stores/unifiedEventStore.js +58 -16
- package/lib/module/types/copySettings.js +29 -0
- package/lib/module/utils/autoDiscoverEventSources.js +260 -39
- package/lib/module/utils/badgeSelectionStorage.js +30 -0
- package/lib/module/utils/eventExportFormatter.js +777 -1
- package/lib/typescript/components/UnifiedEventFilters.d.ts +3 -0
- package/lib/typescript/hooks/useUnifiedEvents.d.ts +2 -0
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/stores/unifiedEventStore.d.ts +18 -2
- package/lib/typescript/types/copySettings.d.ts +25 -1
- package/lib/typescript/types/index.d.ts +3 -1
- package/lib/typescript/utils/autoDiscoverEventSources.d.ts +17 -0
- package/lib/typescript/utils/badgeSelectionStorage.d.ts +9 -0
- package/lib/typescript/utils/eventExportFormatter.d.ts +4 -0
- package/package.json +3 -3
- package/src/components/EventsCopySettingsView.tsx +41 -5
- package/src/components/UnifiedEventDetail.tsx +28 -0
- package/src/components/UnifiedEventFilters.tsx +88 -21
- package/src/components/UnifiedEventItem.tsx +5 -0
- package/src/hooks/useUnifiedEvents.ts +153 -25
- package/src/index.tsx +4 -0
- package/src/stores/unifiedEventStore.ts +58 -12
- package/src/types/copySettings.ts +31 -1
- package/src/types/index.ts +4 -1
- package/src/utils/autoDiscoverEventSources.ts +268 -44
- package/src/utils/badgeSelectionStorage.ts +30 -0
- package/src/utils/eventExportFormatter.ts +797 -0
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
9
|
-
import { useFeatureGate } from "@buoy-gg/shared-ui";
|
|
9
|
+
import { useFeatureGate, subscribeToSubscriberCountChanges } from "@buoy-gg/shared-ui";
|
|
10
10
|
import type { UnifiedEvent, EventSource, SourceInfo } from "../types";
|
|
11
11
|
import {
|
|
12
12
|
subscribe,
|
|
@@ -15,14 +15,24 @@ import {
|
|
|
15
15
|
subscribeToNetwork,
|
|
16
16
|
subscribeToReactQuery,
|
|
17
17
|
subscribeToRoutes,
|
|
18
|
+
subscribeToRender,
|
|
19
|
+
unsubscribeFromStorage,
|
|
20
|
+
unsubscribeFromRedux,
|
|
21
|
+
unsubscribeFromNetwork,
|
|
22
|
+
unsubscribeFromReactQuery,
|
|
23
|
+
unsubscribeFromRoutes,
|
|
24
|
+
unsubscribeFromRender,
|
|
18
25
|
unsubscribeAll,
|
|
19
26
|
getSourceCounts,
|
|
20
27
|
clearEvents as clearStoreEvents,
|
|
21
28
|
getAvailableEventSources,
|
|
29
|
+
getSubscriberCounts,
|
|
22
30
|
} from "../stores/unifiedEventStore";
|
|
23
31
|
import {
|
|
24
32
|
saveEnabledSources,
|
|
25
33
|
loadEnabledSources,
|
|
34
|
+
saveCapturingState,
|
|
35
|
+
loadCapturingState,
|
|
26
36
|
} from "../utils/badgeSelectionStorage";
|
|
27
37
|
import { getSourceDisplayConfig } from "../utils/autoDiscoverEventSources";
|
|
28
38
|
|
|
@@ -39,6 +49,7 @@ const ALL_DISPLAY_SOURCES: EventSource[] = [
|
|
|
39
49
|
"react-query-query",
|
|
40
50
|
"react-query-mutation",
|
|
41
51
|
"route",
|
|
52
|
+
"render",
|
|
42
53
|
];
|
|
43
54
|
|
|
44
55
|
/**
|
|
@@ -53,6 +64,7 @@ const SOURCE_TO_EVENT_SOURCES: Record<EventSource, EventSource[]> = {
|
|
|
53
64
|
"react-query-query": ["react-query", "react-query-query"],
|
|
54
65
|
"react-query-mutation": ["react-query-mutation"],
|
|
55
66
|
route: ["route"],
|
|
67
|
+
render: ["render"],
|
|
56
68
|
};
|
|
57
69
|
|
|
58
70
|
/**
|
|
@@ -67,6 +79,7 @@ const EVENT_SOURCE_TO_DISCOVERY_ID: Record<EventSource, string> = {
|
|
|
67
79
|
"react-query-query": "react-query",
|
|
68
80
|
"react-query-mutation": "react-query",
|
|
69
81
|
route: "route-events",
|
|
82
|
+
render: "render",
|
|
70
83
|
};
|
|
71
84
|
|
|
72
85
|
export interface UseUnifiedEventsResult {
|
|
@@ -89,6 +102,8 @@ export interface UseUnifiedEventsResult {
|
|
|
89
102
|
hiddenEventsCount: number;
|
|
90
103
|
/** Whether user is on Pro plan */
|
|
91
104
|
isPro: boolean;
|
|
105
|
+
/** Total number of subscribers across all event stores (for debugging) */
|
|
106
|
+
totalSubscriberCount: number;
|
|
92
107
|
}
|
|
93
108
|
|
|
94
109
|
export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
@@ -105,6 +120,14 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
105
120
|
return getAvailableEventSources();
|
|
106
121
|
}, []);
|
|
107
122
|
|
|
123
|
+
// Subscribe to subscriber count changes for instant UI updates (TanStack Query pattern)
|
|
124
|
+
const [subscriberCountVersion, setSubscriberCountVersion] = useState(0);
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
return subscribeToSubscriberCountChanges(() => {
|
|
127
|
+
setSubscriberCountVersion((v) => v + 1);
|
|
128
|
+
});
|
|
129
|
+
}, []);
|
|
130
|
+
|
|
108
131
|
// Get available display sources (only show sources for installed packages)
|
|
109
132
|
const availableDisplaySources = useMemo(() => {
|
|
110
133
|
return ALL_DISPLAY_SOURCES.filter((displaySource) => {
|
|
@@ -114,43 +137,56 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
114
137
|
});
|
|
115
138
|
}, [discoveredSources]);
|
|
116
139
|
|
|
117
|
-
// Subscribe to store changes
|
|
140
|
+
// Subscribe to store changes (always subscribe to get events when they come)
|
|
118
141
|
useEffect(() => {
|
|
119
|
-
// Subscribe to store updates
|
|
120
142
|
const unsubscribe = subscribe((newEvents) => {
|
|
121
143
|
setEvents(newEvents);
|
|
122
144
|
});
|
|
123
|
-
|
|
124
|
-
// Start capturing from all available sources
|
|
125
|
-
// These functions are now safe - they check if the source is available
|
|
126
|
-
subscribeToStorage();
|
|
127
|
-
subscribeToRedux();
|
|
128
|
-
subscribeToNetwork();
|
|
129
|
-
subscribeToReactQuery();
|
|
130
|
-
subscribeToRoutes();
|
|
131
|
-
|
|
132
145
|
return unsubscribe;
|
|
133
146
|
}, []);
|
|
134
147
|
|
|
135
|
-
// Restore saved badge selection
|
|
148
|
+
// Restore saved state on mount (badge selection + capturing state)
|
|
136
149
|
useEffect(() => {
|
|
137
150
|
const restoreState = async () => {
|
|
151
|
+
// Load badge selection
|
|
138
152
|
const savedSources = await loadEnabledSources();
|
|
153
|
+
let sourcesToEnable: Set<EventSource>;
|
|
154
|
+
|
|
139
155
|
if (savedSources && savedSources.length > 0) {
|
|
140
156
|
// Filter to only include valid sources that still exist and are available
|
|
141
157
|
const validSources = savedSources.filter(
|
|
142
158
|
(s) => availableDisplaySources.includes(s)
|
|
143
159
|
);
|
|
144
160
|
if (validSources.length > 0) {
|
|
145
|
-
|
|
161
|
+
sourcesToEnable = new Set(validSources);
|
|
146
162
|
} else {
|
|
147
163
|
// If no saved sources are valid, enable all available sources
|
|
148
|
-
|
|
164
|
+
sourcesToEnable = new Set(availableDisplaySources);
|
|
149
165
|
}
|
|
150
166
|
} else {
|
|
151
167
|
// No saved state - enable all available sources
|
|
152
|
-
|
|
168
|
+
sourcesToEnable = new Set(availableDisplaySources);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
setEnabledSources(sourcesToEnable);
|
|
172
|
+
|
|
173
|
+
// Load capturing state (default to true if not saved)
|
|
174
|
+
const savedCapturing = await loadCapturingState();
|
|
175
|
+
const shouldCapture = savedCapturing !== null ? savedCapturing : true;
|
|
176
|
+
setIsCapturing(shouldCapture);
|
|
177
|
+
|
|
178
|
+
// Start capturing if enabled - only subscribe to enabled sources
|
|
179
|
+
if (shouldCapture) {
|
|
180
|
+
if (sourcesToEnable.has("storage-async")) subscribeToStorage();
|
|
181
|
+
if (sourcesToEnable.has("redux")) subscribeToRedux();
|
|
182
|
+
if (sourcesToEnable.has("network")) subscribeToNetwork();
|
|
183
|
+
if (sourcesToEnable.has("react-query-query") || sourcesToEnable.has("react-query-mutation")) {
|
|
184
|
+
subscribeToReactQuery();
|
|
185
|
+
}
|
|
186
|
+
if (sourcesToEnable.has("route")) subscribeToRoutes();
|
|
187
|
+
if (sourcesToEnable.has("render")) subscribeToRender();
|
|
153
188
|
}
|
|
189
|
+
|
|
154
190
|
isStateRestoredRef.current = true;
|
|
155
191
|
};
|
|
156
192
|
restoreState();
|
|
@@ -164,6 +200,14 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
164
200
|
saveEnabledSources(Array.from(enabledSources));
|
|
165
201
|
}, [enabledSources]);
|
|
166
202
|
|
|
203
|
+
// Persist capturing state whenever it changes (after initial restoration)
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (!isStateRestoredRef.current) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
saveCapturingState(isCapturing);
|
|
209
|
+
}, [isCapturing]);
|
|
210
|
+
|
|
167
211
|
// Build set of all event sources that should be shown
|
|
168
212
|
const allowedEventSources = useMemo(() => {
|
|
169
213
|
const allowed = new Set<EventSource>();
|
|
@@ -203,17 +247,44 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
203
247
|
// Get all available sources with counts, sorted: enabled first, disabled last
|
|
204
248
|
const availableSources = useMemo((): SourceInfo[] => {
|
|
205
249
|
const counts = getSourceCounts();
|
|
250
|
+
const subscriberCounts = getSubscriberCounts();
|
|
251
|
+
|
|
252
|
+
// Build a map from source ID to subscriber count
|
|
253
|
+
const subscriberCountBySourceId: Record<string, number> = {};
|
|
254
|
+
for (const source of subscriberCounts.sources) {
|
|
255
|
+
subscriberCountBySourceId[source.sourceId] = source.counts.total;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Map display source to store source ID
|
|
259
|
+
const displaySourceToStoreId: Record<EventSource, string> = {
|
|
260
|
+
"storage-async": "storage",
|
|
261
|
+
"storage-mmkv": "storage",
|
|
262
|
+
redux: "redux",
|
|
263
|
+
network: "network",
|
|
264
|
+
"react-query": "react-query",
|
|
265
|
+
"react-query-query": "react-query",
|
|
266
|
+
"react-query-mutation": "react-query",
|
|
267
|
+
route: "route-events",
|
|
268
|
+
render: "render",
|
|
269
|
+
};
|
|
206
270
|
|
|
207
271
|
const sources = availableDisplaySources.map((source) => {
|
|
208
272
|
const eventSources = SOURCE_TO_EVENT_SOURCES[source] || [source];
|
|
209
273
|
const totalCount = eventSources.reduce((sum, s) => sum + (counts[s] || 0), 0);
|
|
210
274
|
const displayConfig = getSourceDisplayConfig(source);
|
|
275
|
+
const storeId = displaySourceToStoreId[source];
|
|
276
|
+
// Only set subscriberCount if the source has subscriber tracking
|
|
277
|
+
// (undefined means no tracking, vs 0 which means tracked but no subscribers)
|
|
278
|
+
const subscriberCount = storeId && storeId in subscriberCountBySourceId
|
|
279
|
+
? subscriberCountBySourceId[storeId]
|
|
280
|
+
: undefined;
|
|
211
281
|
|
|
212
282
|
return {
|
|
213
283
|
source,
|
|
214
284
|
...displayConfig,
|
|
215
285
|
count: totalCount,
|
|
216
286
|
enabled: enabledSources.has(source),
|
|
287
|
+
subscriberCount,
|
|
217
288
|
};
|
|
218
289
|
});
|
|
219
290
|
|
|
@@ -223,19 +294,68 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
223
294
|
if (!a.enabled && b.enabled) return 1;
|
|
224
295
|
return 0;
|
|
225
296
|
});
|
|
226
|
-
}, [events, enabledSources, availableDisplaySources]);
|
|
297
|
+
}, [events, enabledSources, availableDisplaySources, subscriberCountVersion]);
|
|
227
298
|
|
|
228
299
|
const toggleSource = useCallback((source: EventSource) => {
|
|
229
300
|
setEnabledSources((prev) => {
|
|
230
301
|
const next = new Set(prev);
|
|
231
|
-
|
|
302
|
+
const wasEnabled = next.has(source);
|
|
303
|
+
|
|
304
|
+
if (wasEnabled) {
|
|
232
305
|
next.delete(source);
|
|
306
|
+
// Unsubscribe from the source
|
|
307
|
+
switch (source) {
|
|
308
|
+
case "storage-async":
|
|
309
|
+
unsubscribeFromStorage();
|
|
310
|
+
break;
|
|
311
|
+
case "redux":
|
|
312
|
+
unsubscribeFromRedux();
|
|
313
|
+
break;
|
|
314
|
+
case "network":
|
|
315
|
+
unsubscribeFromNetwork();
|
|
316
|
+
break;
|
|
317
|
+
case "react-query-query":
|
|
318
|
+
case "react-query-mutation":
|
|
319
|
+
unsubscribeFromReactQuery();
|
|
320
|
+
break;
|
|
321
|
+
case "route":
|
|
322
|
+
unsubscribeFromRoutes();
|
|
323
|
+
break;
|
|
324
|
+
case "render":
|
|
325
|
+
unsubscribeFromRender();
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
233
328
|
} else {
|
|
234
329
|
next.add(source);
|
|
330
|
+
// Subscribe to the source (only if capturing is active)
|
|
331
|
+
if (isCapturing) {
|
|
332
|
+
switch (source) {
|
|
333
|
+
case "storage-async":
|
|
334
|
+
subscribeToStorage();
|
|
335
|
+
break;
|
|
336
|
+
case "redux":
|
|
337
|
+
subscribeToRedux();
|
|
338
|
+
break;
|
|
339
|
+
case "network":
|
|
340
|
+
subscribeToNetwork();
|
|
341
|
+
break;
|
|
342
|
+
case "react-query-query":
|
|
343
|
+
case "react-query-mutation":
|
|
344
|
+
subscribeToReactQuery();
|
|
345
|
+
break;
|
|
346
|
+
case "route":
|
|
347
|
+
subscribeToRoutes();
|
|
348
|
+
break;
|
|
349
|
+
case "render":
|
|
350
|
+
subscribeToRender();
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
235
354
|
}
|
|
355
|
+
|
|
236
356
|
return next;
|
|
237
357
|
});
|
|
238
|
-
}, []);
|
|
358
|
+
}, [isCapturing]);
|
|
239
359
|
|
|
240
360
|
const enableAllSources = useCallback(() => {
|
|
241
361
|
setEnabledSources(new Set(availableDisplaySources));
|
|
@@ -246,13 +366,17 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
246
366
|
}, []);
|
|
247
367
|
|
|
248
368
|
const startCapturing = useCallback(() => {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
369
|
+
// Only subscribe to sources that are enabled
|
|
370
|
+
if (enabledSources.has("storage-async")) subscribeToStorage();
|
|
371
|
+
if (enabledSources.has("redux")) subscribeToRedux();
|
|
372
|
+
if (enabledSources.has("network")) subscribeToNetwork();
|
|
373
|
+
if (enabledSources.has("react-query-query") || enabledSources.has("react-query-mutation")) {
|
|
374
|
+
subscribeToReactQuery();
|
|
375
|
+
}
|
|
376
|
+
if (enabledSources.has("route")) subscribeToRoutes();
|
|
377
|
+
if (enabledSources.has("render")) subscribeToRender();
|
|
254
378
|
setIsCapturing(true);
|
|
255
|
-
}, []);
|
|
379
|
+
}, [enabledSources]);
|
|
256
380
|
|
|
257
381
|
const stopCapturing = useCallback(() => {
|
|
258
382
|
unsubscribeAll();
|
|
@@ -267,6 +391,9 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
267
391
|
}
|
|
268
392
|
}, [isCapturing, startCapturing, stopCapturing]);
|
|
269
393
|
|
|
394
|
+
// Get subscriber counts (recalculates on every render, but it's a cheap operation)
|
|
395
|
+
const totalSubscriberCount = getSubscriberCounts().totalSubscribers;
|
|
396
|
+
|
|
270
397
|
return {
|
|
271
398
|
events,
|
|
272
399
|
filteredEvents,
|
|
@@ -284,5 +411,6 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
284
411
|
discoveredSources,
|
|
285
412
|
hiddenEventsCount,
|
|
286
413
|
isPro,
|
|
414
|
+
totalSubscriberCount,
|
|
287
415
|
};
|
|
288
416
|
}
|
package/src/index.tsx
CHANGED
|
@@ -81,6 +81,7 @@ export {
|
|
|
81
81
|
generateMarkdownExport,
|
|
82
82
|
generateJsonExport,
|
|
83
83
|
generatePlaintextExport,
|
|
84
|
+
generateMermaidExport,
|
|
84
85
|
estimateExportSize,
|
|
85
86
|
getExportSummary,
|
|
86
87
|
filterEvents,
|
|
@@ -110,3 +111,6 @@ export {
|
|
|
110
111
|
// - subscribeToRoutes, unsubscribeFromRoutes (internal subscription)
|
|
111
112
|
// - getEvents, getActiveSources, getSourceCounts (internal store operations)
|
|
112
113
|
// - clearEvents, subscribe, getEventCount (internal store operations)
|
|
114
|
+
|
|
115
|
+
// Note: For subscriber count notifications, use @buoy-gg/shared-ui:
|
|
116
|
+
// import { notifySubscriberCountChange, subscribeToSubscriberCountChanges } from "@buoy-gg/shared-ui";
|
|
@@ -14,7 +14,9 @@ import type {
|
|
|
14
14
|
} from "../types";
|
|
15
15
|
import {
|
|
16
16
|
getCachedDiscovery,
|
|
17
|
+
getAggregatedSubscriberCounts,
|
|
17
18
|
type DiscoveredEventSource,
|
|
19
|
+
type AggregatedSubscriberCounts,
|
|
18
20
|
} from "../utils/autoDiscoverEventSources";
|
|
19
21
|
|
|
20
22
|
const MAX_EVENTS = 200;
|
|
@@ -63,18 +65,22 @@ class UnifiedEventStore {
|
|
|
63
65
|
return; // Already subscribed
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
try {
|
|
69
|
+
// Run setup if needed
|
|
70
|
+
if (source.setup) {
|
|
71
|
+
await source.setup();
|
|
72
|
+
}
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
// Subscribe and track the unsubscriber
|
|
75
|
+
const unsubscribe = await source.subscribe((event) => {
|
|
76
|
+
this.addEvent(event);
|
|
77
|
+
});
|
|
75
78
|
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
if (unsubscribe) {
|
|
80
|
+
this.sourceUnsubscribers.set(source.id, unsubscribe);
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
// Silently fail - source may not be available
|
|
78
84
|
}
|
|
79
85
|
}
|
|
80
86
|
|
|
@@ -145,7 +151,8 @@ class UnifiedEventStore {
|
|
|
145
151
|
unsubscribeFromNetwork(): void {
|
|
146
152
|
this.unsubscribeFromSource("network");
|
|
147
153
|
this.activeSources.delete("network");
|
|
148
|
-
|
|
154
|
+
// Don't clear networkEventIdMap - it's needed to deduplicate events
|
|
155
|
+
// when resubscribing while requests are still in flight
|
|
149
156
|
}
|
|
150
157
|
|
|
151
158
|
/**
|
|
@@ -188,6 +195,25 @@ class UnifiedEventStore {
|
|
|
188
195
|
this.activeSources.delete("route");
|
|
189
196
|
}
|
|
190
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Subscribe to render events (if @buoy-gg/highlight-updates is installed)
|
|
200
|
+
*/
|
|
201
|
+
subscribeToRender(): void {
|
|
202
|
+
const { sources } = getCachedDiscovery();
|
|
203
|
+
const renderSource = sources.find((s) => s.id === "render");
|
|
204
|
+
if (renderSource) {
|
|
205
|
+
this.subscribeToSource(renderSource);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Unsubscribe from render events
|
|
211
|
+
*/
|
|
212
|
+
unsubscribeFromRender(): void {
|
|
213
|
+
this.unsubscribeFromSource("render");
|
|
214
|
+
this.activeSources.delete("render");
|
|
215
|
+
}
|
|
216
|
+
|
|
191
217
|
/**
|
|
192
218
|
* Add an event to the store
|
|
193
219
|
*/
|
|
@@ -257,6 +283,7 @@ class UnifiedEventStore {
|
|
|
257
283
|
"react-query-query": 0,
|
|
258
284
|
"react-query-mutation": 0,
|
|
259
285
|
route: 0,
|
|
286
|
+
render: 0,
|
|
260
287
|
};
|
|
261
288
|
|
|
262
289
|
for (const event of this.events) {
|
|
@@ -311,7 +338,7 @@ class UnifiedEventStore {
|
|
|
311
338
|
*/
|
|
312
339
|
unsubscribeAll(): void {
|
|
313
340
|
// Unsubscribe from all tracked sources
|
|
314
|
-
for (const [
|
|
341
|
+
for (const [, unsubscribe] of this.sourceUnsubscribers) {
|
|
315
342
|
unsubscribe();
|
|
316
343
|
}
|
|
317
344
|
this.sourceUnsubscribers.clear();
|
|
@@ -339,6 +366,8 @@ class UnifiedEventStore {
|
|
|
339
366
|
return this.sourceUnsubscribers.has("react-query");
|
|
340
367
|
case "route":
|
|
341
368
|
return this.sourceUnsubscribers.has("route-events");
|
|
369
|
+
case "render":
|
|
370
|
+
return this.sourceUnsubscribers.has("render");
|
|
342
371
|
default:
|
|
343
372
|
return false;
|
|
344
373
|
}
|
|
@@ -357,8 +386,17 @@ class UnifiedEventStore {
|
|
|
357
386
|
"react-query-query": this.sourceUnsubscribers.has("react-query"),
|
|
358
387
|
"react-query-mutation": this.sourceUnsubscribers.has("react-query"),
|
|
359
388
|
route: this.sourceUnsubscribers.has("route-events"),
|
|
389
|
+
render: this.sourceUnsubscribers.has("render"),
|
|
360
390
|
};
|
|
361
391
|
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Get subscriber counts from all underlying event stores.
|
|
395
|
+
* Useful for debugging the event system.
|
|
396
|
+
*/
|
|
397
|
+
getSubscriberCounts(): AggregatedSubscriberCounts {
|
|
398
|
+
return getAggregatedSubscriberCounts();
|
|
399
|
+
}
|
|
362
400
|
}
|
|
363
401
|
|
|
364
402
|
// Singleton instance
|
|
@@ -385,6 +423,10 @@ export const subscribeToRoutes = () =>
|
|
|
385
423
|
unifiedEventStore.subscribeToRoutes();
|
|
386
424
|
export const unsubscribeFromRoutes = () =>
|
|
387
425
|
unifiedEventStore.unsubscribeFromRoutes();
|
|
426
|
+
export const subscribeToRender = () =>
|
|
427
|
+
unifiedEventStore.subscribeToRender();
|
|
428
|
+
export const unsubscribeFromRender = () =>
|
|
429
|
+
unifiedEventStore.unsubscribeFromRender();
|
|
388
430
|
export const getEvents = (enabledSources?: Set<EventSource>) =>
|
|
389
431
|
unifiedEventStore.getEvents(enabledSources);
|
|
390
432
|
export const getActiveSources = () => unifiedEventStore.getActiveSources();
|
|
@@ -403,3 +445,7 @@ export const unsubscribeAll = () => unifiedEventStore.unsubscribeAll();
|
|
|
403
445
|
export const getDiscoveredSources = () => unifiedEventStore.getDiscoveredSources();
|
|
404
446
|
export const getAvailableEventSources = () => unifiedEventStore.getAvailableEventSources();
|
|
405
447
|
export const subscribeToAll = () => unifiedEventStore.subscribeToAll();
|
|
448
|
+
export const getSubscriberCounts = () => unifiedEventStore.getSubscriberCounts();
|
|
449
|
+
|
|
450
|
+
// Re-export type for convenience
|
|
451
|
+
export type { AggregatedSubscriberCounts } from "../utils/autoDiscoverEventSources";
|
|
@@ -30,7 +30,7 @@ export interface EventsCopySettings {
|
|
|
30
30
|
dataSizeThreshold: 1 | 5 | 10 | 50 | -1; // KB, -1 = unlimited
|
|
31
31
|
|
|
32
32
|
// Format
|
|
33
|
-
format: "markdown" | "json" | "plaintext";
|
|
33
|
+
format: "markdown" | "json" | "plaintext" | "mermaid";
|
|
34
34
|
|
|
35
35
|
// Filters
|
|
36
36
|
filterMode: "all" | "errors" | "success" | "pending";
|
|
@@ -204,6 +204,31 @@ export const COPY_PRESETS = {
|
|
|
204
204
|
showStorageDiff: false,
|
|
205
205
|
stripVerboseFields: true,
|
|
206
206
|
},
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Mermaid Diagram - Visual sequence diagram for flow visualization
|
|
210
|
+
*/
|
|
211
|
+
mermaid: {
|
|
212
|
+
timestampFormat: "relative" as const,
|
|
213
|
+
includeSource: true,
|
|
214
|
+
includeStatus: true,
|
|
215
|
+
includeTitle: true,
|
|
216
|
+
includeSubtitle: false,
|
|
217
|
+
includeCorrelation: true,
|
|
218
|
+
includeDuration: true,
|
|
219
|
+
includeSummaryHeader: false,
|
|
220
|
+
includeTotalDuration: false,
|
|
221
|
+
includeEventData: false,
|
|
222
|
+
dataSizeThreshold: 1 as const,
|
|
223
|
+
format: "mermaid" as const,
|
|
224
|
+
filterMode: "all" as const,
|
|
225
|
+
filterSources: [],
|
|
226
|
+
compactMode: false,
|
|
227
|
+
smartJsonParsing: true,
|
|
228
|
+
reduxChangedOnly: true,
|
|
229
|
+
showStorageDiff: true,
|
|
230
|
+
stripVerboseFields: true,
|
|
231
|
+
},
|
|
207
232
|
} as const;
|
|
208
233
|
|
|
209
234
|
export type CopyPresetName = keyof typeof COPY_PRESETS;
|
|
@@ -266,4 +291,9 @@ export const PRESET_METADATA: Record<
|
|
|
266
291
|
description: "Quick reference",
|
|
267
292
|
color: "#6B7280", // Gray
|
|
268
293
|
},
|
|
294
|
+
mermaid: {
|
|
295
|
+
label: "Diagram",
|
|
296
|
+
description: "Visual flow",
|
|
297
|
+
color: "#A855F7", // Purple
|
|
298
|
+
},
|
|
269
299
|
};
|
package/src/types/index.ts
CHANGED
|
@@ -15,7 +15,8 @@ export type EventSource =
|
|
|
15
15
|
| "react-query"
|
|
16
16
|
| "react-query-query"
|
|
17
17
|
| "react-query-mutation"
|
|
18
|
-
| "route"
|
|
18
|
+
| "route"
|
|
19
|
+
| "render";
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Event status for visual indicators
|
|
@@ -83,6 +84,8 @@ export interface SourceInfo {
|
|
|
83
84
|
count: number;
|
|
84
85
|
/** Whether this source is currently enabled (listening and showing events) */
|
|
85
86
|
enabled?: boolean;
|
|
87
|
+
/** Number of subscribers to this source's event store (for debugging) */
|
|
88
|
+
subscriberCount?: number;
|
|
86
89
|
}
|
|
87
90
|
|
|
88
91
|
/**
|