layercache 1.3.4 → 2.0.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.
Files changed (40) hide show
  1. package/README.md +42 -41
  2. package/dist/{chunk-BORDQ3LA.js → chunk-7KMKQ6QZ.js} +15 -1
  3. package/dist/{chunk-5RCAX2BQ.js → chunk-FFZCC7EQ.js} +3 -3
  4. package/dist/{chunk-4PPBOOXT.js → chunk-KJDFYE5T.js} +38 -26
  5. package/dist/cli.cjs +9 -9
  6. package/dist/cli.js +4 -4
  7. package/dist/{edge-DKkrQ_Ky.d.cts → edge-D2FpRlyS.d.cts} +71 -22
  8. package/dist/{edge-DKkrQ_Ky.d.ts → edge-D2FpRlyS.d.ts} +71 -22
  9. package/dist/edge.cjs +9 -9
  10. package/dist/edge.d.cts +1 -1
  11. package/dist/edge.d.ts +1 -1
  12. package/dist/edge.js +2 -2
  13. package/dist/index.cjs +399 -164
  14. package/dist/index.d.cts +6 -6
  15. package/dist/index.d.ts +6 -6
  16. package/dist/index.js +294 -81
  17. package/package.json +5 -5
  18. package/benchmarks/direct.ts +0 -221
  19. package/benchmarks/edge-utils.ts +0 -28
  20. package/benchmarks/edge.ts +0 -491
  21. package/benchmarks/http.ts +0 -99
  22. package/benchmarks/latency.ts +0 -45
  23. package/benchmarks/memory-pressure.ts +0 -144
  24. package/benchmarks/multi-process-fanout.ts +0 -231
  25. package/benchmarks/multi-process-worker.ts +0 -151
  26. package/benchmarks/paths.ts +0 -25
  27. package/benchmarks/queue-amplification-utils.ts +0 -48
  28. package/benchmarks/queue-amplification.ts +0 -230
  29. package/benchmarks/redis-latency-proxy.ts +0 -100
  30. package/benchmarks/redis.ts +0 -107
  31. package/benchmarks/scenario-utils.ts +0 -38
  32. package/benchmarks/server.ts +0 -157
  33. package/benchmarks/slow-redis-latency.ts +0 -309
  34. package/benchmarks/slow-redis-utils.ts +0 -29
  35. package/benchmarks/slow-redis.ts +0 -47
  36. package/benchmarks/stampede.ts +0 -26
  37. package/benchmarks/stats.ts +0 -46
  38. package/benchmarks/workload.ts +0 -77
  39. package/examples/express-api/index.ts +0 -31
  40. package/examples/nextjs-api-routes/route.ts +0 -16
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
  <a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache_2.0-green" alt="license"></a>
20
20
  <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-first-3178C6?logo=typescript&logoColor=white" alt="TypeScript"></a>
21
21
  <img src="https://img.shields.io/badge/Node.js-%E2%89%A5_20-339933?logo=nodedotjs&logoColor=white" alt="Node.js >= 20">
22
- <img src="https://img.shields.io/badge/tests-529_passing-brightgreen" alt="tests">
22
+ <img src="https://img.shields.io/badge/tests-549_passing-brightgreen" alt="tests">
23
23
  <a href="https://coveralls.io/github/flyingsquirrel0419/layercache?branch=main"><img src="https://coveralls.io/repos/github/flyingsquirrel0419/layercache/badge.svg?branch=main&t=20260410" alt="Coveralls"></a>
24
24
  </p>
25
25
 
@@ -64,8 +64,8 @@ import { CacheStack, MemoryLayer, RedisLayer } from 'layercache'
64
64
  import Redis from 'ioredis'
65
65
 
66
66
  const cache = new CacheStack([
67
- new MemoryLayer({ ttl: 60, maxSize: 1_000 }), // L1: in-process
68
- new RedisLayer({ client: new Redis(), ttl: 3600 }), // L2: shared
67
+ new MemoryLayer({ ttl: 60_000, maxSize: 1_000 }), // L1: in-process
68
+ new RedisLayer({ client: new Redis(), ttl: 3_600_000 }), // L2: shared
69
69
  ])
70
70
 
71
71
  // Read-through: fetcher runs once, all layers filled
