@metamask-previews/react-data-query 1.0.0-preview-8bfa290fb → 2.0.0-preview-97d6b86c0
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 +9 -8
- package/dist/{createUIQueryClient.d.mts → createUIQueryClient.d.ts} +3 -3
- package/dist/createUIQueryClient.d.ts.map +1 -0
- package/dist/createUIQueryClient.js +128 -0
- package/dist/createUIQueryClient.js.map +1 -0
- package/dist/{hooks.d.mts → hooks.d.ts} +3 -18
- package/dist/hooks.d.ts.map +1 -0
- package/dist/{hooks.mjs → hooks.js} +4 -21
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +15 -21
- package/dist/createUIQueryClient.cjs +0 -219
- package/dist/createUIQueryClient.cjs.map +0 -1
- package/dist/createUIQueryClient.d.cts +0 -51
- package/dist/createUIQueryClient.d.cts.map +0 -1
- package/dist/createUIQueryClient.d.mts.map +0 -1
- package/dist/createUIQueryClient.mjs +0 -215
- package/dist/createUIQueryClient.mjs.map +0 -1
- package/dist/hooks.cjs +0 -56
- package/dist/hooks.cjs.map +0 -1
- package/dist/hooks.d.cts +0 -36
- package/dist/hooks.d.cts.map +0 -1
- package/dist/hooks.d.mts.map +0 -1
- package/dist/hooks.mjs.map +0 -1
- package/dist/hydrateMutations.cjs +0 -100
- package/dist/hydrateMutations.cjs.map +0 -1
- package/dist/hydrateMutations.d.cts +0 -35
- package/dist/hydrateMutations.d.cts.map +0 -1
- package/dist/hydrateMutations.d.mts +0 -35
- package/dist/hydrateMutations.d.mts.map +0 -1
- package/dist/hydrateMutations.mjs +0 -95
- package/dist/hydrateMutations.mjs.map +0 -1
- package/dist/index.cjs +0 -10
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -3
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.mts +0 -3
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs +0 -3
- package/dist/index.mjs.map +0 -1
- package/dist/loggers.cjs +0 -7
- package/dist/loggers.cjs.map +0 -1
- package/dist/loggers.d.cts +0 -5
- package/dist/loggers.d.cts.map +0 -1
- package/dist/loggers.d.mts +0 -5
- package/dist/loggers.d.mts.map +0 -1
- package/dist/loggers.mjs +0 -4
- package/dist/loggers.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,17 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- Add support for executing mutations by updating `createUIQueryClient` and adding `useMutation` wrapper ([#9324](https://github.com/MetaMask/core/pull/9324))
|
|
13
|
-
- Provided an action in your data service uses `BaseDataService.executeMutation` to make the request instead of `fetchQuery`, you can now use `useMutation` in your UI files via the UI query client and pass a reference to the action as the mutation key.
|
|
14
|
-
- Like `fetchQuery` and `fetchInfiniteQuery`, the `useMutation` wrapper disables retries by default and enforces that `mutationKey` matches the same thing that `executeMutation` takes.
|
|
15
|
-
- `createUIQueryClient` now tags each mutation it creates with a unique `globalId` (stored on the mutation's `meta`) and passes it to the data service action as the trailing argument. It uses this `globalId` to update the exact UI mutation that a `:cacheUpdated` event corresponds to, so mutations sharing a `mutationKey` no longer clobber one another, and mutations that use a custom `mutationFn` are left untouched.
|
|
10
|
+
## [2.0.0]
|
|
16
11
|
|
|
17
12
|
### Changed
|
|
18
13
|
|
|
19
|
-
-
|
|
14
|
+
- **BREAKING:** Drop CommonJS support ([#9536](https://github.com/MetaMask/core/pull/9536))
|
|
15
|
+
- This package is now ESM-only, but can still be used in CommonJS projects via `require(esm)` in modern Node.js versions (22+), or dynamic imports in older Node.js versions.
|
|
16
|
+
- **BREAKING:** Bump minimum Node.js version to 22 ([#9976](https://github.com/MetaMask/core/pull/9976))
|
|
17
|
+
- **BREAKING:** Bump TypeScript target to ES2022 ([#10019](https://github.com/MetaMask/core/pull/10019))
|
|
18
|
+
- This package now ships ES2022 code, requiring a compatible modern environment or bundler configuration to consume.
|
|
20
19
|
- Bump `@metamask/utils` from `^11.11.0` to `^11.12.0` ([#10076](https://github.com/MetaMask/core/pull/10076))
|
|
20
|
+
- Bump `@metamask/base-data-service` from `^1.0.0` to `^2.0.0` ([#10160](https://github.com/MetaMask/core/pull/10160))
|
|
21
21
|
|
|
22
22
|
## [1.0.0]
|
|
23
23
|
|
|
@@ -66,7 +66,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
66
66
|
|
|
67
67
|
- Initial release ([#8039](https://github.com/MetaMask/core/pull/8039), [#8292](https://github.com/MetaMask/core/pull/8292))
|
|
68
68
|
|
|
69
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/react-data-query@
|
|
69
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/react-data-query@2.0.0...HEAD
|
|
70
|
+
[2.0.0]: https://github.com/MetaMask/core/compare/@metamask/react-data-query@1.0.0...@metamask/react-data-query@2.0.0
|
|
70
71
|
[1.0.0]: https://github.com/MetaMask/core/compare/@metamask/react-data-query@0.2.2...@metamask/react-data-query@1.0.0
|
|
71
72
|
[0.2.2]: https://github.com/MetaMask/core/compare/@metamask/react-data-query@0.2.1...@metamask/react-data-query@0.2.2
|
|
72
73
|
[0.2.1]: https://github.com/MetaMask/core/compare/@metamask/react-data-query@0.2.0...@metamask/react-data-query@0.2.1
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { DataServiceGranularCacheUpdatedEvent, DataServiceGranularCacheUpdatedPayload } from
|
|
2
|
-
import { QueryClient, QueryClientConfig } from
|
|
1
|
+
import type { DataServiceGranularCacheUpdatedEvent, DataServiceGranularCacheUpdatedPayload } from '@metamask/base-data-service';
|
|
2
|
+
import { QueryClient, QueryClientConfig } from '@tanstack/query-core';
|
|
3
3
|
/**
|
|
4
4
|
* Handles granular cache update events emitted by data services.
|
|
5
5
|
*/
|
|
@@ -48,4 +48,4 @@ type MessengerAdapter<DataServiceName extends string> = {
|
|
|
48
48
|
*/
|
|
49
49
|
export declare function createUIQueryClient<DataServiceNames extends readonly string[]>(dataServices: DataServiceNames, messenger: MessengerAdapter<DataServiceNames[number]>, config?: QueryClientConfig): QueryClient;
|
|
50
50
|
export {};
|
|
51
|
-
//# sourceMappingURL=createUIQueryClient.d.
|
|
51
|
+
//# sourceMappingURL=createUIQueryClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createUIQueryClient.d.ts","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oCAAoC,EACpC,sCAAsC,EACvC,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAEL,WAAW,EAIX,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;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;;;;;;;;;;;;;;;;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,GAAE,iBAAsB,GAC7B,WAAW,CA8Jb"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { assert } from '@metamask/utils';
|
|
2
|
+
import { hydrate, QueryClient, } from '@tanstack/query-core';
|
|
3
|
+
/**
|
|
4
|
+
* Create a QueryClient that queries and subscribes to data services using a
|
|
5
|
+
* messenger adapter. This is a messenger-like object that carries some
|
|
6
|
+
* constraints:
|
|
7
|
+
*
|
|
8
|
+
* 1. The messenger must support the `call`, `subscribe` and
|
|
9
|
+
* `unsubscribe` methods.
|
|
10
|
+
* 2. All action handler arguments and event payloads must be JSON-compatible.
|
|
11
|
+
* 3. The messenger must minimally support actions that are scoped to the
|
|
12
|
+
* designated data services and must minimally support the
|
|
13
|
+
* `:cacheUpdated:${hash}` event scoped to the designated data services.
|
|
14
|
+
*
|
|
15
|
+
* @param dataServices - A list of data services.
|
|
16
|
+
* @param messenger - A messenger adapter.
|
|
17
|
+
* @param config - Optional query client configuration options.
|
|
18
|
+
* @returns The QueryClient.
|
|
19
|
+
*/
|
|
20
|
+
export function createUIQueryClient(dataServices, messenger, config = {}) {
|
|
21
|
+
const subscriptions = new Map();
|
|
22
|
+
/**
|
|
23
|
+
* Check whether a name is one of the provided data service names.
|
|
24
|
+
*
|
|
25
|
+
* @param service - The service name to check.
|
|
26
|
+
* @returns Whether the service name is configured.
|
|
27
|
+
*/
|
|
28
|
+
function isRecognizedDataService(service) {
|
|
29
|
+
return dataServices.some((dataService) => dataService === service);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Check whether an action belongs to one of the provided data services.
|
|
33
|
+
*
|
|
34
|
+
* @param action - The action name to check.
|
|
35
|
+
* @returns Whether the action belongs to a configured data service.
|
|
36
|
+
*/
|
|
37
|
+
function isRecognizedDataServiceAction(action) {
|
|
38
|
+
return isRecognizedDataService(action.split(':')[0]);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parse a query key to detect a service name.
|
|
42
|
+
*
|
|
43
|
+
* @param queryKey - The query key.
|
|
44
|
+
* @returns The service name if it parsing succeeded, otherwise null.
|
|
45
|
+
*/
|
|
46
|
+
function parseQueryKey(queryKey) {
|
|
47
|
+
const action = queryKey[0];
|
|
48
|
+
if (typeof action !== 'string') {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const service = action.split(':')[0];
|
|
52
|
+
if (!isRecognizedDataService(service)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return service;
|
|
56
|
+
}
|
|
57
|
+
const client = new QueryClient({
|
|
58
|
+
...config,
|
|
59
|
+
defaultOptions: {
|
|
60
|
+
queries: {
|
|
61
|
+
...config.defaultOptions?.queries,
|
|
62
|
+
queryFn: async (options) => {
|
|
63
|
+
const { queryKey } = options;
|
|
64
|
+
const action = queryKey[0];
|
|
65
|
+
assert(typeof action === 'string' && isRecognizedDataServiceAction(action), "Queries must call actions on the messenger provided to createUIQueryClient, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.");
|
|
66
|
+
const params = options.queryKey.slice(1);
|
|
67
|
+
if (options.pageParam !== undefined) {
|
|
68
|
+
params.push(options.pageParam);
|
|
69
|
+
}
|
|
70
|
+
return await messenger.call(action, ...params);
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
mutations: config.defaultOptions?.mutations,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
const cache = client.getQueryCache();
|
|
77
|
+
cache.subscribe((event) => {
|
|
78
|
+
const { query } = event;
|
|
79
|
+
const hash = query.queryHash;
|
|
80
|
+
const hasSubscription = subscriptions.has(hash);
|
|
81
|
+
const observerCount = query.getObserversCount();
|
|
82
|
+
const service = parseQueryKey(query.queryKey);
|
|
83
|
+
if (!service) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!hasSubscription &&
|
|
87
|
+
event.type === 'observerAdded' &&
|
|
88
|
+
observerCount === 1) {
|
|
89
|
+
const cacheListener = (payload) => {
|
|
90
|
+
if (payload.type === 'removed') {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
hydrate(client, payload.state);
|
|
94
|
+
};
|
|
95
|
+
subscriptions.set(hash, cacheListener);
|
|
96
|
+
messenger.subscribe(`${service}:cacheUpdated:${hash}`, cacheListener);
|
|
97
|
+
}
|
|
98
|
+
else if (event.type === 'observerRemoved' &&
|
|
99
|
+
observerCount === 0 &&
|
|
100
|
+
hasSubscription) {
|
|
101
|
+
const subscriptionListener = subscriptions.get(hash);
|
|
102
|
+
// We can't write a test for this, as it's unrealistic
|
|
103
|
+
// (we just need a check to appease TypeScript).
|
|
104
|
+
// istanbul ignore next
|
|
105
|
+
if (subscriptionListener) {
|
|
106
|
+
messenger.unsubscribe(`${service}:cacheUpdated:${hash}`, subscriptionListener);
|
|
107
|
+
}
|
|
108
|
+
subscriptions.delete(hash);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
// Override invalidateQueries to ensure the data service is invalidated as well.
|
|
112
|
+
const originalInvalidate = client.invalidateQueries.bind(client);
|
|
113
|
+
client.invalidateQueries = async (filters, options) => {
|
|
114
|
+
const queries = client.getQueryCache().findAll(filters);
|
|
115
|
+
const services = [
|
|
116
|
+
...new Set(queries.map((query) => parseQueryKey(query.queryKey))),
|
|
117
|
+
];
|
|
118
|
+
await Promise.all(services.map(async (service) => {
|
|
119
|
+
if (!service) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return messenger.call(`${service}:invalidateQueries`, filters, options);
|
|
123
|
+
}));
|
|
124
|
+
return originalInvalidate(filters, options);
|
|
125
|
+
};
|
|
126
|
+
return client;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=createUIQueryClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createUIQueryClient.js","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DataServiceGranularCacheUpdatedEvent,\n DataServiceGranularCacheUpdatedPayload,\n} from '@metamask/base-data-service';\nimport { assert } from '@metamask/utils';\nimport {\n hydrate,\n QueryClient,\n InvalidateQueryFilters,\n InvalidateOptions,\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 * 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 actions that are scoped to the\n * designated data services and must minimally support the\n * `:cacheUpdated:${hash}` event scoped to 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 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 // We can't write a test for this, as it's unrealistic\n // (we just need a check to appease TypeScript).\n // istanbul ignore next\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 client.invalidateQueries = async (\n filters?: InvalidateQueryFilters,\n options?: InvalidateOptions,\n ): Promise<void> => {\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,10 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* We provide re-exports of the underlying TanStack Query hooks with narrower types,
|
|
4
|
-
* removing `staleTime` and `queryFn` which aren't useful when using data services.
|
|
5
|
-
*/
|
|
6
|
-
import { QueryKey, MutationKey } from "@metamask/base-data-service";
|
|
7
|
-
import { OmitKeyof, UseQueryOptions, InitialDataFunction, NonUndefinedGuard, UseInfiniteQueryOptions, UseQueryResult, UseInfiniteQueryResult, DefaultError, InfiniteData, UseMutationOptions, UseMutationResult } from "@tanstack/react-query";
|
|
1
|
+
import { QueryKey } from '@metamask/base-data-service';
|
|
2
|
+
import { OmitKeyof, UseQueryOptions, InitialDataFunction, NonUndefinedGuard, UseInfiniteQueryOptions, UseQueryResult, UseInfiniteQueryResult, DefaultError, InfiniteData } from '@tanstack/react-query';
|
|
8
3
|
/**
|
|
9
4
|
* Consume a query from a data service.
|
|
10
5
|
*
|
|
@@ -23,14 +18,4 @@ export declare function useQuery<TQueryFnData = unknown, TError = DefaultError,
|
|
|
23
18
|
* @returns The paginated query results.
|
|
24
19
|
*/
|
|
25
20
|
export declare function useInfiniteQuery<TQueryFnData = unknown, TError = DefaultError, TData = InfiniteData<TQueryFnData>, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown>(options: OmitKeyof<UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>, 'staleTime' | 'queryFn'>): UseInfiniteQueryResult<TData, TError>;
|
|
26
|
-
|
|
27
|
-
* Execute a mutation through a data service.
|
|
28
|
-
*
|
|
29
|
-
* @param options - The mutation options. Keep in mind that `mutationFn` is not supported
|
|
30
|
-
* when executing mutations through data services.
|
|
31
|
-
* @returns The result of the mutation.
|
|
32
|
-
*/
|
|
33
|
-
export declare function useMutation<TData = unknown, TError = DefaultError, TVariables = void, TOnMutateResult = unknown, TMutationKey extends MutationKey = MutationKey>(options: OmitKeyof<UseMutationOptions<TData, TError, TVariables, TOnMutateResult>, 'mutationKey' | 'mutationFn'> & {
|
|
34
|
-
mutationKey: TMutationKey;
|
|
35
|
-
}): UseMutationResult<TData, TError, TVariables, TOnMutateResult>;
|
|
36
|
-
//# sourceMappingURL=hooks.d.mts.map
|
|
21
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACvD,OAAO,EAGL,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,cAAc,EACd,sBAAsB,EACtB,YAAY,EACZ,YAAY,EACb,MAAM,uBAAuB,CAAC;AAY/B;;;;;;GAMG;AACH,wBAAgB,QAAQ,CACtB,YAAY,GAAG,OAAO,EACtB,MAAM,GAAG,YAAY,EACrB,KAAK,GAAG,YAAY,EACpB,SAAS,SAAS,QAAQ,GAAG,QAAQ,EAErC,OAAO,EAAE,SAAS,CAChB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,EACvD,aAAa,GAAG,WAAW,GAAG,SAAS,CACxC,GAAG;IACF,WAAW,CAAC,EACR,SAAS,GACT,mBAAmB,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC,GACpD,iBAAiB,CAAC,YAAY,CAAC,CAAC;CACrC,GACA,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAE/B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,GAAG,OAAO,EACtB,MAAM,GAAG,YAAY,EACrB,KAAK,GAAG,YAAY,CAAC,YAAY,CAAC,EAClC,SAAS,SAAS,QAAQ,GAAG,QAAQ,EACrC,UAAU,GAAG,OAAO,EAEpB,OAAO,EAAE,SAAS,CAChB,uBAAuB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,EAC3E,WAAW,GAAG,SAAS,CACxB,GACA,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,CAKvC"}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
+
import { useQuery as useQueryTanStack, useInfiniteQuery as useInfiniteQueryTanStack, } from '@tanstack/react-query';
|
|
1
2
|
/**
|
|
2
|
-
* @file
|
|
3
3
|
* We provide re-exports of the underlying TanStack Query hooks with narrower types,
|
|
4
4
|
* removing `staleTime` and `queryFn` which aren't useful when using data services.
|
|
5
5
|
*/
|
|
6
|
-
import { useQuery as useQueryFromTanStack, useInfiniteQuery as useInfiniteQueryFromTanStack, useMutation as useMutationFromTanStack } from "@tanstack/react-query";
|
|
7
6
|
const DATA_SERVICE_QUERY_DEFAULTS = {
|
|
8
7
|
staleTime: 0,
|
|
9
8
|
retry: false,
|
|
10
9
|
};
|
|
11
|
-
const DATA_SERVICE_MUTATION_DEFAULTS = {
|
|
12
|
-
retry: false,
|
|
13
|
-
};
|
|
14
10
|
/**
|
|
15
11
|
* Consume a query from a data service.
|
|
16
12
|
*
|
|
@@ -19,7 +15,7 @@ const DATA_SERVICE_MUTATION_DEFAULTS = {
|
|
|
19
15
|
* @returns The query results.
|
|
20
16
|
*/
|
|
21
17
|
export function useQuery(options) {
|
|
22
|
-
return
|
|
18
|
+
return useQueryTanStack({ ...DATA_SERVICE_QUERY_DEFAULTS, ...options });
|
|
23
19
|
}
|
|
24
20
|
/**
|
|
25
21
|
* Consume a paginated query from a data service.
|
|
@@ -29,22 +25,9 @@ export function useQuery(options) {
|
|
|
29
25
|
* @returns The paginated query results.
|
|
30
26
|
*/
|
|
31
27
|
export function useInfiniteQuery(options) {
|
|
32
|
-
return
|
|
28
|
+
return useInfiniteQueryTanStack({
|
|
33
29
|
...DATA_SERVICE_QUERY_DEFAULTS,
|
|
34
30
|
...options,
|
|
35
31
|
});
|
|
36
32
|
}
|
|
37
|
-
|
|
38
|
-
* Execute a mutation through a data service.
|
|
39
|
-
*
|
|
40
|
-
* @param options - The mutation options. Keep in mind that `mutationFn` is not supported
|
|
41
|
-
* when executing mutations through data services.
|
|
42
|
-
* @returns The result of the mutation.
|
|
43
|
-
*/
|
|
44
|
-
export function useMutation(options) {
|
|
45
|
-
return useMutationFromTanStack({
|
|
46
|
-
...DATA_SERVICE_MUTATION_DEFAULTS,
|
|
47
|
-
...options,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
//# sourceMappingURL=hooks.mjs.map
|
|
33
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,IAAI,gBAAgB,EAC5B,gBAAgB,IAAI,wBAAwB,GAU7C,MAAM,uBAAuB,CAAC;AAE/B;;;GAGG;AAEH,MAAM,2BAA2B,GAAG;IAClC,SAAS,EAAE,CAAC;IACZ,KAAK,EAAE,KAAK;CACb,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAMtB,OAQC;IAED,OAAO,gBAAgB,CAAC,EAAE,GAAG,2BAA2B,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAO9B,OAGC;IAED,OAAO,wBAAwB,CAAC;QAC9B,GAAG,2BAA2B;QAC9B,GAAG,OAAO;KACX,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { QueryKey } from '@metamask/base-data-service';\nimport {\n useQuery as useQueryTanStack,\n useInfiniteQuery as useInfiniteQueryTanStack,\n OmitKeyof,\n UseQueryOptions,\n InitialDataFunction,\n NonUndefinedGuard,\n UseInfiniteQueryOptions,\n UseQueryResult,\n UseInfiniteQueryResult,\n DefaultError,\n InfiniteData,\n} from '@tanstack/react-query';\n\n/**\n * We provide re-exports of the underlying TanStack Query hooks with narrower types,\n * removing `staleTime` and `queryFn` which aren't useful when using data services.\n */\n\nconst DATA_SERVICE_QUERY_DEFAULTS = {\n staleTime: 0,\n retry: false,\n};\n\n/**\n * Consume a query from a data service.\n *\n * @param options - The query options. Keep in mind that `staleTime` and `queryFn` are not supported\n * when querying data services.\n * @returns The query results.\n */\nexport function useQuery<\n TQueryFnData = unknown,\n TError = DefaultError,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n>(\n options: OmitKeyof<\n UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n 'initialData' | 'staleTime' | 'queryFn'\n > & {\n initialData?:\n | undefined\n | InitialDataFunction<NonUndefinedGuard<TQueryFnData>>\n | NonUndefinedGuard<TQueryFnData>;\n },\n): UseQueryResult<TData, TError> {\n return useQueryTanStack({ ...DATA_SERVICE_QUERY_DEFAULTS, ...options });\n}\n\n/**\n * Consume a paginated query from a data service.\n *\n * @param options - The query options. Keep in mind that `staleTime` and `queryFn` are not supported\n * when querying data services.\n * @returns The paginated query results.\n */\nexport function useInfiniteQuery<\n TQueryFnData = unknown,\n TError = DefaultError,\n TData = InfiniteData<TQueryFnData>,\n TQueryKey extends QueryKey = QueryKey,\n TPageParam = unknown,\n>(\n options: OmitKeyof<\n UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,\n 'staleTime' | 'queryFn'\n >,\n): UseInfiniteQueryResult<TData, TError> {\n return useInfiniteQueryTanStack({\n ...DATA_SERVICE_QUERY_DEFAULTS,\n ...options,\n });\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC","sourcesContent":["export { createUIQueryClient } from './createUIQueryClient.js';\nexport { useQuery, useInfiniteQuery } from './hooks.js';\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/react-data-query",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-preview-97d6b86c0",
|
|
4
4
|
"description": "Provides React utilities for consuming data services",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ethereum",
|
|
@@ -18,19 +18,12 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"dist/"
|
|
20
20
|
],
|
|
21
|
+
"type": "module",
|
|
21
22
|
"sideEffects": false,
|
|
22
|
-
"main": "./dist/index.cjs",
|
|
23
|
-
"types": "./dist/index.d.cts",
|
|
24
23
|
"exports": {
|
|
25
24
|
".": {
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
"default": "./dist/index.mjs"
|
|
29
|
-
},
|
|
30
|
-
"require": {
|
|
31
|
-
"types": "./dist/index.d.cts",
|
|
32
|
-
"default": "./dist/index.cjs"
|
|
33
|
-
}
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
34
27
|
},
|
|
35
28
|
"./package.json": "./package.json"
|
|
36
29
|
},
|
|
@@ -39,9 +32,11 @@
|
|
|
39
32
|
"registry": "https://registry.npmjs.org/"
|
|
40
33
|
},
|
|
41
34
|
"scripts": {
|
|
42
|
-
"build": "
|
|
43
|
-
"build:all": "
|
|
35
|
+
"build": "tsc --project tsconfig.build.json",
|
|
36
|
+
"build:all": "tsc --build tsconfig.build.json --verbose",
|
|
37
|
+
"build:clean": "yarn build:only-clean && yarn build",
|
|
44
38
|
"build:docs": "typedoc",
|
|
39
|
+
"build:only-clean": "rimraf './dist' './tsconfig.build.tsbuildinfo'",
|
|
45
40
|
"changelog:update": "../../scripts/update-changelog.sh @metamask/react-data-query",
|
|
46
41
|
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/react-data-query",
|
|
47
42
|
"lint:tsconfigs": "tsx ../../scripts/lint-tsconfigs/lint-tsconfigs.mts",
|
|
@@ -53,25 +48,24 @@
|
|
|
53
48
|
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
|
|
54
49
|
},
|
|
55
50
|
"dependencies": {
|
|
56
|
-
"@metamask/base-data-service": "^
|
|
51
|
+
"@metamask/base-data-service": "^2.0.0",
|
|
57
52
|
"@metamask/utils": "^11.12.0",
|
|
58
53
|
"@tanstack/query-core": "^5.62.16",
|
|
59
|
-
"@tanstack/react-query": "^5.62.16"
|
|
60
|
-
"uuid": "^8.3.2"
|
|
54
|
+
"@tanstack/react-query": "^5.62.16"
|
|
61
55
|
},
|
|
62
56
|
"devDependencies": {
|
|
63
57
|
"@metamask/auto-changelog": "^6.1.0",
|
|
64
|
-
"@metamask/messenger": "^
|
|
65
|
-
"@ts-bridge/cli": "^0.6.4",
|
|
58
|
+
"@metamask/messenger": "^3.0.0",
|
|
66
59
|
"@types/jest": "^30.0.0",
|
|
60
|
+
"@typescript/native": "npm:typescript@^7.0.2",
|
|
67
61
|
"deepmerge": "^4.2.2",
|
|
68
62
|
"jest": "^30.4.2",
|
|
69
|
-
"
|
|
63
|
+
"rimraf": "^5.0.5",
|
|
70
64
|
"ts-jest": "^29.4.11",
|
|
71
65
|
"tsx": "^4.20.5",
|
|
72
66
|
"typedoc": "^0.25.13",
|
|
73
67
|
"typedoc-plugin-missing-exports": "^2.0.0",
|
|
74
|
-
"typescript": "
|
|
68
|
+
"typescript": "npm:@typescript/typescript6@^6.0.2"
|
|
75
69
|
},
|
|
76
70
|
"peerDependencies": {
|
|
77
71
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
@@ -87,6 +81,6 @@
|
|
|
87
81
|
}
|
|
88
82
|
},
|
|
89
83
|
"engines": {
|
|
90
|
-
"node": "^
|
|
84
|
+
"node": "^22.14.0 || ^24"
|
|
91
85
|
}
|
|
92
86
|
}
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createUIQueryClient = void 0;
|
|
4
|
-
const utils_1 = require("@metamask/utils");
|
|
5
|
-
const query_core_1 = require("@tanstack/query-core");
|
|
6
|
-
const uuid_1 = require("uuid");
|
|
7
|
-
const hydrateMutations_js_1 = require("./hydrateMutations.cjs");
|
|
8
|
-
const loggers_js_1 = require("./loggers.cjs");
|
|
9
|
-
const log = (0, loggers_js_1.createModuleLogger)(loggers_js_1.projectLogger, 'createUIQueryClient');
|
|
10
|
-
/**
|
|
11
|
-
* Create a QueryClient that queries and subscribes to data services using a
|
|
12
|
-
* messenger adapter. This is a messenger-like object that carries some
|
|
13
|
-
* constraints:
|
|
14
|
-
*
|
|
15
|
-
* 1. The messenger must support the `call`, `subscribe` and
|
|
16
|
-
* `unsubscribe` methods.
|
|
17
|
-
* 2. All action handler arguments and event payloads must be JSON-compatible.
|
|
18
|
-
* 3. The messenger must minimally support actions that are scoped to the
|
|
19
|
-
* designated data services and must minimally support the
|
|
20
|
-
* `:cacheUpdated:${hash}` event scoped to the designated data services.
|
|
21
|
-
*
|
|
22
|
-
* @param dataServices - A list of data services.
|
|
23
|
-
* @param messenger - A messenger adapter.
|
|
24
|
-
* @param config - Optional query client configuration options.
|
|
25
|
-
* @returns The QueryClient.
|
|
26
|
-
*/
|
|
27
|
-
function createUIQueryClient(dataServices, messenger, config = {}) {
|
|
28
|
-
const subscriptions = new Map();
|
|
29
|
-
// Tracks how many mutation observers are currently relying on each mutation
|
|
30
|
-
// key's cache subscription. Unlike queries, a `Mutation` does not expose its
|
|
31
|
-
// observer count publicly, so we count observers ourselves and only tear down
|
|
32
|
-
// the messenger subscription once the last observer for a key is removed.
|
|
33
|
-
const mutationObserverCounts = new Map();
|
|
34
|
-
/**
|
|
35
|
-
* Check whether a name is one of the provided data service names.
|
|
36
|
-
*
|
|
37
|
-
* @param service - The service name to check.
|
|
38
|
-
* @returns Whether the service name is configured.
|
|
39
|
-
*/
|
|
40
|
-
function isRecognizedDataService(service) {
|
|
41
|
-
return dataServices.some((dataService) => dataService === service);
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Check whether an action belongs to one of the provided data services.
|
|
45
|
-
*
|
|
46
|
-
* @param action - The action name to check.
|
|
47
|
-
* @returns Whether the action belongs to a configured data service.
|
|
48
|
-
*/
|
|
49
|
-
function isRecognizedDataServiceAction(action) {
|
|
50
|
-
return isRecognizedDataService(action.split(':')[0]);
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Parse a query key to detect a service name.
|
|
54
|
-
*
|
|
55
|
-
* @param queryKey - The query key.
|
|
56
|
-
* @returns The service name if it parsing succeeded, otherwise null.
|
|
57
|
-
*/
|
|
58
|
-
function parseQueryKey(queryKey) {
|
|
59
|
-
const action = queryKey[0];
|
|
60
|
-
if (typeof action !== 'string') {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
const service = action.split(':')[0];
|
|
64
|
-
if (!isRecognizedDataService(service)) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
return service;
|
|
68
|
-
}
|
|
69
|
-
const client = new query_core_1.QueryClient({
|
|
70
|
-
...config,
|
|
71
|
-
defaultOptions: {
|
|
72
|
-
queries: {
|
|
73
|
-
...config.defaultOptions?.queries,
|
|
74
|
-
queryFn: async (options) => {
|
|
75
|
-
const { queryKey } = options;
|
|
76
|
-
const action = queryKey[0];
|
|
77
|
-
(0, utils_1.assert)(typeof action === 'string' && isRecognizedDataServiceAction(action), "You must pass a `queryKey` that calls an action on the messenger provided to `createUIQueryClient`, e.g. `queryKey: ['ExampleDataService:getAssets', ...]`.");
|
|
78
|
-
const params = options.queryKey.slice(1);
|
|
79
|
-
if (options.pageParam !== undefined) {
|
|
80
|
-
params.push(options.pageParam);
|
|
81
|
-
}
|
|
82
|
-
return await messenger.call(action, ...params);
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
mutations: config.defaultOptions?.mutations,
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
const queryCache = client.getQueryCache();
|
|
89
|
-
queryCache.subscribe((event) => {
|
|
90
|
-
const { query } = event;
|
|
91
|
-
const hash = query.queryHash;
|
|
92
|
-
const hasSubscription = subscriptions.has(hash);
|
|
93
|
-
const observerCount = query.getObserversCount();
|
|
94
|
-
const service = parseQueryKey(query.queryKey);
|
|
95
|
-
if (!service) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
if (!hasSubscription &&
|
|
99
|
-
event.type === 'observerAdded' &&
|
|
100
|
-
observerCount === 1) {
|
|
101
|
-
const cacheListener = (payload) => {
|
|
102
|
-
if (payload.type === 'removed') {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
log('Hydrating with', payload.state);
|
|
106
|
-
(0, query_core_1.hydrate)(client, payload.state);
|
|
107
|
-
};
|
|
108
|
-
subscriptions.set(hash, cacheListener);
|
|
109
|
-
messenger.subscribe(`${service}:cacheUpdated:${hash}`, cacheListener);
|
|
110
|
-
}
|
|
111
|
-
else if (event.type === 'observerRemoved' &&
|
|
112
|
-
observerCount === 0 &&
|
|
113
|
-
hasSubscription) {
|
|
114
|
-
const subscriptionListener = subscriptions.get(hash);
|
|
115
|
-
// We can't write a test for this, as it's unrealistic
|
|
116
|
-
// (we just need a check to appease TypeScript).
|
|
117
|
-
// istanbul ignore next
|
|
118
|
-
if (subscriptionListener) {
|
|
119
|
-
messenger.unsubscribe(`${service}:cacheUpdated:${hash}`, subscriptionListener);
|
|
120
|
-
}
|
|
121
|
-
subscriptions.delete(hash);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
const mutationCache = client.getMutationCache();
|
|
125
|
-
mutationCache.subscribe((event) => {
|
|
126
|
-
const { mutation } = event;
|
|
127
|
-
if (!mutation?.options.mutationKey) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const hash = (0, query_core_1.hashKey)(mutation.options.mutationKey);
|
|
131
|
-
const hasSubscription = subscriptions.has(hash);
|
|
132
|
-
const service = parseQueryKey(mutation.options.mutationKey);
|
|
133
|
-
if (!service) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
log(`[mutationCache subscription] Received event "${event.type}". Details:`, event.mutation);
|
|
137
|
-
if (event.type === 'observerAdded') {
|
|
138
|
-
mutationObserverCounts.set(hash, (mutationObserverCounts.get(hash) ?? 0) + 1);
|
|
139
|
-
log('[mutationCache subscription] hasSubscription =', hasSubscription);
|
|
140
|
-
if (!hasSubscription) {
|
|
141
|
-
const cacheListener = (payload) => {
|
|
142
|
-
log(`[mutationCache subscription] cacheUpdated:${hash} emitted`, payload);
|
|
143
|
-
if (payload.type === 'removed') {
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
(0, hydrateMutations_js_1.hydrateMutations)(client, payload.state);
|
|
147
|
-
};
|
|
148
|
-
subscriptions.set(hash, cacheListener);
|
|
149
|
-
messenger.subscribe(`${service}:cacheUpdated:${hash}`, cacheListener);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
else if (event.type === 'observerRemoved' && hasSubscription) {
|
|
153
|
-
// We can assume that if an observed mutation is removed, it must have
|
|
154
|
-
// first been added; and that when it was added, the observer count was
|
|
155
|
-
// initialized. (There's no real way to test the alternative, anyway.)
|
|
156
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
157
|
-
const remainingObservers = mutationObserverCounts.get(hash) - 1;
|
|
158
|
-
if (remainingObservers > 0) {
|
|
159
|
-
mutationObserverCounts.set(hash, remainingObservers);
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
mutationObserverCounts.delete(hash);
|
|
163
|
-
const subscriptionListener = subscriptions.get(hash);
|
|
164
|
-
// A subscription always has a listener, since both are set together when
|
|
165
|
-
// the first observer is added.
|
|
166
|
-
// istanbul ignore next
|
|
167
|
-
if (subscriptionListener) {
|
|
168
|
-
messenger.unsubscribe(`${service}:cacheUpdated:${hash}`, subscriptionListener);
|
|
169
|
-
}
|
|
170
|
-
subscriptions.delete(hash);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
// Override invalidateQueries to ensure the data service is invalidated as well.
|
|
174
|
-
const originalInvalidate = client.invalidateQueries.bind(client);
|
|
175
|
-
client.invalidateQueries = async (filters, options) => {
|
|
176
|
-
const queries = client.getQueryCache().findAll(filters);
|
|
177
|
-
const services = [
|
|
178
|
-
...new Set(queries.map((query) => parseQueryKey(query.queryKey))),
|
|
179
|
-
];
|
|
180
|
-
await Promise.all(services.map(async (service) => {
|
|
181
|
-
if (!service) {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
return messenger.call(`${service}:invalidateQueries`, filters, options);
|
|
185
|
-
}));
|
|
186
|
-
return originalInvalidate(filters, options);
|
|
187
|
-
};
|
|
188
|
-
// Override defaultMutationOptions to check for mutationKey if mutationFn is
|
|
189
|
-
// not provided.
|
|
190
|
-
const originalDefaultMutationOptions = client.defaultMutationOptions.bind(client);
|
|
191
|
-
client.defaultMutationOptions = (options) => {
|
|
192
|
-
const defaultedOptions = originalDefaultMutationOptions(options);
|
|
193
|
-
// Only mutations that fall back to the data-service default `mutationFn`
|
|
194
|
-
// need a `globalId` to correlate the UI and service caches. A mutation with
|
|
195
|
-
// a custom `mutationFn` never reaches a data service, so we leave it alone.
|
|
196
|
-
if (defaultedOptions.mutationFn === undefined) {
|
|
197
|
-
// Generate the `globalId` once and memoize it on `meta` so it stays
|
|
198
|
-
// stable across the mutation's lifetime and can be matched against
|
|
199
|
-
// incoming cache updates.
|
|
200
|
-
const globalId = (0, hydrateMutations_js_1.readGlobalId)(defaultedOptions.meta) ?? (0, uuid_1.v4)();
|
|
201
|
-
defaultedOptions.meta = { ...defaultedOptions.meta, globalId };
|
|
202
|
-
defaultedOptions.mutationFn = async () => {
|
|
203
|
-
const { mutationKey } = defaultedOptions;
|
|
204
|
-
(0, utils_1.assert)(mutationKey !== undefined, "You must pass a `mutationKey` that calls an action on the messenger provided to `createUIQueryClient`, e.g. `mutationKey: ['ExampleDataService:createOrder', ...]`.");
|
|
205
|
-
const [action, ...params] = mutationKey;
|
|
206
|
-
(0, utils_1.assert)(typeof action === 'string' && isRecognizedDataServiceAction(action), "You must pass a `mutationKey` that calls an action on the messenger provided to `createUIQueryClient`, e.g. `mutationKey: ['ExampleDataService:createOrder', ...]`.");
|
|
207
|
-
log(`Detected mutation request, calling action: "${action}"`);
|
|
208
|
-
// The `globalId` is passed as the trailing action argument. Each data
|
|
209
|
-
// service mutation method forwards it into `executeMutation`, which
|
|
210
|
-
// echoes it back on the service-side mutation's `meta`.
|
|
211
|
-
return await messenger.call(action, ...params, globalId);
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
return defaultedOptions;
|
|
215
|
-
};
|
|
216
|
-
return client;
|
|
217
|
-
}
|
|
218
|
-
exports.createUIQueryClient = createUIQueryClient;
|
|
219
|
-
//# sourceMappingURL=createUIQueryClient.cjs.map
|