@adviser/cement 0.4.63 → 0.4.64-dev
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/cjs/index.cjs +1 -0
- package/cjs/index.cjs.map +1 -1
- package/cjs/index.d.ts +1 -0
- package/cjs/index.d.ts.map +1 -1
- package/cjs/keyed-ng.cjs +93 -0
- package/cjs/keyed-ng.cjs.map +1 -0
- package/cjs/keyed-ng.d.ts +51 -0
- package/cjs/keyed-ng.d.ts.map +1 -0
- package/cjs/keyed-ng.test.cjs +151 -0
- package/cjs/keyed-ng.test.cjs.map +1 -0
- package/cjs/keyed-ng.test.d.ts +2 -0
- package/cjs/keyed-ng.test.d.ts.map +1 -0
- package/cjs/lru-map-set.cjs +2 -1
- package/cjs/lru-map-set.cjs.map +1 -1
- package/cjs/lru-map-set.d.ts +4 -1
- package/cjs/lru-map-set.d.ts.map +1 -1
- package/cjs/resolve-once.cjs +67 -104
- package/cjs/resolve-once.cjs.map +1 -1
- package/cjs/resolve-once.d.ts +26 -62
- package/cjs/resolve-once.d.ts.map +1 -1
- package/cjs/resolve-once.test.cjs +119 -31
- package/cjs/resolve-once.test.cjs.map +1 -1
- package/cjs/version.cjs +1 -1
- package/cjs/version.cjs.map +1 -1
- package/cjs/version.d.ts.map +1 -1
- package/deno.json +1 -1
- package/esm/index.d.ts +1 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/keyed-ng.d.ts +51 -0
- package/esm/keyed-ng.d.ts.map +1 -0
- package/esm/keyed-ng.js +89 -0
- package/esm/keyed-ng.js.map +1 -0
- package/esm/keyed-ng.test.d.ts +2 -0
- package/esm/keyed-ng.test.d.ts.map +1 -0
- package/esm/keyed-ng.test.js +149 -0
- package/esm/keyed-ng.test.js.map +1 -0
- package/esm/lru-map-set.d.ts +4 -1
- package/esm/lru-map-set.d.ts.map +1 -1
- package/esm/lru-map-set.js +2 -1
- package/esm/lru-map-set.js.map +1 -1
- package/esm/resolve-once.d.ts +26 -62
- package/esm/resolve-once.d.ts.map +1 -1
- package/esm/resolve-once.js +66 -102
- package/esm/resolve-once.js.map +1 -1
- package/esm/resolve-once.test.js +119 -31
- package/esm/resolve-once.test.js.map +1 -1
- package/esm/version.d.ts.map +1 -1
- package/esm/version.js +1 -1
- package/esm/version.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/keyed-ng.ts +628 -0
- package/src/lru-map-set.ts +6 -2
- package/src/resolve-once.ts +281 -324
- package/ts/cjs/index.d.ts +1 -0
- package/ts/cjs/index.d.ts.map +1 -1
- package/ts/cjs/index.js +1 -0
- package/ts/cjs/index.js.map +1 -1
- package/ts/cjs/keyed-ng.d.ts +51 -0
- package/ts/cjs/keyed-ng.d.ts.map +1 -0
- package/ts/cjs/keyed-ng.js +93 -0
- package/ts/cjs/keyed-ng.js.map +1 -0
- package/ts/cjs/keyed-ng.test.d.ts +2 -0
- package/ts/cjs/keyed-ng.test.d.ts.map +1 -0
- package/ts/cjs/keyed-ng.test.js +151 -0
- package/ts/cjs/keyed-ng.test.js.map +1 -0
- package/ts/cjs/lru-map-set.d.ts +4 -1
- package/ts/cjs/lru-map-set.d.ts.map +1 -1
- package/ts/cjs/lru-map-set.js +2 -1
- package/ts/cjs/lru-map-set.js.map +1 -1
- package/ts/cjs/resolve-once.d.ts +26 -62
- package/ts/cjs/resolve-once.d.ts.map +1 -1
- package/ts/cjs/resolve-once.js +67 -104
- package/ts/cjs/resolve-once.js.map +1 -1
- package/ts/cjs/resolve-once.test.js +119 -31
- package/ts/cjs/resolve-once.test.js.map +1 -1
- package/ts/cjs/version.d.ts.map +1 -1
- package/ts/cjs/version.js +1 -1
- package/ts/cjs/version.js.map +1 -1
- package/ts/esm/index.d.ts +1 -0
- package/ts/esm/index.d.ts.map +1 -1
- package/ts/esm/index.js +1 -0
- package/ts/esm/index.js.map +1 -1
- package/ts/esm/keyed-ng.d.ts +51 -0
- package/ts/esm/keyed-ng.d.ts.map +1 -0
- package/ts/esm/keyed-ng.js +89 -0
- package/ts/esm/keyed-ng.js.map +1 -0
- package/ts/esm/keyed-ng.test.d.ts +2 -0
- package/ts/esm/keyed-ng.test.d.ts.map +1 -0
- package/ts/esm/keyed-ng.test.js +149 -0
- package/ts/esm/keyed-ng.test.js.map +1 -0
- package/ts/esm/lru-map-set.d.ts +4 -1
- package/ts/esm/lru-map-set.d.ts.map +1 -1
- package/ts/esm/lru-map-set.js +2 -1
- package/ts/esm/lru-map-set.js.map +1 -1
- package/ts/esm/resolve-once.d.ts +26 -62
- package/ts/esm/resolve-once.d.ts.map +1 -1
- package/ts/esm/resolve-once.js +66 -102
- package/ts/esm/resolve-once.js.map +1 -1
- package/ts/esm/resolve-once.test.js +119 -31
- package/ts/esm/resolve-once.test.js.map +1 -1
- package/ts/esm/version.d.ts.map +1 -1
- package/ts/esm/version.js +1 -1
- package/ts/esm/version.js.map +1 -1
package/src/resolve-once.ts
CHANGED
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
import { Future } from "./future.js";
|
|
18
18
|
import { UnPromisify } from "./is-promise.js";
|
|
19
19
|
import { isPromise } from "./is-promise.js";
|
|
20
|
-
import {
|
|
20
|
+
import { UnregFn } from "./lru-map-set.js";
|
|
21
21
|
import { Result } from "./result.js";
|
|
22
22
|
import { Option } from "./option.js";
|
|
23
|
+
import { KeyedIf, KeyedNg, KeyedNgItem, KeyedNgItemWithoutValue, KeyedNgOptions } from "./keyed-ng.js";
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Internal item representing a queued function in a ResolveSeq sequence.
|
|
@@ -562,6 +563,11 @@ export class ResolveOnce<T, CTX = void> implements ResolveOnceIf<T, CTX> {
|
|
|
562
563
|
|
|
563
564
|
reset<R>(fn?: (c: CTX) => R): ResultOnce<R> {
|
|
564
565
|
if (this.#state === "initial") {
|
|
566
|
+
if (!fn) {
|
|
567
|
+
// eslint-disable-next-line no-console
|
|
568
|
+
console.warn("ResolveOnce.reset called but not yet resolved and no fn given");
|
|
569
|
+
return undefined as ResultOnce<R>;
|
|
570
|
+
}
|
|
565
571
|
return this.once(fn as (c: CTX) => R);
|
|
566
572
|
}
|
|
567
573
|
if (this.#state === "processing") {
|
|
@@ -573,407 +579,364 @@ export class ResolveOnce<T, CTX = void> implements ResolveOnceIf<T, CTX> {
|
|
|
573
579
|
}
|
|
574
580
|
}
|
|
575
581
|
|
|
582
|
+
// /**
|
|
583
|
+
// * Configuration parameters for Keyed instances.
|
|
584
|
+
// * @template K - The key type
|
|
585
|
+
// * @template V - The value type
|
|
586
|
+
// */
|
|
587
|
+
// export interface KeyedParam<K, V> {
|
|
588
|
+
// readonly lru: Partial<LRUParam<V, K>>;
|
|
589
|
+
// }
|
|
590
|
+
|
|
576
591
|
/**
|
|
577
|
-
*
|
|
592
|
+
* Represents a key-value pair where the value is wrapped in a Result.
|
|
578
593
|
* @template K - The key type
|
|
579
594
|
* @template V - The value type
|
|
580
595
|
*/
|
|
581
|
-
export interface
|
|
582
|
-
readonly
|
|
596
|
+
export interface KeyItem<K, V> {
|
|
597
|
+
readonly key: K;
|
|
598
|
+
readonly value: Result<V>;
|
|
583
599
|
}
|
|
584
600
|
|
|
585
601
|
/**
|
|
586
|
-
*
|
|
602
|
+
* Configuration parameters for KeyedResolvOnce, excluding the createValue factory.
|
|
587
603
|
* @template K - The key type
|
|
588
604
|
* @template V - The value type
|
|
589
605
|
* @template CTX - The context type
|
|
590
606
|
*/
|
|
591
|
-
export type AddKeyedParam<K, V, CTX extends NonNullable<object>> =
|
|
607
|
+
export type AddKeyedParam<K, V, CTX extends NonNullable<object>> = Omit<KeyedNgOptions<K, V, CTX>, "createValue">;
|
|
592
608
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
*/
|
|
600
|
-
onSet(fn: (key: K, value: T) => void): UnregFn;
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* Registers a callback that fires when an entry is deleted from the map.
|
|
604
|
-
*
|
|
605
|
-
* @param fn - Callback function receiving key and value
|
|
606
|
-
* @returns Unregister function to remove the callback
|
|
607
|
-
*/
|
|
608
|
-
onDelete(fn: (key: K, value: T) => void): UnregFn;
|
|
609
|
-
|
|
610
|
-
/**
|
|
611
|
-
* Updates the LRU parameters of the underlying map.
|
|
612
|
-
*
|
|
613
|
-
* @param params - New parameters to apply
|
|
614
|
-
*/
|
|
615
|
-
setParam(params: KeyedParam<K, T>): void;
|
|
616
|
-
|
|
617
|
-
/**
|
|
618
|
-
* Async variant of get() that accepts a function returning a promise for the key.
|
|
619
|
-
*
|
|
620
|
-
* @param key - Function that returns a promise resolving to the key
|
|
621
|
-
* @returns Promise resolving to the value
|
|
622
|
-
*/
|
|
623
|
-
asyncGet(key: () => Promise<K>): Promise<T>;
|
|
609
|
+
/**
|
|
610
|
+
* Type helper that adds a key property to a context object.
|
|
611
|
+
* @template X - The context type
|
|
612
|
+
* @template K - The key type
|
|
613
|
+
*/
|
|
614
|
+
export type WithKey<X extends NonNullable<object>, K> = X & { readonly key: K };
|
|
624
615
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
* @param key - The key or function returning the key
|
|
631
|
-
* @returns The value associated with the key
|
|
632
|
-
*/
|
|
633
|
-
get(key: K | (() => K)): T;
|
|
616
|
+
/**
|
|
617
|
+
* Type helper that adds an optional reset method to a value type.
|
|
618
|
+
* @template V - The value type
|
|
619
|
+
*/
|
|
620
|
+
export type WithOptionalReset<V> = V & { readonly reset?: () => void };
|
|
634
621
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
622
|
+
/**
|
|
623
|
+
* Represents an item in a KeyedResolvOnce collection with its resolved result.
|
|
624
|
+
* @template K - The key type
|
|
625
|
+
* @template T - The value type
|
|
626
|
+
* @template CTX - The context type
|
|
627
|
+
*/
|
|
628
|
+
export interface KeyedResolveOnceItem<K, T, CTX extends NonNullable<object>> {
|
|
629
|
+
/** The key associated with this item */
|
|
630
|
+
readonly key: K;
|
|
631
|
+
/** The resolved value wrapped in a Result (Ok or Err) */
|
|
632
|
+
readonly value: Result<T>;
|
|
633
|
+
/** The complete KeyedNgItem containing metadata */
|
|
634
|
+
readonly item: KeyedNgItem<K, ResolveOnce<WithOptionalReset<T>, KeyedNgItemWithoutValue<K, CTX>>, CTX>;
|
|
635
|
+
}
|
|
642
636
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
637
|
+
/**
|
|
638
|
+
* Keyed collection of ResolveOnce instances.
|
|
639
|
+
*
|
|
640
|
+
* Manages a map of ResolveOnce instances indexed by keys, with optional LRU caching.
|
|
641
|
+
* Each key gets its own ResolveOnce instance that can be accessed and manipulated independently.
|
|
642
|
+
* Values can optionally have a reset() method for cleanup on deletion.
|
|
643
|
+
*
|
|
644
|
+
* @template T - The return type of the ResolveOnce instances (must include optional reset)
|
|
645
|
+
* @template K - The key type (defaults to string)
|
|
646
|
+
* @template CTX - Optional context type (defaults to empty object)
|
|
647
|
+
* @template PT - Plain type of T without reset (for internal use)
|
|
648
|
+
*
|
|
649
|
+
* @example
|
|
650
|
+
* ```typescript
|
|
651
|
+
* const cache = new KeyedResolvOnce<number, string>();
|
|
652
|
+
*
|
|
653
|
+
* // Each key gets its own ResolveOnce
|
|
654
|
+
* const result1 = cache.get('key1').once(() => expensiveCalc1());
|
|
655
|
+
* const result2 = cache.get('key2').once(() => expensiveCalc2());
|
|
656
|
+
*
|
|
657
|
+
* // Delete specific key
|
|
658
|
+
* cache.delete('key1');
|
|
659
|
+
*
|
|
660
|
+
* // Iterate over all resolved entries
|
|
661
|
+
* cache.forEach((item) => {
|
|
662
|
+
* console.log(item.key, item.value.Ok);
|
|
663
|
+
* });
|
|
664
|
+
* ```
|
|
665
|
+
*
|
|
666
|
+
* @example
|
|
667
|
+
* ```typescript
|
|
668
|
+
* // With custom key type and context
|
|
669
|
+
* interface UserKey { org: string; id: string; }
|
|
670
|
+
* interface UserContext { apiKey: string; }
|
|
671
|
+
*
|
|
672
|
+
* const users = new KeyedResolvOnce<User, UserKey, UserContext>({
|
|
673
|
+
* key2string: (key) => `${key.org}:${key.id}`,
|
|
674
|
+
* ctx: { apiKey: 'default' },
|
|
675
|
+
* lru: { max: 100 }
|
|
676
|
+
* });
|
|
677
|
+
*
|
|
678
|
+
* const user = users.get({ org: 'acme', id: '123' })
|
|
679
|
+
* .once(({ givenKey, ctx }) => fetchUser(givenKey, ctx));
|
|
680
|
+
* ```
|
|
681
|
+
*/
|
|
682
|
+
export class KeyedResolvOnce<T extends WithOptionalReset<PT>, K = string, CTX extends NonNullable<object> = object, PT = T>
|
|
683
|
+
implements
|
|
684
|
+
Omit<
|
|
685
|
+
// KeyedIf<ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>, WithOptionalReset<T>, K>
|
|
686
|
+
KeyedIf<
|
|
687
|
+
KeyedNgItem<K, ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>, CTX>,
|
|
688
|
+
ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>,
|
|
689
|
+
K,
|
|
690
|
+
CTX
|
|
691
|
+
>,
|
|
692
|
+
"entries" | "forEach" | "onSet" | "onDelete" | "values" | "setParam"
|
|
693
|
+
>
|
|
694
|
+
{
|
|
695
|
+
/** @internal */
|
|
696
|
+
readonly _keyed: KeyedNg<K, ResolveOnce<WithOptionalReset<T>, KeyedNgItemWithoutValue<K, CTX>>, CTX>;
|
|
649
697
|
|
|
650
698
|
/**
|
|
651
|
-
*
|
|
652
|
-
*
|
|
653
|
-
* Calls the value's reset() method before removing it.
|
|
699
|
+
* Creates a new KeyedResolvOnce instance.
|
|
654
700
|
*
|
|
655
|
-
* @param
|
|
701
|
+
* @param kp - Configuration options (key2string, ctx, lru)
|
|
656
702
|
*/
|
|
657
|
-
|
|
703
|
+
constructor(kp: Partial<AddKeyedParam<K, T, CTX>> = {}) {
|
|
704
|
+
this._keyed = new KeyedNg({
|
|
705
|
+
createValue: (
|
|
706
|
+
item: KeyedNgItem<K, ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>, CTX>,
|
|
707
|
+
): ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>> => {
|
|
708
|
+
return new ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>({
|
|
709
|
+
...item,
|
|
710
|
+
ctx: kp.ctx ?? item.ctx,
|
|
711
|
+
});
|
|
712
|
+
},
|
|
713
|
+
key2string: kp.key2string,
|
|
714
|
+
ctx: kp.ctx as CTX,
|
|
715
|
+
lru: kp.lru,
|
|
716
|
+
});
|
|
717
|
+
}
|
|
658
718
|
|
|
659
719
|
/**
|
|
660
|
-
*
|
|
720
|
+
* Returns all keys currently in the collection.
|
|
661
721
|
*
|
|
662
|
-
*
|
|
722
|
+
* @returns Array of all keys
|
|
663
723
|
*/
|
|
664
|
-
|
|
724
|
+
keys(): K[] {
|
|
725
|
+
return this._keyed.keys();
|
|
726
|
+
}
|
|
665
727
|
|
|
666
728
|
/**
|
|
667
|
-
* Returns all values in
|
|
729
|
+
* Returns all resolved items with their values wrapped in Result.
|
|
668
730
|
*
|
|
669
|
-
*
|
|
670
|
-
|
|
671
|
-
values(): T[];
|
|
672
|
-
|
|
673
|
-
/**
|
|
674
|
-
* Returns all keys in the map.
|
|
731
|
+
* Only includes items that have been resolved (ready state).
|
|
732
|
+
* Each item contains the key, Result-wrapped value, and full item metadata.
|
|
675
733
|
*
|
|
676
|
-
* @returns Array of all
|
|
677
|
-
*/
|
|
678
|
-
keys(): K[];
|
|
679
|
-
|
|
680
|
-
/**
|
|
681
|
-
* Iterates over all entries in the map.
|
|
734
|
+
* @returns Array of all resolved items
|
|
682
735
|
*
|
|
683
|
-
* @
|
|
736
|
+
* @example
|
|
737
|
+
* ```typescript
|
|
738
|
+
* const items = cache.values();
|
|
739
|
+
* items.forEach(({ key, value }) => {
|
|
740
|
+
* if (value.Ok) {
|
|
741
|
+
* console.log(key, value.unwrap());
|
|
742
|
+
* } else {
|
|
743
|
+
* console.error(key, value.unwrapErr());
|
|
744
|
+
* }
|
|
745
|
+
* });
|
|
746
|
+
* ```
|
|
684
747
|
*/
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
* and cached with optional LRU eviction. Values must have a `reset()` method for cleanup.
|
|
695
|
-
*
|
|
696
|
-
* @template T - The value type (must have a reset method)
|
|
697
|
-
* @template K - The key type
|
|
698
|
-
* @template CTX - Optional context type passed to the factory
|
|
699
|
-
*
|
|
700
|
-
* @example
|
|
701
|
-
* ```typescript
|
|
702
|
-
* const keyed = new Keyed(
|
|
703
|
-
* (ctx) => new ResolveOnce(ctx),
|
|
704
|
-
* { lru: { maxEntries: 100 } }
|
|
705
|
-
* );
|
|
706
|
-
*
|
|
707
|
-
* const instance = keyed.get('myKey');
|
|
708
|
-
* ```
|
|
709
|
-
*/
|
|
710
|
-
export class Keyed<T extends { reset: () => void }, K = string, CTX extends NonNullable<object> = object> implements KeyedIf<T, K> {
|
|
711
|
-
protected readonly _map: LRUMap<K, T>;
|
|
712
|
-
readonly #ctx: CTX;
|
|
713
|
-
|
|
714
|
-
readonly factory: (ctx: AddKey<CTX, K>) => T;
|
|
715
|
-
|
|
716
|
-
constructor(factory: (ctx: AddKey<CTX, K>) => T, ctx: Partial<AddKeyedParam<K, T, CTX>>) {
|
|
717
|
-
this.#ctx = ctx.ctx || ({} as CTX);
|
|
718
|
-
this.factory = factory;
|
|
719
|
-
this._map = new LRUMap<K, T>(ctx?.lru ?? ({ maxEntries: -1 } as LRUParam<T, K>));
|
|
748
|
+
values(): KeyedResolveOnceItem<K, T, CTX>[] {
|
|
749
|
+
return this._keyed
|
|
750
|
+
.values()
|
|
751
|
+
.filter((i) => i.value.ready)
|
|
752
|
+
.map((item) => ({
|
|
753
|
+
key: item.givenKey,
|
|
754
|
+
value: item.value.error ? Result.Err<T>(item.value.error) : Result.Ok<T>(item.value.value as T),
|
|
755
|
+
item,
|
|
756
|
+
}));
|
|
720
757
|
}
|
|
721
758
|
|
|
722
759
|
/**
|
|
723
|
-
* Registers a callback that fires when a new
|
|
760
|
+
* Registers a callback that fires when a new ResolveOnce instance is created.
|
|
724
761
|
*
|
|
725
|
-
* @param fn - Callback
|
|
726
|
-
* @returns Unregister function
|
|
762
|
+
* @param fn - Callback receiving the key and ResolveOnce instance
|
|
763
|
+
* @returns Unregister function
|
|
727
764
|
*/
|
|
728
|
-
onSet(fn: (key: K, value: T) => void): UnregFn {
|
|
729
|
-
return this.
|
|
765
|
+
onSet(fn: (key: K, value: ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>) => void): UnregFn {
|
|
766
|
+
return this._keyed.onSet((item) => {
|
|
767
|
+
fn(item.givenKey, item.value);
|
|
768
|
+
});
|
|
730
769
|
}
|
|
731
770
|
|
|
732
771
|
/**
|
|
733
|
-
* Registers a callback that fires when
|
|
772
|
+
* Registers a callback that fires when a ResolveOnce instance is deleted.
|
|
734
773
|
*
|
|
735
|
-
* @param fn - Callback
|
|
736
|
-
* @returns Unregister function
|
|
774
|
+
* @param fn - Callback receiving the key and ResolveOnce instance
|
|
775
|
+
* @returns Unregister function
|
|
737
776
|
*/
|
|
738
|
-
onDelete(fn: (key: K, value: T) => void): UnregFn {
|
|
739
|
-
return this.
|
|
777
|
+
onDelete(fn: (key: K, value: ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>) => void): UnregFn {
|
|
778
|
+
return this._keyed.onDelete((item) => {
|
|
779
|
+
fn(item.givenKey, item.value);
|
|
780
|
+
});
|
|
740
781
|
}
|
|
741
782
|
|
|
742
783
|
/**
|
|
743
|
-
* Updates the LRU parameters
|
|
784
|
+
* Updates the LRU parameters dynamically.
|
|
744
785
|
*
|
|
745
|
-
* @param params - New parameters
|
|
786
|
+
* @param params - New LRU parameters
|
|
746
787
|
*/
|
|
747
|
-
setParam(params:
|
|
748
|
-
this.
|
|
788
|
+
setParam(params: Partial<AddKeyedParam<K, ResolveOnce<T, CTX>, CTX>>): void {
|
|
789
|
+
this._keyed.setParam({ lru: params.lru });
|
|
749
790
|
}
|
|
750
791
|
|
|
751
792
|
/**
|
|
752
|
-
*
|
|
793
|
+
* Asynchronously gets or creates a ResolveOnce for a key resolved from a promise.
|
|
753
794
|
*
|
|
754
|
-
* @param key - Function
|
|
755
|
-
* @returns Promise resolving to the
|
|
795
|
+
* @param key - Function returning a promise that resolves to the key
|
|
796
|
+
* @returns Promise resolving to the ResolveOnce instance
|
|
756
797
|
*/
|
|
757
|
-
|
|
758
|
-
return this.
|
|
798
|
+
asyncGet(key: () => Promise<K>): Promise<ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>> {
|
|
799
|
+
return this._keyed.asyncGet(key);
|
|
759
800
|
}
|
|
760
801
|
|
|
761
802
|
/**
|
|
762
|
-
* Gets or creates a
|
|
803
|
+
* Gets or creates a ResolveOnce instance for the given key.
|
|
763
804
|
*
|
|
764
|
-
*
|
|
805
|
+
* This is the primary method for accessing ResolveOnce instances. Each unique
|
|
806
|
+
* key gets its own instance that persists across calls.
|
|
765
807
|
*
|
|
766
808
|
* @param key - The key or function returning the key
|
|
767
|
-
* @
|
|
809
|
+
* @param ctx - Optional context override for this operation
|
|
810
|
+
* @returns The ResolveOnce instance for this key
|
|
811
|
+
*
|
|
812
|
+
* @example
|
|
813
|
+
* ```typescript
|
|
814
|
+
* const result = cache.get('myKey').once(({ refKey, givenKey, ctx }) => {
|
|
815
|
+
* return computeValue(givenKey, ctx);
|
|
816
|
+
* });
|
|
817
|
+
* ```
|
|
768
818
|
*/
|
|
769
|
-
get(key: K | (() => K)): T {
|
|
819
|
+
get(key: K | (() => K), ctx?: CTX): ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>> {
|
|
770
820
|
if (typeof key === "function") {
|
|
771
821
|
key = (key as () => K)();
|
|
772
822
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
823
|
+
return this._keyed.getItem(key, ctx).value;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Gets or creates the complete KeyedNgItem for a key.
|
|
828
|
+
*
|
|
829
|
+
* Useful when you need access to the full item structure including metadata.
|
|
830
|
+
*
|
|
831
|
+
* @param key - The key to get
|
|
832
|
+
* @param ctx - Optional context override
|
|
833
|
+
* @returns The complete KeyedNgItem
|
|
834
|
+
*/
|
|
835
|
+
getItem(key: K, ctx?: CTX): KeyedNgItem<K, ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>, CTX> {
|
|
836
|
+
return this._keyed.getItem(key, ctx);
|
|
779
837
|
}
|
|
780
838
|
|
|
781
839
|
/**
|
|
782
|
-
* Checks if a key exists in the
|
|
840
|
+
* Checks if a key exists in the collection.
|
|
783
841
|
*
|
|
784
842
|
* @param key - The key or function returning the key
|
|
785
843
|
* @returns True if the key exists
|
|
786
844
|
*/
|
|
787
845
|
has(key: K | (() => K)): boolean {
|
|
788
|
-
|
|
789
|
-
key = (key as () => K)();
|
|
790
|
-
}
|
|
791
|
-
return this._map.has(key);
|
|
846
|
+
return this._keyed.has(key);
|
|
792
847
|
}
|
|
793
848
|
|
|
794
849
|
/**
|
|
795
|
-
* Deletes an entry from the
|
|
850
|
+
* Deletes an entry from the collection.
|
|
851
|
+
*
|
|
852
|
+
* Triggers onDelete callbacks before removal.
|
|
796
853
|
*
|
|
797
854
|
* @param key - The key to delete
|
|
798
855
|
*/
|
|
799
856
|
delete(key: K): void {
|
|
800
|
-
this.
|
|
857
|
+
this._keyed.delete(key);
|
|
801
858
|
}
|
|
802
859
|
|
|
803
860
|
/**
|
|
804
|
-
* Resets and
|
|
861
|
+
* Resets and removes an entry from the collection.
|
|
805
862
|
*
|
|
806
|
-
* Calls the
|
|
863
|
+
* Calls the optional reset() method on the value before deletion,
|
|
864
|
+
* allowing for cleanup operations.
|
|
807
865
|
*
|
|
808
866
|
* @param key - The key to reset and delete
|
|
809
867
|
*/
|
|
810
868
|
unget(key: K): void {
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
this.
|
|
869
|
+
const item = this._keyed.getItem(key);
|
|
870
|
+
item.value.reset?.();
|
|
871
|
+
return this._keyed.delete(item.givenKey);
|
|
814
872
|
}
|
|
815
873
|
|
|
816
874
|
/**
|
|
817
|
-
* Resets all entries
|
|
875
|
+
* Resets all entries by calling their optional reset() methods.
|
|
818
876
|
*
|
|
819
|
-
*
|
|
877
|
+
* Does not remove entries from the collection, only resets their state.
|
|
878
|
+
* Useful for cleanup without losing the collection structure.
|
|
820
879
|
*/
|
|
821
880
|
reset(): void {
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
/**
|
|
827
|
-
* Returns all values in the map.
|
|
828
|
-
*
|
|
829
|
-
* @returns Array of all values
|
|
830
|
-
*/
|
|
831
|
-
values(): T[] {
|
|
832
|
-
const results: T[] = [];
|
|
833
|
-
this.forEach((_, v) => {
|
|
834
|
-
results.push(v);
|
|
835
|
-
});
|
|
836
|
-
return results;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
/**
|
|
840
|
-
* Returns all keys in the map.
|
|
841
|
-
*
|
|
842
|
-
* @returns Array of all keys
|
|
843
|
-
*/
|
|
844
|
-
keys(): K[] {
|
|
845
|
-
const results: K[] = [];
|
|
846
|
-
this.forEach((k) => {
|
|
847
|
-
results.push(k);
|
|
848
|
-
});
|
|
849
|
-
return results;
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
/**
|
|
853
|
-
* Iterates over all entries in the map.
|
|
854
|
-
*
|
|
855
|
-
* @yields Key-value pairs
|
|
856
|
-
*/
|
|
857
|
-
forEach(fn: (k: K, v: T, idx: number) => void): void {
|
|
858
|
-
let idx = 0;
|
|
859
|
-
for (const [k, v] of this._map.entries()) {
|
|
860
|
-
fn(k, v, idx++);
|
|
881
|
+
for (const v of this._keyed.values()) {
|
|
882
|
+
v.value.reset?.();
|
|
861
883
|
}
|
|
862
884
|
}
|
|
863
885
|
|
|
864
|
-
*entries(): Iterable<[K, T]> {
|
|
865
|
-
for (const [k, v] of this._map.entries()) {
|
|
866
|
-
yield [k, v];
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
/**
|
|
872
|
-
* Represents a key-value pair where the value is wrapped in a Result.
|
|
873
|
-
* @template K - The key type
|
|
874
|
-
* @template V - The value type
|
|
875
|
-
*/
|
|
876
|
-
export interface KeyItem<K, V> {
|
|
877
|
-
readonly key: K;
|
|
878
|
-
readonly value: Result<V>;
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
/**
|
|
882
|
-
* Keyed collection of ResolveOnce instances.
|
|
883
|
-
*
|
|
884
|
-
* Manages a map of ResolveOnce instances indexed by keys, with optional LRU caching.
|
|
885
|
-
* Each key gets its own ResolveOnce instance that can be accessed and manipulated independently.
|
|
886
|
-
*
|
|
887
|
-
* @template T - The return type of the ResolveOnce instances
|
|
888
|
-
* @template K - The key type
|
|
889
|
-
* @template CTX - Optional context type
|
|
890
|
-
*
|
|
891
|
-
* @example
|
|
892
|
-
* ```typescript
|
|
893
|
-
* const cache = new KeyedResolvOnce<number, string>();
|
|
894
|
-
*
|
|
895
|
-
* // Each key gets its own ResolveOnce
|
|
896
|
-
* const result1 = cache.get('key1').once(() => expensiveCalc1());
|
|
897
|
-
* const result2 = cache.get('key2').once(() => expensiveCalc2());
|
|
898
|
-
* ```
|
|
899
|
-
*/
|
|
900
|
-
export class KeyedResolvOnce<T, K = string, CTX extends NonNullable<object> = object>
|
|
901
|
-
implements Omit<KeyedIf<ResolveOnce<T, AddKey<CTX, K>>, K>, "forEach" | "keys" | "values" | "entries">
|
|
902
|
-
{
|
|
903
|
-
readonly _keyed: KeyedIf<ResolveOnce<T, AddKey<CTX, K>>, K>;
|
|
904
|
-
constructor(kp: Partial<AddKeyedParam<K, ResolveOnce<T, CTX>, CTX>> = {}) {
|
|
905
|
-
this._keyed = new Keyed(
|
|
906
|
-
(ctx) => new ResolveOnce<T, AddKey<CTX, K>>(ctx),
|
|
907
|
-
kp as AddKeyedParam<K, ResolveOnce<T, AddKey<CTX, K>>, CTX>,
|
|
908
|
-
);
|
|
909
|
-
}
|
|
910
|
-
keys(): K[] {
|
|
911
|
-
const results: K[] = [];
|
|
912
|
-
this.forEach((k) => {
|
|
913
|
-
results.push(k.key);
|
|
914
|
-
});
|
|
915
|
-
return results;
|
|
916
|
-
}
|
|
917
|
-
values(): KeyItem<K, T>[] {
|
|
918
|
-
const results: KeyItem<K, T>[] = [];
|
|
919
|
-
this.forEach((v) => {
|
|
920
|
-
results.push(v);
|
|
921
|
-
});
|
|
922
|
-
return results;
|
|
923
|
-
}
|
|
924
|
-
onSet(fn: (key: K, value: ResolveOnce<T, AddKey<CTX, K>>) => void): UnregFn {
|
|
925
|
-
return this._keyed.onSet(fn);
|
|
926
|
-
}
|
|
927
|
-
onDelete(fn: (key: K, value: ResolveOnce<T, AddKey<CTX, K>>) => void): UnregFn {
|
|
928
|
-
return this._keyed.onDelete(fn);
|
|
929
|
-
}
|
|
930
|
-
setParam(params: KeyedParam<K, ResolveOnce<T, AddKey<CTX, K>>>): void {
|
|
931
|
-
this._keyed.setParam(params);
|
|
932
|
-
}
|
|
933
|
-
asyncGet(key: () => Promise<K>): Promise<ResolveOnce<T, AddKey<CTX, K>>> {
|
|
934
|
-
return this._keyed.asyncGet(key);
|
|
935
|
-
}
|
|
936
|
-
get(key: K | (() => K)): ResolveOnce<T, AddKey<CTX, K>> {
|
|
937
|
-
return this._keyed.get(key);
|
|
938
|
-
}
|
|
939
|
-
has(key: K | (() => K)): boolean {
|
|
940
|
-
return this._keyed.has(key);
|
|
941
|
-
}
|
|
942
|
-
delete(key: K): void {
|
|
943
|
-
this._keyed.delete(key);
|
|
944
|
-
}
|
|
945
|
-
unget(key: K): void {
|
|
946
|
-
this._keyed.unget(key);
|
|
947
|
-
}
|
|
948
|
-
reset(): void {
|
|
949
|
-
this._keyed.reset();
|
|
950
|
-
}
|
|
951
|
-
|
|
952
886
|
/**
|
|
953
887
|
* Iterates over all completed entries, yielding key-result pairs.
|
|
954
888
|
*
|
|
955
889
|
* Only yields entries that have been resolved (ready state).
|
|
956
890
|
* Values are wrapped in Result to distinguish success from error.
|
|
957
891
|
*
|
|
958
|
-
* @
|
|
892
|
+
* @param fn - Callback receiving KeyItem and index
|
|
893
|
+
*
|
|
894
|
+
* @example
|
|
895
|
+
* ```typescript
|
|
896
|
+
* cache.forEach((item, idx) => {
|
|
897
|
+
* console.log(idx, item.key);
|
|
898
|
+
* if (item.value.Ok) {
|
|
899
|
+
* console.log('Success:', item.value.unwrap());
|
|
900
|
+
* } else {
|
|
901
|
+
* console.error('Error:', item.value.unwrapErr());
|
|
902
|
+
* }
|
|
903
|
+
* });
|
|
904
|
+
* ```
|
|
959
905
|
*/
|
|
960
906
|
forEach(fn: (ki: KeyItem<K, T>, idx: number) => void): void {
|
|
961
|
-
|
|
962
|
-
|
|
907
|
+
for (const [item, idx] of this._keyed.entries()) {
|
|
908
|
+
const v = item.value;
|
|
909
|
+
const k = item.givenKey;
|
|
963
910
|
if (!v.ready) {
|
|
964
911
|
continue;
|
|
965
912
|
}
|
|
966
913
|
if (v.error) {
|
|
967
|
-
fn({ key: k, value: Result.Err<T>(v.error) }, idx
|
|
914
|
+
fn({ key: k, value: Result.Err<T>(v.error) }, idx);
|
|
968
915
|
} else {
|
|
969
|
-
fn({ key: k, value: Result.Ok<T>(v.value as T) }, idx
|
|
916
|
+
fn({ key: k, value: Result.Ok<T>(v.value as T) }, idx);
|
|
970
917
|
}
|
|
971
918
|
}
|
|
972
919
|
}
|
|
973
920
|
|
|
921
|
+
/**
|
|
922
|
+
* Returns an iterable of all completed entries.
|
|
923
|
+
*
|
|
924
|
+
* Only yields entries that have been resolved. Values are wrapped in Result.
|
|
925
|
+
*
|
|
926
|
+
* @returns Iterable of KeyItem entries
|
|
927
|
+
*
|
|
928
|
+
* @example
|
|
929
|
+
* ```typescript
|
|
930
|
+
* for (const item of cache.entries()) {
|
|
931
|
+
* console.log(item.key, item.value.Ok);
|
|
932
|
+
* }
|
|
933
|
+
* ```
|
|
934
|
+
*/
|
|
974
935
|
*entries(): Iterable<KeyItem<K, T>> {
|
|
975
936
|
/* this is not optimal, but sufficient for now */
|
|
976
|
-
for (const [
|
|
937
|
+
for (const [item] of this._keyed.entries()) {
|
|
938
|
+
const v = item.value;
|
|
939
|
+
const k = item.givenKey;
|
|
977
940
|
if (!v.ready) {
|
|
978
941
|
continue;
|
|
979
942
|
}
|
|
@@ -986,37 +949,14 @@ export class KeyedResolvOnce<T, K = string, CTX extends NonNullable<object> = ob
|
|
|
986
949
|
}
|
|
987
950
|
}
|
|
988
951
|
|
|
989
|
-
/**
|
|
990
|
-
* Type helper that adds a key property to a context object.
|
|
991
|
-
*
|
|
992
|
-
* Used by keyed collections to provide the current key to factory functions and callbacks.
|
|
993
|
-
*
|
|
994
|
-
* @template X - The context type
|
|
995
|
-
* @template K - The key type
|
|
996
|
-
*
|
|
997
|
-
* @example
|
|
998
|
-
* ```typescript
|
|
999
|
-
* type MyContext = { userId: string };
|
|
1000
|
-
* type WithKey = AddKey<MyContext, number>;
|
|
1001
|
-
* // Result: { userId: string, key: number }
|
|
1002
|
-
* ```
|
|
1003
|
-
*/
|
|
1004
|
-
export type AddKey<X extends NonNullable<object>, K> = X & { key: K };
|
|
1005
|
-
|
|
1006
|
-
/**
|
|
1007
|
-
* Configuration type for KeyedResolvSeq.
|
|
1008
|
-
* @internal
|
|
1009
|
-
*/
|
|
1010
|
-
type WithCTX<K, T, CTX extends NonNullable<object>> = KeyedParam<K, ResolveSeq<T, AddKey<CTX, K>>> & { readonly ctx: CTX };
|
|
1011
|
-
|
|
1012
952
|
/**
|
|
1013
953
|
* Keyed collection of ResolveSeq instances.
|
|
1014
954
|
*
|
|
1015
955
|
* Manages a map of ResolveSeq instances indexed by keys, with optional LRU caching.
|
|
1016
956
|
* Each key gets its own ResolveSeq instance for sequential execution of operations.
|
|
1017
957
|
*
|
|
1018
|
-
* @template
|
|
1019
|
-
* @template
|
|
958
|
+
* @template VALUEType - The return type of the ResolveSeq instances
|
|
959
|
+
* @template KEYType - The key type
|
|
1020
960
|
* @template CTX - Optional context type
|
|
1021
961
|
*
|
|
1022
962
|
* @example
|
|
@@ -1028,13 +968,30 @@ type WithCTX<K, T, CTX extends NonNullable<object>> = KeyedParam<K, ResolveSeq<T
|
|
|
1028
968
|
* sequences.get('user2').add(() => updateUser2());
|
|
1029
969
|
* ```
|
|
1030
970
|
*/
|
|
1031
|
-
export class KeyedResolvSeq<
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
CTX
|
|
1035
|
-
> {
|
|
1036
|
-
|
|
1037
|
-
|
|
971
|
+
export class KeyedResolvSeq<
|
|
972
|
+
VALUEType extends NonNullable<unknown>,
|
|
973
|
+
KEYType = string,
|
|
974
|
+
CTX extends NonNullable<object> = object,
|
|
975
|
+
> extends KeyedNg<KEYType, ResolveSeq<VALUEType, KeyedNgItemWithoutValue<KEYType, CTX>>, CTX> {
|
|
976
|
+
/**
|
|
977
|
+
* Creates a new KeyedResolvSeq instance.
|
|
978
|
+
*
|
|
979
|
+
* @param kp - Configuration options (key2string, ctx, lru)
|
|
980
|
+
*/
|
|
981
|
+
constructor(kp: Partial<Omit<KeyedNgOptions<KEYType, VALUEType, CTX>, "createValue">> = {}) {
|
|
982
|
+
super({
|
|
983
|
+
createValue: (
|
|
984
|
+
item: KeyedNgItem<KEYType, ResolveSeq<VALUEType, KeyedNgItemWithoutValue<KEYType, CTX>>, CTX>,
|
|
985
|
+
): ResolveSeq<VALUEType, KeyedNgItemWithoutValue<KEYType, CTX>> => {
|
|
986
|
+
return new ResolveSeq<VALUEType, KeyedNgItemWithoutValue<KEYType, CTX>>({
|
|
987
|
+
...item,
|
|
988
|
+
ctx: kp.ctx ?? item.ctx,
|
|
989
|
+
});
|
|
990
|
+
},
|
|
991
|
+
key2string: kp.key2string,
|
|
992
|
+
ctx: kp.ctx as CTX,
|
|
993
|
+
lru: kp.lru,
|
|
994
|
+
});
|
|
1038
995
|
}
|
|
1039
996
|
}
|
|
1040
997
|
|