@@ -77,7 +77,7 @@ const user = await cache.get('user:123', () => db.findUser(123))
77
77
 
78
78
  ```ts
79
79
  const cache = new CacheStack([
80
- new MemoryLayer({ ttl: 60 })
80
+ new MemoryLayer({ ttl: 60_000 })
81
81
  ])
82
82
  ```
83
83
 
@@ -90,8 +90,8 @@ const cache = new CacheStack([
90
90
  import { CacheStack, MemoryLayer, RedisLayer, DiskLayer } from 'layercache'
91
91
 
92
92
  const cache = new CacheStack([
93
- new MemoryLayer({ ttl: 60, maxSize: 5_000 }),
94
- new RedisLayer({ client: new Redis(), ttl: 3600, compression: 'gzip' }),
93
+ new MemoryLayer({ ttl: 60_000, maxSize: 5_000 }),
94
+ new RedisLayer({ client: new Redis(), ttl: 3_600_000, compression: 'gzip' }),
95
95
  new DiskLayer({ directory: './var/cache', maxFiles: 10_000 }),
96
96
  ])
97
97
  ```
@@ -172,10 +172,10 @@ import {
172
172
  import Redis from 'ioredis'
173
173
 
174
174
  const cache = new CacheStack([
175
- new MemoryLayer({ ttl: 60 }), // s
175
+ new MemoryLayer({ ttl: 60_000 }), // ms
176
176
  new RedisLayer({
177
177
  client: new Redis(),
178
- ttl: 300 // s
178
+ ttl: 300_000 // ms
179
179
  })
180
180
  ])
181
181
 
@@ -194,38 +194,38 @@ const cache = new CacheStack([
194
194
 
195
195
  ## Comparison
196
196
 
197
- | | node-cache-manager | keyv | cacheable | **layercache** |
198
- |---|:---:|:---:|:---:|:---:|
199
- | Multi-layer + auto backfill | Partial | Plugin | -- | **Yes** |
200
- | Stampede prevention | -- | -- | -- | **Yes** |
201
- | Tag invalidation | -- | Yes | Yes | **Yes** |
202
- | TypeScript-first | Partial | Yes | Yes | **Yes** |
203
- | Event hooks | Yes | Yes | Yes | **Yes** |
197
+ | | node-cache-manager | keyv | cacheable | BentoCache | **layercache** |
198
+ |---|:---:|:---:|:---:|:---:|:---:|
199
+ | Multi-layer + auto backfill | Partial | Plugin | -- | Partial | **Yes** |
200
+ | Stampede prevention | -- | -- | -- | Partial | **Yes** |
201
+ | Tag invalidation | -- | Yes | Yes | Yes | **Yes** |
202
+ | TypeScript-first | Partial | Yes | Yes | Yes | **Yes** |
203
+ | Event hooks | Yes | Yes | Yes | Yes | **Yes** |
204
204
 
205
205
  <details>
206
206
  <summary>Full comparison (19 features)</summary>
207
207
 
208
- | | node-cache-manager | keyv | cacheable | **layercache** |
209
- |---|:---:|:---:|:---:|:---:|
210
- | Multi-layer with auto backfill | Partial | Plugin | -- | **Yes** |
211
- | Stampede prevention | -- | -- | -- | **Yes** |
212
- | Distributed single-flight | -- | -- | -- | **Yes** |
213
- | Tag invalidation | -- | Yes | Yes | **Yes** |
214
- | Distributed tags | -- | -- | -- | **Yes** |
215
- | Cross-server L1 flush | -- | -- | -- | **Yes** |
216
- | Stale-while-revalidate | -- | -- | -- | **Yes** |
217
- | Circuit breaker | -- | -- | -- | **Yes** |
218
- | Graceful degradation | -- | -- | -- | **Yes** |
219
- | Sliding / adaptive TTL | -- | -- | -- | **Yes** |
220
- | Cache warming | -- | -- | -- | **Yes** |
221
- | Persistence / snapshots | -- | -- | -- | **Yes** |
222
- | Compression | -- | -- | Yes | **Yes** |
223
- | Admin CLI | -- | -- | -- | **Yes** |
224
- | TypeScript-first | Partial | Yes | Yes | **Yes** |
225
- | Wrap / decorator API | Yes | -- | -- | **Yes** |
226
- | Namespaces | -- | Yes | Yes | **Yes** |
227
- | Event hooks | Yes | Yes | Yes | **Yes** |
228
- | Custom layers | Partial | -- | -- | **Yes** |
208
+ | | node-cache-manager | keyv | cacheable | BentoCache | **layercache** |
209
+ |---|:---:|:---:|:---:|:---:|:---:|
210
+ | Multi-layer with auto backfill | Partial | Plugin | -- | Partial | **Yes** |
211
+ | Stampede prevention | -- | -- | -- | Partial | **Yes** |
212
+ | Distributed single-flight | -- | -- | -- | -- | **Yes** |
213
+ | Tag invalidation | -- | Yes | Yes | Yes | **Yes** |
214
+ | Distributed tags | -- | -- | -- | -- | **Yes** |
215
+ | Cross-server L1 flush | -- | -- | -- | Yes | **Yes** |
216
+ | Stale-while-revalidate | -- | -- | -- | Yes | **Yes** |
217
+ | Circuit breaker | -- | -- | -- | Yes | **Yes** |
218
+ | Graceful degradation | -- | -- | -- | Yes | **Yes** |
219
+ | Sliding / adaptive TTL | -- | -- | -- | -- | **Yes** |
220
+ | Cache warming | -- | -- | -- | -- | **Yes** |
221
+ | Persistence / snapshots | -- | -- | -- | -- | **Yes** |
222
+ | Compression | -- | -- | Yes | -- | **Yes** |
223
+ | Admin CLI | -- | -- | -- | -- | **Yes** |
224
+ | TypeScript-first | Partial | Yes | Yes | Yes | **Yes** |
225
+ | Wrap / decorator API | Yes | -- | -- | Partial | **Yes** |
226
+ | Namespaces | -- | Yes | Yes | Yes | **Yes** |
227
+ | Event hooks | Yes | Yes | Yes | Yes | **Yes** |
228
+ | Custom layers | Partial | -- | -- | Yes | **Yes** |
229
229
 
230
230
  </details>
231
231
 
@@ -265,6 +265,7 @@ const cache = new CacheStack([
265
265
  | **Adaptive TTL** | Auto-ramp TTL for hot keys up to a ceiling |
266
266
  | **Refresh-ahead** | Proactively refresh before expiry |
267
267
  | **TTL policies** | Align expirations to calendar boundaries (`until-midnight`, `next-hour`, custom) |
268
+ | **Context-aware entry options** | Derive TTLs and tags from the cached value right before storage |
268
269
 
269
270
  ### Resilience & Operations
270
271
 
@@ -286,7 +287,7 @@ const cache = new CacheStack([
286
287
  | **Metrics** | Hits, misses, fetches, stale hits, circuit breaker trips, and more |
287
288
  | **Per-layer latency** | Avg, max, and sample count using Welford's algorithm |
288
289
  | **Health checks** | Async health endpoint per layer with latency measurement |
289
- | **Event hooks** | `hit`, `miss`, `set`, `delete`, `stale-serve`, `stampede-dedupe`, `backfill`, `warm`, `error` |
290
+ | **Event hooks** | `hit`, `miss`, `set`, `delete`, `expire`, `stale-serve`, `stampede-dedupe`, `backfill`, `warm`, `error` |
290
291
  | **OpenTelemetry** | Hook-based distributed tracing support without method monkey-patching |
291
292
  | **Prometheus exporter** | Metrics export including latency gauges |
292
293
  | **HTTP stats handler** | JSON endpoint for dashboards |
@@ -316,10 +317,10 @@ layercache plugs into the frameworks you already use:
316
317
  ```ts
317
318
  import { CacheStack, MemoryLayer, createExpressCacheMiddleware } from 'layercache'
318
319
 
319
- const cache = new CacheStack([new MemoryLayer({ ttl: 60 })])
320
+ const cache = new CacheStack([new MemoryLayer({ ttl: 60_000 })])
320
321
 
321
322
  app.get('/api/users', createExpressCacheMiddleware(cache, {
322
- ttl: 30,
323
+ ttl: 30_000,
323
324
  tags: ['users'],
324
325
  keyResolver: (req) => `users:${req.url}`
325
326
  }), async (req, res) => {
@@ -381,8 +382,8 @@ const coordinator = new RedisSingleFlightCoordinator({ client: redis })
381
382
 
382
383
  const cache = new CacheStack(
383
384
  [
384
- new MemoryLayer({ ttl: 60, maxSize: 10_000 }),
385
- new RedisLayer({ client: redis, ttl: 3600, prefix: 'myapp:cache:' })
385
+ new MemoryLayer({ ttl: 60_000, maxSize: 10_000 }),
386
+ new RedisLayer({ client: redis, ttl: 3_600_000, prefix: 'myapp:cache:' })
386
387
  ],
387
388
  {
388
389
  invalidationBus: bus,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  PatternMatcher
3
- } from "./chunk-4PPBOOXT.js";
3
+ } from "./chunk-KJDFYE5T.js";
4
4
 
5
5
  // src/internal/CacheStackValidation.ts
6
6
  var MAX_CACHE_KEY_LENGTH = 1024;
@@ -125,6 +125,19 @@ function validateCircuitBreakerOptions(options) {
125
125
  validatePositiveNumber("circuitBreaker.failureThreshold", options.failureThreshold);
126
126
  validatePositiveNumber("circuitBreaker.cooldownMs", options.cooldownMs);
127
127
  }
128
+ function validateContextEntryOptions(name, options) {
129
+ if (!options) {
130
+ return;
131
+ }
132
+ validateLayerNumberOption(`${name}.ttl`, options.ttl);
133
+ validateLayerNumberOption(`${name}.negativeTtl`, options.negativeTtl);
134
+ validateLayerNumberOption(`${name}.staleWhileRevalidate`, options.staleWhileRevalidate);
135
+ validateLayerNumberOption(`${name}.staleIfError`, options.staleIfError);
136
+ validateLayerNumberOption(`${name}.ttlJitter`, options.ttlJitter);
137
+ validateTtlPolicy(`${name}.ttlPolicy`, options.ttlPolicy);
138
+ validateAdaptiveTtlOptions(options.adaptiveTtl);
139
+ validateTags(options.tags);
140
+ }
128
141
 
129
142
  // src/invalidation/RedisTagIndex.ts
130
143
  var RedisTagIndex = class {
@@ -318,5 +331,6 @@ export {
318
331
  validateTtlPolicy,
319
332
  validateAdaptiveTtlOptions,
320
333
  validateCircuitBreakerOptions,
334
+ validateContextEntryOptions,
321
335
  RedisTagIndex
322
336
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  unwrapStoredValue
3
- } from "./chunk-4PPBOOXT.js";
3
+ } from "./chunk-KJDFYE5T.js";
4
4
 
5
5
  // src/layers/MemoryLayer.ts
6
6
  var MemoryLayer = class {
@@ -57,7 +57,7 @@ var MemoryLayer = class {
57
57
  this.entries.delete(key);
58
58
  this.entries.set(key, {
59
59
  value,
60
- expiresAt: ttl && ttl > 0 ? Date.now() + ttl * 1e3 : null,
60
+ expiresAt: ttl && ttl > 0 ? Date.now() + ttl : null,
61
61
  accessCount: 0,
62
62
  insertedAt: Date.now()
63
63
  });
@@ -88,7 +88,7 @@ var MemoryLayer = class {
88
88
  if (entry.expiresAt === null) {
89
89
  return null;
90
90
  }
91
- return Math.max(0, Math.ceil((entry.expiresAt - Date.now()) / 1e3));
91
+ return Math.max(0, Math.ceil(entry.expiresAt - Date.now()));
92
92
  }
93
93
  async size() {
94
94
  this.pruneExpired();
@@ -38,29 +38,29 @@ function isStoredValueEnvelope(value) {
38
38
  if (typeof v.freshUntil === "number" && typeof v.errorUntil === "number" && v.errorUntil < v.freshUntil) {
39
39
  return false;
40
40
  }
41
- const maxTtlSeconds = 10 * 365 * 24 * 60 * 60;
42
- if (!isValidEnvelopeTtlSeconds(v.freshTtlSeconds, maxTtlSeconds)) {
41
+ const maxTtlMs = 10 * 365 * 24 * 60 * 60 * 1e3;
42
+ if (!isValidEnvelopeTtlMs(v.freshTtlMs, maxTtlMs)) {
43
43
  return false;
44
44
  }
45
- if (!isValidEnvelopeTtlSeconds(v.staleWhileRevalidateSeconds, maxTtlSeconds)) {
45
+ if (!isValidEnvelopeTtlMs(v.staleWhileRevalidateMs, maxTtlMs)) {
46
46
  return false;
47
47
  }
48
- if (!isValidEnvelopeTtlSeconds(v.staleIfErrorSeconds, maxTtlSeconds)) {
48
+ if (!isValidEnvelopeTtlMs(v.staleIfErrorMs, maxTtlMs)) {
49
49
  return false;
50
50
  }
51
- if (v.freshTtlSeconds == null && (v.staleWhileRevalidateSeconds != null || v.staleIfErrorSeconds != null)) {
51
+ if (v.freshTtlMs == null && (v.staleWhileRevalidateMs != null || v.staleIfErrorMs != null)) {
52
52
  return false;
53
53
  }
54
54
  return true;
55
55
  }
56
56
  function createStoredValueEnvelope(options) {
57
57
  const now = options.now ?? Date.now();
58
- const freshTtlSeconds = normalizePositiveSeconds(options.freshTtlSeconds);
59
- const staleWhileRevalidateSeconds = normalizePositiveSeconds(options.staleWhileRevalidateSeconds);
60
- const staleIfErrorSeconds = normalizePositiveSeconds(options.staleIfErrorSeconds);
61
- const freshUntil = freshTtlSeconds ? now + freshTtlSeconds * 1e3 : null;
62
- const staleUntil = freshUntil && staleWhileRevalidateSeconds ? freshUntil + staleWhileRevalidateSeconds * 1e3 : null;
63
- const errorUntil = freshUntil && staleIfErrorSeconds ? freshUntil + staleIfErrorSeconds * 1e3 : null;
58
+ const freshTtlMs = normalizePositiveMs(options.freshTtlMs);
59
+ const staleWhileRevalidateMs = normalizePositiveMs(options.staleWhileRevalidateMs);
60
+ const staleIfErrorMs = normalizePositiveMs(options.staleIfErrorMs);
61
+ const freshUntil = freshTtlMs ? now + freshTtlMs : null;
62
+ const staleUntil = freshUntil && staleWhileRevalidateMs ? freshUntil + staleWhileRevalidateMs : null;
63
+ const errorUntil = freshUntil && staleIfErrorMs ? freshUntil + staleIfErrorMs : null;
64
64
  return {
65
65
  __layercache: 1,
66
66
  kind: options.kind,
@@ -68,9 +68,9 @@ function createStoredValueEnvelope(options) {
68
68
  freshUntil,
69
69
  staleUntil,
70
70
  errorUntil,
71
- freshTtlSeconds: freshTtlSeconds ?? null,
72
- staleWhileRevalidateSeconds: staleWhileRevalidateSeconds ?? null,
73
- staleIfErrorSeconds: staleIfErrorSeconds ?? null
71
+ freshTtlMs: freshTtlMs ?? null,
72
+ staleWhileRevalidateMs: staleWhileRevalidateMs ?? null,
73
+ staleIfErrorMs: staleIfErrorMs ?? null
74
74
  };
75
75
  }
76
76
  function resolveStoredValue(stored, now = Date.now()) {
@@ -97,7 +97,7 @@ function unwrapStoredValue(stored) {
97
97
  }
98
98
  return stored.value ?? null;
99
99
  }
100
- function remainingStoredTtlSeconds(stored, now = Date.now()) {
100
+ function remainingStoredTtlMs(stored, now = Date.now()) {
101
101
  if (!isStoredValueEnvelope(stored)) {
102
102
  return void 0;
103
103
  }
@@ -109,9 +109,9 @@ function remainingStoredTtlSeconds(stored, now = Date.now()) {
109
109
  if (remainingMs <= 0) {
110
110
  return 1;
111
111
  }
112
- return Math.max(1, Math.ceil(remainingMs / 1e3));
112
+ return Math.max(1, Math.ceil(remainingMs));
113
113
  }
114
- function remainingFreshTtlSeconds(stored, now = Date.now()) {
114
+ function remainingFreshTtlMs(stored, now = Date.now()) {
115
115
  if (!isStoredValueEnvelope(stored) || stored.freshUntil === null) {
116
116
  return void 0;
117
117
  }
@@ -119,7 +119,7 @@ function remainingFreshTtlSeconds(stored, now = Date.now()) {
119
119
  if (remainingMs <= 0) {
120
120
  return 0;
121
121
  }
122
- return Math.max(1, Math.ceil(remainingMs / 1e3));
122
+ return Math.max(1, Math.ceil(remainingMs));
123
123
  }
124
124
  function refreshStoredEnvelope(stored, now = Date.now()) {
125
125
  if (!isStoredValueEnvelope(stored)) {
@@ -128,12 +128,23 @@ function refreshStoredEnvelope(stored, now = Date.now()) {
128
128
  return createStoredValueEnvelope({
129
129
  kind: stored.kind,
130
130
  value: stored.value,
131
- freshTtlSeconds: stored.freshTtlSeconds ?? void 0,
132
- staleWhileRevalidateSeconds: stored.staleWhileRevalidateSeconds ?? void 0,
133
- staleIfErrorSeconds: stored.staleIfErrorSeconds ?? void 0,
131
+ freshTtlMs: stored.freshTtlMs ?? void 0,
132
+ staleWhileRevalidateMs: stored.staleWhileRevalidateMs ?? void 0,
133
+ staleIfErrorMs: stored.staleIfErrorMs ?? void 0,
134
134
  now
135
135
  });
136
136
  }
137
+ function expireStoredEnvelope(stored, now = Date.now()) {
138
+ if (!isStoredValueEnvelope(stored)) {
139
+ return stored;
140
+ }
141
+ const futureDeadlines = [stored.staleUntil, stored.errorUntil].filter((value) => value !== null);
142
+ const freshUntil = futureDeadlines.length > 0 ? Math.min(now, ...futureDeadlines) : now;
143
+ return {
144
+ ...stored,
145
+ freshUntil
146
+ };
147
+ }
137
148
  function maxExpiry(stored) {
138
149
  const values = [stored.freshUntil, stored.staleUntil, stored.errorUntil].filter(
139
150
  (value) => value !== null
@@ -143,17 +154,17 @@ function maxExpiry(stored) {
143
154
  }
144
155
  return Math.max(...values);
145
156
  }
146
- function normalizePositiveSeconds(value) {
157
+ function normalizePositiveMs(value) {
147
158
  if (!value || value <= 0) {
148
159
  return void 0;
149
160
  }
150
161
  return value;
151
162
  }
152
- function isValidEnvelopeTtlSeconds(value, maxTtlSeconds) {
163
+ function isValidEnvelopeTtlMs(value, maxTtlMs) {
153
164
  if (value == null) {
154
165
  return true;
155
166
  }
156
- return typeof value === "number" && Number.isFinite(value) && value > 0 && value <= maxTtlSeconds;
167
+ return typeof value === "number" && Number.isFinite(value) && value > 0 && value <= maxTtlMs;
157
168
  }
158
169
 
159
170
  // src/invalidation/PatternMatcher.ts
@@ -209,8 +220,9 @@ export {
209
220
  createStoredValueEnvelope,
210
221
  resolveStoredValue,
211
222
  unwrapStoredValue,
212
- remainingStoredTtlSeconds,
213
- remainingFreshTtlSeconds,
223
+ remainingStoredTtlMs,
224
+ remainingFreshTtlMs,
214
225
  refreshStoredEnvelope,
226
+ expireStoredEnvelope,
215
227
  PatternMatcher
216
228
  };
package/dist/cli.cjs CHANGED
@@ -121,17 +121,17 @@ function isStoredValueEnvelope(value) {
121
121
  if (typeof v.freshUntil === "number" && typeof v.errorUntil === "number" && v.errorUntil < v.freshUntil) {
122
122
  return false;
123
123
  }
124
- const maxTtlSeconds = 10 * 365 * 24 * 60 * 60;
125
- if (!isValidEnvelopeTtlSeconds(v.freshTtlSeconds, maxTtlSeconds)) {
124
+ const maxTtlMs = 10 * 365 * 24 * 60 * 60 * 1e3;
125
+ if (!isValidEnvelopeTtlMs(v.freshTtlMs, maxTtlMs)) {
126
126
  return false;
127
127
  }
128
- if (!isValidEnvelopeTtlSeconds(v.staleWhileRevalidateSeconds, maxTtlSeconds)) {
128
+ if (!isValidEnvelopeTtlMs(v.staleWhileRevalidateMs, maxTtlMs)) {
129
129
  return false;
130
130
  }
131
- if (!isValidEnvelopeTtlSeconds(v.staleIfErrorSeconds, maxTtlSeconds)) {
131
+ if (!isValidEnvelopeTtlMs(v.staleIfErrorMs, maxTtlMs)) {
132
132
  return false;
133
133
  }
134
- if (v.freshTtlSeconds == null && (v.staleWhileRevalidateSeconds != null || v.staleIfErrorSeconds != null)) {
134
+ if (v.freshTtlMs == null && (v.staleWhileRevalidateMs != null || v.staleIfErrorMs != null)) {
135
135
  return false;
136
136
  }
137
137
  return true;
@@ -160,11 +160,11 @@ function unwrapStoredValue(stored) {
160
160
  }
161
161
  return stored.value ?? null;
162
162
  }
163
- function isValidEnvelopeTtlSeconds(value, maxTtlSeconds) {
163
+ function isValidEnvelopeTtlMs(value, maxTtlMs) {
164
164
  if (value == null) {
165
165
  return true;
166
166
  }
167
- return typeof value === "number" && Number.isFinite(value) && value > 0 && value <= maxTtlSeconds;
167
+ return typeof value === "number" && Number.isFinite(value) && value > 0 && value <= maxTtlMs;
168
168
  }
169
169
 
170
170
  // src/invalidation/PatternMatcher.ts
@@ -483,14 +483,14 @@ async function main(argv = process.argv.slice(2)) {
483
483
  }
484
484
  if (!validateCliInput(args.key, validateCacheKey)) return;
485
485
  const payload = await redis.getBuffer(args.key);
486
- const ttl = await redis.ttl(args.key);
486
+ const ttl = await redis.pttl(args.key);
487
487
  const decoded = decodeInspectablePayload(payload);
488
488
  process.stdout.write(
489
489
  `${JSON.stringify(
490
490
  {
491
491
  key: args.key,
492
492
  exists: payload !== null,
493
- ttlSeconds: ttl >= 0 ? ttl : null,
493
+ ttlMs: ttl >= 0 ? ttl : null,
494
494
  sizeBytes: payload?.byteLength ?? 0,
495
495
  isEnvelope: isStoredValueEnvelope(decoded),
496
496
  state: payload === null ? null : resolveStoredValue(decoded).state,
package/dist/cli.js CHANGED
@@ -4,11 +4,11 @@ import {
4
4
  validateCacheKey,
5
5
  validatePattern,
6
6
  validateTag
7
- } from "./chunk-BORDQ3LA.js";
7
+ } from "./chunk-7KMKQ6QZ.js";
8
8
  import {
9
9
  isStoredValueEnvelope,
10
10
  resolveStoredValue
11
- } from "./chunk-4PPBOOXT.js";
11
+ } from "./chunk-KJDFYE5T.js";
12
12
 
13
13
  // src/cli.ts
14
14
  import Redis from "ioredis";
@@ -99,14 +99,14 @@ async function main(argv = process.argv.slice(2)) {
99
99
  }
100
100
  if (!validateCliInput(args.key, validateCacheKey)) return;
101
101
  const payload = await redis.getBuffer(args.key);
102
- const ttl = await redis.ttl(args.key);
102
+ const ttl = await redis.pttl(args.key);
103
103
  const decoded = decodeInspectablePayload(payload);
104
104
  process.stdout.write(
105
105
  `${JSON.stringify(
106
106
  {
107
107
  key: args.key,
108
108
  exists: payload !== null,
109
- ttlSeconds: ttl >= 0 ? ttl : null,
109
+ ttlMs: ttl >= 0 ? ttl : null,
110
110
  sizeBytes: payload?.byteLength ?? 0,
111
111
  isEnvelope: isStoredValueEnvelope(decoded),
112
112
  state: payload === null ? null : resolveStoredValue(decoded).state,