@gravito/stasis 3.1.0 → 3.2.0
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/README.md +64 -80
- package/README.zh-TW.md +40 -79
- package/dist/CacheManager.d.ts +484 -0
- package/dist/CacheRepository.d.ts +495 -0
- package/dist/RateLimiter.d.ts +152 -0
- package/dist/cache-events.d.ts +58 -0
- package/dist/index.cjs +373 -1762
- package/dist/index.cjs.map +27 -0
- package/dist/index.d.ts +29 -2676
- package/dist/index.js +356 -1709
- package/dist/index.js.map +27 -0
- package/dist/locks.d.ts +193 -0
- package/dist/prediction/AccessPredictor.d.ts +64 -0
- package/dist/store.d.ts +200 -0
- package/dist/stores/CircuitBreakerStore.d.ts +78 -0
- package/dist/stores/FileStore.d.ts +36 -0
- package/dist/stores/MemoryStore.d.ts +261 -0
- package/dist/stores/NullStore.d.ts +115 -0
- package/dist/stores/PredictiveStore.d.ts +40 -0
- package/dist/stores/RedisStore.d.ts +83 -0
- package/dist/stores/TieredStore.d.ts +37 -0
- package/dist/tagged-store.d.ts +29 -0
- package/dist/types.d.ts +149 -0
- package/dist/utils/LRUCache.d.ts +104 -0
- package/package.json +8 -6
- package/dist/index.d.cts +0 -2989
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import { type CacheEventMode, type CacheEvents } from './cache-events';
|
|
2
|
+
import type { CacheStore } from './store';
|
|
3
|
+
import { type CacheKey, type CacheTtl, type CompressionOptions } from './types';
|
|
4
|
+
export type { CacheEventMode, CacheEvents };
|
|
5
|
+
/**
|
|
6
|
+
* Options for configuring the `CacheRepository`.
|
|
7
|
+
*
|
|
8
|
+
* Controls behavior such as key prefixing, event emission, and background
|
|
9
|
+
* refresh strategies for flexible caching.
|
|
10
|
+
*
|
|
11
|
+
* @public
|
|
12
|
+
* @since 3.0.0
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const options: CacheRepositoryOptions = {
|
|
17
|
+
* prefix: 'v1:',
|
|
18
|
+
* defaultTtl: 3600,
|
|
19
|
+
* eventsMode: 'async'
|
|
20
|
+
* };
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export type CacheRepositoryOptions = {
|
|
24
|
+
/** Optional prefix for all cache keys. */
|
|
25
|
+
prefix?: string;
|
|
26
|
+
/** Default time-to-live for cache entries. */
|
|
27
|
+
defaultTtl?: CacheTtl;
|
|
28
|
+
/** Event handlers for cache operations. */
|
|
29
|
+
events?: CacheEvents;
|
|
30
|
+
/** Mode for emitting events (sync, async, or off). */
|
|
31
|
+
eventsMode?: CacheEventMode;
|
|
32
|
+
/** Whether to throw an error if an event handler fails. @defaultValue false */
|
|
33
|
+
throwOnEventError?: boolean;
|
|
34
|
+
/** Callback triggered when an event handler encounters an error. */
|
|
35
|
+
onEventError?: (error: unknown, event: keyof CacheEvents, payload: {
|
|
36
|
+
key?: string;
|
|
37
|
+
}) => void;
|
|
38
|
+
/** Timeout for background flexible refresh in milliseconds. @defaultValue 30000 */
|
|
39
|
+
refreshTimeout?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Maximum number of retries for the background flexible refresh callback.
|
|
42
|
+
* @defaultValue 0
|
|
43
|
+
*/
|
|
44
|
+
maxRetries?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Delay between retries for flexible refresh in milliseconds.
|
|
47
|
+
* @defaultValue 50
|
|
48
|
+
*/
|
|
49
|
+
retryDelay?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Compression settings for cached values.
|
|
52
|
+
*
|
|
53
|
+
* @since 3.1.0
|
|
54
|
+
*/
|
|
55
|
+
compression?: CompressionOptions;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Statistics for flexible cache operations.
|
|
59
|
+
*
|
|
60
|
+
* Tracks the performance and reliability of background refresh operations
|
|
61
|
+
* used in stale-while-revalidate patterns.
|
|
62
|
+
*
|
|
63
|
+
* @public
|
|
64
|
+
* @since 3.0.0
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const stats: FlexibleStats = {
|
|
69
|
+
* refreshCount: 10,
|
|
70
|
+
* refreshFailures: 0,
|
|
71
|
+
* avgRefreshTime: 15.5
|
|
72
|
+
* };
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export type FlexibleStats = {
|
|
76
|
+
/** Total number of successful background refreshes. */
|
|
77
|
+
refreshCount: number;
|
|
78
|
+
/** Total number of background refresh failures (after all retries). */
|
|
79
|
+
refreshFailures: number;
|
|
80
|
+
/** Average time taken for a successful refresh in milliseconds. */
|
|
81
|
+
avgRefreshTime: number;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* High-level API for cache operations.
|
|
85
|
+
*
|
|
86
|
+
* Wraps a low-level `CacheStore` to provide developer-friendly features like
|
|
87
|
+
* key prefixing, event emission, and advanced patterns like `remember` and `flexible`.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const cache = new CacheRepository(redisStore, { prefix: 'app:' });
|
|
92
|
+
* const user = await cache.remember('user:1', 3600, () => fetchUser(1));
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* @public
|
|
96
|
+
* @since 3.0.0
|
|
97
|
+
*/
|
|
98
|
+
export declare class CacheRepository {
|
|
99
|
+
protected readonly store: CacheStore;
|
|
100
|
+
protected readonly options: CacheRepositoryOptions;
|
|
101
|
+
private refreshSemaphore;
|
|
102
|
+
private coalesceSemaphore;
|
|
103
|
+
private flexibleStats;
|
|
104
|
+
private eventEmitterConfig;
|
|
105
|
+
constructor(store: CacheStore, options?: CacheRepositoryOptions);
|
|
106
|
+
/**
|
|
107
|
+
* Retrieve statistics about flexible cache operations.
|
|
108
|
+
*
|
|
109
|
+
* Useful for monitoring the health and performance of background refreshes.
|
|
110
|
+
*
|
|
111
|
+
* @returns Current statistics for background refresh operations.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* const stats = cache.getFlexibleStats();
|
|
116
|
+
* console.log(`Refreshed ${stats.refreshCount} times`);
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
getFlexibleStats(): FlexibleStats;
|
|
120
|
+
private emit;
|
|
121
|
+
protected key(key: CacheKey): string;
|
|
122
|
+
protected flexibleFreshUntilKey(fullKey: string): string;
|
|
123
|
+
protected putMetaKey(metaKey: string, value: unknown, ttl: CacheTtl): Promise<void>;
|
|
124
|
+
protected forgetMetaKey(metaKey: string): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Retrieve an item from the cache by its key.
|
|
127
|
+
*
|
|
128
|
+
* Fetches the value from the underlying store. If not found, returns the
|
|
129
|
+
* provided default value or executes the factory function.
|
|
130
|
+
*
|
|
131
|
+
* @param key - The unique cache key.
|
|
132
|
+
* @param defaultValue - A default value or factory function to use if the key is not found.
|
|
133
|
+
* @returns The cached value, or the default value if not found.
|
|
134
|
+
* @throws {Error} If the underlying store fails to retrieve the value.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const user = await cache.get('user:1', { name: 'Guest' });
|
|
139
|
+
* const settings = await cache.get('settings', () => fetchSettings());
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
get<T = unknown>(key: CacheKey, defaultValue?: T | (() => T | Promise<T>)): Promise<T | null>;
|
|
143
|
+
/**
|
|
144
|
+
* Determine if an item exists in the cache.
|
|
145
|
+
*
|
|
146
|
+
* Checks for the presence of a key without necessarily returning its value.
|
|
147
|
+
*
|
|
148
|
+
* @param key - The cache key.
|
|
149
|
+
* @returns True if the item exists, false otherwise.
|
|
150
|
+
* @throws {Error} If the underlying store fails to check existence.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* if (await cache.has('session:active')) {
|
|
155
|
+
* // ...
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
has(key: CacheKey): Promise<boolean>;
|
|
160
|
+
/**
|
|
161
|
+
* Determine if an item is missing from the cache.
|
|
162
|
+
*
|
|
163
|
+
* Inverse of `has()`, used for cleaner conditional logic.
|
|
164
|
+
*
|
|
165
|
+
* @param key - The cache key.
|
|
166
|
+
* @returns True if the item is missing, false otherwise.
|
|
167
|
+
* @throws {Error} If the underlying store fails to check existence.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```typescript
|
|
171
|
+
* if (await cache.missing('config:loaded')) {
|
|
172
|
+
* await loadConfig();
|
|
173
|
+
* }
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
missing(key: CacheKey): Promise<boolean>;
|
|
177
|
+
/**
|
|
178
|
+
* Store an item in the cache for a specific duration.
|
|
179
|
+
*
|
|
180
|
+
* Persists the value in the underlying store with the given TTL.
|
|
181
|
+
*
|
|
182
|
+
* @param key - Unique cache key.
|
|
183
|
+
* @param value - Value to store.
|
|
184
|
+
* @param ttl - Expiration duration.
|
|
185
|
+
* @throws {Error} If the underlying store fails to persist the value or serialization fails.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* await cache.put('token', 'xyz123', 3600);
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
put(key: CacheKey, value: unknown, ttl: CacheTtl): Promise<void>;
|
|
193
|
+
/**
|
|
194
|
+
* Store an item in the cache for a specific duration.
|
|
195
|
+
*
|
|
196
|
+
* Uses the repository's default TTL if none is provided.
|
|
197
|
+
*
|
|
198
|
+
* @param key - The unique cache key.
|
|
199
|
+
* @param value - The value to store.
|
|
200
|
+
* @param ttl - Optional time-to-live.
|
|
201
|
+
* @throws {Error} If the underlying store fails to persist the value.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* await cache.set('theme', 'dark');
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
set(key: CacheKey, value: unknown, ttl?: CacheTtl): Promise<void>;
|
|
209
|
+
/**
|
|
210
|
+
* Store an item in the cache only if the key does not already exist.
|
|
211
|
+
*
|
|
212
|
+
* Atomic operation to prevent overwriting existing data.
|
|
213
|
+
*
|
|
214
|
+
* @param key - The unique cache key.
|
|
215
|
+
* @param value - The value to store.
|
|
216
|
+
* @param ttl - Optional time-to-live.
|
|
217
|
+
* @returns True if the item was added, false otherwise.
|
|
218
|
+
* @throws {Error} If the underlying store fails the atomic operation.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```typescript
|
|
222
|
+
* const added = await cache.add('lock:process', true, 60);
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
add(key: CacheKey, value: unknown, ttl?: CacheTtl): Promise<boolean>;
|
|
226
|
+
/**
|
|
227
|
+
* Store an item in the cache indefinitely.
|
|
228
|
+
*
|
|
229
|
+
* Sets the TTL to null, indicating the value should not expire automatically.
|
|
230
|
+
*
|
|
231
|
+
* @param key - The unique cache key.
|
|
232
|
+
* @param value - The value to store.
|
|
233
|
+
* @throws {Error} If the underlying store fails to persist the value.
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```typescript
|
|
237
|
+
* await cache.forever('system:id', 'node-01');
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
forever(key: CacheKey, value: unknown): Promise<void>;
|
|
241
|
+
/**
|
|
242
|
+
* Get an item from the cache, or execute the given callback and store the result.
|
|
243
|
+
*
|
|
244
|
+
* Implements the "Cache-Aside" pattern, ensuring the callback is only executed
|
|
245
|
+
* on a cache miss.
|
|
246
|
+
*
|
|
247
|
+
* @param key - The unique cache key.
|
|
248
|
+
* @param ttl - Time-to-live.
|
|
249
|
+
* @param callback - The callback to execute if the key is not found.
|
|
250
|
+
* @returns The cached value or the result of the callback.
|
|
251
|
+
* @throws {Error} If the callback or the underlying store fails.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* const data = await cache.remember('users:all', 300, () => db.users.findMany());
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
remember<T = unknown>(key: CacheKey, ttl: CacheTtl, callback: () => Promise<T> | T): Promise<T>;
|
|
259
|
+
/**
|
|
260
|
+
* Get an item from the cache, or execute the given callback and store the result indefinitely.
|
|
261
|
+
*
|
|
262
|
+
* Similar to `remember()`, but the value is stored without an expiration time.
|
|
263
|
+
*
|
|
264
|
+
* @param key - The unique cache key.
|
|
265
|
+
* @param callback - The callback to execute if the key is not found.
|
|
266
|
+
* @returns The cached value or the result of the callback.
|
|
267
|
+
* @throws {Error} If the callback or the underlying store fails.
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* const config = await cache.rememberForever('app:config', () => loadConfig());
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
rememberForever<T = unknown>(key: CacheKey, callback: () => Promise<T> | T): Promise<T>;
|
|
275
|
+
/**
|
|
276
|
+
* Retrieve multiple items from the cache by their keys.
|
|
277
|
+
*
|
|
278
|
+
* Efficiently fetches multiple values, returning a map of keys to values.
|
|
279
|
+
*
|
|
280
|
+
* @param keys - An array of unique cache keys.
|
|
281
|
+
* @returns An object where keys are the original keys and values are the cached values.
|
|
282
|
+
* @throws {Error} If the underlying store fails to retrieve values.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```typescript
|
|
286
|
+
* const results = await cache.many(['user:1', 'user:2']);
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
many<T = unknown>(keys: readonly CacheKey[]): Promise<Record<string, T | null>>;
|
|
290
|
+
/**
|
|
291
|
+
* Store multiple items in the cache for a specific duration.
|
|
292
|
+
*
|
|
293
|
+
* Persists multiple key-value pairs in a single operation if supported by the store.
|
|
294
|
+
*
|
|
295
|
+
* @param values - An object where keys are the unique cache keys and values are the values to store.
|
|
296
|
+
* @param ttl - Time-to-live.
|
|
297
|
+
* @throws {Error} If the underlying store fails to persist values.
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```typescript
|
|
301
|
+
* await cache.putMany({ 'a': 1, 'b': 2 }, 60);
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
putMany(values: Record<string, unknown>, ttl: CacheTtl): Promise<void>;
|
|
305
|
+
/**
|
|
306
|
+
* Laravel-like flexible cache (stale-while-revalidate).
|
|
307
|
+
*
|
|
308
|
+
* Serves stale content while revalidating the cache in the background. This
|
|
309
|
+
* minimizes latency for users by avoiding synchronous revalidation.
|
|
310
|
+
*
|
|
311
|
+
* @param key - The unique cache key.
|
|
312
|
+
* @param ttlSeconds - How long the value is considered fresh.
|
|
313
|
+
* @param staleSeconds - How long the stale value may be served while a refresh happens.
|
|
314
|
+
* @param callback - The callback to execute to refresh the cache.
|
|
315
|
+
* @returns The fresh or stale cached value.
|
|
316
|
+
* @throws {Error} If the callback fails on a cache miss.
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* ```typescript
|
|
320
|
+
* const value = await cache.flexible('stats', 60, 30, () => fetchStats());
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
flexible<T = unknown>(key: CacheKey, ttlSeconds: number, staleSeconds: number, callback: () => Promise<T> | T): Promise<T>;
|
|
324
|
+
private refreshFlexible;
|
|
325
|
+
private doRefresh;
|
|
326
|
+
/**
|
|
327
|
+
* Retrieve an item from the cache and delete it.
|
|
328
|
+
*
|
|
329
|
+
* Atomic-like operation to fetch and immediately remove a value, often used
|
|
330
|
+
* for one-time tokens or flash messages.
|
|
331
|
+
*
|
|
332
|
+
* @param key - The unique cache key.
|
|
333
|
+
* @param defaultValue - A default value to use if the key is not found.
|
|
334
|
+
* @returns The cached value, or the default value if not found.
|
|
335
|
+
* @throws {Error} If the underlying store fails to retrieve or forget the value.
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* ```typescript
|
|
339
|
+
* const message = await cache.pull('flash:status');
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
pull<T = unknown>(key: CacheKey, defaultValue?: T): Promise<T | null>;
|
|
343
|
+
/**
|
|
344
|
+
* Remove an item from the cache by its key.
|
|
345
|
+
*
|
|
346
|
+
* Deletes the value and any associated metadata from the underlying store.
|
|
347
|
+
*
|
|
348
|
+
* @param key - The cache key to remove.
|
|
349
|
+
* @returns True if the item existed and was removed.
|
|
350
|
+
* @throws {Error} If the underlying store fails to remove the value.
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```typescript
|
|
354
|
+
* const deleted = await cache.forget('user:session');
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
forget(key: CacheKey): Promise<boolean>;
|
|
358
|
+
/**
|
|
359
|
+
* Alias for `forget`.
|
|
360
|
+
*
|
|
361
|
+
* Provides compatibility with standard `Map`-like or `Storage` APIs.
|
|
362
|
+
*
|
|
363
|
+
* @param key - The cache key to remove.
|
|
364
|
+
* @returns True if the item existed and was removed.
|
|
365
|
+
* @throws {Error} If the underlying store fails to remove the value.
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* ```typescript
|
|
369
|
+
* await cache.delete('temp:data');
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
delete(key: CacheKey): Promise<boolean>;
|
|
373
|
+
/**
|
|
374
|
+
* Remove all items from the cache storage.
|
|
375
|
+
*
|
|
376
|
+
* Clears the entire underlying store. Use with caution as this affects all
|
|
377
|
+
* keys regardless of prefix.
|
|
378
|
+
*
|
|
379
|
+
* @throws {Error} If the underlying store fails to flush.
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```typescript
|
|
383
|
+
* await cache.flush();
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
flush(): Promise<void>;
|
|
387
|
+
/**
|
|
388
|
+
* Alias for `flush`.
|
|
389
|
+
*
|
|
390
|
+
* Provides compatibility with standard `Map`-like or `Storage` APIs.
|
|
391
|
+
*
|
|
392
|
+
* @throws {Error} If the underlying store fails to clear.
|
|
393
|
+
*
|
|
394
|
+
* @example
|
|
395
|
+
* ```typescript
|
|
396
|
+
* await cache.clear();
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
clear(): Promise<void>;
|
|
400
|
+
/**
|
|
401
|
+
* Increment the value of a numeric item in the cache.
|
|
402
|
+
*
|
|
403
|
+
* Atomically increases the value of a key. If the key does not exist, it is
|
|
404
|
+
* typically initialized to 0 before incrementing.
|
|
405
|
+
*
|
|
406
|
+
* @param key - The cache key.
|
|
407
|
+
* @param value - The amount to increment by.
|
|
408
|
+
* @returns The new value.
|
|
409
|
+
* @throws {Error} If the underlying store fails the atomic increment.
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* ```typescript
|
|
413
|
+
* const count = await cache.increment('page:views');
|
|
414
|
+
* ```
|
|
415
|
+
*/
|
|
416
|
+
increment(key: string, value?: number): Promise<number>;
|
|
417
|
+
/**
|
|
418
|
+
* Decrement the value of a numeric item in the cache.
|
|
419
|
+
*
|
|
420
|
+
* Atomically decreases the value of a key.
|
|
421
|
+
*
|
|
422
|
+
* @param key - The cache key.
|
|
423
|
+
* @param value - The amount to decrement by.
|
|
424
|
+
* @returns The new value.
|
|
425
|
+
* @throws {Error} If the underlying store fails the atomic decrement.
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```typescript
|
|
429
|
+
* const remaining = await cache.decrement('stock:count');
|
|
430
|
+
* ```
|
|
431
|
+
*/
|
|
432
|
+
decrement(key: string, value?: number): Promise<number>;
|
|
433
|
+
/**
|
|
434
|
+
* Get a distributed lock instance for the given name.
|
|
435
|
+
*
|
|
436
|
+
* Provides a mechanism for exclusive access to resources across multiple
|
|
437
|
+
* processes or servers.
|
|
438
|
+
*
|
|
439
|
+
* @param name - The lock name.
|
|
440
|
+
* @param seconds - Optional default duration for the lock in seconds.
|
|
441
|
+
* @returns A `CacheLock` instance if supported, otherwise undefined.
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```typescript
|
|
445
|
+
* const lock = cache.lock('process:heavy', 10);
|
|
446
|
+
* if (await lock.acquire()) {
|
|
447
|
+
* try {
|
|
448
|
+
* // ...
|
|
449
|
+
* } finally {
|
|
450
|
+
* await lock.release();
|
|
451
|
+
* }
|
|
452
|
+
* }
|
|
453
|
+
* ```
|
|
454
|
+
*/
|
|
455
|
+
lock(name: string, seconds?: number): import("./locks").CacheLock;
|
|
456
|
+
/**
|
|
457
|
+
* Create a new repository instance with the given tags.
|
|
458
|
+
*
|
|
459
|
+
* Enables grouping of cache entries for collective operations like flushing
|
|
460
|
+
* all keys associated with specific tags.
|
|
461
|
+
*
|
|
462
|
+
* @param tags - An array of tag names.
|
|
463
|
+
* @returns A new `CacheRepository` instance that uses the given tags.
|
|
464
|
+
* @throws {Error} If the underlying store does not support tagging.
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* ```typescript
|
|
468
|
+
* await cache.tags(['users', 'profiles']).put('user:1', data, 3600);
|
|
469
|
+
* await cache.tags(['users']).flush();
|
|
470
|
+
* ```
|
|
471
|
+
*/
|
|
472
|
+
tags(tags: readonly string[]): CacheRepository;
|
|
473
|
+
/**
|
|
474
|
+
* Retrieve the underlying cache store.
|
|
475
|
+
*
|
|
476
|
+
* Provides direct access to the low-level store implementation for advanced
|
|
477
|
+
* use cases or debugging.
|
|
478
|
+
*
|
|
479
|
+
* @returns The low-level cache store instance.
|
|
480
|
+
*
|
|
481
|
+
* @example
|
|
482
|
+
* ```typescript
|
|
483
|
+
* const store = cache.getStore();
|
|
484
|
+
* ```
|
|
485
|
+
*/
|
|
486
|
+
getStore(): CacheStore;
|
|
487
|
+
/**
|
|
488
|
+
* Compress a value before storage if compression is enabled and thresholds are met.
|
|
489
|
+
*/
|
|
490
|
+
private compress;
|
|
491
|
+
/**
|
|
492
|
+
* Decompress a value after retrieval if it was previously compressed.
|
|
493
|
+
*/
|
|
494
|
+
private decompress;
|
|
495
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { CacheStore } from './store';
|
|
2
|
+
/**
|
|
3
|
+
* Represents the response from a rate limiting attempt.
|
|
4
|
+
*
|
|
5
|
+
* This interface provides the necessary metadata to determine if a request
|
|
6
|
+
* should be throttled and when the client can safely retry.
|
|
7
|
+
*
|
|
8
|
+
* @public
|
|
9
|
+
* @since 3.0.0
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const res: RateLimiterResponse = { allowed: true, remaining: 4, reset: 1622548800 };
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export interface RateLimiterResponse {
|
|
17
|
+
/** Whether the request is allowed based on current usage and limits. */
|
|
18
|
+
allowed: boolean;
|
|
19
|
+
/** Number of attempts remaining within the current time window. */
|
|
20
|
+
remaining: number;
|
|
21
|
+
/** Epoch timestamp in seconds when the rate limit window will reset. */
|
|
22
|
+
reset: number;
|
|
23
|
+
/** Seconds until the rate limit resets, typically used for the `Retry-After` header. */
|
|
24
|
+
retryAfter?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Detailed information about the current rate limit status.
|
|
28
|
+
*
|
|
29
|
+
* Used for inspecting the state of a limiter without necessarily
|
|
30
|
+
* consuming an attempt or triggering a state change.
|
|
31
|
+
*
|
|
32
|
+
* @public
|
|
33
|
+
* @since 3.0.0
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const info: RateLimitInfo = { limit: 10, remaining: 5, reset: 1622548800 };
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export interface RateLimitInfo {
|
|
41
|
+
/** Maximum number of attempts allowed within the configured window. */
|
|
42
|
+
limit: number;
|
|
43
|
+
/** Number of attempts remaining before the limit is reached. */
|
|
44
|
+
remaining: number;
|
|
45
|
+
/** Epoch timestamp in seconds when the rate limit window will reset. */
|
|
46
|
+
reset: number;
|
|
47
|
+
/** Seconds until the rate limit resets, only present when the limit has been exceeded. */
|
|
48
|
+
retryAfter?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* RateLimiter provides a simple mechanism for limiting request frequency.
|
|
52
|
+
*
|
|
53
|
+
* It uses a `CacheStore` backend to track attempt counts and handle
|
|
54
|
+
* expiration. This allows for distributed rate limiting when using
|
|
55
|
+
* shared stores like Redis, or local limiting with memory stores.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* const limiter = new RateLimiter(cacheStore);
|
|
60
|
+
* const status = await limiter.attempt('login:127.0.0.1', 5, 60);
|
|
61
|
+
*
|
|
62
|
+
* if (!status.allowed) {
|
|
63
|
+
* console.log(`Too many attempts. Retry after ${status.retryAfter}s`);
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* @public
|
|
68
|
+
* @since 3.0.0
|
|
69
|
+
*/
|
|
70
|
+
export declare class RateLimiter {
|
|
71
|
+
private store;
|
|
72
|
+
/**
|
|
73
|
+
* Creates a new RateLimiter instance.
|
|
74
|
+
*
|
|
75
|
+
* @param store - Cache backend used to persist attempt counts.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const limiter = new RateLimiter(new MemoryStore());
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
constructor(store: CacheStore);
|
|
83
|
+
/**
|
|
84
|
+
* Attempt to consume a slot in the rate limit window.
|
|
85
|
+
*
|
|
86
|
+
* This method checks the current attempt count for the given key. If the
|
|
87
|
+
* count is below the limit, it increments the count and allows the request.
|
|
88
|
+
* Otherwise, it returns a rejected status with retry information.
|
|
89
|
+
*
|
|
90
|
+
* @param key - The unique identifier for the rate limit (e.g., IP address or user ID).
|
|
91
|
+
* @param maxAttempts - Maximum number of attempts allowed within the decay period.
|
|
92
|
+
* @param decaySeconds - Duration of the rate limit window in seconds.
|
|
93
|
+
* @returns A response indicating if the attempt was successful and the remaining capacity.
|
|
94
|
+
* @throws {Error} If the underlying cache store fails to read or write data.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const response = await limiter.attempt('api-client:123', 100, 3600);
|
|
99
|
+
* if (response.allowed) {
|
|
100
|
+
* // Proceed with request
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
attempt(key: string, maxAttempts: number, decaySeconds: number): Promise<RateLimiterResponse>;
|
|
105
|
+
/**
|
|
106
|
+
* Calculate the number of seconds until the rate limit resets.
|
|
107
|
+
*
|
|
108
|
+
* This helper method attempts to retrieve the TTL from the store. If the
|
|
109
|
+
* store does not support TTL inspection, it falls back to the provided
|
|
110
|
+
* decay period.
|
|
111
|
+
*
|
|
112
|
+
* @param key - Unique identifier for the rate limit.
|
|
113
|
+
* @param decaySeconds - Default decay period to use as a fallback.
|
|
114
|
+
* @returns Number of seconds until the key expires.
|
|
115
|
+
* @throws {Error} If the store fails to retrieve TTL metadata.
|
|
116
|
+
*/
|
|
117
|
+
availableIn(key: string, decaySeconds: number): Promise<number>;
|
|
118
|
+
/**
|
|
119
|
+
* Get detailed information about the current rate limit status without consuming an attempt.
|
|
120
|
+
*
|
|
121
|
+
* Useful for returning rate limit headers (e.g., X-RateLimit-Limit) in
|
|
122
|
+
* middleware or for pre-flight checks.
|
|
123
|
+
*
|
|
124
|
+
* @param key - The unique identifier for the rate limit.
|
|
125
|
+
* @param maxAttempts - Maximum number of attempts allowed.
|
|
126
|
+
* @param decaySeconds - Duration of the rate limit window in seconds.
|
|
127
|
+
* @returns Current status including limit, remaining attempts, and reset time.
|
|
128
|
+
* @throws {Error} If the underlying cache store fails to retrieve data.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* const info = await limiter.getInfo('user:42', 60, 60);
|
|
133
|
+
* console.log(`Remaining: ${info.remaining}/${info.limit}`);
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
getInfo(key: string, maxAttempts: number, decaySeconds: number): Promise<RateLimitInfo>;
|
|
137
|
+
/**
|
|
138
|
+
* Reset the rate limit counter for a specific key.
|
|
139
|
+
*
|
|
140
|
+
* Use this to manually clear a block, for example after a successful
|
|
141
|
+
* login or when an administrator manually unblocks a user.
|
|
142
|
+
*
|
|
143
|
+
* @param key - The unique identifier to clear.
|
|
144
|
+
* @throws {Error} If the store fails to delete the key.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* await limiter.clear('login-attempts:user@example.com');
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
clear(key: string): Promise<void>;
|
|
152
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache event system types and dispatcher.
|
|
3
|
+
*
|
|
4
|
+
* Provides event mode definitions, event handler contracts,
|
|
5
|
+
* and the core emit logic for cache lifecycle events.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
* @since 3.0.0
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Supported modes for emitting cache events.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
* @since 3.0.0
|
|
15
|
+
*/
|
|
16
|
+
export type CacheEventMode = 'sync' | 'async' | 'off';
|
|
17
|
+
/**
|
|
18
|
+
* Event handlers for cache lifecycle events.
|
|
19
|
+
*
|
|
20
|
+
* @public
|
|
21
|
+
* @since 3.0.0
|
|
22
|
+
*/
|
|
23
|
+
export type CacheEvents = {
|
|
24
|
+
/** Triggered on a cache hit. */
|
|
25
|
+
hit?: (key: string) => void | Promise<void>;
|
|
26
|
+
/** Triggered on a cache miss. */
|
|
27
|
+
miss?: (key: string) => void | Promise<void>;
|
|
28
|
+
/** Triggered when a value is written to the cache. */
|
|
29
|
+
write?: (key: string) => void | Promise<void>;
|
|
30
|
+
/** Triggered when a value is removed from the cache. */
|
|
31
|
+
forget?: (key: string) => void | Promise<void>;
|
|
32
|
+
/** Triggered when the entire cache is flushed. */
|
|
33
|
+
flush?: () => void | Promise<void>;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Configuration for the cache event emitter.
|
|
37
|
+
*
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
export type CacheEventEmitterConfig = {
|
|
41
|
+
mode: CacheEventMode;
|
|
42
|
+
throwOnError?: boolean;
|
|
43
|
+
onError?: (error: unknown, event: keyof CacheEvents, payload: {
|
|
44
|
+
key?: string;
|
|
45
|
+
}) => void;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Emit a cache lifecycle event according to the configured mode.
|
|
49
|
+
*
|
|
50
|
+
* @param event - The cache event name
|
|
51
|
+
* @param payload - Event payload
|
|
52
|
+
* @param events - Event handler map
|
|
53
|
+
* @param config - Emitter configuration
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
export declare function emitCacheEvent(event: keyof CacheEvents, payload: {
|
|
57
|
+
key?: string;
|
|
58
|
+
}, events: CacheEvents | undefined, config: CacheEventEmitterConfig): void | Promise<void>;
|