@fjell/cache 4.7.40 → 4.7.41

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.
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Stale-While-Revalidate Cache Pattern
3
+ *
4
+ * Returns cached data immediately (even if stale) while fetching fresh data
5
+ * in the background. This keeps the UI responsive while ensuring eventual
6
+ * data freshness.
7
+ */
8
+ import { TTLCalculator } from '../../ttl/TTLCalculator.js';
9
+ import { ItemCache } from '../layers/ItemCache.js';
10
+ import { CachedItem } from '../types/TwoLayerTypes.js';
11
+ export interface StaleWhileRevalidateOptions {
12
+ /** TTL calculator for determining staleness */
13
+ ttlCalculator: TTLCalculator;
14
+ /** Maximum number of concurrent background refreshes */
15
+ maxConcurrentRefreshes?: number;
16
+ /** How long to wait before giving up on a refresh (milliseconds) */
17
+ refreshTimeout?: number;
18
+ /** Whether to extend TTL on refresh failures */
19
+ extendTTLOnError?: boolean;
20
+ /** Error TTL extension duration (seconds) */
21
+ errorTTLExtension?: number;
22
+ }
23
+ export interface RefreshContext<T> {
24
+ /** The cached item being refreshed */
25
+ cachedItem: CachedItem<T>;
26
+ /** Original TTL for this item */
27
+ originalTTL: number;
28
+ /** When the refresh started */
29
+ refreshStartedAt: Date;
30
+ }
31
+ /**
32
+ * Cache that implements the stale-while-revalidate pattern
33
+ */
34
+ export declare class StaleWhileRevalidateCache<T> {
35
+ private itemCache;
36
+ private pendingRefreshes;
37
+ private refreshContexts;
38
+ private options;
39
+ constructor(itemCache: ItemCache<T>, options: StaleWhileRevalidateOptions);
40
+ /**
41
+ * Get item from cache with stale-while-revalidate behavior
42
+ */
43
+ get(key: string, fetcher: () => Promise<T | null>, ttl: number, itemType?: string): Promise<T | null>;
44
+ /**
45
+ * Force refresh of cached item
46
+ */
47
+ refresh(key: string, fetcher: () => Promise<T | null>, ttl: number): Promise<T | null>;
48
+ /**
49
+ * Check if key is currently being refreshed
50
+ */
51
+ isRefreshing(key: string): boolean;
52
+ /**
53
+ * Get refresh status for debugging
54
+ */
55
+ getRefreshStatus(): {
56
+ pendingRefreshes: number;
57
+ maxConcurrent: number;
58
+ activeRefreshes: Array<{
59
+ key: string;
60
+ startedAt: Date;
61
+ originalTTL: number;
62
+ }>;
63
+ };
64
+ /**
65
+ * Start background refresh for stale data
66
+ */
67
+ private revalidateInBackground;
68
+ /**
69
+ * Perform the actual background refresh
70
+ */
71
+ private performBackgroundRefresh;
72
+ /**
73
+ * Handle refresh errors
74
+ */
75
+ private handleRefreshError;
76
+ /**
77
+ * Fetch fresh data and cache it
78
+ */
79
+ private fetchAndCache;
80
+ /**
81
+ * Cancel pending refresh for a key
82
+ */
83
+ private cancelRefresh;
84
+ /**
85
+ * Clear all pending refreshes (useful for cleanup)
86
+ */
87
+ clearPendingRefreshes(): void;
88
+ /**
89
+ * Get statistics about cache behavior
90
+ */
91
+ getStats(): {
92
+ pendingRefreshes: number;
93
+ averageRefreshTime?: number;
94
+ refreshSuccessRate?: number;
95
+ };
96
+ }
97
+ /**
98
+ * Factory function to create a StaleWhileRevalidateCache
99
+ */
100
+ export declare function createStaleWhileRevalidateCache<T>(itemCache: ItemCache<T>, options: StaleWhileRevalidateOptions): StaleWhileRevalidateCache<T>;
101
+ //# sourceMappingURL=StaleWhileRevalidateCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StaleWhileRevalidateCache.d.ts","sourceRoot":"","sources":["../../../src/cache/patterns/StaleWhileRevalidateCache.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvD,MAAM,WAAW,2BAA2B;IAC1C,+CAA+C;IAC/C,aAAa,EAAE,aAAa,CAAC;IAC7B,wDAAwD;IACxD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6CAA6C;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,sCAAsC;IACtC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1B,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,gBAAgB,EAAE,IAAI,CAAC;CACxB;AAED;;GAEG;AACH,qBAAa,yBAAyB,CAAC,CAAC;IACtC,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,eAAe,CAAiC;IACxD,OAAO,CAAC,OAAO,CAAwC;gBAGrD,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EACvB,OAAO,EAAE,2BAA2B;IAetC;;OAEG;IACG,GAAG,CACP,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,EAChC,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IA0DpB;;OAEG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAO5F;;OAEG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIlC;;OAEG;IACH,gBAAgB,IAAI;QAClB,gBAAgB,EAAE,MAAM,CAAC;QACzB,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,KAAK,CAAC;YACrB,GAAG,EAAE,MAAM,CAAC;YACZ,SAAS,EAAE,IAAI,CAAC;YAChB,WAAW,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;KACF;IAgBH;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAmC9B;;OAEG;YACW,wBAAwB;IAiCtC;;OAEG;YACW,kBAAkB;IAsBhC;;OAEG;YACW,aAAa;IAoB3B;;OAEG;IACH,OAAO,CAAC,aAAa;IAKrB;;OAEG;IACH,qBAAqB,IAAI,IAAI;IAK7B;;OAEG;IACH,QAAQ,IAAI;QACV,gBAAgB,EAAE,MAAM,CAAC;QACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;KAC3B;CAMJ;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAAC,CAAC,EAC/C,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EACvB,OAAO,EAAE,2BAA2B,GACnC,yBAAyB,CAAC,CAAC,CAAC,CAE9B"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Two-Layer Cache Architecture Types
3
+ * Separates item storage from query result storage to prevent cache poisoning
4
+ */
5
+ export interface ItemCacheLayer<T> {
6
+ get(key: string): Promise<T | null>;
7
+ set(key: string, item: T, ttl?: number): Promise<void>;
8
+ delete(key: string): Promise<void>;
9
+ has(key: string): Promise<boolean>;
10
+ clear(): Promise<void>;
11
+ }
12
+ export interface CachedItem<T> {
13
+ data: T;
14
+ createdAt: Date;
15
+ expiresAt: Date;
16
+ }
17
+ export interface QueryCacheLayer {
18
+ getResult(queryKey: string): Promise<QueryResult | null>;
19
+ setResult(queryKey: string, result: QueryResult): Promise<void>;
20
+ invalidatePattern(pattern: string): Promise<void>;
21
+ clear(): Promise<void>;
22
+ }
23
+ export interface QueryResult {
24
+ itemKeys: string[];
25
+ metadata: QueryMetadata;
26
+ }
27
+ export interface QueryMetadata {
28
+ queryType: string;
29
+ isComplete: boolean;
30
+ createdAt: Date;
31
+ expiresAt: Date;
32
+ filter?: string;
33
+ params?: any;
34
+ ttl?: number;
35
+ baseTTL?: number;
36
+ adjustments?: {
37
+ peakHours?: {
38
+ applied: boolean;
39
+ multiplier: number;
40
+ };
41
+ };
42
+ }
43
+ export interface TwoLayerCacheOptions {
44
+ itemTTL?: number;
45
+ queryTTL?: number;
46
+ facetTTL?: number;
47
+ debug?: boolean;
48
+ }
49
+ export interface CacheKeyBuilder {
50
+ buildItemKey<T>(item: T): string;
51
+ buildQueryKey(queryType: string, params?: any): string;
52
+ }
53
+ //# sourceMappingURL=TwoLayerTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TwoLayerTypes.d.ts","sourceRoot":"","sources":["../../../src/cache/types/TwoLayerTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACpC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC;IACR,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAID,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACzD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,GAAG,CAAC;IAGb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE;QACZ,SAAS,CAAC,EAAE;YACV,OAAO,EAAE,OAAO,CAAC;YACjB,UAAU,EAAE,MAAM,CAAC;SACpB,CAAC;KACH,CAAC;CACH;AAID,MAAM,WAAW,oBAAoB;IAEnC,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAID,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC;IACjC,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC;CACxD"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Cache Warming System
3
+ *
4
+ * Proactively populates cache with commonly accessed data to improve hit rates.
5
+ * Supports periodic warming, priority-based execution, and failure handling.
6
+ */
7
+ import { WarmingQuery } from '../../ttl/TTLConfig.js';
8
+ export interface CacheWarmerOptions {
9
+ /** Interval between warming cycles (milliseconds) */
10
+ interval: number;
11
+ /** Maximum number of concurrent warming operations */
12
+ maxConcurrency?: number;
13
+ /** How long to wait for a warming operation before timing out (milliseconds) */
14
+ operationTimeout?: number;
15
+ /** Whether to continue warming on individual failures */
16
+ continueOnError?: boolean;
17
+ /** Debug logging */
18
+ debug?: boolean;
19
+ }
20
+ export interface WarmingOperation<T> {
21
+ /** Unique identifier for this warming operation */
22
+ id: string;
23
+ /** Parameters for the operation */
24
+ params: any;
25
+ /** Priority (1-10, higher = more important) */
26
+ priority: number;
27
+ /** Function to fetch the data */
28
+ fetcher: () => Promise<T[]>;
29
+ /** TTL multiplier for warmed data (optional) */
30
+ ttlMultiplier?: number;
31
+ }
32
+ export interface WarmingResult {
33
+ /** Operation ID */
34
+ operationId: string;
35
+ /** Whether the operation succeeded */
36
+ success: boolean;
37
+ /** Number of items warmed */
38
+ itemsWarmed: number;
39
+ /** Time taken in milliseconds */
40
+ duration: number;
41
+ /** Error if failed */
42
+ error?: string;
43
+ }
44
+ export interface WarmingStats {
45
+ /** Total warming cycles completed */
46
+ totalCycles: number;
47
+ /** Total operations attempted */
48
+ totalOperations: number;
49
+ /** Total successful operations */
50
+ successfulOperations: number;
51
+ /** Total items warmed */
52
+ totalItemsWarmed: number;
53
+ /** Average items per operation */
54
+ averageItemsPerOperation: number;
55
+ /** Success rate (0-1) */
56
+ successRate: number;
57
+ /** Currently running operations */
58
+ activeOperations: number;
59
+ /** Last warming cycle timestamp */
60
+ lastWarmingAt?: Date;
61
+ /** Next warming cycle timestamp */
62
+ nextWarmingAt?: Date;
63
+ }
64
+ /**
65
+ * Cache warming system that proactively populates cache with common queries
66
+ */
67
+ export declare class CacheWarmer<T> {
68
+ private options;
69
+ private operations;
70
+ private activeOperations;
71
+ private intervalId?;
72
+ private stats;
73
+ constructor(options: CacheWarmerOptions);
74
+ /**
75
+ * Add warming operations from configuration
76
+ */
77
+ addOperationsFromConfig(queries: WarmingQuery[], fetcherFactory: (params: any) => () => Promise<T[]>): void;
78
+ /**
79
+ * Add a single warming operation
80
+ */
81
+ addOperation(operation: WarmingOperation<T>): void;
82
+ /**
83
+ * Remove a warming operation
84
+ */
85
+ removeOperation(operationId: string): boolean;
86
+ /**
87
+ * Start periodic warming
88
+ */
89
+ startPeriodicWarming(): void;
90
+ /**
91
+ * Stop periodic warming
92
+ */
93
+ stopPeriodicWarming(): void;
94
+ /**
95
+ * Perform a single warming cycle
96
+ */
97
+ performWarmingCycle(): Promise<WarmingResult[]>;
98
+ /**
99
+ * Perform a single warming operation
100
+ */
101
+ private performWarmingOperation;
102
+ /**
103
+ * Execute the actual operation with timeout handling
104
+ */
105
+ private executeOperation;
106
+ /**
107
+ * Warm specific operations immediately
108
+ */
109
+ warmOperations(operationIds: string[]): Promise<WarmingResult[]>;
110
+ /**
111
+ * Get current warming statistics
112
+ */
113
+ getStats(): WarmingStats;
114
+ /**
115
+ * Get list of configured operations
116
+ */
117
+ getOperations(): Array<{
118
+ id: string;
119
+ priority: number;
120
+ params: any;
121
+ }>;
122
+ /**
123
+ * Check if warming is currently active
124
+ */
125
+ isActive(): boolean;
126
+ /**
127
+ * Clear all statistics
128
+ */
129
+ resetStats(): void;
130
+ /**
131
+ * Update internal statistics
132
+ */
133
+ private updateStats;
134
+ /**
135
+ * Generate a unique operation ID from parameters
136
+ */
137
+ private generateOperationId;
138
+ /**
139
+ * Cleanup - stop warming and clear operations
140
+ */
141
+ cleanup(): void;
142
+ }
143
+ /**
144
+ * Factory function to create a CacheWarmer
145
+ */
146
+ export declare function createCacheWarmer<T>(options: CacheWarmerOptions): CacheWarmer<T>;
147
+ //# sourceMappingURL=CacheWarmer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CacheWarmer.d.ts","sourceRoot":"","sources":["../../../src/cache/warming/CacheWarmer.ts"],"names":[],"mappings":"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"}