layercache 1.3.2 → 1.3.4

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/cli.js CHANGED
@@ -1,7 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- RedisTagIndex
4
- } from "./chunk-BQLL6IM5.js";
3
+ RedisTagIndex,
4
+ validateCacheKey,
5
+ validatePattern,
6
+ validateTag
7
+ } from "./chunk-BORDQ3LA.js";
5
8
  import {
6
9
  isStoredValueEnvelope,
7
10
  resolveStoredValue
@@ -46,13 +49,17 @@ async function main(argv = process.argv.slice(2)) {
46
49
  throw new Error(`Failed to connect to Redis at ${maskRedisUrl(redisUrl)}: ${message}`);
47
50
  });
48
51
  if (args.command === "stats") {
49
- const keys = await scanKeys(redis, args.pattern ?? "*");
50
- process.stdout.write(`${JSON.stringify({ totalKeys: keys.length, pattern: args.pattern ?? "*" }, null, 2)}
52
+ const pattern = args.pattern ?? "*";
53
+ if (args.pattern && !validateCliInput(args.pattern, validatePattern)) return;
54
+ const keys = await scanKeys(redis, pattern);
55
+ process.stdout.write(`${JSON.stringify({ totalKeys: keys.length, pattern }, null, 2)}
51
56
  `);
52
57
  return;
53
58
  }
54
59
  if (args.command === "keys") {
55
- const keys = await scanKeys(redis, args.pattern ?? "*");
60
+ const pattern = args.pattern ?? "*";
61
+ if (args.pattern && !validateCliInput(args.pattern, validatePattern)) return;
62
+ const keys = await scanKeys(redis, pattern);
56
63
  if (keys.length > 0) {
57
64
  process.stdout.write(`${keys.join("\n")}
58
65
  `);
@@ -61,6 +68,7 @@ async function main(argv = process.argv.slice(2)) {
61
68
  }
62
69
  if (args.command === "invalidate") {
63
70
  if (args.tag) {
71
+ if (!validateCliInput(args.tag, validateTag)) return;
64
72
  const tagIndex = new RedisTagIndex({ client: redis, prefix: args.tagIndexPrefix ?? "layercache:tag-index" });
65
73
  const keys2 = await tagIndex.keysForTag(args.tag);
66
74
  if (keys2.length > 0) {
@@ -70,11 +78,18 @@ async function main(argv = process.argv.slice(2)) {
70
78
  `);
71
79
  return;
72
80
  }
73
- const keys = await scanKeys(redis, args.pattern ?? "*");
81
+ const effectivePattern = args.pattern ?? "*";
82
+ if (args.pattern && !validateCliInput(args.pattern, validatePattern)) return;
83
+ const keys = await scanKeys(redis, effectivePattern);
84
+ if (!args.pattern && !args.force && keys.length > 0) {
85
+ process.stderr.write(`Warning: this operation will invalidate ${keys.length} keys. Use --force to confirm.
86
+ `);
87
+ return;
88
+ }
74
89
  if (keys.length > 0) {
75
90
  await batchDelete(redis, keys);
76
91
  }
