express-memorize 1.4.0 → 1.5.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.
- package/CHANGELOG.md +13 -0
- package/README.md +205 -82
- package/dist/MemorizeStore.d.ts +31 -8
- package/dist/MemorizeStore.d.ts.map +1 -1
- package/dist/MemorizeStore.js +98 -14
- package/dist/MemorizeStore.js.map +1 -1
- package/dist/adapters/express.d.ts +25 -0
- package/dist/adapters/express.d.ts.map +1 -0
- package/dist/adapters/express.js +62 -0
- package/dist/adapters/express.js.map +1 -0
- package/dist/adapters/fetch.d.ts +45 -0
- package/dist/adapters/fetch.d.ts.map +1 -0
- package/dist/adapters/fetch.js +72 -0
- package/dist/adapters/fetch.js.map +1 -0
- package/dist/adapters/hono.d.ts +34 -0
- package/dist/adapters/hono.d.ts.map +1 -0
- package/dist/adapters/hono.js +63 -0
- package/dist/adapters/hono.js.map +1 -0
- package/dist/adapters/nestjs.d.ts +84 -0
- package/dist/adapters/nestjs.d.ts.map +1 -0
- package/dist/adapters/nestjs.js +194 -0
- package/dist/adapters/nestjs.js.map +1 -0
- package/dist/domain/CacheEntry.d.ts +3 -1
- package/dist/domain/CacheEntry.d.ts.map +1 -1
- package/dist/domain/CacheInfo.d.ts +1 -1
- package/dist/domain/CacheInfo.d.ts.map +1 -1
- package/dist/domain/Memorize.d.ts +99 -4
- package/dist/domain/Memorize.d.ts.map +1 -1
- package/dist/domain/MemorizeCallOptions.d.ts +2 -1
- package/dist/domain/MemorizeCallOptions.d.ts.map +1 -1
- package/dist/domain/MemorizeEvent.d.ts +2 -1
- package/dist/domain/MemorizeEvent.d.ts.map +1 -1
- package/dist/domain/MemorizeEventType.d.ts +3 -1
- package/dist/domain/MemorizeEventType.d.ts.map +1 -1
- package/dist/domain/MemorizeEventType.js +2 -0
- package/dist/domain/MemorizeEventType.js.map +1 -1
- package/dist/domain/MemorizeEvictEvent.d.ts +18 -0
- package/dist/domain/MemorizeEvictEvent.d.ts.map +1 -0
- package/dist/domain/MemorizeEvictEvent.js +3 -0
- package/dist/domain/MemorizeEvictEvent.js.map +1 -0
- package/dist/domain/MemorizeOptions.d.ts +14 -1
- package/dist/domain/MemorizeOptions.d.ts.map +1 -1
- package/dist/domain/MemorizeSetEvent.d.ts +3 -1
- package/dist/domain/MemorizeSetEvent.d.ts.map +1 -1
- package/dist/domain/MemorizeStats.d.ts +12 -0
- package/dist/domain/MemorizeStats.d.ts.map +1 -0
- package/dist/domain/MemorizeStats.js +3 -0
- package/dist/domain/MemorizeStats.js.map +1 -0
- package/dist/domain/index.d.ts +2 -0
- package/dist/domain/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/memorize.d.ts +19 -16
- package/dist/memorize.d.ts.map +1 -1
- package/dist/memorize.js +51 -50
- package/dist/memorize.js.map +1 -1
- package/package.json +50 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
# [1.5.0](https://github.com/ElJijuna/express-memorize/compare/v1.4.0...v1.5.0) (2026-05-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add framework-agnostic cache API and Express adapter architecture ([78b605d](https://github.com/ElJijuna/express-memorize/commit/78b605d1dc33a361f31339c661f5a527bda47a02)), closes [#6](https://github.com/ElJijuna/express-memorize/issues/6)
|
|
7
|
+
* add generic Fetch API cache adapter ([b36bf48](https://github.com/ElJijuna/express-memorize/commit/b36bf489c5a92cef05a2551df37b3d014d3588db)), closes [#12](https://github.com/ElJijuna/express-memorize/issues/12)
|
|
8
|
+
* add Hono adapter for edge/serverless runtimes ([eb2f967](https://github.com/ElJijuna/express-memorize/commit/eb2f9675cb8f668bad76a88aabb19b622d83fac3)), closes [#13](https://github.com/ElJijuna/express-memorize/issues/13)
|
|
9
|
+
* add Infinity TTL support and finite default TTL ([e353785](https://github.com/ElJijuna/express-memorize/commit/e353785966e3ae425610cba200df6f89691beaa0))
|
|
10
|
+
* add maxEntries limit, LRU eviction, and cache size metrics ([14e4c5a](https://github.com/ElJijuna/express-memorize/commit/14e4c5a670d9bd2dcb253130b89f994988dfe466)), closes [#14](https://github.com/ElJijuna/express-memorize/issues/14)
|
|
11
|
+
* configure package exports for tree-shaking and adapter subpaths ([14d4dcc](https://github.com/ElJijuna/express-memorize/commit/14d4dccd145d02e3966a8f977f0cf719c7123e81)), closes [#10](https://github.com/ElJijuna/express-memorize/issues/10)
|
|
12
|
+
* **nestjs:** add cache interceptor adapter (closes [#7](https://github.com/ElJijuna/express-memorize/issues/7)) ([f1b9707](https://github.com/ElJijuna/express-memorize/commit/f1b970792123062dc32d464d2ccb1ccea7a3d7a1))
|
|
13
|
+
|
|
1
14
|
# Changelog
|
|
2
15
|
|
|
3
16
|
All notable changes to this project will be documented in this file.
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
In-memory cache
|
|
12
|
+
In-memory HTTP cache for <strong>Express, NestJS, Hono, Fetch API</strong>, and more.<br/>
|
|
13
13
|
Caches <code>GET</code> responses with optional TTL — zero dependencies, fully typed.
|
|
14
14
|
</p>
|
|
15
15
|
|
|
@@ -18,13 +18,16 @@
|
|
|
18
18
|
## Features
|
|
19
19
|
|
|
20
20
|
- Caches `GET` responses automatically when status code is `2xx`
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
21
|
+
- Works with **Express**, **NestJS**, **Hono**, **Fetch API / serverless**, and direct service-level usage
|
|
22
|
+
- Per-route TTL override and `noCache` bypass
|
|
23
|
+
- **`maxEntries` cap with LRU eviction** to bound memory usage
|
|
24
|
+
- **Size metrics**: `size()`, `byteSize()`, `getStats()`
|
|
25
|
+
- **Service-level cache**: `remember()`, `set()`, `getValue()`
|
|
26
|
+
- Event hooks: `set`, `delete`, `expire`, `evict`
|
|
27
|
+
- Cache inspection and invalidation API (`get`, `getAll`, `delete`, `deleteMatching`, `clear`)
|
|
24
28
|
- Hit counter per cache entry
|
|
25
29
|
- `X-Cache: HIT | MISS | BYPASS` response header
|
|
26
|
-
- Zero runtime dependencies
|
|
27
|
-
- Full TypeScript support
|
|
30
|
+
- Zero runtime dependencies, fully typed
|
|
28
31
|
|
|
29
32
|
## Installation
|
|
30
33
|
|
|
@@ -32,14 +35,23 @@
|
|
|
32
35
|
npm install express-memorize
|
|
33
36
|
```
|
|
34
37
|
|
|
38
|
+
Adapters for non-Express runtimes are optional — install only what you need:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install hono # only if using the Hono adapter
|
|
42
|
+
npm install @nestjs/common @nestjs/core rxjs # only if using the NestJS adapter
|
|
43
|
+
```
|
|
44
|
+
|
|
35
45
|
## Quick Start
|
|
36
46
|
|
|
47
|
+
### Express
|
|
48
|
+
|
|
37
49
|
```typescript
|
|
38
50
|
import express from 'express';
|
|
39
51
|
import { memorize } from 'express-memorize';
|
|
40
52
|
|
|
41
53
|
const app = express();
|
|
42
|
-
const cache = memorize({ ttl: 30_000 });
|
|
54
|
+
const cache = memorize({ ttl: 30_000 });
|
|
43
55
|
|
|
44
56
|
app.get('/users', cache(), async (req, res) => {
|
|
45
57
|
const users = await db.getUsers();
|
|
@@ -49,32 +61,92 @@ app.get('/users', cache(), async (req, res) => {
|
|
|
49
61
|
app.listen(3000);
|
|
50
62
|
```
|
|
51
63
|
|
|
52
|
-
|
|
64
|
+
### Hono
|
|
53
65
|
|
|
54
|
-
|
|
66
|
+
```typescript
|
|
67
|
+
import { Hono } from 'hono';
|
|
68
|
+
import { memorize } from 'express-memorize';
|
|
69
|
+
import { createHonoMiddleware } from 'express-memorize/hono';
|
|
55
70
|
|
|
56
|
-
|
|
71
|
+
const app = new Hono();
|
|
72
|
+
const cache = memorize({ ttl: 30_000 });
|
|
57
73
|
|
|
58
|
-
|
|
74
|
+
app.get('/users', createHonoMiddleware(cache), async (c) => {
|
|
75
|
+
return c.json(await usersService.findAll());
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### NestJS
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { Module } from '@nestjs/common';
|
|
83
|
+
import { APP_INTERCEPTOR } from '@nestjs/core';
|
|
84
|
+
import {
|
|
85
|
+
MemorizeCacheKey,
|
|
86
|
+
MemorizeInterceptor,
|
|
87
|
+
MemorizeModule,
|
|
88
|
+
MemorizeTtl,
|
|
89
|
+
} from 'express-memorize/nestjs';
|
|
90
|
+
|
|
91
|
+
@Module({
|
|
92
|
+
imports: [MemorizeModule.forRoot({ ttl: 30_000 })],
|
|
93
|
+
providers: [
|
|
94
|
+
{
|
|
95
|
+
provide: APP_INTERCEPTOR,
|
|
96
|
+
useExisting: MemorizeInterceptor,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
})
|
|
100
|
+
export class AppModule {}
|
|
101
|
+
|
|
102
|
+
export class UsersController {
|
|
103
|
+
@MemorizeCacheKey('users:list')
|
|
104
|
+
@MemorizeTtl(10_000)
|
|
105
|
+
findAll() {
|
|
106
|
+
return usersService.findAll();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Fetch API / Serverless
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { memorize } from 'express-memorize';
|
|
115
|
+
import { cacheFetchHandler } from 'express-memorize/fetch';
|
|
116
|
+
|
|
117
|
+
const cache = memorize({ ttl: 30_000 });
|
|
118
|
+
|
|
119
|
+
export default cacheFetchHandler(cache, async (request) => {
|
|
120
|
+
const users = await usersService.findAll();
|
|
121
|
+
return Response.json(users);
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Service-level caching
|
|
126
|
+
|
|
127
|
+
Cache arbitrary values directly — no HTTP layer required.
|
|
59
128
|
|
|
60
129
|
```typescript
|
|
61
130
|
const cache = memorize({ ttl: 60_000 });
|
|
62
131
|
|
|
63
|
-
|
|
132
|
+
// Compute-and-cache pattern
|
|
133
|
+
const users = await cache.remember('users:list', () => usersService.findAll());
|
|
64
134
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
135
|
+
// Explicit set/get
|
|
136
|
+
cache.set('config', appConfig);
|
|
137
|
+
const config = cache.getValue<AppConfig>('config');
|
|
68
138
|
```
|
|
69
139
|
|
|
70
|
-
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Usage
|
|
143
|
+
|
|
144
|
+
### Global middleware (Express)
|
|
71
145
|
|
|
72
146
|
```typescript
|
|
73
147
|
const cache = memorize({ ttl: 60_000 });
|
|
74
148
|
|
|
75
|
-
app.
|
|
76
|
-
res.json({ data: products });
|
|
77
|
-
});
|
|
149
|
+
app.use(cache()); // applies to all GET routes
|
|
78
150
|
```
|
|
79
151
|
|
|
80
152
|
### Per-route TTL override
|
|
@@ -82,40 +154,70 @@ app.get('/products', cache(), (req, res) => {
|
|
|
82
154
|
```typescript
|
|
83
155
|
const cache = memorize({ ttl: 60_000 }); // global: 60s
|
|
84
156
|
|
|
85
|
-
app.get('/users', cache(),
|
|
86
|
-
app.get('/products', cache({ ttl: 10_000 }),
|
|
87
|
-
app.get('/config', cache({ ttl:
|
|
157
|
+
app.get('/users', cache(), handler); // 60s
|
|
158
|
+
app.get('/products', cache({ ttl: 10_000 }), handler); // 10s
|
|
159
|
+
app.get('/config', cache({ ttl: Infinity }), handler); // no expiry
|
|
88
160
|
```
|
|
89
161
|
|
|
90
|
-
###
|
|
162
|
+
### noCache bypass
|
|
91
163
|
|
|
92
164
|
```typescript
|
|
93
|
-
|
|
165
|
+
app.get('/live-feed', cache({ noCache: true }), handler);
|
|
166
|
+
// Sets X-Cache: BYPASS, never reads or writes the cache
|
|
167
|
+
```
|
|
94
168
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
169
|
+
### NestJS decorators
|
|
170
|
+
|
|
171
|
+
Use `MemorizeInterceptor` on a controller or globally, then configure caching at the controller or method level.
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { Controller, Get, UseInterceptors } from '@nestjs/common';
|
|
175
|
+
import {
|
|
176
|
+
MemorizeCacheKey,
|
|
177
|
+
MemorizeInterceptor,
|
|
178
|
+
MemorizeNoCache,
|
|
179
|
+
MemorizeTtl,
|
|
180
|
+
} from 'express-memorize/nestjs';
|
|
181
|
+
|
|
182
|
+
@Controller('users')
|
|
183
|
+
@UseInterceptors(MemorizeInterceptor)
|
|
184
|
+
@MemorizeTtl(30_000)
|
|
185
|
+
export class UsersController {
|
|
186
|
+
@Get()
|
|
187
|
+
@MemorizeCacheKey('users:list')
|
|
188
|
+
findAll() {
|
|
189
|
+
return usersService.findAll();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@Get('live')
|
|
193
|
+
@MemorizeNoCache()
|
|
194
|
+
live() {
|
|
195
|
+
return usersService.live();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
98
199
|
|
|
200
|
+
For global usage, import `MemorizeModule.forRoot()` and register `APP_INTERCEPTOR` with `useExisting: MemorizeInterceptor` so the interceptor receives the module's shared cache instance.
|
|
201
|
+
|
|
202
|
+
### Cache invalidation
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
99
205
|
app.post('/users', (req, res) => {
|
|
100
206
|
users.push(req.body);
|
|
101
|
-
cache.delete('/users');
|
|
102
|
-
res.status(201).json(
|
|
207
|
+
cache.delete('/users');
|
|
208
|
+
res.status(201).json(req.body);
|
|
103
209
|
});
|
|
104
210
|
```
|
|
105
211
|
|
|
106
212
|
### Pattern-based invalidation
|
|
107
213
|
|
|
108
|
-
Use `cache.deleteMatching(pattern)` to remove
|
|
214
|
+
Use `cache.deleteMatching(pattern)` to remove entries by glob pattern.
|
|
109
215
|
|
|
110
216
|
```typescript
|
|
111
|
-
// Cached keys: /api/users/abc123, /api/users/abc123?lang=es, /api/users/abc123?page=1
|
|
112
217
|
app.put('/users/:id', (req, res) => {
|
|
113
218
|
users.update(req.params.id, req.body);
|
|
114
|
-
|
|
115
|
-
// Remove all cached variants of this user, regardless of query params
|
|
116
219
|
const deleted = cache.deleteMatching(`**/users/${req.params.id}*`);
|
|
117
|
-
console.log(`${deleted}
|
|
118
|
-
|
|
220
|
+
console.log(`${deleted} entries removed`);
|
|
119
221
|
res.json({ ok: true });
|
|
120
222
|
});
|
|
121
223
|
```
|
|
@@ -124,35 +226,33 @@ app.put('/users/:id', (req, res) => {
|
|
|
124
226
|
|
|
125
227
|
| Pattern | Behaviour |
|
|
126
228
|
|---------|-----------|
|
|
127
|
-
| `*` | Matches any sequence
|
|
128
|
-
| `**` | Matches any sequence
|
|
229
|
+
| `*` | Matches any sequence within a single path segment (does not cross `/`) |
|
|
230
|
+
| `**` | Matches any sequence across path segments (crosses `/`) |
|
|
129
231
|
| `?` | Matches any single character except `/` |
|
|
130
232
|
|
|
131
|
-
|
|
233
|
+
### Bounding memory with `maxEntries`
|
|
132
234
|
|
|
133
|
-
|
|
235
|
+
Prevent unbounded growth by setting a maximum number of entries. When the limit is reached, the **least-recently-used (LRU)** entry is evicted before the new one is stored.
|
|
134
236
|
|
|
135
237
|
```typescript
|
|
136
|
-
const cache = memorize({ ttl: 30_000 });
|
|
238
|
+
const cache = memorize({ ttl: 30_000, maxEntries: 1_000 });
|
|
239
|
+
```
|
|
137
240
|
|
|
138
|
-
|
|
139
|
-
console.log(`[cache] stored ${e.key} — expires in ${e.expiresAt ? e.expiresAt - Date.now() : '∞'}ms`);
|
|
140
|
-
});
|
|
241
|
+
### Size metrics
|
|
141
242
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
cache.on('expire', (e) => {
|
|
147
|
-
console.log(`[cache] expired ${e.key}`);
|
|
148
|
-
});
|
|
243
|
+
```typescript
|
|
244
|
+
cache.size(); // number of active entries
|
|
245
|
+
cache.byteSize(); // approximate total body size in bytes
|
|
246
|
+
cache.getStats(); // { entries, maxEntries, byteSize }
|
|
149
247
|
```
|
|
150
248
|
|
|
249
|
+
> `byteSize()` is an estimate based on UTF-8 encoding for strings and `byteLength` for buffers. It may not reflect actual VM memory usage.
|
|
250
|
+
|
|
151
251
|
### Inspect the cache
|
|
152
252
|
|
|
153
253
|
```typescript
|
|
154
|
-
cache.get('/users'); // CacheInfo | null
|
|
155
|
-
cache.getAll(); // Record<string, CacheInfo>
|
|
254
|
+
cache.get('/users'); // CacheInfo | null
|
|
255
|
+
cache.getAll(); // Record<string, CacheInfo>
|
|
156
256
|
```
|
|
157
257
|
|
|
158
258
|
`CacheInfo` shape:
|
|
@@ -164,31 +264,28 @@ cache.getAll(); // Record<string, CacheInfo> — all active entries
|
|
|
164
264
|
statusCode: number;
|
|
165
265
|
contentType: string;
|
|
166
266
|
expiresAt: number | null;
|
|
167
|
-
remainingTtl: number | null; // ms until expiry, null
|
|
168
|
-
hits: number; //
|
|
267
|
+
remainingTtl: number | null; // ms until expiry, null when ttl is Infinity
|
|
268
|
+
hits: number; // times this key was served from cache
|
|
269
|
+
size: number; // approximate body size in bytes
|
|
169
270
|
}
|
|
170
271
|
```
|
|
171
272
|
|
|
172
|
-
`hits` starts at `1` on the initial cache miss
|
|
273
|
+
`hits` starts at `1` on the initial cache miss and increments on every hit. It resets to `1` if the entry is evicted and re-cached.
|
|
173
274
|
|
|
174
|
-
|
|
175
|
-
// Example: monitoring hot keys
|
|
176
|
-
const entries = cache.getAll();
|
|
177
|
-
for (const [key, info] of Object.entries(entries)) {
|
|
178
|
-
console.log(`${key} → ${info.hits} hits`);
|
|
179
|
-
}
|
|
180
|
-
// /users → 42 hits
|
|
181
|
-
// /products → 7 hits
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### Clear the cache
|
|
275
|
+
### Event hooks
|
|
185
276
|
|
|
186
277
|
```typescript
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
cache.
|
|
278
|
+
import { MemorizeEventType } from 'express-memorize';
|
|
279
|
+
|
|
280
|
+
cache.on(MemorizeEventType.Set, (e) => console.log('stored', e.key));
|
|
281
|
+
cache.on(MemorizeEventType.Delete, (e) => console.log('deleted', e.key));
|
|
282
|
+
cache.on(MemorizeEventType.Expire, (e) => console.log('expired', e.key));
|
|
283
|
+
cache.on(MemorizeEventType.Evict, (e) => console.log('evicted', e.key)); // maxEntries LRU
|
|
284
|
+
cache.on(MemorizeEventType.Empty, () => console.log('cache is empty'));
|
|
190
285
|
```
|
|
191
286
|
|
|
287
|
+
---
|
|
288
|
+
|
|
192
289
|
## API Reference
|
|
193
290
|
|
|
194
291
|
### `memorize(options?)`
|
|
@@ -197,33 +294,58 @@ Creates a cache instance. Returns a `Memorize` object.
|
|
|
197
294
|
|
|
198
295
|
| Option | Type | Default | Description |
|
|
199
296
|
|--------|------|---------|-------------|
|
|
200
|
-
| `ttl` | `number` | `
|
|
297
|
+
| `ttl` | `number` | `60_000` | Time-to-live in milliseconds. Pass `Infinity` for no expiry. |
|
|
298
|
+
| `maxEntries` | `number` | `undefined` | Maximum number of entries. LRU eviction when reached. |
|
|
201
299
|
|
|
202
|
-
### `cache(options?)`
|
|
300
|
+
### `cache(options?)` / `cache.express(options?)`
|
|
203
301
|
|
|
204
|
-
Returns an Express `RequestHandler`
|
|
302
|
+
Returns an Express `RequestHandler`. `cache()` is a backwards-compatible alias for `cache.express()`.
|
|
205
303
|
|
|
206
304
|
| Option | Type | Default | Description |
|
|
207
305
|
|--------|------|---------|-------------|
|
|
208
|
-
| `ttl` | `number` | global `ttl` | TTL override for this
|
|
306
|
+
| `ttl` | `number` | global `ttl` | TTL override for this route. Pass `Infinity` for no expiry. |
|
|
307
|
+
| `noCache` | `boolean` | `false` | Skip cache entirely. Sets `X-Cache: BYPASS`. |
|
|
308
|
+
|
|
309
|
+
### Service-level cache methods
|
|
310
|
+
|
|
311
|
+
| Method | Signature | Description |
|
|
312
|
+
|--------|-----------|-------------|
|
|
313
|
+
| `remember` | `(key, factory, ttl?) => Promise<T>` | Return cached value or call factory and cache the result. |
|
|
314
|
+
| `set` | `(key, value, ttl?) => void` | Store an arbitrary value. |
|
|
315
|
+
| `getValue` | `(key) => T \| undefined` | Retrieve a value stored via `set` or `remember`. |
|
|
209
316
|
|
|
210
317
|
### Cache management
|
|
211
318
|
|
|
212
319
|
| Method | Signature | Description |
|
|
213
320
|
|--------|-----------|-------------|
|
|
214
|
-
| `get` | `(key
|
|
215
|
-
| `getAll` | `() => Record<string, CacheInfo>` | Returns all active
|
|
216
|
-
| `delete` | `(key
|
|
217
|
-
| `deleteMatching` | `(pattern
|
|
321
|
+
| `get` | `(key) => CacheInfo \| null` | Returns info for a cached key. |
|
|
322
|
+
| `getAll` | `() => Record<string, CacheInfo>` | Returns all active entries. |
|
|
323
|
+
| `delete` | `(key) => boolean` | Removes a single entry. |
|
|
324
|
+
| `deleteMatching` | `(pattern) => number` | Removes entries matching a glob pattern. |
|
|
218
325
|
| `clear` | `() => void` | Removes all entries. |
|
|
326
|
+
| `size` | `() => number` | Number of active entries. |
|
|
327
|
+
| `byteSize` | `() => number` | Approximate total body size in bytes. |
|
|
328
|
+
| `getStats` | `() => MemorizeStats` | Aggregate stats: `{ entries, maxEntries, byteSize }`. |
|
|
329
|
+
|
|
330
|
+
### Adapters
|
|
331
|
+
|
|
332
|
+
| Import path | Export | Framework |
|
|
333
|
+
|-------------|--------|-----------|
|
|
334
|
+
| `express-memorize` | `memorize` | Core factory |
|
|
335
|
+
| `express-memorize/express` | `createExpressAdapter(cache, options?)` | Express |
|
|
336
|
+
| `express-memorize/nestjs` | `MemorizeModule`, `MemorizeInterceptor`, decorators | NestJS |
|
|
337
|
+
| `express-memorize/hono` | `createHonoMiddleware(cache, options?)` | Hono |
|
|
338
|
+
| `express-memorize/fetch` | `cacheFetchHandler(cache, handler, options?)` | Fetch API / Serverless |
|
|
219
339
|
|
|
220
340
|
### Events
|
|
221
341
|
|
|
222
342
|
| Event | Payload | When |
|
|
223
343
|
|-------|---------|------|
|
|
224
|
-
| `set` | `{ type, key, body, statusCode, contentType, expiresAt }` | A response is stored |
|
|
225
|
-
| `delete` | `{ type, key }` | `
|
|
344
|
+
| `set` | `{ type, key, body, statusCode, contentType, expiresAt, size }` | A response is stored |
|
|
345
|
+
| `delete` | `{ type, key }` | Manual removal via `delete`, `deleteMatching`, or `clear` |
|
|
226
346
|
| `expire` | `{ type, key }` | TTL timer fires or lazy expiry is detected |
|
|
347
|
+
| `evict` | `{ type, key }` | LRU eviction due to `maxEntries` limit |
|
|
348
|
+
| `empty` | `{ type }` | Last entry removed, cache is now empty |
|
|
227
349
|
|
|
228
350
|
## Response Headers
|
|
229
351
|
|
|
@@ -231,14 +353,15 @@ Returns an Express `RequestHandler` middleware. Can override the global TTL.
|
|
|
231
353
|
|--------|-------|-------------|
|
|
232
354
|
| `X-Cache` | `HIT` | Response served from cache |
|
|
233
355
|
| `X-Cache` | `MISS` | Response computed and stored |
|
|
234
|
-
| `X-Cache` | `BYPASS` | Cache skipped — `noCache: true`
|
|
356
|
+
| `X-Cache` | `BYPASS` | Cache skipped — `noCache: true` |
|
|
235
357
|
|
|
236
358
|
## Behavior
|
|
237
359
|
|
|
238
|
-
- Only `GET` requests are cached. All other methods bypass the
|
|
360
|
+
- Only `GET` requests are cached. All other methods bypass the cache entirely.
|
|
239
361
|
- Only responses with a `2xx` status code are stored.
|
|
240
|
-
-
|
|
362
|
+
- All middleware and adapter instances created from the same `memorize()` call **share the same store**.
|
|
241
363
|
- Two separate `memorize()` calls produce **independent stores**.
|
|
364
|
+
- Byte size is an approximation — strings use UTF-8 encoding, objects use `JSON.stringify` length.
|
|
242
365
|
|
|
243
366
|
## License
|
|
244
367
|
|
package/dist/MemorizeStore.d.ts
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
import { CacheEntry } from './domain/CacheEntry';
|
|
2
2
|
import { CacheInfo } from './domain/CacheInfo';
|
|
3
|
+
import { MemorizeStats } from './domain/MemorizeStats';
|
|
3
4
|
import { MemorizeEventType } from './domain/MemorizeEventType';
|
|
4
5
|
import { MemorizeEvent } from './domain/MemorizeEvent';
|
|
5
6
|
import { MemorizeSetEvent } from './domain/MemorizeSetEvent';
|
|
6
7
|
import { MemorizeDeleteEvent } from './domain/MemorizeDeleteEvent';
|
|
7
8
|
import { MemorizeExpireEvent } from './domain/MemorizeExpireEvent';
|
|
8
9
|
import { MemorizeEmptyEvent } from './domain/MemorizeEmptyEvent';
|
|
9
|
-
|
|
10
|
+
import { MemorizeEvictEvent } from './domain/MemorizeEvictEvent';
|
|
11
|
+
export type { CacheEntry, CacheInfo, MemorizeStats, MemorizeEvent, MemorizeSetEvent, MemorizeDeleteEvent, MemorizeExpireEvent, MemorizeEmptyEvent, MemorizeEvictEvent, };
|
|
10
12
|
export { MemorizeEventType };
|
|
11
13
|
/**
|
|
12
|
-
* Low-level in-memory key-value store with optional TTL and event emission.
|
|
14
|
+
* Low-level in-memory key-value store with optional TTL, LRU eviction, and event emission.
|
|
13
15
|
*
|
|
14
16
|
* You do not usually interact with this class directly — use the {@link memorize} factory
|
|
15
17
|
* instead, which wraps this store in an Express middleware.
|
|
16
18
|
*/
|
|
17
19
|
export declare class MemorizeStore {
|
|
20
|
+
private readonly _maxEntries?;
|
|
18
21
|
private _store;
|
|
19
22
|
private _timers;
|
|
23
|
+
private _totalByteSize;
|
|
20
24
|
private _listeners;
|
|
25
|
+
constructor(_maxEntries?: number | undefined);
|
|
21
26
|
/**
|
|
22
27
|
* Registers an event listener.
|
|
23
28
|
*
|
|
@@ -26,25 +31,28 @@ export declare class MemorizeStore {
|
|
|
26
31
|
*
|
|
27
32
|
* @example
|
|
28
33
|
* ```ts
|
|
29
|
-
* store.on(MemorizeEventType.Set,
|
|
30
|
-
* store.on(MemorizeEventType.
|
|
34
|
+
* store.on(MemorizeEventType.Set, (e) => console.log('cached', e.key));
|
|
35
|
+
* store.on(MemorizeEventType.Evict, (e) => console.log('evicted', e.key));
|
|
31
36
|
* ```
|
|
32
37
|
*/
|
|
33
38
|
on(event: MemorizeEventType.Set, handler: (e: MemorizeSetEvent) => void): void;
|
|
34
39
|
on(event: MemorizeEventType.Delete, handler: (e: MemorizeDeleteEvent) => void): void;
|
|
35
40
|
on(event: MemorizeEventType.Expire, handler: (e: MemorizeExpireEvent) => void): void;
|
|
36
41
|
on(event: MemorizeEventType.Empty, handler: (e: MemorizeEmptyEvent) => void): void;
|
|
42
|
+
on(event: MemorizeEventType.Evict, handler: (e: MemorizeEvictEvent) => void): void;
|
|
37
43
|
/**
|
|
38
44
|
* Stores an entry in the cache.
|
|
39
45
|
*
|
|
40
46
|
* If an entry already exists for the given key its TTL timer is reset and the
|
|
41
|
-
* value is overwritten.
|
|
47
|
+
* value is overwritten. If `maxEntries` is configured and the store is full,
|
|
48
|
+
* the least-recently-used entry is evicted first. Emits a {@link MemorizeEventType.Set} event.
|
|
42
49
|
*
|
|
43
50
|
* @param key - The cache key (typically `req.originalUrl`).
|
|
44
51
|
* @param entry - The response data to store.
|
|
45
|
-
* @param ttl - Time-to-live in milliseconds. Omit or pass `null`
|
|
52
|
+
* @param ttl - Time-to-live in milliseconds. Omit or pass `null` to use the default TTL.
|
|
53
|
+
* Pass `Infinity` for no expiry.
|
|
46
54
|
*/
|
|
47
|
-
set(key: string, entry: Omit<CacheEntry, 'expiresAt' | 'hits'>, ttl?: number | null): void;
|
|
55
|
+
set(key: string, entry: Omit<CacheEntry, 'expiresAt' | 'hits' | 'size'>, ttl?: number | null): void;
|
|
48
56
|
/**
|
|
49
57
|
* Returns the formatted {@link CacheInfo} for the given key, or `null` if the
|
|
50
58
|
* key does not exist or its TTL has elapsed.
|
|
@@ -82,15 +90,30 @@ export declare class MemorizeStore {
|
|
|
82
90
|
* for each entry.
|
|
83
91
|
*/
|
|
84
92
|
clear(): void;
|
|
93
|
+
/**
|
|
94
|
+
* Returns the number of active cache entries.
|
|
95
|
+
*/
|
|
96
|
+
size(): number;
|
|
97
|
+
/**
|
|
98
|
+
* Returns the approximate total byte size of all cached bodies.
|
|
99
|
+
*
|
|
100
|
+
* The value is an estimate and may not reflect actual memory usage.
|
|
101
|
+
*/
|
|
102
|
+
byteSize(): number;
|
|
103
|
+
/**
|
|
104
|
+
* Returns aggregate cache statistics.
|
|
105
|
+
*/
|
|
106
|
+
getStats(): MemorizeStats;
|
|
85
107
|
/**
|
|
86
108
|
* Returns the raw {@link CacheEntry} for the given key without formatting metadata,
|
|
87
109
|
* or `null` if the entry is missing or expired. Used internally by the middleware
|
|
88
|
-
* to serve cached responses.
|
|
110
|
+
* to serve cached responses. Updates LRU order and increments the hit counter.
|
|
89
111
|
*
|
|
90
112
|
* @param key - The cache key to look up.
|
|
91
113
|
* @internal
|
|
92
114
|
*/
|
|
93
115
|
getRaw(key: string): CacheEntry | null;
|
|
116
|
+
private _evictLRU;
|
|
94
117
|
private _evict;
|
|
95
118
|
private _emit;
|
|
96
119
|
private _format;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemorizeStore.d.ts","sourceRoot":"","sources":["../src/MemorizeStore.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,YAAY,EACV,UAAU,EACV,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,GACnB,CAAC;AACF,OAAO,EAAE,iBAAiB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"MemorizeStore.d.ts","sourceRoot":"","sources":["../src/MemorizeStore.ts"],"names":[],"mappings":"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"}
|