@fjell/cache 4.7.0 → 4.7.4

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.
Files changed (43) hide show
  1. package/CACHE_EVENTS.md +306 -0
  2. package/CACHE_IMPLEMENTATIONS.md +117 -0
  3. package/MEMORY_LEAK_FIXES.md +270 -0
  4. package/README.md +7 -0
  5. package/dist/Aggregator.d.ts +27 -16
  6. package/dist/Cache.d.ts +55 -0
  7. package/dist/CacheContext.d.ts +13 -5
  8. package/dist/CacheMap.d.ts +67 -24
  9. package/dist/CacheStats.d.ts +51 -0
  10. package/dist/Operations.d.ts +20 -17
  11. package/dist/Options.d.ts +1 -3
  12. package/dist/browser/AsyncIndexDBCacheMap.d.ts +19 -2
  13. package/dist/browser/IndexDBCacheMap.d.ts +49 -36
  14. package/dist/browser/LocalStorageCacheMap.d.ts +38 -22
  15. package/dist/browser/SessionStorageCacheMap.d.ts +35 -19
  16. package/dist/events/CacheEventEmitter.d.ts +83 -0
  17. package/dist/events/CacheEventFactory.d.ts +121 -0
  18. package/dist/events/CacheEventTypes.d.ts +122 -0
  19. package/dist/events/index.d.ts +3 -0
  20. package/dist/eviction/EvictionManager.d.ts +57 -0
  21. package/dist/eviction/EvictionStrategy.d.ts +102 -10
  22. package/dist/eviction/index.d.ts +2 -1
  23. package/dist/eviction/strategies/ARCEvictionStrategy.d.ts +10 -5
  24. package/dist/eviction/strategies/FIFOEvictionStrategy.d.ts +6 -5
  25. package/dist/eviction/strategies/LFUEvictionStrategy.d.ts +6 -5
  26. package/dist/eviction/strategies/LRUEvictionStrategy.d.ts +6 -5
  27. package/dist/eviction/strategies/MRUEvictionStrategy.d.ts +6 -5
  28. package/dist/eviction/strategies/RandomEvictionStrategy.d.ts +6 -5
  29. package/dist/eviction/strategies/TwoQueueEvictionStrategy.d.ts +6 -5
  30. package/dist/index.d.ts +9 -3
  31. package/dist/index.js +4959 -2920
  32. package/dist/index.js.map +4 -4
  33. package/dist/memory/EnhancedMemoryCacheMap.d.ts +36 -30
  34. package/dist/memory/MemoryCacheMap.d.ts +34 -19
  35. package/dist/normalization.d.ts +1 -2
  36. package/dist/ops/get.d.ts +1 -0
  37. package/dist/ttl/TTLManager.d.ts +100 -0
  38. package/dist/ttl/index.d.ts +2 -0
  39. package/dist/utils/CacheSize.d.ts +0 -7
  40. package/fix-async-tests.js +116 -0
  41. package/package.json +19 -16
  42. package/debug_test2.js +0 -0
  43. package/debug_test3.js +0 -0
@@ -1,52 +1,65 @@
1
1
  import { AllItemTypeArrays, ComKey, Item, ItemQuery, LocKeyArray, PriKey } from "@fjell/core";
2
- import { CacheMap } from "../CacheMap";
2
+ import { CacheItemMetadata } from "../eviction/EvictionStrategy";
3
3
  import { AsyncIndexDBCacheMap } from "./AsyncIndexDBCacheMap";
4
+ import { CacheMap } from "../CacheMap";
4
5
  /**
5
- * Synchronous wrapper for IndexedDB CacheMap implementation.
6
+ * Synchronous IndexedDB CacheMap wrapper implementation.
6
7
  *
7
- * This implementation provides a synchronous interface over IndexedDB
8
- * by maintaining an in-memory cache that is periodically synchronized
9
- * with IndexedDB storage. For full async capabilities, use AsyncIndexDBCacheMap.
8
+ * This implementation provides synchronous memory operations with background IndexedDB persistence.
9
+ * Memory operations are immediate while IndexedDB operations happen asynchronously in the background.
10
10
  *
11
- * Note: This class maintains synchronous compatibility while providing
12
- * persistent storage benefits of IndexedDB.
11
+ * Benefits:
12
+ * - Fast memory access for immediate operations
13
+ * - Background persistence to IndexedDB for durability
14
+ * - Synchronous API compatible with other CacheMap implementations
15
+ * - Automatic sync between memory and IndexedDB
13
16
  */
