fix(storage-plugin): restore storages after an app reload - #390
Merged
Conversation
The panel iframe is recreated on every reload and asks for storages before the app's React tree has mounted the plugin, so its single discover-storages request is dropped and the panel waits forever. The device now announces itself once its handlers are registered, and the panel re-runs discovery and drops its stale query cache in response.
V3RON
added a commit
that referenced
this pull request
Aug 12, 2026
## Description Fixes the TanStack Query panel staying empty after an app reload until the app is killed and relaunched. - The device now sends a `device-ready` event once its message handlers are registered. - The panel pulls the cache again when it receives that event, clearing what it holds first so it does not render queries from the previous JS context. ## Related Issue Closes #391 ## Context `PluginView` destroys and recreates the panel iframe on every execution-context cycle, so the panel restarts empty on each reload. `BackendExecutionContextCreated` fires as soon as the new JS context exists — before the app's React tree mounts `useTanStackQueryDevTools` — so the recreated panel's single `request-initial-data` message usually arrives while `useHandleInitialData` has not registered its handler yet, and `RozeniteDevToolsClient` drops it. Nothing asks again. `device-ready` is sent from `useHandleInitialData`, which runs last in `useTanStackQueryDevTools`, so a panel reacting to it is guaranteed to find every handler listening. The panel subscribes before it sends its own first request, so the reverse ordering is covered too — whichever side is late, one of the two paths lands. The same race affects `@rozenite/storage-plugin`; it is fixed separately in #390. ## Testing - `pnpm --filter @rozenite/tanstack-query-plugin test` — 20 tests passed, including a new regression guard asserting the panel re-requests and drops its stale cache on `device-ready`. Verified it fails against `main` (`expected [...] to have a length of 2 but got 1`). - `pnpm --filter @rozenite/tanstack-query-plugin typecheck` - `pnpm --filter @rozenite/tanstack-query-plugin lint` - `pnpm format:all` Not verified on a device or emulator — the diagnosis and fix were derived from the panel/runtime lifecycle and reproduced with a message-level harness.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes the Storage panel coming back empty after an app reload, stuck on
Waiting for storages…until the app is killed and relaunched.device-readyevent once its message handlers are registered.Related Issue
Closes #389
Context
PluginViewdestroys and recreates the panel iframe on every execution-context cycle, so the panel restarts with no descriptors on each reload.BackendExecutionContextCreatedfires as soon as the new JS context exists — before the app's React tree mountsuseRozeniteStoragePlugin— so the recreated panel's singlediscover-storagesrequest usually arrives while the device has no listener registered, andRozeniteDevToolsClientdrops it. Discovery usesclient.sendrather thanclient.request, so there is no timeout to surface the loss either, and nothing asks again.This is a regression from #341, which replaced the device's push-on-mount snapshot with panel-initiated discovery. That earlier flow was ordering-independent, and
device-readyrestores that property without giving up the pull-based data path: it is a bare signal, so discovery logic stays in one place and no entries are read speculatively.device-readyis sent after the handler subscriptions are created, so a panel reacting to it is guaranteed to be answered. The panel subscribes before it sends its own first request, so the reverse ordering is covered too — whichever side is late, one of the two paths lands.The same race exists in
@rozenite/tanstack-query-plugin; it is fixed separately in #392.Testing
pnpm --filter @rozenite/storage-plugin test— 194 tests passed, including a new regression guard inpanel.test.tsxthat asserts the panel runs discovery again ondevice-ready. Verified it fails againstmain(expected [...] to have a length of 2 but got 1).pnpm --filter @rozenite/storage-plugin typecheckpnpm --filter @rozenite/storage-plugin lintpnpm format:allNot verified on a device or emulator — the diagnosis and fix were derived from the panel/runtime lifecycle and reproduced with a message-level harness.