@metamask-previews/ramps-controller 8.0.0-preview-b271ae9 → 8.0.0-preview-92d89c2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Changed
11
+
12
+ - Refactor: Consolidate reset logic with a shared resetResource helper and fix abort handling for dependent resources ([#7818](https://github.com/MetaMask/core/pull/7818))
13
+
10
14
  ## [8.0.0]
11
15
 
12
16
  ### 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;
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");
@@ -125,6 +125,21 @@ function getDefaultRampsControllerState() {
125
125
  };
126
126
  }
127
127
  exports.getDefaultRampsControllerState = getDefaultRampsControllerState;
128
+ const DEPENDENT_RESOURCE_KEYS = [
129
+ 'providers',
130
+ 'tokens',
131
+ 'paymentMethods',
132
+ 'quotes',
133
+ ];
134
+ const DEPENDENT_RESOURCE_KEYS_SET = new Set(DEPENDENT_RESOURCE_KEYS);
135
+ function resetResource(state, resourceType, defaultResource) {
136
+ const def = defaultResource ?? getDefaultRampsControllerState()[resourceType];
137
+ const resource = state[resourceType];
138
+ resource.data = def.data;
139
+ resource.selected = def.selected;
140
+ resource.isLoading = def.isLoading;
141
+ resource.error = def.error;
142
+ }
128
143
  /**
129
144
  * Resets region-dependent resources (userRegion, providers, tokens, paymentMethods, quotes).
130
145
  * Mutates state in place; use from within controller update() for atomic updates.
@@ -137,22 +152,10 @@ function resetDependentResources(state, options) {
137
152
  if (options?.clearUserRegionData) {
138
153
  state.userRegion = null;
139
154
  }
140
- state.providers.selected = null;
141
- state.providers.data = [];
142
- state.providers.isLoading = false;
143
- state.providers.error = null;
144
- state.tokens.selected = null;
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;
155
+ const defaultState = getDefaultRampsControllerState();
156
+ for (const key of DEPENDENT_RESOURCE_KEYS) {
157
+ resetResource(state, key, defaultState[key]);
158
+ }
156
159
  }
157
160
  // === HELPER FUNCTIONS ===
158
161
  /**
@@ -276,31 +279,49 @@ class RampsController extends base_controller_1.BaseController {
276
279
  __classPrivateFieldSet(this, _RampsController_requestCacheMaxSize, requestCacheMaxSize, "f");
277
280
  }
278
281
  /**
279
- * Executes a request with caching and deduplication.
282
+ * Executes a request with caching, deduplication, and at most one in-flight
283
+ * request per resource type.
280
284
  *
281
- * If a request with the same cache key is already in flight, returns the
282
- * existing promise. If valid cached data exists, returns it without making
283
- * a new request.
285
+ * 1. **Same cache key in flight** – If a request with this cache key is
286
+ * already pending, returns that promise (deduplication; no second request).
284
287
  *
285
- * @param cacheKey - Unique identifier for this request.
286
- * @param fetcher - Function that performs the actual fetch. Receives an AbortSignal.
287
- * @param options - Options for cache behavior.
288
- * @returns The result of the request.
288
+ * 2. **Cache hit** – If valid, non-expired data exists in state.requests for
289
+ * this key and forceRefresh is not set, returns that data without fetching.
290
+ *
291
+ * 3. **New request** Creates an AbortController and fires the fetcher.
292
+ * If options.resourceType is set, tags the pending request with that
293
+ * resource type (so #abortDependentRequests can cancel it on region
294
+ * change or cleanup) and ref-counts resource-level loading state.
295
+ * On success or error, updates request state and resource error;
296
+ * in finally, clears resource loading only if this request was not
297
+ * aborted.
298
+ *
299
+ * @param cacheKey - Unique identifier for this request (e.g. from createCacheKey).
300
+ * @param fetcher - Async function that performs the fetch. Receives an AbortSignal
301
+ * that is aborted when this request is superseded by another for the same resource.
302
+ * @param options - Optional forceRefresh, ttl, and resourceType for loading/error state.
303
+ * @returns The result of the request (from cache, joined promise, or fetcher).
289
304
  */
290
305
  async executeRequest(cacheKey, fetcher, options) {
306
+ // Get TTL for verifying cache expiration
291
307
  const ttl = options?.ttl ?? __classPrivateFieldGet(this, _RampsController_requestCacheTTL, "f");
292
- // Check for existing pending request - join it instead of making a duplicate
308
+ // DEDUPLICATION:
309
+ // Check if a request is already in flight for this cache key
310
+ // If so, return the original promise for that request
293
311
  const pending = __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").get(cacheKey);
294
312
  if (pending) {
295
313
  return pending.promise;
296
314
  }
315
+ // CACHE HIT:
316
+ // If cache is not expired, return the cached data
297
317
  if (!options?.forceRefresh) {
298
318
  const cached = this.state.requests[cacheKey];
299
319
  if (cached && !(0, RequestCache_1.isCacheExpired)(cached, ttl)) {
300
320
  return cached.data;
301
321
  }
302
322
  }
303
- // Create abort controller for this request
323
+ // Create a new abort controller for this request
324
+ // Record the time the request was started
304
325
  const abortController = new AbortController();
305
326
  const lastFetchedAt = Date.now();
306
327
  const { resourceType } = options ?? {};
@@ -319,15 +340,11 @@ class RampsController extends base_controller_1.BaseController {
319
340
  const promise = (async () => {
320
341
  try {
321
342
  const data = await fetcher(abortController.signal);
322
- // Don't update state if aborted
323
343
  if (abortController.signal.aborted) {
324
344
  throw new Error('Request was aborted');
325
345
  }
326
346
  __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_updateRequestState).call(this, cacheKey, (0, RequestCache_1.createSuccessState)(data, lastFetchedAt));
327
347
  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
348
  const isCurrent = !options?.isResultCurrent || options.isResultCurrent();
332
349
  if (isCurrent) {
333
350
  __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_setResourceError).call(this, resourceType, null);
@@ -336,7 +353,6 @@ class RampsController extends base_controller_1.BaseController {
336
353
  return data;
337
354
  }
338
355
  catch (error) {
339
- // Don't update state if aborted
340
356
  if (abortController.signal.aborted) {
341
357
  throw error;
342
358
  }
@@ -351,13 +367,12 @@ class RampsController extends base_controller_1.BaseController {
351
367
  throw error;
352
368
  }
353
369
  finally {
354
- // Only delete if this is still our entry (not replaced by a new request)
355
- const currentPending = __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").get(cacheKey);
356
- if (currentPending?.abortController === abortController) {
370
+ if (__classPrivateFieldGet(this, _RampsController_pendingRequests, "f").get(cacheKey)?.abortController ===
371
+ abortController) {
357
372
  __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").delete(cacheKey);
358
373
  }
359
374
  // Clear resource-level loading state only when no requests for this resource remain
360
- if (resourceType) {
375
+ if (resourceType && !abortController.signal.aborted) {
361
376
  const count = __classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").get(resourceType) ?? 0;
362
377
  const next = Math.max(0, count - 1);
363
378
  if (next === 0) {
@@ -370,8 +385,11 @@ class RampsController extends base_controller_1.BaseController {
370
385
  }
371
386
  }
372
387
  })();
373
- // Store pending request for deduplication
374
- __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").set(cacheKey, { promise, abortController });
388
+ __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").set(cacheKey, {
389
+ promise,
390
+ abortController,
391
+ resourceType,
392
+ });
375
393
  return promise;
376
394
  }
377
395
  /**
@@ -425,9 +443,8 @@ class RampsController extends base_controller_1.BaseController {
425
443
  !this.state.tokens.data ||
426
444
  this.state.providers.data.length === 0;
427
445
  if (regionChanged) {
446
+ __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_abortDependentRequests).call(this);
428
447
  __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_clearPendingResourceCountForDependentResources).call(this);
429
- }
430
- if (regionChanged) {
431
448
  this.stopQuotePolling();
432
449
  }
433
450
  this.update((state) => {
@@ -468,15 +485,11 @@ class RampsController extends base_controller_1.BaseController {
468
485
  this.stopQuotePolling();
469
486
  this.update((state) => {
470
487
  state.providers.selected = null;
471
- state.paymentMethods.data = [];
472
- state.paymentMethods.selected = null;
488
+ resetResource(state, 'paymentMethods');
473
489
  });
474
490
  return;
475
491
  }
476
- const regionCode = this.state.userRegion?.regionCode;
477
- if (!regionCode) {
478
- throw new Error('Region is required. Cannot set selected provider without valid region information.');
479
- }
492
+ const regionCode = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
480
493
  const providers = this.state.providers.data;
481
494
  if (!providers || providers.length === 0) {
482
495
  throw new Error('Providers not loaded. Cannot set selected provider before providers are fetched.');
@@ -487,8 +500,7 @@ class RampsController extends base_controller_1.BaseController {
487
500
  }
488
501
  this.update((state) => {
489
502
  state.providers.selected = provider;
490
- state.paymentMethods.data = [];
491
- state.paymentMethods.selected = null;
503
+ resetResource(state, 'paymentMethods');
492
504
  state.quotes.selected = null;
493
505
  });
494
506
  __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { provider: provider.id }).then(() => {
@@ -517,10 +529,7 @@ class RampsController extends base_controller_1.BaseController {
517
529
  await this.setUserRegion(regionCode, options);
518
530
  }
519
531
  hydrateState(options) {
520
- const regionCode = this.state.userRegion?.regionCode;
521
- if (!regionCode) {
522
- throw new Error('Region code is required. Cannot hydrate state without valid region information.');
523
- }
532
+ const regionCode = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
524
533
  __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getTokens(regionCode, 'buy', options));
525
534
  __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getProviders(regionCode, options));
526
535
  }
@@ -538,7 +547,7 @@ class RampsController extends base_controller_1.BaseController {
538
547
  return this.messenger.call('RampsService:getCountries');
539
548
  }, { ...options, resourceType: 'countries' });
540
549
  this.update((state) => {
541
- state.countries.data = countries;
550
+ state.countries.data = Array.isArray(countries) ? [...countries] : [];
542
551
  });
543
552
  return countries;
544
553
  }
@@ -553,10 +562,7 @@ class RampsController extends base_controller_1.BaseController {
553
562
  * @returns The tokens response containing topTokens and allTokens.
554
563
  */
555
564
  async getTokens(region, action = 'buy', options) {
556
- const regionToUse = region ?? this.state.userRegion?.regionCode;
557
- if (!regionToUse) {
558
- throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
559
- }
565
+ const regionToUse = region ?? __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
560
566
  const normalizedRegion = regionToUse.toLowerCase().trim();
561
567
  const cacheKey = (0, RequestCache_1.createCacheKey)('getTokens', [
562
568
  normalizedRegion,
@@ -570,8 +576,7 @@ class RampsController extends base_controller_1.BaseController {
570
576
  }, {
571
577
  ...options,
572
578
  resourceType: 'tokens',
573
- isResultCurrent: () => this.state.userRegion?.regionCode === undefined ||
574
- this.state.userRegion?.regionCode === normalizedRegion,
579
+ isResultCurrent: () => __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isRegionCurrent).call(this, normalizedRegion),
575
580
  });
576
581
  this.update((state) => {
577
582
  const userRegionCode = state.userRegion?.regionCode;
@@ -594,15 +599,11 @@ class RampsController extends base_controller_1.BaseController {
594
599
  this.stopQuotePolling();
595
600
  this.update((state) => {
596
601
  state.tokens.selected = null;
597
- state.paymentMethods.data = [];
598
- state.paymentMethods.selected = null;
602
+ resetResource(state, 'paymentMethods');
599
603
  });
600
604
  return;
601
605
  }
602
- const regionCode = this.state.userRegion?.regionCode;
603
- if (!regionCode) {
604
- throw new Error('Region is required. Cannot set selected token without valid region information.');
605
- }
606
+ const regionCode = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
606
607
  const tokens = this.state.tokens.data;
607
608
  if (!tokens) {
608
609
  throw new Error('Tokens not loaded. Cannot set selected token before tokens are fetched.');
@@ -614,8 +615,7 @@ class RampsController extends base_controller_1.BaseController {
614
615
  }
615
616
  this.update((state) => {
616
617
  state.tokens.selected = token;
617
- state.paymentMethods.data = [];
618
- state.paymentMethods.selected = null;
618
+ resetResource(state, 'paymentMethods');
619
619
  state.quotes.selected = null;
620
620
  });
621
621
  __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { assetId: token.assetId }).then(() => {
@@ -636,10 +636,7 @@ class RampsController extends base_controller_1.BaseController {
636
636
  * @returns The providers response containing providers array.
637
637
  */
638
638
  async getProviders(region, options) {
639
- const regionToUse = region ?? this.state.userRegion?.regionCode;
640
- if (!regionToUse) {
641
- throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
642
- }
639
+ const regionToUse = region ?? __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
643
640
  const normalizedRegion = regionToUse.toLowerCase().trim();
644
641
  const cacheKey = (0, RequestCache_1.createCacheKey)('getProviders', [
645
642
  normalizedRegion,
@@ -658,8 +655,7 @@ class RampsController extends base_controller_1.BaseController {
658
655
  }, {
659
656
  ...options,
660
657
  resourceType: 'providers',
661
- isResultCurrent: () => this.state.userRegion?.regionCode === undefined ||
662
- this.state.userRegion?.regionCode === normalizedRegion,
658
+ isResultCurrent: () => __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isRegionCurrent).call(this, normalizedRegion),
663
659
  });
664
660
  this.update((state) => {
665
661
  const userRegionCode = state.userRegion?.regionCode;
@@ -681,13 +677,10 @@ class RampsController extends base_controller_1.BaseController {
681
677
  * @returns The payment methods response containing payments array.
682
678
  */
683
679
  async getPaymentMethods(region, options) {
684
- const regionCode = region ?? this.state.userRegion?.regionCode ?? null;
680
+ const regionCode = region ?? __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
685
681
  const fiatToUse = options?.fiat ?? this.state.userRegion?.country?.currency ?? null;
686
682
  const assetIdToUse = options?.assetId ?? this.state.tokens.selected?.assetId ?? '';
687
683
  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
684
  if (!fiatToUse) {
692
685
  throw new Error('Fiat currency is required. Either provide a fiat parameter or ensure userRegion is set in controller state.');
693
686
  }
@@ -710,10 +703,9 @@ class RampsController extends base_controller_1.BaseController {
710
703
  ...options,
711
704
  resourceType: 'paymentMethods',
712
705
  isResultCurrent: () => {
713
- const regionMatch = this.state.userRegion?.regionCode === undefined ||
714
- this.state.userRegion?.regionCode === normalizedRegion;
715
- const tokenMatch = (this.state.tokens.selected?.assetId ?? '') === assetIdToUse;
716
- const providerMatch = (this.state.providers.selected?.id ?? '') === providerToUse;
706
+ const regionMatch = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isRegionCurrent).call(this, normalizedRegion);
707
+ const tokenMatch = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isTokenCurrent).call(this, assetIdToUse);
708
+ const providerMatch = __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isProviderCurrent).call(this, providerToUse);
717
709
  return regionMatch && tokenMatch && providerMatch;
718
710
  },
719
711
  });
@@ -783,7 +775,7 @@ class RampsController extends base_controller_1.BaseController {
783
775
  * @returns The quotes response containing success, sorted, error, and customActions.
784
776
  */
785
777
  async getQuotes(options) {
786
- const regionToUse = options.region ?? this.state.userRegion?.regionCode;
778
+ const regionToUse = options.region ?? __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
787
779
  const fiatToUse = options.fiat ?? this.state.userRegion?.country?.currency;
788
780
  const paymentMethodsToUse = options.paymentMethods ??
789
781
  this.state.paymentMethods.data.map((pm) => pm.id);
@@ -791,9 +783,6 @@ class RampsController extends base_controller_1.BaseController {
791
783
  this.state.providers.data.map((provider) => provider.id);
792
784
  const action = options.action ?? 'buy';
793
785
  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
786
  if (!fiatToUse) {
798
787
  throw new Error('Fiat currency is required. Either provide a fiat parameter or ensure userRegion is set in controller state.');
799
788
  }
@@ -844,8 +833,7 @@ class RampsController extends base_controller_1.BaseController {
844
833
  forceRefresh: options.forceRefresh,
845
834
  ttl: options.ttl ?? DEFAULT_QUOTES_TTL,
846
835
  resourceType: 'quotes',
847
- isResultCurrent: () => this.state.userRegion?.regionCode === undefined ||
848
- this.state.userRegion?.regionCode === normalizedRegion,
836
+ isResultCurrent: () => __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isRegionCurrent).call(this, normalizedRegion),
849
837
  });
850
838
  this.update((state) => {
851
839
  const userRegionCode = state.userRegion?.regionCode;
@@ -868,14 +856,10 @@ class RampsController extends base_controller_1.BaseController {
868
856
  * @throws If required dependencies (region, token, provider, payment method) are not set.
869
857
  */
870
858
  startQuotePolling(options) {
871
- // Validate required dependencies
872
- const regionCode = this.state.userRegion?.regionCode;
859
+ __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_requireRegion).call(this);
873
860
  const token = this.state.tokens.selected;
874
861
  const provider = this.state.providers.selected;
875
862
  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
863
  if (!token) {
880
864
  throw new Error('Token is required. Cannot start quote polling without a selected token.');
881
865
  }
@@ -980,26 +964,32 @@ class RampsController extends base_controller_1.BaseController {
980
964
  }
981
965
  exports.RampsController = RampsController;
982
966
  _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 types = [
984
- 'providers',
985
- 'tokens',
986
- 'paymentMethods',
987
- 'quotes',
988
- ];
989
- for (const resourceType of types) {
967
+ for (const resourceType of DEPENDENT_RESOURCE_KEYS) {
990
968
  __classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").delete(resourceType);
991
969
  }
992
- }, _RampsController_removeRequestState = function _RampsController_removeRequestState(cacheKey) {
970
+ }, _RampsController_abortDependentRequests = function _RampsController_abortDependentRequests() {
971
+ for (const [cacheKey, pending] of __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").entries()) {
972
+ if (pending.resourceType &&
973
+ DEPENDENT_RESOURCE_KEYS_SET.has(pending.resourceType)) {
974
+ pending.abortController.abort();
975
+ __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").delete(cacheKey);
976
+ __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_removeRequestState).call(this, cacheKey);
977
+ }
978
+ }
979
+ }, _RampsController_mutateRequests = function _RampsController_mutateRequests(fn) {
993
980
  this.update((state) => {
994
981
  const requests = state.requests;
982
+ fn(requests);
983
+ });
984
+ }, _RampsController_removeRequestState = function _RampsController_removeRequestState(cacheKey) {
985
+ __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_mutateRequests).call(this, (requests) => {
995
986
  delete requests[cacheKey];
996
987
  });
997
988
  }, _RampsController_cleanupState = function _RampsController_cleanupState() {
998
989
  this.stopQuotePolling();
990
+ __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_abortDependentRequests).call(this);
999
991
  __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_clearPendingResourceCountForDependentResources).call(this);
1000
- this.update((state) => resetDependentResources(state, {
1001
- clearUserRegionData: true,
1002
- }));
992
+ this.update((state) => resetDependentResources(state, { clearUserRegionData: true }));
1003
993
  }, _RampsController_fireAndForget = function _RampsController_fireAndForget(promise) {
1004
994
  promise.catch((_error) => undefined);
1005
995
  }, _RampsController_restartPollingIfActive = function _RampsController_restartPollingIfActive() {
@@ -1014,6 +1004,21 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
1014
1004
  // when dependencies are available
1015
1005
  }
1016
1006
  }
1007
+ }, _RampsController_requireRegion = function _RampsController_requireRegion() {
1008
+ const regionCode = this.state.userRegion?.regionCode;
1009
+ if (!regionCode) {
1010
+ throw new Error('Region is required. Cannot proceed without valid region information.');
1011
+ }
1012
+ return regionCode;
1013
+ }, _RampsController_isRegionCurrent = function _RampsController_isRegionCurrent(normalizedRegion) {
1014
+ const current = this.state.userRegion?.regionCode;
1015
+ return current === undefined || current === normalizedRegion;
1016
+ }, _RampsController_isTokenCurrent = function _RampsController_isTokenCurrent(normalizedAssetId) {
1017
+ const current = this.state.tokens.selected?.assetId ?? '';
1018
+ return current === normalizedAssetId;
1019
+ }, _RampsController_isProviderCurrent = function _RampsController_isProviderCurrent(normalizedProviderId) {
1020
+ const current = this.state.providers.selected?.id ?? '';
1021
+ return current === normalizedProviderId;
1017
1022
  }, _RampsController_updateResourceField = function _RampsController_updateResourceField(resourceType, field, value) {
1018
1023
  this.update((state) => {
1019
1024
  const resource = state[resourceType];
@@ -1028,11 +1033,8 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
1028
1033
  }, _RampsController_updateRequestState = function _RampsController_updateRequestState(cacheKey, requestState) {
1029
1034
  const maxSize = __classPrivateFieldGet(this, _RampsController_requestCacheMaxSize, "f");
1030
1035
  const ttl = __classPrivateFieldGet(this, _RampsController_requestCacheTTL, "f");
1031
- this.update((state) => {
1032
- const requests = state.requests;
1036
+ __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_mutateRequests).call(this, (requests) => {
1033
1037
  requests[cacheKey] = requestState;
1034
- // Evict expired entries based on TTL
1035
- // Only evict SUCCESS states that have exceeded their TTL
1036
1038
  const keys = Object.keys(requests);
1037
1039
  for (const key of keys) {
1038
1040
  const entry = requests[key];
@@ -1041,16 +1043,13 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
1041
1043
  delete requests[key];
1042
1044
  }
1043
1045
  }
1044
- // Evict oldest entries if cache still exceeds max size
1045
1046
  const remainingKeys = Object.keys(requests);
1046
1047
  if (remainingKeys.length > maxSize) {
1047
- // Sort by timestamp (oldest first)
1048
1048
  const sortedKeys = remainingKeys.sort((a, b) => {
1049
1049
  const aTime = requests[a]?.timestamp ?? 0;
1050
1050
  const bTime = requests[b]?.timestamp ?? 0;
1051
1051
  return aTime - bTime;
1052
1052
  });
1053
- // Remove oldest entries until we're under the limit
1054
1053
  const entriesToRemove = remainingKeys.length - maxSize;
1055
1054
  for (let i = 0; i < entriesToRemove; i++) {
1056
1055
  const keyToRemove = sortedKeys[i];