layercache 1.3.0 → 1.3.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/README.md +2 -52
- package/dist/cli.cjs +23 -1
- package/dist/cli.js +23 -1
- package/dist/{edge-BXWTKlI1.d.cts → edge-CUHTP9Bc.d.cts} +2 -0
- package/dist/{edge-BXWTKlI1.d.ts → edge-CUHTP9Bc.d.ts} +2 -0
- package/dist/edge.d.cts +1 -1
- package/dist/edge.d.ts +1 -1
- package/dist/index.cjs +267 -31
- package/dist/index.d.cts +48 -3
- package/dist/index.d.ts +48 -3
- package/dist/index.js +265 -29
- package/package.json +2 -12
- package/examples/nestjs-module/app.module.ts +0 -15
- package/packages/nestjs/dist/index.cjs +0 -3906
- package/packages/nestjs/dist/index.d.cts +0 -627
- package/packages/nestjs/dist/index.d.ts +0 -627
- package/packages/nestjs/dist/index.js +0 -3869
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-
|
|
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
|
|
|
@@ -182,7 +182,6 @@ layercache plugs into the frameworks you already use:
|
|
|
182
182
|
| **Hono** | `createHonoCacheMiddleware(cache, opts)` - edge-compatible middleware |
|
|
183
183
|
| **tRPC** | `createTrpcCacheMiddleware(cache, prefix, opts)` - procedure middleware |
|
|
184
184
|
| **GraphQL** | `cacheGraphqlResolver(cache, prefix, resolver, opts)` - field resolver wrapper |
|
|
185
|
-
| **NestJS** | `@cachestack/nestjs` - `CacheStackModule.forRoot()`, `@Cacheable()` decorator |
|
|
186
185
|
| **Next.js** | Works natively with App Router and API routes |
|
|
187
186
|
| **OpenTelemetry** | `createOpenTelemetryPlugin(cache, tracer)` - event-driven tracing spans without monkey-patching |
|
|
188
187
|
|
|
@@ -205,42 +204,6 @@ app.get('/api/users', createExpressCacheMiddleware(cache, {
|
|
|
205
204
|
|
|
206
205
|
</details>
|
|
207
206
|
|
|
208
|
-
<details>
|
|
209
|
-
<summary><b>NestJS example</b></summary>
|
|
210
|
-
|
|
211
|
-
```bash
|
|
212
|
-
npm install @cachestack/nestjs
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
```ts
|
|
216
|
-
// app.module.ts
|
|
217
|
-
import { CacheStackModule } from '@cachestack/nestjs'
|
|
218
|
-
|
|
219
|
-
@Module({
|
|
220
|
-
imports: [
|
|
221
|
-
CacheStackModule.forRoot({
|
|
222
|
-
layers: [
|
|
223
|
-
new MemoryLayer({ ttl: 20 }),
|
|
224
|
-
new RedisLayer({ client: redis, ttl: 300 })
|
|
225
|
-
]
|
|
226
|
-
})
|
|
227
|
-
]
|
|
228
|
-
})
|
|
229
|
-
export class AppModule {}
|
|
230
|
-
|
|
231
|
-
// user.service.ts
|
|
232
|
-
@Injectable()
|
|
233
|
-
export class UserService {
|
|
234
|
-
constructor(@InjectCacheStack() private readonly cache: CacheStack) {}
|
|
235
|
-
|
|
236
|
-
async getUser(id: number) {
|
|
237
|
-
return this.cache.get(`user:${id}`, () => this.db.findUser(id))
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
</details>
|
|
243
|
-
|
|
244
207
|
<details>
|
|
245
208
|
<summary><b>Next.js App Router example</b></summary>
|
|
246
209
|
|
|
@@ -326,18 +289,7 @@ const cache = new CacheStack(
|
|
|
326
289
|
└─────────────────────┴────────┘
|
|
327
290
|
```
|
|
328
291
|
|
|
329
|
-
|
|
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.
|
|
292
|
+
Benchmark commands, fixtures, and scenario notes live in [docs/benchmarking.md](./docs/benchmarking.md).
|
|
341
293
|
|
|
342
294
|
---
|
|
343
295
|
|
|
@@ -359,7 +311,6 @@ The benchmark harness defaults to `/root/cache-test/data/users.json` so the in-r
|
|
|
359
311
|
| Persistence / snapshots | -- | -- | -- | **Yes** |
|
|
360
312
|
| Compression | -- | -- | Yes | **Yes** |
|
|
361
313
|
| Admin CLI | -- | -- | -- | **Yes** |
|
|
362
|
-
| NestJS module | -- | -- | -- | **Yes** |
|
|
363
314
|
| TypeScript-first | Partial | Yes | Yes | **Yes** |
|
|
364
315
|
| Wrap / decorator API | Yes | -- | -- | **Yes** |
|
|
365
316
|
| Namespaces | -- | Yes | Yes | **Yes** |
|
|
@@ -388,7 +339,6 @@ The benchmark harness defaults to `/root/cache-test/data/users.json` so the in-r
|
|
|
388
339
|
The [`examples/`](./examples) directory contains ready-to-run projects:
|
|
389
340
|
|
|
390
341
|
- [`express-api/`](./examples/express-api/) - Express REST API with layered caching
|
|
391
|
-
- [`nestjs-module/`](./examples/nestjs-module/) - NestJS module integration
|
|
392
342
|
- [`nextjs-api-routes/`](./examples/nextjs-api-routes/) - Next.js App Router with layercache
|
|
393
343
|
|
|
394
344
|
---
|
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-
|
|
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-
|
|
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';
|