@metamask-previews/ramps-controller 8.0.0-preview-27e39dd44 → 8.0.0-preview-d7935cb09
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 +8 -0
- package/dist/RampsController.cjs +175 -113
- package/dist/RampsController.cjs.map +1 -1
- package/dist/RampsController.d.cts +30 -9
- package/dist/RampsController.d.cts.map +1 -1
- package/dist/RampsController.d.mts +30 -9
- package/dist/RampsController.d.mts.map +1 -1
- package/dist/RampsController.mjs +175 -113
- package/dist/RampsController.mjs.map +1 -1
- package/dist/RequestCache.cjs.map +1 -1
- package/dist/RequestCache.d.cts +2 -0
- package/dist/RequestCache.d.cts.map +1 -1
- package/dist/RequestCache.d.mts +2 -0
- package/dist/RequestCache.d.mts.map +1 -1
- package/dist/RequestCache.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Add `widgetUrl` resource state that automatically fetches and stores the buy widget URL whenever the selected quote changes ([#7920](https://github.com/MetaMask/core/pull/7920))
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Refactor: Consolidate reset logic with a shared resetResource helper and fix abort handling for dependent resources ([#7818](https://github.com/MetaMask/core/pull/7818))
|
|
17
|
+
|
|
10
18
|
## [8.0.0]
|
|
11
19
|
|
|
12
20
|
### Changed
|
package/dist/RampsController.cjs
CHANGED
|
@@ -10,7 +10,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
10
10
|
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");
|
|
11
11
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
12
|
};
|
|
13
|
-
var _RampsController_instances, _RampsController_requestCacheTTL, _RampsController_requestCacheMaxSize, _RampsController_pendingRequests, _RampsController_pendingResourceCount, _RampsController_quotePollingInterval, _RampsController_quotePollingOptions, _RampsController_clearPendingResourceCountForDependentResources, _RampsController_removeRequestState, _RampsController_cleanupState, _RampsController_fireAndForget, _RampsController_restartPollingIfActive, _RampsController_updateResourceField, _RampsController_setResourceLoading, _RampsController_setResourceError, _RampsController_updateRequestState;
|
|
13
|
+
var _RampsController_instances, _RampsController_requestCacheTTL, _RampsController_requestCacheMaxSize, _RampsController_pendingRequests, _RampsController_pendingResourceCount, _RampsController_quotePollingInterval, _RampsController_quotePollingOptions, _RampsController_clearPendingResourceCountForDependentResources, _RampsController_abortDependentRequests, _RampsController_mutateRequests, _RampsController_removeRequestState, _RampsController_cleanupState, _RampsController_fireAndForget, _RampsController_restartPollingIfActive, _RampsController_requireRegion, _RampsController_isRegionCurrent, _RampsController_isTokenCurrent, _RampsController_isProviderCurrent, _RampsController_updateResourceField, _RampsController_setResourceLoading, _RampsController_setResourceError, _RampsController_updateRequestState, _RampsController_syncWidgetUrl;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.RampsController = exports.getDefaultRampsControllerState = exports.RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS = exports.controllerName = void 0;
|
|
16
16
|
const base_controller_1 = require("@metamask/base-controller");
|
|
@@ -81,6 +81,12 @@ const rampsControllerMetadata = {
|
|
|
81
81
|
includeInStateLogs: false,
|
|
82
82
|
usedInUi: true,
|
|
83
83
|
},
|
|
84
|
+
widgetUrl: {
|
|
85
|
+
persist: false,
|
|
86
|
+
includeInDebugSnapshot: true,
|
|
87
|
+
includeInStateLogs: false,
|
|
88
|
+
usedInUi: true,
|
|
89
|
+
},
|
|
84
90
|
requests: {
|
|
85
91
|
persist: false,
|
|
86
92
|
includeInDebugSnapshot: true,
|
|
@@ -121,10 +127,39 @@ function getDefaultRampsControllerState() {
|
|
|
121
127
|
tokens: createDefaultResourceState(null, null),
|
|
122
128
|
paymentMethods: createDefaultResourceState([], null),
|
|
123
129
|
quotes: createDefaultResourceState(null, null),
|
|
130
|
+
widgetUrl: createDefaultResourceState(null),
|
|
124
131
|
requests: {},
|
|
125
132
|
};
|
|
126
133
|
}
|
|
127
134
|
exports.getDefaultRampsControllerState = getDefaultRampsControllerState;
|
|
135
|
+
const DEPENDENT_RESOURCE_KEYS = [
|
|
136
|
+
'providers',
|
|
137
|
+
'tokens',
|
|
138
|
+
'paymentMethods',
|
|
139
|
+
'quotes',
|
|
140
|
+
];
|
|
141
|
+
const DEPENDENT_RESOURCE_KEYS_SET = new Set(DEPENDENT_RESOURCE_KEYS);
|
|
142
|
+
function resetResource(state, resourceType, defaultResource) {
|
|
143
|
+
const def = defaultResource ?? getDefaultRampsControllerState()[resourceType];
|
|
144
|
+
const resource = state[resourceType];
|
|
145
|
+
resource.data = def.data;
|
|
146
|
+
resource.selected = def.selected;
|
|
147
|
+
resource.isLoading = def.isLoading;
|
|
148
|
+
resource.error = def.error;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Resets the widgetUrl resource to its default state.
|
|
152
|
+
* Mutates state in place; use from within controller update() for atomic updates.
|
|
153
|
+
*
|
|
154
|
+
* @param state - The state object to mutate.
|
|
155
|
+
*/
|
|
156
|
+
function resetWidgetUrl(state) {
|
|
157
|
+
const def = getDefaultRampsControllerState().widgetUrl;
|
|
158
|
+
state.widgetUrl.data = def.data;
|
|
159
|
+
state.widgetUrl.selected = def.selected;
|
|
160
|
+
state.widgetUrl.isLoading = def.isLoading;
|
|
161
|
+
state.widgetUrl.error = def.error;
|
|
162
|
+
}
|
|
128
163
|
/**
|
|
129
164
|
* Resets region-dependent resources (userRegion, providers, tokens, paymentMethods, quotes).
|
|
130
165
|
* Mutates state in place; use from within controller update() for atomic updates.
|
|
@@ -137,22 +172,11 @@ function resetDependentResources(state, options) {
|
|
|
137
172
|
if (options?.clearUserRegionData) {
|
|
138
173
|
state.userRegion = null;
|
|
139
174
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
state
|
|
145
|
-
state.tokens.data = null;
|
|
146
|
-
state.tokens.isLoading = false;
|
|
147
|
-
state.tokens.error = null;
|
|
148
|
-
state.paymentMethods.data = [];
|
|
149
|
-
state.paymentMethods.selected = null;
|
|
150
|
-
state.paymentMethods.isLoading = false;
|
|
151
|
-
state.paymentMethods.error = null;
|
|
152
|
-
state.quotes.data = null;
|
|
153
|
-
state.quotes.selected = null;
|
|
154
|
-
state.quotes.isLoading = false;
|
|
155
|
-
state.quotes.error = null;
|
|
175
|
+
const defaultState = getDefaultRampsControllerState();
|
|
176
|
+
for (const key of DEPENDENT_RESOURCE_KEYS) {
|
|
177
|
+
resetResource(state, key, defaultState[key]);
|
|
178
|
+
}
|
|
179
|
+
resetWidgetUrl(state);
|
|
156
180
|
}
|
|
157
181
|
// === HELPER FUNCTIONS ===
|
|
158
182
|
/**
|
|
@@ -276,31 +300,49 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
276
300
|
__classPrivateFieldSet(this, _RampsController_requestCacheMaxSize, requestCacheMaxSize, "f");
|
|
277
301
|
}
|
|
278
302
|
/**
|
|
279
|
-
* Executes a request with caching and
|
|
303
|
+
* Executes a request with caching, deduplication, and at most one in-flight
|
|
304
|
+
* request per resource type.
|
|
280
305
|
*
|
|
281
|
-
* If a request with
|
|
282
|
-
*
|
|
283
|
-
* a new request.
|
|
306
|
+
* 1. **Same cache key in flight** – If a request with this cache key is
|
|
307
|
+
* already pending, returns that promise (deduplication; no second request).
|
|
284
308
|
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
309
|
+
* 2. **Cache hit** – If valid, non-expired data exists in state.requests for
|
|
310
|
+
* this key and forceRefresh is not set, returns that data without fetching.
|
|
311
|
+
*
|
|
312
|
+
* 3. **New request** – Creates an AbortController and fires the fetcher.
|
|
313
|
+
* If options.resourceType is set, tags the pending request with that
|
|
314
|
+
* resource type (so #abortDependentRequests can cancel it on region
|
|
315
|
+
* change or cleanup) and ref-counts resource-level loading state.
|
|
316
|
+
* On success or error, updates request state and resource error;
|
|
317
|
+
* in finally, clears resource loading only if this request was not
|
|
318
|
+
* aborted.
|
|
319
|
+
*
|
|
320
|
+
* @param cacheKey - Unique identifier for this request (e.g. from createCacheKey).
|
|
321
|
+
* @param fetcher - Async function that performs the fetch. Receives an AbortSignal
|
|
322
|
+
* that is aborted when this request is superseded by another for the same resource.
|
|
323
|
+
* @param options - Optional forceRefresh, ttl, and resourceType for loading/error state.
|
|
324
|
+
* @returns The result of the request (from cache, joined promise, or fetcher).
|
|
289
325
|
*/
|
|
290
326
|
async executeRequest(cacheKey, fetcher, options) {
|
|
327
|
+
// Get TTL for verifying cache expiration
|
|
291
328
|
const ttl = options?.ttl ?? __classPrivateFieldGet(this, _RampsController_requestCacheTTL, "f");
|
|
292
|
-
//
|
|
329
|
+
// DEDUPLICATION:
|
|
330
|
+
// Check if a request is already in flight for this cache key
|
|
331
|
+
// If so, return the original promise for that request
|
|
293
332
|
const pending = __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").get(cacheKey);
|
|
294
333
|
if (pending) {
|
|
295
334
|
return pending.promise;
|
|
296
335
|
}
|
|
336
|
+
// CACHE HIT:
|
|
337
|
+
// If cache is not expired, return the cached data
|
|
297
338
|
if (!options?.forceRefresh) {
|
|
298
339
|
const cached = this.state.requests[cacheKey];
|
|
299
340
|
if (cached && !(0, RequestCache_1.isCacheExpired)(cached, ttl)) {
|
|
300
341
|
return cached.data;
|
|
301
342
|
}
|
|
302
343
|
}
|
|
303
|
-
// Create abort controller for this request
|
|
344
|
+
// Create a new abort controller for this request
|
|
345
|
+
// Record the time the request was started
|
|
304
346
|
const abortController = new AbortController();
|
|
305
347
|
const lastFetchedAt = Date.now();
|
|
306
348
|
const { resourceType } = options ?? {};
|
|
@@ -319,15 +361,11 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
319
361
|
const promise = (async () => {
|
|
320
362
|
try {
|
|
321
363
|
const data = await fetcher(abortController.signal);
|
|
322
|
-
// Don't update state if aborted
|
|
323
364
|
if (abortController.signal.aborted) {
|
|
324
365
|
throw new Error('Request was aborted');
|
|
325
366
|
}
|
|
326
367
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_updateRequestState).call(this, cacheKey, (0, RequestCache_1.createSuccessState)(data, lastFetchedAt));
|
|
327
368
|
if (resourceType) {
|
|
328
|
-
// We need the extra logic because there are two situations where we’re allowed to clear the error:
|
|
329
|
-
// No callback → always clear
|
|
330
|
-
// Callback present → clear only when isResultCurrent() returns true.
|
|
331
369
|
const isCurrent = !options?.isResultCurrent || options.isResultCurrent();
|
|
332
370
|
if (isCurrent) {
|
|
333
371
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_setResourceError).call(this, resourceType, null);
|
|
@@ -336,7 +374,6 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
336
374
|
return data;
|
|
337
375
|
}
|
|
338
376
|
catch (error) {
|
|
339
|
-
// Don't update state if aborted
|
|
340
377
|
if (abortController.signal.aborted) {
|
|
341
378
|
throw error;
|
|
342
379
|
}
|
|
@@ -351,13 +388,12 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
351
388
|
throw error;
|
|
352
389
|
}
|
|
353
390
|
finally {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
if (currentPending?.abortController === abortController) {
|
|
391
|
+
if (__classPrivateFieldGet(this, _RampsController_pendingRequests, "f").get(cacheKey)?.abortController ===
|
|
392
|
+
abortController) {
|
|
357
393
|
__classPrivateFieldGet(this, _RampsController_pendingRequests, "f").delete(cacheKey);
|
|
358
394
|
}
|
|
359
395
|
// Clear resource-level loading state only when no requests for this resource remain
|
|
360
|
-
if (resourceType) {
|
|
396
|
+
if (resourceType && !abortController.signal.aborted) {
|
|
361
397
|
const count = __classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").get(resourceType) ?? 0;
|
|
362
398
|
const next = Math.max(0, count - 1);
|
|
363
399
|
if (next === 0) {
|
|
@@ -370,8 +406,11 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
370
406
|
}
|
|
371
407
|
}
|
|
372
408
|
})();
|
|
373
|
-
|
|
374
|
-
|
|
409
|
+
__classPrivateFieldGet(this, _RampsController_pendingRequests, "f").set(cacheKey, {
|
|
410
|
+
promise,
|
|
411
|
+
abortController,
|
|
412
|
+
resourceType,
|
|
413
|
+
});
|
|
375
414
|
return promise;
|
|
376
415
|
}
|
|
377
416
|
/**
|
|
@@ -425,9 +464,8 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
425
464
|
!this.state.tokens.data ||
|
|
426
465
|
this.state.providers.data.length === 0;
|
|
427
466
|
if (regionChanged) {
|
|
467
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_abortDependentRequests).call(this);
|
|
428
468
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_clearPendingResourceCountForDependentResources).call(this);
|
|
429
|
-
}
|
|
430
|
-
if (regionChanged) {
|
|
431
469
|
this.stopQuotePolling();
|
|
432
470
|
}
|
|
433
471
|
this.update((state) => {
|
|
@@ -468,15 +506,11 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
468
506
|
this.stopQuotePolling();
|
|
469
507
|
this.update((state) => {
|
|
470
508
|
state.providers.selected = null;
|
|
471
|
-
state
|
|
472
|
-
state.paymentMethods.selected = null;
|
|
509
|
+
resetResource(state, 'paymentMethods');
|
|
473
510
|
});
|
|
474
511
|
return;
|
|
475
512
|
}
|
|
476
|
-
const regionCode = this.
|
|
477
|
-
if (!regionCode) {
|
|
478
|
-
throw new Error('Region is required. Cannot set selected provider without valid region information.');
|
|
479
|
-
}
|
|
513
|
+
const regionCode = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
|
|
480
514
|
const providers = this.state.providers.data;
|
|
481
515
|
if (!providers || providers.length === 0) {
|
|
482
516
|
throw new Error('Providers not loaded. Cannot set selected provider before providers are fetched.');
|
|
@@ -487,9 +521,9 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
487
521
|
}
|
|
488
522
|
this.update((state) => {
|
|
489
523
|
state.providers.selected = provider;
|
|
490
|
-
state
|
|
491
|
-
state.paymentMethods.selected = null;
|
|
524
|
+
resetResource(state, 'paymentMethods');
|
|
492
525
|
state.quotes.selected = null;
|
|
526
|
+
resetWidgetUrl(state);
|
|
493
527
|
});
|
|
494
528
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { provider: provider.id }).then(() => {
|
|
495
529
|
// Restart quote polling after payment methods are fetched
|
|
@@ -517,10 +551,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
517
551
|
await this.setUserRegion(regionCode, options);
|
|
518
552
|
}
|
|
519
553
|
hydrateState(options) {
|
|
520
|
-
const regionCode = this.
|
|
521
|
-
if (!regionCode) {
|
|
522
|
-
throw new Error('Region code is required. Cannot hydrate state without valid region information.');
|
|
523
|
-
}
|
|
554
|
+
const regionCode = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
|
|
524
555
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getTokens(regionCode, 'buy', options));
|
|
525
556
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getProviders(regionCode, options));
|
|
526
557
|
}
|
|
@@ -538,7 +569,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
538
569
|
return this.messenger.call('RampsService:getCountries');
|
|
539
570
|
}, { ...options, resourceType: 'countries' });
|
|
540
571
|
this.update((state) => {
|
|
541
|
-
state.countries.data = countries;
|
|
572
|
+
state.countries.data = Array.isArray(countries) ? [...countries] : [];
|
|
542
573
|
});
|
|
543
574
|
return countries;
|
|
544
575
|
}
|
|
@@ -553,10 +584,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
553
584
|
* @returns The tokens response containing topTokens and allTokens.
|
|
554
585
|
*/
|
|
555
586
|
async getTokens(region, action = 'buy', options) {
|
|
556
|
-
const regionToUse = region ?? this.
|
|
557
|
-
if (!regionToUse) {
|
|
558
|
-
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
559
|
-
}
|
|
587
|
+
const regionToUse = region ?? __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
|
|
560
588
|
const normalizedRegion = regionToUse.toLowerCase().trim();
|
|
561
589
|
const cacheKey = (0, RequestCache_1.createCacheKey)('getTokens', [
|
|
562
590
|
normalizedRegion,
|
|
@@ -570,8 +598,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
570
598
|
}, {
|
|
571
599
|
...options,
|
|
572
600
|
resourceType: 'tokens',
|
|
573
|
-
isResultCurrent: () => this
|
|
574
|
-
this.state.userRegion?.regionCode === normalizedRegion,
|
|
601
|
+
isResultCurrent: () => __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isRegionCurrent).call(this, normalizedRegion),
|
|
575
602
|
});
|
|
576
603
|
this.update((state) => {
|
|
577
604
|
const userRegionCode = state.userRegion?.regionCode;
|
|
@@ -594,15 +621,11 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
594
621
|
this.stopQuotePolling();
|
|
595
622
|
this.update((state) => {
|
|
596
623
|
state.tokens.selected = null;
|
|
597
|
-
state
|
|
598
|
-
state.paymentMethods.selected = null;
|
|
624
|
+
resetResource(state, 'paymentMethods');
|
|
599
625
|
});
|
|
600
626
|
return;
|
|
601
627
|
}
|
|
602
|
-
const regionCode = this.
|
|
603
|
-
if (!regionCode) {
|
|
604
|
-
throw new Error('Region is required. Cannot set selected token without valid region information.');
|
|
605
|
-
}
|
|
628
|
+
const regionCode = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
|
|
606
629
|
const tokens = this.state.tokens.data;
|
|
607
630
|
if (!tokens) {
|
|
608
631
|
throw new Error('Tokens not loaded. Cannot set selected token before tokens are fetched.');
|
|
@@ -614,9 +637,9 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
614
637
|
}
|
|
615
638
|
this.update((state) => {
|
|
616
639
|
state.tokens.selected = token;
|
|
617
|
-
state
|
|
618
|
-
state.paymentMethods.selected = null;
|
|
640
|
+
resetResource(state, 'paymentMethods');
|
|
619
641
|
state.quotes.selected = null;
|
|
642
|
+
resetWidgetUrl(state);
|
|
620
643
|
});
|
|
621
644
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { assetId: token.assetId }).then(() => {
|
|
622
645
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_restartPollingIfActive).call(this);
|
|
@@ -636,10 +659,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
636
659
|
* @returns The providers response containing providers array.
|
|
637
660
|
*/
|
|
638
661
|
async getProviders(region, options) {
|
|
639
|
-
const regionToUse = region ?? this.
|
|
640
|
-
if (!regionToUse) {
|
|
641
|
-
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
642
|
-
}
|
|
662
|
+
const regionToUse = region ?? __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
|
|
643
663
|
const normalizedRegion = regionToUse.toLowerCase().trim();
|
|
644
664
|
const cacheKey = (0, RequestCache_1.createCacheKey)('getProviders', [
|
|
645
665
|
normalizedRegion,
|
|
@@ -658,8 +678,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
658
678
|
}, {
|
|
659
679
|
...options,
|
|
660
680
|
resourceType: 'providers',
|
|
661
|
-
isResultCurrent: () => this
|
|
662
|
-
this.state.userRegion?.regionCode === normalizedRegion,
|
|
681
|
+
isResultCurrent: () => __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isRegionCurrent).call(this, normalizedRegion),
|
|
663
682
|
});
|
|
664
683
|
this.update((state) => {
|
|
665
684
|
const userRegionCode = state.userRegion?.regionCode;
|
|
@@ -681,13 +700,10 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
681
700
|
* @returns The payment methods response containing payments array.
|
|
682
701
|
*/
|
|
683
702
|
async getPaymentMethods(region, options) {
|
|
684
|
-
const regionCode = region ?? this
|
|
703
|
+
const regionCode = region ?? __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
|
|
685
704
|
const fiatToUse = options?.fiat ?? this.state.userRegion?.country?.currency ?? null;
|
|
686
705
|
const assetIdToUse = options?.assetId ?? this.state.tokens.selected?.assetId ?? '';
|
|
687
706
|
const providerToUse = options?.provider ?? this.state.providers.selected?.id ?? '';
|
|
688
|
-
if (!regionCode) {
|
|
689
|
-
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
690
|
-
}
|
|
691
707
|
if (!fiatToUse) {
|
|
692
708
|
throw new Error('Fiat currency is required. Either provide a fiat parameter or ensure userRegion is set in controller state.');
|
|
693
709
|
}
|
|
@@ -710,10 +726,9 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
710
726
|
...options,
|
|
711
727
|
resourceType: 'paymentMethods',
|
|
712
728
|
isResultCurrent: () => {
|
|
713
|
-
const regionMatch = this
|
|
714
|
-
|
|
715
|
-
const
|
|
716
|
-
const providerMatch = (this.state.providers.selected?.id ?? '') === providerToUse;
|
|
729
|
+
const regionMatch = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isRegionCurrent).call(this, normalizedRegion);
|
|
730
|
+
const tokenMatch = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isTokenCurrent).call(this, assetIdToUse);
|
|
731
|
+
const providerMatch = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isProviderCurrent).call(this, providerToUse);
|
|
717
732
|
return regionMatch && tokenMatch && providerMatch;
|
|
718
733
|
},
|
|
719
734
|
});
|
|
@@ -783,7 +798,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
783
798
|
* @returns The quotes response containing success, sorted, error, and customActions.
|
|
784
799
|
*/
|
|
785
800
|
async getQuotes(options) {
|
|
786
|
-
const regionToUse = options.region ?? this.
|
|
801
|
+
const regionToUse = options.region ?? __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
|
|
787
802
|
const fiatToUse = options.fiat ?? this.state.userRegion?.country?.currency;
|
|
788
803
|
const paymentMethodsToUse = options.paymentMethods ??
|
|
789
804
|
this.state.paymentMethods.data.map((pm) => pm.id);
|
|
@@ -791,9 +806,6 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
791
806
|
this.state.providers.data.map((provider) => provider.id);
|
|
792
807
|
const action = options.action ?? 'buy';
|
|
793
808
|
const assetIdToUse = options.assetId ?? this.state.tokens.selected?.assetId;
|
|
794
|
-
if (!regionToUse) {
|
|
795
|
-
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
796
|
-
}
|
|
797
809
|
if (!fiatToUse) {
|
|
798
810
|
throw new Error('Fiat currency is required. Either provide a fiat parameter or ensure userRegion is set in controller state.');
|
|
799
811
|
}
|
|
@@ -844,8 +856,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
844
856
|
forceRefresh: options.forceRefresh,
|
|
845
857
|
ttl: options.ttl ?? DEFAULT_QUOTES_TTL,
|
|
846
858
|
resourceType: 'quotes',
|
|
847
|
-
isResultCurrent: () => this
|
|
848
|
-
this.state.userRegion?.regionCode === normalizedRegion,
|
|
859
|
+
isResultCurrent: () => __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isRegionCurrent).call(this, normalizedRegion),
|
|
849
860
|
});
|
|
850
861
|
this.update((state) => {
|
|
851
862
|
const userRegionCode = state.userRegion?.regionCode;
|
|
@@ -868,14 +879,10 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
868
879
|
* @throws If required dependencies (region, token, provider, payment method) are not set.
|
|
869
880
|
*/
|
|
870
881
|
startQuotePolling(options) {
|
|
871
|
-
|
|
872
|
-
const regionCode = this.state.userRegion?.regionCode;
|
|
882
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
|
|
873
883
|
const token = this.state.tokens.selected;
|
|
874
884
|
const provider = this.state.providers.selected;
|
|
875
885
|
const paymentMethod = this.state.paymentMethods.selected;
|
|
876
|
-
if (!regionCode) {
|
|
877
|
-
throw new Error('Region is required. Cannot start quote polling without valid region information.');
|
|
878
|
-
}
|
|
879
886
|
if (!token) {
|
|
880
887
|
throw new Error('Token is required. Cannot start quote polling without a selected token.');
|
|
881
888
|
}
|
|
@@ -900,10 +907,12 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
900
907
|
providers: [provider.id],
|
|
901
908
|
forceRefresh: true,
|
|
902
909
|
}).then((response) => {
|
|
910
|
+
let newSelectedQuote = null;
|
|
903
911
|
// Auto-select logic: only when exactly one quote is returned
|
|
904
912
|
this.update((state) => {
|
|
905
913
|
if (response.success.length === 1) {
|
|
906
|
-
|
|
914
|
+
newSelectedQuote = response.success[0];
|
|
915
|
+
state.quotes.selected = newSelectedQuote;
|
|
907
916
|
}
|
|
908
917
|
else {
|
|
909
918
|
// Keep existing selection if still valid, but update with fresh data
|
|
@@ -912,11 +921,12 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
912
921
|
const freshQuote = response.success.find((quote) => quote.provider === currentSelection.provider &&
|
|
913
922
|
quote.quote.paymentMethod ===
|
|
914
923
|
currentSelection.quote.paymentMethod);
|
|
915
|
-
|
|
916
|
-
state.quotes.selected =
|
|
924
|
+
newSelectedQuote = freshQuote ?? null;
|
|
925
|
+
state.quotes.selected = newSelectedQuote;
|
|
917
926
|
}
|
|
918
927
|
}
|
|
919
928
|
});
|
|
929
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_syncWidgetUrl).call(this, newSelectedQuote);
|
|
920
930
|
return undefined;
|
|
921
931
|
}));
|
|
922
932
|
};
|
|
@@ -938,6 +948,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
938
948
|
}
|
|
939
949
|
/**
|
|
940
950
|
* Manually sets the selected quote.
|
|
951
|
+
* Automatically triggers a widget URL fetch for the new quote.
|
|
941
952
|
*
|
|
942
953
|
* @param quote - The quote to select, or null to clear the selection.
|
|
943
954
|
*/
|
|
@@ -945,6 +956,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
945
956
|
this.update((state) => {
|
|
946
957
|
state.quotes.selected = quote;
|
|
947
958
|
});
|
|
959
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_syncWidgetUrl).call(this, quote);
|
|
948
960
|
}
|
|
949
961
|
/**
|
|
950
962
|
* Cleans up controller resources.
|
|
@@ -962,6 +974,8 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
962
974
|
*
|
|
963
975
|
* @param quote - The quote to fetch the widget URL from.
|
|
964
976
|
* @returns Promise resolving to the widget URL string, or null if not available.
|
|
977
|
+
* @deprecated Read `state.widgetUrl` instead. The widget URL is now automatically
|
|
978
|
+
* fetched and stored in state whenever the selected quote changes.
|
|
965
979
|
*/
|
|
966
980
|
async getWidgetUrl(quote) {
|
|
967
981
|
const buyUrl = quote.quote?.buyURL;
|
|
@@ -980,26 +994,32 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
980
994
|
}
|
|
981
995
|
exports.RampsController = RampsController;
|
|
982
996
|
_RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheMaxSize = new WeakMap(), _RampsController_pendingRequests = new WeakMap(), _RampsController_pendingResourceCount = new WeakMap(), _RampsController_quotePollingInterval = new WeakMap(), _RampsController_quotePollingOptions = new WeakMap(), _RampsController_instances = new WeakSet(), _RampsController_clearPendingResourceCountForDependentResources = function _RampsController_clearPendingResourceCountForDependentResources() {
|
|
983
|
-
const
|
|
984
|
-
'providers',
|
|
985
|
-
'tokens',
|
|
986
|
-
'paymentMethods',
|
|
987
|
-
'quotes',
|
|
988
|
-
];
|
|
989
|
-
for (const resourceType of types) {
|
|
997
|
+
for (const resourceType of DEPENDENT_RESOURCE_KEYS) {
|
|
990
998
|
__classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").delete(resourceType);
|
|
991
999
|
}
|
|
992
|
-
},
|
|
1000
|
+
}, _RampsController_abortDependentRequests = function _RampsController_abortDependentRequests() {
|
|
1001
|
+
for (const [cacheKey, pending] of __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").entries()) {
|
|
1002
|
+
if (pending.resourceType &&
|
|
1003
|
+
DEPENDENT_RESOURCE_KEYS_SET.has(pending.resourceType)) {
|
|
1004
|
+
pending.abortController.abort();
|
|
1005
|
+
__classPrivateFieldGet(this, _RampsController_pendingRequests, "f").delete(cacheKey);
|
|
1006
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_removeRequestState).call(this, cacheKey);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}, _RampsController_mutateRequests = function _RampsController_mutateRequests(fn) {
|
|
993
1010
|
this.update((state) => {
|
|
994
1011
|
const requests = state.requests;
|
|
1012
|
+
fn(requests);
|
|
1013
|
+
});
|
|
1014
|
+
}, _RampsController_removeRequestState = function _RampsController_removeRequestState(cacheKey) {
|
|
1015
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_mutateRequests).call(this, (requests) => {
|
|
995
1016
|
delete requests[cacheKey];
|
|
996
1017
|
});
|
|
997
1018
|
}, _RampsController_cleanupState = function _RampsController_cleanupState() {
|
|
998
1019
|
this.stopQuotePolling();
|
|
1020
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_abortDependentRequests).call(this);
|
|
999
1021
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_clearPendingResourceCountForDependentResources).call(this);
|
|
1000
|
-
this.update((state) => resetDependentResources(state, {
|
|
1001
|
-
clearUserRegionData: true,
|
|
1002
|
-
}));
|
|
1022
|
+
this.update((state) => resetDependentResources(state, { clearUserRegionData: true }));
|
|
1003
1023
|
}, _RampsController_fireAndForget = function _RampsController_fireAndForget(promise) {
|
|
1004
1024
|
promise.catch((_error) => undefined);
|
|
1005
1025
|
}, _RampsController_restartPollingIfActive = function _RampsController_restartPollingIfActive() {
|
|
@@ -1014,6 +1034,21 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
1014
1034
|
// when dependencies are available
|
|
1015
1035
|
}
|
|
1016
1036
|
}
|
|
1037
|
+
}, _RampsController_requireRegion = function _RampsController_requireRegion() {
|
|
1038
|
+
const regionCode = this.state.userRegion?.regionCode;
|
|
1039
|
+
if (!regionCode) {
|
|
1040
|
+
throw new Error('Region is required. Cannot proceed without valid region information.');
|
|
1041
|
+
}
|
|
1042
|
+
return regionCode;
|
|
1043
|
+
}, _RampsController_isRegionCurrent = function _RampsController_isRegionCurrent(normalizedRegion) {
|
|
1044
|
+
const current = this.state.userRegion?.regionCode;
|
|
1045
|
+
return current === undefined || current === normalizedRegion;
|
|
1046
|
+
}, _RampsController_isTokenCurrent = function _RampsController_isTokenCurrent(normalizedAssetId) {
|
|
1047
|
+
const current = this.state.tokens.selected?.assetId ?? '';
|
|
1048
|
+
return current === normalizedAssetId;
|
|
1049
|
+
}, _RampsController_isProviderCurrent = function _RampsController_isProviderCurrent(normalizedProviderId) {
|
|
1050
|
+
const current = this.state.providers.selected?.id ?? '';
|
|
1051
|
+
return current === normalizedProviderId;
|
|
1017
1052
|
}, _RampsController_updateResourceField = function _RampsController_updateResourceField(resourceType, field, value) {
|
|
1018
1053
|
this.update((state) => {
|
|
1019
1054
|
const resource = state[resourceType];
|
|
@@ -1028,11 +1063,8 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
1028
1063
|
}, _RampsController_updateRequestState = function _RampsController_updateRequestState(cacheKey, requestState) {
|
|
1029
1064
|
const maxSize = __classPrivateFieldGet(this, _RampsController_requestCacheMaxSize, "f");
|
|
1030
1065
|
const ttl = __classPrivateFieldGet(this, _RampsController_requestCacheTTL, "f");
|
|
1031
|
-
this.
|
|
1032
|
-
const requests = state.requests;
|
|
1066
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_mutateRequests).call(this, (requests) => {
|
|
1033
1067
|
requests[cacheKey] = requestState;
|
|
1034
|
-
// Evict expired entries based on TTL
|
|
1035
|
-
// Only evict SUCCESS states that have exceeded their TTL
|
|
1036
1068
|
const keys = Object.keys(requests);
|
|
1037
1069
|
for (const key of keys) {
|
|
1038
1070
|
const entry = requests[key];
|
|
@@ -1041,16 +1073,13 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
1041
1073
|
delete requests[key];
|
|
1042
1074
|
}
|
|
1043
1075
|
}
|
|
1044
|
-
// Evict oldest entries if cache still exceeds max size
|
|
1045
1076
|
const remainingKeys = Object.keys(requests);
|
|
1046
1077
|
if (remainingKeys.length > maxSize) {
|
|
1047
|
-
// Sort by timestamp (oldest first)
|
|
1048
1078
|
const sortedKeys = remainingKeys.sort((a, b) => {
|
|
1049
1079
|
const aTime = requests[a]?.timestamp ?? 0;
|
|
1050
1080
|
const bTime = requests[b]?.timestamp ?? 0;
|
|
1051
1081
|
return aTime - bTime;
|
|
1052
1082
|
});
|
|
1053
|
-
// Remove oldest entries until we're under the limit
|
|
1054
1083
|
const entriesToRemove = remainingKeys.length - maxSize;
|
|
1055
1084
|
for (let i = 0; i < entriesToRemove; i++) {
|
|
1056
1085
|
const keyToRemove = sortedKeys[i];
|
|
@@ -1060,5 +1089,38 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
1060
1089
|
}
|
|
1061
1090
|
}
|
|
1062
1091
|
});
|
|
1092
|
+
}, _RampsController_syncWidgetUrl = function _RampsController_syncWidgetUrl(quote) {
|
|
1093
|
+
const buyUrl = quote?.quote?.buyURL;
|
|
1094
|
+
if (!buyUrl) {
|
|
1095
|
+
this.update((state) => {
|
|
1096
|
+
resetWidgetUrl(state);
|
|
1097
|
+
});
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
if (this.state.widgetUrl.data === null) {
|
|
1101
|
+
this.update((state) => {
|
|
1102
|
+
state.widgetUrl.isLoading = true;
|
|
1103
|
+
state.widgetUrl.error = null;
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.messenger
|
|
1107
|
+
.call('RampsService:getBuyWidgetUrl', buyUrl)
|
|
1108
|
+
.then((buyWidget) => {
|
|
1109
|
+
this.update((state) => {
|
|
1110
|
+
state.widgetUrl.data = buyWidget;
|
|
1111
|
+
state.widgetUrl.isLoading = false;
|
|
1112
|
+
state.widgetUrl.error = null;
|
|
1113
|
+
});
|
|
1114
|
+
return undefined;
|
|
1115
|
+
})
|
|
1116
|
+
.catch((error) => {
|
|
1117
|
+
this.update((state) => {
|
|
1118
|
+
state.widgetUrl.isLoading = false;
|
|
1119
|
+
state.widgetUrl.error =
|
|
1120
|
+
error instanceof Error
|
|
1121
|
+
? error.message
|
|
1122
|
+
: 'Failed to fetch widget URL';
|
|
1123
|
+
});
|
|
1124
|
+
}));
|
|
1063
1125
|
};
|
|
1064
1126
|
//# sourceMappingURL=RampsController.cjs.map
|