@metamask-previews/assets-controller 2.0.2-preview-980f677 → 2.0.2-preview-3d4d0d0ef

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +0 -8
  2. package/dist/AssetsController.cjs +27 -118
  3. package/dist/AssetsController.cjs.map +1 -1
  4. package/dist/AssetsController.d.cts +4 -16
  5. package/dist/AssetsController.d.cts.map +1 -1
  6. package/dist/AssetsController.d.mts +4 -16
  7. package/dist/AssetsController.d.mts.map +1 -1
  8. package/dist/AssetsController.mjs +27 -118
  9. package/dist/AssetsController.mjs.map +1 -1
  10. package/dist/data-sources/AccountsApiDataSource.cjs +0 -1
  11. package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
  12. package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
  13. package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
  14. package/dist/data-sources/AccountsApiDataSource.mjs +0 -1
  15. package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
  16. package/dist/data-sources/BackendWebsocketDataSource.cjs +1 -1
  17. package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -1
  18. package/dist/data-sources/BackendWebsocketDataSource.mjs +1 -1
  19. package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -1
  20. package/dist/data-sources/PriceDataSource.cjs +14 -20
  21. package/dist/data-sources/PriceDataSource.cjs.map +1 -1
  22. package/dist/data-sources/PriceDataSource.d.cts +2 -2
  23. package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
  24. package/dist/data-sources/PriceDataSource.d.mts +2 -2
  25. package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
  26. package/dist/data-sources/PriceDataSource.mjs +14 -20
  27. package/dist/data-sources/PriceDataSource.mjs.map +1 -1
  28. package/dist/data-sources/RpcDataSource.cjs +0 -2
  29. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  30. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  31. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  32. package/dist/data-sources/RpcDataSource.mjs +0 -2
  33. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  34. package/dist/data-sources/SnapDataSource.cjs +2 -3
  35. package/dist/data-sources/SnapDataSource.cjs.map +1 -1
  36. package/dist/data-sources/SnapDataSource.d.cts.map +1 -1
  37. package/dist/data-sources/SnapDataSource.d.mts.map +1 -1
  38. package/dist/data-sources/SnapDataSource.mjs +2 -3
  39. package/dist/data-sources/SnapDataSource.mjs.map +1 -1
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +1 -1
  42. package/dist/index.d.cts.map +1 -1
  43. package/dist/index.d.mts +1 -1
  44. package/dist/index.d.mts.map +1 -1
  45. package/dist/index.mjs.map +1 -1
  46. package/dist/middlewares/index.cjs +1 -5
  47. package/dist/middlewares/index.cjs.map +1 -1
  48. package/dist/middlewares/index.d.cts +0 -2
  49. package/dist/middlewares/index.d.cts.map +1 -1
  50. package/dist/middlewares/index.d.mts +0 -2
  51. package/dist/middlewares/index.d.mts.map +1 -1
  52. package/dist/middlewares/index.mjs +0 -1
  53. package/dist/middlewares/index.mjs.map +1 -1
  54. package/dist/types.cjs.map +1 -1
  55. package/dist/types.d.cts +0 -16
  56. package/dist/types.d.cts.map +1 -1
  57. package/dist/types.d.mts +0 -16
  58. package/dist/types.d.mts.map +1 -1
  59. package/dist/types.mjs.map +1 -1
  60. package/package.json +3 -4
  61. package/dist/middlewares/ParallelMiddleware.cjs +0 -216
  62. package/dist/middlewares/ParallelMiddleware.cjs.map +0 -1
  63. package/dist/middlewares/ParallelMiddleware.d.cts +0 -45
  64. package/dist/middlewares/ParallelMiddleware.d.cts.map +0 -1
  65. package/dist/middlewares/ParallelMiddleware.d.mts +0 -45
  66. package/dist/middlewares/ParallelMiddleware.d.mts.map +0 -1
  67. package/dist/middlewares/ParallelMiddleware.mjs +0 -214
  68. package/dist/middlewares/ParallelMiddleware.mjs.map +0 -1
