layercache 1.1.0 → 1.2.1
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 +165 -7
- package/dist/chunk-46UH7LNM.js +312 -0
- package/dist/{chunk-QUB5VZFZ.js → chunk-GF47Y3XR.js} +16 -38
- package/dist/chunk-ZMDB5KOK.js +159 -0
- package/dist/cli.cjs +133 -23
- package/dist/cli.js +66 -4
- package/dist/edge-C1sBhTfv.d.cts +667 -0
- package/dist/edge-C1sBhTfv.d.ts +667 -0
- package/dist/edge.cjs +399 -0
- package/dist/edge.d.cts +2 -0
- package/dist/edge.d.ts +2 -0
- package/dist/edge.js +14 -0
- package/dist/index.cjs +1259 -192
- package/dist/index.d.cts +132 -480
- package/dist/index.d.ts +132 -480
- package/dist/index.js +1115 -474
- package/package.json +7 -2
- package/packages/nestjs/dist/index.cjs +1025 -327
- package/packages/nestjs/dist/index.d.cts +167 -1
- package/packages/nestjs/dist/index.d.ts +167 -1
- package/packages/nestjs/dist/index.js +1013 -325
package/dist/index.d.ts
CHANGED
|
@@ -1,438 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { I as InvalidationBus, C as CacheLogger, a as InvalidationMessage, b as CacheTagIndex, c as CacheStack, d as CacheWrapOptions, e as CacheGetOptions, f as CacheLayer, g as CacheSerializer, h as CacheLayerSetManyEntry, i as CacheSingleFlightCoordinator, j as CacheSingleFlightExecutionOptions } from './edge-C1sBhTfv.js';
|
|
2
|
+
export { k as CacheAdaptiveTtlOptions, l as CacheCircuitBreakerOptions, m as CacheDegradationOptions, n as CacheHealthCheckResult, o as CacheHitRateSnapshot, p as CacheInspectResult, q as CacheLayerLatency, r as CacheMGetEntry, s as CacheMSetEntry, t as CacheMetricsSnapshot, u as CacheMissError, v as CacheNamespace, w as CacheRateLimitOptions, x as CacheSnapshotEntry, y as CacheStackEvents, z as CacheStackOptions, A as CacheStatsSnapshot, B as CacheTtlPolicy, D as CacheTtlPolicyContext, E as CacheWarmEntry, F as CacheWarmOptions, G as CacheWarmProgress, H as CacheWriteBehindOptions, J as CacheWriteOptions, K as EvictionPolicy, L as LayerTtlMap, M as MemoryLayer, N as MemoryLayerOptions, O as MemoryLayerSnapshotEntry, P as PatternMatcher, T as TagIndex, Q as createHonoCacheMiddleware } from './edge-C1sBhTfv.js';
|
|
2
3
|
import Redis from 'ioredis';
|
|
3
|
-
|
|
4
|
-
interface LayerTtlMap {
|
|
5
|
-
[layerName: string]: number | undefined;
|
|
6
|
-
}
|
|
7
|
-
interface CacheWriteOptions {
|
|
8
|
-
tags?: string[];
|
|
9
|
-
ttl?: number | LayerTtlMap;
|
|
10
|
-
negativeCache?: boolean;
|
|
11
|
-
negativeTtl?: number | LayerTtlMap;
|
|
12
|
-
staleWhileRevalidate?: number | LayerTtlMap;
|
|
13
|
-
staleIfError?: number | LayerTtlMap;
|
|
14
|
-
ttlJitter?: number | LayerTtlMap;
|
|
15
|
-
slidingTtl?: boolean;
|
|
16
|
-
refreshAhead?: number | LayerTtlMap;
|
|
17
|
-
adaptiveTtl?: boolean | CacheAdaptiveTtlOptions;
|
|
18
|
-
circuitBreaker?: CacheCircuitBreakerOptions;
|
|
19
|
-
}
|
|
20
|
-
interface CacheGetOptions extends CacheWriteOptions {
|
|
21
|
-
}
|
|
22
|
-
interface CacheMGetEntry<T> {
|
|
23
|
-
key: string;
|
|
24
|
-
fetch?: () => Promise<T>;
|
|
25
|
-
options?: CacheGetOptions;
|
|
26
|
-
}
|
|
27
|
-
interface CacheMSetEntry<T> {
|
|
28
|
-
key: string;
|
|
29
|
-
value: T;
|
|
30
|
-
options?: CacheWriteOptions;
|
|
31
|
-
}
|
|
32
|
-
/** Interface that all cache backend implementations must satisfy. */
|
|
33
|
-
interface CacheLayer {
|
|
34
|
-
readonly name: string;
|
|
35
|
-
readonly defaultTtl?: number;
|
|
36
|
-
readonly isLocal?: boolean;
|
|
37
|
-
get<T>(key: string): Promise<T | null>;
|
|
38
|
-
getEntry?<T = unknown>(key: string): Promise<T | null>;
|
|
39
|
-
getMany?<T>(keys: string[]): Promise<Array<T | null>>;
|
|
40
|
-
set(key: string, value: unknown, ttl?: number): Promise<void>;
|
|
41
|
-
delete(key: string): Promise<void>;
|
|
42
|
-
clear(): Promise<void>;
|
|
43
|
-
deleteMany?(keys: string[]): Promise<void>;
|
|
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>;
|
|
61
|
-
}
|
|
62
|
-
interface CacheSerializer {
|
|
63
|
-
serialize(value: unknown): string | Buffer;
|
|
64
|
-
deserialize<T>(payload: string | Buffer): T;
|
|
65
|
-
}
|
|
66
|
-
/** Snapshot of cumulative cache counters. */
|
|
67
|
-
interface CacheMetricsSnapshot {
|
|
68
|
-
hits: number;
|
|
69
|
-
misses: number;
|
|
70
|
-
fetches: number;
|
|
71
|
-
sets: number;
|
|
72
|
-
deletes: number;
|
|
73
|
-
backfills: number;
|
|
74
|
-
invalidations: number;
|
|
75
|
-
staleHits: number;
|
|
76
|
-
refreshes: number;
|
|
77
|
-
refreshErrors: number;
|
|
78
|
-
writeFailures: number;
|
|
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>;
|
|
94
|
-
}
|
|
95
|
-
interface CacheLogger {
|
|
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;
|
|
100
|
-
}
|
|
101
|
-
interface CacheTagIndex {
|
|
102
|
-
touch(key: string): Promise<void>;
|
|
103
|
-
track(key: string, tags: string[]): Promise<void>;
|
|
104
|
-
remove(key: string): Promise<void>;
|
|
105
|
-
keysForTag(tag: string): Promise<string[]>;
|
|
106
|
-
matchPattern(pattern: string): Promise<string[]>;
|
|
107
|
-
clear(): Promise<void>;
|
|
108
|
-
}
|
|
109
|
-
interface InvalidationMessage {
|
|
110
|
-
scope: 'key' | 'keys' | 'clear';
|
|
111
|
-
sourceId: string;
|
|
112
|
-
keys?: string[];
|
|
113
|
-
operation?: 'write' | 'delete' | 'invalidate' | 'clear';
|
|
114
|
-
}
|
|
115
|
-
interface InvalidationBus {
|
|
116
|
-
subscribe(handler: (message: InvalidationMessage) => Promise<void> | void): Promise<() => Promise<void> | void>;
|
|
117
|
-
publish(message: InvalidationMessage): Promise<void>;
|
|
118
|
-
}
|
|
119
|
-
interface CacheSingleFlightExecutionOptions {
|
|
120
|
-
leaseMs: number;
|
|
121
|
-
waitTimeoutMs: number;
|
|
122
|
-
pollIntervalMs: number;
|
|
123
|
-
}
|
|
124
|
-
interface CacheSingleFlightCoordinator {
|
|
125
|
-
execute<T>(key: string, options: CacheSingleFlightExecutionOptions, worker: () => Promise<T>, waiter: () => Promise<T>): Promise<T>;
|
|
126
|
-
}
|
|
127
|
-
interface CacheStackOptions {
|
|
128
|
-
logger?: CacheLogger | boolean;
|
|
129
|
-
metrics?: boolean;
|
|
130
|
-
stampedePrevention?: boolean;
|
|
131
|
-
invalidationBus?: InvalidationBus;
|
|
132
|
-
tagIndex?: CacheTagIndex;
|
|
133
|
-
broadcastL1Invalidation?: boolean;
|
|
134
|
-
/**
|
|
135
|
-
* @deprecated Use `broadcastL1Invalidation` instead.
|
|
136
|
-
*/
|
|
137
|
-
publishSetInvalidation?: boolean;
|
|
138
|
-
negativeCaching?: boolean;
|
|
139
|
-
negativeTtl?: number | LayerTtlMap;
|
|
140
|
-
staleWhileRevalidate?: number | LayerTtlMap;
|
|
141
|
-
staleIfError?: number | LayerTtlMap;
|
|
142
|
-
ttlJitter?: number | LayerTtlMap;
|
|
143
|
-
refreshAhead?: number | LayerTtlMap;
|
|
144
|
-
adaptiveTtl?: boolean | CacheAdaptiveTtlOptions;
|
|
145
|
-
circuitBreaker?: CacheCircuitBreakerOptions;
|
|
146
|
-
gracefulDegradation?: boolean | CacheDegradationOptions;
|
|
147
|
-
writePolicy?: 'strict' | 'best-effort';
|
|
148
|
-
singleFlightCoordinator?: CacheSingleFlightCoordinator;
|
|
149
|
-
singleFlightLeaseMs?: number;
|
|
150
|
-
singleFlightTimeoutMs?: number;
|
|
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
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
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 {
|
|
288
|
-
private readonly layers;
|
|
289
|
-
private readonly options;
|
|
290
|
-
private readonly stampedeGuard;
|
|
291
|
-
private readonly metricsCollector;
|
|
292
|
-
private readonly instanceId;
|
|
293
|
-
private readonly startup;
|
|
294
|
-
private unsubscribeInvalidation?;
|
|
295
|
-
private readonly logger;
|
|
296
|
-
private readonly tagIndex;
|
|
297
|
-
private readonly backgroundRefreshes;
|
|
298
|
-
private readonly layerDegradedUntil;
|
|
299
|
-
private readonly ttlResolver;
|
|
300
|
-
private readonly circuitBreakerManager;
|
|
301
|
-
private isDisconnecting;
|
|
302
|
-
private disconnectPromise?;
|
|
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
|
-
*/
|
|
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
|
-
*/
|
|
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
|
-
*/
|
|
332
|
-
delete(key: string): Promise<void>;
|
|
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>;
|
|
338
|
-
mget<T>(entries: CacheMGetEntry<T>[]): Promise<Array<T | null>>;
|
|
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;
|
|
351
|
-
invalidateByTag(tag: string): Promise<void>;
|
|
352
|
-
invalidateByPattern(pattern: string): Promise<void>;
|
|
353
|
-
getMetrics(): CacheMetricsSnapshot;
|
|
354
|
-
getStats(): CacheStatsSnapshot;
|
|
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>;
|
|
364
|
-
disconnect(): Promise<void>;
|
|
365
|
-
private initialize;
|
|
366
|
-
private fetchWithGuards;
|
|
367
|
-
private waitForFreshValue;
|
|
368
|
-
private fetchAndPopulate;
|
|
369
|
-
private storeEntry;
|
|
370
|
-
private readFromLayers;
|
|
371
|
-
private readLayerEntry;
|
|
372
|
-
private backfill;
|
|
373
|
-
private writeAcrossLayers;
|
|
374
|
-
private executeLayerOperations;
|
|
375
|
-
private resolveFreshTtl;
|
|
376
|
-
private resolveLayerSeconds;
|
|
377
|
-
private shouldNegativeCache;
|
|
378
|
-
private scheduleBackgroundRefresh;
|
|
379
|
-
private resolveSingleFlightOptions;
|
|
380
|
-
private deleteKeys;
|
|
381
|
-
private publishInvalidation;
|
|
382
|
-
private handleInvalidationMessage;
|
|
383
|
-
private formatError;
|
|
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;
|
|
406
|
-
}
|
|
407
|
-
|
|
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
|
-
*/
|
|
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;
|
|
420
|
-
}
|
|
4
|
+
import 'node:events';
|
|
421
5
|
|
|
422
6
|
interface RedisInvalidationBusOptions {
|
|
423
7
|
publisher: Redis;
|
|
424
8
|
subscriber?: Redis;
|
|
425
9
|
channel?: string;
|
|
10
|
+
logger?: CacheLogger;
|
|
426
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Redis pub/sub invalidation bus.
|
|
14
|
+
*
|
|
15
|
+
* Supports multiple concurrent subscriptions — each `CacheStack` instance
|
|
16
|
+
* can independently call `subscribe()` and receive its own unsubscribe handle.
|
|
17
|
+
* The underlying Redis SUBSCRIBE is only issued once and shared across all handlers.
|
|
18
|
+
*/
|
|
427
19
|
declare class RedisInvalidationBus implements InvalidationBus {
|
|
428
20
|
private readonly channel;
|
|
429
21
|
private readonly publisher;
|
|
430
22
|
private readonly subscriber;
|
|
431
|
-
private
|
|
23
|
+
private readonly logger?;
|
|
24
|
+
private readonly handlers;
|
|
25
|
+
private sharedListener?;
|
|
432
26
|
constructor(options: RedisInvalidationBusOptions);
|
|
433
27
|
subscribe(handler: (message: InvalidationMessage) => Promise<void> | void): Promise<() => Promise<void>>;
|
|
434
28
|
publish(message: InvalidationMessage): Promise<void>;
|
|
435
|
-
private
|
|
29
|
+
private dispatchToHandlers;
|
|
436
30
|
private isInvalidationMessage;
|
|
437
31
|
private reportError;
|
|
438
32
|
}
|
|
@@ -451,6 +45,8 @@ declare class RedisTagIndex implements CacheTagIndex {
|
|
|
451
45
|
track(key: string, tags: string[]): Promise<void>;
|
|
452
46
|
remove(key: string): Promise<void>;
|
|
453
47
|
keysForTag(tag: string): Promise<string[]>;
|
|
48
|
+
keysForPrefix(prefix: string): Promise<string[]>;
|
|
49
|
+
tagsForKey(key: string): Promise<string[]>;
|
|
454
50
|
matchPattern(pattern: string): Promise<string[]>;
|
|
455
51
|
clear(): Promise<void>;
|
|
456
52
|
private scanIndexKeys;
|
|
@@ -459,18 +55,6 @@ declare class RedisTagIndex implements CacheTagIndex {
|
|
|
459
55
|
private tagKeysKey;
|
|
460
56
|
}
|
|
461
57
|
|
|
462
|
-
declare class TagIndex implements CacheTagIndex {
|
|
463
|
-
private readonly tagToKeys;
|
|
464
|
-
private readonly keyToTags;
|
|
465
|
-
private readonly knownKeys;
|
|
466
|
-
touch(key: string): Promise<void>;
|
|
467
|
-
track(key: string, tags: string[]): Promise<void>;
|
|
468
|
-
remove(key: string): Promise<void>;
|
|
469
|
-
keysForTag(tag: string): Promise<string[]>;
|
|
470
|
-
matchPattern(pattern: string): Promise<string[]>;
|
|
471
|
-
clear(): Promise<void>;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
58
|
declare function createCacheStatsHandler(cache: CacheStack): (_request: unknown, response: {
|
|
475
59
|
setHeader?: (name: string, value: string) => void;
|
|
476
60
|
end: (body: string) => void;
|
|
@@ -493,11 +77,74 @@ interface FastifyLayercachePluginOptions {
|
|
|
493
77
|
}
|
|
494
78
|
declare function createFastifyLayercachePlugin(cache: CacheStack, options?: FastifyLayercachePluginOptions): (fastify: FastifyLike) => Promise<void>;
|
|
495
79
|
|
|
80
|
+
interface ExpressLikeRequest {
|
|
81
|
+
method?: string;
|
|
82
|
+
url?: string;
|
|
83
|
+
originalUrl?: string;
|
|
84
|
+
path?: string;
|
|
85
|
+
query?: Record<string, unknown>;
|
|
86
|
+
}
|
|
87
|
+
interface ExpressLikeResponse {
|
|
88
|
+
statusCode?: number;
|
|
89
|
+
setHeader?: (name: string, value: string) => void;
|
|
90
|
+
json?: (body: unknown) => void;
|
|
91
|
+
end?: (body?: string) => void;
|
|
92
|
+
}
|
|
93
|
+
type NextFunction = (error?: unknown) => void;
|
|
94
|
+
interface ExpressCacheMiddlewareOptions extends CacheGetOptions {
|
|
95
|
+
/**
|
|
96
|
+
* Resolves a cache key from the incoming request. Defaults to
|
|
97
|
+
* `GET:<req.originalUrl || req.url>`.
|
|
98
|
+
*/
|
|
99
|
+
keyResolver?: (req: ExpressLikeRequest) => string;
|
|
100
|
+
/**
|
|
101
|
+
* Only cache responses for these HTTP methods. Defaults to `['GET']`.
|
|
102
|
+
*/
|
|
103
|
+
methods?: string[];
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Express/Connect-compatible middleware that caches JSON responses.
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* import express from 'express'
|
|
110
|
+
* import { CacheStack, MemoryLayer, createExpressCacheMiddleware } from 'layercache'
|
|
111
|
+
*
|
|
112
|
+
* const cache = new CacheStack([new MemoryLayer({ ttl: 60 })])
|
|
113
|
+
* const app = express()
|
|
114
|
+
*
|
|
115
|
+
* app.get('/api/data', createExpressCacheMiddleware(cache, { ttl: 30 }), (req, res) => {
|
|
116
|
+
* res.json({ fresh: true })
|
|
117
|
+
* })
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
declare function createExpressCacheMiddleware(cache: CacheStack, options?: ExpressCacheMiddlewareOptions): (req: ExpressLikeRequest, res: ExpressLikeResponse, next: NextFunction) => Promise<void>;
|
|
121
|
+
|
|
496
122
|
interface GraphqlCacheOptions<TArgs extends unknown[]> extends CacheGetOptions {
|
|
497
123
|
keyResolver?: (...args: TArgs) => string;
|
|
498
124
|
}
|
|
499
125
|
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
126
|
|
|
127
|
+
interface OpenTelemetrySpan {
|
|
128
|
+
setAttribute?: (name: string, value: unknown) => void;
|
|
129
|
+
recordException?: (error: unknown) => void;
|
|
130
|
+
end: () => void;
|
|
131
|
+
}
|
|
132
|
+
interface OpenTelemetryTracer {
|
|
133
|
+
startSpan: (name: string, options?: {
|
|
134
|
+
attributes?: Record<string, unknown>;
|
|
135
|
+
}) => OpenTelemetrySpan;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Lightweight OpenTelemetry instrumentation for a CacheStack instance.
|
|
139
|
+
*
|
|
140
|
+
* Note: this implementation wraps instance methods directly. Avoid stacking
|
|
141
|
+
* multiple OpenTelemetry plugins on the same CacheStack at the same time,
|
|
142
|
+
* because each plugin replaces and later restores those methods.
|
|
143
|
+
*/
|
|
144
|
+
declare function createOpenTelemetryPlugin(cache: CacheStack, tracer: OpenTelemetryTracer): {
|
|
145
|
+
uninstall(): void;
|
|
146
|
+
};
|
|
147
|
+
|
|
501
148
|
interface TrpcCacheMiddlewareContext<TInput = unknown, TResult = unknown> {
|
|
502
149
|
path?: string;
|
|
503
150
|
type?: string;
|
|
@@ -515,83 +162,44 @@ declare function createTrpcCacheMiddleware<TInput = unknown, TResult = unknown>(
|
|
|
515
162
|
data?: TResult;
|
|
516
163
|
} | null>;
|
|
517
164
|
|
|
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';
|
|
530
|
-
interface MemoryLayerOptions {
|
|
531
|
-
ttl?: number;
|
|
532
|
-
maxSize?: number;
|
|
533
|
-
name?: string;
|
|
534
|
-
evictionPolicy?: EvictionPolicy;
|
|
535
|
-
}
|
|
536
|
-
declare class MemoryLayer implements CacheLayer {
|
|
537
|
-
readonly name: string;
|
|
538
|
-
readonly defaultTtl?: number;
|
|
539
|
-
readonly isLocal = true;
|
|
540
|
-
private readonly maxSize;
|
|
541
|
-
private readonly evictionPolicy;
|
|
542
|
-
private readonly entries;
|
|
543
|
-
constructor(options?: MemoryLayerOptions);
|
|
544
|
-
get<T>(key: string): Promise<T | null>;
|
|
545
|
-
getEntry<T = unknown>(key: string): Promise<T | null>;
|
|
546
|
-
getMany<T>(keys: string[]): Promise<Array<T | null>>;
|
|
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>;
|
|
551
|
-
delete(key: string): Promise<void>;
|
|
552
|
-
deleteMany(keys: string[]): Promise<void>;
|
|
553
|
-
clear(): Promise<void>;
|
|
554
|
-
keys(): Promise<string[]>;
|
|
555
|
-
exportState(): MemoryLayerSnapshotEntry[];
|
|
556
|
-
importState(entries: MemoryLayerSnapshotEntry[]): void;
|
|
557
|
-
private evict;
|
|
558
|
-
private pruneExpired;
|
|
559
|
-
private isExpired;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
165
|
type CompressionAlgorithm = 'gzip' | 'brotli';
|
|
563
166
|
interface RedisLayerOptions {
|
|
564
167
|
client: Redis;
|
|
565
168
|
ttl?: number;
|
|
566
169
|
name?: string;
|
|
567
|
-
serializer?: CacheSerializer;
|
|
170
|
+
serializer?: CacheSerializer | CacheSerializer[];
|
|
568
171
|
prefix?: string;
|
|
569
172
|
allowUnprefixedClear?: boolean;
|
|
570
173
|
scanCount?: number;
|
|
571
174
|
compression?: CompressionAlgorithm;
|
|
572
175
|
compressionThreshold?: number;
|
|
176
|
+
disconnectOnDispose?: boolean;
|
|
573
177
|
}
|
|
574
178
|
declare class RedisLayer implements CacheLayer {
|
|
575
179
|
readonly name: string;
|
|
576
180
|
readonly defaultTtl?: number;
|
|
577
181
|
readonly isLocal = false;
|
|
578
182
|
private readonly client;
|
|
579
|
-
private readonly
|
|
183
|
+
private readonly serializers;
|
|
580
184
|
private readonly prefix;
|
|
581
185
|
private readonly allowUnprefixedClear;
|
|
582
186
|
private readonly scanCount;
|
|
583
187
|
private readonly compression?;
|
|
584
188
|
private readonly compressionThreshold;
|
|
189
|
+
private readonly disconnectOnDispose;
|
|
585
190
|
constructor(options: RedisLayerOptions);
|
|
586
191
|
get<T>(key: string): Promise<T | null>;
|
|
587
192
|
getEntry<T = unknown>(key: string): Promise<T | null>;
|
|
588
193
|
getMany<T>(keys: string[]): Promise<Array<T | null>>;
|
|
194
|
+
setMany(entries: CacheLayerSetManyEntry[]): Promise<void>;
|
|
589
195
|
set(key: string, value: unknown, ttl?: number | undefined): Promise<void>;
|
|
590
196
|
delete(key: string): Promise<void>;
|
|
591
197
|
deleteMany(keys: string[]): Promise<void>;
|
|
592
198
|
has(key: string): Promise<boolean>;
|
|
593
199
|
ttl(key: string): Promise<number | null>;
|
|
594
200
|
size(): Promise<number>;
|
|
201
|
+
ping(): Promise<boolean>;
|
|
202
|
+
dispose(): Promise<void>;
|
|
595
203
|
/**
|
|
596
204
|
* Deletes all keys matching the layer's prefix in batches to avoid
|
|
597
205
|
* loading millions of keys into memory at once.
|
|
@@ -601,8 +209,17 @@ declare class RedisLayer implements CacheLayer {
|
|
|
601
209
|
private scanKeys;
|
|
602
210
|
private withPrefix;
|
|
603
211
|
private deserializeOrDelete;
|
|
212
|
+
private rewriteWithPrimarySerializer;
|
|
213
|
+
private primarySerializer;
|
|
604
214
|
private isSerializablePayload;
|
|
215
|
+
/**
|
|
216
|
+
* Compresses the payload asynchronously if compression is enabled and the
|
|
217
|
+
* payload exceeds the threshold. This avoids blocking the event loop.
|
|
218
|
+
*/
|
|
605
219
|
private encodePayload;
|
|
220
|
+
/**
|
|
221
|
+
* Decompresses the payload asynchronously if a compression header is present.
|
|
222
|
+
*/
|
|
606
223
|
private decodePayload;
|
|
607
224
|
}
|
|
608
225
|
|
|
@@ -611,12 +228,21 @@ interface DiskLayerOptions {
|
|
|
611
228
|
ttl?: number;
|
|
612
229
|
name?: string;
|
|
613
230
|
serializer?: CacheSerializer;
|
|
231
|
+
/**
|
|
232
|
+
* Maximum number of cache files to store on disk. When exceeded, the oldest
|
|
233
|
+
* entries (by file mtime) are evicted to keep the directory bounded.
|
|
234
|
+
* Defaults to unlimited.
|
|
235
|
+
*/
|
|
236
|
+
maxFiles?: number;
|
|
614
237
|
}
|
|
615
238
|
/**
|
|
616
239
|
* A file-system backed cache layer.
|
|
617
240
|
* Each key is stored as a separate JSON file in `directory`.
|
|
618
241
|
* Useful for persisting cache across process restarts without needing Redis.
|
|
619
242
|
*
|
|
243
|
+
* - `keys()` returns the original cache key strings (not hashes).
|
|
244
|
+
* - `maxFiles` limits on-disk entries; when exceeded, oldest files are evicted.
|
|
245
|
+
*
|
|
620
246
|
* NOTE: DiskLayer is designed for low-to-medium traffic scenarios.
|
|
621
247
|
* For high-throughput workloads, use MemoryLayer + RedisLayer.
|
|
622
248
|
*/
|
|
@@ -626,19 +252,34 @@ declare class DiskLayer implements CacheLayer {
|
|
|
626
252
|
readonly isLocal = true;
|
|
627
253
|
private readonly directory;
|
|
628
254
|
private readonly serializer;
|
|
255
|
+
private readonly maxFiles;
|
|
256
|
+
private writeQueue;
|
|
629
257
|
constructor(options: DiskLayerOptions);
|
|
630
258
|
get<T>(key: string): Promise<T | null>;
|
|
631
259
|
getEntry<T = unknown>(key: string): Promise<T | null>;
|
|
632
260
|
set(key: string, value: unknown, ttl?: number | undefined): Promise<void>;
|
|
261
|
+
getMany<T>(keys: string[]): Promise<Array<T | null>>;
|
|
262
|
+
setMany(entries: CacheLayerSetManyEntry[]): Promise<void>;
|
|
633
263
|
has(key: string): Promise<boolean>;
|
|
634
264
|
ttl(key: string): Promise<number | null>;
|
|
635
265
|
delete(key: string): Promise<void>;
|
|
636
266
|
deleteMany(keys: string[]): Promise<void>;
|
|
637
267
|
clear(): Promise<void>;
|
|
268
|
+
/**
|
|
269
|
+
* Returns the original cache key strings stored on disk.
|
|
270
|
+
* Expired entries are skipped and cleaned up during the scan.
|
|
271
|
+
*/
|
|
638
272
|
keys(): Promise<string[]>;
|
|
639
273
|
size(): Promise<number>;
|
|
274
|
+
ping(): Promise<boolean>;
|
|
275
|
+
dispose(): Promise<void>;
|
|
640
276
|
private keyToPath;
|
|
641
277
|
private safeDelete;
|
|
278
|
+
private enqueueWrite;
|
|
279
|
+
/**
|
|
280
|
+
* Removes the oldest files (by mtime) when the directory exceeds maxFiles.
|
|
281
|
+
*/
|
|
282
|
+
private enforceMaxFiles;
|
|
642
283
|
}
|
|
643
284
|
|
|
644
285
|
/**
|
|
@@ -663,10 +304,14 @@ interface MemcachedLayerOptions {
|
|
|
663
304
|
ttl?: number;
|
|
664
305
|
name?: string;
|
|
665
306
|
keyPrefix?: string;
|
|
307
|
+
serializer?: CacheSerializer;
|
|
666
308
|
}
|
|
667
309
|
/**
|
|
668
310
|
* Memcached-backed cache layer.
|
|
669
311
|
*
|
|
312
|
+
* Now supports pluggable serializers (default: JSON), StoredValueEnvelope
|
|
313
|
+
* for stale-while-revalidate / stale-if-error semantics, and bulk reads.
|
|
314
|
+
*
|
|
670
315
|
* Example usage with `memjs`:
|
|
671
316
|
* ```ts
|
|
672
317
|
* import Memjs from 'memjs'
|
|
@@ -685,9 +330,13 @@ declare class MemcachedLayer implements CacheLayer {
|
|
|
685
330
|
readonly isLocal = false;
|
|
686
331
|
private readonly client;
|
|
687
332
|
private readonly keyPrefix;
|
|
333
|
+
private readonly serializer;
|
|
688
334
|
constructor(options: MemcachedLayerOptions);
|
|
689
335
|
get<T>(key: string): Promise<T | null>;
|
|
336
|
+
getEntry<T = unknown>(key: string): Promise<T | null>;
|
|
337
|
+
getMany<T>(keys: string[]): Promise<Array<T | null>>;
|
|
690
338
|
set(key: string, value: unknown, ttl?: number | undefined): Promise<void>;
|
|
339
|
+
has(key: string): Promise<boolean>;
|
|
691
340
|
delete(key: string): Promise<void>;
|
|
692
341
|
deleteMany(keys: string[]): Promise<void>;
|
|
693
342
|
clear(): Promise<void>;
|
|
@@ -725,6 +374,9 @@ declare class StampedeGuard {
|
|
|
725
374
|
* Returns a function that generates a Prometheus-compatible text exposition
|
|
726
375
|
* of the cache metrics from one or more CacheStack instances.
|
|
727
376
|
*
|
|
377
|
+
* Now includes per-layer latency gauges (`layercache_layer_latency_avg_ms`,
|
|
378
|
+
* `layercache_layer_latency_max_ms`, `layercache_layer_latency_count`).
|
|
379
|
+
*
|
|
728
380
|
* Usage example:
|
|
729
381
|
* ```ts
|
|
730
382
|
* const collect = createPrometheusMetricsExporter(cache)
|
|
@@ -742,4 +394,4 @@ declare function createPrometheusMetricsExporter(stacks: CacheStack | Array<{
|
|
|
742
394
|
name: string;
|
|
743
395
|
}>): () => string;
|
|
744
396
|
|
|
745
|
-
export {
|
|
397
|
+
export { CacheGetOptions, CacheLayer, CacheLayerSetManyEntry, CacheLogger, CacheSerializer, CacheSingleFlightCoordinator, CacheSingleFlightExecutionOptions, CacheStack, CacheTagIndex, CacheWrapOptions, DiskLayer, InvalidationBus, InvalidationMessage, JsonSerializer, type MemcachedClient, MemcachedLayer, MsgpackSerializer, RedisInvalidationBus, RedisLayer, RedisSingleFlightCoordinator, RedisTagIndex, StampedeGuard, cacheGraphqlResolver, createCacheStatsHandler, createCachedMethodDecorator, createExpressCacheMiddleware, createFastifyLayercachePlugin, createOpenTelemetryPlugin, createPrometheusMetricsExporter, createTrpcCacheMiddleware };
|