diff --git a/packages/base-data-service/CHANGELOG.md b/packages/base-data-service/CHANGELOG.md index 87aa5230ac5..65c473ee126 100644 --- a/packages/base-data-service/CHANGELOG.md +++ b/packages/base-data-service/CHANGELOG.md @@ -38,6 +38,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING:** Remove `TPageData` type parameter from `invalidateQueries` method ([#9526](https://github.com/MetaMask/core/pull/9526)) - This is technically a breaking change, but this was not used in any of our codebases +- **BREAKING:** Bump `@tanstack/query-core` from `^4.43.0` to `^5.62.16` ([#9712](https://github.com/MetaMask/core/pull/9712)) + - The option types accepted by `fetchQuery`, `fetchInfiniteQuery`, and `invalidateQueries` now follow the query-core v5 API. Subclasses may need to rename `cacheTime` to `gcTime`, and infinite queries no longer accept an explicit page param through the `fetchMore` meta. - Bump `@metamask/utils` from `^11.9.0` to `^11.11.0` ([#9074](https://github.com/MetaMask/core/pull/9074)) - Bump `@metamask/controller-utils` from `^12.1.0` to `^12.3.0` ([#9058](https://github.com/MetaMask/core/pull/9058), [#9083](https://github.com/MetaMask/core/pull/9083), [#9218](https://github.com/MetaMask/core/pull/9218)) - Bump `@metamask/messenger` from `^1.2.0` to `^2.0.0` ([#9392](https://github.com/MetaMask/core/pull/9392)) diff --git a/packages/base-data-service/package.json b/packages/base-data-service/package.json index 32a4ca2d3a7..0ba3f0fb40c 100644 --- a/packages/base-data-service/package.json +++ b/packages/base-data-service/package.json @@ -59,7 +59,7 @@ "@metamask/messenger": "^2.0.0", "@metamask/storage-service": "^1.0.2", "@metamask/utils": "^11.11.0", - "@tanstack/query-core": "^4.43.0", + "@tanstack/query-core": "^5.62.16", "cockatiel": "^3.1.2", "fast-deep-equal": "^3.1.3", "lodash": "^4.17.21" diff --git a/packages/base-data-service/src/BaseDataService.test.ts b/packages/base-data-service/src/BaseDataService.test.ts index df6ed594d36..30d2e430fba 100644 --- a/packages/base-data-service/src/BaseDataService.test.ts +++ b/packages/base-data-service/src/BaseDataService.test.ts @@ -1,5 +1,5 @@ import { MOCK_ANY_NAMESPACE, Messenger } from '@metamask/messenger'; -import { hashQueryKey } from '@tanstack/query-core'; +import { hashKey } from '@tanstack/query-core'; import { BrokenCircuitError } from 'cockatiel'; import { cleanAll } from 'nock'; @@ -121,6 +121,66 @@ describe('BaseDataService', () => { expect(page2.data).not.toStrictEqual(page3.data); }); + it('handles paginated queries without page-param callbacks', async () => { + const messenger = new Messenger({ namespace: serviceName }); + const service = new ExampleDataService(messenger); + + const page1 = await service.getActivityWithoutCallbacks(TEST_ADDRESS); + + expect(page1.data).toHaveLength(3); + + const page2 = await service.getActivityWithoutCallbacks(TEST_ADDRESS, { + page: { after: page1.pageInfo.endCursor }, + }); + + expect(page2.data).toHaveLength(3); + expect(page2.data).not.toStrictEqual(page1.data); + }); + + it('refetches stale paginated queries without page-param callbacks', async () => { + const messenger = new Messenger({ namespace: serviceName }); + const service = new ExampleDataService(messenger); + + const page1 = await service.getActivityWithoutCallbacks(TEST_ADDRESS); + await service.getActivityWithoutCallbacks(TEST_ADDRESS, { + page: { after: page1.pageInfo.endCursor }, + }); + + // The query is stale (zero `staleTime`), so a param-less call rebuilds the + // cached pages. That rebuild walks `getNextPageParam`, which this query + // does not provide, so the base service must supply a no-op to avoid a + // throw. + mockTransactionsPage1(); + const rebuilt = await service.getActivityWithoutCallbacks(TEST_ADDRESS); + + expect(rebuilt.data).toHaveLength(3); + }); + + it('preserves a `null` initial page param', async () => { + const messenger = new Messenger({ namespace: serviceName }); + const service = new ExampleDataService(messenger); + + await service.getActivityWithoutCallbacks(TEST_ADDRESS, { + initialPageParam: null, + }); + + // `null` is a valid page param, so it must reach the query function rather + // than being coerced to `undefined`. + expect(service.pageParamsSeen).toStrictEqual([null]); + }); + + it('does not refetch a fresh paginated query', async () => { + const messenger = new Messenger({ namespace: serviceName }); + const service = new ExampleDataService(messenger); + + // Only one page-1 response is mocked, so a second fetch would fail. A fresh + // cached query must be served without another request. + const page1 = await service.getActivity(TEST_ADDRESS); + const pageAgain = await service.getActivity(TEST_ADDRESS); + + expect(pageAgain.data).toStrictEqual(page1.data); + }); + it('emits `:cacheUpdated` events when cache is updated', async () => { const messenger = new Messenger({ namespace: serviceName }); const service = new ExampleDataService(messenger); @@ -131,7 +191,7 @@ describe('BaseDataService', () => { const queryKey = ['ExampleDataService:getAssets', MOCK_ASSETS]; - const hash = hashQueryKey(queryKey); + const hash = hashKey(queryKey); expect(publishSpy).toHaveBeenNthCalledWith( 6, @@ -186,7 +246,7 @@ describe('BaseDataService', () => { const queryKey = ['ExampleDataService:getAssets', MOCK_ASSETS]; - const hash = hashQueryKey(queryKey); + const hash = hashKey(queryKey); expect(publishSpy).toHaveBeenNthCalledWith( 8, @@ -333,6 +393,7 @@ describe('BaseDataService', () => { state: { queries: [ { + dehydratedAt: expect.any(Number), queryHash: '["ExampleDataService:getAssets",["eip155:1/slip44:60","bip122:000000000019d6689c085ae165831e93/slip44:0","eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f"]]', queryKey: [ diff --git a/packages/base-data-service/src/BaseDataService.ts b/packages/base-data-service/src/BaseDataService.ts index 6c9b1146e19..8088970b0f5 100644 --- a/packages/base-data-service/src/BaseDataService.ts +++ b/packages/base-data-service/src/BaseDataService.ts @@ -15,12 +15,15 @@ import { DehydratedState, FetchInfiniteQueryOptions, FetchQueryOptions, + GetNextPageParamFunction, + GetPreviousPageParamFunction, InfiniteData, InvalidateOptions, InvalidateQueryFilters, OmitKeyof, QueryClient, QueryClientConfig, + SkipToken, WithRequired, dehydrate, hydrate, @@ -37,6 +40,22 @@ import { // Data service queries use the following format: ['ServiceActionName', ...params] export type QueryKey = [string, ...Json[]]; +/** + * The supertype of all messengers, scoped to a namespace. + * + * @template Namespace - The namespace for the messenger's own actions and + * events. + */ +export type BaseMessenger = Messenger< + Namespace, + ActionConstraint, + EventConstraint, + // Use `any` to allow any parent to be set. `any` is harmless in a type constraint anyway, + // it's the one totally safe place to use it. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + any +>; + export type DataServiceGranularCacheUpdatedPayload = | { type: 'added' | 'updated'; state: DehydratedState } | { @@ -53,10 +72,10 @@ type CacheUpdatedType = DataServiceCacheUpdatedPayload['type']; export type DataServiceInvalidateQueriesAction = { type: `${ServiceName}:invalidateQueries`; - handler: ( - filters?: InvalidateQueryFilters, - options?: InvalidateOptions, - ) => Promise; + handler: BaseDataService< + ServiceName, + BaseMessenger + >['invalidateQueries']; }; export type DataServiceActions = @@ -118,15 +137,7 @@ type PersistedCache = { export class BaseDataService< ServiceName extends string, - ServiceMessenger extends Messenger< - ServiceName, - ActionConstraint, - EventConstraint, - // Use `any` to allow any parent to be set. `any` is harmless in a type constraint anyway, - // it's the one totally safe place to use it. - // eslint-disable-next-line @typescript-eslint/no-explicit-any - any - >, + ServiceMessenger extends BaseMessenger, > { public readonly name: ServiceName; @@ -238,8 +249,13 @@ export class BaseDataService< /** * Fetch a query. * - * @param options - The options defining the query. Keep in mind that `queryKey` and `queryFn` are required when using data services. - * Additionally `retry` and `retryDelay` are not available, retries can be customized using the `servicePolicyOptions`. + * @param options - The options defining the query. Note that although this + * method wraps `fetchQuery` from `@tanstack/query-core`, there are a few + * restrictions: + * - `queryKey` and `queryFn` are required + * - `queryFn` must be a function, not a skip token + * - `retry` and `retryDelay` are not available (retries can be customized + * using the constructor's `servicePolicyOptions`). * @returns The query results. */ protected async fetchQuery< @@ -251,10 +267,19 @@ export class BaseDataService< options: WithRequired< OmitKeyof< FetchQueryOptions, - 'retry' | 'retryDelay' + 'retry' | 'retryDelay' | 'queryFn' >, - 'queryKey' | 'queryFn' - >, + 'queryKey' + > & { + // @tanstack/query-core's fetchQuery function accepts a "skip" token, + // but data services always provide a concrete query function. + queryFn: NonNullable< + Exclude< + FetchQueryOptions['queryFn'], + SkipToken + > + >; + }, ): Promise { return this.#queryClient.fetchQuery({ ...options, @@ -266,10 +291,17 @@ export class BaseDataService< /** * Fetch a paginated query. * - * @param options - The options defining the query. Keep in mind that `queryKey` and `queryFn` are required when using data services. - * Additionally `retry` and `retryDelay` are not available, retries can be customized using the `servicePolicyOptions`. + * @param options - The options defining the query. Note that although this + * method wraps `fetchInfiniteQuery` from `@tanstack/query-core`, there are a + * few differences: + * - `queryKey` and `queryFn` are required + * - `queryFn` must be a function, not a skip token + * - `retry` and `retryDelay` are not available (retries can be customized + * using the constructor's `servicePolicyOptions`). * @param pageParam - An optional page parameter. - * @returns The query result, exclusively the requested page is returned. + * @returns A page's worth of data (i.e. what `queryFn` returns). Note that + * this is different from `@tanstack/query-core`'s `fetchInfiniteQuery` + * method, which returns all pages. */ protected async fetchInfiniteQuery< TQueryFnData extends Json, @@ -280,47 +312,117 @@ export class BaseDataService< >( options: WithRequired< OmitKeyof< - FetchInfiniteQueryOptions, - 'retry' | 'retryDelay' + FetchQueryOptions< + TQueryFnData, + TError, + InfiniteData, + TQueryKey, + TPageParam + >, + 'retry' | 'retryDelay' | 'queryFn' | 'initialPageParam' >, - 'queryKey' | 'queryFn' - >, + 'queryKey' + > & { + // @tanstack/query-core's fetchInfiniteQuery function accepts a "skip" + // token, but data services always provide a concrete query function. + queryFn: NonNullable< + Exclude< + FetchInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >['queryFn'], + SkipToken + > + >; + // These are required by @tanstack/query-core for infinite queries but + // remain optional here: consumers may drive pagination purely by passing + // an explicit `pageParam` (see below). + initialPageParam?: TPageParam; + getNextPageParam?: GetNextPageParamFunction; + getPreviousPageParam?: GetPreviousPageParamFunction< + TPageParam, + TQueryFnData + >; + }, pageParam?: TPageParam, ): Promise { const cache = this.#queryClient.getQueryCache(); - const query = cache.find>({ + const query = cache.find< + TQueryFnData, + TError, + InfiniteData + >({ queryKey: options.queryKey, }); if (!query?.state.data || pageParam === undefined) { - const result = await this.#queryClient.fetchInfiniteQuery({ + // @tanstack/query-core requires an `initialPageParam`, which becomes the + // param of the first (and only) page this fetches. Prefer an explicit + // per-call `pageParam` (a cold jump to a specific page); otherwise use the + // consumer's `initialPageParam`. Branching on a strict `undefined` check + // (rather than `??`) preserves `null`, which is a valid `Json` page param + // and query-core's usual first-page sentinel. The value can legitimately + // be `undefined` here (the very first page), which query-core accepts at + // runtime but not in its `TPageParam` type, hence the cast. + let initialPageParam: TPageParam; + if (pageParam === undefined) { + initialPageParam = options.initialPageParam as TPageParam; + } else { + initialPageParam = pageParam; + } + + const result = await this.#queryClient.fetchInfiniteQuery< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >({ ...options, + initialPageParam, + // Provide a no-op `getNextPageParam` when the consumer omits one. + // @tanstack/query-core walks `getNextPageParam` when it refetches a + // multi-page infinite query, so a missing resolver would throw once more + // than one page has been cached. + getNextPageParam: options.getNextPageParam ?? ((): null => null), queryFn: (context) => - this.#policy.execute(() => - options.queryFn({ - ...context, - pageParam: context.pageParam ?? pageParam, - }), - ), + this.#policy.execute(() => options.queryFn(context)), }); return result.pages[0]; } - const { pages } = query.state.data; - const previous = options.getPreviousPageParam?.(pages[0], pages); + const { pages, pageParams } = query.state.data; + const previous = options.getPreviousPageParam?.( + pages[0], + pages, + pageParams[0], + pageParams, + ); const direction = deepEqual(pageParam, previous) ? 'backward' : 'forward'; - const result = await query.fetch(undefined, { - meta: { - fetchMore: { - direction, - pageParam, + // Override the next/previous param callbacks to return exactly the + // requested page, so pagination works even when the consumer did not + // provide page-param callbacks. + const result = await query.fetch( + { + ...query.options, + getNextPageParam: () => pageParam, + getPreviousPageParam: () => pageParam, + } as typeof query.options, + { + meta: { + fetchMore: { + direction, + }, }, }, - }); + ); const pageIndex = result.pageParams.findIndex((param) => deepEqual(param, pageParam), @@ -337,7 +439,7 @@ export class BaseDataService< * @returns Nothing. */ async invalidateQueries( - filters?: InvalidateQueryFilters, + filters?: InvalidateQueryFilters, options?: InvalidateOptions, ): Promise { return this.#queryClient.invalidateQueries(filters, options); diff --git a/packages/base-data-service/tests/ExampleDataService.ts b/packages/base-data-service/tests/ExampleDataService.ts index d84e9edf5d1..b2cd0c8ce46 100644 --- a/packages/base-data-service/tests/ExampleDataService.ts +++ b/packages/base-data-service/tests/ExampleDataService.ts @@ -45,11 +45,10 @@ export type GetActivityResponse = { }; }; -export type PageParam = - | { - before: string; - } - | { after: string }; +export type PageParam = { + before?: string; + after?: string; +}; const MESSENGER_EXPOSED_METHODS = ['getAssets', 'getActivity'] as const; @@ -61,6 +60,10 @@ export class ExampleDataService extends BaseDataService< readonly #tokensBaseUrl = 'https://tokens.api.cx.metamask.io'; + // Records the page params that `getActivityWithoutCallbacks`'s query function + // is invoked with, so tests can assert what actually reached it. + readonly pageParamsSeen: (PageParam | null | undefined)[] = []; + constructor( messenger: ExampleMessenger, { persistenceConfig }: { persistenceConfig?: PersistenceConfiguration } = { @@ -101,7 +104,7 @@ export class ExampleDataService extends BaseDataService< return response.json(); }, staleTime: inMilliseconds(1, Duration.Day), - cacheTime: inMilliseconds(1, Duration.Day), + gcTime: inMilliseconds(1, Duration.Day), }); } @@ -109,7 +112,13 @@ export class ExampleDataService extends BaseDataService< address: string, page?: PageParam, ): Promise { - return this.fetchInfiniteQuery( + return this.fetchInfiniteQuery< + GetActivityResponse, + unknown, + GetActivityResponse, + [string, string], + PageParam + >( { queryKey: [`${this.name}:getActivity`, address], queryFn: async ({ pageParam }) => { @@ -146,6 +155,66 @@ export class ExampleDataService extends BaseDataService< ); } + /** + * Fetch activity without providing page-param callbacks, driving pagination + * purely by the explicit page param passed to the base method (the way a + * consumer that paginates by cursor does). Uses a zero `staleTime` so + * refetches can be exercised, and records every page param the query function + * receives in `pageParamsSeen`. + * + * @param address - The account address. + * @param options - Optional page param and initial page param. + * @param options.page - The page to fetch. + * @param options.initialPageParam - The initial page param to configure. + * @returns A page of activity. + */ + async getActivityWithoutCallbacks( + address: string, + { + page, + initialPageParam, + }: { page?: PageParam; initialPageParam?: PageParam | null } = {}, + ): Promise { + return this.fetchInfiniteQuery< + GetActivityResponse, + unknown, + GetActivityResponse, + [string, string], + PageParam | null + >( + { + queryKey: [`${this.name}:getActivityWithoutCallbacks`, address], + queryFn: async ({ pageParam }) => { + this.pageParamsSeen.push(pageParam); + + const caipAddress = `eip155:0:${address.toLowerCase()}`; + const url = new URL( + `${this.#accountsBaseUrl}/v4/multiaccount/transactions?limit=3&accountAddresses=${caipAddress}`, + ); + + if (pageParam?.after) { + url.searchParams.set('after', pageParam.after); + } else if (pageParam?.before) { + url.searchParams.set('before', pageParam.before); + } + + const response = await fetch(url); + + if (!response.ok) { + throw new Error( + `Query failed with status code: ${response.status}.`, + ); + } + + return response.json(); + }, + initialPageParam, + staleTime: 0, + }, + page, + ); + } + destroy(): void { super.destroy(); } diff --git a/packages/chomp-api-service/CHANGELOG.md b/packages/chomp-api-service/CHANGELOG.md index a9963833e93..2d7c4beb595 100644 --- a/packages/chomp-api-service/CHANGELOG.md +++ b/packages/chomp-api-service/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Bump `@metamask/superstruct` from `^3.1.0` to `^3.4.1` ([#9754](https://github.com/MetaMask/core/pull/9754)) +- Bump `@tanstack/query-core` from `^4.43.0` to `^5.62.16` ([#9712](https://github.com/MetaMask/core/pull/9712)) ## [4.0.0] diff --git a/packages/chomp-api-service/package.json b/packages/chomp-api-service/package.json index 0cb9090ef96..e016265262c 100644 --- a/packages/chomp-api-service/package.json +++ b/packages/chomp-api-service/package.json @@ -58,7 +58,7 @@ "@metamask/messenger": "^2.0.0", "@metamask/superstruct": "^3.4.1", "@metamask/utils": "^11.11.0", - "@tanstack/query-core": "^4.43.0" + "@tanstack/query-core": "^5.62.16" }, "devDependencies": { "@metamask/auto-changelog": "^6.1.0", diff --git a/packages/chomp-api-service/src/chomp-api-service.ts b/packages/chomp-api-service/src/chomp-api-service.ts index a8d400c1bc5..d3348fd7a6a 100644 --- a/packages/chomp-api-service/src/chomp-api-service.ts +++ b/packages/chomp-api-service/src/chomp-api-service.ts @@ -404,7 +404,7 @@ export class ChompApiService extends BaseDataService< * The result is scoped to the authenticated profile and consumers use it * to decide whether an association already exists, so it is always fetched * fresh (`staleTime: 0`) and evicted as soon as the call settles - * (`cacheTime: 0`). The query key carries a SHA-256 digest of the bearer + * (`gcTime: 0`). The query key carries a SHA-256 digest of the bearer * token — the same token the request is made with — so concurrent calls * only share an in-flight request when they are for the same profile. The * digest, not the token, is used because query keys leave the service via @@ -422,7 +422,7 @@ export class ChompApiService extends BaseDataService< const jsonResponse = await this.fetchQuery({ queryKey: [`${this.name}:getAssociatedAddresses`, profileKey], staleTime: 0, - cacheTime: 0, + gcTime: 0, queryFn: async () => { const response = await fetch( new URL('/v1/auth/address', this.#baseUrl), diff --git a/packages/claims-controller/CHANGELOG.md b/packages/claims-controller/CHANGELOG.md index 4dcaa111828..7b08a719c62 100644 --- a/packages/claims-controller/CHANGELOG.md +++ b/packages/claims-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Bump `@tanstack/query-core` from `^4.43.0` to `^5.62.16` ([#9712](https://github.com/MetaMask/core/pull/9712)) + ## [0.6.0] ### Added diff --git a/packages/claims-controller/package.json b/packages/claims-controller/package.json index 0483871a68f..3edf7d6f06c 100644 --- a/packages/claims-controller/package.json +++ b/packages/claims-controller/package.json @@ -63,7 +63,7 @@ "@metamask/profile-sync-controller": "^29.0.0", "@metamask/superstruct": "^3.4.1", "@metamask/utils": "^11.11.0", - "@tanstack/query-core": "^4.43.0" + "@tanstack/query-core": "^5.62.16" }, "devDependencies": { "@metamask/auto-changelog": "^6.1.0", diff --git a/packages/money-account-api-data-service/CHANGELOG.md b/packages/money-account-api-data-service/CHANGELOG.md index fb9c3adf72c..89370758228 100644 --- a/packages/money-account-api-data-service/CHANGELOG.md +++ b/packages/money-account-api-data-service/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Bump `@metamask/superstruct` from `^3.1.0` to `^3.4.1` ([#9754](https://github.com/MetaMask/core/pull/9754)) +- Bump `@tanstack/query-core` from `^4.43.0` to `^5.62.16` ([#9712](https://github.com/MetaMask/core/pull/9712)) ## [0.4.0] diff --git a/packages/money-account-api-data-service/package.json b/packages/money-account-api-data-service/package.json index 7c2503356a0..497d3c30d18 100644 --- a/packages/money-account-api-data-service/package.json +++ b/packages/money-account-api-data-service/package.json @@ -60,7 +60,7 @@ "@metamask/messenger": "^2.0.0", "@metamask/superstruct": "^3.4.1", "@metamask/utils": "^11.11.0", - "@tanstack/query-core": "^4.43.0" + "@tanstack/query-core": "^5.62.16" }, "devDependencies": { "@metamask/auto-changelog": "^6.1.0", diff --git a/packages/sample-controllers/CHANGELOG.md b/packages/sample-controllers/CHANGELOG.md index 5b00cb05fe7..d70e1da8ad9 100644 --- a/packages/sample-controllers/CHANGELOG.md +++ b/packages/sample-controllers/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Bump `@tanstack/query-core` from `^4.43.0` to `^5.62.16` ([#9712](https://github.com/MetaMask/core/pull/9712)) - Bump `@metamask/network-controller` from `^35.0.0` to `^35.0.1` ([#9758](https://github.com/MetaMask/core/pull/9758)) - Bump `@metamask/superstruct` from `^3.1.0` to `^3.4.1` ([#9754](https://github.com/MetaMask/core/pull/9754)) diff --git a/packages/sample-controllers/package.json b/packages/sample-controllers/package.json index d9dcd1e9c04..7648669388f 100644 --- a/packages/sample-controllers/package.json +++ b/packages/sample-controllers/package.json @@ -61,7 +61,7 @@ "@metamask/network-controller": "^35.0.1", "@metamask/superstruct": "^3.4.1", "@metamask/utils": "^11.11.0", - "@tanstack/query-core": "^4.43.0" + "@tanstack/query-core": "^5.62.16" }, "devDependencies": { "@metamask/auto-changelog": "^6.1.0", diff --git a/packages/sentinel-api-service/CHANGELOG.md b/packages/sentinel-api-service/CHANGELOG.md index 3f97d644ea5..eb9c3907266 100644 --- a/packages/sentinel-api-service/CHANGELOG.md +++ b/packages/sentinel-api-service/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Bump `@metamask/superstruct` from `^3.1.0` to `^3.4.1` ([#9754](https://github.com/MetaMask/core/pull/9754)) +- Bump `@tanstack/query-core` from `^4.43.0` to `^5.62.16` ([#9712](https://github.com/MetaMask/core/pull/9712)) ## [1.0.0] diff --git a/packages/sentinel-api-service/package.json b/packages/sentinel-api-service/package.json index f1e43ce619e..e4abdc24c33 100644 --- a/packages/sentinel-api-service/package.json +++ b/packages/sentinel-api-service/package.json @@ -60,7 +60,7 @@ "@metamask/messenger": "^2.0.0", "@metamask/superstruct": "^3.4.1", "@metamask/utils": "^11.11.0", - "@tanstack/query-core": "^4.43.0" + "@tanstack/query-core": "^5.62.16" }, "devDependencies": { "@metamask/auto-changelog": "^6.1.0", diff --git a/packages/shield-controller/CHANGELOG.md b/packages/shield-controller/CHANGELOG.md index 3267a97b25c..ae9125873bc 100644 --- a/packages/shield-controller/CHANGELOG.md +++ b/packages/shield-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Bump `@tanstack/query-core` from `^4.43.0` to `^5.62.16` ([#9712](https://github.com/MetaMask/core/pull/9712)) + ## [6.0.0] ### Added diff --git a/packages/shield-controller/package.json b/packages/shield-controller/package.json index 99ca21a149d..30b7f7335e3 100644 --- a/packages/shield-controller/package.json +++ b/packages/shield-controller/package.json @@ -63,7 +63,7 @@ "@metamask/signature-controller": "^39.2.9", "@metamask/transaction-controller": "^69.5.1", "@metamask/utils": "^11.11.0", - "@tanstack/query-core": "^4.43.0", + "@tanstack/query-core": "^5.62.16", "cockatiel": "^3.1.2" }, "devDependencies": { diff --git a/packages/shield-controller/src/shield-api-service.test.ts b/packages/shield-controller/src/shield-api-service.test.ts index cc3a309c150..4192d8619db 100644 --- a/packages/shield-controller/src/shield-api-service.test.ts +++ b/packages/shield-controller/src/shield-api-service.test.ts @@ -267,20 +267,19 @@ describe('ShieldApiService', () => { const txMeta = generateMockTxMeta(); - let callCount = 0; const startTime = 1000; const expectedLatency = pollInterval + 50; - const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => { - callCount += 1; - // `fetchQuery` during init may call `Date.now()` before polling latency is measured. - if (callCount <= 1) { - return startTime; - } - if (callCount === 2) { - return startTime; - } - return startTime + expectedLatency; - }); + // Advance the clock only once polling has completed (the third fetch, which + // returns the coverage result). Keying off fetch progress rather than the + // number of `Date.now()` calls keeps this robust to how many times + // query-core reads the clock internally. + const nowSpy = jest + .spyOn(Date, 'now') + .mockImplementation(() => + fetchMock.mock.calls.length >= 3 + ? startTime + expectedLatency + : startTime, + ); const coverageResult = await service.checkCoverage({ txMeta }); diff --git a/packages/shield-controller/src/shield-api-service.ts b/packages/shield-controller/src/shield-api-service.ts index 4820283d75a..420fb9e3e82 100644 --- a/packages/shield-controller/src/shield-api-service.ts +++ b/packages/shield-controller/src/shield-api-service.ts @@ -324,7 +324,7 @@ export class ShieldApiService extends BaseDataService< req.status, ], staleTime: 0, - cacheTime: 0, + gcTime: 0, queryFn: async () => { const res = await this.#fetch( `${this.#baseUrl}/v1/signature/coverage/log`, @@ -380,7 +380,7 @@ export class ShieldApiService extends BaseDataService< req.status, ], staleTime: 0, - cacheTime: 0, + gcTime: 0, queryFn: async () => { const res = await this.#fetch( `${this.#baseUrl}/v1/transaction/coverage/log`, @@ -420,7 +420,7 @@ export class ShieldApiService extends BaseDataService< return await this.fetchQuery({ queryKey: [`${this.name}:initCoverageCheck`, path, requestId], staleTime: 0, - cacheTime: 0, + gcTime: 0, queryFn: async () => { const res = await this.#fetch(`${this.#baseUrl}/${path}`, { method: 'POST', diff --git a/packages/subscription-controller/CHANGELOG.md b/packages/subscription-controller/CHANGELOG.md index 6001a894c1a..5c340d04713 100644 --- a/packages/subscription-controller/CHANGELOG.md +++ b/packages/subscription-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Bump `@tanstack/query-core` from `^4.43.0` to `^5.62.16` ([#9712](https://github.com/MetaMask/core/pull/9712)) + ## [7.0.0] ### Changed diff --git a/packages/subscription-controller/package.json b/packages/subscription-controller/package.json index cc1c02a0968..c4404fb032d 100644 --- a/packages/subscription-controller/package.json +++ b/packages/subscription-controller/package.json @@ -64,7 +64,7 @@ "@metamask/superstruct": "^3.4.1", "@metamask/transaction-controller": "^69.5.1", "@metamask/utils": "^11.11.0", - "@tanstack/query-core": "^4.43.0", + "@tanstack/query-core": "^5.62.16", "bignumber.js": "^9.1.2" }, "devDependencies": { diff --git a/packages/subscription-controller/src/SubscriptionService.ts b/packages/subscription-controller/src/SubscriptionService.ts index 4a73388e68c..8bfb50e47c2 100644 --- a/packages/subscription-controller/src/SubscriptionService.ts +++ b/packages/subscription-controller/src/SubscriptionService.ts @@ -569,7 +569,7 @@ export class SubscriptionService extends BaseDataService< requestParams as Json, ], staleTime: 0, - cacheTime: 0, + gcTime: 0, queryFn: async () => { const response = await this.#fetch(url.toString(), { method, diff --git a/packages/wallet-framework-docs/package.json b/packages/wallet-framework-docs/package.json index d0c1cddab4c..de22720ca99 100644 --- a/packages/wallet-framework-docs/package.json +++ b/packages/wallet-framework-docs/package.json @@ -42,7 +42,7 @@ "@metamask/messenger": "^2.0.0", "@metamask/superstruct": "^3.4.1", "@metamask/utils": "^11.11.0", - "@tanstack/query-core": "^4.43.0" + "@tanstack/query-core": "^5.62.16" }, "devDependencies": { "@docusaurus/core": "^3.10.1", diff --git a/yarn.config.cjs b/yarn.config.cjs index 1e1dc0fbf90..2b55c2fd360 100644 --- a/yarn.config.cjs +++ b/yarn.config.cjs @@ -23,9 +23,7 @@ const { inspect } = require('util'); * Only intended as temporary measures to faciliate upgrades and releases. * This should trend towards empty. */ -const ALLOWED_INCONSISTENT_DEPENDENCIES = { - '@tanstack/query-core': ['^4.43.0'], -}; +const ALLOWED_INCONSISTENT_DEPENDENCIES = {}; /** * These packages are allowed as peer dependencies without requiring installation as diff --git a/yarn.lock b/yarn.lock index 8d50be5f5f7..e0c567acc42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6213,7 +6213,7 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/storage-service": "npm:^1.0.2" "@metamask/utils": "npm:^11.11.0" - "@tanstack/query-core": "npm:^4.43.0" + "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" "@types/jest": "npm:^30.0.0" "@types/lodash": "npm:^4.14.191" @@ -6396,7 +6396,7 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/superstruct": "npm:^3.4.1" "@metamask/utils": "npm:^11.11.0" - "@tanstack/query-core": "npm:^4.43.0" + "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" @@ -6423,7 +6423,7 @@ __metadata: "@metamask/profile-sync-controller": "npm:^29.0.0" "@metamask/superstruct": "npm:^3.4.1" "@metamask/utils": "npm:^11.11.0" - "@tanstack/query-core": "npm:^4.43.0" + "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" @@ -7846,7 +7846,7 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/superstruct": "npm:^3.4.1" "@metamask/utils": "npm:^11.11.0" - "@tanstack/query-core": "npm:^4.43.0" + "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" @@ -8792,7 +8792,7 @@ __metadata: "@metamask/network-controller": "npm:^35.0.1" "@metamask/superstruct": "npm:^3.4.1" "@metamask/utils": "npm:^11.11.0" - "@tanstack/query-core": "npm:^4.43.0" + "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" @@ -8888,7 +8888,7 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/superstruct": "npm:^3.4.1" "@metamask/utils": "npm:^11.11.0" - "@tanstack/query-core": "npm:^4.43.0" + "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" "@types/jest": "npm:^30.0.0" deepmerge: "npm:^4.2.2" @@ -8918,7 +8918,7 @@ __metadata: "@metamask/signature-controller": "npm:^39.2.9" "@metamask/transaction-controller": "npm:^69.5.1" "@metamask/utils": "npm:^11.11.0" - "@tanstack/query-core": "npm:^4.43.0" + "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" "@types/jest": "npm:^30.0.0" cockatiel: "npm:^3.1.2" @@ -9264,7 +9264,7 @@ __metadata: "@metamask/superstruct": "npm:^3.4.1" "@metamask/transaction-controller": "npm:^69.5.1" "@metamask/utils": "npm:^11.11.0" - "@tanstack/query-core": "npm:^4.43.0" + "@tanstack/query-core": "npm:^5.62.16" "@ts-bridge/cli": "npm:^0.6.4" "@types/jest": "npm:^30.0.0" bignumber.js: "npm:^9.1.2" @@ -9532,7 +9532,7 @@ __metadata: "@metamask/messenger": "npm:^2.0.0" "@metamask/superstruct": "npm:^3.4.1" "@metamask/utils": "npm:^11.11.0" - "@tanstack/query-core": "npm:^4.43.0" + "@tanstack/query-core": "npm:^5.62.16" "@types/jest": "npm:^30.0.0" "@types/react": "npm:^19.0.0" deepmerge: "npm:^4.2.2" @@ -11529,13 +11529,6 @@ __metadata: languageName: node linkType: hard -"@tanstack/query-core@npm:^4.43.0": - version: 4.43.0 - resolution: "@tanstack/query-core@npm:4.43.0" - checksum: 10/c2a5a151c7adaea8311e01a643255f31946ae3164a71567ba80048242821ae14043f13f5516b695baebe5ea7e4b2cf717fd60908a929d18a5c5125fee925ff67 - languageName: node - linkType: hard - "@tanstack/react-query@npm:^5.62.16": version: 5.101.2 resolution: "@tanstack/react-query@npm:5.101.2"