@@ -1,214 +0,0 @@
1
- function $importDefault(module) {
2
- if (module?.__esModule) {
3
- return module.default;
4
- }
5
- return module;
6
- }
7
- import $pLimit from "p-limit/index.js";
8
- const pLimit = $importDefault($pLimit);
9
- // ============================================================================
10
- // MERGE HELPER
11
- // ============================================================================
12
- /**
13
- * Deep-merge multiple DataResponses into one.
14
- * Used when running balance data sources in parallel.
15
- *
16
- * @param responses - Array of DataResponse from each source.
17
- * @returns Single merged DataResponse.
18
- */
19
- export function mergeDataResponses(responses) {
20
- const merged = {};
21
- for (const response of responses) {
22
- if (response.assetsBalance) {
23
- merged.assetsBalance ?? (merged.assetsBalance = {});
24
- for (const [accountId, accountBalances] of Object.entries(response.assetsBalance)) {
25
- merged.assetsBalance[accountId] = {
26
- ...(merged.assetsBalance[accountId] ?? {}),
27
- ...accountBalances,
28
- };
29
- }
30
- }
31
- if (response.assetsInfo) {
32
- merged.assetsInfo = {
33
- ...(merged.assetsInfo ?? {}),
34
- ...response.assetsInfo,
35
- };
36
- }
37
- if (response.assetsPrice) {
38
- merged.assetsPrice = {
39
- ...(merged.assetsPrice ?? {}),
40
- ...response.assetsPrice,
41
- };
42
- }
43
- if (response.errors) {
44
- merged.errors = {
45
- ...(merged.errors ?? {}),
46
- ...response.errors,
47
- };
48
- }
49
- if (response.detectedAssets) {
50
- merged.detectedAssets = {
51
- ...(merged.detectedAssets ?? {}),
52
- ...response.detectedAssets,
53
- };
54
- }
55
- if (response.updateMode === 'full') {
56
- merged.updateMode = 'full';
57
- }
58
- }
59
- merged.updateMode ?? (merged.updateMode = 'merge');
60
- return merged;
61
- }
62
- // ============================================================================
63
- // PARALLEL BALANCE MIDDLEWARE
64
- // ============================================================================
65
- const PARALLEL_BALANCE_MIDDLEWARE_NAME = 'ParallelBalanceMiddleware';
66
- /** Max concurrent balance source calls (round 1 and fallback). */
67
- const BALANCE_CONCURRENCY = 3;
68
- /**
69
- * Partition request.chainIds so each chain is assigned to exactly one source
70
- * (by source order: first source that supports the chain gets it). Ensures no
71
- * chain overlap across data source calls.
72
- *
73
- * @param request - The data request with chainIds to partition.
74
- * @param sources - Balance sources in priority order (e.g. AccountsAPI, Snap, Rpc).
75
- * @returns Array of requests, one per source, each with only that source's assigned chainIds.
76
- */
77
- function partitionChainsBySource(request, sources) {
78
- const { chainIds } = request;
79
- const assigned = new Set();
80
- return sources.map((source) => {
81
- const supported = new Set(source.getActiveChainsSync());
82
- const chainsForSource = chainIds.filter((id) => supported.has(id) && !assigned.has(id));
83
- chainsForSource.forEach((id) => assigned.add(id));
84
- return {
85
- ...request,
86
- chainIds: chainsForSource,
87
- };
88
- });
89
- }
90
- /**
91
- * Collect chain IDs that failed in the first round (present in response.errors).
92
- * Used to run a fallback round with remaining sources.
93
- *
94
- * @param requests - Partitioned requests, one per source (same order as results).
95
- * @param results - Results from each source; chain IDs in requests[i] that have errors in results[i].response.errors are considered failed.
96
- * @returns Set of chain IDs that had errors in the first round.
97
- */
98
- function getFailedChainIds(requests, results) {
99
- const failed = new Set();
100
- for (let i = 0; i < results.length; i++) {
101
- const errors = results[i].response.errors ?? {};
102
- for (const chainId of requests[i].chainIds) {
103
- if (errors[chainId]) {
104
- failed.add(chainId);
105
- }
106
- }
107
- }
108
- return failed;
109
- }
110
- /**
111
- * Middleware that runs multiple balance data source middlewares in parallel,
112
- * with no chain overlap. Chains that fail (response.errors) are re-partitioned
113
- * and fetched again in a fallback round so lower-priority sources can try them.
114
- *
115
- * @param sources - Array of balance sources in priority order (each with getName(), getActiveChainsSync(), assetsMiddleware).
116
- * @returns A single middleware that runs all sources in parallel and merges responses.
117
- */
118
- export function createParallelBalanceMiddleware(sources) {
119
- return {
120
- getName() {
121
- return PARALLEL_BALANCE_MIDDLEWARE_NAME;
122
- },
123
- assetsMiddleware: async (context, next) => {
124
- if (sources.length === 0) {
125
- return next(context);
126
- }
127
- const noopNext = async (ctx) => ctx;
128
- const limit = pLimit(BALANCE_CONCURRENCY);
129
- // Round 1: partition chains (no overlap), run with limited concurrency
130
- const requests = partitionChainsBySource(context.request, sources);
131
- const results = await Promise.all(sources.map((source, i) => limit(() => source.assetsMiddleware({
132
- request: requests[i],
133
- response: {},
134
- getAssetsState: context.getAssetsState,
135
- }, noopNext))));
136
- let mergedResponse = mergeDataResponses(results.map((result) => result.response));
137
- // Fallback: chains that failed (in errors) get re-partitioned and tried again
138
- const failedChainIds = getFailedChainIds(requests, results);
139
- if (failedChainIds.size > 0) {
140
- const fallbackRequest = {
141
- ...context.request,
142
- chainIds: [...failedChainIds],
143
- };
144
- const fallbackRequests = partitionChainsBySource(fallbackRequest, sources);
145
- const fallbackResults = await Promise.all(sources.map((source, i) => limit(() => source.assetsMiddleware({
146
- request: fallbackRequests[i],
147
- response: {},
148
- getAssetsState: context.getAssetsState,
149
- }, noopNext))));
150
- const fallbackMerged = mergeDataResponses(fallbackResults.map((result) => result.response));
151
- mergedResponse = mergeDataResponses([mergedResponse, fallbackMerged]);
152
- // Remove errors for chains we successfully got balance for in fallback
153
- if (mergedResponse.errors && mergedResponse.assetsBalance) {
154
- const chainsWithBalance = new Set();
155
- for (const accountBalances of Object.values(mergedResponse.assetsBalance)) {
156
- for (const assetId of Object.keys(accountBalances)) {
157
- const chainId = assetId.split('/')[0];
158
- chainsWithBalance.add(chainId);
159
- }
160
- }
161
- for (const chainId of failedChainIds) {
162
- if (chainsWithBalance.has(chainId)) {
163
- delete mergedResponse.errors[chainId];
164
- }
165
- }
166
- }
167
- }
168
- return next({
169
- ...context,
170
- response: mergeDataResponses([context.response, mergedResponse]),
171
- });
172
- },
173
- };
174
- }
175
- // ============================================================================
176
- // PARALLEL TOKEN/PRICE MIDDLEWARE
177
- // ============================================================================
178
- const PARALLEL_MIDDLEWARE_NAME = 'ParallelMiddleware';
179
- /** Max concurrent token/price source calls. */
180
- const CONCURRENCY = 2;
181
- /**
182
- * Middleware that runs multiple data source middlewares (e.g. TokenDataSource,
183
- * PriceDataSource) in parallel with the same request. Responses are merged so
184
- * that assetsInfo (token metadata) and assetsPrice are combined. Use this to
185
- * fetch token and price data concurrently instead of sequentially.
186
- *
187
- * @param sources - Array of sources with getName() and assetsMiddleware.
188
- * @returns A single middleware that runs all sources in parallel and merges responses.
189
- */
190
- export function createParallelMiddleware(sources) {
191
- return {
192
- getName() {
193
- return PARALLEL_MIDDLEWARE_NAME;
194
- },
195
- assetsMiddleware: async (context, next) => {
196
- if (sources.length === 0) {
197
- return next(context);
198
- }
199
- const noopNext = async (ctx) => ctx;
200
- const limit = pLimit(CONCURRENCY);
201
- const results = await Promise.all(sources.map((source) => limit(() => source.assetsMiddleware({
202
- request: context.request,
203
- response: { ...context.response },
204
- getAssetsState: context.getAssetsState,
205
- }, noopNext))));
206
- const mergedResponse = mergeDataResponses(results.map((result) => result.response));
207
- return next({
208
- ...context,
209
- response: mergeDataResponses([context.response, mergedResponse]),
210
- });
211
- },
212
- };
213
- }
214
- //# sourceMappingURL=ParallelMiddleware.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ParallelMiddleware.mjs","sourceRoot":"","sources":["../../src/middlewares/ParallelMiddleware.ts"],"names":[],"mappings":"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","sourcesContent":["import pLimit from 'p-limit';\n\nimport type {\n ChainId,\n Context,\n DataRequest,\n DataResponse,\n Middleware,\n} from '../types';\n\n// ============================================================================\n// MERGE HELPER\n// ============================================================================\n\n/**\n * Deep-merge multiple DataResponses into one.\n * Used when running balance data sources in parallel.\n *\n * @param responses - Array of DataResponse from each source.\n * @returns Single merged DataResponse.\n */\nexport function mergeDataResponses(responses: DataResponse[]): DataResponse {\n const merged: DataResponse = {};\n\n for (const response of responses) {\n if (response.assetsBalance) {\n merged.assetsBalance ??= {};\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n merged.assetsBalance[accountId] = {\n ...(merged.assetsBalance[accountId] ?? {}),\n ...accountBalances,\n };\n }\n }\n if (response.assetsInfo) {\n merged.assetsInfo = {\n ...(merged.assetsInfo ?? {}),\n ...response.assetsInfo,\n };\n }\n if (response.assetsPrice) {\n merged.assetsPrice = {\n ...(merged.assetsPrice ?? {}),\n ...response.assetsPrice,\n };\n }\n if (response.errors) {\n merged.errors = {\n ...(merged.errors ?? {}),\n ...response.errors,\n };\n }\n if (response.detectedAssets) {\n merged.detectedAssets = {\n ...(merged.detectedAssets ?? {}),\n ...response.detectedAssets,\n };\n }\n if (response.updateMode === 'full') {\n merged.updateMode = 'full';\n }\n }\n merged.updateMode ??= 'merge';\n\n return merged;\n}\n\n// ============================================================================\n// PARALLEL BALANCE MIDDLEWARE\n// ============================================================================\n\nconst PARALLEL_BALANCE_MIDDLEWARE_NAME = 'ParallelBalanceMiddleware';\n\n/** Max concurrent balance source calls (round 1 and fallback). */\nconst BALANCE_CONCURRENCY = 3;\n\nexport type BalanceSource = {\n getName(): string;\n /** Chains this source can fetch (e.g. from getActiveChainsSync()). Used to partition chains with no overlap. */\n getActiveChainsSync(): ChainId[];\n assetsMiddleware: Middleware;\n};\n\n/**\n * Partition request.chainIds so each chain is assigned to exactly one source\n * (by source order: first source that supports the chain gets it). Ensures no\n * chain overlap across data source calls.\n *\n * @param request - The data request with chainIds to partition.\n * @param sources - Balance sources in priority order (e.g. AccountsAPI, Snap, Rpc).\n * @returns Array of requests, one per source, each with only that source's assigned chainIds.\n */\nfunction partitionChainsBySource(\n request: DataRequest,\n sources: BalanceSource[],\n): DataRequest[] {\n const { chainIds } = request;\n const assigned = new Set<ChainId>();\n\n return sources.map((source) => {\n const supported = new Set(source.getActiveChainsSync());\n const chainsForSource = chainIds.filter(\n (id) => supported.has(id) && !assigned.has(id),\n );\n chainsForSource.forEach((id) => assigned.add(id));\n\n return {\n ...request,\n chainIds: chainsForSource,\n };\n });\n}\n\n/**\n * Collect chain IDs that failed in the first round (present in response.errors).\n * Used to run a fallback round with remaining sources.\n *\n * @param requests - Partitioned requests, one per source (same order as results).\n * @param results - Results from each source; chain IDs in requests[i] that have errors in results[i].response.errors are considered failed.\n * @returns Set of chain IDs that had errors in the first round.\n */\nfunction getFailedChainIds(\n requests: DataRequest[],\n results: { response: DataResponse }[],\n): Set<ChainId> {\n const failed = new Set<ChainId>();\n for (let i = 0; i < results.length; i++) {\n const errors = results[i].response.errors ?? {};\n for (const chainId of requests[i].chainIds) {\n if (errors[chainId]) {\n failed.add(chainId);\n }\n }\n }\n return failed;\n}\n\n/**\n * Middleware that runs multiple balance data source middlewares in parallel,\n * with no chain overlap. Chains that fail (response.errors) are re-partitioned\n * and fetched again in a fallback round so lower-priority sources can try them.\n *\n * @param sources - Array of balance sources in priority order (each with getName(), getActiveChainsSync(), assetsMiddleware).\n * @returns A single middleware that runs all sources in parallel and merges responses.\n */\nexport function createParallelBalanceMiddleware(sources: BalanceSource[]): {\n getName(): string;\n assetsMiddleware: Middleware;\n} {\n return {\n getName(): string {\n return PARALLEL_BALANCE_MIDDLEWARE_NAME;\n },\n\n assetsMiddleware: async (context, next): Promise<Context> => {\n if (sources.length === 0) {\n return next(context);\n }\n\n const noopNext = async (ctx: typeof context): Promise<typeof context> =>\n ctx;\n const limit = pLimit(BALANCE_CONCURRENCY);\n\n // Round 1: partition chains (no overlap), run with limited concurrency\n const requests = partitionChainsBySource(context.request, sources);\n const results = await Promise.all(\n sources.map((source, i) =>\n limit(() =>\n source.assetsMiddleware(\n {\n request: requests[i],\n response: {},\n getAssetsState: context.getAssetsState,\n },\n noopNext,\n ),\n ),\n ),\n );\n\n let mergedResponse = mergeDataResponses(\n results.map((result) => result.response),\n );\n\n // Fallback: chains that failed (in errors) get re-partitioned and tried again\n const failedChainIds = getFailedChainIds(requests, results);\n if (failedChainIds.size > 0) {\n const fallbackRequest: DataRequest = {\n ...context.request,\n chainIds: [...failedChainIds],\n };\n const fallbackRequests = partitionChainsBySource(\n fallbackRequest,\n sources,\n );\n const fallbackResults = await Promise.all(\n sources.map((source, i) =>\n limit(() =>\n source.assetsMiddleware(\n {\n request: fallbackRequests[i],\n response: {},\n getAssetsState: context.getAssetsState,\n },\n noopNext,\n ),\n ),\n ),\n );\n const fallbackMerged = mergeDataResponses(\n fallbackResults.map((result) => result.response),\n );\n mergedResponse = mergeDataResponses([mergedResponse, fallbackMerged]);\n // Remove errors for chains we successfully got balance for in fallback\n if (mergedResponse.errors && mergedResponse.assetsBalance) {\n const chainsWithBalance = new Set<ChainId>();\n for (const accountBalances of Object.values(\n mergedResponse.assetsBalance,\n )) {\n for (const assetId of Object.keys(accountBalances)) {\n const chainId = assetId.split('/')[0] as ChainId;\n chainsWithBalance.add(chainId);\n }\n }\n for (const chainId of failedChainIds) {\n if (chainsWithBalance.has(chainId)) {\n delete mergedResponse.errors[chainId];\n }\n }\n }\n }\n\n return next({\n ...context,\n response: mergeDataResponses([context.response, mergedResponse]),\n });\n },\n };\n}\n\n// ============================================================================\n// PARALLEL TOKEN/PRICE MIDDLEWARE\n// ============================================================================\n\nconst PARALLEL_MIDDLEWARE_NAME = 'ParallelMiddleware';\n\n/** Max concurrent token/price source calls. */\nconst CONCURRENCY = 2;\n\nexport type TokenPriceSource = {\n getName(): string;\n assetsMiddleware: Middleware;\n};\n\n/**\n * Middleware that runs multiple data source middlewares (e.g. TokenDataSource,\n * PriceDataSource) in parallel with the same request. Responses are merged so\n * that assetsInfo (token metadata) and assetsPrice are combined. Use this to\n * fetch token and price data concurrently instead of sequentially.\n *\n * @param sources - Array of sources with getName() and assetsMiddleware.\n * @returns A single middleware that runs all sources in parallel and merges responses.\n */\nexport function createParallelMiddleware(sources: TokenPriceSource[]): {\n getName(): string;\n assetsMiddleware: Middleware;\n} {\n return {\n getName(): string {\n return PARALLEL_MIDDLEWARE_NAME;\n },\n\n assetsMiddleware: async (context, next): Promise<Context> => {\n if (sources.length === 0) {\n return next(context);\n }\n\n const noopNext = async (ctx: typeof context): Promise<typeof context> =>\n ctx;\n const limit = pLimit(CONCURRENCY);\n\n const results = await Promise.all(\n sources.map((source) =>\n limit(() =>\n source.assetsMiddleware(\n {\n request: context.request,\n response: { ...context.response },\n getAssetsState: context.getAssetsState,\n },\n noopNext,\n ),\n ),\n ),\n );\n\n const mergedResponse = mergeDataResponses(\n results.map((result) => result.response),\n );\n\n return next({\n ...context,\n response: mergeDataResponses([context.response, mergedResponse]),\n });\n },\n };\n}\n"]}