Skip to content

Commit c33adef

Browse files
committed
chore: update version to 0.15.0 in package.json, package-lock.json, and manifest.json; add debounce timers in injected.ts for observer changes
1 parent 099cfe9 commit c33adef

5 files changed

Lines changed: 79 additions & 7 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "tanstack-query-chrome-devtools",
33
"private": true,
4-
"version": "0.14.0",
4+
"version": "0.15.0",
55
"type": "module",
66
"description": "TanStack Query DevTools",
77
"homepage": "https://github.com/DeeCode-inc/tanstack-query-chrome-devtools#readme",

public/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"strict_min_version": "112.0"
99
}
1010
},
11-
"version": "0.14.0",
11+
"version": "0.15.0",
1212
"description": "DevTools extension for debugging TanStack Query applications. Inspect queries, mutations, and cache state in real-time.",
1313
"homepage_url": "https://github.com/DeeCode-inc/tanstack-query-chrome-devtools",
1414

src/injected/injected.ts

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ class InjectedScript {
3030
});
3131
private isInitialized = false;
3232

33+
// Debounce timers for observer count changes (to handle virtualized lists)
34+
private queryObserverDebounceTimer: ReturnType<typeof setTimeout> | null =
35+
null;
36+
private mutationObserverDebounceTimer: ReturnType<typeof setTimeout> | null =
37+
null;
38+
3339
// Initialize injected script
3440
initialize(): void {
3541
if (this.isInitialized) return;
@@ -131,7 +137,37 @@ class InjectedScript {
131137
}
132138

133139
// Subscribe to cache changes
134-
this.queryUnsubscribe = queryClient.getQueryCache().subscribe(() => {
140+
// Filter events carefully to prevent infinite loops while still tracking subscriber count
141+
this.queryUnsubscribe = queryClient.getQueryCache().subscribe((event) => {
142+
// Ignore observer result/option updates as these can cause infinite loops
143+
// when storage updates trigger React component re-renders
144+
if (
145+
event.type === "observerResultsUpdated" ||
146+
event.type === "observerOptionsUpdated"
147+
) {
148+
return;
149+
}
150+
151+
// For observer add/remove events (e.g., virtualized lists scrolling),
152+
// debounce to avoid hundreds of updates per second
153+
if (
154+
event.type === "observerAdded" ||
155+
event.type === "observerRemoved"
156+
) {
157+
// Clear existing timer
158+
if (this.queryObserverDebounceTimer) {
159+
clearTimeout(this.queryObserverDebounceTimer);
160+
}
161+
162+
// Debounce: wait 150ms after last observer change before updating
163+
this.queryObserverDebounceTimer = setTimeout(() => {
164+
this.sendQueryDataUpdate();
165+
this.queryObserverDebounceTimer = null;
166+
}, 150);
167+
return;
168+
}
169+
170+
// Send updates immediately for real cache changes: 'added', 'removed', 'updated'
135171
this.sendQueryDataUpdate();
136172
});
137173
} catch (error) {
@@ -151,9 +187,35 @@ class InjectedScript {
151187
}
152188

153189
// Subscribe to mutation cache changes
190+
// Filter events carefully to prevent infinite loops while still tracking subscriber count
154191
this.mutationUnsubscribe = queryClient
155192
.getMutationCache()
156-
.subscribe(() => {
193+
.subscribe((event) => {
194+
// Ignore observer option updates as these can cause infinite loops
195+
// when storage updates trigger React component re-renders
196+
if (event.type === "observerOptionsUpdated") {
197+
return;
198+
}
199+
200+
// For observer add/remove events, debounce to avoid excessive updates
201+
if (
202+
event.type === "observerAdded" ||
203+
event.type === "observerRemoved"
204+
) {
205+
// Clear existing timer
206+
if (this.mutationObserverDebounceTimer) {
207+
clearTimeout(this.mutationObserverDebounceTimer);
208+
}
209+
210+
// Debounce: wait 150ms after last observer change before updating
211+
this.mutationObserverDebounceTimer = setTimeout(() => {
212+
this.sendMutationDataUpdate();
213+
this.mutationObserverDebounceTimer = null;
214+
}, 150);
215+
return;
216+
}
217+
218+
// Send updates immediately for real cache changes: 'added', 'removed', 'updated'
157219
this.sendMutationDataUpdate();
158220
});
159221
} catch (error) {
@@ -277,6 +339,17 @@ class InjectedScript {
277339

278340
// Cleanup subscriptions
279341
private cleanupSubscriptions(): void {
342+
// Clear any pending debounce timers
343+
if (this.queryObserverDebounceTimer) {
344+
clearTimeout(this.queryObserverDebounceTimer);
345+
this.queryObserverDebounceTimer = null;
346+
}
347+
348+
if (this.mutationObserverDebounceTimer) {
349+
clearTimeout(this.mutationObserverDebounceTimer);
350+
this.mutationObserverDebounceTimer = null;
351+
}
352+
280353
if (this.queryUnsubscribe) {
281354
try {
282355
this.queryUnsubscribe();

src/lib/message-router.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
interface MessageHandler<T> {
55
validate: (message: unknown) => message is T;
66
handle: (message: T) => Promise<void> | void;
7-
batchable?: boolean; // Whether this handler supports batching
87
}
98

109
// Error handler interface for message handling errors

0 commit comments

Comments
 (0)