@geekmidas/cache 0.0.3 → 0.0.4
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 +512 -0
- package/dist/__tests__/cache.spec.cjs +30 -29
- package/dist/__tests__/cache.spec.d.cts +1 -0
- package/dist/__tests__/cache.spec.d.mts +1 -0
- package/dist/__tests__/cache.spec.mjs +18 -18
- package/dist/__tests__/integration.spec.cjs +45 -44
- package/dist/__tests__/integration.spec.d.cts +1 -0
- package/dist/__tests__/integration.spec.d.mts +1 -0
- package/dist/__tests__/integration.spec.mjs +26 -26
- package/dist/__tests__/memory.spec.cjs +45 -44
- package/dist/__tests__/memory.spec.d.cts +1 -0
- package/dist/__tests__/memory.spec.d.mts +1 -0
- package/dist/__tests__/memory.spec.mjs +23 -23
- package/dist/__tests__/upstash.spec.cjs +50 -49
- package/dist/__tests__/upstash.spec.d.cts +1 -0
- package/dist/__tests__/upstash.spec.d.mts +1 -0
- package/dist/__tests__/upstash.spec.mjs +23 -23
- package/dist/index-BPnJ3lrS.d.mts +25 -0
- package/dist/index-q3xYn8_0.d.cts +25 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.mts +2 -0
- package/dist/memory.d.cts +11 -0
- package/dist/memory.d.mts +11 -0
- package/dist/memory.mjs +1 -1
- package/dist/upstash.d.cts +12 -0
- package/dist/upstash.d.mts +12 -0
- package/dist/upstash.mjs +1 -1
- package/package.json +7 -7
- package/dist/magic-string.es-BvJNBYzY.mjs +0 -1014
- package/dist/magic-string.es-CKiDiYoS.cjs +0 -1015
- package/dist/vi.DgezovHB-McgltIC5.mjs +0 -13079
- package/dist/vi.DgezovHB-NqP74KGR.cjs +0 -13104
- /package/dist/{memory-YteQXj7m.mjs → memory-NQi0XmZ2.mjs} +0 -0
- /package/dist/{upstash-_05vyc5o.mjs → upstash-CTY-M8vR.mjs} +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
# @geekmidas/cache
|
|
2
|
+
|
|
3
|
+
A type-safe, flexible caching library for TypeScript applications with support for multiple cache implementations and a unified interface.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Type-safe**: Full TypeScript support with generics for strongly-typed cache values
|
|
8
|
+
- **Unified interface**: Common `Cache<T>` interface for all implementations
|
|
9
|
+
- **Multiple backends**: In-memory and Redis (Upstash) implementations included
|
|
10
|
+
- **Async API**: Promise-based interface for consistency across implementations
|
|
11
|
+
- **Flexible TTL**: Support for time-to-live where applicable
|
|
12
|
+
- **Easy testing**: Simple interface makes mocking and testing straightforward
|
|
13
|
+
- **Modular exports**: Import only what you need
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @geekmidas/cache
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Optional Dependencies
|
|
22
|
+
|
|
23
|
+
For Redis support via Upstash:
|
|
24
|
+
```bash
|
|
25
|
+
npm install @upstash/redis
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### In-Memory Cache
|
|
31
|
+
|
|
32
|
+
Perfect for development, testing, or simple applications:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { InMemoryCache } from '@geekmidas/cache/memory';
|
|
36
|
+
|
|
37
|
+
const cache = new InMemoryCache<string>();
|
|
38
|
+
|
|
39
|
+
// Set a value
|
|
40
|
+
await cache.set('user:123', 'John Doe');
|
|
41
|
+
|
|
42
|
+
// Get a value
|
|
43
|
+
const userName = await cache.get('user:123'); // 'John Doe'
|
|
44
|
+
|
|
45
|
+
// Delete a value
|
|
46
|
+
await cache.delete('user:123');
|
|
47
|
+
|
|
48
|
+
// Check if value exists
|
|
49
|
+
const exists = await cache.get('user:123'); // undefined
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Redis Cache (Upstash)
|
|
53
|
+
|
|
54
|
+
For production applications needing distributed caching:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { UpstashCache } from '@geekmidas/cache/upstash';
|
|
58
|
+
|
|
59
|
+
const cache = new UpstashCache<User>(
|
|
60
|
+
process.env.UPSTASH_REDIS_URL!,
|
|
61
|
+
process.env.UPSTASH_REDIS_TOKEN!
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Set with TTL (1 hour)
|
|
65
|
+
await cache.set('user:123', { id: 123, name: 'John Doe' }, 3600);
|
|
66
|
+
|
|
67
|
+
// Get typed object
|
|
68
|
+
const user = await cache.get('user:123'); // User | undefined
|
|
69
|
+
|
|
70
|
+
// Delete
|
|
71
|
+
await cache.delete('user:123');
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## API Reference
|
|
75
|
+
|
|
76
|
+
### Cache Interface
|
|
77
|
+
|
|
78
|
+
All cache implementations follow this interface:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
interface Cache<T> {
|
|
82
|
+
get(key: string): Promise<T | undefined>;
|
|
83
|
+
set(key: string, value: T, ttl?: number): Promise<void>;
|
|
84
|
+
delete(key: string): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### Methods
|
|
89
|
+
|
|
90
|
+
##### `get(key: string): Promise<T | undefined>`
|
|
91
|
+
|
|
92
|
+
Retrieves a value from the cache.
|
|
93
|
+
|
|
94
|
+
- **Parameters**: `key` - The cache key
|
|
95
|
+
- **Returns**: Promise resolving to the cached value or `undefined` if not found
|
|
96
|
+
- **Example**:
|
|
97
|
+
```typescript
|
|
98
|
+
const value = await cache.get('my-key');
|
|
99
|
+
if (value !== undefined) {
|
|
100
|
+
// Use the value
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
##### `set(key: string, value: T, ttl?: number): Promise<void>`
|
|
105
|
+
|
|
106
|
+
Stores a value in the cache.
|
|
107
|
+
|
|
108
|
+
- **Parameters**:
|
|
109
|
+
- `key` - The cache key
|
|
110
|
+
- `value` - The value to store
|
|
111
|
+
- `ttl` - Optional time-to-live in seconds
|
|
112
|
+
- **Returns**: Promise resolving when the operation completes
|
|
113
|
+
- **Example**:
|
|
114
|
+
```typescript
|
|
115
|
+
// Set without TTL
|
|
116
|
+
await cache.set('key', 'value');
|
|
117
|
+
|
|
118
|
+
// Set with 1 hour TTL
|
|
119
|
+
await cache.set('key', 'value', 3600);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
##### `delete(key: string): Promise<void>`
|
|
123
|
+
|
|
124
|
+
Removes a value from the cache.
|
|
125
|
+
|
|
126
|
+
- **Parameters**: `key` - The cache key to delete
|
|
127
|
+
- **Returns**: Promise resolving when the operation completes
|
|
128
|
+
- **Example**:
|
|
129
|
+
```typescript
|
|
130
|
+
await cache.delete('my-key');
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### InMemoryCache
|
|
134
|
+
|
|
135
|
+
Simple in-memory cache implementation.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { InMemoryCache } from '@geekmidas/cache/memory';
|
|
139
|
+
|
|
140
|
+
class InMemoryCache<T> implements Cache<T>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### Features
|
|
144
|
+
|
|
145
|
+
- ✅ Fast access (no network latency)
|
|
146
|
+
- ✅ Reference equality for objects
|
|
147
|
+
- ✅ No external dependencies
|
|
148
|
+
- ❌ No TTL support (TTL parameter is ignored)
|
|
149
|
+
- ❌ Data lost on process restart
|
|
150
|
+
- ❌ Not suitable for multi-instance applications
|
|
151
|
+
|
|
152
|
+
#### Example
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
const cache = new InMemoryCache<number>();
|
|
156
|
+
await cache.set('counter', 42);
|
|
157
|
+
const count = await cache.get('counter'); // 42
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### UpstashCache
|
|
161
|
+
|
|
162
|
+
Redis-based cache using Upstash Redis client.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { UpstashCache } from '@geekmidas/cache/upstash';
|
|
166
|
+
|
|
167
|
+
class UpstashCache<T> implements Cache<T>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### Constructor
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
new UpstashCache<T>(url: string, token: string)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- `url`: Upstash Redis URL
|
|
177
|
+
- `token`: Upstash Redis token
|
|
178
|
+
|
|
179
|
+
#### Features
|
|
180
|
+
|
|
181
|
+
- ✅ Persistent storage
|
|
182
|
+
- ✅ TTL support (defaults to 3600 seconds)
|
|
183
|
+
- ✅ Distributed caching
|
|
184
|
+
- ✅ JSON serialization/deserialization
|
|
185
|
+
- ❌ Requires external Redis service
|
|
186
|
+
- ❌ Network latency for operations
|
|
187
|
+
|
|
188
|
+
#### Example
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
interface User {
|
|
192
|
+
id: number;
|
|
193
|
+
name: string;
|
|
194
|
+
email: string;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const cache = new UpstashCache<User>(
|
|
198
|
+
'https://your-redis-url.upstash.io',
|
|
199
|
+
'your-token'
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
await cache.set('user:1', {
|
|
203
|
+
id: 1,
|
|
204
|
+
name: 'John Doe',
|
|
205
|
+
email: 'john@example.com'
|
|
206
|
+
}, 7200); // 2 hours TTL
|
|
207
|
+
|
|
208
|
+
const user = await cache.get('user:1');
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Advanced Usage
|
|
212
|
+
|
|
213
|
+
### Type Safety
|
|
214
|
+
|
|
215
|
+
The cache is fully type-safe with TypeScript generics:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// String cache
|
|
219
|
+
const stringCache = new InMemoryCache<string>();
|
|
220
|
+
await stringCache.set('key', 'value');
|
|
221
|
+
const str: string | undefined = await stringCache.get('key');
|
|
222
|
+
|
|
223
|
+
// Object cache
|
|
224
|
+
interface Product {
|
|
225
|
+
id: number;
|
|
226
|
+
name: string;
|
|
227
|
+
price: number;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const productCache = new InMemoryCache<Product>();
|
|
231
|
+
await productCache.set('product:1', { id: 1, name: 'Widget', price: 9.99 });
|
|
232
|
+
const product: Product | undefined = await productCache.get('product:1');
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Cache Abstraction
|
|
236
|
+
|
|
237
|
+
Create cache-agnostic services:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
interface UserService {
|
|
241
|
+
getUser(id: number): Promise<User | undefined>;
|
|
242
|
+
setUser(user: User): Promise<void>;
|
|
243
|
+
deleteUser(id: number): Promise<void>;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
class CachedUserService implements UserService {
|
|
247
|
+
constructor(
|
|
248
|
+
private cache: Cache<User>,
|
|
249
|
+
private userRepository: UserRepository
|
|
250
|
+
) {}
|
|
251
|
+
|
|
252
|
+
async getUser(id: number): Promise<User | undefined> {
|
|
253
|
+
const cacheKey = `user:${id}`;
|
|
254
|
+
|
|
255
|
+
// Try cache first
|
|
256
|
+
let user = await this.cache.get(cacheKey);
|
|
257
|
+
if (user) return user;
|
|
258
|
+
|
|
259
|
+
// Fallback to repository
|
|
260
|
+
user = await this.userRepository.findById(id);
|
|
261
|
+
if (user) {
|
|
262
|
+
await this.cache.set(cacheKey, user, 3600); // 1 hour
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return user;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async setUser(user: User): Promise<void> {
|
|
269
|
+
await this.userRepository.save(user);
|
|
270
|
+
await this.cache.set(`user:${user.id}`, user, 3600);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async deleteUser(id: number): Promise<void> {
|
|
274
|
+
await this.userRepository.delete(id);
|
|
275
|
+
await this.cache.delete(`user:${id}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Use with any cache implementation
|
|
280
|
+
const service = new CachedUserService(
|
|
281
|
+
new InMemoryCache<User>(), // or new UpstashCache<User>(url, token)
|
|
282
|
+
new UserRepository()
|
|
283
|
+
);
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Factory Pattern
|
|
287
|
+
|
|
288
|
+
Create cache instances based on configuration:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
interface CacheConfig {
|
|
292
|
+
type: 'memory' | 'redis';
|
|
293
|
+
redis?: {
|
|
294
|
+
url: string;
|
|
295
|
+
token: string;
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function createCache<T>(config: CacheConfig): Cache<T> {
|
|
300
|
+
switch (config.type) {
|
|
301
|
+
case 'memory':
|
|
302
|
+
return new InMemoryCache<T>();
|
|
303
|
+
case 'redis':
|
|
304
|
+
if (!config.redis) throw new Error('Redis config required');
|
|
305
|
+
return new UpstashCache<T>(config.redis.url, config.redis.token);
|
|
306
|
+
default:
|
|
307
|
+
throw new Error(`Unsupported cache type: ${config.type}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const cache = createCache<User>({
|
|
312
|
+
type: process.env.NODE_ENV === 'production' ? 'redis' : 'memory',
|
|
313
|
+
redis: {
|
|
314
|
+
url: process.env.UPSTASH_REDIS_URL!,
|
|
315
|
+
token: process.env.UPSTASH_REDIS_TOKEN!,
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Wrapper Pattern
|
|
321
|
+
|
|
322
|
+
Add functionality like logging or metrics:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
class LoggingCache<T> implements Cache<T> {
|
|
326
|
+
constructor(
|
|
327
|
+
private cache: Cache<T>,
|
|
328
|
+
private logger: Logger
|
|
329
|
+
) {}
|
|
330
|
+
|
|
331
|
+
async get(key: string): Promise<T | undefined> {
|
|
332
|
+
this.logger.debug(`Cache GET: ${key}`);
|
|
333
|
+
const value = await this.cache.get(key);
|
|
334
|
+
this.logger.debug(`Cache GET result: ${value ? 'HIT' : 'MISS'}`);
|
|
335
|
+
return value;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async set(key: string, value: T, ttl?: number): Promise<void> {
|
|
339
|
+
this.logger.debug(`Cache SET: ${key} (TTL: ${ttl})`);
|
|
340
|
+
await this.cache.set(key, value, ttl);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async delete(key: string): Promise<void> {
|
|
344
|
+
this.logger.debug(`Cache DELETE: ${key}`);
|
|
345
|
+
await this.cache.delete(key);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const cache = new LoggingCache(
|
|
350
|
+
new UpstashCache<User>(url, token),
|
|
351
|
+
logger
|
|
352
|
+
);
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Testing
|
|
356
|
+
|
|
357
|
+
### Mocking
|
|
358
|
+
|
|
359
|
+
The interface makes testing easy:
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
import { Cache } from '@geekmidas/cache';
|
|
363
|
+
|
|
364
|
+
class MockCache<T> implements Cache<T> {
|
|
365
|
+
private store = new Map<string, T>();
|
|
366
|
+
|
|
367
|
+
async get(key: string): Promise<T | undefined> {
|
|
368
|
+
return this.store.get(key);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async set(key: string, value: T): Promise<void> {
|
|
372
|
+
this.store.set(key, value);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async delete(key: string): Promise<void> {
|
|
376
|
+
this.store.delete(key);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Use in tests
|
|
381
|
+
const mockCache = new MockCache<User>();
|
|
382
|
+
const service = new CachedUserService(mockCache, mockRepository);
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Testing with Real Implementations
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
import { InMemoryCache } from '@geekmidas/cache/memory';
|
|
389
|
+
|
|
390
|
+
describe('UserService', () => {
|
|
391
|
+
let cache: Cache<User>;
|
|
392
|
+
let service: CachedUserService;
|
|
393
|
+
|
|
394
|
+
beforeEach(() => {
|
|
395
|
+
cache = new InMemoryCache<User>();
|
|
396
|
+
service = new CachedUserService(cache, mockRepository);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('should cache user data', async () => {
|
|
400
|
+
const user = { id: 1, name: 'John' };
|
|
401
|
+
await service.setUser(user);
|
|
402
|
+
|
|
403
|
+
const cachedUser = await cache.get('user:1');
|
|
404
|
+
expect(cachedUser).toEqual(user);
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Performance Considerations
|
|
410
|
+
|
|
411
|
+
### InMemoryCache
|
|
412
|
+
- **Pros**: Very fast, no network latency
|
|
413
|
+
- **Cons**: Limited by available memory, not persistent
|
|
414
|
+
- **Best for**: Development, testing, single-instance apps
|
|
415
|
+
|
|
416
|
+
### UpstashCache
|
|
417
|
+
- **Pros**: Persistent, distributed, scalable
|
|
418
|
+
- **Cons**: Network latency, external dependency
|
|
419
|
+
- **Best for**: Production, multi-instance apps, shared cache
|
|
420
|
+
|
|
421
|
+
### Key Strategies
|
|
422
|
+
|
|
423
|
+
1. **Use appropriate TTL**: Balance between cache freshness and performance
|
|
424
|
+
2. **Consider cache size**: Monitor memory usage for in-memory cache
|
|
425
|
+
3. **Implement fallback**: Always have a fallback when cache is unavailable
|
|
426
|
+
4. **Use compression**: For large objects, consider compression before caching
|
|
427
|
+
|
|
428
|
+
## Error Handling
|
|
429
|
+
|
|
430
|
+
Cache operations can fail, so implement proper error handling:
|
|
431
|
+
|
|
432
|
+
```typescript
|
|
433
|
+
async function getUserSafely(id: number): Promise<User | undefined> {
|
|
434
|
+
try {
|
|
435
|
+
const cached = await cache.get(`user:${id}`);
|
|
436
|
+
if (cached) return cached;
|
|
437
|
+
} catch (error) {
|
|
438
|
+
console.warn('Cache get failed, falling back to database:', error);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
try {
|
|
442
|
+
const user = await database.getUser(id);
|
|
443
|
+
if (user) {
|
|
444
|
+
try {
|
|
445
|
+
await cache.set(`user:${id}`, user, 3600);
|
|
446
|
+
} catch (error) {
|
|
447
|
+
console.warn('Cache set failed:', error);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return user;
|
|
451
|
+
} catch (error) {
|
|
452
|
+
console.error('Database query failed:', error);
|
|
453
|
+
throw error;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Migration Guide
|
|
459
|
+
|
|
460
|
+
### From Map to Cache
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
// Old way
|
|
464
|
+
const cache = new Map<string, User>();
|
|
465
|
+
cache.set('user:1', user);
|
|
466
|
+
const user = cache.get('user:1');
|
|
467
|
+
|
|
468
|
+
// New way
|
|
469
|
+
const cache = new InMemoryCache<User>();
|
|
470
|
+
await cache.set('user:1', user);
|
|
471
|
+
const user = await cache.get('user:1');
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### From Other Cache Libraries
|
|
475
|
+
|
|
476
|
+
Most cache libraries can be adapted to implement the `Cache<T>` interface:
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
class RedisCache<T> implements Cache<T> {
|
|
480
|
+
constructor(private redis: RedisClient) {}
|
|
481
|
+
|
|
482
|
+
async get(key: string): Promise<T | undefined> {
|
|
483
|
+
const value = await this.redis.get(key);
|
|
484
|
+
return value ? JSON.parse(value) : undefined;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
async set(key: string, value: T, ttl?: number): Promise<void> {
|
|
488
|
+
const serialized = JSON.stringify(value);
|
|
489
|
+
if (ttl) {
|
|
490
|
+
await this.redis.setex(key, ttl, serialized);
|
|
491
|
+
} else {
|
|
492
|
+
await this.redis.set(key, serialized);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async delete(key: string): Promise<void> {
|
|
497
|
+
await this.redis.del(key);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
## Contributing
|
|
503
|
+
|
|
504
|
+
1. Follow the existing code style (2 spaces, single quotes, semicolons)
|
|
505
|
+
2. Add comprehensive tests for new features
|
|
506
|
+
3. Ensure all implementations follow the `Cache<T>` interface
|
|
507
|
+
4. Update documentation for API changes
|
|
508
|
+
5. Use the "Integration over Unit" testing philosophy
|
|
509
|
+
|
|
510
|
+
## License
|
|
511
|
+
|
|
512
|
+
MIT License - see the LICENSE file for details.
|
|
@@ -1,54 +1,55 @@
|
|
|
1
|
+
const require_chunk = require('../chunk-CUT6urMc.cjs');
|
|
1
2
|
const require_memory = require('../memory-n6Iosq6J.cjs');
|
|
2
3
|
const require_upstash = require('../upstash-0RolGx7m.cjs');
|
|
3
|
-
const
|
|
4
|
+
const vitest = require_chunk.__toESM(require("vitest"));
|
|
4
5
|
|
|
5
6
|
//#region src/__tests__/cache.spec.ts
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
(0, vitest.describe)("Cache Interface", () => {
|
|
8
|
+
(0, vitest.describe)("interface compliance", () => {
|
|
9
|
+
(0, vitest.it)("InMemoryCache should implement Cache interface", () => {
|
|
9
10
|
const cache = new require_memory.InMemoryCache();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
(0, vitest.expect)(typeof cache.get).toBe("function");
|
|
12
|
+
(0, vitest.expect)(typeof cache.set).toBe("function");
|
|
13
|
+
(0, vitest.expect)(typeof cache.delete).toBe("function");
|
|
13
14
|
});
|
|
14
|
-
|
|
15
|
+
(0, vitest.it)("UpstashCache should implement Cache interface", () => {
|
|
15
16
|
const cache = new require_upstash.UpstashCache("http://localhost:8079", "example_token");
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
(0, vitest.expect)(typeof cache.get).toBe("function");
|
|
18
|
+
(0, vitest.expect)(typeof cache.set).toBe("function");
|
|
19
|
+
(0, vitest.expect)(typeof cache.delete).toBe("function");
|
|
19
20
|
});
|
|
20
21
|
});
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
(0, vitest.describe)("method signatures", () => {
|
|
23
|
+
(0, vitest.it)("should have correct get method signature", () => {
|
|
23
24
|
const cache = new require_memory.InMemoryCache();
|
|
24
25
|
const getResult = cache.get("test");
|
|
25
|
-
|
|
26
|
+
(0, vitest.expect)(getResult).toBeInstanceOf(Promise);
|
|
26
27
|
});
|
|
27
|
-
|
|
28
|
+
(0, vitest.it)("should have correct set method signature", () => {
|
|
28
29
|
const cache = new require_memory.InMemoryCache();
|
|
29
30
|
const setResult = cache.set("test", "value");
|
|
30
|
-
|
|
31
|
+
(0, vitest.expect)(setResult).toBeInstanceOf(Promise);
|
|
31
32
|
});
|
|
32
|
-
|
|
33
|
+
(0, vitest.it)("should have correct delete method signature", () => {
|
|
33
34
|
const cache = new require_memory.InMemoryCache();
|
|
34
35
|
const deleteResult = cache.delete("test");
|
|
35
|
-
|
|
36
|
+
(0, vitest.expect)(deleteResult).toBeInstanceOf(Promise);
|
|
36
37
|
});
|
|
37
38
|
});
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
(0, vitest.describe)("generic type support", () => {
|
|
40
|
+
(0, vitest.it)("should support string types", async () => {
|
|
40
41
|
const cache = new require_memory.InMemoryCache();
|
|
41
42
|
await cache.set("key", "value");
|
|
42
43
|
const result = await cache.get("key");
|
|
43
|
-
|
|
44
|
+
(0, vitest.expect)(typeof result === "string" || result === void 0).toBe(true);
|
|
44
45
|
});
|
|
45
|
-
|
|
46
|
+
(0, vitest.it)("should support number types", async () => {
|
|
46
47
|
const cache = new require_memory.InMemoryCache();
|
|
47
48
|
await cache.set("key", 42);
|
|
48
49
|
const result = await cache.get("key");
|
|
49
|
-
|
|
50
|
+
(0, vitest.expect)(typeof result === "number" || result === void 0).toBe(true);
|
|
50
51
|
});
|
|
51
|
-
|
|
52
|
+
(0, vitest.it)("should support object types", async () => {
|
|
52
53
|
const cache = new require_memory.InMemoryCache();
|
|
53
54
|
const user = {
|
|
54
55
|
id: 1,
|
|
@@ -56,9 +57,9 @@ require_vi_DgezovHB.describe("Cache Interface", () => {
|
|
|
56
57
|
};
|
|
57
58
|
await cache.set("user", user);
|
|
58
59
|
const result = await cache.get("user");
|
|
59
|
-
|
|
60
|
+
(0, vitest.expect)(result === void 0 || typeof result === "object" && "id" in result && "name" in result).toBe(true);
|
|
60
61
|
});
|
|
61
|
-
|
|
62
|
+
(0, vitest.it)("should support union types", async () => {
|
|
62
63
|
const cache = new require_memory.InMemoryCache();
|
|
63
64
|
await cache.set("string", "text");
|
|
64
65
|
await cache.set("number", 42);
|
|
@@ -66,9 +67,9 @@ require_vi_DgezovHB.describe("Cache Interface", () => {
|
|
|
66
67
|
const stringResult = await cache.get("string");
|
|
67
68
|
const numberResult = await cache.get("number");
|
|
68
69
|
const booleanResult = await cache.get("boolean");
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
(0, vitest.expect)(stringResult).toBe("text");
|
|
71
|
+
(0, vitest.expect)(numberResult).toBe(42);
|
|
72
|
+
(0, vitest.expect)(booleanResult).toBe(true);
|
|
72
73
|
});
|
|
73
74
|
});
|
|
74
75
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|