@metamask-previews/react-data-query 0.2.2-preview-4328c6196 → 0.2.2-preview-318d52ea7

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/CHANGELOG.md CHANGED
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [0.2.2]
10
+ ### Added
11
+
12
+ - Export `MessengerAdapter` type ([#9475](https://github.com/MetaMask/core/pull/9475))
11
13
 
12
14
  ### Changed
13
15
 
@@ -18,6 +20,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
18
20
  - If you're passing a messenger adapter directly (it is not defined in its own variable), then it should also "just work".
19
21
  - The types for `createUIQueryClient` no longer check that the provided messenger's actions are JSON-compatible ([#9475](https://github.com/MetaMask/core/pull/9475))
20
22
  - If you are experiencing any errors calling actions through the query client, check to make sure their parameters are JSON-compatible.
23
+
24
+ ## [0.2.2]
25
+
26
+ ### Changed
27
+
21
28
  - Bump `@metamask/utils` from `^11.9.0` to `^11.11.0` ([#9074](https://github.com/MetaMask/core/pull/9074))
22
29
  - Make `react-dom` and `react-native` peer dependencies optional ([#9295](https://github.com/MetaMask/core/pull/9295))
23
30
 
@@ -1 +1 @@
1
- {"version":3,"file":"createUIQueryClient.cjs","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DataServiceActions,\n DataServiceGranularCacheUpdatedEvent,\n DataServiceGranularCacheUpdatedPayload,\n} from '@metamask/base-data-service';\nimport type {\n ActionConstraint,\n EventConstraint,\n Messenger,\n} from '@metamask/messenger';\nimport { assert } from '@metamask/utils';\nimport {\n hydrate,\n QueryClient,\n InvalidateQueryFilters,\n InvalidateOptions,\n OmitKeyof,\n parseFilterArgs,\n QueryKey,\n QueryClientConfig,\n} from '@tanstack/query-core';\n\n/**\n * Handles granular cache update events emitted by data services.\n */\ntype DataServiceGranularCacheUpdatedHandler = (\n payload: DataServiceGranularCacheUpdatedPayload,\n) => void;\n\n/**\n * A narrower subset of the `Messenger` type, tailored to the messenger\n * that `createUIQueryClient` interacts with.\n */\ntype MessengerAdapter<DataServiceName extends string> = {\n /**\n * Call an action on one of the configured data services.\n *\n * Note: The parameters are typed as `unknown[]` rather than `Json[]`. For\n * concrete messengers, each action's parameters exist as fixed-length tuple,\n * and a variadic `Json[]` is not assignable to a fixed-length tuple, so using\n * `Json[]` here would reject otherwise valid messengers.\n */\n call(\n actionType: `${DataServiceName}:${string}`,\n ...params: unknown[]\n ): unknown;\n\n /**\n * Subscribe to a granular cache update event on one of the configured data\n * services.\n */\n subscribe(\n eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'],\n handler: DataServiceGranularCacheUpdatedHandler,\n ): void;\n\n /**\n * Unsubscribe from a granular cache update event on one of the configured\n * data services.\n */\n unsubscribe(\n eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'],\n handler: DataServiceGranularCacheUpdatedHandler,\n ): void;\n};\n\n/**\n * Constraints a messenger such that it must minimally support a subset of\n * capabilities that the given data services provide. Specifically, it must at\n * least allow all data service actions to be called, and it must at least allow\n * the `:cacheUpdated:${hash}` event to be subscribed to.\n */\n// Notes on the implementation:\n//\n// - The messenger type parameter is deliberately *not* constrained (e.g. to\n// `Messenger<string, ActionConstraint, EventConstraint, any>`); instead we\n// `infer` the action and event unions directly. This differs from\n// `BaseController`, which does constrain its `ControllerMessenger` type\n// parameter to `Messenger<...>`. The reason for the difference is what each\n// receives:\n// - `BaseController` is always given an instance of Messenger, and expects an\n// instance of Messenger in its type parameters, so it can make a direct\n// comparison.\n// - `createUIQueryClient`, on the other hand, is given a root messenger, which\n// in Mobile is a *subclass* of `Messenger` (called\n// `ExtendedRootMessenger`). A subclass is a different class, so TypeScript\n// falls back to a structural comparison that includes `Messenger`'s private\n// fields (and because some private fields are functions or contain\n// functions, it attempts to compare action handlers or event subscriptions (e.g.\n// `#subscriptionDelegationTargets`), and fails due to contravariance.\n// Inferring the unions instead of constraining the type sidesteps this\n// private-field check.\ntype SupportsDataServices<MessengerInstance, DataServiceName extends string> =\n MessengerInstance extends Messenger<\n string,\n infer Action extends ActionConstraint,\n infer Event extends EventConstraint,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n >\n ? DataServiceActions<DataServiceName>['type'] extends Action['type']\n ? DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'] extends Event['type']\n ? MessengerInstance\n : never\n : never\n : never;\n\n/**\n * Create a QueryClient that queries and subscribes to data services using a\n * messenger adapter. This is a messenger-like object that carries some\n * constraints:\n *\n * 1. The messenger must support the `call`, `subscribe` and\n * `unsubscribe` methods.\n * 2. All action handler arguments and event payloads must be JSON-compatible.\n * 3. The messenger must minimally support capabilities that belong to the\n * designated data services and must minimally support the\n * `:cacheUpdated:${hash}` event of the the designated data services.\n *\n * @param dataServices - A list of data services.\n * @param messenger - A messenger adapter.\n * @param config - Optional query client configuration options.\n * @returns The QueryClient.\n */\nexport function createUIQueryClient<DataServiceNames extends readonly string[]>(\n dataServices: DataServiceNames,\n messenger: MessengerAdapter<DataServiceNames[number]>,\n config?: QueryClientConfig,\n): QueryClient;\n\n/**\n * Create a QueryClient that queries and subscribes to data services using a\n * messenger. This messenger carries some constraints:\n *\n * 1. All action handler arguments and event payloads must be JSON-compatible.\n * 2. The messenger must minimally support capabilities that belong to the\n * designated data services and must minimally support the\n * `:cacheUpdated:${hash}` event of the the designated data services.\n *\n * @param dataServices - A list of data services.\n * @param messenger - A messenger.\n * @param config - Optional query client configuration options.\n * @returns The QueryClient.\n */\nexport function createUIQueryClient<\n DataServiceNames extends readonly string[],\n MessengerInstance,\n>(\n dataServices: DataServiceNames,\n messenger: SupportsDataServices<MessengerInstance, DataServiceNames[number]>,\n config?: QueryClientConfig,\n): QueryClient;\n\nexport function createUIQueryClient<DataServiceNames extends readonly string[]>(\n dataServices: DataServiceNames,\n messenger: MessengerAdapter<DataServiceNames[number]>,\n config: QueryClientConfig = {},\n): QueryClient {\n const subscriptions = new Map<\n string,\n DataServiceGranularCacheUpdatedHandler\n >();\n\n /**\n * Check whether a name is one of the provided data service names.\n *\n * @param service - The service name to check.\n * @returns Whether the service name is configured.\n */\n function isRecognizedDataService(\n service: string,\n ): service is DataServiceNames[number] {\n return dataServices.some((dataService) => dataService === service);\n }\n\n /**\n * Check whether an action belongs to one of the provided data services.\n *\n * @param action - The action name to check.\n * @returns Whether the action belongs to a configured data service.\n */\n function isRecognizedDataServiceAction(\n action: string,\n ): action is `${DataServiceNames[number]}:${string}` {\n return isRecognizedDataService(action.split(':')[0]);\n }\n\n /**\n * Parse a query key to detect a service name.\n *\n * @param queryKey - The query key.\n * @returns The service name if it parsing succeeded, otherwise null.\n */\n function parseQueryKey(queryKey: QueryKey): DataServiceNames[number] | null {\n const action = queryKey[0];\n\n if (typeof action !== 'string') {\n return null;\n }\n\n const service = action.split(':')[0];\n\n if (!isRecognizedDataService(service)) {\n return null;\n }\n\n return service;\n }\n\n const client: QueryClient = new QueryClient({\n ...config,\n defaultOptions: {\n queries: {\n ...config.defaultOptions?.queries,\n queryFn: async (options): Promise<unknown> => {\n const { queryKey } = options;\n\n const action = queryKey[0];\n\n assert(\n typeof action === 'string' && isRecognizedDataServiceAction(action),\n \"Queries must call actions on the messenger provided to createUIQueryClient, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.\",\n );\n\n const params = options.queryKey.slice(1);\n if (options.pageParam !== undefined) {\n params.push(options.pageParam);\n }\n\n return await messenger.call(action, ...params);\n },\n },\n mutations: config.defaultOptions?.mutations,\n },\n });\n\n const cache = client.getQueryCache();\n\n cache.subscribe((event) => {\n const { query } = event;\n\n const hash = query.queryHash;\n const hasSubscription = subscriptions.has(hash);\n const observerCount = query.getObserversCount();\n\n const service = parseQueryKey(query.queryKey);\n\n if (!service) {\n return;\n }\n\n if (\n !hasSubscription &&\n event.type === 'observerAdded' &&\n observerCount === 1\n ) {\n const cacheListener: DataServiceGranularCacheUpdatedHandler = (\n payload,\n ) => {\n if (payload.type === 'removed') {\n return;\n }\n\n hydrate(client, payload.state);\n };\n\n subscriptions.set(hash, cacheListener);\n messenger.subscribe(`${service}:cacheUpdated:${hash}`, cacheListener);\n } else if (\n event.type === 'observerRemoved' &&\n observerCount === 0 &&\n hasSubscription\n ) {\n const subscriptionListener = subscriptions.get(hash);\n\n if (subscriptionListener) {\n messenger.unsubscribe(\n `${service}:cacheUpdated:${hash}`,\n subscriptionListener,\n );\n }\n subscriptions.delete(hash);\n }\n });\n\n // Override invalidateQueries to ensure the data service is invalidated as well.\n const originalInvalidate = client.invalidateQueries.bind(client);\n\n // This function is defined in this way to have full support for all function overloads.\n client.invalidateQueries = async (\n arg1?: QueryKey | InvalidateQueryFilters,\n arg2?: OmitKeyof<InvalidateQueryFilters, 'queryKey'> | InvalidateOptions,\n arg3?: InvalidateOptions,\n ): Promise<void> => {\n const [filters, options] = parseFilterArgs(arg1, arg2, arg3);\n\n const queries = client.getQueryCache().findAll(filters);\n\n const services = [\n ...new Set(queries.map((query) => parseQueryKey(query.queryKey))),\n ];\n\n await Promise.all(\n services.map(async (service) => {\n if (!service) {\n return null;\n }\n\n return messenger.call(`${service}:invalidateQueries`, filters, options);\n }),\n );\n\n return originalInvalidate(filters, options);\n };\n\n return client;\n}\n"]}
1
+ {"version":3,"file":"createUIQueryClient.cjs","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DataServiceActions,\n DataServiceGranularCacheUpdatedEvent,\n DataServiceGranularCacheUpdatedPayload,\n} from '@metamask/base-data-service';\nimport type {\n ActionConstraint,\n EventConstraint,\n Messenger,\n} from '@metamask/messenger';\nimport { assert } from '@metamask/utils';\nimport {\n hydrate,\n QueryClient,\n InvalidateQueryFilters,\n InvalidateOptions,\n OmitKeyof,\n parseFilterArgs,\n QueryKey,\n QueryClientConfig,\n} from '@tanstack/query-core';\n\n/**\n * Handles granular cache update events emitted by data services.\n */\ntype DataServiceGranularCacheUpdatedHandler = (\n payload: DataServiceGranularCacheUpdatedPayload,\n) => void;\n\n/**\n * A narrower subset of the `Messenger` type, tailored to the messenger\n * that `createUIQueryClient` interacts with.\n */\nexport type MessengerAdapter<DataServiceName extends string> = {\n /**\n * Call an action on one of the configured data services.\n *\n * Note: The parameters are typed as `unknown[]` rather than `Json[]`. For\n * concrete messengers, each action's parameters exist as fixed-length tuple,\n * and a variadic `Json[]` is not assignable to a fixed-length tuple, so using\n * `Json[]` here would reject otherwise valid messengers.\n */\n call(\n actionType: `${DataServiceName}:${string}`,\n ...params: unknown[]\n ): unknown;\n\n /**\n * Subscribe to a granular cache update event on one of the configured data\n * services.\n */\n subscribe(\n eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'],\n handler: DataServiceGranularCacheUpdatedHandler,\n ): void;\n\n /**\n * Unsubscribe from a granular cache update event on one of the configured\n * data services.\n */\n unsubscribe(\n eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'],\n handler: DataServiceGranularCacheUpdatedHandler,\n ): void;\n};\n\n/**\n * Constraints a messenger such that it must minimally support a subset of\n * capabilities that the given data services provide. Specifically, it must at\n * least allow all data service actions to be called, and it must at least allow\n * the `:cacheUpdated:${hash}` event to be subscribed to.\n */\n// Notes on the implementation:\n//\n// - The messenger type parameter is deliberately *not* constrained (e.g. to\n// `Messenger<string, ActionConstraint, EventConstraint, any>`); instead we\n// `infer` the action and event unions directly. This differs from\n// `BaseController`, which does constrain its `ControllerMessenger` type\n// parameter to `Messenger<...>`. The reason for the difference is what each\n// receives:\n// - `BaseController` is always given an instance of Messenger, and expects an\n// instance of Messenger in its type parameters, so it can make a direct\n// comparison.\n// - `createUIQueryClient`, on the other hand, is given a root messenger, which\n// in Mobile is a *subclass* of `Messenger` (called\n// `ExtendedRootMessenger`). A subclass is a different class, so TypeScript\n// falls back to a structural comparison that includes `Messenger`'s private\n// fields (and because some private fields are functions or contain\n// functions, it attempts to compare action handlers or event subscriptions (e.g.\n// `#subscriptionDelegationTargets`), and fails due to contravariance.\n// Inferring the unions instead of constraining the type sidesteps this\n// private-field check.\ntype SupportsDataServices<MessengerInstance, DataServiceName extends string> =\n MessengerInstance extends Messenger<\n string,\n infer Action extends ActionConstraint,\n infer Event extends EventConstraint,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n >\n ? DataServiceActions<DataServiceName>['type'] extends Action['type']\n ? DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'] extends Event['type']\n ? MessengerInstance\n : never\n : never\n : never;\n\n/**\n * Create a QueryClient that queries and subscribes to data services using a\n * messenger adapter. This is a messenger-like object that carries some\n * constraints:\n *\n * 1. The messenger must support the `call`, `subscribe` and\n * `unsubscribe` methods.\n * 2. All action handler arguments and event payloads must be JSON-compatible.\n * 3. The messenger must minimally support capabilities that belong to the\n * designated data services and must minimally support the\n * `:cacheUpdated:${hash}` event of the the designated data services.\n *\n * @param dataServices - A list of data services.\n * @param messenger - A messenger adapter.\n * @param config - Optional query client configuration options.\n * @returns The QueryClient.\n */\nexport function createUIQueryClient<DataServiceNames extends readonly string[]>(\n dataServices: DataServiceNames,\n messenger: MessengerAdapter<DataServiceNames[number]>,\n config?: QueryClientConfig,\n): QueryClient;\n\n/**\n * Create a QueryClient that queries and subscribes to data services using a\n * messenger. This messenger carries some constraints:\n *\n * 1. All action handler arguments and event payloads must be JSON-compatible.\n * 2. The messenger must minimally support capabilities that belong to the\n * designated data services and must minimally support the\n * `:cacheUpdated:${hash}` event of the the designated data services.\n *\n * @param dataServices - A list of data services.\n * @param messenger - A messenger.\n * @param config - Optional query client configuration options.\n * @returns The QueryClient.\n */\nexport function createUIQueryClient<\n DataServiceNames extends readonly string[],\n MessengerInstance,\n>(\n dataServices: DataServiceNames,\n messenger: SupportsDataServices<MessengerInstance, DataServiceNames[number]>,\n config?: QueryClientConfig,\n): QueryClient;\n\nexport function createUIQueryClient<DataServiceNames extends readonly string[]>(\n dataServices: DataServiceNames,\n messenger: MessengerAdapter<DataServiceNames[number]>,\n config: QueryClientConfig = {},\n): QueryClient {\n const subscriptions = new Map<\n string,\n DataServiceGranularCacheUpdatedHandler\n >();\n\n /**\n * Check whether a name is one of the provided data service names.\n *\n * @param service - The service name to check.\n * @returns Whether the service name is configured.\n */\n function isRecognizedDataService(\n service: string,\n ): service is DataServiceNames[number] {\n return dataServices.some((dataService) => dataService === service);\n }\n\n /**\n * Check whether an action belongs to one of the provided data services.\n *\n * @param action - The action name to check.\n * @returns Whether the action belongs to a configured data service.\n */\n function isRecognizedDataServiceAction(\n action: string,\n ): action is `${DataServiceNames[number]}:${string}` {\n return isRecognizedDataService(action.split(':')[0]);\n }\n\n /**\n * Parse a query key to detect a service name.\n *\n * @param queryKey - The query key.\n * @returns The service name if it parsing succeeded, otherwise null.\n */\n function parseQueryKey(queryKey: QueryKey): DataServiceNames[number] | null {\n const action = queryKey[0];\n\n if (typeof action !== 'string') {\n return null;\n }\n\n const service = action.split(':')[0];\n\n if (!isRecognizedDataService(service)) {\n return null;\n }\n\n return service;\n }\n\n const client: QueryClient = new QueryClient({\n ...config,\n defaultOptions: {\n queries: {\n ...config.defaultOptions?.queries,\n queryFn: async (options): Promise<unknown> => {\n const { queryKey } = options;\n\n const action = queryKey[0];\n\n assert(\n typeof action === 'string' && isRecognizedDataServiceAction(action),\n \"Queries must call actions on the messenger provided to createUIQueryClient, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.\",\n );\n\n const params = options.queryKey.slice(1);\n if (options.pageParam !== undefined) {\n params.push(options.pageParam);\n }\n\n return await messenger.call(action, ...params);\n },\n },\n mutations: config.defaultOptions?.mutations,\n },\n });\n\n const cache = client.getQueryCache();\n\n cache.subscribe((event) => {\n const { query } = event;\n\n const hash = query.queryHash;\n const hasSubscription = subscriptions.has(hash);\n const observerCount = query.getObserversCount();\n\n const service = parseQueryKey(query.queryKey);\n\n if (!service) {\n return;\n }\n\n if (\n !hasSubscription &&\n event.type === 'observerAdded' &&\n observerCount === 1\n ) {\n const cacheListener: DataServiceGranularCacheUpdatedHandler = (\n payload,\n ) => {\n if (payload.type === 'removed') {\n return;\n }\n\n hydrate(client, payload.state);\n };\n\n subscriptions.set(hash, cacheListener);\n messenger.subscribe(`${service}:cacheUpdated:${hash}`, cacheListener);\n } else if (\n event.type === 'observerRemoved' &&\n observerCount === 0 &&\n hasSubscription\n ) {\n const subscriptionListener = subscriptions.get(hash);\n\n if (subscriptionListener) {\n messenger.unsubscribe(\n `${service}:cacheUpdated:${hash}`,\n subscriptionListener,\n );\n }\n subscriptions.delete(hash);\n }\n });\n\n // Override invalidateQueries to ensure the data service is invalidated as well.\n const originalInvalidate = client.invalidateQueries.bind(client);\n\n // This function is defined in this way to have full support for all function overloads.\n client.invalidateQueries = async (\n arg1?: QueryKey | InvalidateQueryFilters,\n arg2?: OmitKeyof<InvalidateQueryFilters, 'queryKey'> | InvalidateOptions,\n arg3?: InvalidateOptions,\n ): Promise<void> => {\n const [filters, options] = parseFilterArgs(arg1, arg2, arg3);\n\n const queries = client.getQueryCache().findAll(filters);\n\n const services = [\n ...new Set(queries.map((query) => parseQueryKey(query.queryKey))),\n ];\n\n await Promise.all(\n services.map(async (service) => {\n if (!service) {\n return null;\n }\n\n return messenger.call(`${service}:invalidateQueries`, filters, options);\n }),\n );\n\n return originalInvalidate(filters, options);\n };\n\n return client;\n}\n"]}
@@ -9,7 +9,7 @@ type DataServiceGranularCacheUpdatedHandler = (payload: DataServiceGranularCache
9
9
  * A narrower subset of the `Messenger` type, tailored to the messenger
10
10
  * that `createUIQueryClient` interacts with.
11
11
  */
12
- type MessengerAdapter<DataServiceName extends string> = {
12
+ export type MessengerAdapter<DataServiceName extends string> = {
13
13
  /**
14
14
  * Call an action on one of the configured data services.
15
15
  *
@@ -1 +1 @@
1
- {"version":3,"file":"createUIQueryClient.d.cts","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,oCAAoC,EACpC,sCAAsC,EACvC,oCAAoC;AACrC,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACV,4BAA4B;AAE7B,OAAO,EAEL,WAAW,EAMX,iBAAiB,EAClB,6BAA6B;AAE9B;;GAEG;AACH,KAAK,sCAAsC,GAAG,CAC5C,OAAO,EAAE,sCAAsC,KAC5C,IAAI,CAAC;AAEV;;;GAGG;AACH,KAAK,gBAAgB,CAAC,eAAe,SAAS,MAAM,IAAI;IACtD;;;;;;;OAOG;IACH,IAAI,CACF,UAAU,EAAE,GAAG,eAAe,IAAI,MAAM,EAAE,EAC1C,GAAG,MAAM,EAAE,OAAO,EAAE,GACnB,OAAO,CAAC;IAEX;;;OAGG;IACH,SAAS,CACP,SAAS,EAAE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,EACxE,OAAO,EAAE,sCAAsC,GAC9C,IAAI,CAAC;IAER;;;OAGG;IACH,WAAW,CACT,SAAS,EAAE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,EACxE,OAAO,EAAE,sCAAsC,GAC9C,IAAI,CAAC;CACT,CAAC;AAEF;;;;;GAKG;AAqBH,KAAK,oBAAoB,CAAC,iBAAiB,EAAE,eAAe,SAAS,MAAM,IACzE,iBAAiB,SAAS,SAAS,CACjC,MAAM,EACN,MAAM,MAAM,SAAS,gBAAgB,EACrC,MAAM,KAAK,SAAS,eAAe,EAEnC,GAAG,CACJ,GACG,kBAAkB,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,GAChE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,GACjF,iBAAiB,GACjB,KAAK,GACP,KAAK,GACP,KAAK,CAAC;AAEZ;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,SAAS,SAAS,MAAM,EAAE,EAC5E,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,EACrD,MAAM,CAAC,EAAE,iBAAiB,GACzB,WAAW,CAAC;AAEf;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,gBAAgB,SAAS,SAAS,MAAM,EAAE,EAC1C,iBAAiB,EAEjB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,oBAAoB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAC5E,MAAM,CAAC,EAAE,iBAAiB,GACzB,WAAW,CAAC"}
1
+ {"version":3,"file":"createUIQueryClient.d.cts","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,oCAAoC,EACpC,sCAAsC,EACvC,oCAAoC;AACrC,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACV,4BAA4B;AAE7B,OAAO,EAEL,WAAW,EAMX,iBAAiB,EAClB,6BAA6B;AAE9B;;GAEG;AACH,KAAK,sCAAsC,GAAG,CAC5C,OAAO,EAAE,sCAAsC,KAC5C,IAAI,CAAC;AAEV;;;GAGG;AACH,MAAM,MAAM,gBAAgB,CAAC,eAAe,SAAS,MAAM,IAAI;IAC7D;;;;;;;OAOG;IACH,IAAI,CACF,UAAU,EAAE,GAAG,eAAe,IAAI,MAAM,EAAE,EAC1C,GAAG,MAAM,EAAE,OAAO,EAAE,GACnB,OAAO,CAAC;IAEX;;;OAGG;IACH,SAAS,CACP,SAAS,EAAE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,EACxE,OAAO,EAAE,sCAAsC,GAC9C,IAAI,CAAC;IAER;;;OAGG;IACH,WAAW,CACT,SAAS,EAAE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,EACxE,OAAO,EAAE,sCAAsC,GAC9C,IAAI,CAAC;CACT,CAAC;AAEF;;;;;GAKG;AAqBH,KAAK,oBAAoB,CAAC,iBAAiB,EAAE,eAAe,SAAS,MAAM,IACzE,iBAAiB,SAAS,SAAS,CACjC,MAAM,EACN,MAAM,MAAM,SAAS,gBAAgB,EACrC,MAAM,KAAK,SAAS,eAAe,EAEnC,GAAG,CACJ,GACG,kBAAkB,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,GAChE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,GACjF,iBAAiB,GACjB,KAAK,GACP,KAAK,GACP,KAAK,CAAC;AAEZ;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,SAAS,SAAS,MAAM,EAAE,EAC5E,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,EACrD,MAAM,CAAC,EAAE,iBAAiB,GACzB,WAAW,CAAC;AAEf;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,gBAAgB,SAAS,SAAS,MAAM,EAAE,EAC1C,iBAAiB,EAEjB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,oBAAoB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAC5E,MAAM,CAAC,EAAE,iBAAiB,GACzB,WAAW,CAAC"}
@@ -9,7 +9,7 @@ type DataServiceGranularCacheUpdatedHandler = (payload: DataServiceGranularCache
9
9
  * A narrower subset of the `Messenger` type, tailored to the messenger
10
10
  * that `createUIQueryClient` interacts with.
11
11
  */
12
- type MessengerAdapter<DataServiceName extends string> = {
12
+ export type MessengerAdapter<DataServiceName extends string> = {
13
13
  /**
14
14
  * Call an action on one of the configured data services.
15
15
  *
@@ -1 +1 @@
1
- {"version":3,"file":"createUIQueryClient.d.mts","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,oCAAoC,EACpC,sCAAsC,EACvC,oCAAoC;AACrC,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACV,4BAA4B;AAE7B,OAAO,EAEL,WAAW,EAMX,iBAAiB,EAClB,6BAA6B;AAE9B;;GAEG;AACH,KAAK,sCAAsC,GAAG,CAC5C,OAAO,EAAE,sCAAsC,KAC5C,IAAI,CAAC;AAEV;;;GAGG;AACH,KAAK,gBAAgB,CAAC,eAAe,SAAS,MAAM,IAAI;IACtD;;;;;;;OAOG;IACH,IAAI,CACF,UAAU,EAAE,GAAG,eAAe,IAAI,MAAM,EAAE,EAC1C,GAAG,MAAM,EAAE,OAAO,EAAE,GACnB,OAAO,CAAC;IAEX;;;OAGG;IACH,SAAS,CACP,SAAS,EAAE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,EACxE,OAAO,EAAE,sCAAsC,GAC9C,IAAI,CAAC;IAER;;;OAGG;IACH,WAAW,CACT,SAAS,EAAE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,EACxE,OAAO,EAAE,sCAAsC,GAC9C,IAAI,CAAC;CACT,CAAC;AAEF;;;;;GAKG;AAqBH,KAAK,oBAAoB,CAAC,iBAAiB,EAAE,eAAe,SAAS,MAAM,IACzE,iBAAiB,SAAS,SAAS,CACjC,MAAM,EACN,MAAM,MAAM,SAAS,gBAAgB,EACrC,MAAM,KAAK,SAAS,eAAe,EAEnC,GAAG,CACJ,GACG,kBAAkB,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,GAChE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,GACjF,iBAAiB,GACjB,KAAK,GACP,KAAK,GACP,KAAK,CAAC;AAEZ;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,SAAS,SAAS,MAAM,EAAE,EAC5E,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,EACrD,MAAM,CAAC,EAAE,iBAAiB,GACzB,WAAW,CAAC;AAEf;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,gBAAgB,SAAS,SAAS,MAAM,EAAE,EAC1C,iBAAiB,EAEjB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,oBAAoB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAC5E,MAAM,CAAC,EAAE,iBAAiB,GACzB,WAAW,CAAC"}
1
+ {"version":3,"file":"createUIQueryClient.d.mts","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,oCAAoC,EACpC,sCAAsC,EACvC,oCAAoC;AACrC,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACV,4BAA4B;AAE7B,OAAO,EAEL,WAAW,EAMX,iBAAiB,EAClB,6BAA6B;AAE9B;;GAEG;AACH,KAAK,sCAAsC,GAAG,CAC5C,OAAO,EAAE,sCAAsC,KAC5C,IAAI,CAAC;AAEV;;;GAGG;AACH,MAAM,MAAM,gBAAgB,CAAC,eAAe,SAAS,MAAM,IAAI;IAC7D;;;;;;;OAOG;IACH,IAAI,CACF,UAAU,EAAE,GAAG,eAAe,IAAI,MAAM,EAAE,EAC1C,GAAG,MAAM,EAAE,OAAO,EAAE,GACnB,OAAO,CAAC;IAEX;;;OAGG;IACH,SAAS,CACP,SAAS,EAAE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,EACxE,OAAO,EAAE,sCAAsC,GAC9C,IAAI,CAAC;IAER;;;OAGG;IACH,WAAW,CACT,SAAS,EAAE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,EACxE,OAAO,EAAE,sCAAsC,GAC9C,IAAI,CAAC;CACT,CAAC;AAEF;;;;;GAKG;AAqBH,KAAK,oBAAoB,CAAC,iBAAiB,EAAE,eAAe,SAAS,MAAM,IACzE,iBAAiB,SAAS,SAAS,CACjC,MAAM,EACN,MAAM,MAAM,SAAS,gBAAgB,EACrC,MAAM,KAAK,SAAS,eAAe,EAEnC,GAAG,CACJ,GACG,kBAAkB,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,GAChE,oCAAoC,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,GACjF,iBAAiB,GACjB,KAAK,GACP,KAAK,GACP,KAAK,CAAC;AAEZ;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,SAAS,SAAS,MAAM,EAAE,EAC5E,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,EACrD,MAAM,CAAC,EAAE,iBAAiB,GACzB,WAAW,CAAC;AAEf;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,gBAAgB,SAAS,SAAS,MAAM,EAAE,EAC1C,iBAAiB,EAEjB,YAAY,EAAE,gBAAgB,EAC9B,SAAS,EAAE,oBAAoB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAC5E,MAAM,CAAC,EAAE,iBAAiB,GACzB,WAAW,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"createUIQueryClient.mjs","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,MAAM,EAAE,wBAAwB;AACzC,OAAO,EACL,OAAO,EACP,WAAW,EAIX,eAAe,EAGhB,6BAA6B;AAqI9B,MAAM,UAAU,mBAAmB,CACjC,YAA8B,EAC9B,SAAqD,EACrD,SAA4B,EAAE;IAE9B,MAAM,aAAa,GAAG,IAAI,GAAG,EAG1B,CAAC;IAEJ;;;;;OAKG;IACH,SAAS,uBAAuB,CAC9B,OAAe;QAEf,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,KAAK,OAAO,CAAC,CAAC;IACrE,CAAC;IAED;;;;;OAKG;IACH,SAAS,6BAA6B,CACpC,MAAc;QAEd,OAAO,uBAAuB,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IAED;;;;;OAKG;IACH,SAAS,aAAa,CAAC,QAAkB;QACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAE3B,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAErC,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,EAAE,CAAC;YACtC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,MAAM,GAAgB,IAAI,WAAW,CAAC;QAC1C,GAAG,MAAM;QACT,cAAc,EAAE;YACd,OAAO,EAAE;gBACP,GAAG,MAAM,CAAC,cAAc,EAAE,OAAO;gBACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAoB,EAAE;oBAC3C,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;oBAE7B,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;oBAE3B,MAAM,CACJ,OAAO,MAAM,KAAK,QAAQ,IAAI,6BAA6B,CAAC,MAAM,CAAC,EACnE,qIAAqI,CACtI,CAAC;oBAEF,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;oBACzC,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;wBACpC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;oBACjC,CAAC;oBAED,OAAO,MAAM,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC;gBACjD,CAAC;aACF;YACD,SAAS,EAAE,MAAM,CAAC,cAAc,EAAE,SAAS;SAC5C;KACF,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC;IAErC,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;QACxB,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;QAExB,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC;QAC7B,MAAM,eAAe,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,aAAa,GAAG,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAEhD,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAE9C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QAED,IACE,CAAC,eAAe;YAChB,KAAK,CAAC,IAAI,KAAK,eAAe;YAC9B,aAAa,KAAK,CAAC,EACnB,CAAC;YACD,MAAM,aAAa,GAA2C,CAC5D,OAAO,EACP,EAAE;gBACF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBAED,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YACjC,CAAC,CAAC;YAEF,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;YACvC,SAAS,CAAC,SAAS,CAAC,GAAG,OAAO,iBAAiB,IAAI,EAAE,EAAE,aAAa,CAAC,CAAC;QACxE,CAAC;aAAM,IACL,KAAK,CAAC,IAAI,KAAK,iBAAiB;YAChC,aAAa,KAAK,CAAC;YACnB,eAAe,EACf,CAAC;YACD,MAAM,oBAAoB,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAErD,IAAI,oBAAoB,EAAE,CAAC;gBACzB,SAAS,CAAC,WAAW,CACnB,GAAG,OAAO,iBAAiB,IAAI,EAAE,EACjC,oBAAoB,CACrB,CAAC;YACJ,CAAC;YACD,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,gFAAgF;IAChF,MAAM,kBAAkB,GAAG,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEjE,wFAAwF;IACxF,MAAM,CAAC,iBAAiB,GAAG,KAAK,EAC9B,IAAwC,EACxC,IAAwE,EACxE,IAAwB,EACT,EAAE;QACjB,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAE7D,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAExD,MAAM,QAAQ,GAAG;YACf,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;SAClE,CAAC;QAEF,MAAM,OAAO,CAAC,GAAG,CACf,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO,SAAS,CAAC,IAAI,CAAC,GAAG,OAAO,oBAAoB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1E,CAAC,CAAC,CACH,CAAC;QAEF,OAAO,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type {\n DataServiceActions,\n DataServiceGranularCacheUpdatedEvent,\n DataServiceGranularCacheUpdatedPayload,\n} from '@metamask/base-data-service';\nimport type {\n ActionConstraint,\n EventConstraint,\n Messenger,\n} from '@metamask/messenger';\nimport { assert } from '@metamask/utils';\nimport {\n hydrate,\n QueryClient,\n InvalidateQueryFilters,\n InvalidateOptions,\n OmitKeyof,\n parseFilterArgs,\n QueryKey,\n QueryClientConfig,\n} from '@tanstack/query-core';\n\n/**\n * Handles granular cache update events emitted by data services.\n */\ntype DataServiceGranularCacheUpdatedHandler = (\n payload: DataServiceGranularCacheUpdatedPayload,\n) => void;\n\n/**\n * A narrower subset of the `Messenger` type, tailored to the messenger\n * that `createUIQueryClient` interacts with.\n */\ntype MessengerAdapter<DataServiceName extends string> = {\n /**\n * Call an action on one of the configured data services.\n *\n * Note: The parameters are typed as `unknown[]` rather than `Json[]`. For\n * concrete messengers, each action's parameters exist as fixed-length tuple,\n * and a variadic `Json[]` is not assignable to a fixed-length tuple, so using\n * `Json[]` here would reject otherwise valid messengers.\n */\n call(\n actionType: `${DataServiceName}:${string}`,\n ...params: unknown[]\n ): unknown;\n\n /**\n * Subscribe to a granular cache update event on one of the configured data\n * services.\n */\n subscribe(\n eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'],\n handler: DataServiceGranularCacheUpdatedHandler,\n ): void;\n\n /**\n * Unsubscribe from a granular cache update event on one of the configured\n * data services.\n */\n unsubscribe(\n eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'],\n handler: DataServiceGranularCacheUpdatedHandler,\n ): void;\n};\n\n/**\n * Constraints a messenger such that it must minimally support a subset of\n * capabilities that the given data services provide. Specifically, it must at\n * least allow all data service actions to be called, and it must at least allow\n * the `:cacheUpdated:${hash}` event to be subscribed to.\n */\n// Notes on the implementation:\n//\n// - The messenger type parameter is deliberately *not* constrained (e.g. to\n// `Messenger<string, ActionConstraint, EventConstraint, any>`); instead we\n// `infer` the action and event unions directly. This differs from\n// `BaseController`, which does constrain its `ControllerMessenger` type\n// parameter to `Messenger<...>`. The reason for the difference is what each\n// receives:\n// - `BaseController` is always given an instance of Messenger, and expects an\n// instance of Messenger in its type parameters, so it can make a direct\n// comparison.\n// - `createUIQueryClient`, on the other hand, is given a root messenger, which\n// in Mobile is a *subclass* of `Messenger` (called\n// `ExtendedRootMessenger`). A subclass is a different class, so TypeScript\n// falls back to a structural comparison that includes `Messenger`'s private\n// fields (and because some private fields are functions or contain\n// functions, it attempts to compare action handlers or event subscriptions (e.g.\n// `#subscriptionDelegationTargets`), and fails due to contravariance.\n// Inferring the unions instead of constraining the type sidesteps this\n// private-field check.\ntype SupportsDataServices<MessengerInstance, DataServiceName extends string> =\n MessengerInstance extends Messenger<\n string,\n infer Action extends ActionConstraint,\n infer Event extends EventConstraint,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n >\n ? DataServiceActions<DataServiceName>['type'] extends Action['type']\n ? DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'] extends Event['type']\n ? MessengerInstance\n : never\n : never\n : never;\n\n/**\n * Create a QueryClient that queries and subscribes to data services using a\n * messenger adapter. This is a messenger-like object that carries some\n * constraints:\n *\n * 1. The messenger must support the `call`, `subscribe` and\n * `unsubscribe` methods.\n * 2. All action handler arguments and event payloads must be JSON-compatible.\n * 3. The messenger must minimally support capabilities that belong to the\n * designated data services and must minimally support the\n * `:cacheUpdated:${hash}` event of the the designated data services.\n *\n * @param dataServices - A list of data services.\n * @param messenger - A messenger adapter.\n * @param config - Optional query client configuration options.\n * @returns The QueryClient.\n */\nexport function createUIQueryClient<DataServiceNames extends readonly string[]>(\n dataServices: DataServiceNames,\n messenger: MessengerAdapter<DataServiceNames[number]>,\n config?: QueryClientConfig,\n): QueryClient;\n\n/**\n * Create a QueryClient that queries and subscribes to data services using a\n * messenger. This messenger carries some constraints:\n *\n * 1. All action handler arguments and event payloads must be JSON-compatible.\n * 2. The messenger must minimally support capabilities that belong to the\n * designated data services and must minimally support the\n * `:cacheUpdated:${hash}` event of the the designated data services.\n *\n * @param dataServices - A list of data services.\n * @param messenger - A messenger.\n * @param config - Optional query client configuration options.\n * @returns The QueryClient.\n */\nexport function createUIQueryClient<\n DataServiceNames extends readonly string[],\n MessengerInstance,\n>(\n dataServices: DataServiceNames,\n messenger: SupportsDataServices<MessengerInstance, DataServiceNames[number]>,\n config?: QueryClientConfig,\n): QueryClient;\n\nexport function createUIQueryClient<DataServiceNames extends readonly string[]>(\n dataServices: DataServiceNames,\n messenger: MessengerAdapter<DataServiceNames[number]>,\n config: QueryClientConfig = {},\n): QueryClient {\n const subscriptions = new Map<\n string,\n DataServiceGranularCacheUpdatedHandler\n >();\n\n /**\n * Check whether a name is one of the provided data service names.\n *\n * @param service - The service name to check.\n * @returns Whether the service name is configured.\n */\n function isRecognizedDataService(\n service: string,\n ): service is DataServiceNames[number] {\n return dataServices.some((dataService) => dataService === service);\n }\n\n /**\n * Check whether an action belongs to one of the provided data services.\n *\n * @param action - The action name to check.\n * @returns Whether the action belongs to a configured data service.\n */\n function isRecognizedDataServiceAction(\n action: string,\n ): action is `${DataServiceNames[number]}:${string}` {\n return isRecognizedDataService(action.split(':')[0]);\n }\n\n /**\n * Parse a query key to detect a service name.\n *\n * @param queryKey - The query key.\n * @returns The service name if it parsing succeeded, otherwise null.\n */\n function parseQueryKey(queryKey: QueryKey): DataServiceNames[number] | null {\n const action = queryKey[0];\n\n if (typeof action !== 'string') {\n return null;\n }\n\n const service = action.split(':')[0];\n\n if (!isRecognizedDataService(service)) {\n return null;\n }\n\n return service;\n }\n\n const client: QueryClient = new QueryClient({\n ...config,\n defaultOptions: {\n queries: {\n ...config.defaultOptions?.queries,\n queryFn: async (options): Promise<unknown> => {\n const { queryKey } = options;\n\n const action = queryKey[0];\n\n assert(\n typeof action === 'string' && isRecognizedDataServiceAction(action),\n \"Queries must call actions on the messenger provided to createUIQueryClient, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.\",\n );\n\n const params = options.queryKey.slice(1);\n if (options.pageParam !== undefined) {\n params.push(options.pageParam);\n }\n\n return await messenger.call(action, ...params);\n },\n },\n mutations: config.defaultOptions?.mutations,\n },\n });\n\n const cache = client.getQueryCache();\n\n cache.subscribe((event) => {\n const { query } = event;\n\n const hash = query.queryHash;\n const hasSubscription = subscriptions.has(hash);\n const observerCount = query.getObserversCount();\n\n const service = parseQueryKey(query.queryKey);\n\n if (!service) {\n return;\n }\n\n if (\n !hasSubscription &&\n event.type === 'observerAdded' &&\n observerCount === 1\n ) {\n const cacheListener: DataServiceGranularCacheUpdatedHandler = (\n payload,\n ) => {\n if (payload.type === 'removed') {\n return;\n }\n\n hydrate(client, payload.state);\n };\n\n subscriptions.set(hash, cacheListener);\n messenger.subscribe(`${service}:cacheUpdated:${hash}`, cacheListener);\n } else if (\n event.type === 'observerRemoved' &&\n observerCount === 0 &&\n hasSubscription\n ) {\n const subscriptionListener = subscriptions.get(hash);\n\n if (subscriptionListener) {\n messenger.unsubscribe(\n `${service}:cacheUpdated:${hash}`,\n subscriptionListener,\n );\n }\n subscriptions.delete(hash);\n }\n });\n\n // Override invalidateQueries to ensure the data service is invalidated as well.\n const originalInvalidate = client.invalidateQueries.bind(client);\n\n // This function is defined in this way to have full support for all function overloads.\n client.invalidateQueries = async (\n arg1?: QueryKey | InvalidateQueryFilters,\n arg2?: OmitKeyof<InvalidateQueryFilters, 'queryKey'> | InvalidateOptions,\n arg3?: InvalidateOptions,\n ): Promise<void> => {\n const [filters, options] = parseFilterArgs(arg1, arg2, arg3);\n\n const queries = client.getQueryCache().findAll(filters);\n\n const services = [\n ...new Set(queries.map((query) => parseQueryKey(query.queryKey))),\n ];\n\n await Promise.all(\n services.map(async (service) => {\n if (!service) {\n return null;\n }\n\n return messenger.call(`${service}:invalidateQueries`, filters, options);\n }),\n );\n\n return originalInvalidate(filters, options);\n };\n\n return client;\n}\n"]}
1
+ {"version":3,"file":"createUIQueryClient.mjs","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DataServiceActions,\n DataServiceGranularCacheUpdatedEvent,\n DataServiceGranularCacheUpdatedPayload,\n} from '@metamask/base-data-service';\nimport type {\n ActionConstraint,\n EventConstraint,\n Messenger,\n} from '@metamask/messenger';\nimport { assert } from '@metamask/utils';\nimport {\n hydrate,\n QueryClient,\n InvalidateQueryFilters,\n InvalidateOptions,\n OmitKeyof,\n parseFilterArgs,\n QueryKey,\n QueryClientConfig,\n} from '@tanstack/query-core';\n\n/**\n * Handles granular cache update events emitted by data services.\n */\ntype DataServiceGranularCacheUpdatedHandler = (\n payload: DataServiceGranularCacheUpdatedPayload,\n) => void;\n\n/**\n * A narrower subset of the `Messenger` type, tailored to the messenger\n * that `createUIQueryClient` interacts with.\n */\nexport type MessengerAdapter<DataServiceName extends string> = {\n /**\n * Call an action on one of the configured data services.\n *\n * Note: The parameters are typed as `unknown[]` rather than `Json[]`. For\n * concrete messengers, each action's parameters exist as fixed-length tuple,\n * and a variadic `Json[]` is not assignable to a fixed-length tuple, so using\n * `Json[]` here would reject otherwise valid messengers.\n */\n call(\n actionType: `${DataServiceName}:${string}`,\n ...params: unknown[]\n ): unknown;\n\n /**\n * Subscribe to a granular cache update event on one of the configured data\n * services.\n */\n subscribe(\n eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'],\n handler: DataServiceGranularCacheUpdatedHandler,\n ): void;\n\n /**\n * Unsubscribe from a granular cache update event on one of the configured\n * data services.\n */\n unsubscribe(\n eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'],\n handler: DataServiceGranularCacheUpdatedHandler,\n ): void;\n};\n\n/**\n * Constraints a messenger such that it must minimally support a subset of\n * capabilities that the given data services provide. Specifically, it must at\n * least allow all data service actions to be called, and it must at least allow\n * the `:cacheUpdated:${hash}` event to be subscribed to.\n */\n// Notes on the implementation:\n//\n// - The messenger type parameter is deliberately *not* constrained (e.g. to\n// `Messenger<string, ActionConstraint, EventConstraint, any>`); instead we\n// `infer` the action and event unions directly. This differs from\n// `BaseController`, which does constrain its `ControllerMessenger` type\n// parameter to `Messenger<...>`. The reason for the difference is what each\n// receives:\n// - `BaseController` is always given an instance of Messenger, and expects an\n// instance of Messenger in its type parameters, so it can make a direct\n// comparison.\n// - `createUIQueryClient`, on the other hand, is given a root messenger, which\n// in Mobile is a *subclass* of `Messenger` (called\n// `ExtendedRootMessenger`). A subclass is a different class, so TypeScript\n// falls back to a structural comparison that includes `Messenger`'s private\n// fields (and because some private fields are functions or contain\n// functions, it attempts to compare action handlers or event subscriptions (e.g.\n// `#subscriptionDelegationTargets`), and fails due to contravariance.\n// Inferring the unions instead of constraining the type sidesteps this\n// private-field check.\ntype SupportsDataServices<MessengerInstance, DataServiceName extends string> =\n MessengerInstance extends Messenger<\n string,\n infer Action extends ActionConstraint,\n infer Event extends EventConstraint,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n >\n ? DataServiceActions<DataServiceName>['type'] extends Action['type']\n ? DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'] extends Event['type']\n ? MessengerInstance\n : never\n : never\n : never;\n\n/**\n * Create a QueryClient that queries and subscribes to data services using a\n * messenger adapter. This is a messenger-like object that carries some\n * constraints:\n *\n * 1. The messenger must support the `call`, `subscribe` and\n * `unsubscribe` methods.\n * 2. All action handler arguments and event payloads must be JSON-compatible.\n * 3. The messenger must minimally support capabilities that belong to the\n * designated data services and must minimally support the\n * `:cacheUpdated:${hash}` event of the the designated data services.\n *\n * @param dataServices - A list of data services.\n * @param messenger - A messenger adapter.\n * @param config - Optional query client configuration options.\n * @returns The QueryClient.\n */\nexport function createUIQueryClient<DataServiceNames extends readonly string[]>(\n dataServices: DataServiceNames,\n messenger: MessengerAdapter<DataServiceNames[number]>,\n config?: QueryClientConfig,\n): QueryClient;\n\n/**\n * Create a QueryClient that queries and subscribes to data services using a\n * messenger. This messenger carries some constraints:\n *\n * 1. All action handler arguments and event payloads must be JSON-compatible.\n * 2. The messenger must minimally support capabilities that belong to the\n * designated data services and must minimally support the\n * `:cacheUpdated:${hash}` event of the the designated data services.\n *\n * @param dataServices - A list of data services.\n * @param messenger - A messenger.\n * @param config - Optional query client configuration options.\n * @returns The QueryClient.\n */\nexport function createUIQueryClient<\n DataServiceNames extends readonly string[],\n MessengerInstance,\n>(\n dataServices: DataServiceNames,\n messenger: SupportsDataServices<MessengerInstance, DataServiceNames[number]>,\n config?: QueryClientConfig,\n): QueryClient;\n\nexport function createUIQueryClient<DataServiceNames extends readonly string[]>(\n dataServices: DataServiceNames,\n messenger: MessengerAdapter<DataServiceNames[number]>,\n config: QueryClientConfig = {},\n): QueryClient {\n const subscriptions = new Map<\n string,\n DataServiceGranularCacheUpdatedHandler\n >();\n\n /**\n * Check whether a name is one of the provided data service names.\n *\n * @param service - The service name to check.\n * @returns Whether the service name is configured.\n */\n function isRecognizedDataService(\n service: string,\n ): service is DataServiceNames[number] {\n return dataServices.some((dataService) => dataService === service);\n }\n\n /**\n * Check whether an action belongs to one of the provided data services.\n *\n * @param action - The action name to check.\n * @returns Whether the action belongs to a configured data service.\n */\n function isRecognizedDataServiceAction(\n action: string,\n ): action is `${DataServiceNames[number]}:${string}` {\n return isRecognizedDataService(action.split(':')[0]);\n }\n\n /**\n * Parse a query key to detect a service name.\n *\n * @param queryKey - The query key.\n * @returns The service name if it parsing succeeded, otherwise null.\n */\n function parseQueryKey(queryKey: QueryKey): DataServiceNames[number] | null {\n const action = queryKey[0];\n\n if (typeof action !== 'string') {\n return null;\n }\n\n const service = action.split(':')[0];\n\n if (!isRecognizedDataService(service)) {\n return null;\n }\n\n return service;\n }\n\n const client: QueryClient = new QueryClient({\n ...config,\n defaultOptions: {\n queries: {\n ...config.defaultOptions?.queries,\n queryFn: async (options): Promise<unknown> => {\n const { queryKey } = options;\n\n const action = queryKey[0];\n\n assert(\n typeof action === 'string' && isRecognizedDataServiceAction(action),\n \"Queries must call actions on the messenger provided to createUIQueryClient, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.\",\n );\n\n const params = options.queryKey.slice(1);\n if (options.pageParam !== undefined) {\n params.push(options.pageParam);\n }\n\n return await messenger.call(action, ...params);\n },\n },\n mutations: config.defaultOptions?.mutations,\n },\n });\n\n const cache = client.getQueryCache();\n\n cache.subscribe((event) => {\n const { query } = event;\n\n const hash = query.queryHash;\n const hasSubscription = subscriptions.has(hash);\n const observerCount = query.getObserversCount();\n\n const service = parseQueryKey(query.queryKey);\n\n if (!service) {\n return;\n }\n\n if (\n !hasSubscription &&\n event.type === 'observerAdded' &&\n observerCount === 1\n ) {\n const cacheListener: DataServiceGranularCacheUpdatedHandler = (\n payload,\n ) => {\n if (payload.type === 'removed') {\n return;\n }\n\n hydrate(client, payload.state);\n };\n\n subscriptions.set(hash, cacheListener);\n messenger.subscribe(`${service}:cacheUpdated:${hash}`, cacheListener);\n } else if (\n event.type === 'observerRemoved' &&\n observerCount === 0 &&\n hasSubscription\n ) {\n const subscriptionListener = subscriptions.get(hash);\n\n if (subscriptionListener) {\n messenger.unsubscribe(\n `${service}:cacheUpdated:${hash}`,\n subscriptionListener,\n );\n }\n subscriptions.delete(hash);\n }\n });\n\n // Override invalidateQueries to ensure the data service is invalidated as well.\n const originalInvalidate = client.invalidateQueries.bind(client);\n\n // This function is defined in this way to have full support for all function overloads.\n client.invalidateQueries = async (\n arg1?: QueryKey | InvalidateQueryFilters,\n arg2?: OmitKeyof<InvalidateQueryFilters, 'queryKey'> | InvalidateOptions,\n arg3?: InvalidateOptions,\n ): Promise<void> => {\n const [filters, options] = parseFilterArgs(arg1, arg2, arg3);\n\n const queries = client.getQueryCache().findAll(filters);\n\n const services = [\n ...new Set(queries.map((query) => parseQueryKey(query.queryKey))),\n ];\n\n await Promise.all(\n services.map(async (service) => {\n if (!service) {\n return null;\n }\n\n return messenger.call(`${service}:invalidateQueries`, filters, options);\n }),\n );\n\n return originalInvalidate(filters, options);\n };\n\n return client;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/react-data-query",
3
- "version": "0.2.2-preview-4328c6196",
3
+ "version": "0.2.2-preview-318d52ea7",
4
4
  "description": "Provides React utilities for consuming data services",
5
5
  "keywords": [
6
6
  "Ethereum",