A cache-manager compatible file-based cache using Links Notation (.lino) format instead of JSON.
- cache-manager compatible - Implements the full cache-manager store interface
- Links Notation storage - Uses lino-objects-codec for serialization
- Two storage modes:
- Folder mode - Each cache key stored in a separate
.linofile - Single-file mode - All cache entries in one
.linofile
- Folder mode - Each cache key stored in a separate
- TTL support - Time-to-live for automatic expiration
- Atomic cache primitives - Conditional writes, TTL mutation, counters, string mutation, and get-and-mutate operations
- Multi-runtime - Works with Node.js, Bun, and Deno
- TypeScript support - Full type definitions included
See the competitor compatibility matrix for an explicit comparison with Redis, Memcached, and Dragonfly and the staged implementation roadmap. The package currently provides semantic compatibility for common cache operations; it does not claim wire-protocol compatibility.
npm install lino-cacheimport { LinoCache, createLinoCache, linoStore } from 'lino-cache';
// Create a cache instance (folder mode by default)
const cache = new LinoCache({
basePath: '.cache',
ttl: 60000, // Default TTL: 1 minute
});
// Basic operations
await cache.set('user:1', { name: 'Alice', age: 30 });
const user = await cache.get('user:1');
console.log(user); // { name: 'Alice', age: 30 }
// Delete
await cache.del('user:1');
// Check existence
const exists = await cache.has('user:1'); // falseEach cache key is stored in a separate .lino file. Best for:
- Large number of cache entries
- Independent access to cache entries
- When you need to inspect individual cached values
const cache = new LinoCache({
mode: 'folder', // Default
basePath: '.cache',
});
await cache.set('key1', 'value1'); // Creates .cache/key1.lino
await cache.set('key2', 'value2'); // Creates .cache/key2.linoAll cache entries stored in one .lino file. Best for:
- Small number of cache entries
- When you want all cache data in one file
- Simpler file management
const cache = new LinoCache({
mode: 'file',
basePath: '.cache',
fileName: 'cache.lino',
});
await cache.set('key1', 'value1'); // Both stored in
await cache.set('key2', 'value2'); // .cache/cache.linointerface LinoCacheOptions {
ttl?: number; // Default TTL in milliseconds (0 = no expiration)
mode?: 'file' | 'folder'; // Storage mode (default: 'folder')
basePath?: string; // Cache directory (default: '.cache')
fileName?: string; // File name for single-file mode (default: 'cache.lino')
}Sets a value in the cache.
await cache.set('key', 'value');
await cache.set('key', 'value', 5000); // With 5 second TTLGets a value from the cache. Returns undefined if not found or expired.
const value = await cache.get('key');Deletes a value from the cache.
const deleted = await cache.del('key'); // true if deletedConditionally writes a value. add writes only when no live entry exists;
replace writes only when one does. Both return whether the write occurred.
await cache.add('job-lock', 'worker-1', 5000); // true
await cache.add('job-lock', 'worker-2', 5000); // false
await cache.replace('job-lock', 'worker-3', 5000); // trueUpdates expiration without replacing the value. touch returns whether the
key existed; getex returns the value while updating its expiration.
await cache.touch('session', 60000);
const session = await cache.getex('session', 120000);Returns and deletes a value as one local atomic operation.
const job = await cache.getdel('next-job');Descriptive aliases getAndDelete and getAndTouch are also available.
Atomically mutates finite numeric values. Missing counters start at zero,
results may be negative, and an existing TTL is preserved. The longer aliases
increment and decrement are also available.
await cache.incr('requests'); // 1
await cache.incr('requests', 4); // 5
await cache.decr('requests', 2); // 3Atomically mutates string values, creates a missing key from an empty string, and preserves an existing TTL.
await cache.append('message', 'world');
await cache.prepend('message', 'hello '); // 'hello world'Atomic mutation methods are serialized across cache instances in the same JavaScript process. Concurrent-process locking and network protocol compatibility are separate roadmap items.
Checks if a key exists and is not expired.
const exists = await cache.has('key'); // booleanReturns all non-expired keys.
const allKeys = await cache.keys(); // ['key1', 'key2', ...]Clears all values from the cache.
await cache.clear();Sets multiple values at once.
await cache.mset([
{ key: 'key1', value: 'value1' },
{ key: 'key2', value: 'value2', ttl: 5000 },
]);Gets multiple values at once.
const values = await cache.mget(['key1', 'key2', 'key3']);
// [value1, value2, undefined]Deletes multiple values at once.
const deleted = await cache.mdel(['key1', 'key2']);Wraps a function with caching. Returns cached value if available, otherwise executes the function and caches the result.
const data = await cache.wrap(
'expensive-operation',
async () => {
// This only runs if not cached
return await fetchExpensiveData();
},
60000
);Gets the remaining TTL for a key in milliseconds.
const remaining = await cache.ttl('key');
// > 0: remaining time
// -1: no TTL set
// -2: key not foundCloses the cache and releases resources.
await cache.disconnect();Creates a new LinoCache instance.
const cache = createLinoCache({ basePath: '.cache' });Creates a cache-manager compatible store.
import { caching } from 'cache-manager';
import { linoStore } from 'lino-cache';
const cache = await caching(
linoStore({
basePath: '.cache',
ttl: 60000,
})
);Links Notation (.lino) is a human-readable serialization format that:
- Supports circular references natively
- Preserves object identity
- Is more compact for certain data structures
- Provides better debugging experience
Learn more: Links Notation
# Install dependencies
npm install
# Run tests
npm test
# Lint code
npm run lint
# Format code
npm run format
# Run all checks
npm run checkUnlicense - Public Domain