Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
1b629b1 to
c19b3d7
Compare
|
@metamaskbot publish-previews |
|
Preview builds have been published. See these instructions for more information about preview builds. Expand for full list of packages and versions. |
86eff63 to
5111712
Compare
|
@metamaskbot publish-previews |
|
Preview builds have been published. See these instructions for more information about preview builds. Expand for full list of packages and versions. |
|
@metamaskbot publish-previews |
1afe924 to
c36e274
Compare
|
@metamaskbot publish-previews |
|
Preview builds have been published. See these instructions for more information about preview builds. Expand for full list of packages and versions. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| return result.pages[0]; | ||
| } | ||
|
|
||
| const { pages } = query.state.data as InfiniteData<TQueryFnData>; |
There was a problem hiding this comment.
Crash when query exists but has no data
Medium Severity
In fetchInfiniteQuery, the guard if (!query || !pageParam) doesn't account for the case where a query exists in the cache but query.state.data is undefined (e.g., after a previous failed fetch). When the query exists and pageParam is truthy, execution falls through to line 173 where const { pages } = query.state.data as InfiniteData<TQueryFnData> destructures undefined, throwing a TypeError. The condition needs an additional check like !query.state.data to fall back to the initial fetch branch.
| */ | ||
| const ALLOWED_INCONSISTENT_DEPENDENCIES = { | ||
| // '@metamask/json-rpc-engine': ['^9.0.3'], | ||
| '@tanstack/query-core': ['^4.43.0'], |
There was a problem hiding this comment.
core-backend will need to downgrade to use BaseDataService or wait until we are able to bump everywhere (blocked by extension not supporting React 18).
| (filters?: InvalidateQueryFilters<Json>, options?: InvalidateOptions) => | ||
| this.invalidateQueries(filters, options), |
There was a problem hiding this comment.
Nit: Does it not work to say:
| (filters?: InvalidateQueryFilters<Json>, options?: InvalidateOptions) => | |
| this.invalidateQueries(filters, options), | |
| this.invalidateQueries.bind(this), |
| export default function greeter(name: string): string { | ||
| return `Hello, ${name}!`; | ||
| } | ||
| export * from './BaseDataService'; |
There was a problem hiding this comment.
What are your thoughts on not using export * and naming each export explicitly? We are considering banning these entirely, see here for more: MetaMask/eslint-config#331
| this.#messenger.publish(`${this.name}:cacheUpdate` as const, payload); | ||
| this.#messenger.publish( | ||
| `${this.name}:cacheUpdate:${hash}` as const, | ||
| payload, |
There was a problem hiding this comment.
Nit: Since we know that we are publishing cacheUpdate for a specific hash, do we need to include the hash in the payload? Maybe the payload for this particular event can just be state.
| payload, | |
| state, |
| }: { | ||
| name: ServiceName; | ||
| messenger: ServiceMessenger; | ||
| clientConfig?: QueryClientConfig; |
There was a problem hiding this comment.
Should we call this queryClientConfig just to be explicit?
| clientConfig?: QueryClientConfig; | |
| queryClientConfig?: QueryClientConfig; |
|
|
||
| protected messenger: ServiceMessenger; | ||
|
|
||
| readonly #client: QueryClient; |
There was a problem hiding this comment.
Should we call this #queryClient just to be explicit?
| readonly #client: QueryClient; | |
| readonly #queryClient: QueryClient; |
| "@metamask/messenger": "^0.3.0", | ||
| "@metamask/utils": "^11.9.0", | ||
| "@tanstack/query-core": "^4.43.0", | ||
| "@tanstack/react-query": "^4.43.0", |
There was a problem hiding this comment.
It looks like react-query has a peer dependency on React 18.x or 19.x: https://github.com/TanStack/query/blob/67cf8b60d923ad158fdf89c80f86decea073f472/packages/react-query/package.json#L84. Do we want to declare this peer dependency here as well?
| // We provide re-exports of the underlying TanStack Query hooks with narrower types, | ||
| // removing `staleTime` and `queryFn` which aren't useful when using data services. | ||
|
|
||
| export function useQuery< |
There was a problem hiding this comment.
Should these hooks go in this package? Should we split this and createUIQueryClient off into their own UI-specific packages?
|
I know that TanStack Query is the motivation for this PR, but before we merge this I want to make sure that we've (at least briefly) considered how this new package would overlap with the other "data service layer" features which are already implemented and which we've discussed adding in the future. Namely:
I guess the theme of these questions is that I want to understand what the intended domain of this package is. Should it be only restricted to TanStack Query integration in the future — meaning that we may create other packages to solve other problems later — or should it be designed to encompass other things that are data-service-related in the future? |
|
I was expecting that we'd continue to use Cockatiel. TanStack does have basic built-in retry functionality, but nothing remotely similar to what our current retry/circuit break policies do. |
|
Related question: Are there any data services where this query-related functionality would not be useful? i.e. where extending this base class would be unwanted. If so, we could rename this to I think the answer here is "no" though. When wouldn't we want request deduplication, and easier-to-use caching options? |


Explanation
This PR implements
BaseDataServiceand a function to wrapQueryClientto proxy requests accordingly.The
BaseDataService, similarly to theBaseControllerprovides the framework for building a service that can be registered and accessed via the messenger system, but also provides guarantees about per-request deduping, retries, caching, invalidation, state-while-revalidate etc via@tanstack/query-core.The
BaseDataServiceprovides two utilities for this:fetchQueryandfetchInfiniteQuery, which is similar but one is separated for special pagination behaviour. Each service has its own cache for the APIs that it exposes that must also be synchronized with the UI processes. To facilitate this synchronization, theBaseDataServicealso automatically provides acacheUpdateevent.The overall goal of the PR is to provide a base layer that can keep as much compatibility as possible with native TanStack Query while also simultaneously allowing us to have one source of truth per data service.
The synchronization is achieved via a special
QueryClientcreated bycreateUIQueryClient, which wraps functionality such as cache invalidation, provides the default proxied fetch behaviour and subscribes to cache updates from data services that it is observing (e.g. has active queries for).References
https://consensyssoftware.atlassian.net/browse/WPC-445
Checklist
Note
Medium Risk
Introduces new caching/synchronization infrastructure around TanStack Query and messenger events; incorrect query key conventions or cache hydration/invalidation behavior could cause stale or inconsistent UI data.
Overview
Implements a new
BaseDataServiceabstraction backed by@tanstack/query-corethat standardizesfetchQuery/fetchInfiniteQuery, exposes aninvalidateQueriesmessenger action, and broadcasts dehydrated per-query cache updates viacacheUpdateevents.Adds
createUIQueryClientto create a UI-sideQueryClientthat proxies query execution through messenger actions, auto-subscribes/unsubscribes to granular cache-update events tohydratelocal cache, and overridesinvalidateQueriesto invalidate both the UI cache and the underlying service.Replaces the previous placeholder
greeterexport with public exports for the new APIs, adds typed wrapper hooks (useQuery,useInfiniteQuery) that disallowqueryFn/staleTime, and includes anExampleDataServiceplus nock-based tests covering basic queries, pagination direction, cache sync, and error cases. Updates package deps/tsconfig references and adds an initial changelog entry.Written by Cursor Bugbot for commit a44a2d4. This will update automatically on new commits. Configure here.