@buoy-gg/events 2.1.1
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/LICENSE +58 -0
- package/README.md +55 -0
- package/lib/commonjs/components/EventsCopySettingsView.js +645 -0
- package/lib/commonjs/components/EventsModal.js +263 -0
- package/lib/commonjs/components/ReactQueryEventDetail.js +428 -0
- package/lib/commonjs/components/UnifiedEventDetail.js +370 -0
- package/lib/commonjs/components/UnifiedEventFilters.js +113 -0
- package/lib/commonjs/components/UnifiedEventItem.js +349 -0
- package/lib/commonjs/components/UnifiedEventList.js +154 -0
- package/lib/commonjs/components/UnifiedEventViewer.js +126 -0
- package/lib/commonjs/hooks/useUnifiedEvents.js +237 -0
- package/lib/commonjs/index.js +205 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/preset.js +66 -0
- package/lib/commonjs/stores/unifiedEventStore.js +413 -0
- package/lib/commonjs/types/copySettings.js +220 -0
- package/lib/commonjs/types/index.js +17 -0
- package/lib/commonjs/utils/autoDiscoverEventSources.js +640 -0
- package/lib/commonjs/utils/badgeSelectionStorage.js +58 -0
- package/lib/commonjs/utils/copySettingsStorage.js +66 -0
- package/lib/commonjs/utils/correlationUtils.js +130 -0
- package/lib/commonjs/utils/eventExportFormatter.js +1095 -0
- package/lib/commonjs/utils/eventTransformers.js +496 -0
- package/lib/module/components/EventsCopySettingsView.js +641 -0
- package/lib/module/components/EventsModal.js +259 -0
- package/lib/module/components/ReactQueryEventDetail.js +424 -0
- package/lib/module/components/UnifiedEventDetail.js +366 -0
- package/lib/module/components/UnifiedEventFilters.js +109 -0
- package/lib/module/components/UnifiedEventItem.js +345 -0
- package/lib/module/components/UnifiedEventList.js +150 -0
- package/lib/module/components/UnifiedEventViewer.js +122 -0
- package/lib/module/hooks/useUnifiedEvents.js +234 -0
- package/lib/module/index.js +77 -0
- package/lib/module/preset.js +62 -0
- package/lib/module/stores/unifiedEventStore.js +387 -0
- package/lib/module/types/copySettings.js +215 -0
- package/lib/module/types/index.js +37 -0
- package/lib/module/utils/autoDiscoverEventSources.js +633 -0
- package/lib/module/utils/badgeSelectionStorage.js +52 -0
- package/lib/module/utils/copySettingsStorage.js +61 -0
- package/lib/module/utils/correlationUtils.js +120 -0
- package/lib/module/utils/eventExportFormatter.js +1085 -0
- package/lib/module/utils/eventTransformers.js +487 -0
- package/lib/typescript/components/EventsCopySettingsView.d.ts +16 -0
- package/lib/typescript/components/EventsModal.d.ts +16 -0
- package/lib/typescript/components/ReactQueryEventDetail.d.ts +15 -0
- package/lib/typescript/components/UnifiedEventDetail.d.ts +15 -0
- package/lib/typescript/components/UnifiedEventFilters.d.ts +21 -0
- package/lib/typescript/components/UnifiedEventItem.d.ts +26 -0
- package/lib/typescript/components/UnifiedEventList.d.ts +27 -0
- package/lib/typescript/components/UnifiedEventViewer.d.ts +8 -0
- package/lib/typescript/hooks/useUnifiedEvents.d.ts +30 -0
- package/lib/typescript/index.d.ts +28 -0
- package/lib/typescript/preset.d.ts +62 -0
- package/lib/typescript/stores/unifiedEventStore.d.ts +146 -0
- package/lib/typescript/types/copySettings.d.ts +179 -0
- package/lib/typescript/types/index.d.ts +73 -0
- package/lib/typescript/utils/autoDiscoverEventSources.d.ts +74 -0
- package/lib/typescript/utils/badgeSelectionStorage.d.ts +21 -0
- package/lib/typescript/utils/copySettingsStorage.d.ts +21 -0
- package/lib/typescript/utils/correlationUtils.d.ts +36 -0
- package/lib/typescript/utils/eventExportFormatter.d.ts +49 -0
- package/lib/typescript/utils/eventTransformers.d.ts +119 -0
- package/package.json +91 -0
- package/src/components/EventsCopySettingsView.tsx +742 -0
- package/src/components/EventsModal.tsx +328 -0
- package/src/components/ReactQueryEventDetail.tsx +413 -0
- package/src/components/UnifiedEventDetail.tsx +371 -0
- package/src/components/UnifiedEventFilters.tsx +156 -0
- package/src/components/UnifiedEventItem.tsx +396 -0
- package/src/components/UnifiedEventList.tsx +197 -0
- package/src/components/UnifiedEventViewer.tsx +132 -0
- package/src/hooks/useUnifiedEvents.ts +288 -0
- package/src/index.tsx +112 -0
- package/src/preset.tsx +57 -0
- package/src/stores/unifiedEventStore.ts +405 -0
- package/src/types/copySettings.ts +269 -0
- package/src/types/index.ts +96 -0
- package/src/utils/autoDiscoverEventSources.ts +690 -0
- package/src/utils/badgeSelectionStorage.ts +51 -0
- package/src/utils/copySettingsStorage.ts +61 -0
- package/src/utils/correlationUtils.ts +146 -0
- package/src/utils/eventExportFormatter.ts +1233 -0
- package/src/utils/eventTransformers.ts +567 -0
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Auto-Discover Event Sources
|
|
5
|
+
*
|
|
6
|
+
* Automatically discovers and loads event stores from installed packages.
|
|
7
|
+
* Uses try-catch pattern to safely handle missing packages - if a tool
|
|
8
|
+
* isn't installed, it's silently skipped.
|
|
9
|
+
*
|
|
10
|
+
* This enables zero-config setup - just install the dev tool packages you
|
|
11
|
+
* want and they'll automatically appear in the unified events timeline!
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Types for discovered event sources
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Configuration for a discovered event source
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Result of auto-discovery
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Event transformers (inline to avoid import dependencies)
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
let eventIdCounter = 0;
|
|
31
|
+
function generateEventId(source) {
|
|
32
|
+
return `${source}-${Date.now()}-${++eventIdCounter}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Storage Event Source Discovery
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
function tryLoadStorageSource() {
|
|
40
|
+
try {
|
|
41
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
42
|
+
const {
|
|
43
|
+
storageEventStore
|
|
44
|
+
} = require("@buoy-gg/storage");
|
|
45
|
+
return {
|
|
46
|
+
id: "storage",
|
|
47
|
+
name: "Storage",
|
|
48
|
+
eventSources: ["storage-async", "storage-mmkv"],
|
|
49
|
+
available: true,
|
|
50
|
+
setup: async () => {
|
|
51
|
+
await storageEventStore.startCapturing();
|
|
52
|
+
},
|
|
53
|
+
subscribe: onEvent => {
|
|
54
|
+
return storageEventStore.onEvent(event => {
|
|
55
|
+
const unifiedEvent = transformStorageEvent(event);
|
|
56
|
+
onEvent(unifiedEvent);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function transformStorageEvent(event) {
|
|
65
|
+
const e = event;
|
|
66
|
+
const source = e.storageType === "async" ? "storage-async" : "storage-mmkv";
|
|
67
|
+
|
|
68
|
+
// Get action label
|
|
69
|
+
let title = e.action;
|
|
70
|
+
if (e.storageType === "async") {
|
|
71
|
+
const labelMap = {
|
|
72
|
+
setItem: "Set Item",
|
|
73
|
+
removeItem: "Remove Item",
|
|
74
|
+
mergeItem: "Merge Item",
|
|
75
|
+
clear: "Clear All",
|
|
76
|
+
multiSet: "Multi Set",
|
|
77
|
+
multiRemove: "Multi Remove",
|
|
78
|
+
multiMerge: "Multi Merge"
|
|
79
|
+
};
|
|
80
|
+
title = labelMap[e.action] || e.action;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Get subtitle
|
|
84
|
+
let subtitle = "";
|
|
85
|
+
if (e.storageType === "async") {
|
|
86
|
+
switch (e.action) {
|
|
87
|
+
case "setItem":
|
|
88
|
+
case "removeItem":
|
|
89
|
+
case "mergeItem":
|
|
90
|
+
subtitle = e.data?.key || "unknown key";
|
|
91
|
+
break;
|
|
92
|
+
case "multiSet":
|
|
93
|
+
case "multiMerge":
|
|
94
|
+
{
|
|
95
|
+
const pairCount = e.data?.pairs?.length || 0;
|
|
96
|
+
subtitle = `${pairCount} key${pairCount !== 1 ? "s" : ""}`;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "multiRemove":
|
|
100
|
+
{
|
|
101
|
+
const keyCount = e.data?.keys?.length || 0;
|
|
102
|
+
subtitle = `${keyCount} key${keyCount !== 1 ? "s" : ""}`;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case "clear":
|
|
106
|
+
subtitle = "all keys";
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
subtitle = e.data?.key || e.data?.instanceId || "unknown";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Get status
|
|
114
|
+
let status = "neutral";
|
|
115
|
+
if (e.action.includes("set") || e.action.includes("Set") || e.action.includes("merge") || e.action.includes("Merge")) {
|
|
116
|
+
status = "success";
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
id: generateEventId(source),
|
|
120
|
+
source,
|
|
121
|
+
timestamp: e.timestamp.getTime(),
|
|
122
|
+
title,
|
|
123
|
+
subtitle,
|
|
124
|
+
status,
|
|
125
|
+
originalEvent: event
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// Redux Event Source Discovery
|
|
131
|
+
// ============================================================================
|
|
132
|
+
|
|
133
|
+
function tryLoadReduxSource() {
|
|
134
|
+
try {
|
|
135
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
136
|
+
const {
|
|
137
|
+
reduxActionStore
|
|
138
|
+
} = require("@buoy-gg/redux");
|
|
139
|
+
let lastActionId = null;
|
|
140
|
+
return {
|
|
141
|
+
id: "redux",
|
|
142
|
+
name: "Redux",
|
|
143
|
+
eventSources: ["redux"],
|
|
144
|
+
available: true,
|
|
145
|
+
subscribe: onEvent => {
|
|
146
|
+
return reduxActionStore.subscribe(actions => {
|
|
147
|
+
if (actions.length > 0) {
|
|
148
|
+
const latestAction = actions[0];
|
|
149
|
+
if (latestAction.id !== lastActionId) {
|
|
150
|
+
lastActionId = latestAction.id;
|
|
151
|
+
const unifiedEvent = transformReduxAction(latestAction);
|
|
152
|
+
onEvent(unifiedEvent);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
} catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function transformReduxAction(action) {
|
|
163
|
+
const a = action;
|
|
164
|
+
|
|
165
|
+
// Get status
|
|
166
|
+
let status = "neutral";
|
|
167
|
+
switch (a.category) {
|
|
168
|
+
case "fulfilled":
|
|
169
|
+
status = "success";
|
|
170
|
+
break;
|
|
171
|
+
case "rejected":
|
|
172
|
+
status = "error";
|
|
173
|
+
break;
|
|
174
|
+
case "pending":
|
|
175
|
+
status = "pending";
|
|
176
|
+
break;
|
|
177
|
+
default:
|
|
178
|
+
status = a.hasStateChange ? "success" : "neutral";
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Get subtitle
|
|
182
|
+
let subtitle = "";
|
|
183
|
+
if (a.payloadPreview) {
|
|
184
|
+
subtitle = a.payloadPreview;
|
|
185
|
+
} else if (a.hasStateChange && a.diffSummary) {
|
|
186
|
+
subtitle = a.diffSummary;
|
|
187
|
+
} else if (a.duration !== undefined) {
|
|
188
|
+
subtitle = `${a.duration.toFixed(1)}ms`;
|
|
189
|
+
} else {
|
|
190
|
+
subtitle = a.sliceName || "";
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
id: generateEventId("redux"),
|
|
194
|
+
source: "redux",
|
|
195
|
+
timestamp: a.timestamp,
|
|
196
|
+
title: a.type,
|
|
197
|
+
subtitle,
|
|
198
|
+
status,
|
|
199
|
+
originalEvent: action
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ============================================================================
|
|
204
|
+
// Network Event Source Discovery
|
|
205
|
+
// ============================================================================
|
|
206
|
+
|
|
207
|
+
function tryLoadNetworkSource() {
|
|
208
|
+
try {
|
|
209
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
210
|
+
const {
|
|
211
|
+
networkEventStore,
|
|
212
|
+
startNetworkListener,
|
|
213
|
+
addNetworkListener
|
|
214
|
+
} = require("@buoy-gg/network");
|
|
215
|
+
|
|
216
|
+
// Map network event IDs to unified event IDs for updates
|
|
217
|
+
const networkEventIdMap = new Map();
|
|
218
|
+
let storeUnsubscribe = null;
|
|
219
|
+
let listenerUnsubscribe = null;
|
|
220
|
+
return {
|
|
221
|
+
id: "network",
|
|
222
|
+
name: "Network",
|
|
223
|
+
eventSources: ["network"],
|
|
224
|
+
available: true,
|
|
225
|
+
setup: () => {
|
|
226
|
+
startNetworkListener();
|
|
227
|
+
},
|
|
228
|
+
subscribe: onEvent => {
|
|
229
|
+
// Add listener to process network events into the store
|
|
230
|
+
listenerUnsubscribe = addNetworkListener(event => {
|
|
231
|
+
networkEventStore.processNetworkEvent(event);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Subscribe to network event store
|
|
235
|
+
storeUnsubscribe = networkEventStore.subscribe(events => {
|
|
236
|
+
for (const networkEvent of events) {
|
|
237
|
+
const ne = networkEvent;
|
|
238
|
+
const existingUnifiedId = networkEventIdMap.get(ne.id);
|
|
239
|
+
if (!existingUnifiedId) {
|
|
240
|
+
// New event
|
|
241
|
+
const unifiedEvent = transformNetworkEvent(networkEvent);
|
|
242
|
+
networkEventIdMap.set(ne.id, unifiedEvent.id);
|
|
243
|
+
onEvent(unifiedEvent);
|
|
244
|
+
} else {
|
|
245
|
+
// Existing event updated (e.g., pending -> success/error)
|
|
246
|
+
// Create updated unified event with same ID so store can update it
|
|
247
|
+
const unifiedEvent = transformNetworkEvent(networkEvent);
|
|
248
|
+
unifiedEvent.id = existingUnifiedId; // Keep the same unified ID
|
|
249
|
+
onEvent(unifiedEvent);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
return () => {
|
|
254
|
+
listenerUnsubscribe?.();
|
|
255
|
+
storeUnsubscribe?.();
|
|
256
|
+
listenerUnsubscribe = null;
|
|
257
|
+
storeUnsubscribe = null;
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
} catch {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function transformNetworkEvent(event) {
|
|
266
|
+
const e = event;
|
|
267
|
+
|
|
268
|
+
// Get status
|
|
269
|
+
let status = "neutral";
|
|
270
|
+
if (e.status === undefined) {
|
|
271
|
+
status = "pending";
|
|
272
|
+
} else if (e.error || e.status >= 400) {
|
|
273
|
+
status = "error";
|
|
274
|
+
} else if (e.status >= 200 && e.status < 400) {
|
|
275
|
+
status = "success";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Get title
|
|
279
|
+
const title = e.operationName || `${e.method} ${e.path || e.url}`;
|
|
280
|
+
|
|
281
|
+
// Get subtitle
|
|
282
|
+
const parts = [];
|
|
283
|
+
if (e.status !== undefined) {
|
|
284
|
+
parts.push(`${e.status}`);
|
|
285
|
+
} else if (e.error) {
|
|
286
|
+
parts.push("Error");
|
|
287
|
+
} else {
|
|
288
|
+
parts.push("Pending");
|
|
289
|
+
}
|
|
290
|
+
if (e.duration !== undefined) {
|
|
291
|
+
parts.push(`${e.duration.toFixed(0)}ms`);
|
|
292
|
+
}
|
|
293
|
+
if (e.host) {
|
|
294
|
+
parts.push(e.host);
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
id: generateEventId("network"),
|
|
298
|
+
source: "network",
|
|
299
|
+
timestamp: e.timestamp,
|
|
300
|
+
title,
|
|
301
|
+
subtitle: parts.join(" · "),
|
|
302
|
+
status,
|
|
303
|
+
originalEvent: event
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ============================================================================
|
|
308
|
+
// React Query Event Source Discovery
|
|
309
|
+
// ============================================================================
|
|
310
|
+
|
|
311
|
+
function tryLoadReactQuerySource() {
|
|
312
|
+
try {
|
|
313
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
314
|
+
const {
|
|
315
|
+
reactQueryEventStore
|
|
316
|
+
} = require("@buoy-gg/react-query");
|
|
317
|
+
let lastEventId = null;
|
|
318
|
+
return {
|
|
319
|
+
id: "react-query",
|
|
320
|
+
name: "React Query",
|
|
321
|
+
eventSources: ["react-query", "react-query-query", "react-query-mutation"],
|
|
322
|
+
available: true,
|
|
323
|
+
subscribe: onEvent => {
|
|
324
|
+
return reactQueryEventStore.subscribe(events => {
|
|
325
|
+
if (events.length > 0) {
|
|
326
|
+
const latestEvent = events[0];
|
|
327
|
+
if (latestEvent.id !== lastEventId) {
|
|
328
|
+
lastEventId = latestEvent.id;
|
|
329
|
+
const unifiedEvent = transformReactQueryEvent(latestEvent);
|
|
330
|
+
onEvent(unifiedEvent);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
} catch {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function formatQueryKey(queryKey) {
|
|
341
|
+
if (!queryKey || queryKey.length === 0) return "unknown";
|
|
342
|
+
return queryKey.map(part => {
|
|
343
|
+
if (typeof part === "string") return part;
|
|
344
|
+
if (typeof part === "number") return String(part);
|
|
345
|
+
if (typeof part === "object" && part !== null) {
|
|
346
|
+
const obj = part;
|
|
347
|
+
const firstValue = Object.values(obj)[0];
|
|
348
|
+
if (typeof firstValue === "string" || typeof firstValue === "number") {
|
|
349
|
+
return String(firstValue);
|
|
350
|
+
}
|
|
351
|
+
return JSON.stringify(part).slice(0, 20);
|
|
352
|
+
}
|
|
353
|
+
return String(part);
|
|
354
|
+
}).join(" › ");
|
|
355
|
+
}
|
|
356
|
+
function transformReactQueryEvent(event) {
|
|
357
|
+
const e = event;
|
|
358
|
+
const isMutation = e.type.startsWith("mutation-");
|
|
359
|
+
const source = isMutation ? "react-query-mutation" : "react-query-query";
|
|
360
|
+
|
|
361
|
+
// Get status
|
|
362
|
+
let status = "neutral";
|
|
363
|
+
switch (e.type) {
|
|
364
|
+
case "query-fetch-start":
|
|
365
|
+
case "mutation-start":
|
|
366
|
+
status = "pending";
|
|
367
|
+
break;
|
|
368
|
+
case "query-fetch-success":
|
|
369
|
+
case "mutation-success":
|
|
370
|
+
status = "success";
|
|
371
|
+
break;
|
|
372
|
+
case "query-fetch-error":
|
|
373
|
+
case "mutation-error":
|
|
374
|
+
status = "error";
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Get title
|
|
379
|
+
let title = "";
|
|
380
|
+
if (e.queryKey) {
|
|
381
|
+
title = formatQueryKey(e.queryKey);
|
|
382
|
+
} else if (e.mutationKey) {
|
|
383
|
+
title = formatQueryKey(e.mutationKey);
|
|
384
|
+
} else if (e.mutationId !== undefined) {
|
|
385
|
+
title = `Mutation #${e.mutationId}`;
|
|
386
|
+
} else {
|
|
387
|
+
title = e.type.replace(/-/g, " ");
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Get subtitle
|
|
391
|
+
const parts = [];
|
|
392
|
+
switch (e.type) {
|
|
393
|
+
case "query-fetch-start":
|
|
394
|
+
parts.push("Fetching");
|
|
395
|
+
break;
|
|
396
|
+
case "query-fetch-success":
|
|
397
|
+
parts.push("Success");
|
|
398
|
+
break;
|
|
399
|
+
case "query-fetch-error":
|
|
400
|
+
parts.push("Error");
|
|
401
|
+
break;
|
|
402
|
+
case "query-invalidated":
|
|
403
|
+
parts.push("Invalidated");
|
|
404
|
+
break;
|
|
405
|
+
case "mutation-start":
|
|
406
|
+
parts.push("Mutating");
|
|
407
|
+
break;
|
|
408
|
+
case "mutation-success":
|
|
409
|
+
parts.push("Success");
|
|
410
|
+
break;
|
|
411
|
+
case "mutation-error":
|
|
412
|
+
parts.push("Error");
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
if (e.duration !== undefined) {
|
|
416
|
+
parts.push(`${e.duration.toFixed(0)}ms`);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Get correlation info
|
|
420
|
+
let correlationId;
|
|
421
|
+
if (e.queryHash) {
|
|
422
|
+
correlationId = `rq-query-${e.queryHash}`;
|
|
423
|
+
} else if (e.mutationId !== undefined) {
|
|
424
|
+
correlationId = `rq-mutation-${e.mutationId}`;
|
|
425
|
+
}
|
|
426
|
+
let sequenceInGroup = 1;
|
|
427
|
+
switch (e.type) {
|
|
428
|
+
case "query-fetch-start":
|
|
429
|
+
case "mutation-start":
|
|
430
|
+
sequenceInGroup = 1;
|
|
431
|
+
break;
|
|
432
|
+
case "query-fetch-success":
|
|
433
|
+
case "query-fetch-error":
|
|
434
|
+
case "mutation-success":
|
|
435
|
+
case "mutation-error":
|
|
436
|
+
sequenceInGroup = 2;
|
|
437
|
+
break;
|
|
438
|
+
case "query-invalidated":
|
|
439
|
+
sequenceInGroup = 3;
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
return {
|
|
443
|
+
id: generateEventId(source),
|
|
444
|
+
source,
|
|
445
|
+
timestamp: e.timestamp,
|
|
446
|
+
title,
|
|
447
|
+
subtitle: parts.join(" · "),
|
|
448
|
+
status,
|
|
449
|
+
originalEvent: event,
|
|
450
|
+
correlationId,
|
|
451
|
+
sequenceInGroup
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// ============================================================================
|
|
456
|
+
// Route Events Source Discovery
|
|
457
|
+
// ============================================================================
|
|
458
|
+
|
|
459
|
+
function tryLoadRouteEventsSource() {
|
|
460
|
+
try {
|
|
461
|
+
// @ts-ignore - Dynamic import that may not exist
|
|
462
|
+
const {
|
|
463
|
+
routeObserver
|
|
464
|
+
} = require("@buoy-gg/route-events");
|
|
465
|
+
return {
|
|
466
|
+
id: "route-events",
|
|
467
|
+
name: "Routes",
|
|
468
|
+
eventSources: ["route"],
|
|
469
|
+
available: true,
|
|
470
|
+
subscribe: onEvent => {
|
|
471
|
+
return routeObserver.addListener(event => {
|
|
472
|
+
const unifiedEvent = transformRouteEvent(event);
|
|
473
|
+
onEvent(unifiedEvent);
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
} catch {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function transformRouteEvent(event) {
|
|
482
|
+
const e = event;
|
|
483
|
+
|
|
484
|
+
// Get status
|
|
485
|
+
let status = "neutral";
|
|
486
|
+
if (e.pathname === "/") {
|
|
487
|
+
status = "success";
|
|
488
|
+
} else if (Object.keys(e.params).length > 0) {
|
|
489
|
+
status = "success";
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Get subtitle
|
|
493
|
+
const parts = [];
|
|
494
|
+
const paramCount = Object.keys(e.params).length;
|
|
495
|
+
if (paramCount > 0) {
|
|
496
|
+
parts.push(`${paramCount} param${paramCount !== 1 ? "s" : ""}`);
|
|
497
|
+
}
|
|
498
|
+
if (e.timeSincePrevious !== undefined && e.timeSincePrevious > 0) {
|
|
499
|
+
if (e.timeSincePrevious < 1000) {
|
|
500
|
+
parts.push(`${e.timeSincePrevious}ms`);
|
|
501
|
+
} else {
|
|
502
|
+
parts.push(`${(e.timeSincePrevious / 1000).toFixed(1)}s`);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (e.previousPathname && e.previousPathname !== e.pathname) {
|
|
506
|
+
parts.push(`from ${e.previousPathname}`);
|
|
507
|
+
}
|
|
508
|
+
return {
|
|
509
|
+
id: generateEventId("route"),
|
|
510
|
+
source: "route",
|
|
511
|
+
timestamp: e.timestamp,
|
|
512
|
+
title: e.pathname || "/",
|
|
513
|
+
subtitle: parts.join(" · ") || "navigation",
|
|
514
|
+
status,
|
|
515
|
+
originalEvent: event
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// ============================================================================
|
|
520
|
+
// Main Auto-Discovery Function
|
|
521
|
+
// ============================================================================
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Automatically discovers which event source packages are installed.
|
|
525
|
+
*
|
|
526
|
+
* This function attempts to load event stores from known dev tool packages.
|
|
527
|
+
* If a package is installed, its event source will be available.
|
|
528
|
+
* If a package is not installed, it will be silently skipped.
|
|
529
|
+
*
|
|
530
|
+
* @returns Discovery result with all available sources
|
|
531
|
+
*
|
|
532
|
+
* @example
|
|
533
|
+
* ```tsx
|
|
534
|
+
* const { sources, availableEventSources } = autoDiscoverEventSources();
|
|
535
|
+
*
|
|
536
|
+
* // sources contains all available event source configs
|
|
537
|
+
* // availableEventSources contains the Set of EventSource types available
|
|
538
|
+
* ```
|
|
539
|
+
*/
|
|
540
|
+
export function autoDiscoverEventSources() {
|
|
541
|
+
const sources = [];
|
|
542
|
+
const availableEventSources = new Set();
|
|
543
|
+
|
|
544
|
+
// Try to load each source
|
|
545
|
+
const loaders = [tryLoadStorageSource, tryLoadReduxSource, tryLoadNetworkSource, tryLoadReactQuerySource, tryLoadRouteEventsSource];
|
|
546
|
+
for (const loader of loaders) {
|
|
547
|
+
const source = loader();
|
|
548
|
+
if (source && source.available) {
|
|
549
|
+
sources.push(source);
|
|
550
|
+
source.eventSources.forEach(es => availableEventSources.add(es));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return {
|
|
554
|
+
sources,
|
|
555
|
+
availableEventSources
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Get display configuration for an event source.
|
|
561
|
+
* Returns config even for unavailable sources (for consistent UI).
|
|
562
|
+
*/
|
|
563
|
+
export function getSourceDisplayConfig(source) {
|
|
564
|
+
const configs = {
|
|
565
|
+
"storage-async": {
|
|
566
|
+
label: "Storage",
|
|
567
|
+
color: "#8B5CF6",
|
|
568
|
+
icon: "server-outline"
|
|
569
|
+
},
|
|
570
|
+
"storage-mmkv": {
|
|
571
|
+
label: "MMKV",
|
|
572
|
+
color: "#F59E0B",
|
|
573
|
+
icon: "flash-outline"
|
|
574
|
+
},
|
|
575
|
+
redux: {
|
|
576
|
+
label: "Redux",
|
|
577
|
+
color: "#3B82F6",
|
|
578
|
+
icon: "git-branch-outline"
|
|
579
|
+
},
|
|
580
|
+
network: {
|
|
581
|
+
label: "Network",
|
|
582
|
+
color: "#10B981",
|
|
583
|
+
icon: "globe-outline"
|
|
584
|
+
},
|
|
585
|
+
"react-query": {
|
|
586
|
+
label: "Query",
|
|
587
|
+
color: "#EC4899",
|
|
588
|
+
icon: "sync-outline"
|
|
589
|
+
},
|
|
590
|
+
"react-query-query": {
|
|
591
|
+
label: "Query",
|
|
592
|
+
color: "#EC4899",
|
|
593
|
+
icon: "sync-outline"
|
|
594
|
+
},
|
|
595
|
+
"react-query-mutation": {
|
|
596
|
+
label: "Mutation",
|
|
597
|
+
color: "#F97316",
|
|
598
|
+
icon: "flash-outline"
|
|
599
|
+
},
|
|
600
|
+
route: {
|
|
601
|
+
label: "Route",
|
|
602
|
+
color: "#06B6D4",
|
|
603
|
+
icon: "navigate-outline"
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
return configs[source] || {
|
|
607
|
+
label: source,
|
|
608
|
+
color: "#6B7280",
|
|
609
|
+
icon: "help-outline"
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Cache for discovered sources (singleton)
|
|
614
|
+
let cachedDiscovery = null;
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Get cached discovery result, or run discovery if not cached.
|
|
618
|
+
* Use this for performance - avoids repeated require() calls.
|
|
619
|
+
*/
|
|
620
|
+
export function getCachedDiscovery() {
|
|
621
|
+
if (!cachedDiscovery) {
|
|
622
|
+
cachedDiscovery = autoDiscoverEventSources();
|
|
623
|
+
}
|
|
624
|
+
return cachedDiscovery;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Clear the discovery cache (useful for testing or hot reload).
|
|
629
|
+
*/
|
|
630
|
+
export function clearDiscoveryCache() {
|
|
631
|
+
cachedDiscovery = null;
|
|
632
|
+
}
|
|
633
|
+
//# sourceMappingURL=autoDiscoverEventSources.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Badge Selection Storage
|
|
5
|
+
*
|
|
6
|
+
* Utilities for persisting and restoring the enabled event source badges.
|
|
7
|
+
* Uses the same storage patterns as other packages (React Query, Network, etc.)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { persistentStorage, devToolsStorageKeys } from "@buoy-gg/shared-ui";
|
|
11
|
+
/**
|
|
12
|
+
* Save the enabled sources to persistent storage
|
|
13
|
+
*/
|
|
14
|
+
export async function saveEnabledSources(sources) {
|
|
15
|
+
try {
|
|
16
|
+
const key = devToolsStorageKeys.events.enabledSources();
|
|
17
|
+
await persistentStorage.setItem(key, JSON.stringify(sources));
|
|
18
|
+
} catch {
|
|
19
|
+
// Silently fail - persistence is optional
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Load the enabled sources from persistent storage
|
|
25
|
+
* Returns null if no saved state exists
|
|
26
|
+
*/
|
|
27
|
+
export async function loadEnabledSources() {
|
|
28
|
+
try {
|
|
29
|
+
const key = devToolsStorageKeys.events.enabledSources();
|
|
30
|
+
const value = await persistentStorage.getItem(key);
|
|
31
|
+
if (value && value !== "") {
|
|
32
|
+
return JSON.parse(value);
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
} catch {
|
|
36
|
+
// Silently fail - return null to use defaults
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Clear the saved enabled sources
|
|
43
|
+
*/
|
|
44
|
+
export async function clearEnabledSources() {
|
|
45
|
+
try {
|
|
46
|
+
const key = devToolsStorageKeys.events.enabledSources();
|
|
47
|
+
await persistentStorage.removeItem(key);
|
|
48
|
+
} catch {
|
|
49
|
+
// Silently fail
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=badgeSelectionStorage.js.map
|