@zelgadis87/utils-core 5.3.0 → 5.3.1
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 +196 -15
- package/.rollup/index.cjs.map +1 -1
- package/.rollup/index.d.ts +113 -7
- package/.rollup/index.mjs +196 -16
- package/.rollup/index.mjs.map +1 -1
- package/.rollup/tsconfig.tsbuildinfo +1 -1
- package/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/lazy/Cached.ts +216 -0
- package/src/lazy/Lazy.ts +4 -0
- package/src/lazy/LazyAsync.ts +4 -0
- package/src/lazy/_index.ts +1 -0
- package/src/utils/arrays/indexBy.ts +17 -17
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,
|
|
428
|
-
return doIndexByWith(arr,
|
|
427
|
+
function indexByStringWith(arr, keyGetter, valueMapper = v => v) {
|
|
428
|
+
return doIndexByWith(arr, keyGetter, valueMapper);
|
|
429
429
|
}
|
|
430
|
-
function indexByNumberWith(arr,
|
|
431
|
-
return doIndexByWith(arr,
|
|
430
|
+
function indexByNumberWith(arr, keyGetter, valueMapper = v => v) {
|
|
431
|
+
return doIndexByWith(arr, keyGetter, valueMapper);
|
|
432
432
|
}
|
|
433
|
-
function indexBySymbolWith(arr,
|
|
434
|
-
return doIndexByWith(arr,
|
|
433
|
+
function indexBySymbolWith(arr, keyGetter, valueMapper = v => v) {
|
|
434
|
+
return doIndexByWith(arr, keyGetter, valueMapper);
|
|
435
435
|
}
|
|
436
|
-
function doIndexByWith(arr,
|
|
436
|
+
function doIndexByWith(arr, keyGetter, valueMapper = v => v) {
|
|
437
437
|
return arr.reduce((dict, cur) => {
|
|
438
|
-
const key =
|
|
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;
|