@buoy-gg/events 2.1.10 → 2.1.13
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 +3 -1
- package/lib/commonjs/components/ReactQueryEventDetail.js +7 -3
- package/lib/commonjs/components/UnifiedEventDetail.js +68 -2
- package/lib/commonjs/components/UnifiedEventItem.js +59 -1
- package/lib/commonjs/hooks/useUnifiedEvents.js +23 -1
- package/lib/commonjs/stores/unifiedEventStore.js +59 -1
- package/lib/commonjs/utils/autoDiscoverEventSources.js +146 -1
- package/lib/commonjs/utils/eventExportFormatter.js +2 -0
- package/lib/module/components/EventsCopySettingsView.js +3 -1
- package/lib/module/components/ReactQueryEventDetail.js +7 -3
- package/lib/module/components/UnifiedEventDetail.js +68 -2
- package/lib/module/components/UnifiedEventItem.js +59 -1
- package/lib/module/hooks/useUnifiedEvents.js +24 -2
- package/lib/module/stores/unifiedEventStore.js +54 -0
- package/lib/module/utils/autoDiscoverEventSources.js +146 -1
- package/lib/module/utils/eventExportFormatter.js +2 -0
- package/lib/typescript/stores/unifiedEventStore.d.ts +20 -0
- package/lib/typescript/types/index.d.ts +1 -1
- package/package.json +3 -3
- package/src/components/EventsCopySettingsView.tsx +3 -1
- package/src/components/ReactQueryEventDetail.tsx +7 -3
- package/src/components/UnifiedEventDetail.tsx +79 -1
- package/src/components/UnifiedEventItem.tsx +66 -0
- package/src/hooks/useUnifiedEvents.ts +28 -0
- package/src/stores/unifiedEventStore.ts +54 -0
- package/src/types/index.ts +3 -1
- package/src/utils/autoDiscoverEventSources.ts +165 -0
- package/src/utils/eventExportFormatter.ts +2 -0
|
@@ -48,6 +48,20 @@ interface OptionalDetailComponents {
|
|
|
48
48
|
RenderDetailView: ComponentType<{
|
|
49
49
|
render: unknown;
|
|
50
50
|
}> | null;
|
|
51
|
+
ZustandStateDetailContent: ComponentType<{
|
|
52
|
+
change: unknown;
|
|
53
|
+
changes: unknown[];
|
|
54
|
+
selectedIndex: number;
|
|
55
|
+
onIndexChange: (index: number) => void;
|
|
56
|
+
disableInternalFooter?: boolean;
|
|
57
|
+
}> | null;
|
|
58
|
+
JotaiAtomDetailContent: ComponentType<{
|
|
59
|
+
change: unknown;
|
|
60
|
+
changes: unknown[];
|
|
61
|
+
selectedIndex: number;
|
|
62
|
+
onIndexChange: (index: number) => void;
|
|
63
|
+
disableInternalFooter?: boolean;
|
|
64
|
+
}> | null;
|
|
51
65
|
}
|
|
52
66
|
|
|
53
67
|
function tryLoadOptionalDetailComponents(): OptionalDetailComponents {
|
|
@@ -56,6 +70,8 @@ function tryLoadOptionalDetailComponents(): OptionalDetailComponents {
|
|
|
56
70
|
ReduxActionDetailContent: null,
|
|
57
71
|
NetworkEventDetailView: null,
|
|
58
72
|
RenderDetailView: null,
|
|
73
|
+
ZustandStateDetailContent: null,
|
|
74
|
+
JotaiAtomDetailContent: null,
|
|
59
75
|
};
|
|
60
76
|
|
|
61
77
|
// Try to load storage detail component
|
|
@@ -94,6 +110,24 @@ function tryLoadOptionalDetailComponents(): OptionalDetailComponents {
|
|
|
94
110
|
// Optional dependency not installed
|
|
95
111
|
}
|
|
96
112
|
|
|
113
|
+
// Try to load zustand detail component
|
|
114
|
+
try {
|
|
115
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
116
|
+
const zustand = require("@buoy-gg/zustand");
|
|
117
|
+
components.ZustandStateDetailContent = zustand.ZustandStateDetailContent;
|
|
118
|
+
} catch {
|
|
119
|
+
// Optional dependency not installed
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Try to load jotai detail component
|
|
123
|
+
try {
|
|
124
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
125
|
+
const jotai = require("@buoy-gg/jotai");
|
|
126
|
+
components.JotaiAtomDetailContent = jotai.JotaiAtomDetailContent;
|
|
127
|
+
} catch {
|
|
128
|
+
// Optional dependency not installed
|
|
129
|
+
}
|
|
130
|
+
|
|
97
131
|
return components;
|
|
98
132
|
}
|
|
99
133
|
|
|
@@ -116,6 +150,8 @@ const SOURCE_CONFIG: Record<EventSource, { label: string; color: string }> = {
|
|
|
116
150
|
"react-query-query": { label: "Query", color: "#EC4899" },
|
|
117
151
|
"react-query-mutation": { label: "Mutation", color: "#F97316" },
|
|
118
152
|
route: { label: "Route", color: "#06B6D4" },
|
|
153
|
+
zustand: { label: "Zustand", color: "#764ABC" },
|
|
154
|
+
jotai: { label: "Jotai", color: "#14B8A6" },
|
|
119
155
|
render: { label: "Render", color: "#F472B6" },
|
|
120
156
|
};
|
|
121
157
|
|
|
@@ -236,6 +272,42 @@ export const UnifiedEventDetail = memo(function UnifiedEventDetail({
|
|
|
236
272
|
);
|
|
237
273
|
}
|
|
238
274
|
|
|
275
|
+
// For Zustand events, use the shared ZustandStateDetailContent if available
|
|
276
|
+
if (event.source === "zustand" && optionalComponents.ZustandStateDetailContent) {
|
|
277
|
+
const { ZustandStateDetailContent } = optionalComponents;
|
|
278
|
+
const zustandChange = event.originalEvent;
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<View style={styles.container}>
|
|
282
|
+
<ZustandStateDetailContent
|
|
283
|
+
change={zustandChange}
|
|
284
|
+
changes={[zustandChange]}
|
|
285
|
+
selectedIndex={0}
|
|
286
|
+
onIndexChange={() => {}}
|
|
287
|
+
disableInternalFooter={true}
|
|
288
|
+
/>
|
|
289
|
+
</View>
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// For Jotai events, use the shared JotaiAtomDetailContent if available
|
|
294
|
+
if (event.source === "jotai" && optionalComponents.JotaiAtomDetailContent) {
|
|
295
|
+
const { JotaiAtomDetailContent } = optionalComponents;
|
|
296
|
+
const jotaiChange = event.originalEvent;
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<View style={styles.container}>
|
|
300
|
+
<JotaiAtomDetailContent
|
|
301
|
+
change={jotaiChange}
|
|
302
|
+
changes={[jotaiChange]}
|
|
303
|
+
selectedIndex={0}
|
|
304
|
+
onIndexChange={() => {}}
|
|
305
|
+
disableInternalFooter={true}
|
|
306
|
+
/>
|
|
307
|
+
</View>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
239
311
|
// For render events, use the RenderDetailView if available
|
|
240
312
|
if (event.source === "render" && optionalComponents.RenderDetailView) {
|
|
241
313
|
const { RenderDetailView } = optionalComponents;
|
|
@@ -307,7 +379,13 @@ export const UnifiedEventDetail = memo(function UnifiedEventDetail({
|
|
|
307
379
|
<View style={styles.dataSection}>
|
|
308
380
|
<Text style={styles.sectionLabel}>Event Data</Text>
|
|
309
381
|
<View style={styles.dataContainer}>
|
|
310
|
-
<DataViewer
|
|
382
|
+
<DataViewer
|
|
383
|
+
data={getEventData()}
|
|
384
|
+
title=""
|
|
385
|
+
showTypeFilter={true}
|
|
386
|
+
rawMode={true}
|
|
387
|
+
initialExpanded={true}
|
|
388
|
+
/>
|
|
311
389
|
</View>
|
|
312
390
|
</View>
|
|
313
391
|
</ScrollView>
|
|
@@ -43,6 +43,14 @@ interface OptionalComponents {
|
|
|
43
43
|
onPress: () => void;
|
|
44
44
|
onNavigate?: (pathname: string) => void;
|
|
45
45
|
}> | null;
|
|
46
|
+
ZustandStateChangeItem: ComponentType<{
|
|
47
|
+
change: unknown;
|
|
48
|
+
onPress: (change: unknown) => void;
|
|
49
|
+
}> | null;
|
|
50
|
+
JotaiAtomChangeItem: ComponentType<{
|
|
51
|
+
change: unknown;
|
|
52
|
+
onPress: (change: unknown) => void;
|
|
53
|
+
}> | null;
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
function tryLoadOptionalComponents(): OptionalComponents {
|
|
@@ -52,6 +60,8 @@ function tryLoadOptionalComponents(): OptionalComponents {
|
|
|
52
60
|
ReduxActionItem: null,
|
|
53
61
|
NetworkEventItemCompact: null,
|
|
54
62
|
RouteEventItemCompact: null,
|
|
63
|
+
ZustandStateChangeItem: null,
|
|
64
|
+
JotaiAtomChangeItem: null,
|
|
55
65
|
};
|
|
56
66
|
|
|
57
67
|
// Try to load storage components
|
|
@@ -91,6 +101,24 @@ function tryLoadOptionalComponents(): OptionalComponents {
|
|
|
91
101
|
// Optional dependency not installed
|
|
92
102
|
}
|
|
93
103
|
|
|
104
|
+
// Try to load zustand components
|
|
105
|
+
try {
|
|
106
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
107
|
+
const zustand = require("@buoy-gg/zustand");
|
|
108
|
+
components.ZustandStateChangeItem = zustand.ZustandStateChangeItem;
|
|
109
|
+
} catch {
|
|
110
|
+
// Optional dependency not installed
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Try to load jotai components
|
|
114
|
+
try {
|
|
115
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
116
|
+
const jotai = require("@buoy-gg/jotai");
|
|
117
|
+
components.JotaiAtomChangeItem = jotai.JotaiAtomChangeItem;
|
|
118
|
+
} catch {
|
|
119
|
+
// Optional dependency not installed
|
|
120
|
+
}
|
|
121
|
+
|
|
94
122
|
return components;
|
|
95
123
|
}
|
|
96
124
|
|
|
@@ -140,6 +168,14 @@ const SOURCE_CONFIG: Record<
|
|
|
140
168
|
label: "Route",
|
|
141
169
|
color: "#06B6D4", // Cyan
|
|
142
170
|
},
|
|
171
|
+
zustand: {
|
|
172
|
+
label: "Zustand",
|
|
173
|
+
color: "#764ABC", // Purple
|
|
174
|
+
},
|
|
175
|
+
jotai: {
|
|
176
|
+
label: "Jotai",
|
|
177
|
+
color: "#14B8A6", // Teal
|
|
178
|
+
},
|
|
143
179
|
render: {
|
|
144
180
|
label: "Render",
|
|
145
181
|
color: "#F472B6", // Pink
|
|
@@ -220,6 +256,8 @@ const SOURCE_BADGE_LABELS: Record<EventSource, string> = {
|
|
|
220
256
|
"react-query-query": "QUERY",
|
|
221
257
|
"react-query-mutation": "MUTATION",
|
|
222
258
|
route: "ROUTE",
|
|
259
|
+
zustand: "ZUSTAND",
|
|
260
|
+
jotai: "JOTAI",
|
|
223
261
|
render: "RENDER",
|
|
224
262
|
};
|
|
225
263
|
|
|
@@ -342,6 +380,34 @@ export const UnifiedEventItem = memo(function UnifiedEventItem({
|
|
|
342
380
|
);
|
|
343
381
|
}
|
|
344
382
|
|
|
383
|
+
// For Zustand events, try to use the shared ZustandStateChangeItem component
|
|
384
|
+
if (event.source === "zustand" && optionalComponents.ZustandStateChangeItem) {
|
|
385
|
+
const { ZustandStateChangeItem } = optionalComponents;
|
|
386
|
+
|
|
387
|
+
return (
|
|
388
|
+
<SourceBadgeWrapper source={event.source}>
|
|
389
|
+
<ZustandStateChangeItem
|
|
390
|
+
change={event.originalEvent}
|
|
391
|
+
onPress={() => onPress()}
|
|
392
|
+
/>
|
|
393
|
+
</SourceBadgeWrapper>
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// For Jotai events, try to use the shared JotaiAtomChangeItem component
|
|
398
|
+
if (event.source === "jotai" && optionalComponents.JotaiAtomChangeItem) {
|
|
399
|
+
const { JotaiAtomChangeItem } = optionalComponents;
|
|
400
|
+
|
|
401
|
+
return (
|
|
402
|
+
<SourceBadgeWrapper source={event.source}>
|
|
403
|
+
<JotaiAtomChangeItem
|
|
404
|
+
change={event.originalEvent}
|
|
405
|
+
onPress={() => onPress()}
|
|
406
|
+
/>
|
|
407
|
+
</SourceBadgeWrapper>
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
345
411
|
// Fallback: Use CompactRow for all events when specific components aren't available
|
|
346
412
|
const customBadge =
|
|
347
413
|
correlationLabel && correlationColor ? (
|
|
@@ -15,12 +15,16 @@ import {
|
|
|
15
15
|
subscribeToNetwork,
|
|
16
16
|
subscribeToReactQuery,
|
|
17
17
|
subscribeToRoutes,
|
|
18
|
+
subscribeToZustand,
|
|
19
|
+
subscribeToJotai,
|
|
18
20
|
subscribeToRender,
|
|
19
21
|
unsubscribeFromStorage,
|
|
20
22
|
unsubscribeFromRedux,
|
|
21
23
|
unsubscribeFromNetwork,
|
|
22
24
|
unsubscribeFromReactQuery,
|
|
23
25
|
unsubscribeFromRoutes,
|
|
26
|
+
unsubscribeFromZustand,
|
|
27
|
+
unsubscribeFromJotai,
|
|
24
28
|
unsubscribeFromRender,
|
|
25
29
|
unsubscribeAll,
|
|
26
30
|
getSourceCounts,
|
|
@@ -82,6 +86,8 @@ const ALL_DISPLAY_SOURCES: EventSource[] = [
|
|
|
82
86
|
"react-query-query",
|
|
83
87
|
"react-query-mutation",
|
|
84
88
|
"route",
|
|
89
|
+
"zustand",
|
|
90
|
+
"jotai",
|
|
85
91
|
"render",
|
|
86
92
|
];
|
|
87
93
|
|
|
@@ -106,6 +112,8 @@ const SOURCE_TO_EVENT_SOURCES: Record<EventSource, EventSource[]> = {
|
|
|
106
112
|
"react-query-query": ["react-query", "react-query-query"],
|
|
107
113
|
"react-query-mutation": ["react-query-mutation"],
|
|
108
114
|
route: ["route"],
|
|
115
|
+
zustand: ["zustand"],
|
|
116
|
+
jotai: ["jotai"],
|
|
109
117
|
render: ["render"],
|
|
110
118
|
};
|
|
111
119
|
|
|
@@ -121,6 +129,8 @@ const EVENT_SOURCE_TO_DISCOVERY_ID: Record<EventSource, string> = {
|
|
|
121
129
|
"react-query-query": "react-query",
|
|
122
130
|
"react-query-mutation": "react-query",
|
|
123
131
|
route: "route-events",
|
|
132
|
+
zustand: "zustand",
|
|
133
|
+
jotai: "jotai",
|
|
124
134
|
render: "render",
|
|
125
135
|
};
|
|
126
136
|
|
|
@@ -231,6 +241,8 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
231
241
|
subscribeToReactQuery();
|
|
232
242
|
}
|
|
233
243
|
if (sourcesToEnable.has("route")) subscribeToRoutes();
|
|
244
|
+
if (sourcesToEnable.has("zustand")) subscribeToZustand();
|
|
245
|
+
if (sourcesToEnable.has("jotai")) subscribeToJotai();
|
|
234
246
|
if (sourcesToEnable.has("render")) subscribeToRender();
|
|
235
247
|
}
|
|
236
248
|
|
|
@@ -314,6 +326,8 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
314
326
|
"react-query-query": "react-query",
|
|
315
327
|
"react-query-mutation": "react-query",
|
|
316
328
|
route: "route-events",
|
|
329
|
+
zustand: "zustand",
|
|
330
|
+
jotai: "jotai",
|
|
317
331
|
render: "render",
|
|
318
332
|
};
|
|
319
333
|
|
|
@@ -370,6 +384,12 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
370
384
|
case "route":
|
|
371
385
|
unsubscribeFromRoutes();
|
|
372
386
|
break;
|
|
387
|
+
case "zustand":
|
|
388
|
+
unsubscribeFromZustand();
|
|
389
|
+
break;
|
|
390
|
+
case "jotai":
|
|
391
|
+
unsubscribeFromJotai();
|
|
392
|
+
break;
|
|
373
393
|
case "render":
|
|
374
394
|
unsubscribeFromRender();
|
|
375
395
|
break;
|
|
@@ -395,6 +415,12 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
395
415
|
case "route":
|
|
396
416
|
subscribeToRoutes();
|
|
397
417
|
break;
|
|
418
|
+
case "zustand":
|
|
419
|
+
subscribeToZustand();
|
|
420
|
+
break;
|
|
421
|
+
case "jotai":
|
|
422
|
+
subscribeToJotai();
|
|
423
|
+
break;
|
|
398
424
|
case "render":
|
|
399
425
|
subscribeToRender();
|
|
400
426
|
break;
|
|
@@ -423,6 +449,8 @@ export function useUnifiedEvents(): UseUnifiedEventsResult {
|
|
|
423
449
|
subscribeToReactQuery();
|
|
424
450
|
}
|
|
425
451
|
if (enabledSources.has("route")) subscribeToRoutes();
|
|
452
|
+
if (enabledSources.has("zustand")) subscribeToZustand();
|
|
453
|
+
if (enabledSources.has("jotai")) subscribeToJotai();
|
|
426
454
|
if (enabledSources.has("render")) subscribeToRender();
|
|
427
455
|
setIsCapturing(true);
|
|
428
456
|
}, [enabledSources]);
|
|
@@ -195,6 +195,44 @@ class UnifiedEventStore {
|
|
|
195
195
|
this.activeSources.delete("route");
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Subscribe to Zustand state changes (if @buoy-gg/zustand is installed)
|
|
200
|
+
*/
|
|
201
|
+
subscribeToZustand(): void {
|
|
202
|
+
const { sources } = getCachedDiscovery();
|
|
203
|
+
const zustandSource = sources.find((s) => s.id === "zustand");
|
|
204
|
+
if (zustandSource) {
|
|
205
|
+
this.subscribeToSource(zustandSource);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Unsubscribe from Zustand state changes
|
|
211
|
+
*/
|
|
212
|
+
unsubscribeFromZustand(): void {
|
|
213
|
+
this.unsubscribeFromSource("zustand");
|
|
214
|
+
this.activeSources.delete("zustand");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Subscribe to Jotai atom changes (if @buoy-gg/jotai is installed)
|
|
219
|
+
*/
|
|
220
|
+
subscribeToJotai(): void {
|
|
221
|
+
const { sources } = getCachedDiscovery();
|
|
222
|
+
const jotaiSource = sources.find((s) => s.id === "jotai");
|
|
223
|
+
if (jotaiSource) {
|
|
224
|
+
this.subscribeToSource(jotaiSource);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Unsubscribe from Jotai atom changes
|
|
230
|
+
*/
|
|
231
|
+
unsubscribeFromJotai(): void {
|
|
232
|
+
this.unsubscribeFromSource("jotai");
|
|
233
|
+
this.activeSources.delete("jotai");
|
|
234
|
+
}
|
|
235
|
+
|
|
198
236
|
/**
|
|
199
237
|
* Subscribe to render events (if @buoy-gg/highlight-updates is installed)
|
|
200
238
|
*/
|
|
@@ -283,6 +321,8 @@ class UnifiedEventStore {
|
|
|
283
321
|
"react-query-query": 0,
|
|
284
322
|
"react-query-mutation": 0,
|
|
285
323
|
route: 0,
|
|
324
|
+
zustand: 0,
|
|
325
|
+
jotai: 0,
|
|
286
326
|
render: 0,
|
|
287
327
|
};
|
|
288
328
|
|
|
@@ -366,6 +406,10 @@ class UnifiedEventStore {
|
|
|
366
406
|
return this.sourceUnsubscribers.has("react-query");
|
|
367
407
|
case "route":
|
|
368
408
|
return this.sourceUnsubscribers.has("route-events");
|
|
409
|
+
case "zustand":
|
|
410
|
+
return this.sourceUnsubscribers.has("zustand");
|
|
411
|
+
case "jotai":
|
|
412
|
+
return this.sourceUnsubscribers.has("jotai");
|
|
369
413
|
case "render":
|
|
370
414
|
return this.sourceUnsubscribers.has("render");
|
|
371
415
|
default:
|
|
@@ -386,6 +430,8 @@ class UnifiedEventStore {
|
|
|
386
430
|
"react-query-query": this.sourceUnsubscribers.has("react-query"),
|
|
387
431
|
"react-query-mutation": this.sourceUnsubscribers.has("react-query"),
|
|
388
432
|
route: this.sourceUnsubscribers.has("route-events"),
|
|
433
|
+
zustand: this.sourceUnsubscribers.has("zustand"),
|
|
434
|
+
jotai: this.sourceUnsubscribers.has("jotai"),
|
|
389
435
|
render: this.sourceUnsubscribers.has("render"),
|
|
390
436
|
};
|
|
391
437
|
}
|
|
@@ -423,6 +469,14 @@ export const subscribeToRoutes = () =>
|
|
|
423
469
|
unifiedEventStore.subscribeToRoutes();
|
|
424
470
|
export const unsubscribeFromRoutes = () =>
|
|
425
471
|
unifiedEventStore.unsubscribeFromRoutes();
|
|
472
|
+
export const subscribeToZustand = () =>
|
|
473
|
+
unifiedEventStore.subscribeToZustand();
|
|
474
|
+
export const unsubscribeFromZustand = () =>
|
|
475
|
+
unifiedEventStore.unsubscribeFromZustand();
|
|
476
|
+
export const subscribeToJotai = () =>
|
|
477
|
+
unifiedEventStore.subscribeToJotai();
|
|
478
|
+
export const unsubscribeFromJotai = () =>
|
|
479
|
+
unifiedEventStore.unsubscribeFromJotai();
|
|
426
480
|
export const subscribeToRender = () =>
|
|
427
481
|
unifiedEventStore.subscribeToRender();
|
|
428
482
|
export const unsubscribeFromRender = () =>
|
package/src/types/index.ts
CHANGED
|
@@ -596,6 +596,167 @@ function transformRouteEvent(event: unknown): UnifiedEvent {
|
|
|
596
596
|
};
|
|
597
597
|
}
|
|
598
598
|
|
|
599
|
+
// ============================================================================
|
|
600
|
+
// Zustand Event Source Discovery
|
|
601
|
+
// ============================================================================
|
|
602
|
+
|
|
603
|
+
function tryLoadZustandSource(): DiscoveredEventSource | null {
|
|
604
|
+
try {
|
|
605
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
606
|
+
const { zustandStateStore } = require("@buoy-gg/zustand");
|
|
607
|
+
|
|
608
|
+
let lastChangeId: string | null = null;
|
|
609
|
+
|
|
610
|
+
return {
|
|
611
|
+
id: "zustand",
|
|
612
|
+
name: "Zustand",
|
|
613
|
+
eventSources: ["zustand"],
|
|
614
|
+
available: true,
|
|
615
|
+
subscribe: (onEvent) => {
|
|
616
|
+
return zustandStateStore.subscribe((changes: unknown[]) => {
|
|
617
|
+
if (changes.length > 0) {
|
|
618
|
+
const latestChange = changes[0] as { id: string };
|
|
619
|
+
if (latestChange.id !== lastChangeId) {
|
|
620
|
+
lastChangeId = latestChange.id;
|
|
621
|
+
const unifiedEvent = transformZustandChange(latestChange);
|
|
622
|
+
onEvent(unifiedEvent);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
},
|
|
627
|
+
};
|
|
628
|
+
} catch {
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function transformZustandChange(change: unknown): UnifiedEvent {
|
|
634
|
+
const c = change as {
|
|
635
|
+
id: string;
|
|
636
|
+
storeName: string;
|
|
637
|
+
timestamp: number;
|
|
638
|
+
hasStateChange?: boolean;
|
|
639
|
+
category?: "setState" | "replace";
|
|
640
|
+
changedKeys?: string[];
|
|
641
|
+
changedKeysCount?: number;
|
|
642
|
+
diffSummary?: string;
|
|
643
|
+
partialPreview?: string;
|
|
644
|
+
duration?: number;
|
|
645
|
+
isSlowUpdate?: boolean;
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
// Get status
|
|
649
|
+
let status: "success" | "error" | "pending" | "neutral" = "neutral";
|
|
650
|
+
if (c.isSlowUpdate) {
|
|
651
|
+
status = "pending"; // Yellow for slow updates
|
|
652
|
+
} else if (c.hasStateChange) {
|
|
653
|
+
status = "success";
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Get subtitle
|
|
657
|
+
const parts: string[] = [];
|
|
658
|
+
if (c.changedKeys && c.changedKeys.length > 0) {
|
|
659
|
+
const keys = c.changedKeys.slice(0, 3).join(", ");
|
|
660
|
+
const more = c.changedKeys.length > 3 ? ` +${c.changedKeys.length - 3}` : "";
|
|
661
|
+
parts.push(`${keys}${more}`);
|
|
662
|
+
} else if (c.diffSummary) {
|
|
663
|
+
parts.push(c.diffSummary);
|
|
664
|
+
}
|
|
665
|
+
if (c.duration !== undefined) {
|
|
666
|
+
parts.push(`${c.duration.toFixed(1)}ms`);
|
|
667
|
+
}
|
|
668
|
+
if (c.category === "replace") {
|
|
669
|
+
parts.push("replace");
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return {
|
|
673
|
+
id: generateEventId("zustand"),
|
|
674
|
+
source: "zustand",
|
|
675
|
+
timestamp: c.timestamp,
|
|
676
|
+
title: c.storeName,
|
|
677
|
+
subtitle: parts.join(" · ") || (c.hasStateChange ? "state changed" : "no change"),
|
|
678
|
+
status,
|
|
679
|
+
originalEvent: change,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// ============================================================================
|
|
684
|
+
// Jotai Event Source Discovery
|
|
685
|
+
// ============================================================================
|
|
686
|
+
|
|
687
|
+
function tryLoadJotaiSource(): DiscoveredEventSource | null {
|
|
688
|
+
try {
|
|
689
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
690
|
+
const { jotaiStateStore } = require("@buoy-gg/jotai");
|
|
691
|
+
|
|
692
|
+
let lastChangeId: string | null = null;
|
|
693
|
+
|
|
694
|
+
return {
|
|
695
|
+
id: "jotai",
|
|
696
|
+
name: "Jotai",
|
|
697
|
+
eventSources: ["jotai"],
|
|
698
|
+
available: true,
|
|
699
|
+
subscribe: (onEvent) => {
|
|
700
|
+
return jotaiStateStore.subscribe((changes: unknown[]) => {
|
|
701
|
+
if (changes.length > 0) {
|
|
702
|
+
const latestChange = changes[0] as { id: string };
|
|
703
|
+
if (latestChange.id !== lastChangeId) {
|
|
704
|
+
lastChangeId = latestChange.id;
|
|
705
|
+
const unifiedEvent = transformJotaiChange(latestChange);
|
|
706
|
+
onEvent(unifiedEvent);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
},
|
|
711
|
+
};
|
|
712
|
+
} catch {
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function transformJotaiChange(change: unknown): UnifiedEvent {
|
|
718
|
+
const c = change as {
|
|
719
|
+
id: string;
|
|
720
|
+
atomLabel: string;
|
|
721
|
+
timestamp: number;
|
|
722
|
+
hasValueChange?: boolean;
|
|
723
|
+
category?: "write" | "read";
|
|
724
|
+
changedKeys?: string[];
|
|
725
|
+
changedKeysCount?: number;
|
|
726
|
+
diffSummary?: string;
|
|
727
|
+
valuePreview?: string;
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
// Get status
|
|
731
|
+
let status: "success" | "error" | "pending" | "neutral" = "neutral";
|
|
732
|
+
if (c.category === "write" && c.hasValueChange) {
|
|
733
|
+
status = "success";
|
|
734
|
+
} else if (c.category === "read") {
|
|
735
|
+
status = "neutral";
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Get subtitle
|
|
739
|
+
const parts: string[] = [];
|
|
740
|
+
if (c.valuePreview) {
|
|
741
|
+
parts.push(c.valuePreview);
|
|
742
|
+
} else if (c.diffSummary) {
|
|
743
|
+
parts.push(c.diffSummary);
|
|
744
|
+
}
|
|
745
|
+
if (c.category) {
|
|
746
|
+
parts.push(c.category);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
return {
|
|
750
|
+
id: generateEventId("jotai"),
|
|
751
|
+
source: "jotai",
|
|
752
|
+
timestamp: c.timestamp,
|
|
753
|
+
title: c.atomLabel,
|
|
754
|
+
subtitle: parts.join(" · ") || (c.hasValueChange ? "value changed" : "no change"),
|
|
755
|
+
status,
|
|
756
|
+
originalEvent: change,
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
|
|
599
760
|
// ============================================================================
|
|
600
761
|
// Render Events Source Discovery (Highlight Updates)
|
|
601
762
|
// ============================================================================
|
|
@@ -776,6 +937,8 @@ export function autoDiscoverEventSources(): DiscoveryResult {
|
|
|
776
937
|
tryLoadNetworkSource,
|
|
777
938
|
tryLoadReactQuerySource,
|
|
778
939
|
tryLoadRouteEventsSource,
|
|
940
|
+
tryLoadZustandSource,
|
|
941
|
+
tryLoadJotaiSource,
|
|
779
942
|
tryLoadHighlightUpdatesSource,
|
|
780
943
|
];
|
|
781
944
|
|
|
@@ -808,6 +971,8 @@ export function getSourceDisplayConfig(source: EventSource): {
|
|
|
808
971
|
"react-query-query": { label: "Query", color: "#EC4899", icon: "sync-outline" },
|
|
809
972
|
"react-query-mutation": { label: "Mutation", color: "#F97316", icon: "flash-outline" },
|
|
810
973
|
route: { label: "Route", color: "#06B6D4", icon: "navigate-outline" },
|
|
974
|
+
zustand: { label: "Zustand", color: "#764ABC", icon: "cube-outline" },
|
|
975
|
+
jotai: { label: "Jotai", color: "#14B8A6", icon: "ellipse-outline" },
|
|
811
976
|
render: { label: "Render", color: "#F472B6", icon: "layers-outline" },
|
|
812
977
|
};
|
|
813
978
|
|