77
- process.stdout.write(`${JSON.stringify({ deletedKeys: keys.length, pattern: args.pattern ?? "*" }, null, 2)}
92
+ process.stdout.write(`${JSON.stringify({ deletedKeys: keys.length, pattern: effectivePattern }, null, 2)}
78
93
  `);
79
94
  return;
80
95
  }
@@ -82,6 +97,7 @@ async function main(argv = process.argv.slice(2)) {
82
97
  if (!args.key) {
83
98
  throw new Error("inspect requires --key <key>.");
84
99
  }
100
+ if (!validateCliInput(args.key, validateCacheKey)) return;
85
101
  const payload = await redis.getBuffer(args.key);
86
102
  const ttl = await redis.ttl(args.key);
87
103
  const decoded = decodeInspectablePayload(payload);
@@ -156,6 +172,8 @@ function parseArgs(argv) {
156
172
  index += 1;
157
173
  } else if (token === "--require-tls") {
158
174
  parsed.requireTls = true;
175
+ } else if (token === "--force") {
176
+ parsed.force = true;
159
177
  }
160
178
  }
161
179
  return parsed;
@@ -232,6 +250,18 @@ function maskRedisUrl(url) {
232
250
  return url.replace(/:([^@/]+)@/, ":***@");
233
251
  }
234
252
  }
253
+ function validateCliInput(value, validator) {
254
+ try {
255
+ validator(value);
256
+ return true;
257
+ } catch (error) {
258
+ const message = error instanceof Error ? error.message : String(error);
259
+ process.stderr.write(`Error: ${message}
260
+ `);
261
+ process.exitCode = 1;
262
+ return false;
263
+ }
264
+ }
235
265
  if (process.argv[1]?.endsWith("cli.cjs") || process.argv[1]?.endsWith("cli.js")) {
236
266
  void main();
237
267
  }
@@ -557,8 +557,6 @@ declare class CacheStack extends EventEmitter {
557
557
  private readonly invalidation;
558
558
  private readonly layerWriter;
559
559
  private readonly snapshots;
560
- private readonly backgroundRefreshes;
561
- private readonly backgroundRefreshAbort;
562
560
  private readonly layerDegradedUntil;
563
561
  private readonly maintenance;
564
562
  private readonly ttlResolver;
@@ -566,6 +564,7 @@ declare class CacheStack extends EventEmitter {
566
564
  private nextOperationId;
567
565
  private currentGeneration?;
568
566
  private isDisconnecting;
567
+ private readonly reader;
569
568
  private disconnectPromise?;
570
569
  constructor(layers: CacheLayer[], options?: CacheStackOptions);
571
570
  /**
@@ -575,7 +574,6 @@ declare class CacheStack extends EventEmitter {
575
574
  * and no `fetcher` is provided.
576
575
  */
577
576
  get<T>(key: string, fetcher?: () => Promise<T>, options?: CacheGetOptions): Promise<T | null>;
578
- private getPrepared;
579
577
  /**
580
578
  * Alias for `get(key, fetcher, options)` — explicit get-or-set pattern.
581
579
  * Fetches and caches the value if not already present.
@@ -652,20 +650,10 @@ declare class CacheStack extends EventEmitter {
652
650
  restoreFromFile(filePath: string): Promise<void>;
653
651
  disconnect(): Promise<void>;
654
652
  private initialize;
655
- private fetchWithGuards;
656
- private waitForFreshValue;
657
- private fetchAndPopulate;
658
653
  private storeEntry;
659
654
  private writeBatch;
660
- private readFromLayers;
661
- private readLayerEntry;
662
- private backfill;
663
655
  private resolveFreshTtl;
664
656
  private resolveLayerSeconds;
665
- private shouldNegativeCache;
666
- private scheduleBackgroundRefresh;
667
- private runBackgroundRefresh;
668
- private resolveSingleFlightOptions;
669
657
  private deleteKeys;
670
658
  private publishInvalidation;
671
659
  private handleInvalidationMessage;
@@ -689,13 +677,14 @@ declare class CacheStack extends EventEmitter {
689
677
  private validateWriteOptions;
690
678
  private assertActive;
691
679
  private awaitStartup;
680
+ private readLayerEntry;
681
+ private scheduleBackgroundRefresh;
692
682
  private applyFreshReadPolicies;
693
683
  private shouldSkipLayer;
694
684
  private handleLayerFailure;
695
685
  private reportRecoverableLayerFailure;
696
686
  private isGracefulDegradationEnabled;
697
687
  private recordCircuitFailure;
698
- private isNegativeStoredValue;
699
688
  private emitError;
700
689
  private snapshotMaxBytes;
701
690
  private snapshotMaxEntries;
@@ -557,8 +557,6 @@ declare class CacheStack extends EventEmitter {
557
557
  private readonly invalidation;
558
558
  private readonly layerWriter;
559
559
  private readonly snapshots;
560
- private readonly backgroundRefreshes;
561
- private readonly backgroundRefreshAbort;
562
560
  private readonly layerDegradedUntil;
563
561
  private readonly maintenance;
564
562
  private readonly ttlResolver;
@@ -566,6 +564,7 @@ declare class CacheStack extends EventEmitter {
566
564
  private nextOperationId;
567
565
  private currentGeneration?;
568
566
  private isDisconnecting;
567
+ private readonly reader;
569
568
  private disconnectPromise?;
570
569
  constructor(layers: CacheLayer[], options?: CacheStackOptions);
571
570
  /**
@@ -575,7 +574,6 @@ declare class CacheStack extends EventEmitter {
575
574
  * and no `fetcher` is provided.
576
575
  */
577
576
  get<T>(key: string, fetcher?: () => Promise<T>, options?: CacheGetOptions): Promise<T | null>;
578
- private getPrepared;
579
577
  /**
580
578
  * Alias for `get(key, fetcher, options)` — explicit get-or-set pattern.
581
579
  * Fetches and caches the value if not already present.
@@ -652,20 +650,10 @@ declare class CacheStack extends EventEmitter {
652
650
  restoreFromFile(filePath: string): Promise<void>;
653
651
  disconnect(): Promise<void>;
654
652
  private initialize;
655
- private fetchWithGuards;
656
- private waitForFreshValue;
657
- private fetchAndPopulate;
658
653
  private storeEntry;
659
654
  private writeBatch;
660
- private readFromLayers;
661
- private readLayerEntry;
662
- private backfill;
663
655
  private resolveFreshTtl;
664
656
  private resolveLayerSeconds;
665
- private shouldNegativeCache;
666
- private scheduleBackgroundRefresh;
667
- private runBackgroundRefresh;
668
- private resolveSingleFlightOptions;
669
657
  private deleteKeys;
670
658
  private publishInvalidation;
671
659
  private handleInvalidationMessage;
@@ -689,13 +677,14 @@ declare class CacheStack extends EventEmitter {
689
677
  private validateWriteOptions;
690
678
  private assertActive;
691
679
  private awaitStartup;
680
+ private readLayerEntry;
681
+ private scheduleBackgroundRefresh;
692
682
  private applyFreshReadPolicies;
693
683
  private shouldSkipLayer;
694
684
  private handleLayerFailure;
695
685
  private reportRecoverableLayerFailure;
696
686
  private isGracefulDegradationEnabled;
697
687
  private recordCircuitFailure;
698
- private isNegativeStoredValue;
699
688
  private emitError;
700
689
  private snapshotMaxBytes;
701
690
  private snapshotMaxEntries;
package/dist/edge.cjs CHANGED
@@ -339,7 +339,7 @@ var MAX_PATTERN_RECURSION_DEPTH = 500;
339
339
  var TagIndex = class {
340
340
  tagToKeys = /* @__PURE__ */ new Map();
341
341
  keyToTags = /* @__PURE__ */ new Map();
342
- knownKeys = /* @__PURE__ */ new Set();
342
+ knownKeys = /* @__PURE__ */ new Map();
343
343
  maxKnownKeys;
344
344
  nextNodeId = 1;
345
345
  root = this.createTrieNode();
@@ -427,10 +427,11 @@ var TagIndex = class {
427
427
  };
428
428
  }
429
429
  insertKnownKey(key) {
430
- if (this.knownKeys.has(key)) {
430
+ const isNew = !this.knownKeys.has(key);
431
+ this.knownKeys.set(key, Date.now());
432
+ if (!isNew) {
431
433
  return;
432
434
  }
433
- this.knownKeys.add(key);
434
435
  let node = this.root;
435
436
  for (const character of key) {
436
437
  let child = node.children.get(character);
@@ -525,14 +526,13 @@ var TagIndex = class {
525
526
  if (this.maxKnownKeys === void 0 || this.knownKeys.size <= this.maxKnownKeys) {
526
527
  return;
527
528
  }
529
+ const sorted = [...this.knownKeys.entries()].sort((a, b) => a[1] - b[1]);
528
530
  const toRemove = Math.ceil(this.maxKnownKeys * 0.1);
529
- let removed = 0;
530
- for (const key of this.knownKeys) {
531
- if (removed >= toRemove) {
532
- break;
531
+ for (let i = 0; i < toRemove && i < sorted.length; i += 1) {
532
+ const entry = sorted[i];
533
+ if (entry) {
534
+ this.removeKey(entry[0]);
533
535
  }
534
- this.removeKey(key);
535
- removed += 1;
536
536
  }
537
537
  }
538
538
  removeKey(key) {
package/dist/edge.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- export { e as CacheGetOptions, f as CacheLayer, h as CacheLayerSetManyEntry, t as CacheMetricsSnapshot, w as CacheRateLimitOptions, B as CacheTtlPolicy, D as CacheTtlPolicyContext, J as CacheWriteOptions, K as EvictionPolicy, M as MemoryLayer, N as MemoryLayerOptions, O as MemoryLayerSnapshotEntry, P as PatternMatcher, T as TagIndex, Q as createHonoCacheMiddleware } from './edge-CUHTP9Bc.cjs';
1
+ export { e as CacheGetOptions, f as CacheLayer, h as CacheLayerSetManyEntry, t as CacheMetricsSnapshot, w as CacheRateLimitOptions, B as CacheTtlPolicy, D as CacheTtlPolicyContext, J as CacheWriteOptions, K as EvictionPolicy, M as MemoryLayer, N as MemoryLayerOptions, O as MemoryLayerSnapshotEntry, P as PatternMatcher, T as TagIndex, Q as createHonoCacheMiddleware } from './edge-DKkrQ_Ky.cjs';
2
2
  import 'node:events';
package/dist/edge.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { e as CacheGetOptions, f as CacheLayer, h as CacheLayerSetManyEntry, t as CacheMetricsSnapshot, w as CacheRateLimitOptions, B as CacheTtlPolicy, D as CacheTtlPolicyContext, J as CacheWriteOptions, K as EvictionPolicy, M as MemoryLayer, N as MemoryLayerOptions, O as MemoryLayerSnapshotEntry, P as PatternMatcher, T as TagIndex, Q as createHonoCacheMiddleware } from './edge-CUHTP9Bc.js';
1
+ export { e as CacheGetOptions, f as CacheLayer, h as CacheLayerSetManyEntry, t as CacheMetricsSnapshot, w as CacheRateLimitOptions, B as CacheTtlPolicy, D as CacheTtlPolicyContext, J as CacheWriteOptions, K as EvictionPolicy, M as MemoryLayer, N as MemoryLayerOptions, O as MemoryLayerSnapshotEntry, P as PatternMatcher, T as TagIndex, Q as createHonoCacheMiddleware } from './edge-DKkrQ_Ky.js';
2
2
  import 'node:events';
package/dist/edge.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  MemoryLayer,
3
3
  TagIndex,
4
4
  createHonoCacheMiddleware
5
- } from "./chunk-GJBKCFE6.js";
5
+ } from "./chunk-5RCAX2BQ.js";
6
6
  import {
7
7
  PatternMatcher
8
8
  } from "./chunk-4PPBOOXT.js";