layercache 1.2.3 → 1.2.5

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.
@@ -374,6 +374,7 @@ var CacheNamespace = class _CacheNamespace {
374
374
  * ```
375
375
  */
376
376
  namespace(childPrefix) {
377
+ validateNamespaceKey(childPrefix);
377
378
  return new _CacheNamespace(this.cache, `${this.prefix}:${childPrefix}`);
378
379
  }
379
380
  qualify(key) {
@@ -503,6 +504,118 @@ function addMap(base, delta) {
503
504
  }
504
505
  return result;
505
506
  }
507
+ function validateNamespaceKey(key) {
508
+ if (key.length === 0) {
509
+ throw new Error("Namespace prefix must not be empty.");
510
+ }
511
+ if (key.length > 256) {
512
+ throw new Error("Namespace prefix must be at most 256 characters.");
513
+ }
514
+ if (/[\u0000-\u001F\u007F]/.test(key)) {
515
+ throw new Error("Namespace prefix contains unsupported control characters.");
516
+ }
517
+ }
518
+
519
+ // ../../src/invalidation/PatternMatcher.ts
520
+ var PatternMatcher = class _PatternMatcher {
521
+ /**
522
+ * Tests whether a glob-style pattern matches a value.
523
+ * Supports `*` (any sequence of characters) and `?` (any single character).
524
+ * Uses a two-pointer algorithm to avoid ReDoS vulnerabilities and
525
+ * quadratic memory usage on long patterns/keys.
526
+ */
527
+ static matches(pattern, value) {
528
+ return _PatternMatcher.matchLinear(pattern, value);
529
+ }
530
+ /**
531
+ * Linear-time glob matching with O(1) extra memory.
532
+ */
533
+ static matchLinear(pattern, value) {
534
+ let patternIndex = 0;
535
+ let valueIndex = 0;
536
+ let starIndex = -1;
537
+ let backtrackValueIndex = 0;
538
+ while (valueIndex < value.length) {
539
+ const patternChar = pattern[patternIndex];
540
+ const valueChar = value[valueIndex];
541
+ if (patternChar === "*" && patternIndex < pattern.length) {
542
+ starIndex = patternIndex;
543
+ patternIndex += 1;
544
+ backtrackValueIndex = valueIndex;
545
+ continue;
546
+ }
547
+ if (patternChar === "?" || patternChar === valueChar) {
548
+ patternIndex += 1;
549
+ valueIndex += 1;
550
+ continue;
551
+ }
552
+ if (starIndex !== -1) {
553
+ patternIndex = starIndex + 1;
554
+ backtrackValueIndex += 1;
555
+ valueIndex = backtrackValueIndex;
556
+ continue;
557
+ }
558
+ return false;
559
+ }
560
+ while (patternIndex < pattern.length && pattern[patternIndex] === "*") {
561
+ patternIndex += 1;
562
+ }
563
+ return patternIndex === pattern.length;
564
+ }
565
+ };
566
+
567
+ // ../../src/internal/CacheKeyDiscovery.ts
568
+ var CacheKeyDiscovery = class {
569
+ constructor(options) {
570
+ this.options = options;
571
+ }
572
+ options;
573
+ async collectKeysWithPrefix(prefix) {
574
+ const { tagIndex } = this.options;
575
+ const matches = new Set(
576
+ tagIndex.keysForPrefix ? await tagIndex.keysForPrefix(prefix) : await tagIndex.matchPattern(`${prefix}*`)
577
+ );
578
+ await Promise.all(
579
+ this.options.layers.map(async (layer) => {
580
+ if (!layer.keys || this.options.shouldSkipLayer(layer)) {
581
+ return;
582
+ }
583
+ try {
584
+ const keys = await layer.keys();
585
+ for (const key of keys) {
586
+ if (key.startsWith(prefix)) {
587
+ matches.add(key);
588
+ }
589
+ }
590
+ } catch (error) {
591
+ await this.options.handleLayerFailure(layer, "invalidate-prefix-scan", error);
592
+ }
593
+ })
594
+ );
595
+ return [...matches];
596
+ }
597
+ async collectKeysMatchingPattern(pattern) {
598
+ const matches = new Set(await this.options.tagIndex.matchPattern(pattern));
599
+ await Promise.all(
600
+ this.options.layers.map(async (layer) => {
601
+ if (!layer.keys || this.options.shouldSkipLayer(layer)) {
602
+ return;
603
+ }
604
+ try {
605
+ const keys = await layer.keys();
606
+ for (const key of keys) {
607
+ if (PatternMatcher.matches(pattern, key)) {
608
+ matches.add(key);
609
+ }
610
+ }
611
+ } catch (error) {
612
+ await this.options.handleLayerFailure(layer, "invalidate-pattern-scan", error);
613
+ }
614
+ })
615
+ );
616
+ return [...matches];
617
+ }
618
+ };
506
619
 
507
620
  // ../../src/internal/CircuitBreakerManager.ts
508
621
  var CircuitBreakerManager = class {
@@ -522,9 +635,7 @@ var CircuitBreakerManager = class {
522
635
  }
523
636
  const now = Date.now();
524
637
  if (state.openUntil <= now) {
525
- state.openUntil = null;
526
- state.failures = 0;
527
- this.breakers.set(key, state);
638
+ this.breakers.delete(key);
528
639
  return;
529
640
  }
530
641
  const remainingMs = state.openUntil - now;
@@ -535,15 +646,15 @@ var CircuitBreakerManager = class {
535
646
  if (!options) {
536
647
  return;
537
648
  }
649
+ this.pruneIfNeeded();
538
650
  const failureThreshold = options.failureThreshold ?? 3;
539
651
  const cooldownMs = options.cooldownMs ?? 3e4;
540
- const state = this.breakers.get(key) ?? { failures: 0, openUntil: null };
652
+ const state = this.breakers.get(key) ?? { failures: 0, openUntil: null, createdAt: Date.now() };
541
653
  state.failures += 1;
542
654
  if (state.failures >= failureThreshold) {
543
655
  state.openUntil = Date.now() + cooldownMs;
544
656
  }
545
657
  this.breakers.set(key, state);
546
- this.pruneIfNeeded();
547
658
  }
548
659
  recordSuccess(key) {
549
660
  this.breakers.delete(key);
@@ -554,8 +665,7 @@ var CircuitBreakerManager = class {
554
665
  return false;
555
666
  }
556
667
  if (state.openUntil <= Date.now()) {
557
- state.openUntil = null;
558
- state.failures = 0;
668
+ this.breakers.delete(key);
559
669
  return false;
560
670
  }
561
671
  return true;
@@ -579,15 +689,20 @@ var CircuitBreakerManager = class {
579
689
  if (this.breakers.size <= this.maxEntries) {
580
690
  return;
581
691
  }
692
+ const now = Date.now();
582
693
  for (const [key, state] of this.breakers.entries()) {
583
694
  if (this.breakers.size <= this.maxEntries) {
584
- break;
695
+ return;
585
696
  }
586
- if (!state.openUntil || state.openUntil <= Date.now()) {
697
+ if (!state.openUntil || state.openUntil <= now) {
587
698
  this.breakers.delete(key);
588
699
  }
589
700
  }
590
- for (const key of this.breakers.keys()) {
701
+ if (this.breakers.size <= this.maxEntries) {
702
+ return;
703
+ }
704
+ const sorted = [...this.breakers.entries()].sort((a, b) => a[1].createdAt - b[1].createdAt);
705
+ for (const [key] of sorted) {
591
706
  if (this.breakers.size <= this.maxEntries) {
592
707
  break;
593
708
  }
@@ -597,6 +712,7 @@ var CircuitBreakerManager = class {
597
712
  };
598
713
 
599
714
  // ../../src/internal/FetchRateLimiter.ts
715
+ var MAX_BUCKETS = 1e4;
600
716
  var FetchRateLimiter = class {
601
717
  buckets = /* @__PURE__ */ new Map();
602
718
  queuesByBucket = /* @__PURE__ */ new Map();
@@ -762,10 +878,25 @@ var FetchRateLimiter = class {
762
878
  if (existing) {
763
879
  return existing;
764
880
  }
881
+ if (this.buckets.size >= MAX_BUCKETS) {
882
+ this.evictIdleBuckets();
883
+ }
765
884
  const bucket = { active: 0, startedAt: [] };
766
885
  this.buckets.set(bucketKey, bucket);
767
886
  return bucket;
768
887
  }
888
+ evictIdleBuckets() {
889
+ for (const [key, bucket] of this.buckets.entries()) {
890
+ if (this.buckets.size <= MAX_BUCKETS * 0.9) {
891
+ break;
892
+ }
893
+ if (bucket.active === 0 && bucket.startedAt.length === 0 && !this.queuesByBucket.has(key)) {
894
+ this.buckets.delete(key);
895
+ this.queuesByBucket.delete(key);
896
+ this.pendingBuckets.delete(key);
897
+ }
898
+ }
899
+ }
769
900
  cleanupBucket(bucketKey, bucket, intervalMs) {
770
901
  const queued = this.queuesByBucket.get(bucketKey)?.length ?? 0;
771
902
  if (queued === 0 && bucket.active === 0 && bucket.startedAt.length === 0) {
@@ -1092,66 +1223,18 @@ var TtlResolver = class {
1092
1223
  return;
1093
1224
  }
1094
1225
  const toRemove = Math.ceil(this.maxProfileEntries * 0.1);
1095
- let removed = 0;
1096
- for (const key of this.accessProfiles.keys()) {
1097
- if (removed >= toRemove) {
1098
- break;
1226
+ const sorted = [...this.accessProfiles.entries()].sort((a, b) => a[1].lastAccessAt - b[1].lastAccessAt);
1227
+ for (let i = 0; i < toRemove && i < sorted.length; i++) {
1228
+ const entry = sorted[i];
1229
+ if (entry) {
1230
+ this.accessProfiles.delete(entry[0]);
1099
1231
  }
1100
- this.accessProfiles.delete(key);
1101
- removed += 1;
1102
- }
1103
- }
1104
- };
1105
-
1106
- // ../../src/invalidation/PatternMatcher.ts
1107
- var PatternMatcher = class _PatternMatcher {
1108
- /**
1109
- * Tests whether a glob-style pattern matches a value.
1110
- * Supports `*` (any sequence of characters) and `?` (any single character).
1111
- * Uses a two-pointer algorithm to avoid ReDoS vulnerabilities and
1112
- * quadratic memory usage on long patterns/keys.
1113
- */
1114
- static matches(pattern, value) {
1115
- return _PatternMatcher.matchLinear(pattern, value);
1116
- }
1117
- /**
1118
- * Linear-time glob matching with O(1) extra memory.
1119
- */
1120
- static matchLinear(pattern, value) {
1121
- let patternIndex = 0;
1122
- let valueIndex = 0;
1123
- let starIndex = -1;
1124
- let backtrackValueIndex = 0;
1125
- while (valueIndex < value.length) {
1126
- const patternChar = pattern[patternIndex];
1127
- const valueChar = value[valueIndex];
1128
- if (patternChar === "*" && patternIndex < pattern.length) {
1129
- starIndex = patternIndex;
1130
- patternIndex += 1;
1131
- backtrackValueIndex = valueIndex;
1132
- continue;
1133
- }
1134
- if (patternChar === "?" || patternChar === valueChar) {
1135
- patternIndex += 1;
1136
- valueIndex += 1;
1137
- continue;
1138
- }
1139
- if (starIndex !== -1) {
1140
- patternIndex = starIndex + 1;
1141
- backtrackValueIndex += 1;
1142
- valueIndex = backtrackValueIndex;
1143
- continue;
1144
- }
1145
- return false;
1146
- }
1147
- while (patternIndex < pattern.length && pattern[patternIndex] === "*") {
1148
- patternIndex += 1;
1149
1232
  }
1150
- return patternIndex === pattern.length;
1151
1233
  }
1152
1234
  };
1153
1235
 
1154
1236
  // ../../src/invalidation/TagIndex.ts
1237
+ var MAX_PATTERN_RECURSION_DEPTH = 500;
1155
1238
  var TagIndex = class {
1156
1239
  tagToKeys = /* @__PURE__ */ new Map();
1157
1240
  keyToTags = /* @__PURE__ */ new Map();
@@ -1206,7 +1289,7 @@ var TagIndex = class {
1206
1289
  }
1207
1290
  async matchPattern(pattern) {
1208
1291
  const matches = /* @__PURE__ */ new Set();
1209
- this.collectPatternMatches(this.root, "", pattern, 0, matches, /* @__PURE__ */ new Set());
1292
+ this.collectPatternMatches(this.root, "", pattern, 0, matches, /* @__PURE__ */ new Set(), 0);
1210
1293
  return [...matches];
1211
1294
  }
1212
1295
  async clear() {
@@ -1258,7 +1341,10 @@ var TagIndex = class {
1258
1341
  this.collectFromNode(child, `${prefix}${character}`, matches);
1259
1342
  }
1260
1343
  }
1261
- collectPatternMatches(node, prefix, pattern, patternIndex, matches, visited) {
1344
+ collectPatternMatches(node, prefix, pattern, patternIndex, matches, visited, depth) {
1345
+ if (depth > MAX_PATTERN_RECURSION_DEPTH) {
1346
+ return;
1347
+ }
1262
1348
  const stateKey = `${node.id}:${patternIndex}`;
1263
1349
  if (visited.has(stateKey)) {
1264
1350
  return;
@@ -1275,21 +1361,37 @@ var TagIndex = class {
1275
1361
  return;
1276
1362
  }
1277
1363
  if (patternChar === "*") {
1278
- this.collectPatternMatches(node, prefix, pattern, patternIndex + 1, matches, visited);
1364
+ this.collectPatternMatches(node, prefix, pattern, patternIndex + 1, matches, visited, depth + 1);
1279
1365
  for (const [character, child2] of node.children) {
1280
- this.collectPatternMatches(child2, `${prefix}${character}`, pattern, patternIndex, matches, visited);
1366
+ this.collectPatternMatches(child2, `${prefix}${character}`, pattern, patternIndex, matches, visited, depth + 1);
1281
1367
  }
1282
1368
  return;
1283
1369
  }
1284
1370
  if (patternChar === "?") {
1285
1371
  for (const [character, child2] of node.children) {
1286
- this.collectPatternMatches(child2, `${prefix}${character}`, pattern, patternIndex + 1, matches, visited);
1372
+ this.collectPatternMatches(
1373
+ child2,
1374
+ `${prefix}${character}`,
1375
+ pattern,
1376
+ patternIndex + 1,
1377
+ matches,
1378
+ visited,
1379
+ depth + 1
1380
+ );
1287
1381
  }
1288
1382
  return;
1289
1383
  }
1290
1384
  const child = node.children.get(patternChar);
1291
1385
  if (child) {
1292
- this.collectPatternMatches(child, `${prefix}${patternChar}`, pattern, patternIndex + 1, matches, visited);
1386
+ this.collectPatternMatches(
1387
+ child,
1388
+ `${prefix}${patternChar}`,
1389
+ pattern,
1390
+ patternIndex + 1,
1391
+ matches,
1392
+ visited,
1393
+ depth + 1
1394
+ );
1293
1395
  }
1294
1396
  }
1295
1397
  pruneKnownKeysIfNeeded() {
@@ -1431,6 +1533,7 @@ var DEFAULT_SINGLE_FLIGHT_TIMEOUT_MS = 5e3;
1431
1533
  var DEFAULT_SINGLE_FLIGHT_POLL_MS = 50;
1432
1534
  var DEFAULT_BACKGROUND_REFRESH_TIMEOUT_MS = 3e4;
1433
1535
  var MAX_CACHE_KEY_LENGTH = 1024;
1536
+ var MAX_PATTERN_LENGTH = 1024;
1434
1537
  var DEFAULT_MAX_PROFILE_ENTRIES = 1e5;
1435
1538
  var DANGEROUS_OBJECT_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
1436
1539
  var DebugLogger = class {
@@ -1479,6 +1582,14 @@ var CacheStack = class extends import_node_events.EventEmitter {
1479
1582
  const debugEnv = process.env.DEBUG?.split(",").includes("layercache:debug") ?? false;
1480
1583
  this.logger = typeof options.logger === "object" ? options.logger : new DebugLogger(Boolean(options.logger) || debugEnv);
1481
1584
  this.tagIndex = options.tagIndex ?? new TagIndex();
1585
+ this.keyDiscovery = new CacheKeyDiscovery({
1586
+ layers: this.layers,
1587
+ tagIndex: this.tagIndex,
1588
+ shouldSkipLayer: (layer) => this.shouldSkipLayer(layer),
1589
+ handleLayerFailure: async (layer, operation, error) => {
1590
+ await this.handleLayerFailure(layer, operation, error);
1591
+ }
1592
+ });
1482
1593
  if (!options.tagIndex && layers.some((layer) => layer.isLocal === false)) {
1483
1594
  this.logger.warn?.(
1484
1595
  "Using the default in-memory TagIndex with a shared cache layer only tracks keys seen by this process. Use RedisTagIndex for cross-instance tag invalidation."
@@ -1506,6 +1617,7 @@ var CacheStack = class extends import_node_events.EventEmitter {
1506
1617
  unsubscribeInvalidation;
1507
1618
  logger;
1508
1619
  tagIndex;
1620
+ keyDiscovery;
1509
1621
  fetchRateLimiter = new FetchRateLimiter();
1510
1622
  snapshotSerializer = new JsonSerializer();
1511
1623
  backgroundRefreshes = /* @__PURE__ */ new Map();
@@ -1855,15 +1967,16 @@ var CacheStack = class extends import_node_events.EventEmitter {
1855
1967
  await this.publishInvalidation({ scope: "keys", keys, sourceId: this.instanceId, operation: "invalidate" });
1856
1968
  }
1857
1969
  async invalidateByPattern(pattern) {
1970
+ this.validatePattern(pattern);
1858
1971
  await this.awaitStartup("invalidateByPattern");
1859
- const keys = await this.collectKeysMatchingPattern(this.qualifyPattern(pattern));
1972
+ const keys = await this.keyDiscovery.collectKeysMatchingPattern(this.qualifyPattern(pattern));
1860
1973
  await this.deleteKeys(keys);
1861
1974
  await this.publishInvalidation({ scope: "keys", keys, sourceId: this.instanceId, operation: "invalidate" });
1862
1975
  }
1863
1976
  async invalidateByPrefix(prefix) {
1864
1977
  await this.awaitStartup("invalidateByPrefix");
1865
1978
  const qualifiedPrefix = this.qualifyKey(this.validateCacheKey(prefix));
1866
- const keys = await this.collectKeysWithPrefix(qualifiedPrefix);
1979
+ const keys = await this.keyDiscovery.collectKeysWithPrefix(qualifiedPrefix);
1867
1980
  await this.deleteKeys(keys);
1868
1981
  await this.publishInvalidation({ scope: "keys", keys, sourceId: this.instanceId, operation: "invalidate" });
1869
1982
  }
@@ -2476,50 +2589,6 @@ var CacheStack = class extends import_node_events.EventEmitter {
2476
2589
  shouldBroadcastL1Invalidation() {
2477
2590
  return this.options.broadcastL1Invalidation ?? this.options.publishSetInvalidation ?? false;
2478
2591
  }
2479
- async collectKeysWithPrefix(prefix) {
2480
- const matches = new Set(
2481
- this.tagIndex.keysForPrefix ? await this.tagIndex.keysForPrefix(prefix) : await this.tagIndex.matchPattern(`${prefix}*`)
2482
- );
2483
- await Promise.all(
2484
- this.layers.map(async (layer) => {
2485
- if (!layer.keys || this.shouldSkipLayer(layer)) {
2486
- return;
2487
- }
2488
- try {
2489
- const keys = await layer.keys();
2490
- for (const key of keys) {
2491
- if (key.startsWith(prefix)) {
2492
- matches.add(key);
2493
- }
2494
- }
2495
- } catch (error) {
2496
- await this.handleLayerFailure(layer, "invalidate-prefix-scan", error);
2497
- }
2498
- })
2499
- );
2500
- return [...matches];
2501
- }
2502
- async collectKeysMatchingPattern(pattern) {
2503
- const matches = new Set(await this.tagIndex.matchPattern(pattern));
2504
- await Promise.all(
2505
- this.layers.map(async (layer) => {
2506
- if (!layer.keys || this.shouldSkipLayer(layer)) {
2507
- return;
2508
- }
2509
- try {
2510
- const keys = await layer.keys();
2511
- for (const key of keys) {
2512
- if (PatternMatcher.matches(pattern, key)) {
2513
- matches.add(key);
2514
- }
2515
- }
2516
- } catch (error) {
2517
- await this.handleLayerFailure(layer, "invalidate-pattern-scan", error);
2518
- }
2519
- })
2520
- );
2521
- return [...matches];
2522
- }
2523
2592
  shouldCleanupGenerations() {
2524
2593
  return Boolean(this.options.generationCleanup);
2525
2594
  }
@@ -2542,7 +2611,7 @@ var CacheStack = class extends import_node_events.EventEmitter {
2542
2611
  }
2543
2612
  async cleanupGeneration(generation) {
2544
2613
  const prefix = `v${generation}:`;
2545
- const keys = await this.collectKeysWithPrefix(prefix);
2614
+ const keys = await this.keyDiscovery.collectKeysWithPrefix(prefix);
2546
2615
  if (keys.length === 0) {
2547
2616
  return;
2548
2617
  }
@@ -2792,6 +2861,17 @@ var CacheStack = class extends import_node_events.EventEmitter {
2792
2861
  }
2793
2862
  return key;
2794
2863
  }
2864
+ validatePattern(pattern) {
2865
+ if (pattern.length === 0) {
2866
+ throw new Error("Pattern must not be empty.");
2867
+ }
2868
+ if (pattern.length > MAX_PATTERN_LENGTH) {
2869
+ throw new Error(`Pattern length must be at most ${MAX_PATTERN_LENGTH} characters.`);
2870
+ }
2871
+ if (/[\u0000-\u001F\u007F]/.test(pattern)) {
2872
+ throw new Error("Pattern contains unsupported control characters.");
2873
+ }
2874
+ }
2795
2875
  validateTtlPolicy(name, policy) {
2796
2876
  if (!policy || typeof policy === "function" || policy === "until-midnight" || policy === "next-hour") {
2797
2877
  return;
@@ -2956,7 +3036,18 @@ var CacheStack = class extends import_node_events.EventEmitter {
2956
3036
  }
2957
3037
  };
2958
3038
  function createInstanceId() {
2959
- return globalThis.crypto?.randomUUID?.() ?? `layercache-${Date.now()}-${Math.random().toString(36).slice(2)}`;
3039
+ if (globalThis.crypto?.randomUUID) {
3040
+ return globalThis.crypto.randomUUID();
3041
+ }
3042
+ const bytes = new Uint8Array(16);
3043
+ if (globalThis.crypto?.getRandomValues) {
3044
+ globalThis.crypto.getRandomValues(bytes);
3045
+ } else {
3046
+ for (let i = 0; i < bytes.length; i++) {
3047
+ bytes[i] = Math.floor(Math.random() * 256);
3048
+ }
3049
+ }
3050
+ return `layercache-${Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("")}`;
2960
3051
  }
2961
3052
 
2962
3053
  // src/module.ts
@@ -410,6 +410,7 @@ declare class CacheStack extends EventEmitter {
410
410
  private unsubscribeInvalidation?;
411
411
  private readonly logger;
412
412
  private readonly tagIndex;
413
+ private readonly keyDiscovery;
413
414
  private readonly fetchRateLimiter;
414
415
  private readonly snapshotSerializer;
415
416
  private readonly backgroundRefreshes;
@@ -532,8 +533,6 @@ declare class CacheStack extends EventEmitter {
532
533
  private sleep;
533
534
  private withTimeout;
534
535
  private shouldBroadcastL1Invalidation;
535
- private collectKeysWithPrefix;
536
- private collectKeysMatchingPattern;
537
536
  private shouldCleanupGenerations;
538
537
  private generationCleanupBatchSize;
539
538
  private scheduleGenerationCleanup;
@@ -556,6 +555,7 @@ declare class CacheStack extends EventEmitter {
556
555
  private validateRateLimitOptions;
557
556
  private validateNonNegativeNumber;
558
557
  private validateCacheKey;
558
+ private validatePattern;
559
559
  private validateTtlPolicy;
560
560
  private assertActive;
561
561
  private awaitStartup;
@@ -410,6 +410,7 @@ declare class CacheStack extends EventEmitter {
410
410
  private unsubscribeInvalidation?;
411
411
  private readonly logger;
412
412
  private readonly tagIndex;
413
+ private readonly keyDiscovery;
413
414
  private readonly fetchRateLimiter;
414
415
  private readonly snapshotSerializer;
415
416
  private readonly backgroundRefreshes;
@@ -532,8 +533,6 @@ declare class CacheStack extends EventEmitter {
532
533
  private sleep;
533
534
  private withTimeout;
534
535
  private shouldBroadcastL1Invalidation;
535
- private collectKeysWithPrefix;
536
- private collectKeysMatchingPattern;
537
536
  private shouldCleanupGenerations;
538
537
  private generationCleanupBatchSize;
539
538
  private scheduleGenerationCleanup;
@@ -556,6 +555,7 @@ declare class CacheStack extends EventEmitter {
556
555
  private validateRateLimitOptions;
557
556
  private validateNonNegativeNumber;
558
557
  private validateCacheKey;
558
+ private validatePattern;
559
559
  private validateTtlPolicy;
560
560
  private assertActive;
561
561
  private awaitStartup;