layercache 1.3.0 → 1.3.1

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/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
  <a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache_2.0-green" alt="license"></a>
16
16
  <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-first-3178C6?logo=typescript&logoColor=white" alt="TypeScript"></a>
17
17
  <img src="https://img.shields.io/badge/Node.js-%E2%89%A5_20-339933?logo=nodedotjs&logoColor=white" alt="Node.js >= 20">
18
- <img src="https://img.shields.io/badge/tests-431_passing-brightgreen" alt="tests">
18
+ <img src="https://img.shields.io/badge/tests-467_passing-brightgreen" alt="tests">
19
19
  <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>
20
20
  </p>
21
21
 
@@ -326,18 +326,7 @@ const cache = new CacheStack(
326
326
  └─────────────────────┴────────┘
327
327
  ```
328
328
 
329
- Run benchmarks locally:
330
-
331
- ```bash
332
- npm run bench:direct
333
- npm run bench:edge
334
- npm run bench:slow-redis
335
- npm run bench:queue-amplification
336
- npm run bench:http
337
- npm run bench:multi-process-fanout
338
- ```
339
-
340
- The benchmark harness defaults to `/root/cache-test/data/users.json` so the in-repo scripts stay aligned with the external reproduction workspace. Set `LAYERCACHE_BENCH_FIXTURE_PATH` if you want to point at a different workload fixture.
329
+ Benchmark commands, fixtures, and scenario notes live in [docs/benchmarking.md](./docs/benchmarking.md).
341
330
 
342
331
  ---
343
332
 
package/dist/cli.cjs CHANGED
@@ -365,6 +365,18 @@ async function main(argv = process.argv.slice(2)) {
365
365
  process.exitCode = 1;
366
366
  return;
367
367
  }
368
+ if (isPlaintextRedisUrl(redisUrl)) {
369
+ if (args.requireTls) {
370
+ process.stderr.write(
371
+ "Error: --require-tls is set but the URL uses redis:// (plaintext). Use rediss:// for TLS-encrypted connections.\n"
372
+ );
373
+ process.exitCode = 1;
374
+ return;
375
+ }
376
+ process.stderr.write(
377
+ "Warning: connecting to Redis without TLS (redis://). All data including cached values and credentials will be transmitted in plaintext. Use rediss:// in production environments, or set --require-tls.\n"
378
+ );
379
+ }
368
380
  const redis = new import_ioredis.default(redisUrl, {
369
381
  connectTimeout: CONNECT_TIMEOUT_MS,
370
382
  lazyConnect: true,
@@ -484,6 +496,8 @@ function parseArgs(argv) {
484
496
  } else if (token === "--tag-index-prefix") {
485
497
  parsed.tagIndexPrefix = value;
486
498
  index += 1;
499
+ } else if (token === "--require-tls") {
500
+ parsed.requireTls = true;
487
501
  }
488
502
  }
489
503
  return parsed;
@@ -515,7 +529,7 @@ async function scanKeys(redis, pattern) {
515
529
  }
516
530
  function printUsage() {
517
531
  process.stdout.write(
518
- "Usage:\n layercache stats --redis <url> [--pattern <glob>]\n layercache keys --redis <url> [--pattern <glob>]\n layercache inspect --redis <url> --key <key>\n layercache invalidate --redis <url> [--pattern <glob> | --tag <tag>] [--tag-index-prefix <prefix>]\n\nOptions:\n --redis <url> Redis connection URL (e.g. redis://localhost:6379)\n --pattern <glob> Glob pattern to filter keys (default: *)\n --key <key> Exact cache key to inspect\n --tag <tag> Invalidate by tag name\n --tag-index-prefix <prefix> Redis key prefix for tag index (default: layercache:tag-index)\n"
532
+ "Usage:\n layercache stats --redis <url> [--pattern <glob>]\n layercache keys --redis <url> [--pattern <glob>]\n layercache inspect --redis <url> --key <key>\n layercache invalidate --redis <url> [--pattern <glob> | --tag <tag>] [--tag-index-prefix <prefix>]\n\nOptions:\n --redis <url> Redis connection URL (e.g. redis://localhost:6379)\n --pattern <glob> Glob pattern to filter keys (default: *)\n --key <key> Exact cache key to inspect\n --tag <tag> Invalidate by tag name\n --tag-index-prefix <prefix> Redis key prefix for tag index (default: layercache:tag-index)\n --require-tls Reject non-TLS (redis://) connections\n"
519
533
  );
520
534
  }
521
535
  function decodeInspectablePayload(payload) {
@@ -541,6 +555,14 @@ function summarizeInspectableValue(value) {
541
555
  }
542
556
  return value;
543
557
  }
558
+ function isPlaintextRedisUrl(url) {
559
+ try {
560
+ const parsed = new URL(url);
561
+ return parsed.protocol === "redis:";
562
+ } catch {
563
+ return true;
564
+ }
565
+ }
544
566
  function maskRedisUrl(url) {
545
567
  try {
546
568
  const parsed = new URL(url);
package/dist/cli.js CHANGED
@@ -23,6 +23,18 @@ async function main(argv = process.argv.slice(2)) {
23
23
  process.exitCode = 1;
24
24
  return;
25
25
  }
26
+ if (isPlaintextRedisUrl(redisUrl)) {
27
+ if (args.requireTls) {
28
+ process.stderr.write(
29
+ "Error: --require-tls is set but the URL uses redis:// (plaintext). Use rediss:// for TLS-encrypted connections.\n"
30
+ );
31
+ process.exitCode = 1;
32
+ return;
33
+ }
34
+ process.stderr.write(
35
+ "Warning: connecting to Redis without TLS (redis://). All data including cached values and credentials will be transmitted in plaintext. Use rediss:// in production environments, or set --require-tls.\n"
36
+ );
37
+ }
26
38
  const redis = new Redis(redisUrl, {
27
39
  connectTimeout: CONNECT_TIMEOUT_MS,
28
40
  lazyConnect: true,
@@ -142,6 +154,8 @@ function parseArgs(argv) {
142
154
  } else if (token === "--tag-index-prefix") {
143
155
  parsed.tagIndexPrefix = value;
144
156
  index += 1;
157
+ } else if (token === "--require-tls") {
158
+ parsed.requireTls = true;
145
159
  }
146
160
  }
147
161
  return parsed;
@@ -173,7 +187,7 @@ async function scanKeys(redis, pattern) {
173
187
  }
174
188
  function printUsage() {
175
189
  process.stdout.write(
176
- "Usage:\n layercache stats --redis <url> [--pattern <glob>]\n layercache keys --redis <url> [--pattern <glob>]\n layercache inspect --redis <url> --key <key>\n layercache invalidate --redis <url> [--pattern <glob> | --tag <tag>] [--tag-index-prefix <prefix>]\n\nOptions:\n --redis <url> Redis connection URL (e.g. redis://localhost:6379)\n --pattern <glob> Glob pattern to filter keys (default: *)\n --key <key> Exact cache key to inspect\n --tag <tag> Invalidate by tag name\n --tag-index-prefix <prefix> Redis key prefix for tag index (default: layercache:tag-index)\n"
190
+ "Usage:\n layercache stats --redis <url> [--pattern <glob>]\n layercache keys --redis <url> [--pattern <glob>]\n layercache inspect --redis <url> --key <key>\n layercache invalidate --redis <url> [--pattern <glob> | --tag <tag>] [--tag-index-prefix <prefix>]\n\nOptions:\n --redis <url> Redis connection URL (e.g. redis://localhost:6379)\n --pattern <glob> Glob pattern to filter keys (default: *)\n --key <key> Exact cache key to inspect\n --tag <tag> Invalidate by tag name\n --tag-index-prefix <prefix> Redis key prefix for tag index (default: layercache:tag-index)\n --require-tls Reject non-TLS (redis://) connections\n"
177
191
  );
178
192
  }
179
193
  function decodeInspectablePayload(payload) {
@@ -199,6 +213,14 @@ function summarizeInspectableValue(value) {
199
213
  }
200
214
  return value;
201
215
  }
216
+ function isPlaintextRedisUrl(url) {
217
+ try {
218
+ const parsed = new URL(url);
219
+ return parsed.protocol === "redis:";
220
+ } catch {
221
+ return true;
222
+ }
223
+ }
202
224
  function maskRedisUrl(url) {
203
225
  try {
204
226
  const parsed = new URL(url);
@@ -178,6 +178,8 @@ interface CacheStackOptions {
178
178
  logger?: CacheLogger | boolean;
179
179
  metrics?: boolean;
180
180
  stampedePrevention?: boolean;
181
+ stampedeMaxInFlight?: number;
182
+ stampedeEntryTimeoutMs?: number;
181
183
  invalidationBus?: InvalidationBus;
182
184
  tagIndex?: CacheTagIndex;
183
185
  generation?: number;
@@ -178,6 +178,8 @@ interface CacheStackOptions {
178
178
  logger?: CacheLogger | boolean;
179
179
  metrics?: boolean;
180
180
  stampedePrevention?: boolean;
181
+ stampedeMaxInFlight?: number;
182
+ stampedeEntryTimeoutMs?: number;
181
183
  invalidationBus?: InvalidationBus;
182
184
  tagIndex?: CacheTagIndex;
183
185
  generation?: number;
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-BXWTKlI1.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-CUHTP9Bc.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-BXWTKlI1.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-CUHTP9Bc.js';
2
2
  import 'node:events';