@naman_deep_singh/cache 1.1.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/README.md +637 -0
- package/dist/cjs/adapters/memcache/MemcacheCache.d.ts +71 -0
- package/dist/cjs/adapters/memcache/MemcacheCache.js +347 -0
- package/dist/cjs/adapters/memcache/index.d.ts +1 -0
- package/dist/cjs/adapters/memcache/index.js +5 -0
- package/dist/cjs/adapters/memory/MemoryCache.d.ts +63 -0
- package/dist/cjs/adapters/memory/MemoryCache.js +273 -0
- package/dist/cjs/adapters/memory/index.d.ts +1 -0
- package/dist/cjs/adapters/memory/index.js +5 -0
- package/dist/cjs/adapters/redis/RedisCache.d.ts +67 -0
- package/dist/cjs/adapters/redis/RedisCache.js +288 -0
- package/dist/cjs/adapters/redis/index.d.ts +1 -0
- package/dist/cjs/adapters/redis/index.js +5 -0
- package/dist/cjs/core/BaseCache.d.ts +78 -0
- package/dist/cjs/core/BaseCache.js +138 -0
- package/dist/cjs/core/factory.d.ts +16 -0
- package/dist/cjs/core/factory.js +51 -0
- package/dist/cjs/core/interfaces/ICache.d.ts +58 -0
- package/dist/cjs/core/interfaces/ICache.js +2 -0
- package/dist/cjs/core/interfaces/ISession.d.ts +30 -0
- package/dist/cjs/core/interfaces/ISession.js +2 -0
- package/dist/cjs/core/interfaces/index.d.ts +2 -0
- package/dist/cjs/core/interfaces/index.js +2 -0
- package/dist/cjs/errors/CacheError.d.ts +9 -0
- package/dist/cjs/errors/CacheError.js +17 -0
- package/dist/cjs/errors/index.d.ts +1 -0
- package/dist/cjs/errors/index.js +5 -0
- package/dist/cjs/index.d.ts +10 -0
- package/dist/cjs/index.js +27 -0
- package/dist/cjs/middleware/express/cacheMiddleware.d.ts +22 -0
- package/dist/cjs/middleware/express/cacheMiddleware.js +100 -0
- package/dist/cjs/middleware/express/index.d.ts +1 -0
- package/dist/cjs/middleware/express/index.js +7 -0
- package/dist/cjs/session/SessionStore.d.ts +51 -0
- package/dist/cjs/session/SessionStore.js +153 -0
- package/dist/cjs/session/index.d.ts +2 -0
- package/dist/cjs/session/index.js +5 -0
- package/dist/cjs/types.d.ts +83 -0
- package/dist/cjs/types.js +2 -0
- package/dist/esm/adapters/memcache/MemcacheCache.d.ts +71 -0
- package/dist/esm/adapters/memcache/MemcacheCache.js +340 -0
- package/dist/esm/adapters/memcache/index.d.ts +1 -0
- package/dist/esm/adapters/memcache/index.js +1 -0
- package/dist/esm/adapters/memory/MemoryCache.d.ts +63 -0
- package/dist/esm/adapters/memory/MemoryCache.js +269 -0
- package/dist/esm/adapters/memory/index.d.ts +1 -0
- package/dist/esm/adapters/memory/index.js +1 -0
- package/dist/esm/adapters/redis/RedisCache.d.ts +67 -0
- package/dist/esm/adapters/redis/RedisCache.js +284 -0
- package/dist/esm/adapters/redis/index.d.ts +1 -0
- package/dist/esm/adapters/redis/index.js +1 -0
- package/dist/esm/core/BaseCache.d.ts +78 -0
- package/dist/esm/core/BaseCache.js +134 -0
- package/dist/esm/core/factory.d.ts +16 -0
- package/dist/esm/core/factory.js +47 -0
- package/dist/esm/core/interfaces/ICache.d.ts +58 -0
- package/dist/esm/core/interfaces/ICache.js +1 -0
- package/dist/esm/core/interfaces/ISession.d.ts +30 -0
- package/dist/esm/core/interfaces/ISession.js +1 -0
- package/dist/esm/core/interfaces/index.d.ts +2 -0
- package/dist/esm/core/interfaces/index.js +1 -0
- package/dist/esm/errors/CacheError.d.ts +9 -0
- package/dist/esm/errors/CacheError.js +13 -0
- package/dist/esm/errors/index.d.ts +1 -0
- package/dist/esm/errors/index.js +1 -0
- package/dist/esm/index.d.ts +10 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/middleware/express/cacheMiddleware.d.ts +22 -0
- package/dist/esm/middleware/express/cacheMiddleware.js +95 -0
- package/dist/esm/middleware/express/index.d.ts +1 -0
- package/dist/esm/middleware/express/index.js +1 -0
- package/dist/esm/session/SessionStore.d.ts +51 -0
- package/dist/esm/session/SessionStore.js +149 -0
- package/dist/esm/session/index.d.ts +2 -0
- package/dist/esm/session/index.js +1 -0
- package/dist/esm/types.d.ts +83 -0
- package/dist/esm/types.js +1 -0
- package/dist/types/adapters/memcache/MemcacheCache.d.ts +71 -0
- package/dist/types/adapters/memcache/index.d.ts +1 -0
- package/dist/types/adapters/memory/MemoryCache.d.ts +63 -0
- package/dist/types/adapters/memory/index.d.ts +1 -0
- package/dist/types/adapters/redis/RedisCache.d.ts +67 -0
- package/dist/types/adapters/redis/index.d.ts +1 -0
- package/dist/types/core/BaseCache.d.ts +78 -0
- package/dist/types/core/factory.d.ts +16 -0
- package/dist/types/core/interfaces/ICache.d.ts +58 -0
- package/dist/types/core/interfaces/ISession.d.ts +30 -0
- package/dist/types/core/interfaces/index.d.ts +2 -0
- package/dist/types/errors/CacheError.d.ts +9 -0
- package/dist/types/errors/index.d.ts +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/middleware/express/cacheMiddleware.d.ts +22 -0
- package/dist/types/middleware/express/index.d.ts +1 -0
- package/dist/types/session/SessionStore.d.ts +51 -0
- package/dist/types/session/index.d.ts +2 -0
- package/dist/types/types.d.ts +83 -0
- package/package.json +48 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { RedisCacheConfig, HealthCheckResponse } from '../../types';
|
|
2
|
+
import { BaseCache } from '../../core/BaseCache';
|
|
3
|
+
/**
|
|
4
|
+
* Redis cache adapter
|
|
5
|
+
*/
|
|
6
|
+
export declare class RedisCache<T = unknown> extends BaseCache<T> {
|
|
7
|
+
private redisConfig;
|
|
8
|
+
private client;
|
|
9
|
+
private isConnected;
|
|
10
|
+
constructor(redisConfig: RedisCacheConfig);
|
|
11
|
+
/**
|
|
12
|
+
* Connect to Redis
|
|
13
|
+
*/
|
|
14
|
+
connect(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Ensure client is connected
|
|
17
|
+
*/
|
|
18
|
+
private ensureConnected;
|
|
19
|
+
/**
|
|
20
|
+
* Get a value from Redis
|
|
21
|
+
*/
|
|
22
|
+
get(key: string): Promise<T | null>;
|
|
23
|
+
/**
|
|
24
|
+
* Set a value in Redis
|
|
25
|
+
*/
|
|
26
|
+
set(key: string, value: T, ttl?: number): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Delete a key from Redis
|
|
29
|
+
*/
|
|
30
|
+
delete(key: string): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* Check if key exists
|
|
33
|
+
*/
|
|
34
|
+
exists(key: string): Promise<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* Clear all keys with current namespace
|
|
37
|
+
*/
|
|
38
|
+
clear(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Get multiple values at once
|
|
41
|
+
*/
|
|
42
|
+
getMultiple(keys: string[]): Promise<Record<string, T | null>>;
|
|
43
|
+
/**
|
|
44
|
+
* Set multiple values at once
|
|
45
|
+
*/
|
|
46
|
+
setMultiple(data: Record<string, T>, ttl?: number): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Delete multiple keys at once
|
|
49
|
+
*/
|
|
50
|
+
deleteMultiple(keys: string[]): Promise<number>;
|
|
51
|
+
/**
|
|
52
|
+
* Increment a numeric value
|
|
53
|
+
*/
|
|
54
|
+
increment(key: string, amount?: number): Promise<number>;
|
|
55
|
+
/**
|
|
56
|
+
* Decrement a numeric value
|
|
57
|
+
*/
|
|
58
|
+
decrement(key: string, amount?: number): Promise<number>;
|
|
59
|
+
/**
|
|
60
|
+
* Check if Redis is alive
|
|
61
|
+
*/
|
|
62
|
+
isAlive(): Promise<HealthCheckResponse>;
|
|
63
|
+
/**
|
|
64
|
+
* Close Redis connection
|
|
65
|
+
*/
|
|
66
|
+
close(): Promise<void>;
|
|
67
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedisCache = void 0;
|
|
4
|
+
const redis_1 = require("redis");
|
|
5
|
+
const BaseCache_1 = require("../../core/BaseCache");
|
|
6
|
+
const errors_1 = require("../../errors");
|
|
7
|
+
/**
|
|
8
|
+
* Redis cache adapter
|
|
9
|
+
*/
|
|
10
|
+
class RedisCache extends BaseCache_1.BaseCache {
|
|
11
|
+
constructor(redisConfig) {
|
|
12
|
+
super(redisConfig);
|
|
13
|
+
this.redisConfig = redisConfig;
|
|
14
|
+
this.client = null;
|
|
15
|
+
this.isConnected = false;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Connect to Redis
|
|
19
|
+
*/
|
|
20
|
+
async connect() {
|
|
21
|
+
try {
|
|
22
|
+
const options = {
|
|
23
|
+
host: this.redisConfig.host ?? 'localhost',
|
|
24
|
+
port: this.redisConfig.port ?? 6379,
|
|
25
|
+
db: this.redisConfig.db ?? 0
|
|
26
|
+
};
|
|
27
|
+
if (this.redisConfig.username) {
|
|
28
|
+
options.username = this.redisConfig.username;
|
|
29
|
+
}
|
|
30
|
+
if (this.redisConfig.password) {
|
|
31
|
+
options.password = this.redisConfig.password;
|
|
32
|
+
}
|
|
33
|
+
if (this.redisConfig.tls) {
|
|
34
|
+
options.tls = true;
|
|
35
|
+
}
|
|
36
|
+
this.client = (0, redis_1.createClient)(options);
|
|
37
|
+
this.client.on('error', (err) => {
|
|
38
|
+
this.isConnected = false;
|
|
39
|
+
console.error('Redis connection error:', err);
|
|
40
|
+
});
|
|
41
|
+
this.client.on('connect', () => {
|
|
42
|
+
this.isConnected = true;
|
|
43
|
+
});
|
|
44
|
+
await this.client.connect();
|
|
45
|
+
this.isConnected = true;
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
throw new errors_1.CacheError('Failed to connect to Redis', 'REDIS_CONNECTION_ERROR', 'redis', err);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Ensure client is connected
|
|
53
|
+
*/
|
|
54
|
+
async ensureConnected() {
|
|
55
|
+
if (!this.client) {
|
|
56
|
+
await this.connect();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get a value from Redis
|
|
61
|
+
*/
|
|
62
|
+
async get(key) {
|
|
63
|
+
try {
|
|
64
|
+
await this.ensureConnected();
|
|
65
|
+
const fullKey = this.buildKey(key);
|
|
66
|
+
const value = await this.client.get(fullKey);
|
|
67
|
+
if (value === null) {
|
|
68
|
+
this.recordMiss();
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
this.recordHit();
|
|
72
|
+
return this.deserialize(value);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
throw new errors_1.CacheError(`Failed to get key "${key}" from Redis`, 'REDIS_GET_ERROR', 'redis', err);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Set a value in Redis
|
|
80
|
+
*/
|
|
81
|
+
async set(key, value, ttl) {
|
|
82
|
+
try {
|
|
83
|
+
await this.ensureConnected();
|
|
84
|
+
const fullKey = this.buildKey(key);
|
|
85
|
+
const serialized = this.serialize(value);
|
|
86
|
+
const expiry = ttl ?? this.ttl;
|
|
87
|
+
if (expiry > 0) {
|
|
88
|
+
await this.client.setEx(fullKey, expiry, serialized);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
await this.client.set(fullKey, serialized);
|
|
92
|
+
}
|
|
93
|
+
this.recordSet();
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
throw new errors_1.CacheError(`Failed to set key "${key}" in Redis`, 'REDIS_SET_ERROR', 'redis', err);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Delete a key from Redis
|
|
101
|
+
*/
|
|
102
|
+
async delete(key) {
|
|
103
|
+
try {
|
|
104
|
+
await this.ensureConnected();
|
|
105
|
+
const fullKey = this.buildKey(key);
|
|
106
|
+
const result = await this.client.del(fullKey);
|
|
107
|
+
this.recordDelete();
|
|
108
|
+
return result > 0;
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
throw new errors_1.CacheError(`Failed to delete key "${key}" from Redis`, 'REDIS_DELETE_ERROR', 'redis', err);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Check if key exists
|
|
116
|
+
*/
|
|
117
|
+
async exists(key) {
|
|
118
|
+
try {
|
|
119
|
+
await this.ensureConnected();
|
|
120
|
+
const fullKey = this.buildKey(key);
|
|
121
|
+
const result = await this.client.exists(fullKey);
|
|
122
|
+
return result > 0;
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
throw new errors_1.CacheError(`Failed to check existence of key "${key}" in Redis`, 'REDIS_EXISTS_ERROR', 'redis', err);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Clear all keys with current namespace
|
|
130
|
+
*/
|
|
131
|
+
async clear() {
|
|
132
|
+
try {
|
|
133
|
+
await this.ensureConnected();
|
|
134
|
+
if (this.namespace) {
|
|
135
|
+
// Clear only keys with the current namespace
|
|
136
|
+
const pattern = `${this.namespace}*`;
|
|
137
|
+
const keys = await this.client.keys(pattern);
|
|
138
|
+
if (keys.length > 0) {
|
|
139
|
+
await this.client.del(keys);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Clear all keys
|
|
144
|
+
await this.client.flushDb();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
throw new errors_1.CacheError('Failed to clear Redis cache', 'REDIS_CLEAR_ERROR', 'redis', err);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get multiple values at once
|
|
153
|
+
*/
|
|
154
|
+
async getMultiple(keys) {
|
|
155
|
+
try {
|
|
156
|
+
await this.ensureConnected();
|
|
157
|
+
const fullKeys = keys.map(k => this.buildKey(k));
|
|
158
|
+
const values = await this.client.mGet(fullKeys);
|
|
159
|
+
const result = {};
|
|
160
|
+
keys.forEach((key, index) => {
|
|
161
|
+
const value = values[index];
|
|
162
|
+
if (value === null) {
|
|
163
|
+
this.recordMiss();
|
|
164
|
+
result[key] = null;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
this.recordHit();
|
|
168
|
+
result[key] = this.deserialize(value);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
throw new errors_1.CacheError('Failed to get multiple keys from Redis', 'REDIS_GET_MULTIPLE_ERROR', 'redis', err);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Set multiple values at once
|
|
179
|
+
*/
|
|
180
|
+
async setMultiple(data, ttl) {
|
|
181
|
+
try {
|
|
182
|
+
await this.ensureConnected();
|
|
183
|
+
const expiry = ttl ?? this.ttl;
|
|
184
|
+
if (expiry > 0) {
|
|
185
|
+
// Use pipeline for batch operations with TTL
|
|
186
|
+
const pipeline = this.client.multi();
|
|
187
|
+
for (const [key, value] of Object.entries(data)) {
|
|
188
|
+
const fullKey = this.buildKey(key);
|
|
189
|
+
const serialized = this.serialize(value);
|
|
190
|
+
pipeline.setEx(fullKey, expiry, serialized);
|
|
191
|
+
}
|
|
192
|
+
await pipeline.exec();
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
// Use mSet for batch operations without TTL
|
|
196
|
+
const flatData = {};
|
|
197
|
+
for (const [key, value] of Object.entries(data)) {
|
|
198
|
+
const fullKey = this.buildKey(key);
|
|
199
|
+
flatData[fullKey] = this.serialize(value);
|
|
200
|
+
}
|
|
201
|
+
await this.client.mSet(flatData);
|
|
202
|
+
}
|
|
203
|
+
this.stats.sets += Object.keys(data).length;
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
throw new errors_1.CacheError('Failed to set multiple keys in Redis', 'REDIS_SET_MULTIPLE_ERROR', 'redis', err);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Delete multiple keys at once
|
|
211
|
+
*/
|
|
212
|
+
async deleteMultiple(keys) {
|
|
213
|
+
try {
|
|
214
|
+
await this.ensureConnected();
|
|
215
|
+
const fullKeys = keys.map(k => this.buildKey(k));
|
|
216
|
+
const result = await this.client.del(fullKeys);
|
|
217
|
+
this.stats.deletes += result;
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
throw new errors_1.CacheError('Failed to delete multiple keys from Redis', 'REDIS_DELETE_MULTIPLE_ERROR', 'redis', err);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Increment a numeric value
|
|
226
|
+
*/
|
|
227
|
+
async increment(key, amount = 1) {
|
|
228
|
+
try {
|
|
229
|
+
await this.ensureConnected();
|
|
230
|
+
const fullKey = this.buildKey(key);
|
|
231
|
+
return await this.client.incrBy(fullKey, amount);
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
throw new errors_1.CacheError(`Failed to increment key "${key}" in Redis`, 'REDIS_INCREMENT_ERROR', 'redis', err);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Decrement a numeric value
|
|
239
|
+
*/
|
|
240
|
+
async decrement(key, amount = 1) {
|
|
241
|
+
try {
|
|
242
|
+
await this.ensureConnected();
|
|
243
|
+
const fullKey = this.buildKey(key);
|
|
244
|
+
return await this.client.decrBy(fullKey, amount);
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
throw new errors_1.CacheError(`Failed to decrement key "${key}" in Redis`, 'REDIS_DECREMENT_ERROR', 'redis', err);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Check if Redis is alive
|
|
252
|
+
*/
|
|
253
|
+
async isAlive() {
|
|
254
|
+
try {
|
|
255
|
+
await this.ensureConnected();
|
|
256
|
+
await this.client.ping();
|
|
257
|
+
return {
|
|
258
|
+
isAlive: true,
|
|
259
|
+
adapter: 'redis',
|
|
260
|
+
timestamp: new Date()
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
return {
|
|
265
|
+
isAlive: false,
|
|
266
|
+
adapter: 'redis',
|
|
267
|
+
timestamp: new Date(),
|
|
268
|
+
error: err.message
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Close Redis connection
|
|
274
|
+
*/
|
|
275
|
+
async close() {
|
|
276
|
+
try {
|
|
277
|
+
if (this.client && this.isConnected) {
|
|
278
|
+
await this.client.quit();
|
|
279
|
+
this.isConnected = false;
|
|
280
|
+
this.client = null;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
throw new errors_1.CacheError('Failed to close Redis connection', 'REDIS_CLOSE_ERROR', 'redis', err);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
exports.RedisCache = RedisCache;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RedisCache } from './RedisCache';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedisCache = void 0;
|
|
4
|
+
var RedisCache_1 = require("./RedisCache");
|
|
5
|
+
Object.defineProperty(exports, "RedisCache", { enumerable: true, get: function () { return RedisCache_1.RedisCache; } });
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { CacheConfig, CacheStats, HealthCheckResponse, BatchResult } from '../types';
|
|
2
|
+
import type { ICache } from './interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Abstract base class for all cache adapters
|
|
5
|
+
*/
|
|
6
|
+
export declare abstract class BaseCache<T = unknown> implements ICache<T> {
|
|
7
|
+
protected config: CacheConfig;
|
|
8
|
+
protected readonly namespace: string;
|
|
9
|
+
protected readonly ttl: number;
|
|
10
|
+
protected stats: CacheStats;
|
|
11
|
+
constructor(config: CacheConfig);
|
|
12
|
+
/**
|
|
13
|
+
* Build full key with namespace prefix
|
|
14
|
+
*/
|
|
15
|
+
protected buildKey(key: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Deserialize string to object
|
|
18
|
+
*/
|
|
19
|
+
protected deserialize(data: string): T;
|
|
20
|
+
/**
|
|
21
|
+
* Serialize object to string
|
|
22
|
+
*/
|
|
23
|
+
protected serialize(value: T): string;
|
|
24
|
+
/**
|
|
25
|
+
* Track cache hit
|
|
26
|
+
*/
|
|
27
|
+
protected recordHit(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Track cache miss
|
|
30
|
+
*/
|
|
31
|
+
protected recordMiss(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Track cache set
|
|
34
|
+
*/
|
|
35
|
+
protected recordSet(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Track cache delete
|
|
38
|
+
*/
|
|
39
|
+
protected recordDelete(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Reset statistics
|
|
42
|
+
*/
|
|
43
|
+
protected resetStats(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Get cache statistics
|
|
46
|
+
*/
|
|
47
|
+
getStats(): Promise<CacheStats>;
|
|
48
|
+
/**
|
|
49
|
+
* Abstract methods - must be implemented by subclasses
|
|
50
|
+
*/
|
|
51
|
+
abstract get(key: string): Promise<T | null>;
|
|
52
|
+
abstract set(key: string, value: T, ttl?: number): Promise<void>;
|
|
53
|
+
abstract delete(key: string): Promise<boolean>;
|
|
54
|
+
abstract exists(key: string): Promise<boolean>;
|
|
55
|
+
abstract clear(): Promise<void>;
|
|
56
|
+
abstract isAlive(): Promise<HealthCheckResponse>;
|
|
57
|
+
abstract close(): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Default implementation for getMultiple
|
|
60
|
+
*/
|
|
61
|
+
getMultiple(keys: string[]): Promise<BatchResult<T>>;
|
|
62
|
+
/**
|
|
63
|
+
* Default implementation for setMultiple
|
|
64
|
+
*/
|
|
65
|
+
setMultiple(data: Record<string, T>, ttl?: number): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Default implementation for deleteMultiple
|
|
68
|
+
*/
|
|
69
|
+
deleteMultiple(keys: string[]): Promise<number>;
|
|
70
|
+
/**
|
|
71
|
+
* Default implementation for increment
|
|
72
|
+
*/
|
|
73
|
+
increment(key: string, amount?: number): Promise<number>;
|
|
74
|
+
/**
|
|
75
|
+
* Default implementation for decrement
|
|
76
|
+
*/
|
|
77
|
+
decrement(key: string, amount?: number): Promise<number>;
|
|
78
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseCache = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
/**
|
|
6
|
+
* Abstract base class for all cache adapters
|
|
7
|
+
*/
|
|
8
|
+
class BaseCache {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.stats = {
|
|
12
|
+
hits: 0,
|
|
13
|
+
misses: 0,
|
|
14
|
+
sets: 0,
|
|
15
|
+
deletes: 0
|
|
16
|
+
};
|
|
17
|
+
this.namespace = config.namespace ? `${config.namespace}:` : '';
|
|
18
|
+
this.ttl = config.ttl ?? 3600; // 1 hour default
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build full key with namespace prefix
|
|
22
|
+
*/
|
|
23
|
+
buildKey(key) {
|
|
24
|
+
return `${this.namespace}${key}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Deserialize string to object
|
|
28
|
+
*/
|
|
29
|
+
deserialize(data) {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(data);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
throw new errors_1.CacheError(`Failed to deserialize cache value for key`, 'DESERIALIZE_ERROR', this.config.adapter, err);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Serialize object to string
|
|
39
|
+
*/
|
|
40
|
+
serialize(value) {
|
|
41
|
+
try {
|
|
42
|
+
return JSON.stringify(value);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
throw new errors_1.CacheError(`Failed to serialize cache value`, 'SERIALIZE_ERROR', this.config.adapter, err);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Track cache hit
|
|
50
|
+
*/
|
|
51
|
+
recordHit() {
|
|
52
|
+
this.stats.hits++;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Track cache miss
|
|
56
|
+
*/
|
|
57
|
+
recordMiss() {
|
|
58
|
+
this.stats.misses++;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Track cache set
|
|
62
|
+
*/
|
|
63
|
+
recordSet() {
|
|
64
|
+
this.stats.sets++;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Track cache delete
|
|
68
|
+
*/
|
|
69
|
+
recordDelete() {
|
|
70
|
+
this.stats.deletes++;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Reset statistics
|
|
74
|
+
*/
|
|
75
|
+
resetStats() {
|
|
76
|
+
this.stats = {
|
|
77
|
+
hits: 0,
|
|
78
|
+
misses: 0,
|
|
79
|
+
sets: 0,
|
|
80
|
+
deletes: 0
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get cache statistics
|
|
85
|
+
*/
|
|
86
|
+
async getStats() {
|
|
87
|
+
return { ...this.stats };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Default implementation for getMultiple
|
|
91
|
+
*/
|
|
92
|
+
async getMultiple(keys) {
|
|
93
|
+
const result = {};
|
|
94
|
+
for (const key of keys) {
|
|
95
|
+
result[key] = await this.get(key);
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Default implementation for setMultiple
|
|
101
|
+
*/
|
|
102
|
+
async setMultiple(data, ttl) {
|
|
103
|
+
for (const [key, value] of Object.entries(data)) {
|
|
104
|
+
await this.set(key, value, ttl);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Default implementation for deleteMultiple
|
|
109
|
+
*/
|
|
110
|
+
async deleteMultiple(keys) {
|
|
111
|
+
let count = 0;
|
|
112
|
+
for (const key of keys) {
|
|
113
|
+
const deleted = await this.delete(key);
|
|
114
|
+
if (deleted)
|
|
115
|
+
count++;
|
|
116
|
+
}
|
|
117
|
+
return count;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Default implementation for increment
|
|
121
|
+
*/
|
|
122
|
+
async increment(key, amount = 1) {
|
|
123
|
+
const current = await this.get(key);
|
|
124
|
+
const value = (typeof current === 'number' ? current : 0) + amount;
|
|
125
|
+
await this.set(key, value);
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Default implementation for decrement
|
|
130
|
+
*/
|
|
131
|
+
async decrement(key, amount = 1) {
|
|
132
|
+
const current = await this.get(key);
|
|
133
|
+
const value = (typeof current === 'number' ? current : 0) - amount;
|
|
134
|
+
await this.set(key, value);
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
exports.BaseCache = BaseCache;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CacheConfig } from '../types';
|
|
2
|
+
import type { ICache } from './interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Factory for creating cache instances
|
|
5
|
+
*/
|
|
6
|
+
export declare class CacheFactory {
|
|
7
|
+
/**
|
|
8
|
+
* Create a cache instance based on configuration
|
|
9
|
+
*/
|
|
10
|
+
static create<T = unknown>(config: CacheConfig): ICache<T>;
|
|
11
|
+
/**
|
|
12
|
+
* Create a cache with fallback support
|
|
13
|
+
* If primary adapter fails to connect, falls back to memory cache
|
|
14
|
+
*/
|
|
15
|
+
static createWithFallback<T = unknown>(config: CacheConfig): Promise<ICache<T>>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CacheFactory = void 0;
|
|
4
|
+
const redis_1 = require("../adapters/redis");
|
|
5
|
+
const memcache_1 = require("../adapters/memcache");
|
|
6
|
+
const memory_1 = require("../adapters/memory");
|
|
7
|
+
const errors_1 = require("../errors");
|
|
8
|
+
/**
|
|
9
|
+
* Factory for creating cache instances
|
|
10
|
+
*/
|
|
11
|
+
class CacheFactory {
|
|
12
|
+
/**
|
|
13
|
+
* Create a cache instance based on configuration
|
|
14
|
+
*/
|
|
15
|
+
static create(config) {
|
|
16
|
+
switch (config.adapter) {
|
|
17
|
+
case 'redis':
|
|
18
|
+
return new redis_1.RedisCache(config);
|
|
19
|
+
case 'memcache':
|
|
20
|
+
return new memcache_1.MemcacheCache(config);
|
|
21
|
+
case 'memory':
|
|
22
|
+
return new memory_1.MemoryCache(config);
|
|
23
|
+
default:
|
|
24
|
+
throw new errors_1.CacheError(`Unknown cache adapter: ${config.adapter}`, 'UNKNOWN_ADAPTER');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create a cache with fallback support
|
|
29
|
+
* If primary adapter fails to connect, falls back to memory cache
|
|
30
|
+
*/
|
|
31
|
+
static async createWithFallback(config) {
|
|
32
|
+
const cache = this.create(config);
|
|
33
|
+
// Check if primary cache is alive
|
|
34
|
+
const health = await cache.isAlive();
|
|
35
|
+
if (health.isAlive) {
|
|
36
|
+
return cache;
|
|
37
|
+
}
|
|
38
|
+
// If primary cache failed and fallback is enabled, use memory cache
|
|
39
|
+
if (config.fallback !== false) {
|
|
40
|
+
console.warn(`Failed to connect to ${config.adapter} cache, falling back to memory cache`);
|
|
41
|
+
return new memory_1.MemoryCache({
|
|
42
|
+
adapter: 'memory',
|
|
43
|
+
namespace: config.namespace,
|
|
44
|
+
ttl: config.ttl
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// No fallback, throw error
|
|
48
|
+
throw new errors_1.CacheError(`Failed to initialize ${config.adapter} cache and fallback is disabled`, 'CACHE_INIT_ERROR');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.CacheFactory = CacheFactory;
|