rust-node-cache 0.1.1 → 0.2.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 +65 -11
- package/binding.d.ts +38 -19
- package/dist/express.d.mts +10 -10
- package/dist/express.d.ts +10 -10
- package/dist/express.js.map +1 -1
- package/dist/express.mjs.map +1 -1
- package/dist/fastify.d.mts +9 -9
- package/dist/fastify.d.ts +9 -9
- package/dist/fastify.js.map +1 -1
- package/dist/fastify.mjs.map +1 -1
- package/dist/index.d.mts +52 -36
- package/dist/index.d.ts +52 -36
- package/dist/index.js +21 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +21 -12
- package/dist/index.mjs.map +1 -1
- package/dist/nestjs.d.mts +11 -11
- package/dist/nestjs.d.ts +11 -11
- package/dist/nestjs.js.map +1 -1
- package/dist/nestjs.mjs.map +1 -1
- package/package.json +4 -2
- package/rust-node-cache.darwin-arm64.node +0 -0
- package/rust-node-cache.darwin-x64.node +0 -0
- package/rust-node-cache.linux-arm64-gnu.node +0 -0
- package/rust-node-cache.linux-arm64-musl.node +0 -0
- package/rust-node-cache.linux-x64-gnu.node +0 -0
- package/rust-node-cache.linux-x64-musl.node +0 -0
- package/rust-node-cache.win32-x64-msvc.node +0 -0
package/README.md
CHANGED
|
@@ -24,8 +24,10 @@ is **no compiler required at install time** on supported platforms.
|
|
|
24
24
|
- 🦀 **Rust-powered** core for predictable, low-overhead performance
|
|
25
25
|
- 🔒 **Thread-safe** via `DashMap` (lock-sharded concurrent map)
|
|
26
26
|
- ⏱️ **TTL support** per entry (lazy expiration + active cleanup)
|
|
27
|
+
- 🧹 **Background expiration** — optional sweeper thread (`cleanupIntervalSeconds`)
|
|
28
|
+
- 🧠 **LRU eviction** — bound the cache by `maxSize` with `evictionPolicy: "lru"`
|
|
27
29
|
- ⚡ **Fast reads** and **fast writes**
|
|
28
|
-
- 📊 Built-in **statistics** (hits, misses, sets, deletes, expired, size)
|
|
30
|
+
- 📊 Built-in **statistics** (hits, misses, sets, deletes, expired, evicted, size)
|
|
29
31
|
- 🧩 **TypeScript** support with generics out of the box
|
|
30
32
|
- 🌐 **Framework agnostic** — works with Express, Fastify, NestJS, Hono,
|
|
31
33
|
Next.js, or plain Node.js
|
|
@@ -42,6 +44,25 @@ Works with both **ESM** and **CommonJS**, and requires **Node.js 18+**.
|
|
|
42
44
|
|
|
43
45
|
---
|
|
44
46
|
|
|
47
|
+
## Supported platforms
|
|
48
|
+
|
|
49
|
+
Prebuilt binaries are published for:
|
|
50
|
+
|
|
51
|
+
| OS | Architecture | Triple |
|
|
52
|
+
| ------- | --------------------- | ---------------------------- |
|
|
53
|
+
| macOS | arm64 | `aarch64-apple-darwin` |
|
|
54
|
+
| macOS | x64 | `x86_64-apple-darwin` |
|
|
55
|
+
| Linux | x64 (glibc) | `x86_64-unknown-linux-gnu` |
|
|
56
|
+
| Linux | arm64 (glibc) | `aarch64-unknown-linux-gnu` |
|
|
57
|
+
| Linux | x64 (musl, Alpine) | `x86_64-unknown-linux-musl` |
|
|
58
|
+
| Linux | arm64 (musl, Alpine) | `aarch64-unknown-linux-musl` |
|
|
59
|
+
| Windows | x64 | `x86_64-pc-windows-msvc` |
|
|
60
|
+
|
|
61
|
+
Every release is installed from the packed tarball and exercised on each of these platforms
|
|
62
|
+
in CI before it is published.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
45
66
|
## Quick Start
|
|
46
67
|
|
|
47
68
|
```ts
|
|
@@ -142,6 +163,35 @@ an active sweep:
|
|
|
142
163
|
const removed = cache.cleanupExpired(); // number of entries removed
|
|
143
164
|
```
|
|
144
165
|
|
|
166
|
+
Or let the cache sweep for you in the background — set `cleanupIntervalSeconds`
|
|
167
|
+
and a thread reclaims expired entries on its own (no manual `cleanupExpired()`):
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const cache = new Cache({ cleanupIntervalSeconds: 30 });
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The thread is tied to the cache's lifetime: it stops automatically when the cache
|
|
174
|
+
is garbage-collected.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Eviction (maxSize)
|
|
179
|
+
|
|
180
|
+
Cap the number of keys with `maxSize`. When the cache is full and a **new** key
|
|
181
|
+
is written, `evictionPolicy` decides what happens:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
// Default: reject the write (set returns false), keep existing entries.
|
|
185
|
+
const a = new Cache({ maxSize: 1000 }); // evictionPolicy: "reject"
|
|
186
|
+
|
|
187
|
+
// LRU: evict the least-recently-used entry to make room, then insert.
|
|
188
|
+
const b = new Cache({ maxSize: 1000, evictionPolicy: "lru" });
|
|
189
|
+
b.evictionPolicy; // "lru"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
LRU recency is tracked per entry (updated on every `get` hit) and the evicted
|
|
193
|
+
count is surfaced in `stats().evicted`. Overwriting an existing key never evicts.
|
|
194
|
+
|
|
145
195
|
---
|
|
146
196
|
|
|
147
197
|
## Statistics
|
|
@@ -159,6 +209,7 @@ Returns:
|
|
|
159
209
|
sets: 800,
|
|
160
210
|
deletes: 20,
|
|
161
211
|
expired: 15,
|
|
212
|
+
evicted: 40,
|
|
162
213
|
size: 780
|
|
163
214
|
}
|
|
164
215
|
```
|
|
@@ -208,8 +259,9 @@ Bincode) later without touching the cache logic.
|
|
|
208
259
|
|
|
209
260
|
| Method | Returns | Description |
|
|
210
261
|
| ------------------------------ | -------------- | ----------------------------------------------------------------- |
|
|
211
|
-
| `new Cache(options?)` | `Cache` | Create a cache
|
|
262
|
+
| `new Cache(options?)` | `Cache` | Create a cache (`maxSize`, `evictionPolicy`, `cleanupIntervalSeconds`). |
|
|
212
263
|
| `set(key, value, options?)` | `boolean` | Store/overwrite a value. `options.ttlSeconds` sets expiration. |
|
|
264
|
+
| `evictionPolicy` (getter) | `string` | The active policy: `"reject"` or `"lru"`. |
|
|
213
265
|
| `get<T>(key)` | `T \| null` | Read a value (lazy-expires stale entries). |
|
|
214
266
|
| `delete(key)` | `boolean` | Remove a key. `true` if it existed. |
|
|
215
267
|
| `exists(key)` | `boolean` | Whether the key exists and is valid. |
|
|
@@ -223,6 +275,8 @@ Bincode) later without touching the cache logic.
|
|
|
223
275
|
```ts
|
|
224
276
|
interface CacheOptions {
|
|
225
277
|
maxSize?: number;
|
|
278
|
+
evictionPolicy?: "reject" | "lru"; // default "reject"
|
|
279
|
+
cleanupIntervalSeconds?: number; // enables the background sweeper
|
|
226
280
|
}
|
|
227
281
|
|
|
228
282
|
interface SetOptions {
|
|
@@ -235,6 +289,7 @@ interface CacheStats {
|
|
|
235
289
|
sets: number;
|
|
236
290
|
deletes: number;
|
|
237
291
|
expired: number;
|
|
292
|
+
evicted: number;
|
|
238
293
|
size: number;
|
|
239
294
|
}
|
|
240
295
|
```
|
|
@@ -323,15 +378,14 @@ Coming soon.
|
|
|
323
378
|
|
|
324
379
|
## Roadmap
|
|
325
380
|
|
|
326
|
-
| Version | Feature |
|
|
327
|
-
| ------- | ------------------------ |
|
|
328
|
-
| v0.1 | Basic cache |
|
|
329
|
-
| v0.2 | TTL cleanup thread
|
|
330
|
-
| v0.3 |
|
|
331
|
-
| v0.4 |
|
|
332
|
-
| v0.5 |
|
|
333
|
-
| v0.6 |
|
|
334
|
-
| v0.7 | ImmutableLog integration |
|
|
381
|
+
| Version | Feature | Status |
|
|
382
|
+
| ------- | ------------------------ | ------ |
|
|
383
|
+
| v0.1 | Basic cache | ✅ |
|
|
384
|
+
| v0.2 | TTL cleanup thread + LRU eviction | ✅ |
|
|
385
|
+
| v0.3 | LFU cache | 🔜 |
|
|
386
|
+
| v0.4 | Redis synchronization | 🔜 |
|
|
387
|
+
| v0.5 | Prometheus metrics | 🔜 |
|
|
388
|
+
| v0.6 | ImmutableLog integration | 🔜 |
|
|
335
389
|
|
|
336
390
|
### Future: ImmutableLog Integration
|
|
337
391
|
|
package/binding.d.ts
CHANGED
|
@@ -3,52 +3,71 @@
|
|
|
3
3
|
|
|
4
4
|
/* auto-generated by NAPI-RS */
|
|
5
5
|
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Constructor options:
|
|
8
|
+
* `new Cache({ maxSize, evictionPolicy, cleanupIntervalSeconds })`.
|
|
9
|
+
*/
|
|
7
10
|
export interface CacheOptions {
|
|
8
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Optional limit on the number of keys. What happens when it is reached
|
|
13
|
+
* depends on `evictionPolicy`.
|
|
14
|
+
*/
|
|
9
15
|
maxSize?: number
|
|
16
|
+
/**
|
|
17
|
+
* `"reject"` (default) — `set` of a new key returns `false` when full;
|
|
18
|
+
* `"lru"` — removes the least recently used entry and goes on.
|
|
19
|
+
*/
|
|
20
|
+
evictionPolicy?: string
|
|
21
|
+
/**
|
|
22
|
+
* When set (> 0), starts a thread that sweeps expired entries every N
|
|
23
|
+
* seconds (background expiration, on top of the lazy one).
|
|
24
|
+
*/
|
|
25
|
+
cleanupIntervalSeconds?: number
|
|
10
26
|
}
|
|
11
|
-
/**
|
|
27
|
+
/** Per-write options: `cache.set(key, value, { ttlSeconds })`. */
|
|
12
28
|
export interface SetOptions {
|
|
13
|
-
/**
|
|
29
|
+
/** Time to live in seconds. Absent => the entry does not expire by time. */
|
|
14
30
|
ttlSeconds?: number
|
|
15
31
|
}
|
|
16
|
-
/**
|
|
32
|
+
/** Object returned by `cache.stats()`. `i64` fields become `number` in JS. */
|
|
17
33
|
export interface CacheStatsObject {
|
|
18
34
|
hits: number
|
|
19
35
|
misses: number
|
|
20
36
|
sets: number
|
|
21
37
|
deletes: number
|
|
22
38
|
expired: number
|
|
39
|
+
evicted: number
|
|
23
40
|
size: number
|
|
24
41
|
}
|
|
25
42
|
/**
|
|
26
|
-
*
|
|
43
|
+
* In-memory cache exposed to Node as the `Cache` class.
|
|
27
44
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
45
|
+
* It delegates everything to `RustCache` (which holds the `DashMap` and the
|
|
46
|
+
* atomic counters). Every method takes `&self`: the state is concurrent
|
|
47
|
+
* inside, so it is safe to use from several threads (such as the sweeper).
|
|
31
48
|
*/
|
|
32
49
|
export declare class Cache {
|
|
33
|
-
/** `new Cache()`
|
|
50
|
+
/** `new Cache()` or `new Cache({ maxSize, evictionPolicy, cleanupIntervalSeconds })`. */
|
|
34
51
|
constructor(options?: CacheOptions | undefined | null)
|
|
52
|
+
/** The active eviction policy: `"reject"` or `"lru"`. */
|
|
53
|
+
get evictionPolicy(): string
|
|
35
54
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
55
|
+
* Inserts or updates a key. Returns `true` on success, `false` if the cache
|
|
56
|
+
* is full (`maxSize`) and the key is new.
|
|
38
57
|
*/
|
|
39
58
|
set(key: string, value: any, options?: SetOptions | undefined | null): boolean
|
|
40
|
-
/**
|
|
59
|
+
/** Reads a key. Returns the value, or `null` if missing or expired. */
|
|
41
60
|
get(key: string): any | null
|
|
42
|
-
/**
|
|
61
|
+
/** Removes a key. Returns `true` if it existed, `false` otherwise. */
|
|
43
62
|
delete(key: string): boolean
|
|
44
|
-
/**
|
|
63
|
+
/** Whether the key exists and is still valid (not expired). */
|
|
45
64
|
exists(key: string): boolean
|
|
46
|
-
/**
|
|
65
|
+
/** Removes every entry from the cache. */
|
|
47
66
|
clear(): void
|
|
48
|
-
/**
|
|
67
|
+
/** Number of keys currently stored. */
|
|
49
68
|
size(): number
|
|
50
|
-
/**
|
|
69
|
+
/** Sweeps and removes every expired entry. Returns how many it removed. */
|
|
51
70
|
cleanupExpired(): number
|
|
52
|
-
/**
|
|
71
|
+
/** Accumulated statistics + the current size. */
|
|
53
72
|
stats(): CacheStatsObject
|
|
54
73
|
}
|
package/dist/express.d.mts
CHANGED
|
@@ -2,32 +2,32 @@ import { Request, Response, NextFunction } from 'express';
|
|
|
2
2
|
import { Cache } from './index.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Express integration: caching of JSON responses.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Usage:
|
|
8
8
|
* import { Cache } from "rust-node-cache";
|
|
9
9
|
* import { cacheMiddleware } from "rust-node-cache/express";
|
|
10
10
|
*
|
|
11
11
|
* const cache = new Cache();
|
|
12
12
|
* app.get("/users/:id", cacheMiddleware({ cache, ttlSeconds: 60 }), handler);
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* MISS
|
|
14
|
+
* The middleware intercepts `res.json`: on a HIT it responds straight from the
|
|
15
|
+
* cache; on a MISS it lets the handler run and stores the response body (2xx only).
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
interface CacheMiddlewareOptions {
|
|
19
|
-
/**
|
|
19
|
+
/** Cache instance to use. */
|
|
20
20
|
cache: Cache;
|
|
21
|
-
/** TTL
|
|
21
|
+
/** TTL applied to stored responses (in seconds). */
|
|
22
22
|
ttlSeconds?: number;
|
|
23
|
-
/**
|
|
23
|
+
/** HTTP methods eligible for caching. Default: `["GET"]`. */
|
|
24
24
|
methods?: string[];
|
|
25
|
-
/**
|
|
25
|
+
/** Builds the cache key from the request. Default: `METHOD:url`. */
|
|
26
26
|
keyGenerator?: (req: Request) => string;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
30
|
-
* `X-Cache: HIT|MISS`
|
|
29
|
+
* Creates an Express middleware that caches JSON responses. Adds an
|
|
30
|
+
* `X-Cache: HIT|MISS` header for observability.
|
|
31
31
|
*/
|
|
32
32
|
declare function cacheMiddleware(options: CacheMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => void;
|
|
33
33
|
|
package/dist/express.d.ts
CHANGED
|
@@ -2,32 +2,32 @@ import { Request, Response, NextFunction } from 'express';
|
|
|
2
2
|
import { Cache } from './index.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Express integration: caching of JSON responses.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Usage:
|
|
8
8
|
* import { Cache } from "rust-node-cache";
|
|
9
9
|
* import { cacheMiddleware } from "rust-node-cache/express";
|
|
10
10
|
*
|
|
11
11
|
* const cache = new Cache();
|
|
12
12
|
* app.get("/users/:id", cacheMiddleware({ cache, ttlSeconds: 60 }), handler);
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* MISS
|
|
14
|
+
* The middleware intercepts `res.json`: on a HIT it responds straight from the
|
|
15
|
+
* cache; on a MISS it lets the handler run and stores the response body (2xx only).
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
interface CacheMiddlewareOptions {
|
|
19
|
-
/**
|
|
19
|
+
/** Cache instance to use. */
|
|
20
20
|
cache: Cache;
|
|
21
|
-
/** TTL
|
|
21
|
+
/** TTL applied to stored responses (in seconds). */
|
|
22
22
|
ttlSeconds?: number;
|
|
23
|
-
/**
|
|
23
|
+
/** HTTP methods eligible for caching. Default: `["GET"]`. */
|
|
24
24
|
methods?: string[];
|
|
25
|
-
/**
|
|
25
|
+
/** Builds the cache key from the request. Default: `METHOD:url`. */
|
|
26
26
|
keyGenerator?: (req: Request) => string;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
30
|
-
* `X-Cache: HIT|MISS`
|
|
29
|
+
* Creates an Express middleware that caches JSON responses. Adds an
|
|
30
|
+
* `X-Cache: HIT|MISS` header for observability.
|
|
31
31
|
*/
|
|
32
32
|
declare function cacheMiddleware(options: CacheMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => void;
|
|
33
33
|
|
package/dist/express.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../js/express.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"sources":["../js/express.ts"],"sourcesContent":["/**\n * Express integration: caching of JSON responses.\n *\n * Usage:\n * import { Cache } from \"rust-node-cache\";\n * import { cacheMiddleware } from \"rust-node-cache/express\";\n *\n * const cache = new Cache();\n * app.get(\"/users/:id\", cacheMiddleware({ cache, ttlSeconds: 60 }), handler);\n *\n * The middleware intercepts `res.json`: on a HIT it responds straight from the\n * cache; on a MISS it lets the handler run and stores the response body (2xx only).\n */\n\nimport type { Request, Response, NextFunction } from \"express\";\nimport type { Cache } from \"./index\";\n\nexport interface CacheMiddlewareOptions {\n /** Cache instance to use. */\n cache: Cache;\n /** TTL applied to stored responses (in seconds). */\n ttlSeconds?: number;\n /** HTTP methods eligible for caching. Default: `[\"GET\"]`. */\n methods?: string[];\n /** Builds the cache key from the request. Default: `METHOD:url`. */\n keyGenerator?: (req: Request) => string;\n}\n\n/**\n * Creates an Express middleware that caches JSON responses. Adds an\n * `X-Cache: HIT|MISS` header for observability.\n */\nexport function cacheMiddleware(options: CacheMiddlewareOptions) {\n const { cache } = options;\n const ttlSeconds = options.ttlSeconds;\n const methods = options.methods ?? [\"GET\"];\n const keyGenerator =\n options.keyGenerator ?? ((req: Request) => `${req.method}:${req.originalUrl}`);\n\n return function rustNodeCache(\n req: Request,\n res: Response,\n next: NextFunction,\n ): void {\n if (!methods.includes(req.method)) {\n next();\n return;\n }\n\n const key = keyGenerator(req);\n const cached = cache.get(key);\n if (cached !== null) {\n res.setHeader(\"X-Cache\", \"HIT\");\n res.json(cached);\n return;\n }\n\n res.setHeader(\"X-Cache\", \"MISS\");\n const originalJson = res.json.bind(res);\n res.json = (body: unknown): Response => {\n // Only successful responses (2xx) are stored.\n if (res.statusCode >= 200 && res.statusCode < 300) {\n cache.set(key, body, ttlSeconds ? { ttlSeconds } : undefined);\n }\n return originalJson(body);\n };\n\n next();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCO,SAAS,gBAAgB,SAAiC;AAC/D,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,aAAa,QAAQ;AAC3B,QAAM,UAAU,QAAQ,WAAW,CAAC,KAAK;AACzC,QAAM,eACJ,QAAQ,iBAAiB,CAAC,QAAiB,GAAG,IAAI,MAAM,IAAI,IAAI,WAAW;AAE7E,SAAO,SAAS,cACd,KACA,KACA,MACM;AACN,QAAI,CAAC,QAAQ,SAAS,IAAI,MAAM,GAAG;AACjC,WAAK;AACL;AAAA,IACF;AAEA,UAAM,MAAM,aAAa,GAAG;AAC5B,UAAM,SAAS,MAAM,IAAI,GAAG;AAC5B,QAAI,WAAW,MAAM;AACnB,UAAI,UAAU,WAAW,KAAK;AAC9B,UAAI,KAAK,MAAM;AACf;AAAA,IACF;AAEA,QAAI,UAAU,WAAW,MAAM;AAC/B,UAAM,eAAe,IAAI,KAAK,KAAK,GAAG;AACtC,QAAI,OAAO,CAAC,SAA4B;AAEtC,UAAI,IAAI,cAAc,OAAO,IAAI,aAAa,KAAK;AACjD,cAAM,IAAI,KAAK,MAAM,aAAa,EAAE,WAAW,IAAI,MAAS;AAAA,MAC9D;AACA,aAAO,aAAa,IAAI;AAAA,IAC1B;AAEA,SAAK;AAAA,EACP;AACF;","names":[]}
|
package/dist/express.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../js/express.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"sources":["../js/express.ts"],"sourcesContent":["/**\n * Express integration: caching of JSON responses.\n *\n * Usage:\n * import { Cache } from \"rust-node-cache\";\n * import { cacheMiddleware } from \"rust-node-cache/express\";\n *\n * const cache = new Cache();\n * app.get(\"/users/:id\", cacheMiddleware({ cache, ttlSeconds: 60 }), handler);\n *\n * The middleware intercepts `res.json`: on a HIT it responds straight from the\n * cache; on a MISS it lets the handler run and stores the response body (2xx only).\n */\n\nimport type { Request, Response, NextFunction } from \"express\";\nimport type { Cache } from \"./index\";\n\nexport interface CacheMiddlewareOptions {\n /** Cache instance to use. */\n cache: Cache;\n /** TTL applied to stored responses (in seconds). */\n ttlSeconds?: number;\n /** HTTP methods eligible for caching. Default: `[\"GET\"]`. */\n methods?: string[];\n /** Builds the cache key from the request. Default: `METHOD:url`. */\n keyGenerator?: (req: Request) => string;\n}\n\n/**\n * Creates an Express middleware that caches JSON responses. Adds an\n * `X-Cache: HIT|MISS` header for observability.\n */\nexport function cacheMiddleware(options: CacheMiddlewareOptions) {\n const { cache } = options;\n const ttlSeconds = options.ttlSeconds;\n const methods = options.methods ?? [\"GET\"];\n const keyGenerator =\n options.keyGenerator ?? ((req: Request) => `${req.method}:${req.originalUrl}`);\n\n return function rustNodeCache(\n req: Request,\n res: Response,\n next: NextFunction,\n ): void {\n if (!methods.includes(req.method)) {\n next();\n return;\n }\n\n const key = keyGenerator(req);\n const cached = cache.get(key);\n if (cached !== null) {\n res.setHeader(\"X-Cache\", \"HIT\");\n res.json(cached);\n return;\n }\n\n res.setHeader(\"X-Cache\", \"MISS\");\n const originalJson = res.json.bind(res);\n res.json = (body: unknown): Response => {\n // Only successful responses (2xx) are stored.\n if (res.statusCode >= 200 && res.statusCode < 300) {\n cache.set(key, body, ttlSeconds ? { ttlSeconds } : undefined);\n }\n return originalJson(body);\n };\n\n next();\n };\n}\n"],"mappings":";AAgCO,SAAS,gBAAgB,SAAiC;AAC/D,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,aAAa,QAAQ;AAC3B,QAAM,UAAU,QAAQ,WAAW,CAAC,KAAK;AACzC,QAAM,eACJ,QAAQ,iBAAiB,CAAC,QAAiB,GAAG,IAAI,MAAM,IAAI,IAAI,WAAW;AAE7E,SAAO,SAAS,cACd,KACA,KACA,MACM;AACN,QAAI,CAAC,QAAQ,SAAS,IAAI,MAAM,GAAG;AACjC,WAAK;AACL;AAAA,IACF;AAEA,UAAM,MAAM,aAAa,GAAG;AAC5B,UAAM,SAAS,MAAM,IAAI,GAAG;AAC5B,QAAI,WAAW,MAAM;AACnB,UAAI,UAAU,WAAW,KAAK;AAC9B,UAAI,KAAK,MAAM;AACf;AAAA,IACF;AAEA,QAAI,UAAU,WAAW,MAAM;AAC/B,UAAM,eAAe,IAAI,KAAK,KAAK,GAAG;AACtC,QAAI,OAAO,CAAC,SAA4B;AAEtC,UAAI,IAAI,cAAc,OAAO,IAAI,aAAa,KAAK;AACjD,cAAM,IAAI,KAAK,MAAM,aAAa,EAAE,WAAW,IAAI,MAAS;AAAA,MAC9D;AACA,aAAO,aAAa,IAAI;AAAA,IAC1B;AAEA,SAAK;AAAA,EACP;AACF;","names":[]}
|
package/dist/fastify.d.mts
CHANGED
|
@@ -2,31 +2,31 @@ import { FastifyRequest, FastifyInstance } from 'fastify';
|
|
|
2
2
|
import { Cache } from './index.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Fastify integration: caching of JSON responses through a plugin.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Usage:
|
|
8
8
|
* import { Cache } from "rust-node-cache";
|
|
9
9
|
* import { cachePlugin } from "rust-node-cache/fastify";
|
|
10
10
|
*
|
|
11
11
|
* const cache = new Cache();
|
|
12
12
|
* fastify.register(cachePlugin, { cache, ttlSeconds: 60 });
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* (
|
|
14
|
+
* Flow: the `onRequest` hook checks the cache and, on a HIT, responds right away
|
|
15
|
+
* (short-circuit). On a MISS, the `onSend` hook stores the serialized payload.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
interface CachePluginOptions {
|
|
19
|
-
/**
|
|
19
|
+
/** Cache instance to use. */
|
|
20
20
|
cache: Cache;
|
|
21
|
-
/** TTL
|
|
21
|
+
/** TTL applied to stored responses (in seconds). */
|
|
22
22
|
ttlSeconds?: number;
|
|
23
|
-
/**
|
|
23
|
+
/** HTTP methods eligible for caching. Default: `["GET"]`. */
|
|
24
24
|
methods?: string[];
|
|
25
|
-
/**
|
|
25
|
+
/** Builds the cache key from the request. Default: `METHOD:url`. */
|
|
26
26
|
keyGenerator?: (req: FastifyRequest) => string;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* Fastify plugin that caches JSON responses. Use it with `fastify.register`.
|
|
30
30
|
*/
|
|
31
31
|
declare function cachePlugin(fastify: FastifyInstance, options: CachePluginOptions): Promise<void>;
|
|
32
32
|
|
package/dist/fastify.d.ts
CHANGED
|
@@ -2,31 +2,31 @@ import { FastifyRequest, FastifyInstance } from 'fastify';
|
|
|
2
2
|
import { Cache } from './index.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Fastify integration: caching of JSON responses through a plugin.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Usage:
|
|
8
8
|
* import { Cache } from "rust-node-cache";
|
|
9
9
|
* import { cachePlugin } from "rust-node-cache/fastify";
|
|
10
10
|
*
|
|
11
11
|
* const cache = new Cache();
|
|
12
12
|
* fastify.register(cachePlugin, { cache, ttlSeconds: 60 });
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* (
|
|
14
|
+
* Flow: the `onRequest` hook checks the cache and, on a HIT, responds right away
|
|
15
|
+
* (short-circuit). On a MISS, the `onSend` hook stores the serialized payload.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
interface CachePluginOptions {
|
|
19
|
-
/**
|
|
19
|
+
/** Cache instance to use. */
|
|
20
20
|
cache: Cache;
|
|
21
|
-
/** TTL
|
|
21
|
+
/** TTL applied to stored responses (in seconds). */
|
|
22
22
|
ttlSeconds?: number;
|
|
23
|
-
/**
|
|
23
|
+
/** HTTP methods eligible for caching. Default: `["GET"]`. */
|
|
24
24
|
methods?: string[];
|
|
25
|
-
/**
|
|
25
|
+
/** Builds the cache key from the request. Default: `METHOD:url`. */
|
|
26
26
|
keyGenerator?: (req: FastifyRequest) => string;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* Fastify plugin that caches JSON responses. Use it with `fastify.register`.
|
|
30
30
|
*/
|
|
31
31
|
declare function cachePlugin(fastify: FastifyInstance, options: CachePluginOptions): Promise<void>;
|
|
32
32
|
|
package/dist/fastify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../js/fastify.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"sources":["../js/fastify.ts"],"sourcesContent":["/**\n * Fastify integration: caching of JSON responses through a plugin.\n *\n * Usage:\n * import { Cache } from \"rust-node-cache\";\n * import { cachePlugin } from \"rust-node-cache/fastify\";\n *\n * const cache = new Cache();\n * fastify.register(cachePlugin, { cache, ttlSeconds: 60 });\n *\n * Flow: the `onRequest` hook checks the cache and, on a HIT, responds right away\n * (short-circuit). On a MISS, the `onSend` hook stores the serialized payload.\n */\n\nimport type {\n FastifyInstance,\n FastifyReply,\n FastifyRequest,\n} from \"fastify\";\nimport type { Cache } from \"./index\";\n\nexport interface CachePluginOptions {\n /** Cache instance to use. */\n cache: Cache;\n /** TTL applied to stored responses (in seconds). */\n ttlSeconds?: number;\n /** HTTP methods eligible for caching. Default: `[\"GET\"]`. */\n methods?: string[];\n /** Builds the cache key from the request. Default: `METHOD:url`. */\n keyGenerator?: (req: FastifyRequest) => string;\n}\n\n// The computed key is kept on the request itself between hooks.\nconst CACHE_KEY = Symbol(\"rustNodeCacheKey\");\n\n/**\n * Fastify plugin that caches JSON responses. Use it with `fastify.register`.\n */\nexport async function cachePlugin(\n fastify: FastifyInstance,\n options: CachePluginOptions,\n): Promise<void> {\n const { cache } = options;\n const ttlSeconds = options.ttlSeconds;\n const methods = options.methods ?? [\"GET\"];\n const keyGenerator =\n options.keyGenerator ?? ((req: FastifyRequest) => `${req.method}:${req.url}`);\n\n fastify.addHook(\n \"onRequest\",\n (request: FastifyRequest, reply: FastifyReply, done: () => void) => {\n if (!methods.includes(request.method)) {\n done();\n return;\n }\n\n const key = keyGenerator(request);\n const cached = cache.get(key);\n if (cached !== null) {\n reply.header(\"X-Cache\", \"HIT\");\n reply.send(cached);\n return; // done() is not called: the response was already sent.\n }\n\n reply.header(\"X-Cache\", \"MISS\");\n (request as unknown as Record<symbol, string>)[CACHE_KEY] = key;\n done();\n },\n );\n\n fastify.addHook(\n \"onSend\",\n (\n request: FastifyRequest,\n reply: FastifyReply,\n payload: unknown,\n done: (err: Error | null, value?: unknown) => void,\n ) => {\n const key = (request as unknown as Record<symbol, string | undefined>)[\n CACHE_KEY\n ];\n if (\n key &&\n typeof payload === \"string\" &&\n reply.statusCode >= 200 &&\n reply.statusCode < 300\n ) {\n try {\n cache.set(\n key,\n JSON.parse(payload),\n ttlSeconds ? { ttlSeconds } : undefined,\n );\n } catch {\n // non-JSON payload: simply not cached.\n }\n }\n done(null, payload);\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiCA,IAAM,YAAY,uBAAO,kBAAkB;AAK3C,eAAsB,YACpB,SACA,SACe;AACf,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,aAAa,QAAQ;AAC3B,QAAM,UAAU,QAAQ,WAAW,CAAC,KAAK;AACzC,QAAM,eACJ,QAAQ,iBAAiB,CAAC,QAAwB,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG;AAE5E,UAAQ;AAAA,IACN;AAAA,IACA,CAAC,SAAyB,OAAqB,SAAqB;AAClE,UAAI,CAAC,QAAQ,SAAS,QAAQ,MAAM,GAAG;AACrC,aAAK;AACL;AAAA,MACF;AAEA,YAAM,MAAM,aAAa,OAAO;AAChC,YAAM,SAAS,MAAM,IAAI,GAAG;AAC5B,UAAI,WAAW,MAAM;AACnB,cAAM,OAAO,WAAW,KAAK;AAC7B,cAAM,KAAK,MAAM;AACjB;AAAA,MACF;AAEA,YAAM,OAAO,WAAW,MAAM;AAC9B,MAAC,QAA8C,SAAS,IAAI;AAC5D,WAAK;AAAA,IACP;AAAA,EACF;AAEA,UAAQ;AAAA,IACN;AAAA,IACA,CACE,SACA,OACA,SACA,SACG;AACH,YAAM,MAAO,QACX,SACF;AACA,UACE,OACA,OAAO,YAAY,YACnB,MAAM,cAAc,OACpB,MAAM,aAAa,KACnB;AACA,YAAI;AACF,gBAAM;AAAA,YACJ;AAAA,YACA,KAAK,MAAM,OAAO;AAAA,YAClB,aAAa,EAAE,WAAW,IAAI;AAAA,UAChC;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AACA,WAAK,MAAM,OAAO;AAAA,IACpB;AAAA,EACF;AACF;","names":[]}
|
package/dist/fastify.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../js/fastify.ts"],"sourcesContent":["/**\n *
|
|
1
|
+
{"version":3,"sources":["../js/fastify.ts"],"sourcesContent":["/**\n * Fastify integration: caching of JSON responses through a plugin.\n *\n * Usage:\n * import { Cache } from \"rust-node-cache\";\n * import { cachePlugin } from \"rust-node-cache/fastify\";\n *\n * const cache = new Cache();\n * fastify.register(cachePlugin, { cache, ttlSeconds: 60 });\n *\n * Flow: the `onRequest` hook checks the cache and, on a HIT, responds right away\n * (short-circuit). On a MISS, the `onSend` hook stores the serialized payload.\n */\n\nimport type {\n FastifyInstance,\n FastifyReply,\n FastifyRequest,\n} from \"fastify\";\nimport type { Cache } from \"./index\";\n\nexport interface CachePluginOptions {\n /** Cache instance to use. */\n cache: Cache;\n /** TTL applied to stored responses (in seconds). */\n ttlSeconds?: number;\n /** HTTP methods eligible for caching. Default: `[\"GET\"]`. */\n methods?: string[];\n /** Builds the cache key from the request. Default: `METHOD:url`. */\n keyGenerator?: (req: FastifyRequest) => string;\n}\n\n// The computed key is kept on the request itself between hooks.\nconst CACHE_KEY = Symbol(\"rustNodeCacheKey\");\n\n/**\n * Fastify plugin that caches JSON responses. Use it with `fastify.register`.\n */\nexport async function cachePlugin(\n fastify: FastifyInstance,\n options: CachePluginOptions,\n): Promise<void> {\n const { cache } = options;\n const ttlSeconds = options.ttlSeconds;\n const methods = options.methods ?? [\"GET\"];\n const keyGenerator =\n options.keyGenerator ?? ((req: FastifyRequest) => `${req.method}:${req.url}`);\n\n fastify.addHook(\n \"onRequest\",\n (request: FastifyRequest, reply: FastifyReply, done: () => void) => {\n if (!methods.includes(request.method)) {\n done();\n return;\n }\n\n const key = keyGenerator(request);\n const cached = cache.get(key);\n if (cached !== null) {\n reply.header(\"X-Cache\", \"HIT\");\n reply.send(cached);\n return; // done() is not called: the response was already sent.\n }\n\n reply.header(\"X-Cache\", \"MISS\");\n (request as unknown as Record<symbol, string>)[CACHE_KEY] = key;\n done();\n },\n );\n\n fastify.addHook(\n \"onSend\",\n (\n request: FastifyRequest,\n reply: FastifyReply,\n payload: unknown,\n done: (err: Error | null, value?: unknown) => void,\n ) => {\n const key = (request as unknown as Record<symbol, string | undefined>)[\n CACHE_KEY\n ];\n if (\n key &&\n typeof payload === \"string\" &&\n reply.statusCode >= 200 &&\n reply.statusCode < 300\n ) {\n try {\n cache.set(\n key,\n JSON.parse(payload),\n ttlSeconds ? { ttlSeconds } : undefined,\n );\n } catch {\n // non-JSON payload: simply not cached.\n }\n }\n done(null, payload);\n },\n );\n}\n"],"mappings":";AAiCA,IAAM,YAAY,uBAAO,kBAAkB;AAK3C,eAAsB,YACpB,SACA,SACe;AACf,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,aAAa,QAAQ;AAC3B,QAAM,UAAU,QAAQ,WAAW,CAAC,KAAK;AACzC,QAAM,eACJ,QAAQ,iBAAiB,CAAC,QAAwB,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG;AAE5E,UAAQ;AAAA,IACN;AAAA,IACA,CAAC,SAAyB,OAAqB,SAAqB;AAClE,UAAI,CAAC,QAAQ,SAAS,QAAQ,MAAM,GAAG;AACrC,aAAK;AACL;AAAA,MACF;AAEA,YAAM,MAAM,aAAa,OAAO;AAChC,YAAM,SAAS,MAAM,IAAI,GAAG;AAC5B,UAAI,WAAW,MAAM;AACnB,cAAM,OAAO,WAAW,KAAK;AAC7B,cAAM,KAAK,MAAM;AACjB;AAAA,MACF;AAEA,YAAM,OAAO,WAAW,MAAM;AAC9B,MAAC,QAA8C,SAAS,IAAI;AAC5D,WAAK;AAAA,IACP;AAAA,EACF;AAEA,UAAQ;AAAA,IACN;AAAA,IACA,CACE,SACA,OACA,SACA,SACG;AACH,YAAM,MAAO,QACX,SACF;AACA,UACE,OACA,OAAO,YAAY,YACnB,MAAM,cAAc,OACpB,MAAM,aAAa,KACnB;AACA,YAAI;AACF,gBAAM;AAAA,YACJ;AAAA,YACA,KAAK,MAAM,OAAO;AAAA,YAClB,aAAa,EAAE,WAAW,IAAI;AAAA,UAChC;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AACA,WAAK,MAAM,OAAO;AAAA,IACpB;AAAA,EACF;AACF;","names":[]}
|