layercache 1.0.0 → 1.0.2

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 {
@@ -6,6 +7,15 @@ interface LayerTtlMap {
6
7
  interface CacheWriteOptions {
7
8
  tags?: string[];
8
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;
9
19
  }
10
20
  interface CacheGetOptions extends CacheWriteOptions {
11
21
  }
@@ -24,6 +34,8 @@ interface CacheLayer {
24
34
  readonly defaultTtl?: number;
25
35
  readonly isLocal?: boolean;
26
36
  get<T>(key: string): Promise<T | null>;
37
+ getEntry?<T = unknown>(key: string): Promise<T | null>;
38
+ getMany?<T>(keys: string[]): Promise<Array<T | null>>;
27
39
  set(key: string, value: unknown, ttl?: number): Promise<void>;
28
40
  delete(key: string): Promise<void>;
29
41
  clear(): Promise<void>;
@@ -42,9 +54,22 @@ interface CacheMetricsSnapshot {
42
54
  deletes: number;
43
55
  backfills: number;
44
56
  invalidations: number;
57
+ staleHits: number;
58
+ refreshes: number;
59
+ refreshErrors: number;
60
+ writeFailures: number;
61
+ singleFlightWaits: number;
62
+ negativeCacheHits: number;
63
+ circuitBreakerTrips: number;
64
+ degradedOperations: number;
65
+ hitsByLayer: Record<string, number>;
66
+ missesByLayer: Record<string, number>;
45
67
  }
46
68
  interface CacheLogger {
47
- debug(message: string, context?: Record<string, unknown>): void;
69
+ debug?(message: string, context?: Record<string, unknown>): void;
70
+ info?(message: string, context?: Record<string, unknown>): void;
71
+ warn?(message: string, context?: Record<string, unknown>): void;
72
+ error?(message: string, context?: Record<string, unknown>): void;
48
73
  }
49
74
  interface CacheTagIndex {
50
75
  touch(key: string): Promise<void>;
@@ -64,16 +89,96 @@ interface InvalidationBus {
64
89
  subscribe(handler: (message: InvalidationMessage) => Promise<void> | void): Promise<() => Promise<void> | void>;
65
90
  publish(message: InvalidationMessage): Promise<void>;
66
91
  }
92
+ interface CacheSingleFlightExecutionOptions {
93
+ leaseMs: number;
94
+ waitTimeoutMs: number;
95
+ pollIntervalMs: number;
96
+ }
97
+ interface CacheSingleFlightCoordinator {
98
+ execute<T>(key: string, options: CacheSingleFlightExecutionOptions, worker: () => Promise<T>, waiter: () => Promise<T>): Promise<T>;
99
+ }
67
100
  interface CacheStackOptions {
68
101
  logger?: CacheLogger | boolean;
69
102
  metrics?: boolean;
70
103
  stampedePrevention?: boolean;
71
104
  invalidationBus?: InvalidationBus;
72
105
  tagIndex?: CacheTagIndex;
106
+ broadcastL1Invalidation?: boolean;
73
107
  publishSetInvalidation?: boolean;
108
+ negativeCaching?: boolean;
109
+ negativeTtl?: number | LayerTtlMap;
110
+ staleWhileRevalidate?: number | LayerTtlMap;
111
+ staleIfError?: number | LayerTtlMap;
112
+ ttlJitter?: number | LayerTtlMap;
113
+ refreshAhead?: number | LayerTtlMap;
114
+ adaptiveTtl?: boolean | CacheAdaptiveTtlOptions;
115
+ circuitBreaker?: CacheCircuitBreakerOptions;
116
+ gracefulDegradation?: boolean | CacheDegradationOptions;
117
+ writePolicy?: 'strict' | 'best-effort';
118
+ singleFlightCoordinator?: CacheSingleFlightCoordinator;
119
+ singleFlightLeaseMs?: number;
120
+ singleFlightTimeoutMs?: number;
121
+ singleFlightPollMs?: number;
122
+ }
123
+ interface CacheAdaptiveTtlOptions {
124
+ hotAfter?: number;
125
+ step?: number | LayerTtlMap;
126
+ maxTtl?: number | LayerTtlMap;
127
+ }
128
+ interface CacheCircuitBreakerOptions {
129
+ failureThreshold?: number;
130
+ cooldownMs?: number;
131
+ }
132
+ interface CacheDegradationOptions {
133
+ retryAfterMs?: number;
134
+ }
135
+ interface CacheWarmEntry<T = unknown> {
136
+ key: string;
137
+ fetcher: () => Promise<T>;
138
+ options?: CacheGetOptions;
139
+ priority?: number;
140
+ }
141
+ interface CacheWarmOptions {
142
+ concurrency?: number;
143
+ continueOnError?: boolean;
144
+ }
145
+ interface CacheWrapOptions<TArgs extends unknown[] = unknown[]> extends CacheGetOptions {
146
+ keyResolver?: (...args: TArgs) => string;
147
+ }
148
+ interface CacheSnapshotEntry {
149
+ key: string;
150
+ value: unknown;
151
+ ttl?: number;
152
+ }
153
+ interface CacheStatsSnapshot {
154
+ metrics: CacheMetricsSnapshot;
155
+ layers: Array<{
156
+ name: string;
157
+ isLocal: boolean;
158
+ degradedUntil: number | null;
159
+ }>;
160
+ backgroundRefreshes: number;
74
161
  }
75
162
 
76
- declare class CacheStack {
163
+ declare class CacheNamespace {
164
+ private readonly cache;
165
+ private readonly prefix;
166
+ constructor(cache: CacheStack, prefix: string);
167
+ get<T>(key: string, fetcher?: () => Promise<T>, options?: CacheGetOptions): Promise<T | null>;
168
+ set<T>(key: string, value: T, options?: CacheWriteOptions): Promise<void>;
169
+ delete(key: string): Promise<void>;
170
+ clear(): Promise<void>;
171
+ mget<T>(entries: CacheMGetEntry<T>[]): Promise<Array<T | null>>;
172
+ mset<T>(entries: CacheMSetEntry<T>[]): Promise<void>;
173
+ invalidateByTag(tag: string): Promise<void>;
174
+ invalidateByPattern(pattern: string): Promise<void>;
175
+ wrap<TArgs extends unknown[], TResult>(keyPrefix: string, fetcher: (...args: TArgs) => Promise<TResult>, options?: CacheWrapOptions<TArgs>): (...args: TArgs) => Promise<TResult | null>;
176
+ warm(entries: CacheWarmEntry[], options?: CacheWarmOptions): Promise<void>;
177
+ getMetrics(): CacheMetricsSnapshot;
178
+ qualify(key: string): string;
179
+ }
180
+
181
+ declare class CacheStack extends EventEmitter {
77
182
  private readonly layers;
78
183
  private readonly options;
79
184
  private readonly stampedeGuard;
@@ -83,6 +188,12 @@ declare class CacheStack {
83
188
  private unsubscribeInvalidation?;
84
189
  private readonly logger;
85
190
  private readonly tagIndex;
191
+ private readonly backgroundRefreshes;
192
+ private readonly accessProfiles;
193
+ private readonly layerDegradedUntil;
194
+ private readonly circuitBreakers;
195
+ private isDisconnecting;
196
+ private disconnectPromise?;
86
197
  constructor(layers: CacheLayer[], options?: CacheStackOptions);
87
198
  get<T>(key: string, fetcher?: () => Promise<T>, options?: CacheGetOptions): Promise<T | null>;
88
199
  set<T>(key: string, value: T, options?: CacheWriteOptions): Promise<void>;
@@ -90,19 +201,67 @@ declare class CacheStack {
90
201
  clear(): Promise<void>;
91
202
  mget<T>(entries: CacheMGetEntry<T>[]): Promise<Array<T | null>>;
92
203
  mset<T>(entries: CacheMSetEntry<T>[]): Promise<void>;
204
+ warm(entries: CacheWarmEntry[], options?: CacheWarmOptions): Promise<void>;
205
+ wrap<TArgs extends unknown[], TResult>(prefix: string, fetcher: (...args: TArgs) => Promise<TResult>, options?: CacheWrapOptions<TArgs>): (...args: TArgs) => Promise<TResult | null>;
206
+ namespace(prefix: string): CacheNamespace;
93
207
  invalidateByTag(tag: string): Promise<void>;
94
208
  invalidateByPattern(pattern: string): Promise<void>;
95
209
  getMetrics(): CacheMetricsSnapshot;
210
+ getStats(): CacheStatsSnapshot;
96
211
  resetMetrics(): void;
212
+ exportState(): Promise<CacheSnapshotEntry[]>;
213
+ importState(entries: CacheSnapshotEntry[]): Promise<void>;
214
+ persistToFile(filePath: string): Promise<void>;
215
+ restoreFromFile(filePath: string): Promise<void>;
97
216
  disconnect(): Promise<void>;
98
217
  private initialize;
99
- private getFromLayers;
218
+ private fetchWithGuards;
219
+ private waitForFreshValue;
220
+ private fetchAndPopulate;
221
+ private storeEntry;
222
+ private readFromLayers;
223
+ private readLayerEntry;
100
224
  private backfill;
101
- private setAcrossLayers;
102
- private resolveTtl;
225
+ private writeAcrossLayers;
226
+ private executeLayerOperations;
227
+ private resolveFreshTtl;
228
+ private resolveLayerSeconds;
229
+ private readLayerNumber;
230
+ private applyJitter;
231
+ private shouldNegativeCache;
232
+ private scheduleBackgroundRefresh;
233
+ private resolveSingleFlightOptions;
103
234
  private deleteKeys;
104
235
  private publishInvalidation;
105
236
  private handleInvalidationMessage;
237
+ private formatError;
238
+ private sleep;
239
+ private shouldBroadcastL1Invalidation;
240
+ private deleteKeysFromLayers;
241
+ private validateConfiguration;
242
+ private validateWriteOptions;
243
+ private validateLayerNumberOption;
244
+ private validatePositiveNumber;
245
+ private validateNonNegativeNumber;
246
+ private validateCacheKey;
247
+ private serializeOptions;
248
+ private validateAdaptiveTtlOptions;
249
+ private validateCircuitBreakerOptions;
250
+ private applyFreshReadPolicies;
251
+ private applyAdaptiveTtl;
252
+ private recordAccess;
253
+ private incrementMetricMap;
254
+ private shouldSkipLayer;
255
+ private handleLayerFailure;
256
+ private isGracefulDegradationEnabled;
257
+ private assertCircuitClosed;
258
+ private recordCircuitFailure;
259
+ private resetCircuitBreaker;
260
+ private isNegativeStoredValue;
261
+ private emitError;
262
+ private serializeKeyPart;
263
+ private isCacheSnapshotEntries;
264
+ private normalizeForSerialization;
106
265
  }
107
266
 
108
267
  declare class PatternMatcher {
@@ -118,9 +277,13 @@ declare class RedisInvalidationBus implements InvalidationBus {
118
277
  private readonly channel;
119
278
  private readonly publisher;
120
279
  private readonly subscriber;
280
+ private activeListener?;
121
281
  constructor(options: RedisInvalidationBusOptions);
122
282
  subscribe(handler: (message: InvalidationMessage) => Promise<void> | void): Promise<() => Promise<void>>;
123
283
  publish(message: InvalidationMessage): Promise<void>;
284
+ private handleMessage;
285
+ private isInvalidationMessage;
286
+ private reportError;
124
287
  }
125
288
 
126
289
  interface RedisTagIndexOptions {
@@ -157,6 +320,55 @@ declare class TagIndex implements CacheTagIndex {
157
320
  clear(): Promise<void>;
158
321
  }
159
322
 
323
+ declare function createCacheStatsHandler(cache: CacheStack): (_request: unknown, response: {
324
+ setHeader?: (name: string, value: string) => void;
325
+ end: (body: string) => void;
326
+ statusCode?: number;
327
+ }) => Promise<void>;
328
+
329
+ interface CachedMethodDecoratorOptions<TArgs extends unknown[]> extends CacheWrapOptions<TArgs> {
330
+ cache: (instance: unknown) => CacheStack;
331
+ prefix?: string;
332
+ }
333
+ declare function createCachedMethodDecorator<TArgs extends unknown[] = unknown[]>(options: CachedMethodDecoratorOptions<TArgs>): MethodDecorator;
334
+
335
+ interface FastifyLike {
336
+ decorate: (name: string, value: unknown) => void;
337
+ get?: (path: string, handler: () => unknown | Promise<unknown>) => void;
338
+ }
339
+ interface FastifyLayercachePluginOptions {
340
+ exposeStatsRoute?: boolean;
341
+ statsPath?: string;
342
+ }
343
+ declare function createFastifyLayercachePlugin(cache: CacheStack, options?: FastifyLayercachePluginOptions): (fastify: FastifyLike) => Promise<void>;
344
+
345
+ interface GraphqlCacheOptions<TArgs extends unknown[]> extends CacheGetOptions {
346
+ keyResolver?: (...args: TArgs) => string;
347
+ }
348
+ declare function cacheGraphqlResolver<TArgs extends unknown[], TResult>(cache: CacheStack, prefix: string, resolver: (...args: TArgs) => Promise<TResult>, options?: GraphqlCacheOptions<TArgs>): (...args: TArgs) => Promise<TResult | null>;
349
+
350
+ interface TrpcCacheMiddlewareContext<TInput = unknown, TResult = unknown> {
351
+ path?: string;
352
+ type?: string;
353
+ rawInput?: TInput;
354
+ next: () => Promise<{
355
+ ok: boolean;
356
+ data?: TResult;
357
+ }>;
358
+ }
359
+ interface TrpcCacheMiddlewareOptions<TInput> extends CacheGetOptions {
360
+ keyResolver?: (input: TInput, path?: string, type?: string) => string;
361
+ }
362
+ declare function createTrpcCacheMiddleware<TInput = unknown, TResult = unknown>(cache: CacheStack, prefix: string, options?: TrpcCacheMiddlewareOptions<TInput>): (context: TrpcCacheMiddlewareContext<TInput, TResult>) => Promise<{
363
+ ok: boolean;
364
+ data?: TResult;
365
+ } | null>;
366
+
367
+ interface MemoryLayerSnapshotEntry {
368
+ key: string;
369
+ value: unknown;
370
+ expiresAt: number | null;
371
+ }
160
372
  interface MemoryLayerOptions {
161
373
  ttl?: number;
162
374
  maxSize?: number;
@@ -170,15 +382,20 @@ declare class MemoryLayer implements CacheLayer {
170
382
  private readonly entries;
171
383
  constructor(options?: MemoryLayerOptions);
172
384
  get<T>(key: string): Promise<T | null>;
385
+ getEntry<T = unknown>(key: string): Promise<T | null>;
386
+ getMany<T>(keys: string[]): Promise<Array<T | null>>;
173
387
  set(key: string, value: unknown, ttl?: number | undefined): Promise<void>;
174
388
  delete(key: string): Promise<void>;
175
389
  deleteMany(keys: string[]): Promise<void>;
176
390
  clear(): Promise<void>;
177
391
  keys(): Promise<string[]>;
392
+ exportState(): MemoryLayerSnapshotEntry[];
393
+ importState(entries: MemoryLayerSnapshotEntry[]): void;
178
394
  private pruneExpired;
179
395
  private isExpired;
180
396
  }
181
397
 
398
+ type CompressionAlgorithm = 'gzip' | 'brotli';
182
399
  interface RedisLayerOptions {
183
400
  client: Redis;
184
401
  ttl?: number;
@@ -187,6 +404,8 @@ interface RedisLayerOptions {
187
404
  prefix?: string;
188
405
  allowUnprefixedClear?: boolean;
189
406
  scanCount?: number;
407
+ compression?: CompressionAlgorithm;
408
+ compressionThreshold?: number;
190
409
  }
191
410
  declare class RedisLayer implements CacheLayer {
192
411
  readonly name: string;
@@ -197,8 +416,12 @@ declare class RedisLayer implements CacheLayer {
197
416
  private readonly prefix;
198
417
  private readonly allowUnprefixedClear;
199
418
  private readonly scanCount;
419
+ private readonly compression?;
420
+ private readonly compressionThreshold;
200
421
  constructor(options: RedisLayerOptions);
201
422
  get<T>(key: string): Promise<T | null>;
423
+ getEntry<T = unknown>(key: string): Promise<T | null>;
424
+ getMany<T>(keys: string[]): Promise<Array<T | null>>;
202
425
  set(key: string, value: unknown, ttl?: number | undefined): Promise<void>;
203
426
  delete(key: string): Promise<void>;
204
427
  deleteMany(keys: string[]): Promise<void>;
@@ -206,6 +429,10 @@ declare class RedisLayer implements CacheLayer {
206
429
  keys(): Promise<string[]>;
207
430
  private scanKeys;
208
431
  private withPrefix;
432
+ private deserializeOrDelete;
433
+ private isSerializablePayload;
434
+ private encodePayload;
435
+ private decodePayload;
209
436
  }
210
437
 
211
438
  declare class JsonSerializer implements CacheSerializer {
@@ -218,10 +445,21 @@ declare class MsgpackSerializer implements CacheSerializer {
218
445
  deserialize<T>(payload: string | Buffer): T;
219
446
  }
220
447
 
448
+ interface RedisSingleFlightCoordinatorOptions {
449
+ client: Redis;
450
+ prefix?: string;
451
+ }
452
+ declare class RedisSingleFlightCoordinator implements CacheSingleFlightCoordinator {
453
+ private readonly client;
454
+ private readonly prefix;
455
+ constructor(options: RedisSingleFlightCoordinatorOptions);
456
+ execute<T>(key: string, options: CacheSingleFlightExecutionOptions, worker: () => Promise<T>, waiter: () => Promise<T>): Promise<T>;
457
+ }
458
+
221
459
  declare class StampedeGuard {
222
460
  private readonly mutexes;
223
461
  execute<T>(key: string, task: () => Promise<T>): Promise<T>;
224
- private getMutex;
462
+ private getMutexEntry;
225
463
  }
226
464
 
227
- export { type CacheGetOptions, type CacheLayer, type CacheLogger, type CacheMGetEntry, type CacheMSetEntry, type CacheMetricsSnapshot, type CacheSerializer, CacheStack, type CacheStackOptions, type CacheTagIndex, type CacheWriteOptions, type InvalidationBus, type InvalidationMessage, JsonSerializer, type LayerTtlMap, MemoryLayer, MsgpackSerializer, PatternMatcher, RedisInvalidationBus, RedisLayer, RedisTagIndex, StampedeGuard, TagIndex };
465
+ export { type CacheAdaptiveTtlOptions, type CacheCircuitBreakerOptions, type CacheDegradationOptions, type CacheGetOptions, type CacheLayer, type CacheLogger, type CacheMGetEntry, type CacheMSetEntry, type CacheMetricsSnapshot, CacheNamespace, type CacheSerializer, type CacheSingleFlightCoordinator, type CacheSingleFlightExecutionOptions, type CacheSnapshotEntry, CacheStack, type CacheStackOptions, type CacheStatsSnapshot, type CacheTagIndex, type CacheWarmEntry, type CacheWarmOptions, type CacheWrapOptions, type CacheWriteOptions, type InvalidationBus, type InvalidationMessage, JsonSerializer, type LayerTtlMap, MemoryLayer, MsgpackSerializer, PatternMatcher, RedisInvalidationBus, RedisLayer, RedisSingleFlightCoordinator, RedisTagIndex, StampedeGuard, TagIndex, cacheGraphqlResolver, createCacheStatsHandler, createCachedMethodDecorator, createFastifyLayercachePlugin, createTrpcCacheMiddleware };