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/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 CacheStack {
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 metrics;
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 getMutex;
721
+ private getMutexEntry;
285
722
  }
286
723
 
287
- export { type CacheGetOptions, type CacheLayer, type CacheLogger, type CacheMGetEntry, type CacheMSetEntry, type CacheMetricsSnapshot, type CacheSerializer, type CacheSingleFlightCoordinator, type CacheSingleFlightExecutionOptions, CacheStack, type CacheStackOptions, type CacheTagIndex, type CacheWriteOptions, type InvalidationBus, type InvalidationMessage, JsonSerializer, type LayerTtlMap, MemoryLayer, MsgpackSerializer, PatternMatcher, RedisInvalidationBus, RedisLayer, RedisSingleFlightCoordinator, RedisTagIndex, StampedeGuard, TagIndex };
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 };