@naman_deep_singh/cache 1.2.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 +7 -1
- package/dist/cjs/adapters/memcache/MemcacheCache.d.ts +1 -1
- package/dist/cjs/adapters/memcache/MemcacheCache.js +50 -18
- package/dist/cjs/adapters/memory/MemoryCache.d.ts +1 -1
- package/dist/cjs/adapters/memory/MemoryCache.js +7 -3
- package/dist/cjs/adapters/redis/RedisCache.d.ts +1 -1
- package/dist/cjs/adapters/redis/RedisCache.js +22 -9
- package/dist/cjs/core/BaseCache.d.ts +1 -1
- package/dist/cjs/core/BaseCache.js +2 -2
- package/dist/cjs/core/factory.js +2 -2
- package/dist/cjs/core/interfaces/ICache.d.ts +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/middleware/express/cacheMiddleware.d.ts +2 -2
- package/dist/cjs/middleware/express/cacheMiddleware.js +22 -6
- package/dist/cjs/middleware/express/index.d.ts +1 -1
- package/dist/cjs/session/SessionStore.d.ts +6 -0
- package/dist/cjs/session/SessionStore.js +9 -1
- package/dist/esm/adapters/memcache/MemcacheCache.d.ts +1 -1
- package/dist/esm/adapters/memcache/MemcacheCache.js +50 -18
- package/dist/esm/adapters/memory/MemoryCache.d.ts +1 -1
- package/dist/esm/adapters/memory/MemoryCache.js +7 -3
- package/dist/esm/adapters/redis/RedisCache.d.ts +1 -1
- package/dist/esm/adapters/redis/RedisCache.js +22 -9
- package/dist/esm/core/BaseCache.d.ts +1 -1
- package/dist/esm/core/BaseCache.js +2 -2
- package/dist/esm/core/factory.js +2 -2
- package/dist/esm/core/interfaces/ICache.d.ts +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/middleware/express/cacheMiddleware.d.ts +2 -2
- package/dist/esm/middleware/express/cacheMiddleware.js +22 -6
- package/dist/esm/middleware/express/index.d.ts +1 -1
- package/dist/esm/middleware/express/index.js +1 -1
- package/dist/esm/session/SessionStore.d.ts +6 -0
- package/dist/esm/session/SessionStore.js +9 -1
- package/dist/types/adapters/memcache/MemcacheCache.d.ts +1 -1
- package/dist/types/adapters/memory/MemoryCache.d.ts +1 -1
- package/dist/types/adapters/redis/RedisCache.d.ts +1 -1
- package/dist/types/core/BaseCache.d.ts +1 -1
- package/dist/types/core/interfaces/ICache.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/middleware/express/cacheMiddleware.d.ts +2 -2
- package/dist/types/middleware/express/index.d.ts +1 -1
- package/dist/types/session/SessionStore.d.ts +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @naman_deep_singh/cache
|
|
2
2
|
|
|
3
|
-
**Version:** 1.
|
|
3
|
+
**Version:** 1.3.1 (with Redis Clustering support)
|
|
4
4
|
|
|
5
5
|
A flexible, extensible caching layer with support for Redis, Memcache, and in-memory caches. Includes session management, health checks, and Express middleware.
|
|
6
6
|
|
|
@@ -687,3 +687,9 @@ ISC
|
|
|
687
687
|
## Author
|
|
688
688
|
|
|
689
689
|
Naman Deep Singh
|
|
690
|
+
|
|
691
|
+
## TypeScript Notes
|
|
692
|
+
|
|
693
|
+
- The package provides strong generics for `ICache<T>` and `SessionStore` so you can store typed values safely.
|
|
694
|
+
- Middleware in this package may attach runtime properties (for example session data) to `Request` objects in Express. If you're using TypeScript, import the package into your project so the included type augmentations are picked up automatically — no need to cast `req` to `any` in most cases.
|
|
695
|
+
|
|
@@ -31,12 +31,14 @@ class MemcacheCache extends BaseCache_1.BaseCache {
|
|
|
31
31
|
remove: true,
|
|
32
32
|
failOverServers: [],
|
|
33
33
|
maxValue: 1048576, // 1MB default
|
|
34
|
-
idle: 30000
|
|
34
|
+
idle: 30000,
|
|
35
35
|
};
|
|
36
36
|
if (this.memcacheConfig.username) {
|
|
37
|
+
;
|
|
37
38
|
options.username = this.memcacheConfig.username;
|
|
38
39
|
}
|
|
39
40
|
if (this.memcacheConfig.password) {
|
|
41
|
+
;
|
|
40
42
|
options.password = this.memcacheConfig.password;
|
|
41
43
|
}
|
|
42
44
|
this.client = new memcached_1.default(servers, options);
|
|
@@ -86,19 +88,28 @@ class MemcacheCache extends BaseCache_1.BaseCache {
|
|
|
86
88
|
reject(err);
|
|
87
89
|
return;
|
|
88
90
|
}
|
|
89
|
-
if (data === undefined) {
|
|
91
|
+
if (data === undefined || data === null) {
|
|
90
92
|
this.recordMiss();
|
|
91
93
|
resolve(null);
|
|
94
|
+
return;
|
|
92
95
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
this.recordHit();
|
|
97
|
+
try {
|
|
98
|
+
if (typeof data === 'string') {
|
|
96
99
|
resolve(this.deserialize(data));
|
|
97
100
|
}
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
else if (Buffer.isBuffer(data)) {
|
|
102
|
+
resolve(this.deserialize(data.toString()));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Unknown shape from memcached client - treat as miss
|
|
106
|
+
this.recordMiss();
|
|
107
|
+
resolve(null);
|
|
100
108
|
}
|
|
101
109
|
}
|
|
110
|
+
catch (parseErr) {
|
|
111
|
+
reject(parseErr);
|
|
112
|
+
}
|
|
102
113
|
});
|
|
103
114
|
});
|
|
104
115
|
}
|
|
@@ -193,7 +204,7 @@ class MemcacheCache extends BaseCache_1.BaseCache {
|
|
|
193
204
|
async getMultiple(keys) {
|
|
194
205
|
try {
|
|
195
206
|
await this.ensureConnected();
|
|
196
|
-
const fullKeys = keys.map(k => this.buildKey(k));
|
|
207
|
+
const fullKeys = keys.map((k) => this.buildKey(k));
|
|
197
208
|
return new Promise((resolve, reject) => {
|
|
198
209
|
this.client.getMulti(fullKeys, (err, data) => {
|
|
199
210
|
if (err) {
|
|
@@ -201,21 +212,42 @@ class MemcacheCache extends BaseCache_1.BaseCache {
|
|
|
201
212
|
return;
|
|
202
213
|
}
|
|
203
214
|
const result = {};
|
|
215
|
+
if (!data || typeof data !== 'object') {
|
|
216
|
+
// Treat as all misses
|
|
217
|
+
for (const key of keys) {
|
|
218
|
+
this.recordMiss();
|
|
219
|
+
result[key] = null;
|
|
220
|
+
}
|
|
221
|
+
resolve(result);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const map = data;
|
|
204
225
|
keys.forEach((key) => {
|
|
205
226
|
const fullKey = this.buildKey(key);
|
|
206
|
-
|
|
227
|
+
const value = map[fullKey];
|
|
228
|
+
if (value === undefined || value === null) {
|
|
229
|
+
this.recordMiss();
|
|
230
|
+
result[key] = null;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
207
233
|
this.recordHit();
|
|
208
234
|
try {
|
|
209
|
-
|
|
235
|
+
if (typeof value === 'string') {
|
|
236
|
+
result[key] = this.deserialize(value);
|
|
237
|
+
}
|
|
238
|
+
else if (Buffer.isBuffer(value)) {
|
|
239
|
+
result[key] = this.deserialize(value.toString());
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
// Unknown, treat as miss
|
|
243
|
+
this.recordMiss();
|
|
244
|
+
result[key] = null;
|
|
245
|
+
}
|
|
210
246
|
}
|
|
211
247
|
catch (parseErr) {
|
|
212
248
|
reject(parseErr);
|
|
213
249
|
}
|
|
214
250
|
}
|
|
215
|
-
else {
|
|
216
|
-
this.recordMiss();
|
|
217
|
-
result[key] = null;
|
|
218
|
-
}
|
|
219
251
|
});
|
|
220
252
|
resolve(result);
|
|
221
253
|
});
|
|
@@ -259,9 +291,9 @@ class MemcacheCache extends BaseCache_1.BaseCache {
|
|
|
259
291
|
async deleteMultiple(keys) {
|
|
260
292
|
try {
|
|
261
293
|
await this.ensureConnected();
|
|
262
|
-
const fullKeys = keys.map(k => this.buildKey(k));
|
|
294
|
+
const fullKeys = keys.map((k) => this.buildKey(k));
|
|
263
295
|
let deletedCount = 0;
|
|
264
|
-
await Promise.all(fullKeys.map(key => new Promise((resolve, reject) => {
|
|
296
|
+
await Promise.all(fullKeys.map((key) => new Promise((resolve, reject) => {
|
|
265
297
|
this.client.del(key, (err) => {
|
|
266
298
|
if (err) {
|
|
267
299
|
reject(err);
|
|
@@ -316,7 +348,7 @@ class MemcacheCache extends BaseCache_1.BaseCache {
|
|
|
316
348
|
return {
|
|
317
349
|
isAlive: true,
|
|
318
350
|
adapter: 'memcache',
|
|
319
|
-
timestamp: new Date()
|
|
351
|
+
timestamp: new Date(),
|
|
320
352
|
};
|
|
321
353
|
}
|
|
322
354
|
catch (err) {
|
|
@@ -324,7 +356,7 @@ class MemcacheCache extends BaseCache_1.BaseCache {
|
|
|
324
356
|
isAlive: false,
|
|
325
357
|
adapter: 'memcache',
|
|
326
358
|
timestamp: new Date(),
|
|
327
|
-
error: err.message
|
|
359
|
+
error: err.message,
|
|
328
360
|
};
|
|
329
361
|
}
|
|
330
362
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { MemoryCacheConfig, HealthCheckResponse } from '../../types';
|
|
2
1
|
import { BaseCache } from '../../core/BaseCache';
|
|
2
|
+
import type { HealthCheckResponse, MemoryCacheConfig } from '../../types';
|
|
3
3
|
/**
|
|
4
4
|
* In-memory cache adapter for development and testing
|
|
5
5
|
*/
|
|
@@ -212,7 +212,9 @@ class MemoryCache extends BaseCache_1.BaseCache {
|
|
|
212
212
|
const fullKey = this.buildKey(key);
|
|
213
213
|
const entry = this.store.get(fullKey);
|
|
214
214
|
const current = entry && (!entry.expiresAt || entry.expiresAt >= Date.now())
|
|
215
|
-
?
|
|
215
|
+
? typeof entry.value === 'number'
|
|
216
|
+
? entry.value
|
|
217
|
+
: 0
|
|
216
218
|
: 0;
|
|
217
219
|
const value = current + amount;
|
|
218
220
|
const expiry = this.ttl;
|
|
@@ -232,7 +234,9 @@ class MemoryCache extends BaseCache_1.BaseCache {
|
|
|
232
234
|
const fullKey = this.buildKey(key);
|
|
233
235
|
const entry = this.store.get(fullKey);
|
|
234
236
|
const current = entry && (!entry.expiresAt || entry.expiresAt >= Date.now())
|
|
235
|
-
?
|
|
237
|
+
? typeof entry.value === 'number'
|
|
238
|
+
? entry.value
|
|
239
|
+
: 0
|
|
236
240
|
: 0;
|
|
237
241
|
const value = current - amount;
|
|
238
242
|
const expiry = this.ttl;
|
|
@@ -251,7 +255,7 @@ class MemoryCache extends BaseCache_1.BaseCache {
|
|
|
251
255
|
return {
|
|
252
256
|
isAlive: true,
|
|
253
257
|
adapter: 'memory',
|
|
254
|
-
timestamp: new Date()
|
|
258
|
+
timestamp: new Date(),
|
|
255
259
|
};
|
|
256
260
|
}
|
|
257
261
|
/**
|
|
@@ -20,7 +20,10 @@ class RedisCache extends BaseCache_1.BaseCache {
|
|
|
20
20
|
async connect() {
|
|
21
21
|
try {
|
|
22
22
|
const cluster = this.redisConfig.cluster;
|
|
23
|
-
const hasCluster = cluster &&
|
|
23
|
+
const hasCluster = cluster &&
|
|
24
|
+
(Array.isArray(cluster)
|
|
25
|
+
? cluster.length > 0
|
|
26
|
+
: cluster.nodes?.length > 0);
|
|
24
27
|
if (hasCluster && cluster) {
|
|
25
28
|
// Cluster mode
|
|
26
29
|
let nodes = [];
|
|
@@ -31,7 +34,9 @@ class RedisCache extends BaseCache_1.BaseCache {
|
|
|
31
34
|
nodes = cluster.nodes;
|
|
32
35
|
}
|
|
33
36
|
this.client = (0, redis_1.createCluster)({
|
|
34
|
-
rootNodes: nodes.map(node => ({
|
|
37
|
+
rootNodes: nodes.map((node) => ({
|
|
38
|
+
url: `redis://${node.host}:${node.port}`,
|
|
39
|
+
})),
|
|
35
40
|
});
|
|
36
41
|
}
|
|
37
42
|
else {
|
|
@@ -39,7 +44,7 @@ class RedisCache extends BaseCache_1.BaseCache {
|
|
|
39
44
|
const options = {
|
|
40
45
|
host: this.redisConfig.host ?? 'localhost',
|
|
41
46
|
port: this.redisConfig.port ?? 6379,
|
|
42
|
-
db: this.redisConfig.db ?? 0
|
|
47
|
+
db: this.redisConfig.db ?? 0,
|
|
43
48
|
};
|
|
44
49
|
if (this.redisConfig.username) {
|
|
45
50
|
options.username = this.redisConfig.username;
|
|
@@ -159,7 +164,7 @@ class RedisCache extends BaseCache_1.BaseCache {
|
|
|
159
164
|
else {
|
|
160
165
|
// Clear all keys only in single-instance mode
|
|
161
166
|
const client = this.client;
|
|
162
|
-
if (client.flushDb) {
|
|
167
|
+
if (client && typeof client.flushDb === 'function') {
|
|
163
168
|
await client.flushDb();
|
|
164
169
|
}
|
|
165
170
|
else {
|
|
@@ -177,7 +182,7 @@ class RedisCache extends BaseCache_1.BaseCache {
|
|
|
177
182
|
async getMultiple(keys) {
|
|
178
183
|
try {
|
|
179
184
|
await this.ensureConnected();
|
|
180
|
-
const fullKeys = keys.map(k => this.buildKey(k));
|
|
185
|
+
const fullKeys = keys.map((k) => this.buildKey(k));
|
|
181
186
|
const values = await this.client.mGet(fullKeys);
|
|
182
187
|
const result = {};
|
|
183
188
|
keys.forEach((key, index) => {
|
|
@@ -235,7 +240,7 @@ class RedisCache extends BaseCache_1.BaseCache {
|
|
|
235
240
|
async deleteMultiple(keys) {
|
|
236
241
|
try {
|
|
237
242
|
await this.ensureConnected();
|
|
238
|
-
const fullKeys = keys.map(k => this.buildKey(k));
|
|
243
|
+
const fullKeys = keys.map((k) => this.buildKey(k));
|
|
239
244
|
const result = await this.client.del(fullKeys);
|
|
240
245
|
this.stats.deletes += result;
|
|
241
246
|
return result;
|
|
@@ -277,11 +282,19 @@ class RedisCache extends BaseCache_1.BaseCache {
|
|
|
277
282
|
try {
|
|
278
283
|
await this.ensureConnected();
|
|
279
284
|
// Use sendCommand which works for both single and cluster
|
|
280
|
-
|
|
285
|
+
// `sendCommand` exists on both single and cluster clients in runtime; cast narrowly for the call
|
|
286
|
+
if (this.client &&
|
|
287
|
+
typeof this.client.sendCommand === 'function') {
|
|
288
|
+
await this.client.sendCommand(['PING']);
|
|
289
|
+
}
|
|
290
|
+
else if (this.client &&
|
|
291
|
+
typeof this.client.ping === 'function') {
|
|
292
|
+
await this.client.ping();
|
|
293
|
+
}
|
|
281
294
|
return {
|
|
282
295
|
isAlive: true,
|
|
283
296
|
adapter: 'redis',
|
|
284
|
-
timestamp: new Date()
|
|
297
|
+
timestamp: new Date(),
|
|
285
298
|
};
|
|
286
299
|
}
|
|
287
300
|
catch (err) {
|
|
@@ -289,7 +302,7 @@ class RedisCache extends BaseCache_1.BaseCache {
|
|
|
289
302
|
isAlive: false,
|
|
290
303
|
adapter: 'redis',
|
|
291
304
|
timestamp: new Date(),
|
|
292
|
-
error: err.message
|
|
305
|
+
error: err.message,
|
|
293
306
|
};
|
|
294
307
|
}
|
|
295
308
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CacheConfig, CacheStats, HealthCheckResponse
|
|
1
|
+
import type { BatchResult, CacheConfig, CacheStats, HealthCheckResponse } from '../types';
|
|
2
2
|
import type { ICache } from './interfaces';
|
|
3
3
|
/**
|
|
4
4
|
* Abstract base class for all cache adapters
|
|
@@ -12,7 +12,7 @@ class BaseCache {
|
|
|
12
12
|
hits: 0,
|
|
13
13
|
misses: 0,
|
|
14
14
|
sets: 0,
|
|
15
|
-
deletes: 0
|
|
15
|
+
deletes: 0,
|
|
16
16
|
};
|
|
17
17
|
this.namespace = config.namespace ? `${config.namespace}:` : '';
|
|
18
18
|
this.ttl = config.ttl ?? 3600; // 1 hour default
|
|
@@ -77,7 +77,7 @@ class BaseCache {
|
|
|
77
77
|
hits: 0,
|
|
78
78
|
misses: 0,
|
|
79
79
|
sets: 0,
|
|
80
|
-
deletes: 0
|
|
80
|
+
deletes: 0,
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
83
|
/**
|
package/dist/cjs/core/factory.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CacheFactory = void 0;
|
|
4
|
-
const redis_1 = require("../adapters/redis");
|
|
5
4
|
const memcache_1 = require("../adapters/memcache");
|
|
6
5
|
const memory_1 = require("../adapters/memory");
|
|
6
|
+
const redis_1 = require("../adapters/redis");
|
|
7
7
|
const errors_1 = require("../errors");
|
|
8
8
|
/**
|
|
9
9
|
* Factory for creating cache instances
|
|
@@ -50,7 +50,7 @@ class CacheFactory {
|
|
|
50
50
|
return new memory_1.MemoryCache({
|
|
51
51
|
adapter: 'memory',
|
|
52
52
|
namespace: config.namespace,
|
|
53
|
-
ttl: config.ttl
|
|
53
|
+
ttl: config.ttl,
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
56
|
// No fallback, throw error
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { CacheConfig, RedisCacheConfig, MemcacheCacheConfig, MemoryCacheConfig, SessionData, SessionOptions, CacheStats, HealthCheckResponse, BatchResult, CacheEntry } from './types';
|
|
1
|
+
export type { CacheConfig, RedisCacheConfig, MemcacheCacheConfig, MemoryCacheConfig, SessionData, SessionOptions, CacheStats, HealthCheckResponse, BatchResult, CacheEntry, } from './types';
|
|
2
2
|
export { CacheError } from './errors';
|
|
3
3
|
export type { ICache, ISession } from './core/interfaces';
|
|
4
4
|
export { BaseCache } from './core/BaseCache';
|
|
@@ -7,4 +7,4 @@ export { MemcacheCache } from './adapters/memcache';
|
|
|
7
7
|
export { MemoryCache } from './adapters/memory';
|
|
8
8
|
export { CacheFactory } from './core/factory';
|
|
9
9
|
export { SessionStore } from './session';
|
|
10
|
-
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware } from './middleware/express';
|
|
10
|
+
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware, } from './middleware/express';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Request, Response
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
2
|
import type { ICache } from '../../core/interfaces';
|
|
3
|
-
import { SessionStore } from '../../session/SessionStore';
|
|
3
|
+
import type { SessionStore } from '../../session/SessionStore';
|
|
4
4
|
/**
|
|
5
5
|
* Express middleware for session management using cache
|
|
6
6
|
*/
|
|
@@ -17,6 +17,7 @@ function cacheSessionMiddleware(sessionStore, options) {
|
|
|
17
17
|
// Fetch session data and extend expiry
|
|
18
18
|
const sessionData = await sessionStore.getAndExtend(sessionId);
|
|
19
19
|
if (sessionData) {
|
|
20
|
+
;
|
|
20
21
|
req[sessionDataKey] = sessionData;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
@@ -44,7 +45,7 @@ function cacheHealthCheckMiddleware(cache, endpoint = '/.health/cache') {
|
|
|
44
45
|
isAlive: false,
|
|
45
46
|
adapter: 'unknown',
|
|
46
47
|
timestamp: new Date(),
|
|
47
|
-
error: err.message
|
|
48
|
+
error: err.message,
|
|
48
49
|
});
|
|
49
50
|
});
|
|
50
51
|
return;
|
|
@@ -58,7 +59,9 @@ function cacheHealthCheckMiddleware(cache, endpoint = '/.health/cache') {
|
|
|
58
59
|
function cacheResponseMiddleware(cache, options) {
|
|
59
60
|
const ttl = options?.ttl ?? 300; // 5 minutes default
|
|
60
61
|
const keyPrefix = options?.keyPrefix ?? 'response:';
|
|
61
|
-
const excludeStatusCodes = options?.excludeStatusCodes ?? [
|
|
62
|
+
const excludeStatusCodes = options?.excludeStatusCodes ?? [
|
|
63
|
+
300, 301, 302, 303, 304, 307, 308, 404, 500, 501, 502, 503,
|
|
64
|
+
];
|
|
62
65
|
return (req, res, next) => {
|
|
63
66
|
// Only cache GET requests
|
|
64
67
|
if (req.method !== 'GET') {
|
|
@@ -82,10 +85,23 @@ function cacheResponseMiddleware(cache, options) {
|
|
|
82
85
|
if (res.statusCode >= 200 &&
|
|
83
86
|
res.statusCode < 300 &&
|
|
84
87
|
!excludeStatusCodes.includes(res.statusCode)) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
88
|
+
let responseData = null;
|
|
89
|
+
if (typeof data === 'string') {
|
|
90
|
+
responseData = data;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
try {
|
|
94
|
+
responseData = JSON.stringify(data);
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
responseData = null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (responseData !== null) {
|
|
101
|
+
cache.set(cacheKey, responseData, ttl).catch((err) => {
|
|
102
|
+
console.error('Failed to cache response:', err);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
89
105
|
}
|
|
90
106
|
res.set('X-Cache', 'MISS');
|
|
91
107
|
return originalSend.call(this, data);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware } from './cacheMiddleware';
|
|
1
|
+
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware, } from './cacheMiddleware';
|
|
@@ -48,4 +48,10 @@ export declare class SessionStore {
|
|
|
48
48
|
* Get session data and extend expiry in one operation
|
|
49
49
|
*/
|
|
50
50
|
getAndExtend(sessionId: string, ttl?: number): Promise<SessionData | null>;
|
|
51
|
+
/**
|
|
52
|
+
* Optional close hook for graceful shutdowns.
|
|
53
|
+
* Present to allow callers to call `close()` during shutdown without
|
|
54
|
+
* requiring every store implementation to provide one.
|
|
55
|
+
*/
|
|
56
|
+
close(): Promise<void>;
|
|
51
57
|
}
|
|
@@ -12,7 +12,7 @@ class SessionStore {
|
|
|
12
12
|
this.options = {
|
|
13
13
|
ttl: options.ttl ?? 3600, // 1 hour default
|
|
14
14
|
serialize: options.serialize ?? ((data) => JSON.stringify(data)),
|
|
15
|
-
deserialize: options.deserialize ?? ((data) => JSON.parse(data))
|
|
15
|
+
deserialize: options.deserialize ?? ((data) => JSON.parse(data)),
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
@@ -149,5 +149,13 @@ class SessionStore {
|
|
|
149
149
|
throw new errors_1.CacheError(`Failed to get and extend session "${sessionId}"`, 'SESSION_GET_EXTEND_ERROR', 'session', err);
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Optional close hook for graceful shutdowns.
|
|
154
|
+
* Present to allow callers to call `close()` during shutdown without
|
|
155
|
+
* requiring every store implementation to provide one.
|
|
156
|
+
*/
|
|
157
|
+
async close() {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
152
160
|
}
|
|
153
161
|
exports.SessionStore = SessionStore;
|
|
@@ -25,12 +25,14 @@ export class MemcacheCache extends BaseCache {
|
|
|
25
25
|
remove: true,
|
|
26
26
|
failOverServers: [],
|
|
27
27
|
maxValue: 1048576, // 1MB default
|
|
28
|
-
idle: 30000
|
|
28
|
+
idle: 30000,
|
|
29
29
|
};
|
|
30
30
|
if (this.memcacheConfig.username) {
|
|
31
|
+
;
|
|
31
32
|
options.username = this.memcacheConfig.username;
|
|
32
33
|
}
|
|
33
34
|
if (this.memcacheConfig.password) {
|
|
35
|
+
;
|
|
34
36
|
options.password = this.memcacheConfig.password;
|
|
35
37
|
}
|
|
36
38
|
this.client = new Memcached(servers, options);
|
|
@@ -80,19 +82,28 @@ export class MemcacheCache extends BaseCache {
|
|
|
80
82
|
reject(err);
|
|
81
83
|
return;
|
|
82
84
|
}
|
|
83
|
-
if (data === undefined) {
|
|
85
|
+
if (data === undefined || data === null) {
|
|
84
86
|
this.recordMiss();
|
|
85
87
|
resolve(null);
|
|
88
|
+
return;
|
|
86
89
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
this.recordHit();
|
|
91
|
+
try {
|
|
92
|
+
if (typeof data === 'string') {
|
|
90
93
|
resolve(this.deserialize(data));
|
|
91
94
|
}
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
else if (Buffer.isBuffer(data)) {
|
|
96
|
+
resolve(this.deserialize(data.toString()));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Unknown shape from memcached client - treat as miss
|
|
100
|
+
this.recordMiss();
|
|
101
|
+
resolve(null);
|
|
94
102
|
}
|
|
95
103
|
}
|
|
104
|
+
catch (parseErr) {
|
|
105
|
+
reject(parseErr);
|
|
106
|
+
}
|
|
96
107
|
});
|
|
97
108
|
});
|
|
98
109
|
}
|
|
@@ -187,7 +198,7 @@ export class MemcacheCache extends BaseCache {
|
|
|
187
198
|
async getMultiple(keys) {
|
|
188
199
|
try {
|
|
189
200
|
await this.ensureConnected();
|
|
190
|
-
const fullKeys = keys.map(k => this.buildKey(k));
|
|
201
|
+
const fullKeys = keys.map((k) => this.buildKey(k));
|
|
191
202
|
return new Promise((resolve, reject) => {
|
|
192
203
|
this.client.getMulti(fullKeys, (err, data) => {
|
|
193
204
|
if (err) {
|
|
@@ -195,21 +206,42 @@ export class MemcacheCache extends BaseCache {
|
|
|
195
206
|
return;
|
|
196
207
|
}
|
|
197
208
|
const result = {};
|
|
209
|
+
if (!data || typeof data !== 'object') {
|
|
210
|
+
// Treat as all misses
|
|
211
|
+
for (const key of keys) {
|
|
212
|
+
this.recordMiss();
|
|
213
|
+
result[key] = null;
|
|
214
|
+
}
|
|
215
|
+
resolve(result);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const map = data;
|
|
198
219
|
keys.forEach((key) => {
|
|
199
220
|
const fullKey = this.buildKey(key);
|
|
200
|
-
|
|
221
|
+
const value = map[fullKey];
|
|
222
|
+
if (value === undefined || value === null) {
|
|
223
|
+
this.recordMiss();
|
|
224
|
+
result[key] = null;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
201
227
|
this.recordHit();
|
|
202
228
|
try {
|
|
203
|
-
|
|
229
|
+
if (typeof value === 'string') {
|
|
230
|
+
result[key] = this.deserialize(value);
|
|
231
|
+
}
|
|
232
|
+
else if (Buffer.isBuffer(value)) {
|
|
233
|
+
result[key] = this.deserialize(value.toString());
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
// Unknown, treat as miss
|
|
237
|
+
this.recordMiss();
|
|
238
|
+
result[key] = null;
|
|
239
|
+
}
|
|
204
240
|
}
|
|
205
241
|
catch (parseErr) {
|
|
206
242
|
reject(parseErr);
|
|
207
243
|
}
|
|
208
244
|
}
|
|
209
|
-
else {
|
|
210
|
-
this.recordMiss();
|
|
211
|
-
result[key] = null;
|
|
212
|
-
}
|
|
213
245
|
});
|
|
214
246
|
resolve(result);
|
|
215
247
|
});
|
|
@@ -253,9 +285,9 @@ export class MemcacheCache extends BaseCache {
|
|
|
253
285
|
async deleteMultiple(keys) {
|
|
254
286
|
try {
|
|
255
287
|
await this.ensureConnected();
|
|
256
|
-
const fullKeys = keys.map(k => this.buildKey(k));
|
|
288
|
+
const fullKeys = keys.map((k) => this.buildKey(k));
|
|
257
289
|
let deletedCount = 0;
|
|
258
|
-
await Promise.all(fullKeys.map(key => new Promise((resolve, reject) => {
|
|
290
|
+
await Promise.all(fullKeys.map((key) => new Promise((resolve, reject) => {
|
|
259
291
|
this.client.del(key, (err) => {
|
|
260
292
|
if (err) {
|
|
261
293
|
reject(err);
|
|
@@ -310,7 +342,7 @@ export class MemcacheCache extends BaseCache {
|
|
|
310
342
|
return {
|
|
311
343
|
isAlive: true,
|
|
312
344
|
adapter: 'memcache',
|
|
313
|
-
timestamp: new Date()
|
|
345
|
+
timestamp: new Date(),
|
|
314
346
|
};
|
|
315
347
|
}
|
|
316
348
|
catch (err) {
|
|
@@ -318,7 +350,7 @@ export class MemcacheCache extends BaseCache {
|
|
|
318
350
|
isAlive: false,
|
|
319
351
|
adapter: 'memcache',
|
|
320
352
|
timestamp: new Date(),
|
|
321
|
-
error: err.message
|
|
353
|
+
error: err.message,
|
|
322
354
|
};
|
|
323
355
|
}
|
|
324
356
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { MemoryCacheConfig, HealthCheckResponse } from '../../types';
|
|
2
1
|
import { BaseCache } from '../../core/BaseCache';
|
|
2
|
+
import type { HealthCheckResponse, MemoryCacheConfig } from '../../types';
|
|
3
3
|
/**
|
|
4
4
|
* In-memory cache adapter for development and testing
|
|
5
5
|
*/
|
|
@@ -209,7 +209,9 @@ export class MemoryCache extends BaseCache {
|
|
|
209
209
|
const fullKey = this.buildKey(key);
|
|
210
210
|
const entry = this.store.get(fullKey);
|
|
211
211
|
const current = entry && (!entry.expiresAt || entry.expiresAt >= Date.now())
|
|
212
|
-
?
|
|
212
|
+
? typeof entry.value === 'number'
|
|
213
|
+
? entry.value
|
|
214
|
+
: 0
|
|
213
215
|
: 0;
|
|
214
216
|
const value = current + amount;
|
|
215
217
|
const expiry = this.ttl;
|
|
@@ -229,7 +231,9 @@ export class MemoryCache extends BaseCache {
|
|
|
229
231
|
const fullKey = this.buildKey(key);
|
|
230
232
|
const entry = this.store.get(fullKey);
|
|
231
233
|
const current = entry && (!entry.expiresAt || entry.expiresAt >= Date.now())
|
|
232
|
-
?
|
|
234
|
+
? typeof entry.value === 'number'
|
|
235
|
+
? entry.value
|
|
236
|
+
: 0
|
|
233
237
|
: 0;
|
|
234
238
|
const value = current - amount;
|
|
235
239
|
const expiry = this.ttl;
|
|
@@ -248,7 +252,7 @@ export class MemoryCache extends BaseCache {
|
|
|
248
252
|
return {
|
|
249
253
|
isAlive: true,
|
|
250
254
|
adapter: 'memory',
|
|
251
|
-
timestamp: new Date()
|
|
255
|
+
timestamp: new Date(),
|
|
252
256
|
};
|
|
253
257
|
}
|
|
254
258
|
/**
|
|
@@ -17,7 +17,10 @@ export class RedisCache extends BaseCache {
|
|
|
17
17
|
async connect() {
|
|
18
18
|
try {
|
|
19
19
|
const cluster = this.redisConfig.cluster;
|
|
20
|
-
const hasCluster = cluster &&
|
|
20
|
+
const hasCluster = cluster &&
|
|
21
|
+
(Array.isArray(cluster)
|
|
22
|
+
? cluster.length > 0
|
|
23
|
+
: cluster.nodes?.length > 0);
|
|
21
24
|
if (hasCluster && cluster) {
|
|
22
25
|
// Cluster mode
|
|
23
26
|
let nodes = [];
|
|
@@ -28,7 +31,9 @@ export class RedisCache extends BaseCache {
|
|
|
28
31
|
nodes = cluster.nodes;
|
|
29
32
|
}
|
|
30
33
|
this.client = createCluster({
|
|
31
|
-
rootNodes: nodes.map(node => ({
|
|
34
|
+
rootNodes: nodes.map((node) => ({
|
|
35
|
+
url: `redis://${node.host}:${node.port}`,
|
|
36
|
+
})),
|
|
32
37
|
});
|
|
33
38
|
}
|
|
34
39
|
else {
|
|
@@ -36,7 +41,7 @@ export class RedisCache extends BaseCache {
|
|
|
36
41
|
const options = {
|
|
37
42
|
host: this.redisConfig.host ?? 'localhost',
|
|
38
43
|
port: this.redisConfig.port ?? 6379,
|
|
39
|
-
db: this.redisConfig.db ?? 0
|
|
44
|
+
db: this.redisConfig.db ?? 0,
|
|
40
45
|
};
|
|
41
46
|
if (this.redisConfig.username) {
|
|
42
47
|
options.username = this.redisConfig.username;
|
|
@@ -156,7 +161,7 @@ export class RedisCache extends BaseCache {
|
|
|
156
161
|
else {
|
|
157
162
|
// Clear all keys only in single-instance mode
|
|
158
163
|
const client = this.client;
|
|
159
|
-
if (client.flushDb) {
|
|
164
|
+
if (client && typeof client.flushDb === 'function') {
|
|
160
165
|
await client.flushDb();
|
|
161
166
|
}
|
|
162
167
|
else {
|
|
@@ -174,7 +179,7 @@ export class RedisCache extends BaseCache {
|
|
|
174
179
|
async getMultiple(keys) {
|
|
175
180
|
try {
|
|
176
181
|
await this.ensureConnected();
|
|
177
|
-
const fullKeys = keys.map(k => this.buildKey(k));
|
|
182
|
+
const fullKeys = keys.map((k) => this.buildKey(k));
|
|
178
183
|
const values = await this.client.mGet(fullKeys);
|
|
179
184
|
const result = {};
|
|
180
185
|
keys.forEach((key, index) => {
|
|
@@ -232,7 +237,7 @@ export class RedisCache extends BaseCache {
|
|
|
232
237
|
async deleteMultiple(keys) {
|
|
233
238
|
try {
|
|
234
239
|
await this.ensureConnected();
|
|
235
|
-
const fullKeys = keys.map(k => this.buildKey(k));
|
|
240
|
+
const fullKeys = keys.map((k) => this.buildKey(k));
|
|
236
241
|
const result = await this.client.del(fullKeys);
|
|
237
242
|
this.stats.deletes += result;
|
|
238
243
|
return result;
|
|
@@ -274,11 +279,19 @@ export class RedisCache extends BaseCache {
|
|
|
274
279
|
try {
|
|
275
280
|
await this.ensureConnected();
|
|
276
281
|
// Use sendCommand which works for both single and cluster
|
|
277
|
-
|
|
282
|
+
// `sendCommand` exists on both single and cluster clients in runtime; cast narrowly for the call
|
|
283
|
+
if (this.client &&
|
|
284
|
+
typeof this.client.sendCommand === 'function') {
|
|
285
|
+
await this.client.sendCommand(['PING']);
|
|
286
|
+
}
|
|
287
|
+
else if (this.client &&
|
|
288
|
+
typeof this.client.ping === 'function') {
|
|
289
|
+
await this.client.ping();
|
|
290
|
+
}
|
|
278
291
|
return {
|
|
279
292
|
isAlive: true,
|
|
280
293
|
adapter: 'redis',
|
|
281
|
-
timestamp: new Date()
|
|
294
|
+
timestamp: new Date(),
|
|
282
295
|
};
|
|
283
296
|
}
|
|
284
297
|
catch (err) {
|
|
@@ -286,7 +299,7 @@ export class RedisCache extends BaseCache {
|
|
|
286
299
|
isAlive: false,
|
|
287
300
|
adapter: 'redis',
|
|
288
301
|
timestamp: new Date(),
|
|
289
|
-
error: err.message
|
|
302
|
+
error: err.message,
|
|
290
303
|
};
|
|
291
304
|
}
|
|
292
305
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CacheConfig, CacheStats, HealthCheckResponse
|
|
1
|
+
import type { BatchResult, CacheConfig, CacheStats, HealthCheckResponse } from '../types';
|
|
2
2
|
import type { ICache } from './interfaces';
|
|
3
3
|
/**
|
|
4
4
|
* Abstract base class for all cache adapters
|
|
@@ -9,7 +9,7 @@ export class BaseCache {
|
|
|
9
9
|
hits: 0,
|
|
10
10
|
misses: 0,
|
|
11
11
|
sets: 0,
|
|
12
|
-
deletes: 0
|
|
12
|
+
deletes: 0,
|
|
13
13
|
};
|
|
14
14
|
this.namespace = config.namespace ? `${config.namespace}:` : '';
|
|
15
15
|
this.ttl = config.ttl ?? 3600; // 1 hour default
|
|
@@ -74,7 +74,7 @@ export class BaseCache {
|
|
|
74
74
|
hits: 0,
|
|
75
75
|
misses: 0,
|
|
76
76
|
sets: 0,
|
|
77
|
-
deletes: 0
|
|
77
|
+
deletes: 0,
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
80
|
/**
|
package/dist/esm/core/factory.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { RedisCache } from '../adapters/redis';
|
|
2
1
|
import { MemcacheCache } from '../adapters/memcache';
|
|
3
2
|
import { MemoryCache } from '../adapters/memory';
|
|
3
|
+
import { RedisCache } from '../adapters/redis';
|
|
4
4
|
import { CacheError } from '../errors';
|
|
5
5
|
/**
|
|
6
6
|
* Factory for creating cache instances
|
|
@@ -47,7 +47,7 @@ export class CacheFactory {
|
|
|
47
47
|
return new MemoryCache({
|
|
48
48
|
adapter: 'memory',
|
|
49
49
|
namespace: config.namespace,
|
|
50
|
-
ttl: config.ttl
|
|
50
|
+
ttl: config.ttl,
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
// No fallback, throw error
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { CacheConfig, RedisCacheConfig, MemcacheCacheConfig, MemoryCacheConfig, SessionData, SessionOptions, CacheStats, HealthCheckResponse, BatchResult, CacheEntry } from './types';
|
|
1
|
+
export type { CacheConfig, RedisCacheConfig, MemcacheCacheConfig, MemoryCacheConfig, SessionData, SessionOptions, CacheStats, HealthCheckResponse, BatchResult, CacheEntry, } from './types';
|
|
2
2
|
export { CacheError } from './errors';
|
|
3
3
|
export type { ICache, ISession } from './core/interfaces';
|
|
4
4
|
export { BaseCache } from './core/BaseCache';
|
|
@@ -7,4 +7,4 @@ export { MemcacheCache } from './adapters/memcache';
|
|
|
7
7
|
export { MemoryCache } from './adapters/memory';
|
|
8
8
|
export { CacheFactory } from './core/factory';
|
|
9
9
|
export { SessionStore } from './session';
|
|
10
|
-
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware } from './middleware/express';
|
|
10
|
+
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware, } from './middleware/express';
|
package/dist/esm/index.js
CHANGED
|
@@ -11,4 +11,4 @@ export { CacheFactory } from './core/factory';
|
|
|
11
11
|
// Session
|
|
12
12
|
export { SessionStore } from './session';
|
|
13
13
|
// Middleware
|
|
14
|
-
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware } from './middleware/express';
|
|
14
|
+
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware, } from './middleware/express';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Request, Response
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
2
|
import type { ICache } from '../../core/interfaces';
|
|
3
|
-
import { SessionStore } from '../../session/SessionStore';
|
|
3
|
+
import type { SessionStore } from '../../session/SessionStore';
|
|
4
4
|
/**
|
|
5
5
|
* Express middleware for session management using cache
|
|
6
6
|
*/
|
|
@@ -12,6 +12,7 @@ export function cacheSessionMiddleware(sessionStore, options) {
|
|
|
12
12
|
// Fetch session data and extend expiry
|
|
13
13
|
const sessionData = await sessionStore.getAndExtend(sessionId);
|
|
14
14
|
if (sessionData) {
|
|
15
|
+
;
|
|
15
16
|
req[sessionDataKey] = sessionData;
|
|
16
17
|
}
|
|
17
18
|
}
|
|
@@ -39,7 +40,7 @@ export function cacheHealthCheckMiddleware(cache, endpoint = '/.health/cache') {
|
|
|
39
40
|
isAlive: false,
|
|
40
41
|
adapter: 'unknown',
|
|
41
42
|
timestamp: new Date(),
|
|
42
|
-
error: err.message
|
|
43
|
+
error: err.message,
|
|
43
44
|
});
|
|
44
45
|
});
|
|
45
46
|
return;
|
|
@@ -53,7 +54,9 @@ export function cacheHealthCheckMiddleware(cache, endpoint = '/.health/cache') {
|
|
|
53
54
|
export function cacheResponseMiddleware(cache, options) {
|
|
54
55
|
const ttl = options?.ttl ?? 300; // 5 minutes default
|
|
55
56
|
const keyPrefix = options?.keyPrefix ?? 'response:';
|
|
56
|
-
const excludeStatusCodes = options?.excludeStatusCodes ?? [
|
|
57
|
+
const excludeStatusCodes = options?.excludeStatusCodes ?? [
|
|
58
|
+
300, 301, 302, 303, 304, 307, 308, 404, 500, 501, 502, 503,
|
|
59
|
+
];
|
|
57
60
|
return (req, res, next) => {
|
|
58
61
|
// Only cache GET requests
|
|
59
62
|
if (req.method !== 'GET') {
|
|
@@ -77,10 +80,23 @@ export function cacheResponseMiddleware(cache, options) {
|
|
|
77
80
|
if (res.statusCode >= 200 &&
|
|
78
81
|
res.statusCode < 300 &&
|
|
79
82
|
!excludeStatusCodes.includes(res.statusCode)) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
83
|
+
let responseData = null;
|
|
84
|
+
if (typeof data === 'string') {
|
|
85
|
+
responseData = data;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
try {
|
|
89
|
+
responseData = JSON.stringify(data);
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
responseData = null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (responseData !== null) {
|
|
96
|
+
cache.set(cacheKey, responseData, ttl).catch((err) => {
|
|
97
|
+
console.error('Failed to cache response:', err);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
84
100
|
}
|
|
85
101
|
res.set('X-Cache', 'MISS');
|
|
86
102
|
return originalSend.call(this, data);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware } from './cacheMiddleware';
|
|
1
|
+
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware, } from './cacheMiddleware';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware } from './cacheMiddleware';
|
|
1
|
+
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware, } from './cacheMiddleware';
|
|
@@ -48,4 +48,10 @@ export declare class SessionStore {
|
|
|
48
48
|
* Get session data and extend expiry in one operation
|
|
49
49
|
*/
|
|
50
50
|
getAndExtend(sessionId: string, ttl?: number): Promise<SessionData | null>;
|
|
51
|
+
/**
|
|
52
|
+
* Optional close hook for graceful shutdowns.
|
|
53
|
+
* Present to allow callers to call `close()` during shutdown without
|
|
54
|
+
* requiring every store implementation to provide one.
|
|
55
|
+
*/
|
|
56
|
+
close(): Promise<void>;
|
|
51
57
|
}
|
|
@@ -9,7 +9,7 @@ export class SessionStore {
|
|
|
9
9
|
this.options = {
|
|
10
10
|
ttl: options.ttl ?? 3600, // 1 hour default
|
|
11
11
|
serialize: options.serialize ?? ((data) => JSON.stringify(data)),
|
|
12
|
-
deserialize: options.deserialize ?? ((data) => JSON.parse(data))
|
|
12
|
+
deserialize: options.deserialize ?? ((data) => JSON.parse(data)),
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
@@ -146,4 +146,12 @@ export class SessionStore {
|
|
|
146
146
|
throw new CacheError(`Failed to get and extend session "${sessionId}"`, 'SESSION_GET_EXTEND_ERROR', 'session', err);
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Optional close hook for graceful shutdowns.
|
|
151
|
+
* Present to allow callers to call `close()` during shutdown without
|
|
152
|
+
* requiring every store implementation to provide one.
|
|
153
|
+
*/
|
|
154
|
+
async close() {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
149
157
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { MemoryCacheConfig, HealthCheckResponse } from '../../types';
|
|
2
1
|
import { BaseCache } from '../../core/BaseCache';
|
|
2
|
+
import type { HealthCheckResponse, MemoryCacheConfig } from '../../types';
|
|
3
3
|
/**
|
|
4
4
|
* In-memory cache adapter for development and testing
|
|
5
5
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CacheConfig, CacheStats, HealthCheckResponse
|
|
1
|
+
import type { BatchResult, CacheConfig, CacheStats, HealthCheckResponse } from '../types';
|
|
2
2
|
import type { ICache } from './interfaces';
|
|
3
3
|
/**
|
|
4
4
|
* Abstract base class for all cache adapters
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { CacheConfig, RedisCacheConfig, MemcacheCacheConfig, MemoryCacheConfig, SessionData, SessionOptions, CacheStats, HealthCheckResponse, BatchResult, CacheEntry } from './types';
|
|
1
|
+
export type { CacheConfig, RedisCacheConfig, MemcacheCacheConfig, MemoryCacheConfig, SessionData, SessionOptions, CacheStats, HealthCheckResponse, BatchResult, CacheEntry, } from './types';
|
|
2
2
|
export { CacheError } from './errors';
|
|
3
3
|
export type { ICache, ISession } from './core/interfaces';
|
|
4
4
|
export { BaseCache } from './core/BaseCache';
|
|
@@ -7,4 +7,4 @@ export { MemcacheCache } from './adapters/memcache';
|
|
|
7
7
|
export { MemoryCache } from './adapters/memory';
|
|
8
8
|
export { CacheFactory } from './core/factory';
|
|
9
9
|
export { SessionStore } from './session';
|
|
10
|
-
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware } from './middleware/express';
|
|
10
|
+
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware, } from './middleware/express';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Request, Response
|
|
1
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
2
2
|
import type { ICache } from '../../core/interfaces';
|
|
3
|
-
import { SessionStore } from '../../session/SessionStore';
|
|
3
|
+
import type { SessionStore } from '../../session/SessionStore';
|
|
4
4
|
/**
|
|
5
5
|
* Express middleware for session management using cache
|
|
6
6
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware } from './cacheMiddleware';
|
|
1
|
+
export { cacheSessionMiddleware, cacheHealthCheckMiddleware, cacheResponseMiddleware, } from './cacheMiddleware';
|
|
@@ -48,4 +48,10 @@ export declare class SessionStore {
|
|
|
48
48
|
* Get session data and extend expiry in one operation
|
|
49
49
|
*/
|
|
50
50
|
getAndExtend(sessionId: string, ttl?: number): Promise<SessionData | null>;
|
|
51
|
+
/**
|
|
52
|
+
* Optional close hook for graceful shutdowns.
|
|
53
|
+
* Present to allow callers to call `close()` during shutdown without
|
|
54
|
+
* requiring every store implementation to provide one.
|
|
55
|
+
*/
|
|
56
|
+
close(): Promise<void>;
|
|
51
57
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naman_deep_singh/cache",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Extensible caching layer supporting Redis, Memcache, and in-memory caches with automatic fallback, namespacing, session management, and Express middleware.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|