@metamask-previews/base-data-service 0.0.0-preview-0d44fa472 → 0.0.0-preview-86eff6358
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/dist/BaseDataService.cjs +112 -0
- package/dist/BaseDataService.cjs.map +1 -0
- package/dist/BaseDataService.d.cts +38 -0
- package/dist/BaseDataService.d.cts.map +1 -0
- package/dist/BaseDataService.d.mts +38 -0
- package/dist/BaseDataService.d.mts.map +1 -0
- package/dist/BaseDataService.mjs +112 -0
- package/dist/BaseDataService.mjs.map +1 -0
- package/dist/createUIQueryClient.cjs +101 -0
- package/dist/createUIQueryClient.cjs.map +1 -0
- package/dist/createUIQueryClient.d.cts +17 -0
- package/dist/createUIQueryClient.d.cts.map +1 -0
- package/dist/createUIQueryClient.d.mts +17 -0
- package/dist/createUIQueryClient.d.mts.map +1 -0
- package/dist/createUIQueryClient.mjs +97 -0
- package/dist/createUIQueryClient.mjs.map +1 -0
- package/dist/index.cjs +16 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -7
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -7
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
var _BaseDataService_instances, _BaseDataService_messenger, _BaseDataService_client, _BaseDataService_subscriptions, _BaseDataService_registerMessageHandlers, _BaseDataService_setupCacheListener, _BaseDataService_handleSubscribe, _BaseDataService_handleUnsubscribe, _BaseDataService_getDehydratedState, _BaseDataService_broadcastCacheUpdate;
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.BaseDataService = void 0;
|
|
19
|
+
const messenger_1 = require("@metamask/messenger");
|
|
20
|
+
const query_core_1 = require("@tanstack/query-core");
|
|
21
|
+
const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
|
|
22
|
+
class BaseDataService {
|
|
23
|
+
constructor({ name, messenger, }) {
|
|
24
|
+
_BaseDataService_instances.add(this);
|
|
25
|
+
_BaseDataService_messenger.set(this, void 0);
|
|
26
|
+
_BaseDataService_client.set(this, new query_core_1.QueryClient());
|
|
27
|
+
_BaseDataService_subscriptions.set(this, new Map());
|
|
28
|
+
this.name = name;
|
|
29
|
+
__classPrivateFieldSet(this, _BaseDataService_messenger, messenger, "f");
|
|
30
|
+
__classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_registerMessageHandlers).call(this);
|
|
31
|
+
__classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_setupCacheListener).call(this);
|
|
32
|
+
}
|
|
33
|
+
async fetchQuery(options) {
|
|
34
|
+
return __classPrivateFieldGet(this, _BaseDataService_client, "f").fetchQuery(options);
|
|
35
|
+
}
|
|
36
|
+
async fetchInfiniteQuery(options, pageParam) {
|
|
37
|
+
const query = __classPrivateFieldGet(this, _BaseDataService_client, "f")
|
|
38
|
+
.getQueryCache()
|
|
39
|
+
.find({ queryKey: options.queryKey });
|
|
40
|
+
if (!query || !pageParam) {
|
|
41
|
+
const result = await __classPrivateFieldGet(this, _BaseDataService_client, "f").fetchInfiniteQuery({
|
|
42
|
+
...options,
|
|
43
|
+
queryFn: (context) => options.queryFn({
|
|
44
|
+
...context,
|
|
45
|
+
pageParam: context.pageParam ?? pageParam,
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
return result.pages[0];
|
|
49
|
+
}
|
|
50
|
+
const { pages } = query.state.data;
|
|
51
|
+
const previous = options.getPreviousPageParam?.(pages[0], pages);
|
|
52
|
+
const direction = (0, fast_deep_equal_1.default)(pageParam, previous) ? 'backward' : 'forward';
|
|
53
|
+
const result = (await query.fetch(undefined, {
|
|
54
|
+
meta: {
|
|
55
|
+
fetchMore: {
|
|
56
|
+
direction,
|
|
57
|
+
pageParam,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}));
|
|
61
|
+
const pageIndex = result.pageParams.indexOf(pageParam);
|
|
62
|
+
return result.pages[pageIndex];
|
|
63
|
+
}
|
|
64
|
+
async invalidateQueries(filters, options) {
|
|
65
|
+
return __classPrivateFieldGet(this, _BaseDataService_client, "f").invalidateQueries(filters, options);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.BaseDataService = BaseDataService;
|
|
69
|
+
_BaseDataService_messenger = new WeakMap(), _BaseDataService_client = new WeakMap(), _BaseDataService_subscriptions = new WeakMap(), _BaseDataService_instances = new WeakSet(), _BaseDataService_registerMessageHandlers = function _BaseDataService_registerMessageHandlers() {
|
|
70
|
+
// Casts are required since `registerActionHandler` isn't able to extract the method parameters correctly.
|
|
71
|
+
__classPrivateFieldGet(this, _BaseDataService_messenger, "f").registerActionHandler(`${this.name}:subscribe`, ((queryKey, callback) => __classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_handleSubscribe).call(this, queryKey, callback)));
|
|
72
|
+
__classPrivateFieldGet(this, _BaseDataService_messenger, "f").registerActionHandler(`${this.name}:unsubscribe`, ((queryKey, callback) => __classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_handleUnsubscribe).call(this, queryKey, callback)));
|
|
73
|
+
__classPrivateFieldGet(this, _BaseDataService_messenger, "f").registerActionHandler(`${this.name}:invalidateQueries`, ((filters, options) => this.invalidateQueries(filters, options)));
|
|
74
|
+
}, _BaseDataService_setupCacheListener = function _BaseDataService_setupCacheListener() {
|
|
75
|
+
__classPrivateFieldGet(this, _BaseDataService_client, "f").getQueryCache().subscribe((event) => {
|
|
76
|
+
if (['added', 'updated', 'removed'].includes(event.type)) {
|
|
77
|
+
__classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_broadcastCacheUpdate).call(this, event.query.queryKey);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}, _BaseDataService_handleSubscribe = function _BaseDataService_handleSubscribe(queryKey, subscription) {
|
|
81
|
+
const hash = (0, query_core_1.hashQueryKey)(queryKey);
|
|
82
|
+
if (!__classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").has(hash)) {
|
|
83
|
+
__classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").set(hash, new Set());
|
|
84
|
+
}
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
86
|
+
__classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").get(hash).add(subscription);
|
|
87
|
+
return __classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_getDehydratedState).call(this, queryKey);
|
|
88
|
+
}, _BaseDataService_handleUnsubscribe = function _BaseDataService_handleUnsubscribe(queryKey, subscription) {
|
|
89
|
+
const hash = (0, query_core_1.hashQueryKey)(queryKey);
|
|
90
|
+
const subscribers = __classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").get(hash);
|
|
91
|
+
subscribers?.delete(subscription);
|
|
92
|
+
if (subscribers?.size === 0) {
|
|
93
|
+
__classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").delete(hash);
|
|
94
|
+
}
|
|
95
|
+
}, _BaseDataService_getDehydratedState = function _BaseDataService_getDehydratedState(queryKey) {
|
|
96
|
+
const hash = (0, query_core_1.hashQueryKey)(queryKey);
|
|
97
|
+
return (0, query_core_1.dehydrate)(__classPrivateFieldGet(this, _BaseDataService_client, "f"), {
|
|
98
|
+
shouldDehydrateQuery: (query) => query.queryHash === hash,
|
|
99
|
+
});
|
|
100
|
+
}, _BaseDataService_broadcastCacheUpdate = function _BaseDataService_broadcastCacheUpdate(queryKey) {
|
|
101
|
+
const hash = (0, query_core_1.hashQueryKey)(queryKey);
|
|
102
|
+
const state = __classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_getDehydratedState).call(this, queryKey);
|
|
103
|
+
const payload = {
|
|
104
|
+
hash,
|
|
105
|
+
state,
|
|
106
|
+
};
|
|
107
|
+
__classPrivateFieldGet(this, _BaseDataService_messenger, "f").publish(`${this.name}:cacheUpdate`, payload);
|
|
108
|
+
// TODO: Determine if we can leverage `messenger.publish` entirely in order to not keep track of subscriptions manually.
|
|
109
|
+
const subscribers = __classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").get(hash);
|
|
110
|
+
subscribers?.forEach((subscriber) => subscriber(payload));
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=BaseDataService.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseDataService.cjs","sourceRoot":"","sources":["../src/BaseDataService.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Messenger,\n ActionConstraint,\n EventConstraint,\n ActionHandler,\n} from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\nimport {\n DehydratedState,\n FetchInfiniteQueryOptions,\n FetchQueryOptions,\n InfiniteData,\n InvalidateOptions,\n InvalidateQueryFilters,\n QueryClient,\n QueryKey,\n WithRequired,\n dehydrate,\n hashQueryKey,\n} from '@tanstack/query-core';\nimport deepEqual from 'fast-deep-equal';\n\nexport type SubscriptionPayload = { hash: string; state: DehydratedState };\nexport type SubscriptionCallback = (payload: SubscriptionPayload) => void;\n\nexport type DataServiceSubscribeAction<ServiceName extends string> = {\n type: `${ServiceName}:subscribe`;\n handler: (\n queryKey: QueryKey,\n callback: SubscriptionCallback,\n ) => DehydratedState;\n};\n\nexport type DataServiceUnsubscribeAction<ServiceName extends string> = {\n type: `${ServiceName}:unsubscribe`;\n handler: (queryKey: QueryKey, callback: SubscriptionCallback) => void;\n};\n\nexport type DataServiceInvalidateQueriesAction<ServiceName extends string> = {\n type: `${ServiceName}:invalidateQueries`;\n handler: (\n filters?: InvalidateQueryFilters<Json>,\n options?: InvalidateOptions,\n ) => Promise<void>;\n};\n\nexport type DataServiceActions<ServiceName extends string> =\n | DataServiceSubscribeAction<ServiceName>\n | DataServiceUnsubscribeAction<ServiceName>\n | DataServiceInvalidateQueriesAction<ServiceName>;\n\nexport type DataServiceCacheUpdateEvent<ServiceName extends string> = {\n type: `${ServiceName}:cacheUpdate`;\n payload: [SubscriptionPayload];\n};\n\nexport type DataServiceEvents<ServiceName extends string> =\n DataServiceCacheUpdateEvent<ServiceName>;\n\nexport class BaseDataService<\n ServiceName extends string,\n ServiceMessenger extends Messenger<\n ServiceName,\n ActionConstraint,\n EventConstraint,\n // Use `any` to allow any parent to be set. `any` is harmless in a type constraint anyway,\n // it's the one totally safe place to use it.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n >,\n> {\n public readonly name: ServiceName;\n\n readonly #messenger: Messenger<\n ServiceName,\n DataServiceActions<ServiceName>,\n DataServiceEvents<ServiceName>\n >;\n\n readonly #client = new QueryClient();\n\n readonly #subscriptions: Map<string, Set<SubscriptionCallback>> = new Map();\n\n constructor({\n name,\n messenger,\n }: {\n name: ServiceName;\n messenger: ServiceMessenger;\n }) {\n this.name = name;\n\n this.#messenger = messenger as unknown as Messenger<\n ServiceName,\n DataServiceActions<ServiceName>,\n DataServiceEvents<ServiceName>\n >;\n\n this.#registerMessageHandlers();\n this.#setupCacheListener();\n }\n\n #registerMessageHandlers(): void {\n // Casts are required since `registerActionHandler` isn't able to extract the method parameters correctly.\n this.#messenger.registerActionHandler(`${this.name}:subscribe`, ((\n queryKey: QueryKey,\n callback: SubscriptionCallback,\n ) => this.#handleSubscribe(queryKey, callback)) as ActionHandler<\n DataServiceActions<ServiceName>\n >);\n\n this.#messenger.registerActionHandler(`${this.name}:unsubscribe`, ((\n queryKey: QueryKey,\n callback: SubscriptionCallback,\n ) => this.#handleUnsubscribe(queryKey, callback)) as ActionHandler<\n DataServiceActions<ServiceName>\n >);\n\n this.#messenger.registerActionHandler(`${this.name}:invalidateQueries`, ((\n filters?: InvalidateQueryFilters<Json>,\n options?: InvalidateOptions,\n ) => this.invalidateQueries(filters, options)) as ActionHandler<\n DataServiceActions<ServiceName>\n >);\n }\n\n #setupCacheListener(): void {\n this.#client.getQueryCache().subscribe((event) => {\n if (['added', 'updated', 'removed'].includes(event.type)) {\n this.#broadcastCacheUpdate(event.query.queryKey);\n }\n });\n }\n\n protected async fetchQuery<\n TQueryFnData extends Json,\n TError = unknown,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n >(\n options: WithRequired<\n FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n 'queryKey' | 'queryFn'\n >,\n ): Promise<TData> {\n return this.#client.fetchQuery(options);\n }\n\n protected async fetchInfiniteQuery<\n TQueryFnData extends Json,\n TError = unknown,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n TPageParam extends Json = Json,\n >(\n options: WithRequired<\n FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n 'queryKey' | 'queryFn'\n >,\n pageParam?: TPageParam,\n ): Promise<TData> {\n const query = this.#client\n .getQueryCache()\n .find<TQueryFnData, TError, TData>({ queryKey: options.queryKey });\n\n if (!query || !pageParam) {\n const result = await this.#client.fetchInfiniteQuery({\n ...options,\n queryFn: (context) =>\n options.queryFn({\n ...context,\n pageParam: context.pageParam ?? pageParam,\n }),\n });\n\n return result.pages[0];\n }\n\n const { pages } = query.state.data as InfiniteData<TQueryFnData>;\n const previous = options.getPreviousPageParam?.(pages[0], pages);\n\n const direction = deepEqual(pageParam, previous) ? 'backward' : 'forward';\n\n const result = (await query.fetch(undefined, {\n meta: {\n fetchMore: {\n direction,\n pageParam,\n },\n },\n })) as InfiniteData<TData>;\n\n const pageIndex = result.pageParams.indexOf(pageParam);\n\n return result.pages[pageIndex];\n }\n\n async invalidateQueries<TPageData extends Json>(\n filters?: InvalidateQueryFilters<TPageData>,\n options?: InvalidateOptions,\n ): Promise<void> {\n return this.#client.invalidateQueries(filters, options);\n }\n\n #handleSubscribe(\n queryKey: QueryKey,\n subscription: SubscriptionCallback,\n ): DehydratedState {\n const hash = hashQueryKey(queryKey);\n\n if (!this.#subscriptions.has(hash)) {\n this.#subscriptions.set(hash, new Set());\n }\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.#subscriptions.get(hash)!.add(subscription);\n\n return this.#getDehydratedState(queryKey);\n }\n\n #handleUnsubscribe(\n queryKey: QueryKey,\n subscription: SubscriptionCallback,\n ): void {\n const hash = hashQueryKey(queryKey);\n const subscribers = this.#subscriptions.get(hash);\n\n subscribers?.delete(subscription);\n if (subscribers?.size === 0) {\n this.#subscriptions.delete(hash);\n }\n }\n\n #getDehydratedState(queryKey: QueryKey): DehydratedState {\n const hash = hashQueryKey(queryKey);\n return dehydrate(this.#client, {\n shouldDehydrateQuery: (query) => query.queryHash === hash,\n });\n }\n\n #broadcastCacheUpdate(queryKey: QueryKey): void {\n const hash = hashQueryKey(queryKey);\n const state = this.#getDehydratedState(queryKey);\n\n const payload = {\n hash,\n state,\n };\n\n this.#messenger.publish(`${this.name}:cacheUpdate` as const, payload);\n\n // TODO: Determine if we can leverage `messenger.publish` entirely in order to not keep track of subscriptions manually.\n const subscribers = this.#subscriptions.get(hash);\n subscribers?.forEach((subscriber) => subscriber(payload));\n }\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Messenger, ActionConstraint, EventConstraint } from "@metamask/messenger";
|
|
2
|
+
import type { Json } from "@metamask/utils";
|
|
3
|
+
import { DehydratedState, FetchInfiniteQueryOptions, FetchQueryOptions, InvalidateOptions, InvalidateQueryFilters, QueryKey, WithRequired } from "@tanstack/query-core";
|
|
4
|
+
export type SubscriptionPayload = {
|
|
5
|
+
hash: string;
|
|
6
|
+
state: DehydratedState;
|
|
7
|
+
};
|
|
8
|
+
export type SubscriptionCallback = (payload: SubscriptionPayload) => void;
|
|
9
|
+
export type DataServiceSubscribeAction<ServiceName extends string> = {
|
|
10
|
+
type: `${ServiceName}:subscribe`;
|
|
11
|
+
handler: (queryKey: QueryKey, callback: SubscriptionCallback) => DehydratedState;
|
|
12
|
+
};
|
|
13
|
+
export type DataServiceUnsubscribeAction<ServiceName extends string> = {
|
|
14
|
+
type: `${ServiceName}:unsubscribe`;
|
|
15
|
+
handler: (queryKey: QueryKey, callback: SubscriptionCallback) => void;
|
|
16
|
+
};
|
|
17
|
+
export type DataServiceInvalidateQueriesAction<ServiceName extends string> = {
|
|
18
|
+
type: `${ServiceName}:invalidateQueries`;
|
|
19
|
+
handler: (filters?: InvalidateQueryFilters<Json>, options?: InvalidateOptions) => Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
export type DataServiceActions<ServiceName extends string> = DataServiceSubscribeAction<ServiceName> | DataServiceUnsubscribeAction<ServiceName> | DataServiceInvalidateQueriesAction<ServiceName>;
|
|
22
|
+
export type DataServiceCacheUpdateEvent<ServiceName extends string> = {
|
|
23
|
+
type: `${ServiceName}:cacheUpdate`;
|
|
24
|
+
payload: [SubscriptionPayload];
|
|
25
|
+
};
|
|
26
|
+
export type DataServiceEvents<ServiceName extends string> = DataServiceCacheUpdateEvent<ServiceName>;
|
|
27
|
+
export declare class BaseDataService<ServiceName extends string, ServiceMessenger extends Messenger<ServiceName, ActionConstraint, EventConstraint, any>> {
|
|
28
|
+
#private;
|
|
29
|
+
readonly name: ServiceName;
|
|
30
|
+
constructor({ name, messenger, }: {
|
|
31
|
+
name: ServiceName;
|
|
32
|
+
messenger: ServiceMessenger;
|
|
33
|
+
});
|
|
34
|
+
protected fetchQuery<TQueryFnData extends Json, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(options: WithRequired<FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'queryKey' | 'queryFn'>): Promise<TData>;
|
|
35
|
+
protected fetchInfiniteQuery<TQueryFnData extends Json, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam extends Json = Json>(options: WithRequired<FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'queryKey' | 'queryFn'>, pageParam?: TPageParam): Promise<TData>;
|
|
36
|
+
invalidateQueries<TPageData extends Json>(filters?: InvalidateQueryFilters<TPageData>, options?: InvalidateOptions): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=BaseDataService.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseDataService.d.cts","sourceRoot":"","sources":["../src/BaseDataService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Messenger, ActionConstraint, EventConstraint } from "@metamask/messenger";
|
|
2
|
+
import type { Json } from "@metamask/utils";
|
|
3
|
+
import { DehydratedState, FetchInfiniteQueryOptions, FetchQueryOptions, InvalidateOptions, InvalidateQueryFilters, QueryKey, WithRequired } from "@tanstack/query-core";
|
|
4
|
+
export type SubscriptionPayload = {
|
|
5
|
+
hash: string;
|
|
6
|
+
state: DehydratedState;
|
|
7
|
+
};
|
|
8
|
+
export type SubscriptionCallback = (payload: SubscriptionPayload) => void;
|
|
9
|
+
export type DataServiceSubscribeAction<ServiceName extends string> = {
|
|
10
|
+
type: `${ServiceName}:subscribe`;
|
|
11
|
+
handler: (queryKey: QueryKey, callback: SubscriptionCallback) => DehydratedState;
|
|
12
|
+
};
|
|
13
|
+
export type DataServiceUnsubscribeAction<ServiceName extends string> = {
|
|
14
|
+
type: `${ServiceName}:unsubscribe`;
|
|
15
|
+
handler: (queryKey: QueryKey, callback: SubscriptionCallback) => void;
|
|
16
|
+
};
|
|
17
|
+
export type DataServiceInvalidateQueriesAction<ServiceName extends string> = {
|
|
18
|
+
type: `${ServiceName}:invalidateQueries`;
|
|
19
|
+
handler: (filters?: InvalidateQueryFilters<Json>, options?: InvalidateOptions) => Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
export type DataServiceActions<ServiceName extends string> = DataServiceSubscribeAction<ServiceName> | DataServiceUnsubscribeAction<ServiceName> | DataServiceInvalidateQueriesAction<ServiceName>;
|
|
22
|
+
export type DataServiceCacheUpdateEvent<ServiceName extends string> = {
|
|
23
|
+
type: `${ServiceName}:cacheUpdate`;
|
|
24
|
+
payload: [SubscriptionPayload];
|
|
25
|
+
};
|
|
26
|
+
export type DataServiceEvents<ServiceName extends string> = DataServiceCacheUpdateEvent<ServiceName>;
|
|
27
|
+
export declare class BaseDataService<ServiceName extends string, ServiceMessenger extends Messenger<ServiceName, ActionConstraint, EventConstraint, any>> {
|
|
28
|
+
#private;
|
|
29
|
+
readonly name: ServiceName;
|
|
30
|
+
constructor({ name, messenger, }: {
|
|
31
|
+
name: ServiceName;
|
|
32
|
+
messenger: ServiceMessenger;
|
|
33
|
+
});
|
|
34
|
+
protected fetchQuery<TQueryFnData extends Json, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(options: WithRequired<FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'queryKey' | 'queryFn'>): Promise<TData>;
|
|
35
|
+
protected fetchInfiniteQuery<TQueryFnData extends Json, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TPageParam extends Json = Json>(options: WithRequired<FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'queryKey' | 'queryFn'>, pageParam?: TPageParam): Promise<TData>;
|
|
36
|
+
invalidateQueries<TPageData extends Json>(filters?: InvalidateQueryFilters<TPageData>, options?: InvalidateOptions): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=BaseDataService.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseDataService.d.mts","sourceRoot":"","sources":["../src/BaseDataService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _BaseDataService_instances, _BaseDataService_messenger, _BaseDataService_client, _BaseDataService_subscriptions, _BaseDataService_registerMessageHandlers, _BaseDataService_setupCacheListener, _BaseDataService_handleSubscribe, _BaseDataService_handleUnsubscribe, _BaseDataService_getDehydratedState, _BaseDataService_broadcastCacheUpdate;
|
|
13
|
+
function $importDefault(module) {
|
|
14
|
+
if (module?.__esModule) {
|
|
15
|
+
return module.default;
|
|
16
|
+
}
|
|
17
|
+
return module;
|
|
18
|
+
}
|
|
19
|
+
import { Messenger } from "@metamask/messenger";
|
|
20
|
+
import { QueryClient, dehydrate, hashQueryKey } from "@tanstack/query-core";
|
|
21
|
+
import $deepEqual from "fast-deep-equal";
|
|
22
|
+
const deepEqual = $importDefault($deepEqual);
|
|
23
|
+
export class BaseDataService {
|
|
24
|
+
constructor({ name, messenger, }) {
|
|
25
|
+
_BaseDataService_instances.add(this);
|
|
26
|
+
_BaseDataService_messenger.set(this, void 0);
|
|
27
|
+
_BaseDataService_client.set(this, new QueryClient());
|
|
28
|
+
_BaseDataService_subscriptions.set(this, new Map());
|
|
29
|
+
this.name = name;
|
|
30
|
+
__classPrivateFieldSet(this, _BaseDataService_messenger, messenger, "f");
|
|
31
|
+
__classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_registerMessageHandlers).call(this);
|
|
32
|
+
__classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_setupCacheListener).call(this);
|
|
33
|
+
}
|
|
34
|
+
async fetchQuery(options) {
|
|
35
|
+
return __classPrivateFieldGet(this, _BaseDataService_client, "f").fetchQuery(options);
|
|
36
|
+
}
|
|
37
|
+
async fetchInfiniteQuery(options, pageParam) {
|
|
38
|
+
const query = __classPrivateFieldGet(this, _BaseDataService_client, "f")
|
|
39
|
+
.getQueryCache()
|
|
40
|
+
.find({ queryKey: options.queryKey });
|
|
41
|
+
if (!query || !pageParam) {
|
|
42
|
+
const result = await __classPrivateFieldGet(this, _BaseDataService_client, "f").fetchInfiniteQuery({
|
|
43
|
+
...options,
|
|
44
|
+
queryFn: (context) => options.queryFn({
|
|
45
|
+
...context,
|
|
46
|
+
pageParam: context.pageParam ?? pageParam,
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
return result.pages[0];
|
|
50
|
+
}
|
|
51
|
+
const { pages } = query.state.data;
|
|
52
|
+
const previous = options.getPreviousPageParam?.(pages[0], pages);
|
|
53
|
+
const direction = deepEqual(pageParam, previous) ? 'backward' : 'forward';
|
|
54
|
+
const result = (await query.fetch(undefined, {
|
|
55
|
+
meta: {
|
|
56
|
+
fetchMore: {
|
|
57
|
+
direction,
|
|
58
|
+
pageParam,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
}));
|
|
62
|
+
const pageIndex = result.pageParams.indexOf(pageParam);
|
|
63
|
+
return result.pages[pageIndex];
|
|
64
|
+
}
|
|
65
|
+
async invalidateQueries(filters, options) {
|
|
66
|
+
return __classPrivateFieldGet(this, _BaseDataService_client, "f").invalidateQueries(filters, options);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
_BaseDataService_messenger = new WeakMap(), _BaseDataService_client = new WeakMap(), _BaseDataService_subscriptions = new WeakMap(), _BaseDataService_instances = new WeakSet(), _BaseDataService_registerMessageHandlers = function _BaseDataService_registerMessageHandlers() {
|
|
70
|
+
// Casts are required since `registerActionHandler` isn't able to extract the method parameters correctly.
|
|
71
|
+
__classPrivateFieldGet(this, _BaseDataService_messenger, "f").registerActionHandler(`${this.name}:subscribe`, ((queryKey, callback) => __classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_handleSubscribe).call(this, queryKey, callback)));
|
|
72
|
+
__classPrivateFieldGet(this, _BaseDataService_messenger, "f").registerActionHandler(`${this.name}:unsubscribe`, ((queryKey, callback) => __classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_handleUnsubscribe).call(this, queryKey, callback)));
|
|
73
|
+
__classPrivateFieldGet(this, _BaseDataService_messenger, "f").registerActionHandler(`${this.name}:invalidateQueries`, ((filters, options) => this.invalidateQueries(filters, options)));
|
|
74
|
+
}, _BaseDataService_setupCacheListener = function _BaseDataService_setupCacheListener() {
|
|
75
|
+
__classPrivateFieldGet(this, _BaseDataService_client, "f").getQueryCache().subscribe((event) => {
|
|
76
|
+
if (['added', 'updated', 'removed'].includes(event.type)) {
|
|
77
|
+
__classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_broadcastCacheUpdate).call(this, event.query.queryKey);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}, _BaseDataService_handleSubscribe = function _BaseDataService_handleSubscribe(queryKey, subscription) {
|
|
81
|
+
const hash = hashQueryKey(queryKey);
|
|
82
|
+
if (!__classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").has(hash)) {
|
|
83
|
+
__classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").set(hash, new Set());
|
|
84
|
+
}
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
86
|
+
__classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").get(hash).add(subscription);
|
|
87
|
+
return __classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_getDehydratedState).call(this, queryKey);
|
|
88
|
+
}, _BaseDataService_handleUnsubscribe = function _BaseDataService_handleUnsubscribe(queryKey, subscription) {
|
|
89
|
+
const hash = hashQueryKey(queryKey);
|
|
90
|
+
const subscribers = __classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").get(hash);
|
|
91
|
+
subscribers?.delete(subscription);
|
|
92
|
+
if (subscribers?.size === 0) {
|
|
93
|
+
__classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").delete(hash);
|
|
94
|
+
}
|
|
95
|
+
}, _BaseDataService_getDehydratedState = function _BaseDataService_getDehydratedState(queryKey) {
|
|
96
|
+
const hash = hashQueryKey(queryKey);
|
|
97
|
+
return dehydrate(__classPrivateFieldGet(this, _BaseDataService_client, "f"), {
|
|
98
|
+
shouldDehydrateQuery: (query) => query.queryHash === hash,
|
|
99
|
+
});
|
|
100
|
+
}, _BaseDataService_broadcastCacheUpdate = function _BaseDataService_broadcastCacheUpdate(queryKey) {
|
|
101
|
+
const hash = hashQueryKey(queryKey);
|
|
102
|
+
const state = __classPrivateFieldGet(this, _BaseDataService_instances, "m", _BaseDataService_getDehydratedState).call(this, queryKey);
|
|
103
|
+
const payload = {
|
|
104
|
+
hash,
|
|
105
|
+
state,
|
|
106
|
+
};
|
|
107
|
+
__classPrivateFieldGet(this, _BaseDataService_messenger, "f").publish(`${this.name}:cacheUpdate`, payload);
|
|
108
|
+
// TODO: Determine if we can leverage `messenger.publish` entirely in order to not keep track of subscriptions manually.
|
|
109
|
+
const subscribers = __classPrivateFieldGet(this, _BaseDataService_subscriptions, "f").get(hash);
|
|
110
|
+
subscribers?.forEach((subscriber) => subscriber(payload));
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=BaseDataService.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseDataService.mjs","sourceRoot":"","sources":["../src/BaseDataService.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Messenger,\n ActionConstraint,\n EventConstraint,\n ActionHandler,\n} from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\nimport {\n DehydratedState,\n FetchInfiniteQueryOptions,\n FetchQueryOptions,\n InfiniteData,\n InvalidateOptions,\n InvalidateQueryFilters,\n QueryClient,\n QueryKey,\n WithRequired,\n dehydrate,\n hashQueryKey,\n} from '@tanstack/query-core';\nimport deepEqual from 'fast-deep-equal';\n\nexport type SubscriptionPayload = { hash: string; state: DehydratedState };\nexport type SubscriptionCallback = (payload: SubscriptionPayload) => void;\n\nexport type DataServiceSubscribeAction<ServiceName extends string> = {\n type: `${ServiceName}:subscribe`;\n handler: (\n queryKey: QueryKey,\n callback: SubscriptionCallback,\n ) => DehydratedState;\n};\n\nexport type DataServiceUnsubscribeAction<ServiceName extends string> = {\n type: `${ServiceName}:unsubscribe`;\n handler: (queryKey: QueryKey, callback: SubscriptionCallback) => void;\n};\n\nexport type DataServiceInvalidateQueriesAction<ServiceName extends string> = {\n type: `${ServiceName}:invalidateQueries`;\n handler: (\n filters?: InvalidateQueryFilters<Json>,\n options?: InvalidateOptions,\n ) => Promise<void>;\n};\n\nexport type DataServiceActions<ServiceName extends string> =\n | DataServiceSubscribeAction<ServiceName>\n | DataServiceUnsubscribeAction<ServiceName>\n | DataServiceInvalidateQueriesAction<ServiceName>;\n\nexport type DataServiceCacheUpdateEvent<ServiceName extends string> = {\n type: `${ServiceName}:cacheUpdate`;\n payload: [SubscriptionPayload];\n};\n\nexport type DataServiceEvents<ServiceName extends string> =\n DataServiceCacheUpdateEvent<ServiceName>;\n\nexport class BaseDataService<\n ServiceName extends string,\n ServiceMessenger extends Messenger<\n ServiceName,\n ActionConstraint,\n EventConstraint,\n // Use `any` to allow any parent to be set. `any` is harmless in a type constraint anyway,\n // it's the one totally safe place to use it.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n >,\n> {\n public readonly name: ServiceName;\n\n readonly #messenger: Messenger<\n ServiceName,\n DataServiceActions<ServiceName>,\n DataServiceEvents<ServiceName>\n >;\n\n readonly #client = new QueryClient();\n\n readonly #subscriptions: Map<string, Set<SubscriptionCallback>> = new Map();\n\n constructor({\n name,\n messenger,\n }: {\n name: ServiceName;\n messenger: ServiceMessenger;\n }) {\n this.name = name;\n\n this.#messenger = messenger as unknown as Messenger<\n ServiceName,\n DataServiceActions<ServiceName>,\n DataServiceEvents<ServiceName>\n >;\n\n this.#registerMessageHandlers();\n this.#setupCacheListener();\n }\n\n #registerMessageHandlers(): void {\n // Casts are required since `registerActionHandler` isn't able to extract the method parameters correctly.\n this.#messenger.registerActionHandler(`${this.name}:subscribe`, ((\n queryKey: QueryKey,\n callback: SubscriptionCallback,\n ) => this.#handleSubscribe(queryKey, callback)) as ActionHandler<\n DataServiceActions<ServiceName>\n >);\n\n this.#messenger.registerActionHandler(`${this.name}:unsubscribe`, ((\n queryKey: QueryKey,\n callback: SubscriptionCallback,\n ) => this.#handleUnsubscribe(queryKey, callback)) as ActionHandler<\n DataServiceActions<ServiceName>\n >);\n\n this.#messenger.registerActionHandler(`${this.name}:invalidateQueries`, ((\n filters?: InvalidateQueryFilters<Json>,\n options?: InvalidateOptions,\n ) => this.invalidateQueries(filters, options)) as ActionHandler<\n DataServiceActions<ServiceName>\n >);\n }\n\n #setupCacheListener(): void {\n this.#client.getQueryCache().subscribe((event) => {\n if (['added', 'updated', 'removed'].includes(event.type)) {\n this.#broadcastCacheUpdate(event.query.queryKey);\n }\n });\n }\n\n protected async fetchQuery<\n TQueryFnData extends Json,\n TError = unknown,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n >(\n options: WithRequired<\n FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n 'queryKey' | 'queryFn'\n >,\n ): Promise<TData> {\n return this.#client.fetchQuery(options);\n }\n\n protected async fetchInfiniteQuery<\n TQueryFnData extends Json,\n TError = unknown,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n TPageParam extends Json = Json,\n >(\n options: WithRequired<\n FetchInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n 'queryKey' | 'queryFn'\n >,\n pageParam?: TPageParam,\n ): Promise<TData> {\n const query = this.#client\n .getQueryCache()\n .find<TQueryFnData, TError, TData>({ queryKey: options.queryKey });\n\n if (!query || !pageParam) {\n const result = await this.#client.fetchInfiniteQuery({\n ...options,\n queryFn: (context) =>\n options.queryFn({\n ...context,\n pageParam: context.pageParam ?? pageParam,\n }),\n });\n\n return result.pages[0];\n }\n\n const { pages } = query.state.data as InfiniteData<TQueryFnData>;\n const previous = options.getPreviousPageParam?.(pages[0], pages);\n\n const direction = deepEqual(pageParam, previous) ? 'backward' : 'forward';\n\n const result = (await query.fetch(undefined, {\n meta: {\n fetchMore: {\n direction,\n pageParam,\n },\n },\n })) as InfiniteData<TData>;\n\n const pageIndex = result.pageParams.indexOf(pageParam);\n\n return result.pages[pageIndex];\n }\n\n async invalidateQueries<TPageData extends Json>(\n filters?: InvalidateQueryFilters<TPageData>,\n options?: InvalidateOptions,\n ): Promise<void> {\n return this.#client.invalidateQueries(filters, options);\n }\n\n #handleSubscribe(\n queryKey: QueryKey,\n subscription: SubscriptionCallback,\n ): DehydratedState {\n const hash = hashQueryKey(queryKey);\n\n if (!this.#subscriptions.has(hash)) {\n this.#subscriptions.set(hash, new Set());\n }\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.#subscriptions.get(hash)!.add(subscription);\n\n return this.#getDehydratedState(queryKey);\n }\n\n #handleUnsubscribe(\n queryKey: QueryKey,\n subscription: SubscriptionCallback,\n ): void {\n const hash = hashQueryKey(queryKey);\n const subscribers = this.#subscriptions.get(hash);\n\n subscribers?.delete(subscription);\n if (subscribers?.size === 0) {\n this.#subscriptions.delete(hash);\n }\n }\n\n #getDehydratedState(queryKey: QueryKey): DehydratedState {\n const hash = hashQueryKey(queryKey);\n return dehydrate(this.#client, {\n shouldDehydrateQuery: (query) => query.queryHash === hash,\n });\n }\n\n #broadcastCacheUpdate(queryKey: QueryKey): void {\n const hash = hashQueryKey(queryKey);\n const state = this.#getDehydratedState(queryKey);\n\n const payload = {\n hash,\n state,\n };\n\n this.#messenger.publish(`${this.name}:cacheUpdate` as const, payload);\n\n // TODO: Determine if we can leverage `messenger.publish` entirely in order to not keep track of subscriptions manually.\n const subscribers = this.#subscriptions.get(hash);\n subscribers?.forEach((subscriber) => subscriber(payload));\n }\n}\n"]}
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
/**
|
|
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
|
+
* @returns The QueryClient.
|
|
12
|
+
*/
|
|
13
|
+
function createUIQueryClient(dataServices, messenger) {
|
|
14
|
+
const subscriptions = new Set();
|
|
15
|
+
const cacheListener = (data) => {
|
|
16
|
+
const castData = data;
|
|
17
|
+
if (subscriptions.has(castData.hash)) {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
19
|
+
(0, query_core_1.hydrate)(client, castData.state);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const getServiceFromQueryKey = (queryKey) => {
|
|
23
|
+
try {
|
|
24
|
+
const action = queryKey[0];
|
|
25
|
+
(0, utils_1.assert)(typeof action === 'string');
|
|
26
|
+
const service = action.split(':')[0];
|
|
27
|
+
(0, utils_1.assert)(dataServices.includes(service));
|
|
28
|
+
return service;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const client = new query_core_1.QueryClient({
|
|
35
|
+
defaultOptions: {
|
|
36
|
+
queries: {
|
|
37
|
+
queryFn: async (options) => {
|
|
38
|
+
const { queryKey } = options;
|
|
39
|
+
const action = queryKey?.[0];
|
|
40
|
+
(0, utils_1.assert)(typeof action === 'string', 'The first element of a query key must be a string.');
|
|
41
|
+
(0, utils_1.assert)(dataServices.includes(action.split(':')?.[0]), 'Queries must use data service actions.');
|
|
42
|
+
return (await messenger.call(action, ...options.queryKey.slice(1), options.pageParam));
|
|
43
|
+
},
|
|
44
|
+
// TODO: Decide on values for these.
|
|
45
|
+
staleTime: Infinity,
|
|
46
|
+
refetchOnWindowFocus: false,
|
|
47
|
+
refetchOnReconnect: false,
|
|
48
|
+
refetchOnMount: false,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
client.getQueryCache().subscribe((event) => {
|
|
53
|
+
const { query } = event;
|
|
54
|
+
const hash = query.queryHash;
|
|
55
|
+
const hasSubscription = subscriptions.has(hash);
|
|
56
|
+
const observerCount = query.getObserversCount();
|
|
57
|
+
const service = getServiceFromQueryKey(query.queryKey);
|
|
58
|
+
if (!service) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (!hasSubscription &&
|
|
62
|
+
event.type === 'observerAdded' &&
|
|
63
|
+
observerCount === 1) {
|
|
64
|
+
subscriptions.add(hash);
|
|
65
|
+
// This is a bit of a mess because we can't pass functions across the process boundary, so we call subscribe
|
|
66
|
+
// but also register listeners for :cacheUpdate which will be sent to subscribed processes
|
|
67
|
+
messenger.subscribe(`${service}:cacheUpdate`, cacheListener);
|
|
68
|
+
messenger
|
|
69
|
+
.call(`${service}:subscribe`, query.queryKey)
|
|
70
|
+
.then((state) => (0, query_core_1.hydrate)(client, state))
|
|
71
|
+
.catch(console.error);
|
|
72
|
+
}
|
|
73
|
+
else if (event.type === 'observerRemoved' &&
|
|
74
|
+
observerCount === 0 &&
|
|
75
|
+
hasSubscription) {
|
|
76
|
+
subscriptions.delete(hash);
|
|
77
|
+
messenger.unsubscribe(`${service}:cacheUpdate`, cacheListener);
|
|
78
|
+
messenger
|
|
79
|
+
.call(`${service}:unsubscribe`, query.queryKey)
|
|
80
|
+
.catch(console.error);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
// Override invalidateQueries to ensure the data service is invalidated as well.
|
|
84
|
+
const originalInvalidate = client.invalidateQueries.bind(client);
|
|
85
|
+
// This function is defined in this way to have full support for all function overloads.
|
|
86
|
+
client.invalidateQueries = async (arg1, arg2, arg3) => {
|
|
87
|
+
const [filters, options] = (0, query_core_1.parseFilterArgs)(arg1, arg2, arg3);
|
|
88
|
+
const queries = client.getQueryCache().findAll(filters);
|
|
89
|
+
await Promise.all(queries.map(async (query) => {
|
|
90
|
+
const service = getServiceFromQueryKey(query.queryKey);
|
|
91
|
+
if (!service) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return messenger.call(`${service}:invalidateQueries`, filters, options);
|
|
95
|
+
}));
|
|
96
|
+
return originalInvalidate(filters, options);
|
|
97
|
+
};
|
|
98
|
+
return client;
|
|
99
|
+
}
|
|
100
|
+
exports.createUIQueryClient = createUIQueryClient;
|
|
101
|
+
//# sourceMappingURL=createUIQueryClient.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createUIQueryClient.cjs","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert, Json } from '@metamask/utils';\nimport {\n hydrate,\n QueryClient,\n InvalidateQueryFilters,\n InvalidateOptions,\n OmitKeyof,\n parseFilterArgs,\n QueryKey,\n} from '@tanstack/query-core';\n\n// When UI messengers are available this should simply be a proper messenger that allows access to DataServiceActions\ntype MessengerAdapter = {\n call: (method: string, ...params: Json[]) => Promise<Json | void>;\n subscribe: (method: string, callback: (data: Json) => void) => void;\n unsubscribe: (method: string, callback: (data: Json) => void) => 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 * @returns The QueryClient.\n */\nexport function createUIQueryClient(\n dataServices: string[],\n messenger: MessengerAdapter,\n): QueryClient {\n const subscriptions = new Set<string>();\n\n const cacheListener = (data: Json): void => {\n const castData = data as { hash: string; state: Json };\n if (subscriptions.has(castData.hash)) {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n hydrate(client, castData.state);\n }\n };\n\n const getServiceFromQueryKey = (queryKey: QueryKey): string | null => {\n try {\n const action = queryKey[0];\n assert(typeof action === 'string');\n\n const service = action.split(':')[0];\n assert(dataServices.includes(service));\n\n return service;\n } catch {\n return null;\n }\n };\n\n const client: QueryClient = new QueryClient({\n defaultOptions: {\n queries: {\n queryFn: async (options): Promise<Json> => {\n const { queryKey } = options;\n\n const action = queryKey?.[0];\n\n assert(\n typeof action === 'string',\n 'The first element of a query key must be a string.',\n );\n\n assert(\n dataServices.includes(action.split(':')?.[0]),\n 'Queries must use data service actions.',\n );\n\n return (await messenger.call(\n action,\n ...(options.queryKey.slice(1) as Json[]),\n options.pageParam,\n )) as Json;\n },\n // TODO: Decide on values for these.\n staleTime: Infinity,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n refetchOnMount: false,\n },\n },\n });\n\n client.getQueryCache().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 = getServiceFromQueryKey(query.queryKey);\n\n if (!service) {\n return;\n }\n\n if (\n !hasSubscription &&\n event.type === 'observerAdded' &&\n observerCount === 1\n ) {\n subscriptions.add(hash);\n\n // This is a bit of a mess because we can't pass functions across the process boundary, so we call subscribe\n // but also register listeners for :cacheUpdate which will be sent to subscribed processes\n messenger.subscribe(`${service}:cacheUpdate`, cacheListener);\n\n messenger\n .call(`${service}:subscribe`, query.queryKey)\n .then((state) => hydrate(client, state))\n .catch(console.error);\n } else if (\n event.type === 'observerRemoved' &&\n observerCount === 0 &&\n hasSubscription\n ) {\n subscriptions.delete(hash);\n\n messenger.unsubscribe(`${service}:cacheUpdate`, cacheListener);\n\n messenger\n .call(`${service}:unsubscribe`, query.queryKey)\n .catch(console.error);\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 await Promise.all(\n queries.map(async (query) => {\n const service = getServiceFromQueryKey(query.queryKey);\n\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"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Json } from "@metamask/utils";
|
|
2
|
+
import { QueryClient } from "@tanstack/query-core";
|
|
3
|
+
type MessengerAdapter = {
|
|
4
|
+
call: (method: string, ...params: Json[]) => Promise<Json | void>;
|
|
5
|
+
subscribe: (method: string, callback: (data: Json) => void) => void;
|
|
6
|
+
unsubscribe: (method: string, callback: (data: Json) => void) => void;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Create a QueryClient queries and subscribes to data services using the messenger.
|
|
10
|
+
*
|
|
11
|
+
* @param dataServices - A list of data services.
|
|
12
|
+
* @param messenger - A messenger adapter.
|
|
13
|
+
* @returns The QueryClient.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createUIQueryClient(dataServices: string[], messenger: MessengerAdapter): QueryClient;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=createUIQueryClient.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createUIQueryClient.d.cts","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,IAAI,EAAE,wBAAwB;AAC/C,OAAO,EAEL,WAAW,EAMZ,6BAA6B;AAG9B,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,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC;IACpE,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC;CACvE,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EAAE,EACtB,SAAS,EAAE,gBAAgB,GAC1B,WAAW,CAqIb"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Json } from "@metamask/utils";
|
|
2
|
+
import { QueryClient } from "@tanstack/query-core";
|
|
3
|
+
type MessengerAdapter = {
|
|
4
|
+
call: (method: string, ...params: Json[]) => Promise<Json | void>;
|
|
5
|
+
subscribe: (method: string, callback: (data: Json) => void) => void;
|
|
6
|
+
unsubscribe: (method: string, callback: (data: Json) => void) => void;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Create a QueryClient queries and subscribes to data services using the messenger.
|
|
10
|
+
*
|
|
11
|
+
* @param dataServices - A list of data services.
|
|
12
|
+
* @param messenger - A messenger adapter.
|
|
13
|
+
* @returns The QueryClient.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createUIQueryClient(dataServices: string[], messenger: MessengerAdapter): QueryClient;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=createUIQueryClient.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createUIQueryClient.d.mts","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,IAAI,EAAE,wBAAwB;AAC/C,OAAO,EAEL,WAAW,EAMZ,6BAA6B;AAG9B,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,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC;IACpE,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC;CACvE,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EAAE,EACtB,SAAS,EAAE,gBAAgB,GAC1B,WAAW,CAqIb"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { assert } from "@metamask/utils";
|
|
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
|
+
* @returns The QueryClient.
|
|
9
|
+
*/
|
|
10
|
+
export function createUIQueryClient(dataServices, messenger) {
|
|
11
|
+
const subscriptions = new Set();
|
|
12
|
+
const cacheListener = (data) => {
|
|
13
|
+
const castData = data;
|
|
14
|
+
if (subscriptions.has(castData.hash)) {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
16
|
+
hydrate(client, castData.state);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const getServiceFromQueryKey = (queryKey) => {
|
|
20
|
+
try {
|
|
21
|
+
const action = queryKey[0];
|
|
22
|
+
assert(typeof action === 'string');
|
|
23
|
+
const service = action.split(':')[0];
|
|
24
|
+
assert(dataServices.includes(service));
|
|
25
|
+
return service;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const client = new QueryClient({
|
|
32
|
+
defaultOptions: {
|
|
33
|
+
queries: {
|
|
34
|
+
queryFn: async (options) => {
|
|
35
|
+
const { queryKey } = options;
|
|
36
|
+
const action = queryKey?.[0];
|
|
37
|
+
assert(typeof action === 'string', 'The first element of a query key must be a string.');
|
|
38
|
+
assert(dataServices.includes(action.split(':')?.[0]), 'Queries must use data service actions.');
|
|
39
|
+
return (await messenger.call(action, ...options.queryKey.slice(1), options.pageParam));
|
|
40
|
+
},
|
|
41
|
+
// TODO: Decide on values for these.
|
|
42
|
+
staleTime: Infinity,
|
|
43
|
+
refetchOnWindowFocus: false,
|
|
44
|
+
refetchOnReconnect: false,
|
|
45
|
+
refetchOnMount: false,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
client.getQueryCache().subscribe((event) => {
|
|
50
|
+
const { query } = event;
|
|
51
|
+
const hash = query.queryHash;
|
|
52
|
+
const hasSubscription = subscriptions.has(hash);
|
|
53
|
+
const observerCount = query.getObserversCount();
|
|
54
|
+
const service = getServiceFromQueryKey(query.queryKey);
|
|
55
|
+
if (!service) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!hasSubscription &&
|
|
59
|
+
event.type === 'observerAdded' &&
|
|
60
|
+
observerCount === 1) {
|
|
61
|
+
subscriptions.add(hash);
|
|
62
|
+
// This is a bit of a mess because we can't pass functions across the process boundary, so we call subscribe
|
|
63
|
+
// but also register listeners for :cacheUpdate which will be sent to subscribed processes
|
|
64
|
+
messenger.subscribe(`${service}:cacheUpdate`, cacheListener);
|
|
65
|
+
messenger
|
|
66
|
+
.call(`${service}:subscribe`, query.queryKey)
|
|
67
|
+
.then((state) => hydrate(client, state))
|
|
68
|
+
.catch(console.error);
|
|
69
|
+
}
|
|
70
|
+
else if (event.type === 'observerRemoved' &&
|
|
71
|
+
observerCount === 0 &&
|
|
72
|
+
hasSubscription) {
|
|
73
|
+
subscriptions.delete(hash);
|
|
74
|
+
messenger.unsubscribe(`${service}:cacheUpdate`, cacheListener);
|
|
75
|
+
messenger
|
|
76
|
+
.call(`${service}:unsubscribe`, query.queryKey)
|
|
77
|
+
.catch(console.error);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// Override invalidateQueries to ensure the data service is invalidated as well.
|
|
81
|
+
const originalInvalidate = client.invalidateQueries.bind(client);
|
|
82
|
+
// This function is defined in this way to have full support for all function overloads.
|
|
83
|
+
client.invalidateQueries = async (arg1, arg2, arg3) => {
|
|
84
|
+
const [filters, options] = parseFilterArgs(arg1, arg2, arg3);
|
|
85
|
+
const queries = client.getQueryCache().findAll(filters);
|
|
86
|
+
await Promise.all(queries.map(async (query) => {
|
|
87
|
+
const service = getServiceFromQueryKey(query.queryKey);
|
|
88
|
+
if (!service) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return messenger.call(`${service}:invalidateQueries`, filters, options);
|
|
92
|
+
}));
|
|
93
|
+
return originalInvalidate(filters, options);
|
|
94
|
+
};
|
|
95
|
+
return client;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=createUIQueryClient.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createUIQueryClient.mjs","sourceRoot":"","sources":["../src/createUIQueryClient.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert, Json } from '@metamask/utils';\nimport {\n hydrate,\n QueryClient,\n InvalidateQueryFilters,\n InvalidateOptions,\n OmitKeyof,\n parseFilterArgs,\n QueryKey,\n} from '@tanstack/query-core';\n\n// When UI messengers are available this should simply be a proper messenger that allows access to DataServiceActions\ntype MessengerAdapter = {\n call: (method: string, ...params: Json[]) => Promise<Json | void>;\n subscribe: (method: string, callback: (data: Json) => void) => void;\n unsubscribe: (method: string, callback: (data: Json) => void) => 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 * @returns The QueryClient.\n */\nexport function createUIQueryClient(\n dataServices: string[],\n messenger: MessengerAdapter,\n): QueryClient {\n const subscriptions = new Set<string>();\n\n const cacheListener = (data: Json): void => {\n const castData = data as { hash: string; state: Json };\n if (subscriptions.has(castData.hash)) {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n hydrate(client, castData.state);\n }\n };\n\n const getServiceFromQueryKey = (queryKey: QueryKey): string | null => {\n try {\n const action = queryKey[0];\n assert(typeof action === 'string');\n\n const service = action.split(':')[0];\n assert(dataServices.includes(service));\n\n return service;\n } catch {\n return null;\n }\n };\n\n const client: QueryClient = new QueryClient({\n defaultOptions: {\n queries: {\n queryFn: async (options): Promise<Json> => {\n const { queryKey } = options;\n\n const action = queryKey?.[0];\n\n assert(\n typeof action === 'string',\n 'The first element of a query key must be a string.',\n );\n\n assert(\n dataServices.includes(action.split(':')?.[0]),\n 'Queries must use data service actions.',\n );\n\n return (await messenger.call(\n action,\n ...(options.queryKey.slice(1) as Json[]),\n options.pageParam,\n )) as Json;\n },\n // TODO: Decide on values for these.\n staleTime: Infinity,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n refetchOnMount: false,\n },\n },\n });\n\n client.getQueryCache().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 = getServiceFromQueryKey(query.queryKey);\n\n if (!service) {\n return;\n }\n\n if (\n !hasSubscription &&\n event.type === 'observerAdded' &&\n observerCount === 1\n ) {\n subscriptions.add(hash);\n\n // This is a bit of a mess because we can't pass functions across the process boundary, so we call subscribe\n // but also register listeners for :cacheUpdate which will be sent to subscribed processes\n messenger.subscribe(`${service}:cacheUpdate`, cacheListener);\n\n messenger\n .call(`${service}:subscribe`, query.queryKey)\n .then((state) => hydrate(client, state))\n .catch(console.error);\n } else if (\n event.type === 'observerRemoved' &&\n observerCount === 0 &&\n hasSubscription\n ) {\n subscriptions.delete(hash);\n\n messenger.unsubscribe(`${service}:cacheUpdate`, cacheListener);\n\n messenger\n .call(`${service}:unsubscribe`, query.queryKey)\n .catch(console.error);\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 await Promise.all(\n queries.map(async (query) => {\n const service = getServiceFromQueryKey(query.queryKey);\n\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/dist/index.cjs
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* @param name - The name to greet.
|
|
7
|
-
* @returns The greeting.
|
|
8
|
-
*/
|
|
9
|
-
function greeter(name) {
|
|
10
|
-
return `Hello, ${name}!`;
|
|
11
|
-
}
|
|
12
|
-
exports.default = greeter;
|
|
17
|
+
__exportStar(require("./BaseDataService.cjs"), exports);
|
|
18
|
+
__exportStar(require("./createUIQueryClient.cjs"), exports);
|
|
13
19
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wDAAkC;AAClC,4DAAsC","sourcesContent":["export * from './BaseDataService';\nexport * from './createUIQueryClient';\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* @param name - The name to greet.
|
|
5
|
-
* @returns The greeting.
|
|
6
|
-
*/
|
|
7
|
-
export default function greeter(name: string): string;
|
|
1
|
+
export * from "./BaseDataService.cjs";
|
|
2
|
+
export * from "./createUIQueryClient.cjs";
|
|
8
3
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sCAAkC;AAClC,0CAAsC"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* @param name - The name to greet.
|
|
5
|
-
* @returns The greeting.
|
|
6
|
-
*/
|
|
7
|
-
export default function greeter(name: string): string;
|
|
1
|
+
export * from "./BaseDataService.mjs";
|
|
2
|
+
export * from "./createUIQueryClient.mjs";
|
|
8
3
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sCAAkC;AAClC,0CAAsC"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* @param name - The name to greet.
|
|
5
|
-
* @returns The greeting.
|
|
6
|
-
*/
|
|
7
|
-
export default function greeter(name) {
|
|
8
|
-
return `Hello, ${name}!`;
|
|
9
|
-
}
|
|
1
|
+
export * from "./BaseDataService.mjs";
|
|
2
|
+
export * from "./createUIQueryClient.mjs";
|
|
10
3
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sCAAkC;AAClC,0CAAsC","sourcesContent":["export * from './BaseDataService';\nexport * from './createUIQueryClient';\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/base-data-service",
|
|
3
|
-
"version": "0.0.0-preview-
|
|
3
|
+
"version": "0.0.0-preview-86eff6358",
|
|
4
4
|
"description": "Provides utilities for building data services",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -47,12 +47,19 @@
|
|
|
47
47
|
"test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
|
|
48
48
|
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
|
|
49
49
|
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@metamask/messenger": "^0.3.0",
|
|
52
|
+
"@metamask/utils": "^11.9.0",
|
|
53
|
+
"@tanstack/query-core": "^4.43.0",
|
|
54
|
+
"fast-deep-equal": "^3.1.3"
|
|
55
|
+
},
|
|
50
56
|
"devDependencies": {
|
|
51
57
|
"@metamask/auto-changelog": "^3.4.4",
|
|
52
58
|
"@ts-bridge/cli": "^0.6.4",
|
|
53
59
|
"@types/jest": "^29.5.14",
|
|
54
60
|
"deepmerge": "^4.2.2",
|
|
55
61
|
"jest": "^29.7.0",
|
|
62
|
+
"nock": "^13.3.1",
|
|
56
63
|
"ts-jest": "^29.2.5",
|
|
57
64
|
"typedoc": "^0.25.13",
|
|
58
65
|
"typedoc-plugin-missing-exports": "^2.0.0",
|