@zelgadis87/utils-core 5.3.0 → 5.3.2

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/.rollup/index.cjs CHANGED
@@ -415,32 +415,32 @@ function doGroupByWith(arr, getter) {
415
415
  }, {});
416
416
  }
417
417
 
418
- function indexByString(arr, field) {
419
- return indexByStringWith(arr, t => t[field]);
418
+ function indexByString(arr, field, valueMapper = v => v) {
419
+ return indexByStringWith(arr, t => t[field], valueMapper);
420
420
  }
421
- function indexByNumber(arr, field) {
422
- return indexByNumberWith(arr, t => t[field]);
421
+ function indexByNumber(arr, field, valueMapper = v => v) {
422
+ return indexByNumberWith(arr, t => t[field], valueMapper);
423
423
  }
424
- function indexBySymbol(arr, field) {
425
- return indexBySymbolWith(arr, t => t[field]);
424
+ function indexBySymbol(arr, field, valueMapper = v => v) {
425
+ return indexBySymbolWith(arr, t => t[field], valueMapper);
426
426
  }
427
- function indexByStringWith(arr, getter) {
428
- return doIndexByWith(arr, getter);
427
+ function indexByStringWith(arr, keyGetter, valueMapper = v => v) {
428
+ return indexByWith(arr, keyGetter, valueMapper);
429
429
  }
430
- function indexByNumberWith(arr, getter) {
431
- return doIndexByWith(arr, getter);
430
+ function indexByNumberWith(arr, keyGetter, valueMapper = v => v) {
431
+ return indexByWith(arr, keyGetter, valueMapper);
432
432
  }
433
- function indexBySymbolWith(arr, getter) {
434
- return doIndexByWith(arr, getter);
433
+ function indexBySymbolWith(arr, keyGetter, valueMapper = v => v) {
434
+ return indexByWith(arr, keyGetter, valueMapper);
435
435
  }
436
- function doIndexByWith(arr, getter) {
436
+ function indexByWith(arr, keyGetter, valueMapper = v => v) {
437
437
  return arr.reduce((dict, cur) => {
438
- const key = getter(cur);
438
+ const key = keyGetter(cur);
439
439
  if (key !== null && key !== undefined) {
440
440
  if (dict[key]) {
441
441
  throw new Error(`Multiple values indexed by same key "${String(key)}".`);
442
442
  }
443
- dict[key] = cur;
443
+ dict[key] = valueMapper(cur);
444
444
  }
445
445
  return dict;
446
446
  }, {});
@@ -1317,6 +1317,9 @@ class Lazy {
1317
1317
  fnEmpty();
1318
1318
  }
1319
1319
  }
1320
+ empty() {
1321
+ this._initialized = false;
1322
+ }
1320
1323
  static of(generator) {
1321
1324
  return new Lazy(generator);
1322
1325
  }
@@ -1398,6 +1401,9 @@ class LazyAsync {
1398
1401
  throw new Error(errorMessage);
1399
1402
  return this._promise;
1400
1403
  }
1404
+ empty() {
1405
+ this._promise = undefined;
1406
+ }
1401
1407
  static of(generator) {
1402
1408
  return new LazyAsync(generator);
1403
1409
  }
@@ -2612,6 +2618,180 @@ class Logger {
2612
2618
  }
2613
2619
  }
2614
2620
 
2621
+ const logger = new Logger('Cached');
2622
+ /**
2623
+ * A cached value with stale-while-revalidate pattern.
2624
+ * On initialization, the class starts calculating the async result (isPending() === true).
2625
+ * When complete, the class will store the generated value (isReady() === true).
2626
+ * After maxStale duration, the value becomes stale and will refresh in background on next access (isStale() === true).
2627
+ * After maxAge has passed, the cached value is no longer used (isExpired() === true).
2628
+ */
2629
+ class Cached {
2630
+ /** Current promise for the value generation. */
2631
+ _promise = undefined;
2632
+ /** Flag indicating if a generation is currently in progress. */
2633
+ _pending = false;
2634
+ /** The last successfully generated value. */
2635
+ _resolvedValue = undefined;
2636
+ /** Function that generates the cached value. */
2637
+ _generator;
2638
+ /** Timestamp of the last successful value generation. */
2639
+ _generatedAt = TimeInstant.ZERO;
2640
+ /** The duration for which value should be kept cached. After maxAge from the last generated value, this object is considered empty. */
2641
+ _maxAge;
2642
+ /** The duration for which the value should be served from cache, even if stale. Has no effect if greater or equal to maxAge. */
2643
+ _maxStale;
2644
+ /** Callback invoked when new data becomes available. */
2645
+ _onNewDataAvailable = noop;
2646
+ /** Callback invoked when generation fails. */
2647
+ _onError = err => logger.error('Cache generation failed:', err);
2648
+ constructor(generator, maxAge, maxStale) {
2649
+ this._generator = ensureDefined(generator);
2650
+ this._maxAge = ensureDefined(maxAge);
2651
+ this._maxStale = ensureDefined(TimeDuration.lowest(maxAge, maxStale));
2652
+ }
2653
+ /**
2654
+ * Gets the cached value.
2655
+ * Returns fresh data if available, stale data while revalidating, or triggers generation if empty/expired.
2656
+ * @returns Promise resolving to the cached value.
2657
+ */
2658
+ async get() {
2659
+ if (this.isPending()) {
2660
+ return this._promise;
2661
+ }
2662
+ else if (this.isEmpty() || this.isExpired()) {
2663
+ this._promise = this.generate();
2664
+ return this._promise;
2665
+ }
2666
+ else {
2667
+ if (this.isStale()) {
2668
+ // Start re-generating the contents, while serving stale data.
2669
+ void this.generate();
2670
+ }
2671
+ return this._resolvedValue;
2672
+ }
2673
+ }
2674
+ /**
2675
+ * Generates a new value using the generator function.
2676
+ * Sets up promise handlers for success and error cases.
2677
+ * @returns The generation promise.
2678
+ */
2679
+ generate() {
2680
+ if (this._pending)
2681
+ return this._promise;
2682
+ this._pending = true;
2683
+ const deferred = new Deferred();
2684
+ this._generator().then(value => {
2685
+ this._pending = false;
2686
+ this._resolvedValue = value;
2687
+ this._generatedAt = TimeInstant.now();
2688
+ deferred.resolve(value);
2689
+ withTryCatch(() => this._onNewDataAvailable(value));
2690
+ }, error => {
2691
+ const err = asError(error);
2692
+ this._pending = false;
2693
+ deferred.reject(err);
2694
+ withTryCatch(() => this._onError(err));
2695
+ });
2696
+ return deferred.asPromise();
2697
+ }
2698
+ /**
2699
+ * Checks if value has been requested at least once.
2700
+ * @returns True if a promise exists.
2701
+ */
2702
+ isPresent() {
2703
+ return this._promise !== undefined;
2704
+ }
2705
+ /**
2706
+ * Checks value has never been requested.
2707
+ * @returns True if empty.
2708
+ */
2709
+ isEmpty() {
2710
+ return this._promise === undefined;
2711
+ }
2712
+ /**
2713
+ * Checks if value generation is currently in progress.
2714
+ * @returns True if generation is pending.
2715
+ */
2716
+ isPending() {
2717
+ return this.isPresent() && this._pending === true;
2718
+ }
2719
+ /**
2720
+ * Checks if value is stored and fresh.
2721
+ * @returns True if stored and fresh.
2722
+ */
2723
+ isReady() {
2724
+ return this.isPresent() && this.getAge().isLessThan(this._maxAge);
2725
+ }
2726
+ // protected isResolved() {
2727
+ // return this.isReady() && this._error === undefined;
2728
+ // }
2729
+ // protected isError() {
2730
+ // return this.isReady() && this._error !== undefined;
2731
+ // }
2732
+ /**
2733
+ * Checks if cached value is stale (older than maxStale duration).
2734
+ * @returns True if value is stale.
2735
+ */
2736
+ isStale() {
2737
+ return this.isPresent() && this.getAge().isGreaterThan(this._maxStale);
2738
+ }
2739
+ /**
2740
+ * Checks if cached value is expired (older than maxAge duration).
2741
+ * @returns True if value is expired.
2742
+ */
2743
+ isExpired() {
2744
+ return this.isPresent() && this.getAge().isGreaterThan(this._maxAge);
2745
+ }
2746
+ /**
2747
+ * Gets the timestamp when the value was last successfully generated.
2748
+ * @returns The last update timestamp.
2749
+ */
2750
+ getLastUpdated() {
2751
+ return this._generatedAt;
2752
+ }
2753
+ /**
2754
+ * Gets the age of the cached value.
2755
+ * @returns The duration since the value was last generated.
2756
+ */
2757
+ getAge() {
2758
+ return this._generatedAt.distanceFromNow();
2759
+ }
2760
+ /**
2761
+ * Invalidates the cache by resetting the generation timestamp.
2762
+ * Next access will trigger fresh generation.
2763
+ */
2764
+ invalidate() {
2765
+ this._generatedAt = TimeInstant.ZERO;
2766
+ }
2767
+ /**
2768
+ * Sets a callback to be invoked when new data becomes available.
2769
+ * @param callback Function to call with the new value.
2770
+ */
2771
+ onNewDataAvailable(callback) {
2772
+ this._onNewDataAvailable = ensureDefined(callback);
2773
+ }
2774
+ /**
2775
+ * Sets a callback to be invoked when generation fails.
2776
+ * @param callback Function to call with the error.
2777
+ */
2778
+ onError(callback) {
2779
+ this._onError = ensureDefined(callback);
2780
+ }
2781
+ /**
2782
+ * Creates a new Cached instance.
2783
+ * @param generator Function that generates the value to cache.
2784
+ * @param maxAge Maximum duration for which cached value is considered valid.
2785
+ * @param maxStale Maximum duration for which stale value is served while revalidating. Defaults to maxAge.
2786
+ * @returns A new Cached instance.
2787
+ */
2788
+ static of(generator, maxAge, maxStale = maxAge) {
2789
+ const cached = new Cached(generator, maxAge, maxStale);
2790
+ void cached.get();
2791
+ return cached;
2792
+ }
2793
+ }
2794
+
2615
2795
  const defaultCompareValuesOptions = {
2616
2796
  nullsFirst: false,
2617
2797
  };
@@ -3086,6 +3266,7 @@ function isUpgradable(obj) {
3086
3266
  return isDefined(obj) && typeof obj === "object" && VERSION_FIELD in obj && isNumber(obj.$version) && isPositiveNumber(obj.$version);
3087
3267
  }
3088
3268
 
3269
+ exports.Cached = Cached;
3089
3270
  exports.DataUpgrader = DataUpgrader;
3090
3271
  exports.Deferred = Deferred;
3091
3272
  exports.DeferredCanceledError = DeferredCanceledError;
@@ -3186,6 +3367,7 @@ exports.indexByString = indexByString;
3186
3367
  exports.indexByStringWith = indexByStringWith;
3187
3368
  exports.indexBySymbol = indexBySymbol;
3188
3369
  exports.indexBySymbolWith = indexBySymbolWith;
3370
+ exports.indexByWith = indexByWith;
3189
3371
  exports.isAllowedTimeDuration = isAllowedTimeDuration;
3190
3372
  exports.isArray = isArray;
3191
3373
  exports.isDefined = isDefined;