layercache 1.0.1 → 1.1.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 +286 -7
- package/benchmarks/latency.ts +1 -1
- package/benchmarks/stampede.ts +1 -4
- package/dist/chunk-QUB5VZFZ.js +132 -0
- package/dist/cli.cjs +296 -0
- package/dist/cli.d.cts +4 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +135 -0
- package/dist/index.cjs +1576 -184
- package/dist/index.d.cts +465 -7
- package/dist/index.d.ts +465 -7
- package/dist/index.js +1526 -266
- package/examples/express-api/index.ts +12 -8
- package/examples/nestjs-module/app.module.ts +2 -5
- package/examples/nextjs-api-routes/route.ts +1 -4
- package/package.json +10 -2
- package/packages/nestjs/dist/index.cjs +1058 -155
- package/packages/nestjs/dist/index.d.cts +345 -2
- package/packages/nestjs/dist/index.d.ts +345 -2
- package/packages/nestjs/dist/index.js +1057 -155
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
1
2
|
import Redis from 'ioredis';
|
|
2
3
|
|
|
3
4
|
interface LayerTtlMap {
|
|
@@ -11,6 +12,10 @@ interface CacheWriteOptions {
|
|
|
11
12
|
staleWhileRevalidate?: number | LayerTtlMap;
|
|
12
13
|
staleIfError?: number | LayerTtlMap;
|
|
13
14
|
ttlJitter?: number | LayerTtlMap;
|
|
15
|
+
slidingTtl?: boolean;
|
|
16
|
+
refreshAhead?: number | LayerTtlMap;
|
|
17
|
+
adaptiveTtl?: boolean | CacheAdaptiveTtlOptions;
|
|
18
|
+
circuitBreaker?: CacheCircuitBreakerOptions;
|
|
14
19
|
}
|
|
15
20
|
interface CacheGetOptions extends CacheWriteOptions {
|
|
16
21
|
}
|
|
@@ -24,6 +29,7 @@ interface CacheMSetEntry<T> {
|
|
|
24
29
|
value: T;
|
|
25
30
|
options?: CacheWriteOptions;
|
|
26
31
|
}
|
|
32
|
+
/** Interface that all cache backend implementations must satisfy. */
|
|
27
33
|
interface CacheLayer {
|
|
28
34
|
readonly name: string;
|
|
29
35
|
readonly defaultTtl?: number;
|
|
@@ -36,11 +42,28 @@ interface CacheLayer {
|
|
|
36
42
|
clear(): Promise<void>;
|
|
37
43
|
deleteMany?(keys: string[]): Promise<void>;
|
|
38
44
|
keys?(): Promise<string[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Returns true if the key exists and has not expired.
|
|
47
|
+
* Implementations may omit this; CacheStack will fall back to `get()`.
|
|
48
|
+
*/
|
|
49
|
+
has?(key: string): Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* Returns the remaining TTL in seconds for the key, or null if the key
|
|
52
|
+
* does not exist, has no TTL, or has already expired.
|
|
53
|
+
* Implementations may omit this.
|
|
54
|
+
*/
|
|
55
|
+
ttl?(key: string): Promise<number | null>;
|
|
56
|
+
/**
|
|
57
|
+
* Returns the number of entries currently held by this layer.
|
|
58
|
+
* Implementations may omit this.
|
|
59
|
+
*/
|
|
60
|
+
size?(): Promise<number>;
|
|
39
61
|
}
|
|
40
62
|
interface CacheSerializer {
|
|
41
63
|
serialize(value: unknown): string | Buffer;
|
|
42
64
|
deserialize<T>(payload: string | Buffer): T;
|
|
43
65
|
}
|
|
66
|
+
/** Snapshot of cumulative cache counters. */
|
|
44
67
|
interface CacheMetricsSnapshot {
|
|
45
68
|
hits: number;
|
|
46
69
|
misses: number;
|
|
@@ -54,9 +77,26 @@ interface CacheMetricsSnapshot {
|
|
|
54
77
|
refreshErrors: number;
|
|
55
78
|
writeFailures: number;
|
|
56
79
|
singleFlightWaits: number;
|
|
80
|
+
negativeCacheHits: number;
|
|
81
|
+
circuitBreakerTrips: number;
|
|
82
|
+
degradedOperations: number;
|
|
83
|
+
hitsByLayer: Record<string, number>;
|
|
84
|
+
missesByLayer: Record<string, number>;
|
|
85
|
+
/** Timestamp (ms since epoch) when metrics were last reset. */
|
|
86
|
+
resetAt: number;
|
|
87
|
+
}
|
|
88
|
+
/** Computed hit-rate statistics derived from CacheMetricsSnapshot. */
|
|
89
|
+
interface CacheHitRateSnapshot {
|
|
90
|
+
/** Overall hit rate across all layers (0–1). */
|
|
91
|
+
overall: number;
|
|
92
|
+
/** Per-layer hit rates (0–1 each). */
|
|
93
|
+
byLayer: Record<string, number>;
|
|
57
94
|
}
|
|
58
95
|
interface CacheLogger {
|
|
59
|
-
debug(message: string, context?: Record<string, unknown>): void;
|
|
96
|
+
debug?(message: string, context?: Record<string, unknown>): void;
|
|
97
|
+
info?(message: string, context?: Record<string, unknown>): void;
|
|
98
|
+
warn?(message: string, context?: Record<string, unknown>): void;
|
|
99
|
+
error?(message: string, context?: Record<string, unknown>): void;
|
|
60
100
|
}
|
|
61
101
|
interface CacheTagIndex {
|
|
62
102
|
touch(key: string): Promise<void>;
|
|
@@ -90,41 +130,237 @@ interface CacheStackOptions {
|
|
|
90
130
|
stampedePrevention?: boolean;
|
|
91
131
|
invalidationBus?: InvalidationBus;
|
|
92
132
|
tagIndex?: CacheTagIndex;
|
|
133
|
+
broadcastL1Invalidation?: boolean;
|
|
134
|
+
/**
|
|
135
|
+
* @deprecated Use `broadcastL1Invalidation` instead.
|
|
136
|
+
*/
|
|
93
137
|
publishSetInvalidation?: boolean;
|
|
94
138
|
negativeCaching?: boolean;
|
|
95
139
|
negativeTtl?: number | LayerTtlMap;
|
|
96
140
|
staleWhileRevalidate?: number | LayerTtlMap;
|
|
97
141
|
staleIfError?: number | LayerTtlMap;
|
|
98
142
|
ttlJitter?: number | LayerTtlMap;
|
|
143
|
+
refreshAhead?: number | LayerTtlMap;
|
|
144
|
+
adaptiveTtl?: boolean | CacheAdaptiveTtlOptions;
|
|
145
|
+
circuitBreaker?: CacheCircuitBreakerOptions;
|
|
146
|
+
gracefulDegradation?: boolean | CacheDegradationOptions;
|
|
99
147
|
writePolicy?: 'strict' | 'best-effort';
|
|
100
148
|
singleFlightCoordinator?: CacheSingleFlightCoordinator;
|
|
101
149
|
singleFlightLeaseMs?: number;
|
|
102
150
|
singleFlightTimeoutMs?: number;
|
|
103
151
|
singleFlightPollMs?: number;
|
|
152
|
+
/**
|
|
153
|
+
* Maximum number of entries in `accessProfiles` and `circuitBreakers` maps
|
|
154
|
+
* before the oldest entries are pruned. Prevents unbounded memory growth.
|
|
155
|
+
* Defaults to 100 000.
|
|
156
|
+
*/
|
|
157
|
+
maxProfileEntries?: number;
|
|
158
|
+
}
|
|
159
|
+
interface CacheAdaptiveTtlOptions {
|
|
160
|
+
hotAfter?: number;
|
|
161
|
+
step?: number | LayerTtlMap;
|
|
162
|
+
maxTtl?: number | LayerTtlMap;
|
|
163
|
+
}
|
|
164
|
+
interface CacheCircuitBreakerOptions {
|
|
165
|
+
failureThreshold?: number;
|
|
166
|
+
cooldownMs?: number;
|
|
167
|
+
}
|
|
168
|
+
interface CacheDegradationOptions {
|
|
169
|
+
retryAfterMs?: number;
|
|
170
|
+
}
|
|
171
|
+
interface CacheWarmEntry<T = unknown> {
|
|
172
|
+
key: string;
|
|
173
|
+
fetcher: () => Promise<T>;
|
|
174
|
+
options?: CacheGetOptions;
|
|
175
|
+
priority?: number;
|
|
176
|
+
}
|
|
177
|
+
/** Options controlling the cache warm-up process. */
|
|
178
|
+
interface CacheWarmOptions {
|
|
179
|
+
concurrency?: number;
|
|
180
|
+
continueOnError?: boolean;
|
|
181
|
+
/** Called after each entry is processed (success or failure). */
|
|
182
|
+
onProgress?: (progress: CacheWarmProgress) => void;
|
|
183
|
+
}
|
|
184
|
+
/** Progress information delivered to `CacheWarmOptions.onProgress`. */
|
|
185
|
+
interface CacheWarmProgress {
|
|
186
|
+
completed: number;
|
|
187
|
+
total: number;
|
|
188
|
+
key: string;
|
|
189
|
+
success: boolean;
|
|
190
|
+
}
|
|
191
|
+
interface CacheWrapOptions<TArgs extends unknown[] = unknown[]> extends CacheGetOptions {
|
|
192
|
+
keyResolver?: (...args: TArgs) => string;
|
|
193
|
+
}
|
|
194
|
+
interface CacheSnapshotEntry {
|
|
195
|
+
key: string;
|
|
196
|
+
value: unknown;
|
|
197
|
+
ttl?: number;
|
|
198
|
+
}
|
|
199
|
+
interface CacheStatsSnapshot {
|
|
200
|
+
metrics: CacheMetricsSnapshot;
|
|
201
|
+
layers: Array<{
|
|
202
|
+
name: string;
|
|
203
|
+
isLocal: boolean;
|
|
204
|
+
degradedUntil: number | null;
|
|
205
|
+
}>;
|
|
206
|
+
backgroundRefreshes: number;
|
|
207
|
+
}
|
|
208
|
+
/** All events emitted by CacheStack and their payload shapes. */
|
|
209
|
+
interface CacheStackEvents {
|
|
210
|
+
/** Fired on every cache hit. */
|
|
211
|
+
hit: {
|
|
212
|
+
key: string;
|
|
213
|
+
layer: string;
|
|
214
|
+
state: 'fresh' | 'stale-while-revalidate' | 'stale-if-error';
|
|
215
|
+
};
|
|
216
|
+
/** Fired on every cache miss before the fetcher runs. */
|
|
217
|
+
miss: {
|
|
218
|
+
key: string;
|
|
219
|
+
mode: string;
|
|
220
|
+
};
|
|
221
|
+
/** Fired after a value is stored in the cache. */
|
|
222
|
+
set: {
|
|
223
|
+
key: string;
|
|
224
|
+
kind: string;
|
|
225
|
+
tags?: string[];
|
|
226
|
+
};
|
|
227
|
+
/** Fired after one or more keys are deleted. */
|
|
228
|
+
delete: {
|
|
229
|
+
keys: string[];
|
|
230
|
+
};
|
|
231
|
+
/** Fired when a value is backfilled into a faster layer. */
|
|
232
|
+
backfill: {
|
|
233
|
+
key: string;
|
|
234
|
+
layer: string;
|
|
235
|
+
};
|
|
236
|
+
/** Fired when a stale value is returned to the caller. */
|
|
237
|
+
'stale-serve': {
|
|
238
|
+
key: string;
|
|
239
|
+
state: string;
|
|
240
|
+
layer: string;
|
|
241
|
+
};
|
|
242
|
+
/** Fired when a duplicate request is deduplicated in stampede prevention. */
|
|
243
|
+
'stampede-dedupe': {
|
|
244
|
+
key: string;
|
|
245
|
+
};
|
|
246
|
+
/** Fired after a key is successfully warmed. */
|
|
247
|
+
warm: {
|
|
248
|
+
key: string;
|
|
249
|
+
};
|
|
250
|
+
/** Fired when an error occurs (layer failure, circuit breaker, etc.). */
|
|
251
|
+
error: {
|
|
252
|
+
operation: string;
|
|
253
|
+
[key: string]: unknown;
|
|
254
|
+
};
|
|
104
255
|
}
|
|
105
256
|
|
|
106
|
-
declare class
|
|
257
|
+
declare class CacheNamespace {
|
|
258
|
+
private readonly cache;
|
|
259
|
+
private readonly prefix;
|
|
260
|
+
constructor(cache: CacheStack, prefix: string);
|
|
261
|
+
get<T>(key: string, fetcher?: () => Promise<T>, options?: CacheGetOptions): Promise<T | null>;
|
|
262
|
+
getOrSet<T>(key: string, fetcher: () => Promise<T>, options?: CacheGetOptions): Promise<T | null>;
|
|
263
|
+
has(key: string): Promise<boolean>;
|
|
264
|
+
ttl(key: string): Promise<number | null>;
|
|
265
|
+
set<T>(key: string, value: T, options?: CacheWriteOptions): Promise<void>;
|
|
266
|
+
delete(key: string): Promise<void>;
|
|
267
|
+
mdelete(keys: string[]): Promise<void>;
|
|
268
|
+
clear(): Promise<void>;
|
|
269
|
+
mget<T>(entries: CacheMGetEntry<T>[]): Promise<Array<T | null>>;
|
|
270
|
+
mset<T>(entries: CacheMSetEntry<T>[]): Promise<void>;
|
|
271
|
+
invalidateByTag(tag: string): Promise<void>;
|
|
272
|
+
invalidateByPattern(pattern: string): Promise<void>;
|
|
273
|
+
wrap<TArgs extends unknown[], TResult>(keyPrefix: string, fetcher: (...args: TArgs) => Promise<TResult>, options?: CacheWrapOptions<TArgs>): (...args: TArgs) => Promise<TResult | null>;
|
|
274
|
+
warm(entries: CacheWarmEntry[], options?: CacheWarmOptions): Promise<void>;
|
|
275
|
+
getMetrics(): CacheMetricsSnapshot;
|
|
276
|
+
getHitRate(): CacheHitRateSnapshot;
|
|
277
|
+
qualify(key: string): string;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Typed overloads for EventEmitter so callers get autocomplete on event names. */
|
|
281
|
+
interface CacheStack {
|
|
282
|
+
on<K extends keyof CacheStackEvents>(event: K, listener: (data: CacheStackEvents[K]) => void): this;
|
|
283
|
+
once<K extends keyof CacheStackEvents>(event: K, listener: (data: CacheStackEvents[K]) => void): this;
|
|
284
|
+
off<K extends keyof CacheStackEvents>(event: K, listener: (data: CacheStackEvents[K]) => void): this;
|
|
285
|
+
emit<K extends keyof CacheStackEvents>(event: K, data: CacheStackEvents[K]): boolean;
|
|
286
|
+
}
|
|
287
|
+
declare class CacheStack extends EventEmitter {
|
|
107
288
|
private readonly layers;
|
|
108
289
|
private readonly options;
|
|
109
290
|
private readonly stampedeGuard;
|
|
110
|
-
private readonly
|
|
291
|
+
private readonly metricsCollector;
|
|
111
292
|
private readonly instanceId;
|
|
112
293
|
private readonly startup;
|
|
113
294
|
private unsubscribeInvalidation?;
|
|
114
295
|
private readonly logger;
|
|
115
296
|
private readonly tagIndex;
|
|
116
297
|
private readonly backgroundRefreshes;
|
|
298
|
+
private readonly layerDegradedUntil;
|
|
299
|
+
private readonly ttlResolver;
|
|
300
|
+
private readonly circuitBreakerManager;
|
|
301
|
+
private isDisconnecting;
|
|
302
|
+
private disconnectPromise?;
|
|
117
303
|
constructor(layers: CacheLayer[], options?: CacheStackOptions);
|
|
304
|
+
/**
|
|
305
|
+
* Read-through cache get.
|
|
306
|
+
* Returns the cached value if present and fresh, or invokes `fetcher` on a miss
|
|
307
|
+
* and stores the result across all layers. Returns `null` if the key is not found
|
|
308
|
+
* and no `fetcher` is provided.
|
|
309
|
+
*/
|
|
118
310
|
get<T>(key: string, fetcher?: () => Promise<T>, options?: CacheGetOptions): Promise<T | null>;
|
|
311
|
+
/**
|
|
312
|
+
* Alias for `get(key, fetcher, options)` — explicit get-or-set pattern.
|
|
313
|
+
* Fetches and caches the value if not already present.
|
|
314
|
+
*/
|
|
315
|
+
getOrSet<T>(key: string, fetcher: () => Promise<T>, options?: CacheGetOptions): Promise<T | null>;
|
|
316
|
+
/**
|
|
317
|
+
* Returns true if the given key exists and is not expired in any layer.
|
|
318
|
+
*/
|
|
319
|
+
has(key: string): Promise<boolean>;
|
|
320
|
+
/**
|
|
321
|
+
* Returns the remaining TTL in seconds for the key in the fastest layer
|
|
322
|
+
* that has it, or null if the key is not found / has no TTL.
|
|
323
|
+
*/
|
|
324
|
+
ttl(key: string): Promise<number | null>;
|
|
325
|
+
/**
|
|
326
|
+
* Stores a value in all cache layers. Overwrites any existing value.
|
|
327
|
+
*/
|
|
119
328
|
set<T>(key: string, value: T, options?: CacheWriteOptions): Promise<void>;
|
|
329
|
+
/**
|
|
330
|
+
* Deletes the key from all layers and publishes an invalidation message.
|
|
331
|
+
*/
|
|
120
332
|
delete(key: string): Promise<void>;
|
|
121
333
|
clear(): Promise<void>;
|
|
334
|
+
/**
|
|
335
|
+
* Deletes multiple keys at once. More efficient than calling `delete()` in a loop.
|
|
336
|
+
*/
|
|
337
|
+
mdelete(keys: string[]): Promise<void>;
|
|
122
338
|
mget<T>(entries: CacheMGetEntry<T>[]): Promise<Array<T | null>>;
|
|
123
339
|
mset<T>(entries: CacheMSetEntry<T>[]): Promise<void>;
|
|
340
|
+
warm(entries: CacheWarmEntry[], options?: CacheWarmOptions): Promise<void>;
|
|
341
|
+
/**
|
|
342
|
+
* Returns a cached version of `fetcher`. The cache key is derived from
|
|
343
|
+
* `prefix` plus the serialized arguments unless a `keyResolver` is provided.
|
|
344
|
+
*/
|
|
345
|
+
wrap<TArgs extends unknown[], TResult>(prefix: string, fetcher: (...args: TArgs) => Promise<TResult>, options?: CacheWrapOptions<TArgs>): (...args: TArgs) => Promise<TResult | null>;
|
|
346
|
+
/**
|
|
347
|
+
* Creates a `CacheNamespace` that automatically prefixes all keys with
|
|
348
|
+
* `prefix:`. Useful for multi-tenant or module-level isolation.
|
|
349
|
+
*/
|
|
350
|
+
namespace(prefix: string): CacheNamespace;
|
|
124
351
|
invalidateByTag(tag: string): Promise<void>;
|
|
125
352
|
invalidateByPattern(pattern: string): Promise<void>;
|
|
126
353
|
getMetrics(): CacheMetricsSnapshot;
|
|
354
|
+
getStats(): CacheStatsSnapshot;
|
|
127
355
|
resetMetrics(): void;
|
|
356
|
+
/**
|
|
357
|
+
* Returns computed hit-rate statistics (overall and per-layer).
|
|
358
|
+
*/
|
|
359
|
+
getHitRate(): CacheHitRateSnapshot;
|
|
360
|
+
exportState(): Promise<CacheSnapshotEntry[]>;
|
|
361
|
+
importState(entries: CacheSnapshotEntry[]): Promise<void>;
|
|
362
|
+
persistToFile(filePath: string): Promise<void>;
|
|
363
|
+
restoreFromFile(filePath: string): Promise<void>;
|
|
128
364
|
disconnect(): Promise<void>;
|
|
129
365
|
private initialize;
|
|
130
366
|
private fetchWithGuards;
|
|
@@ -138,8 +374,6 @@ declare class CacheStack {
|
|
|
138
374
|
private executeLayerOperations;
|
|
139
375
|
private resolveFreshTtl;
|
|
140
376
|
private resolveLayerSeconds;
|
|
141
|
-
private readLayerNumber;
|
|
142
|
-
private applyJitter;
|
|
143
377
|
private shouldNegativeCache;
|
|
144
378
|
private scheduleBackgroundRefresh;
|
|
145
379
|
private resolveSingleFlightOptions;
|
|
@@ -148,10 +382,41 @@ declare class CacheStack {
|
|
|
148
382
|
private handleInvalidationMessage;
|
|
149
383
|
private formatError;
|
|
150
384
|
private sleep;
|
|
385
|
+
private shouldBroadcastL1Invalidation;
|
|
386
|
+
private deleteKeysFromLayers;
|
|
387
|
+
private validateConfiguration;
|
|
388
|
+
private validateWriteOptions;
|
|
389
|
+
private validateLayerNumberOption;
|
|
390
|
+
private validatePositiveNumber;
|
|
391
|
+
private validateNonNegativeNumber;
|
|
392
|
+
private validateCacheKey;
|
|
393
|
+
private serializeOptions;
|
|
394
|
+
private validateAdaptiveTtlOptions;
|
|
395
|
+
private validateCircuitBreakerOptions;
|
|
396
|
+
private applyFreshReadPolicies;
|
|
397
|
+
private shouldSkipLayer;
|
|
398
|
+
private handleLayerFailure;
|
|
399
|
+
private isGracefulDegradationEnabled;
|
|
400
|
+
private recordCircuitFailure;
|
|
401
|
+
private isNegativeStoredValue;
|
|
402
|
+
private emitError;
|
|
403
|
+
private serializeKeyPart;
|
|
404
|
+
private isCacheSnapshotEntries;
|
|
405
|
+
private normalizeForSerialization;
|
|
151
406
|
}
|
|
152
407
|
|
|
153
408
|
declare class PatternMatcher {
|
|
409
|
+
/**
|
|
410
|
+
* Tests whether a glob-style pattern matches a value.
|
|
411
|
+
* Supports `*` (any sequence of characters) and `?` (any single character).
|
|
412
|
+
* Uses a linear-time algorithm to avoid ReDoS vulnerabilities.
|
|
413
|
+
*/
|
|
154
414
|
static matches(pattern: string, value: string): boolean;
|
|
415
|
+
/**
|
|
416
|
+
* Linear-time glob matching using dynamic programming.
|
|
417
|
+
* Avoids catastrophic backtracking that RegExp-based glob matching can cause.
|
|
418
|
+
*/
|
|
419
|
+
private static matchLinear;
|
|
155
420
|
}
|
|
156
421
|
|
|
157
422
|
interface RedisInvalidationBusOptions {
|
|
@@ -163,9 +428,13 @@ declare class RedisInvalidationBus implements InvalidationBus {
|
|
|
163
428
|
private readonly channel;
|
|
164
429
|
private readonly publisher;
|
|
165
430
|
private readonly subscriber;
|
|
431
|
+
private activeListener?;
|
|
166
432
|
constructor(options: RedisInvalidationBusOptions);
|
|
167
433
|
subscribe(handler: (message: InvalidationMessage) => Promise<void> | void): Promise<() => Promise<void>>;
|
|
168
434
|
publish(message: InvalidationMessage): Promise<void>;
|
|
435
|
+
private handleMessage;
|
|
436
|
+
private isInvalidationMessage;
|
|
437
|
+
private reportError;
|
|
169
438
|
}
|
|
170
439
|
|
|
171
440
|
interface RedisTagIndexOptions {
|
|
@@ -202,30 +471,95 @@ declare class TagIndex implements CacheTagIndex {
|
|
|
202
471
|
clear(): Promise<void>;
|
|
203
472
|
}
|
|
204
473
|
|
|
474
|
+
declare function createCacheStatsHandler(cache: CacheStack): (_request: unknown, response: {
|
|
475
|
+
setHeader?: (name: string, value: string) => void;
|
|
476
|
+
end: (body: string) => void;
|
|
477
|
+
statusCode?: number;
|
|
478
|
+
}) => Promise<void>;
|
|
479
|
+
|
|
480
|
+
interface CachedMethodDecoratorOptions<TArgs extends unknown[]> extends CacheWrapOptions<TArgs> {
|
|
481
|
+
cache: (instance: unknown) => CacheStack;
|
|
482
|
+
prefix?: string;
|
|
483
|
+
}
|
|
484
|
+
declare function createCachedMethodDecorator<TArgs extends unknown[] = unknown[]>(options: CachedMethodDecoratorOptions<TArgs>): MethodDecorator;
|
|
485
|
+
|
|
486
|
+
interface FastifyLike {
|
|
487
|
+
decorate: (name: string, value: unknown) => void;
|
|
488
|
+
get?: (path: string, handler: () => unknown | Promise<unknown>) => void;
|
|
489
|
+
}
|
|
490
|
+
interface FastifyLayercachePluginOptions {
|
|
491
|
+
exposeStatsRoute?: boolean;
|
|
492
|
+
statsPath?: string;
|
|
493
|
+
}
|
|
494
|
+
declare function createFastifyLayercachePlugin(cache: CacheStack, options?: FastifyLayercachePluginOptions): (fastify: FastifyLike) => Promise<void>;
|
|
495
|
+
|
|
496
|
+
interface GraphqlCacheOptions<TArgs extends unknown[]> extends CacheGetOptions {
|
|
497
|
+
keyResolver?: (...args: TArgs) => string;
|
|
498
|
+
}
|
|
499
|
+
declare function cacheGraphqlResolver<TArgs extends unknown[], TResult>(cache: CacheStack, prefix: string, resolver: (...args: TArgs) => Promise<TResult>, options?: GraphqlCacheOptions<TArgs>): (...args: TArgs) => Promise<TResult | null>;
|
|
500
|
+
|
|
501
|
+
interface TrpcCacheMiddlewareContext<TInput = unknown, TResult = unknown> {
|
|
502
|
+
path?: string;
|
|
503
|
+
type?: string;
|
|
504
|
+
rawInput?: TInput;
|
|
505
|
+
next: () => Promise<{
|
|
506
|
+
ok: boolean;
|
|
507
|
+
data?: TResult;
|
|
508
|
+
}>;
|
|
509
|
+
}
|
|
510
|
+
interface TrpcCacheMiddlewareOptions<TInput> extends CacheGetOptions {
|
|
511
|
+
keyResolver?: (input: TInput, path?: string, type?: string) => string;
|
|
512
|
+
}
|
|
513
|
+
declare function createTrpcCacheMiddleware<TInput = unknown, TResult = unknown>(cache: CacheStack, prefix: string, options?: TrpcCacheMiddlewareOptions<TInput>): (context: TrpcCacheMiddlewareContext<TInput, TResult>) => Promise<{
|
|
514
|
+
ok: boolean;
|
|
515
|
+
data?: TResult;
|
|
516
|
+
} | null>;
|
|
517
|
+
|
|
518
|
+
interface MemoryLayerSnapshotEntry {
|
|
519
|
+
key: string;
|
|
520
|
+
value: unknown;
|
|
521
|
+
expiresAt: number | null;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Eviction policy applied when `maxSize` is reached.
|
|
525
|
+
* - `lru` (default): evicts the Least Recently Used entry.
|
|
526
|
+
* - `lfu`: evicts the Least Frequently Used entry.
|
|
527
|
+
* - `fifo`: evicts the oldest inserted entry.
|
|
528
|
+
*/
|
|
529
|
+
type EvictionPolicy = 'lru' | 'lfu' | 'fifo';
|
|
205
530
|
interface MemoryLayerOptions {
|
|
206
531
|
ttl?: number;
|
|
207
532
|
maxSize?: number;
|
|
208
533
|
name?: string;
|
|
534
|
+
evictionPolicy?: EvictionPolicy;
|
|
209
535
|
}
|
|
210
536
|
declare class MemoryLayer implements CacheLayer {
|
|
211
537
|
readonly name: string;
|
|
212
538
|
readonly defaultTtl?: number;
|
|
213
539
|
readonly isLocal = true;
|
|
214
540
|
private readonly maxSize;
|
|
541
|
+
private readonly evictionPolicy;
|
|
215
542
|
private readonly entries;
|
|
216
543
|
constructor(options?: MemoryLayerOptions);
|
|
217
544
|
get<T>(key: string): Promise<T | null>;
|
|
218
545
|
getEntry<T = unknown>(key: string): Promise<T | null>;
|
|
219
546
|
getMany<T>(keys: string[]): Promise<Array<T | null>>;
|
|
220
547
|
set(key: string, value: unknown, ttl?: number | undefined): Promise<void>;
|
|
548
|
+
has(key: string): Promise<boolean>;
|
|
549
|
+
ttl(key: string): Promise<number | null>;
|
|
550
|
+
size(): Promise<number>;
|
|
221
551
|
delete(key: string): Promise<void>;
|
|
222
552
|
deleteMany(keys: string[]): Promise<void>;
|
|
223
553
|
clear(): Promise<void>;
|
|
224
554
|
keys(): Promise<string[]>;
|
|
555
|
+
exportState(): MemoryLayerSnapshotEntry[];
|
|
556
|
+
importState(entries: MemoryLayerSnapshotEntry[]): void;
|
|
557
|
+
private evict;
|
|
225
558
|
private pruneExpired;
|
|
226
559
|
private isExpired;
|
|
227
560
|
}
|
|
228
561
|
|
|
562
|
+
type CompressionAlgorithm = 'gzip' | 'brotli';
|
|
229
563
|
interface RedisLayerOptions {
|
|
230
564
|
client: Redis;
|
|
231
565
|
ttl?: number;
|
|
@@ -234,6 +568,8 @@ interface RedisLayerOptions {
|
|
|
234
568
|
prefix?: string;
|
|
235
569
|
allowUnprefixedClear?: boolean;
|
|
236
570
|
scanCount?: number;
|
|
571
|
+
compression?: CompressionAlgorithm;
|
|
572
|
+
compressionThreshold?: number;
|
|
237
573
|
}
|
|
238
574
|
declare class RedisLayer implements CacheLayer {
|
|
239
575
|
readonly name: string;
|
|
@@ -244,6 +580,8 @@ declare class RedisLayer implements CacheLayer {
|
|
|
244
580
|
private readonly prefix;
|
|
245
581
|
private readonly allowUnprefixedClear;
|
|
246
582
|
private readonly scanCount;
|
|
583
|
+
private readonly compression?;
|
|
584
|
+
private readonly compressionThreshold;
|
|
247
585
|
constructor(options: RedisLayerOptions);
|
|
248
586
|
get<T>(key: string): Promise<T | null>;
|
|
249
587
|
getEntry<T = unknown>(key: string): Promise<T | null>;
|
|
@@ -251,10 +589,109 @@ declare class RedisLayer implements CacheLayer {
|
|
|
251
589
|
set(key: string, value: unknown, ttl?: number | undefined): Promise<void>;
|
|
252
590
|
delete(key: string): Promise<void>;
|
|
253
591
|
deleteMany(keys: string[]): Promise<void>;
|
|
592
|
+
has(key: string): Promise<boolean>;
|
|
593
|
+
ttl(key: string): Promise<number | null>;
|
|
594
|
+
size(): Promise<number>;
|
|
595
|
+
/**
|
|
596
|
+
* Deletes all keys matching the layer's prefix in batches to avoid
|
|
597
|
+
* loading millions of keys into memory at once.
|
|
598
|
+
*/
|
|
254
599
|
clear(): Promise<void>;
|
|
255
600
|
keys(): Promise<string[]>;
|
|
256
601
|
private scanKeys;
|
|
257
602
|
private withPrefix;
|
|
603
|
+
private deserializeOrDelete;
|
|
604
|
+
private isSerializablePayload;
|
|
605
|
+
private encodePayload;
|
|
606
|
+
private decodePayload;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
interface DiskLayerOptions {
|
|
610
|
+
directory: string;
|
|
611
|
+
ttl?: number;
|
|
612
|
+
name?: string;
|
|
613
|
+
serializer?: CacheSerializer;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* A file-system backed cache layer.
|
|
617
|
+
* Each key is stored as a separate JSON file in `directory`.
|
|
618
|
+
* Useful for persisting cache across process restarts without needing Redis.
|
|
619
|
+
*
|
|
620
|
+
* NOTE: DiskLayer is designed for low-to-medium traffic scenarios.
|
|
621
|
+
* For high-throughput workloads, use MemoryLayer + RedisLayer.
|
|
622
|
+
*/
|
|
623
|
+
declare class DiskLayer implements CacheLayer {
|
|
624
|
+
readonly name: string;
|
|
625
|
+
readonly defaultTtl?: number;
|
|
626
|
+
readonly isLocal = true;
|
|
627
|
+
private readonly directory;
|
|
628
|
+
private readonly serializer;
|
|
629
|
+
constructor(options: DiskLayerOptions);
|
|
630
|
+
get<T>(key: string): Promise<T | null>;
|
|
631
|
+
getEntry<T = unknown>(key: string): Promise<T | null>;
|
|
632
|
+
set(key: string, value: unknown, ttl?: number | undefined): Promise<void>;
|
|
633
|
+
has(key: string): Promise<boolean>;
|
|
634
|
+
ttl(key: string): Promise<number | null>;
|
|
635
|
+
delete(key: string): Promise<void>;
|
|
636
|
+
deleteMany(keys: string[]): Promise<void>;
|
|
637
|
+
clear(): Promise<void>;
|
|
638
|
+
keys(): Promise<string[]>;
|
|
639
|
+
size(): Promise<number>;
|
|
640
|
+
private keyToPath;
|
|
641
|
+
private safeDelete;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Minimal interface that MemcachedLayer expects from a Memcached client.
|
|
646
|
+
* Compatible with the `memjs` and `memcache-client` npm packages.
|
|
647
|
+
*
|
|
648
|
+
* Install one of:
|
|
649
|
+
* npm install memjs
|
|
650
|
+
* npm install memcache-client
|
|
651
|
+
*/
|
|
652
|
+
interface MemcachedClient {
|
|
653
|
+
get(key: string): Promise<{
|
|
654
|
+
value: Buffer | null;
|
|
655
|
+
} | null>;
|
|
656
|
+
set(key: string, value: string | Buffer, options?: {
|
|
657
|
+
expires?: number;
|
|
658
|
+
}): Promise<boolean | undefined>;
|
|
659
|
+
delete(key: string): Promise<boolean | undefined>;
|
|
660
|
+
}
|
|
661
|
+
interface MemcachedLayerOptions {
|
|
662
|
+
client: MemcachedClient;
|
|
663
|
+
ttl?: number;
|
|
664
|
+
name?: string;
|
|
665
|
+
keyPrefix?: string;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Memcached-backed cache layer.
|
|
669
|
+
*
|
|
670
|
+
* Example usage with `memjs`:
|
|
671
|
+
* ```ts
|
|
672
|
+
* import Memjs from 'memjs'
|
|
673
|
+
* import { CacheStack, MemcachedLayer, MemoryLayer } from 'layercache'
|
|
674
|
+
*
|
|
675
|
+
* const memcached = Memjs.Client.create('localhost:11211')
|
|
676
|
+
* const cache = new CacheStack([
|
|
677
|
+
* new MemoryLayer({ ttl: 30 }),
|
|
678
|
+
* new MemcachedLayer({ client: memcached, ttl: 300 })
|
|
679
|
+
* ])
|
|
680
|
+
* ```
|
|
681
|
+
*/
|
|
682
|
+
declare class MemcachedLayer implements CacheLayer {
|
|
683
|
+
readonly name: string;
|
|
684
|
+
readonly defaultTtl?: number;
|
|
685
|
+
readonly isLocal = false;
|
|
686
|
+
private readonly client;
|
|
687
|
+
private readonly keyPrefix;
|
|
688
|
+
constructor(options: MemcachedLayerOptions);
|
|
689
|
+
get<T>(key: string): Promise<T | null>;
|
|
690
|
+
set(key: string, value: unknown, ttl?: number | undefined): Promise<void>;
|
|
691
|
+
delete(key: string): Promise<void>;
|
|
692
|
+
deleteMany(keys: string[]): Promise<void>;
|
|
693
|
+
clear(): Promise<void>;
|
|
694
|
+
private withPrefix;
|
|
258
695
|
}
|
|
259
696
|
|
|
260
697
|
declare class JsonSerializer implements CacheSerializer {
|
|
@@ -281,7 +718,28 @@ declare class RedisSingleFlightCoordinator implements CacheSingleFlightCoordinat
|
|
|
281
718
|
declare class StampedeGuard {
|
|
282
719
|
private readonly mutexes;
|
|
283
720
|
execute<T>(key: string, task: () => Promise<T>): Promise<T>;
|
|
284
|
-
private
|
|
721
|
+
private getMutexEntry;
|
|
285
722
|
}
|
|
286
723
|
|
|
287
|
-
|
|
724
|
+
/**
|
|
725
|
+
* Returns a function that generates a Prometheus-compatible text exposition
|
|
726
|
+
* of the cache metrics from one or more CacheStack instances.
|
|
727
|
+
*
|
|
728
|
+
* Usage example:
|
|
729
|
+
* ```ts
|
|
730
|
+
* const collect = createPrometheusMetricsExporter(cache)
|
|
731
|
+
* http.createServer(async (_req, res) => {
|
|
732
|
+
* res.setHeader('content-type', 'text/plain; version=0.0.4; charset=utf-8')
|
|
733
|
+
* res.end(collect())
|
|
734
|
+
* }).listen(9091)
|
|
735
|
+
* ```
|
|
736
|
+
*
|
|
737
|
+
* @param stacks One or more CacheStack instances. When multiple stacks are
|
|
738
|
+
* given, each must be named via the optional `name` parameter.
|
|
739
|
+
*/
|
|
740
|
+
declare function createPrometheusMetricsExporter(stacks: CacheStack | Array<{
|
|
741
|
+
stack: CacheStack;
|
|
742
|
+
name: string;
|
|
743
|
+
}>): () => string;
|
|
744
|
+
|
|
745
|
+
export { type CacheAdaptiveTtlOptions, type CacheCircuitBreakerOptions, type CacheDegradationOptions, type CacheGetOptions, type CacheHitRateSnapshot, type CacheLayer, type CacheLogger, type CacheMGetEntry, type CacheMSetEntry, type CacheMetricsSnapshot, CacheNamespace, type CacheSerializer, type CacheSingleFlightCoordinator, type CacheSingleFlightExecutionOptions, type CacheSnapshotEntry, CacheStack, type CacheStackEvents, type CacheStackOptions, type CacheStatsSnapshot, type CacheTagIndex, type CacheWarmEntry, type CacheWarmOptions, type CacheWarmProgress, type CacheWrapOptions, type CacheWriteOptions, DiskLayer, type EvictionPolicy, type InvalidationBus, type InvalidationMessage, JsonSerializer, type LayerTtlMap, type MemcachedClient, MemcachedLayer, MemoryLayer, type MemoryLayerSnapshotEntry, MsgpackSerializer, PatternMatcher, RedisInvalidationBus, RedisLayer, RedisSingleFlightCoordinator, RedisTagIndex, StampedeGuard, TagIndex, cacheGraphqlResolver, createCacheStatsHandler, createCachedMethodDecorator, createFastifyLayercachePlugin, createPrometheusMetricsExporter, createTrpcCacheMiddleware };
|