Licence
GPL-2.0-only
Version
1.0.1
Deps
2
Size
266 kB
Vulns
0
Weekly
285
@amiki/cache
Pluggable cache strategies for amiki-framework — memory, SQLite, Redis, Hybrid.
Unified Cache<T> interface with multiple backends, TTL support, bulk operations, scan/pagination, and transport-based invalidation.
Installation
bun add @amiki/cacheQuick Start
import { createCache } from '@amiki/cache';
// In-memory LRU with TTL
const cache = createCache({ strategy: 'memory', maxSize: 10_000, ttl: 60_000 });
await cache.set('myKey', { hello: 'world' });
const val = await cache.get('myKey'); // { hello: 'world' }
await cache.delete('myKey');Backends
| Strategy | Class | Description | Persistence |
|---|---|---|---|
memory |
MemoryCache |
LRU + TTL, zero-dependency | Volatile |
sqlite |
SqliteCache |
Bun.sqlite WAL mode, persistent | File-based |
redis |
RedisCache |
Bun.RedisClient, distributed | Remote |
hybrid |
HybridCache |
L1 memory + L2 Redis, read-through/write-through | Remote |
Features
- TTL: per-key or default, automatic sweep
- Bulk ops:
getMany,setMany,deleteMany - Scan: key prefix filter, cursor-based pagination
- Stats: hit/miss counters, size, evictions, memory usage
- Hybrid mode: L1 MemoryCache (fast) + L2 RedisCache (shared), read-through/write-through
- Invalidation:
CacheInvalidatorbroadcasts invalidation events over transport - 152 tests, 263
expect()across all backends
Usage
Factory
const memory = createCache({ strategy: 'memory', maxSize: 5_000, ttl: 30_000 });
const sqlite = createCache({ strategy: 'sqlite', path: './cache.db', ttl: 60_000 });
const redis = createCache({ strategy: 'redis', redisUrl: 'redis://localhost:6379' });
// Hybrid: memory (L1) + Redis (L2)
const hybrid = createCache({
strategy: 'hybrid',
redisUrl: 'redis://localhost:6379',
maxSize: 10_000,
ttl: 300_000,
});Note:
redisandhybridstrategies requireawait cache.connect()before use.
Direct Construction
import { MemoryCache, SqliteCache, RedisCache, HybridCache, CacheInvalidator } from '@amiki/cache';
const cache = new MemoryCache<string>({ maxSize: 1000, defaultTtl: 60_000 });
await cache.set('key', 'value');Cache Invalidation
import { createTransport } from '@amiki/transport';
import { CacheInvalidator } from '@amiki/cache';
const transport = createTransport({ type: 'redis', redisUrl: 'redis://localhost:6379' });
await transport.connect();
const invalidator = new CacheInvalidator({ transport, namespace: 'my-cache' });
await invalidator.connect();
// Broadcast invalidation to all processes
await invalidator.invalidate(['key1', 'key2'], 'user-update');
// Listen for invalidation events
invalidator.onInvalidation((event) => {
console.log('Invalidation received:', event.keys);
});API
Cache<T> Interface
| Method | Description |
|---|---|
get(key) |
Get value by key |
set(key, value, ttl?) |
Set value with optional TTL |
delete(key) |
Delete key |
has(key) |
Check key existence |
getMany(keys) |
Bulk get |
setMany(entries, ttl?) |
Bulk set |
deleteMany(keys) |
Bulk delete |
scan(options?) |
Scan keys with prefix filter |
clear() |
Clear all entries |
stats() |
Cache statistics |
name |
Cache namespace |
CacheStats
{ size, capacity, gets, hits, misses, sets, deletes, evictions, memoryUsageBytes }Test Coverage
| Backend | Tests | Key Coverage |
|---|---|---|
| MemoryCache | 40 | LRU eviction, TTL sweep, scan pagination, concurrent access |
| SqliteCache | 33 | WAL persistence, TTL, multi-cache isolation, scan |
| RedisCache | 39 | 11 structure + 28 integration (graceful skip if no Redis) |
| HybridCache | 23 | L1 hit, L1→L2 read-through, write-through, invalidateLocal |
| CacheInvalidator | 17 | Mock transport, publish/subscribe roundtrip, malformed payloads |
License
GPL-2.0-only — see LICENSE for details.