@metamask-previews/react-data-query 0.2.2-preview-789f7f935 → 0.2.2-preview-eade524

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
@@ -11,13 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  ### Changed
13
13
 
14
- - **BREAKING:** Constrain `createUIQueryClient`'s messenger-like type to match the given data services ([#9475](https://github.com/MetaMask/core/pull/9475))
15
- - The messenger-like object that `createUIQueryClient` takes must minimally support actions or `:cacheUpdated:${hash}` events which are namespaced by the provided data service names.
16
- - If you're passing a messenger, it should "just work" as long as your messenger supports the right actions and events.
17
- - If you're passing a messenger adapter defined in its own variable, you may need to update its type. See `MessengerAdapter` in `packages/react-data-query/src/createUIQueryClient.ts` for an example.
18
- - If you're passing a messenger adapter directly (it is not defined in its own variable), then it should also "just work".
19
- - 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
- - If you are experiencing any errors calling actions through the query client, check to make sure their parameters are JSON-compatible.
21
14
  - Bump `@metamask/utils` from `^11.9.0` to `^11.11.0` ([#9074](https://github.com/MetaMask/core/pull/9074))
22
15
  - Make `react-dom` and `react-native` peer dependencies optional ([#9295](https://github.com/MetaMask/core/pull/9295))
23
16
 
@@ -3,26 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createUIQueryClient = void 0;
4
4
  const utils_1 = require("@metamask/utils");
5
5
  const query_core_1 = require("@tanstack/query-core");
6
+ /**
7
+ * Create a QueryClient queries and subscribes to data services using the messenger.
8
+ *
9
+ * @param dataServices - A list of data services.
10
+ * @param messenger - A messenger adapter.
11
+ * @param config - Optional query client configuration options.
12
+ * @returns The QueryClient.
13
+ */
6
14
  function createUIQueryClient(dataServices, messenger, config = {}) {
7
15
  const subscriptions = new Map();
8
- /**
9
- * Check whether a name is one of the provided data service names.
10
- *
11
- * @param service - The service name to check.
12
- * @returns Whether the service name is configured.
13
- */
14
- function isRecognizedDataService(service) {
15
- return dataServices.some((dataService) => dataService === service);
16
- }
17
- /**
18
- * Check whether an action belongs to one of the provided data services.
19
- *
20
- * @param action - The action name to check.
21
- * @returns Whether the action belongs to a configured data service.
22
- */
23
- function isRecognizedDataServiceAction(action) {
24
- return isRecognizedDataService(action.split(':')[0]);
25
- }
26
16
  /**
27
17
  * Parse a query key to detect a service name.
28
18
  *
@@ -35,7 +25,7 @@ function createUIQueryClient(dataServices, messenger, config = {}) {
35
25
  return null;
36
26
  }
37
27
  const service = action.split(':')[0];
38
- if (!isRecognizedDataService(service)) {
28
+ if (!dataServices.includes(service)) {
39
29
  return null;
40
30
  }
41
31
  return service;
@@ -48,12 +38,9 @@ function createUIQueryClient(dataServices, messenger, config = {}) {
48
38
  queryFn: async (options) => {
49
39
  const { queryKey } = options;
50
40
  const action = queryKey[0];
51
- (0, utils_1.assert)(typeof action === 'string' && isRecognizedDataServiceAction(action), "Queries must call actions on the messenger provided to createUIQueryClient, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.");
52
- const params = options.queryKey.slice(1);
53
- if (options.pageParam !== undefined) {
54
- params.push(options.pageParam);
55
- }
56
- return await messenger.call(action, ...params);
41
+ (0, utils_1.assert)(typeof action === 'string' &&
42
+ dataServices.includes(action.split(':')?.[0]), "Queries must call actions on the messenger provided to createUIQueryClient, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.");
43
+ return await messenger.call(action, ...options.queryKey.slice(1), options.pageParam);
57
44
  },
58
45
  },
59
46
  mutations: config.defaultOptions?.mutations,
@@ -85,9 +72,7 @@ function createUIQueryClient(dataServices, messenger, config = {}) {
85
72
  observerCount === 0 &&
86
73
  hasSubscription) {
87
74
  const subscriptionListener = subscriptions.get(hash);
88
- if (subscriptionListener) {
89
- messenger.unsubscribe(`${service}:cacheUpdated:${hash}`, subscriptionListener);
90
- }
75
+ messenger.unsubscribe(`${service}:cacheUpdated:${hash}`, subscriptionListener);
91
76
  subscriptions.delete(hash);
92
77
  }
93
78
  });
@@ -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 ): Promise<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 { DataServiceGranularCacheUpdatedPayload } from '@metamask/base-data-service';\nimport { assert, Json } 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\ntype SubscriptionCallback = (\n payload: DataServiceGranularCacheUpdatedPayload,\n) => void;\n\ntype JsonSubscriptionCallback = (data: Json) => void;\n\n// TODO: Figure out if we can replace with a better Messenger type\ntype MessengerAdapter = {\n call: (method: string, ...params: Json[]) => Promise<Json | void>;\n subscribe: (method: string, callback: JsonSubscriptionCallback) => void;\n unsubscribe: (method: string, callback: JsonSubscriptionCallback) => void;\n};\n\n/**\n * Create a QueryClient queries and subscribes to data services using the messenger.\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(\n dataServices: string[],\n messenger: MessengerAdapter,\n config: QueryClientConfig = {},\n): QueryClient {\n const subscriptions = new Map<string, SubscriptionCallback>();\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): string | 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 (!dataServices.includes(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' &&\n dataServices.includes(action.split(':')?.[0]),\n \"Queries must call actions on the messenger provided to createUIQueryClient, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.\",\n );\n\n return await messenger.call(\n action,\n ...(options.queryKey.slice(1) as Json[]),\n options.pageParam,\n );\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 = (\n payload: DataServiceGranularCacheUpdatedPayload,\n ): void => {\n if (payload.type === 'removed') {\n return;\n }\n\n hydrate(client, payload.state);\n };\n\n subscriptions.set(hash, cacheListener);\n messenger.subscribe(\n `${service}:cacheUpdated:${hash}`,\n cacheListener as JsonSubscriptionCallback,\n );\n } else if (\n event.type === 'observerRemoved' &&\n observerCount === 0 &&\n hasSubscription\n ) {\n const subscriptionListener = subscriptions.get(hash);\n\n messenger.unsubscribe(\n `${service}:cacheUpdated:${hash}`,\n subscriptionListener as JsonSubscriptionCallback,\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(\n `${service}:invalidateQueries`,\n filters as Json,\n options as Json,\n );\n }),\n );\n\n return originalInvalidate(filters, options);\n };\n\n return client;\n}\n"]}
@@ -1,74 +1,19 @@
1
- import type { DataServiceActions, DataServiceGranularCacheUpdatedEvent, DataServiceGranularCacheUpdatedPayload } from "@metamask/base-data-service";
2
- import type { ActionConstraint, EventConstraint, Messenger } from "@metamask/messenger";
1
+ import { Json } from "@metamask/utils";
3
2
  import { QueryClient, QueryClientConfig } from "@tanstack/query-core";
4
- /**
5
- * Handles granular cache update events emitted by data services.
6
- */
7
- type DataServiceGranularCacheUpdatedHandler = (payload: DataServiceGranularCacheUpdatedPayload) => void;
8
- /**
9
- * A narrower subset of the `Messenger` type, tailored to the messenger
10
- * that `createUIQueryClient` interacts with.
11
- */
12
- type MessengerAdapter<DataServiceName extends string> = {
13
- /**
14
- * Call an action on one of the configured data services.
15
- *
16
- * Note: The parameters are typed as `unknown[]` rather than `Json[]`. For
17
- * concrete messengers, each action's parameters exist as fixed-length tuple,
18
- * and a variadic `Json[]` is not assignable to a fixed-length tuple, so using
19
- * `Json[]` here would reject otherwise valid messengers.
20
- */
21
- call(actionType: `${DataServiceName}:${string}`, ...params: unknown[]): Promise<unknown>;
22
- /**
23
- * Subscribe to a granular cache update event on one of the configured data
24
- * services.
25
- */
26
- subscribe(eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'], handler: DataServiceGranularCacheUpdatedHandler): void;
27
- /**
28
- * Unsubscribe from a granular cache update event on one of the configured
29
- * data services.
30
- */
31
- unsubscribe(eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'], handler: DataServiceGranularCacheUpdatedHandler): void;
3
+ type JsonSubscriptionCallback = (data: Json) => void;
4
+ type MessengerAdapter = {
5
+ call: (method: string, ...params: Json[]) => Promise<Json | void>;
6
+ subscribe: (method: string, callback: JsonSubscriptionCallback) => void;
7
+ unsubscribe: (method: string, callback: JsonSubscriptionCallback) => void;
32
8
  };
33
9
  /**
34
- * Constraints a messenger such that it must minimally support a subset of
35
- * capabilities that the given data services provide. Specifically, it must at
36
- * least allow all data service actions to be called, and it must at least allow
37
- * the `:cacheUpdated:${hash}` event to be subscribed to.
38
- */
39
- type SupportsDataServices<MessengerInstance, DataServiceName extends string> = MessengerInstance extends Messenger<string, infer Action extends ActionConstraint, infer Event extends EventConstraint, any> ? DataServiceActions<DataServiceName>['type'] extends Action['type'] ? DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'] extends Event['type'] ? MessengerInstance : never : never : never;
40
- /**
41
- * Create a QueryClient that queries and subscribes to data services using a
42
- * messenger adapter. This is a messenger-like object that carries some
43
- * constraints:
44
- *
45
- * 1. The messenger must support the `call`, `subscribe` and
46
- * `unsubscribe` methods.
47
- * 2. All action handler arguments and event payloads must be JSON-compatible.
48
- * 3. The messenger must minimally support capabilities that belong to the
49
- * designated data services and must minimally support the
50
- * `:cacheUpdated:${hash}` event of the the designated data services.
10
+ * Create a QueryClient queries and subscribes to data services using the messenger.
51
11
  *
52
12
  * @param dataServices - A list of data services.
53
13
  * @param messenger - A messenger adapter.
54
14
  * @param config - Optional query client configuration options.
55
15
  * @returns The QueryClient.
56
16
  */
57
- export declare function createUIQueryClient<DataServiceNames extends readonly string[]>(dataServices: DataServiceNames, messenger: MessengerAdapter<DataServiceNames[number]>, config?: QueryClientConfig): QueryClient;
58
- /**
59
- * Create a QueryClient that queries and subscribes to data services using a
60
- * messenger. This messenger carries some constraints:
61
- *
62
- * 1. All action handler arguments and event payloads must be JSON-compatible.
63
- * 2. The messenger must minimally support capabilities that belong to the
64
- * designated data services and must minimally support the
65
- * `:cacheUpdated:${hash}` event of the the designated data services.
66
- *
67
- * @param dataServices - A list of data services.
68
- * @param messenger - A messenger.
69
- * @param config - Optional query client configuration options.
70
- * @returns The QueryClient.
71
- */
72
- export declare function createUIQueryClient<DataServiceNames extends readonly string[], MessengerInstance>(dataServices: DataServiceNames, messenger: SupportsDataServices<MessengerInstance, DataServiceNames[number]>, config?: QueryClientConfig): QueryClient;
17
+ export declare function createUIQueryClient(dataServices: string[], messenger: MessengerAdapter, config?: QueryClientConfig): QueryClient;
73
18
  export {};
74
19
  //# sourceMappingURL=createUIQueryClient.d.cts.map
@@ -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,OAAO,CAAC,CAAC;IAEpB;;;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":"AACA,OAAO,EAAU,IAAI,EAAE,wBAAwB;AAC/C,OAAO,EAEL,WAAW,EAMX,iBAAiB,EAClB,6BAA6B;AAM9B,KAAK,wBAAwB,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;AAGrD,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAClE,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,wBAAwB,KAAK,IAAI,CAAC;IACxE,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,wBAAwB,KAAK,IAAI,CAAC;CAC3E,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EAAE,EACtB,SAAS,EAAE,gBAAgB,EAC3B,MAAM,GAAE,iBAAsB,GAC7B,WAAW,CAyIb"}
@@ -1,74 +1,19 @@
1
- import type { DataServiceActions, DataServiceGranularCacheUpdatedEvent, DataServiceGranularCacheUpdatedPayload } from "@metamask/base-data-service";
2
- import type { ActionConstraint, EventConstraint, Messenger } from "@metamask/messenger";
1
+ import { Json } from "@metamask/utils";
3
2
  import { QueryClient, QueryClientConfig } from "@tanstack/query-core";
4
- /**
5
- * Handles granular cache update events emitted by data services.
6
- */
7
- type DataServiceGranularCacheUpdatedHandler = (payload: DataServiceGranularCacheUpdatedPayload) => void;
8
- /**
9
- * A narrower subset of the `Messenger` type, tailored to the messenger
10
- * that `createUIQueryClient` interacts with.
11
- */
12
- type MessengerAdapter<DataServiceName extends string> = {
13
- /**
14
- * Call an action on one of the configured data services.
15
- *
16
- * Note: The parameters are typed as `unknown[]` rather than `Json[]`. For
17
- * concrete messengers, each action's parameters exist as fixed-length tuple,
18
- * and a variadic `Json[]` is not assignable to a fixed-length tuple, so using
19
- * `Json[]` here would reject otherwise valid messengers.
20
- */
21
- call(actionType: `${DataServiceName}:${string}`, ...params: unknown[]): Promise<unknown>;
22
- /**
23
- * Subscribe to a granular cache update event on one of the configured data
24
- * services.
25
- */
26
- subscribe(eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'], handler: DataServiceGranularCacheUpdatedHandler): void;
27
- /**
28
- * Unsubscribe from a granular cache update event on one of the configured
29
- * data services.
30
- */
31
- unsubscribe(eventType: DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'], handler: DataServiceGranularCacheUpdatedHandler): void;
3
+ type JsonSubscriptionCallback = (data: Json) => void;
4
+ type MessengerAdapter = {
5
+ call: (method: string, ...params: Json[]) => Promise<Json | void>;
6
+ subscribe: (method: string, callback: JsonSubscriptionCallback) => void;
7
+ unsubscribe: (method: string, callback: JsonSubscriptionCallback) => void;
32
8
  };
33
9
  /**
34
- * Constraints a messenger such that it must minimally support a subset of
35
- * capabilities that the given data services provide. Specifically, it must at
36
- * least allow all data service actions to be called, and it must at least allow
37
- * the `:cacheUpdated:${hash}` event to be subscribed to.
38
- */
39
- type SupportsDataServices<MessengerInstance, DataServiceName extends string> = MessengerInstance extends Messenger<string, infer Action extends ActionConstraint, infer Event extends EventConstraint, any> ? DataServiceActions<DataServiceName>['type'] extends Action['type'] ? DataServiceGranularCacheUpdatedEvent<DataServiceName>['type'] extends Event['type'] ? MessengerInstance : never : never : never;
40
- /**
41
- * Create a QueryClient that queries and subscribes to data services using a
42
- * messenger adapter. This is a messenger-like object that carries some
43
- * constraints:
44
- *
45
- * 1. The messenger must support the `call`, `subscribe` and
46
- * `unsubscribe` methods.
47
- * 2. All action handler arguments and event payloads must be JSON-compatible.
48
- * 3. The messenger must minimally support capabilities that belong to the
49
- * designated data services and must minimally support the
50
- * `:cacheUpdated:${hash}` event of the the designated data services.
10
+ * Create a QueryClient queries and subscribes to data services using the messenger.
51
11
  *
52
12
  * @param dataServices - A list of data services.
53
13
  * @param messenger - A messenger adapter.
54
14
  * @param config - Optional query client configuration options.
55
15
  * @returns The QueryClient.
56
16
  */
57
- export declare function createUIQueryClient<DataServiceNames extends readonly string[]>(dataServices: DataServiceNames, messenger: MessengerAdapter<DataServiceNames[number]>, config?: QueryClientConfig): QueryClient;
58
- /**
59
- * Create a QueryClient that queries and subscribes to data services using a
60
- * messenger. This messenger carries some constraints:
61
- *
62
- * 1. All action handler arguments and event payloads must be JSON-compatible.
63
- * 2. The messenger must minimally support capabilities that belong to the
64
- * designated data services and must minimally support the
65
- * `:cacheUpdated:${hash}` event of the the designated data services.
66
- *
67
- * @param dataServices - A list of data services.
68
- * @param messenger - A messenger.
69
- * @param config - Optional query client configuration options.
70
- * @returns The QueryClient.
71
- */
72
- export declare function createUIQueryClient<DataServiceNames extends readonly string[], MessengerInstance>(dataServices: DataServiceNames, messenger: SupportsDataServices<MessengerInstance, DataServiceNames[number]>, config?: QueryClientConfig): QueryClient;
17
+ export declare function createUIQueryClient(dataServices: string[], messenger: MessengerAdapter, config?: QueryClientConfig): QueryClient;
73
18
  export {};
74
19
  //# sourceMappingURL=createUIQueryClient.d.mts.map
@@ -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,OAAO,CAAC,CAAC;IAEpB;;;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":"AACA,OAAO,EAAU,IAAI,EAAE,wBAAwB;AAC/C,OAAO,EAEL,WAAW,EAMX,iBAAiB,EAClB,6BAA6B;AAM9B,KAAK,wBAAwB,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;AAGrD,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAClE,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,wBAAwB,KAAK,IAAI,CAAC;IACxE,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,wBAAwB,KAAK,IAAI,CAAC;CAC3E,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EAAE,EACtB,SAAS,EAAE,gBAAgB,EAC3B,MAAM,GAAE,iBAAsB,GAC7B,WAAW,CAyIb"}
@@ -1,25 +1,15 @@
1
1
  import { assert } from "@metamask/utils";
2
2
  import { hydrate, QueryClient, parseFilterArgs } from "@tanstack/query-core";
3
+ /**
4
+ * Create a QueryClient queries and subscribes to data services using the messenger.
5
+ *
6
+ * @param dataServices - A list of data services.
7
+ * @param messenger - A messenger adapter.
8
+ * @param config - Optional query client configuration options.
9
+ * @returns The QueryClient.
10
+ */
3
11
  export function createUIQueryClient(dataServices, messenger, config = {}) {
4
12
  const subscriptions = new Map();
5
- /**
6
- * Check whether a name is one of the provided data service names.
7
- *
8
- * @param service - The service name to check.
9
- * @returns Whether the service name is configured.
10
- */
11
- function isRecognizedDataService(service) {
12
- return dataServices.some((dataService) => dataService === service);
13
- }
14
- /**
15
- * Check whether an action belongs to one of the provided data services.
16
- *
17
- * @param action - The action name to check.
18
- * @returns Whether the action belongs to a configured data service.
19
- */
20
- function isRecognizedDataServiceAction(action) {
21
- return isRecognizedDataService(action.split(':')[0]);
22
- }
23
13
  /**
24
14
  * Parse a query key to detect a service name.
25
15
  *
@@ -32,7 +22,7 @@ export function createUIQueryClient(dataServices, messenger, config = {}) {
32
22
  return null;
33
23
  }
34
24
  const service = action.split(':')[0];
35
- if (!isRecognizedDataService(service)) {
25
+ if (!dataServices.includes(service)) {
36
26
  return null;
37
27
  }
38
28
  return service;
@@ -45,12 +35,9 @@ export function createUIQueryClient(dataServices, messenger, config = {}) {
45
35
  queryFn: async (options) => {
46
36
  const { queryKey } = options;
47
37
  const action = queryKey[0];
48
- assert(typeof action === 'string' && isRecognizedDataServiceAction(action), "Queries must call actions on the messenger provided to createUIQueryClient, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.");
49
- const params = options.queryKey.slice(1);
50
- if (options.pageParam !== undefined) {
51
- params.push(options.pageParam);
52
- }
53
- return await messenger.call(action, ...params);
38
+ assert(typeof action === 'string' &&
39
+ dataServices.includes(action.split(':')?.[0]), "Queries must call actions on the messenger provided to createUIQueryClient, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.");
40
+ return await messenger.call(action, ...options.queryKey.slice(1), options.pageParam);
54
41
  },
55
42
  },
56
43
  mutations: config.defaultOptions?.mutations,
@@ -82,9 +69,7 @@ export function createUIQueryClient(dataServices, messenger, config = {}) {
82
69
  observerCount === 0 &&
83
70
  hasSubscription) {
84
71
  const subscriptionListener = subscriptions.get(hash);
85
- if (subscriptionListener) {
86
- messenger.unsubscribe(`${service}:cacheUpdated:${hash}`, subscriptionListener);
87
- }
72
+ messenger.unsubscribe(`${service}:cacheUpdated:${hash}`, subscriptionListener);
88
73
  subscriptions.delete(hash);
89
74
  }
90
75
  });
@@ -1 +1 @@
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 */\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 ): Promise<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 { DataServiceGranularCacheUpdatedPayload } from '@metamask/base-data-service';\nimport { assert, Json } 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\ntype SubscriptionCallback = (\n payload: DataServiceGranularCacheUpdatedPayload,\n) => void;\n\ntype JsonSubscriptionCallback = (data: Json) => void;\n\n// TODO: Figure out if we can replace with a better Messenger type\ntype MessengerAdapter = {\n call: (method: string, ...params: Json[]) => Promise<Json | void>;\n subscribe: (method: string, callback: JsonSubscriptionCallback) => void;\n unsubscribe: (method: string, callback: JsonSubscriptionCallback) => void;\n};\n\n/**\n * Create a QueryClient queries and subscribes to data services using the messenger.\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(\n dataServices: string[],\n messenger: MessengerAdapter,\n config: QueryClientConfig = {},\n): QueryClient {\n const subscriptions = new Map<string, SubscriptionCallback>();\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): string | 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 (!dataServices.includes(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' &&\n dataServices.includes(action.split(':')?.[0]),\n \"Queries must call actions on the messenger provided to createUIQueryClient, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.\",\n );\n\n return await messenger.call(\n action,\n ...(options.queryKey.slice(1) as Json[]),\n options.pageParam,\n );\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 = (\n payload: DataServiceGranularCacheUpdatedPayload,\n ): void => {\n if (payload.type === 'removed') {\n return;\n }\n\n hydrate(client, payload.state);\n };\n\n subscriptions.set(hash, cacheListener);\n messenger.subscribe(\n `${service}:cacheUpdated:${hash}`,\n cacheListener as JsonSubscriptionCallback,\n );\n } else if (\n event.type === 'observerRemoved' &&\n observerCount === 0 &&\n hasSubscription\n ) {\n const subscriptionListener = subscriptions.get(hash);\n\n messenger.unsubscribe(\n `${service}:cacheUpdated:${hash}`,\n subscriptionListener as JsonSubscriptionCallback,\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(\n `${service}:invalidateQueries`,\n filters as Json,\n options as Json,\n );\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-789f7f935",
3
+ "version": "0.2.2-preview-eade524",
4
4
  "description": "Provides React utilities for consuming data services",
5
5
  "keywords": [
6
6
  "Ethereum",
@@ -47,13 +47,9 @@
47
47
  "lint:tsconfigs": "tsx ../../scripts/lint-tsconfigs/lint-tsconfigs.mts",
48
48
  "lint:tsconfigs:fix": "tsx ../../scripts/lint-tsconfigs/lint-tsconfigs.mts --fix",
49
49
  "since-latest-release": "../../scripts/since-latest-release.sh",
50
- "test": "yarn test:jest && yarn test:types",
51
- "test:clean": "yarn test:jest:clean && yarn test:types",
52
- "test:jest": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter",
53
- "test:jest:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache",
54
- "test:jest:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
55
- "test:types": "tsc --noEmit --project tsconfig.type-tests.json && echo \"Type tests pass\"",
56
- "test:verbose": "yarn test:jest:verbose && yarn test:types",
50
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter",
51
+ "test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache",
52
+ "test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
57
53
  "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
58
54
  },
59
55
  "dependencies": {