14
17
  export declare class IndexDBCacheMap<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> extends CacheMap<V, S, L1, L2, L3, L4, L5> {
18
+ readonly implementationType = "browser/indexedDB";
19
+ private memoryMap;
20
+ private queryResultCache;
21
+ private metadataMap;
22
+ private normalizedHashFunction;
23
+ protected types: AllItemTypeArrays<S, L1, L2, L3, L4, L5>;
15
24
  asyncCache: AsyncIndexDBCacheMap<V, S, L1, L2, L3, L4, L5>;
16
- private memoryCache;
17
25
  private syncInterval;
18
- private readonly SYNC_INTERVAL_MS;
19
- private pendingSyncOperations;
20
- private initializationPromise;
21
- private isInitialized;
22
- private readonly MAX_RETRY_ATTEMPTS;
26
+ private pendingOperations;
27
+ private sequenceCounter;
23
28
  constructor(types: AllItemTypeArrays<S, L1, L2, L3, L4, L5>, dbName?: string, storeName?: string, version?: number);
24
29
  private initializeFromIndexedDB;
25
30
  private startPeriodicSync;
26
- private syncToIndexedDB;
27
31
  private processPendingOperations;
28
- private queueForSync;
29
- private queueDeleteForSync;
30
- private queueClearForSync;
31
- get(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): V | null;
32
- getWithTTL(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, ttl: number): V | null;
33
- set(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, value: V): void;
34
- includesKey(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): boolean;
35
- delete(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): void;
36
- allIn(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): V[];
37
- contains(query: ItemQuery, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): boolean;
38
- queryIn(query: ItemQuery, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): V[];
39
- clone(): IndexDBCacheMap<V, S, L1, L2, L3, L4, L5>;
40
- keys(): (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[];
41
- values(): V[];
42
- clear(): void;
43
- setQueryResult(queryHash: string, itemKeys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[], ttl?: number): void;
44
- getQueryResult(queryHash: string): (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[] | null;
45
- hasQueryResult(queryHash: string): boolean;
46
- deleteQueryResult(queryHash: string): void;
47
- invalidateItemKeys(keys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]): void;
48
- invalidateLocation(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): void;
49
- clearQueryResults(): void;
32
+ get(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): Promise<V | null>;
33
+ set(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, value: V): Promise<void>;
34
+ includesKey(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): Promise<boolean>;
35
+ delete(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): Promise<void>;
36
+ keys(): Promise<(ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]>;
37
+ values(): Promise<V[]>;
38
+ clear(): Promise<void>;
39
+ allIn(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V[]>;
40
+ contains(query: ItemQuery, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<boolean>;
41
+ queryIn(query: ItemQuery, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V[]>;
42
+ setQueryResult(queryHash: string, itemKeys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]): Promise<void>;
43
+ getQueryResult(queryHash: string): Promise<(ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[] | null>;
44
+ hasQueryResult(queryHash: string): Promise<boolean>;
45
+ deleteQueryResult(queryHash: string): Promise<void>;
46
+ clearQueryResults(): Promise<void>;
47
+ invalidateItemKeys(keys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]): Promise<void>;
48
+ invalidateLocation(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<void>;
49
+ getMetadata(key: string): Promise<CacheItemMetadata | null>;
50
+ setMetadata(key: string, metadata: CacheItemMetadata): Promise<void>;
51
+ deleteMetadata(key: string): Promise<void>;
52
+ getAllMetadata(): Promise<Map<string, CacheItemMetadata>>;
53
+ clearMetadata(): Promise<void>;
54
+ getCurrentSize(): Promise<{
55
+ itemCount: number;
56
+ sizeBytes: number;
57
+ }>;
58
+ getSizeLimits(): Promise<{
59
+ maxItems: number | null;
60
+ maxSizeBytes: number | null;
61
+ }>;
62
+ clone(): Promise<CacheMap<V, S, L1, L2, L3, L4, L5>>;
50
63
  /**
51
64
  * Clean up resources when the cache is no longer needed
52
65
  */
@@ -1,43 +1,59 @@
1
1
  import { AllItemTypeArrays, ComKey, Item, ItemQuery, LocKeyArray, PriKey } from "@fjell/core";
2
2
  import { CacheMap } from "../CacheMap";
3
+ import { CacheItemMetadata } from "../eviction/EvictionStrategy";
3
4
  /**
4
5
  * LocalStorage implementation of CacheMap for browser environments.
5
6
  * Data persists across browser sessions and page reloads.
6
7
  *
7
8
  * Note: LocalStorage has a ~5-10MB limit and stores strings only.
8
9
  * Data is synchronous and survives browser restarts.
10
+ * Will throw errors if storage quota is exceeded, though it attempts
11
+ * to clean up old entries first.
9
12
  */
10
13
  export declare class LocalStorageCacheMap<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> extends CacheMap<V, S, L1, L2, L3, L4, L5> {
14
+ readonly implementationType = "browser/localStorage";
11
15
  private keyPrefix;
12
16
  private normalizedHashFunction;
13
- private fallbackCache;
14
- private quotaExceeded;
17
+ private readonly MAX_RETRY_ATTEMPTS;
18
+ private readonly AGGRESSIVE_CLEANUP_PERCENTAGE;
15
19
  constructor(types: AllItemTypeArrays<S, L1, L2, L3, L4, L5>, keyPrefix?: string);
16
20
  private getStorageKey;
17
- private initializeFallbackCache;
18
21
  private isQuotaExceededError;
22
+ private getAllKeysStartingWith;
19
23
  private tryCleanupOldEntries;
20
24
  private collectCacheEntries;
21
25
  private removeOldestEntries;
22
26
  private getAllStorageKeys;
23
- get(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): V | null;
24
- getWithTTL(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, ttl: number): V | null;
25
- set(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, value: V): void;
26
- includesKey(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): boolean;
27
- delete(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): void;
28
- allIn(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): V[];
29
- contains(query: ItemQuery, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): boolean;
30
- queryIn(query: ItemQuery, locations?: LocKeyArray<L1, L2, L3, L4, L5> | []): V[];
31
- clone(): LocalStorageCacheMap<V, S, L1, L2, L3, L4, L5>;
27
+ get(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): Promise<V | null>;
28
+ set(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, value: V): Promise<void>;
29
+ includesKey(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): Promise<boolean>;
30
+ delete(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): Promise<void>;
31
+ allIn(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V[]>;
32
+ contains(query: ItemQuery, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<boolean>;
33
+ queryIn(query: ItemQuery, locations?: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V[]>;
34
+ clone(): Promise<LocalStorageCacheMap<V, S, L1, L2, L3, L4, L5>>;
32
35
  private parseStorageEntry;
33
- keys(): (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[];
34
- values(): V[];
35
- clear(): void;
36
- setQueryResult(queryHash: string, itemKeys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[], ttl?: number): void;
37
- getQueryResult(queryHash: string): (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[] | null;
38
- hasQueryResult(queryHash: string): boolean;
39
- deleteQueryResult(queryHash: string): void;
40
- invalidateItemKeys(keys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]): void;
41
- invalidateLocation(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): void;
42
- clearQueryResults(): void;
36
+ keys(): Promise<(ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]>;
37
+ values(): Promise<V[]>;
38
+ clear(): Promise<void>;
39
+ setQueryResult(queryHash: string, itemKeys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]): Promise<void>;
40
+ getQueryResult(queryHash: string): Promise<(ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[] | null>;
41
+ hasQueryResult(queryHash: string): Promise<boolean>;
42
+ deleteQueryResult(queryHash: string): Promise<void>;
43
+ invalidateItemKeys(keys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]): Promise<void>;
44
+ invalidateLocation(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<void>;
45
+ clearQueryResults(): Promise<void>;
46
+ getMetadata(key: string): Promise<CacheItemMetadata | null>;
47
+ setMetadata(key: string, metadata: CacheItemMetadata): Promise<void>;
48
+ deleteMetadata(key: string): Promise<void>;
49
+ getAllMetadata(): Promise<Map<string, CacheItemMetadata>>;
50
+ clearMetadata(): Promise<void>;
51
+ getCurrentSize(): Promise<{
52
+ itemCount: number;
53
+ sizeBytes: number;
54
+ }>;
55
+ getSizeLimits(): Promise<{
56
+ maxItems: number | null;
57
+ maxSizeBytes: number | null;
58
+ }>;
43
59
  }
@@ -1,5 +1,6 @@
1
1
  import { AllItemTypeArrays, ComKey, Item, ItemQuery, LocKeyArray, PriKey } from "@fjell/core";
2
2
  import { CacheMap } from "../CacheMap";
3
+ import { CacheItemMetadata } from "../eviction/EvictionStrategy";
3
4
  /**
4
5
  * SessionStorage implementation of CacheMap for browser environments.
5
6
  * Data persists only for the current browser tab/session.
@@ -8,28 +9,43 @@ import { CacheMap } from "../CacheMap";
8
9
  * Data is synchronous but is lost when the tab is closed.
9
10
  */
10
11
  export declare class SessionStorageCacheMap<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> extends CacheMap<V, S, L1, L2, L3, L4, L5> {
12
+ readonly implementationType = "browser/sessionStorage";
11
13
  private keyPrefix;
12
14
  private normalizedHashFunction;
15
+ private readonly verificationHashFunction;
13
16
  constructor(types: AllItemTypeArrays<S, L1, L2, L3, L4, L5>, keyPrefix?: string);
14
17
  private getStorageKey;
15
18
  private getAllStorageKeys;
16
- get(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): V | null;
17
- getWithTTL(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, ttl: number): V | null;
18
- set(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, value: V): void;
19
- includesKey(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): boolean;
20
- delete(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): void;
21
- allIn(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): V[];
22
- contains(query: ItemQuery, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): boolean;
23
- queryIn(query: ItemQuery, locations?: LocKeyArray<L1, L2, L3, L4, L5> | []): V[];
24
- clone(): SessionStorageCacheMap<V, S, L1, L2, L3, L4, L5>;
25
- keys(): (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[];
26
- values(): V[];
27
- clear(): void;
28
- setQueryResult(queryHash: string, itemKeys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[], ttl?: number): void;
29
- getQueryResult(queryHash: string): (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[] | null;
30
- hasQueryResult(queryHash: string): boolean;
31
- deleteQueryResult(queryHash: string): void;
32
- invalidateItemKeys(keys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]): void;
33
- invalidateLocation(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): void;
34
- clearQueryResults(): void;
19
+ private hasCollisionForHash;
20
+ get(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): Promise<V | null>;
21
+ set(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, value: V): Promise<void>;
22
+ includesKey(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): Promise<boolean>;
23
+ delete(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>): Promise<void>;
24
+ allIn(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V[]>;
25
+ contains(query: ItemQuery, locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<boolean>;
26
+ queryIn(query: ItemQuery, locations?: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<V[]>;
27
+ clone(): Promise<SessionStorageCacheMap<V, S, L1, L2, L3, L4, L5>>;
28
+ keys(): Promise<(ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]>;
29
+ values(): Promise<V[]>;
30
+ clear(): Promise<void>;
31
+ setQueryResult(queryHash: string, itemKeys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]): Promise<void>;
32
+ getQueryResult(queryHash: string): Promise<(ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[] | null>;
33
+ hasQueryResult(queryHash: string): Promise<boolean>;
34
+ deleteQueryResult(queryHash: string): Promise<void>;
35
+ invalidateItemKeys(keys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[]): Promise<void>;
36
+ invalidateLocation(locations: LocKeyArray<L1, L2, L3, L4, L5> | []): Promise<void>;
37
+ clearQueryResults(): Promise<void>;
38
+ getMetadata(key: string): Promise<CacheItemMetadata | null>;
39
+ setMetadata(key: string, metadata: CacheItemMetadata): Promise<void>;
40
+ deleteMetadata(key: string): Promise<void>;
41
+ getAllMetadata(): Promise<Map<string, CacheItemMetadata>>;
42
+ clearMetadata(): Promise<void>;
43
+ getCurrentSize(): Promise<{
44
+ itemCount: number;
45
+ sizeBytes: number;
46
+ }>;
47
+ getSizeLimits(): Promise<{
48
+ maxItems: number | null;
49
+ maxSizeBytes: number | null;
50
+ }>;
35
51
  }
@@ -0,0 +1,83 @@
1
+ import { Item } from "@fjell/core";
2
+ import { AnyCacheEvent, CacheEventListener, CacheSubscription, CacheSubscriptionOptions } from "./CacheEventTypes";
3
+ /**
4
+ * Cache event emitter that manages subscriptions and event dispatching
5
+ */
6
+ export declare class CacheEventEmitter<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> {
7
+ private subscriptions;
8
+ private nextSubscriptionId;
9
+ private isDestroyed;
10
+ private cleanupInterval;
11
+ private readonly CLEANUP_INTERVAL_MS;
12
+ private readonly MAX_INACTIVE_TIME_MS;
13
+ private readonly WEAK_REF_ENABLED;
14
+ constructor();
15
+ /**
16
+ * Start periodic cleanup of inactive subscriptions
17
+ */
18
+ private startPeriodicCleanup;
19
+ /**
20
+ * Perform periodic cleanup of inactive subscriptions
21
+ */
22
+ private performPeriodicCleanup;
23
+ /**
24
+ * Subscribe to cache events
25
+ */
26
+ subscribe(listener: CacheEventListener<V, S, L1, L2, L3, L4, L5>, options?: CacheSubscriptionOptions<S, L1, L2, L3, L4, L5>): CacheSubscription;
27
+ /**
28
+ * Unsubscribe from events
29
+ */
30
+ unsubscribe(subscriptionId: string): boolean;
31
+ /**
32
+ * Emit an event to all matching subscriptions
33
+ */
34
+ emit(event: AnyCacheEvent<V, S, L1, L2, L3, L4, L5>): void;
35
+ /**
36
+ * Get count of active subscriptions
37
+ */
38
+ getSubscriptionCount(): number;
39
+ /**
40
+ * Get subscription details (for debugging)
41
+ */
42
+ getSubscriptions(): Array<{
43
+ id: string;
44
+ options: CacheSubscriptionOptions<S, L1, L2, L3, L4, L5>;
45
+ }>;
46
+ /**
47
+ * Destroy the event emitter and clean up all subscriptions
48
+ */
49
+ destroy(): void;
50
+ /**
51
+ * Check if an event should be emitted to a specific subscription
52
+ */
53
+ private shouldEmitToSubscription;
54
+ /**
55
+ * Emit event to a specific subscription, handling debouncing
56
+ */
57
+ private emitToSubscription;
58
+ /**
59
+ * Normalize a key for comparison
60
+ */
61
+ private normalizeKey;
62
+ /**
63
+ * Normalize a location key for comparison
64
+ */
65
+ private normalizeLocKey;
66
+ /**
67
+ * Check if two location arrays match
68
+ * Filter locations must be a prefix of event locations (in order)
69
+ */
70
+ private locationsMatch;
71
+ /**
72
+ * Check if a key matches location filters
73
+ */
74
+ private keyMatchesLocations;
75
+ /**
76
+ * Check if two queries match (improved comparison)
77
+ */
78
+ private queriesMatch;
79
+ /**
80
+ * Handle errors that occur in event listeners
81
+ */
82
+ private handleListenerError;
83
+ }
@@ -0,0 +1,121 @@
1
+ import { ComKey, Item, ItemQuery, LocKeyArray, PriKey } from "@fjell/core";
2
+ import { CacheClearedEvent, ItemEvent, LocationInvalidatedEvent, QueryEvent, QueryInvalidatedEvent } from "./CacheEventTypes";
3
+ /**
4
+ * Factory functions for creating cache events
5
+ */
6
+ export declare class CacheEventFactory {
7
+ private static lastTimestamp;
8
+ private static cleanupInterval;
9
+ private static instanceCount;
10
+ private static readonly CLEANUP_INTERVAL_MS;
11
+ private static readonly MAX_TIMESTAMP_AGE_MS;
12
+ /**
13
+ * Initialize cleanup mechanism when first instance is created
14
+ */
15
+ private static initializeCleanup;
16
+ /**
17
+ * Cleanup mechanism when instance is destroyed
18
+ */
19
+ static destroyInstance(): void;
20
+ /**
21
+ * Start automatic cleanup timer
22
+ */
23
+ private static startCleanupTimer;
24
+ /**
25
+ * Stop automatic cleanup timer
26
+ */
27
+ private static stopCleanupTimer;
28
+ /**
29
+ * Perform periodic cleanup of stale timestamp state
30
+ */
31
+ private static performCleanup;
32
+ /**
33
+ * Reset the timestamp state (useful for testing)
34
+ */
35
+ static resetTimestamp(): void;
36
+ /**
37
+ * Generate a unique timestamp that is always greater than the previous one
38
+ */
39
+ private static generateTimestamp;
40
+ /**
41
+ * Extract affected locations from an item key
42
+ */
43
+ private static extractAffectedLocations;
44
+ /**
45
+ * Create an item-related event
46
+ */
47
+ static createItemEvent<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(type: 'item_created' | 'item_updated' | 'item_removed' | 'item_retrieved' | 'item_set', key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, item: V | null, options?: {
48
+ previousItem?: V | null | null;
49
+ source?: 'api' | 'cache' | 'operation';
50
+ affectedLocations?: LocKeyArray<L1, L2, L3, L4, L5> | [];
51
+ context?: {
52
+ operation?: string;
53
+ requestId?: string;
54
+ userId?: string;
55
+ };
56
+ }): ItemEvent<V, S, L1, L2, L3, L4, L5>;
57
+ /**
58
+ * Create a query event
59
+ */
60
+ static createQueryEvent<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(query: ItemQuery, locations: LocKeyArray<L1, L2, L3, L4, L5> | [], items: V[], options?: {
61
+ source?: 'api' | 'cache' | 'operation';
62
+ context?: {
63
+ operation?: string;
64
+ requestId?: string;
65
+ userId?: string;
66
+ };
67
+ }): QueryEvent<V, S, L1, L2, L3, L4, L5>;
68
+ /**
69
+ * Create a cache cleared event
70
+ */
71
+ static createCacheClearedEvent(itemsCleared: number, queryCacheCleared?: boolean, options?: {
72
+ source?: 'api' | 'cache' | 'operation';
73
+ context?: {
74
+ operation?: string;
75
+ requestId?: string;
76
+ userId?: string;
77
+ };
78
+ }): CacheClearedEvent;
79
+ /**
80
+ * Create a location invalidated event
81
+ */
82
+ static createLocationInvalidatedEvent<S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(locations: LocKeyArray<L1, L2, L3, L4, L5> | [], affectedKeys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[], options?: {
83
+ source?: 'api' | 'cache' | 'operation';
84
+ context?: {
85
+ operation?: string;
86
+ requestId?: string;
87
+ userId?: string;
88
+ };
89
+ }): LocationInvalidatedEvent<S, L1, L2, L3, L4, L5>;
90
+ /**
91
+ * Create a query invalidated event
92
+ */
93
+ static createQueryInvalidatedEvent(invalidatedQueries: string[], reason: 'manual' | 'item_changed' | 'location_changed' | 'ttl_expired', options?: {
94
+ source?: 'api' | 'cache' | 'operation';
95
+ context?: {
96
+ operation?: string;
97
+ requestId?: string;
98
+ userId?: string;
99
+ };
100
+ }): QueryInvalidatedEvent;
101
+ /**
102
+ * Create an item created event
103
+ */
104
+ static itemCreated<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, item: V, source?: 'api' | 'cache' | 'operation'): ItemEvent<V, S, L1, L2, L3, L4, L5>;
105
+ /**
106
+ * Create an item updated event
107
+ */
108
+ static itemUpdated<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, item: V, previousItem?: V | null, source?: 'api' | 'cache' | 'operation'): ItemEvent<V, S, L1, L2, L3, L4, L5>;
109
+ /**
110
+ * Create an item removed event
111
+ */
112
+ static itemRemoved<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, previousItem?: V | null, source?: 'api' | 'cache' | 'operation'): ItemEvent<V, S, L1, L2, L3, L4, L5>;
113
+ /**
114
+ * Create an item retrieved event
115
+ */
116
+ static itemRetrieved<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, item: V, source?: 'api' | 'cache' | 'operation'): ItemEvent<V, S, L1, L2, L3, L4, L5>;
117
+ /**
118
+ * Create an item set event (direct cache operation)
119
+ */
120
+ static itemSet<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>, item: V, previousItem?: V | null): ItemEvent<V, S, L1, L2, L3, L4, L5>;
121
+ }
@@ -0,0 +1,122 @@
1
+ import { ComKey, Item, ItemQuery, LocKeyArray, PriKey } from "@fjell/core";
2
+ /**
3
+ * Types of events that can be emitted by the cache system
4
+ */
5
+ export type CacheEventType = 'item_created' | 'item_updated' | 'item_removed' | 'item_retrieved' | 'item_set' | 'items_queried' | 'cache_cleared' | 'location_invalidated' | 'query_invalidated';
6
+ /**
7
+ * Base interface for all cache events
8
+ */
9
+ export interface CacheEvent {
10
+ /** Type of the event */
11
+ type: CacheEventType;
12
+ /** Timestamp when the event occurred */
13
+ timestamp: number;
14
+ /** Source of the event */
15
+ source: 'api' | 'cache' | 'operation';
16
+ /** Optional context about what triggered this event */
17
+ context?: {
18
+ operation?: string;
19
+ requestId?: string;
20
+ userId?: string;
21
+ };
22
+ }
23
+ /**
24
+ * Event emitted when a single item is affected
25
+ */
26
+ export interface ItemEvent<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> extends CacheEvent {
27
+ type: 'item_created' | 'item_updated' | 'item_removed' | 'item_retrieved' | 'item_set';
28
+ /** The key of the affected item */
29
+ key: ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>;
30
+ /** The current item (null for 'item_removed') */
31
+ item: V | null;
32
+ /** The previous item before the change (for updates/removals) */
33
+ previousItem?: V | null;
34
+ /** Locations affected by this change */
35
+ affectedLocations?: LocKeyArray<L1, L2, L3, L4, L5> | [];
36
+ }
37
+ /**
38
+ * Event emitted when multiple items are affected by a query
39
+ */
40
+ export interface QueryEvent<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> extends CacheEvent {
41
+ type: 'items_queried';
42
+ /** The query that was executed */
43
+ query: ItemQuery;
44
+ /** Locations where the query was executed */
45
+ locations: LocKeyArray<L1, L2, L3, L4, L5> | [];
46
+ /** Items returned by the query */
47
+ items: V[];
48
+ /** Keys of all items affected by this query */
49
+ affectedKeys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[];
50
+ }
51
+ /**
52
+ * Event emitted when the entire cache is cleared
53
+ */
54
+ export interface CacheClearedEvent extends CacheEvent {
55
+ type: 'cache_cleared';
56
+ /** Number of items that were cleared */
57
+ itemsCleared: number;
58
+ /** Whether query cache was also cleared */
59
+ queryCacheCleared: boolean;
60
+ }
61
+ /**
62
+ * Event emitted when specific locations are invalidated
63
+ */
64
+ export interface LocationInvalidatedEvent<S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> extends CacheEvent {
65
+ type: 'location_invalidated';
66
+ /** Locations that were invalidated */
67
+ locations: LocKeyArray<L1, L2, L3, L4, L5> | [];
68
+ /** Keys of items that were affected by the invalidation */
69
+ affectedKeys: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[];
70
+ }
71
+ /**
72
+ * Event emitted when cached query results are invalidated
73
+ */
74
+ export interface QueryInvalidatedEvent extends CacheEvent {
75
+ type: 'query_invalidated';
76
+ /** Queries that were invalidated (query hashes) */
77
+ invalidatedQueries: string[];
78
+ /** Reason for invalidation */
79
+ reason: 'manual' | 'item_changed' | 'location_changed' | 'ttl_expired';
80
+ }
81
+ /**
82
+ * Union type of all possible cache events
83
+ */
84
+ export type AnyCacheEvent<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> = ItemEvent<V, S, L1, L2, L3, L4, L5> | QueryEvent<V, S, L1, L2, L3, L4, L5> | CacheClearedEvent | LocationInvalidatedEvent<S, L1, L2, L3, L4, L5> | QueryInvalidatedEvent;
85
+ /**
86
+ * Function type for cache event listeners
87
+ */
88
+ export type CacheEventListener<V extends Item<S, L1, L2, L3, L4, L5>, S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> = (event: AnyCacheEvent<V, S, L1, L2, L3, L4, L5>) => void;
89
+ /**
90
+ * Options for subscribing to cache events
91
+ */
92
+ export interface CacheSubscriptionOptions<S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never> {
93
+ /** Only emit events for specific keys */
94
+ keys?: (ComKey<S, L1, L2, L3, L4, L5> | PriKey<S>)[];
95
+ /** Only emit events for specific locations */
96
+ locations?: LocKeyArray<L1, L2, L3, L4, L5> | [];
97
+ /** Only emit events matching this query */
98
+ query?: ItemQuery;
99
+ /** Filter by event types */
100
+ eventTypes?: CacheEventType[];
101
+ /** Debounce events by this many milliseconds */
102
+ debounceMs?: number;
103
+ /** Whether to emit events for items that match the subscription criteria */
104
+ includeExistingItems?: boolean;
105
+ /** Optional error handler for listener errors */
106
+ onError?: (error: Error, event: any) => void;
107
+ /** Use weak references for the listener (default: true if WeakRef is available) */
108
+ useWeakRef?: boolean;
109
+ }
110
+ /**
111
+ * Represents an active subscription to cache events
112
+ */
113
+ export interface CacheSubscription {
114
+ /** Unique identifier for this subscription */
115
+ id: string;
116
+ /** Unsubscribe from events */
117
+ unsubscribe: () => void;
118
+ /** Check if this subscription is still active */
119
+ isActive: () => boolean;
120
+ /** Get subscription options */
121
+ getOptions: () => CacheSubscriptionOptions<any, any, any, any, any, any>;
122
+ }
@@ -0,0 +1,3 @@
1
+ export * from './CacheEventTypes';
2
+ export * from './CacheEventEmitter';
3
+ export * from './CacheEventFactory';
@@ -0,0 +1,57 @@
1
+ import { CacheMapMetadataProvider, EvictionStrategy } from './EvictionStrategy';
2
+ /**
3
+ * Manages eviction logic independently of CacheMap implementations.
4
+ * This class coordinates between eviction strategies and cache metadata.
5
+ */
6
+ export declare class EvictionManager {
7
+ private evictionStrategy;
8
+ constructor(evictionStrategy?: EvictionStrategy);
9
+ /**
10
+ * Set or update the eviction strategy
11
+ * @param strategy - The eviction strategy to use
12
+ */
13
+ setEvictionStrategy(strategy: EvictionStrategy | null): void;
14
+ /**
15
+ * Get the current eviction strategy name
16
+ * @returns Strategy name or null if no eviction
17
+ */
18
+ getEvictionStrategyName(): string | null;
19
+ /**
20
+ * Handle item access - update metadata for eviction strategy
21
+ * @param key - Item key
22
+ * @param metadataProvider - Cache metadata provider
23
+ */
24
+ onItemAccessed(key: string, metadataProvider: CacheMapMetadataProvider): Promise<void>;
25
+ /**
26
+ * Handle item addition - update metadata and perform eviction if needed
27
+ * @param key - Item key
28
+ * @param value - Item value (for size estimation)
29
+ * @param metadataProvider - Cache metadata provider
30
+ * @returns Array of keys that were evicted
31
+ */
32
+ onItemAdded<T>(key: string, value: T, metadataProvider: CacheMapMetadataProvider): Promise<string[]>;
33
+ /**
34
+ * Handle item removal - clean up metadata
35
+ * @param key - Item key
36
+ * @param metadataProvider - Cache metadata provider
37
+ */
38
+ onItemRemoved(key: string, metadataProvider: CacheMapMetadataProvider): void;
39
+ /**
40
+ * Perform manual eviction check
41
+ * @param metadataProvider - Cache metadata provider
42
+ * @returns Array of keys that were evicted
43
+ */
44
+ performEviction(metadataProvider: CacheMapMetadataProvider): Promise<string[]>;
45
+ /**
46
+ * Check if eviction is supported (i.e., strategy is set)
47
+ * @returns True if eviction is supported
48
+ */
49
+ isEvictionSupported(): boolean;
50
+ /**
51
+ * Create eviction context from current cache state
52
+ * @param metadataProvider - Cache metadata provider
53
+ * @param newItemSize - Size of item being added (optional)
54
+ * @returns Eviction context
55
+ */
56
+ private createEvictionContext;
57
+ }