cachemind
v0.1.1
Published
A lightweight, universal JavaScript cache library with multiple caching strategies
Maintainers
Readme
CacheMind
Uma biblioteca de cache JavaScript leve, universal e modular com múltiplas estratégias de cache.
🎯 Características
- ⚡ Performance O(1): Operações de leitura e escrita com complexidade de tempo constante
- 🌐 Universal (Isomórfico): Funciona tanto em navegadores modernos quanto no Node.js
- 📦 Bundle Leve: Sem dependências externas, otimizado para tree-shaking
- 🧪 Bem Testado: Alta cobertura de testes unitários
- 📚 Bem Documentado: JSDoc completo e exemplos claros
- 🔧 Modular: Arquitetura extensível para novas estratégias de cache
📦 Instalação
npm install cachemindyarn add cachemindpnpm add cachemind🚀 Uso Básico
Importação
import { BaseCache, TTLCache } from 'cachemind';Exemplo Simples com BaseCache
import { BaseCache } from 'cachemind';
// Criar uma instância de cache
const cache = new BaseCache<string>();
// Armazenar valores
cache.set('user:1', 'John Doe');
cache.set('user:2', 'Jane Smith');
// Recuperar valores
const user1 = cache.get('user:1'); // 'John Doe'
const user2 = cache.get('user:2'); // 'Jane Smith'
// Verificar existência
if (cache.has('user:1')) {
console.log('Usuário existe no cache');
}
// Remover item
cache.delete('user:1');
// Limpar todo o cache
cache.clear();Exemplo com TTLCache (Estratégia TTL)
import { TTLCache } from 'cachemind';
// Criar cache com TTL padrão de 1 hora
const cache = new TTLCache<string>({ defaultTTL: 3600000 });
// Armazenar com TTL padrão
cache.set('user:1', 'John Doe');
// Armazenar com TTL customizado (5 minutos)
cache.set('session:123', 'active', { ttl: 5 * 60 * 1000 });
// Verificar se item está expirado (stale)
if (cache.isStale('user:1')) {
// Item existe mas está expirado - pode fazer refresh
cache.set('user:1', await fetchFreshData(), { ttl: 3600000 });
}
// Verificar tempo restante até expiração
const remaining = cache.getRemainingTTL('session:123');
if (remaining !== undefined) {
console.log(`Sessão expira em ${remaining}ms`);
}Exemplo com SWRCache (Estratégia SWR)
import { SWRCache } from 'cachemind';
// Criar cache SWR com TTL padrão de 1 hora
const cache = new SWRCache<string>({ defaultTTL: 3600000 });
// Configurar item com função de fetch para revalidação automática
await cache.setWithFetch('user:1', 'John Doe', {
ttl: 3600000,
fetch: async () => {
const response = await fetch('/api/user/1');
const data = await response.json();
return data.name;
}
});
// Primeira vez: retorna valor imediatamente
const user = cache.get('user:1'); // 'John Doe'
// Após expirar: retorna valor stale imediatamente e revalida em background
// (o valor stale é retornado sem esperar pela revalidação)
const staleUser = cache.get('user:1'); // 'John Doe' (stale, mas retornado)
// Em background: fetch é chamado e cache é atualizado silenciosamenteExemplo com LRUCache (Estratégia LRU)
import { LRUCache } from 'cachemind';
// Criar cache LRU com capacidade máxima de 3 itens
const cache = new LRUCache<string>({ maxSize: 3 });
// Preenche cache até capacidade máxima
cache.set('key1', 'value1');
cache.set('key2', 'value2');
cache.set('key3', 'value3');
// Acessa key1 (torna-se mais recentemente usado)
cache.get('key1');
// Adiciona novo item - key2 é removido (menos recentemente usado)
cache.set('key4', 'value4');
// key2 não está mais no cacheExemplo com DTTCache (Estratégia Dynamic TTL)
import { DTTCache } from 'cachemind';
const cache = new DTTCache<string>();
// TTL baseado em headers HTTP
await cache.setWithDynamicTTL('api:data', 'response data', {
ttlCalculator: (value, metadata) => {
const cacheControl = metadata?.headers?.['cache-control'];
if (cacheControl) {
const maxAgeMatch = cacheControl.match(/max-age=(\d+)/);
if (maxAgeMatch) {
return parseInt(maxAgeMatch[1], 10) * 1000; // Converte para ms
}
}
return 3600000; // Fallback: 1 hora
},
metadata: {
headers: {
'cache-control': 'max-age=300', // 5 minutos
},
},
});
// TTL baseado em propriedade do dado
interface Product {
isPremium: boolean;
category: string;
}
const productCache = new DTTCache<Product>();
await productCache.setWithDynamicTTL('product:1', product, {
ttlCalculator: (value) => {
// Produtos premium têm TTL maior
return value.isPremium ? 7200000 : 3600000;
},
});📖 API Reference
BaseCache<T>
Classe base para cache em memória. Permite TTL opcional.
TTLCache<T>
Estratégia de cache baseada em TTL (Time-To-Live). Garante que todos os itens tenham TTL definido.
Construtor
new TTLCache<T>(options: { defaultTTL: number })Cria uma nova instância de cache com TTL obrigatório.
Parâmetros:
options.defaultTTL: TTL padrão em milissegundos (obrigatório)
Exemplo:
// Cache com TTL padrão de 1 hora
const cache = new TTLCache<string>({ defaultTTL: 3600000 });
// Cache com TTL padrão de 5 minutos
const sessionCache = new TTLCache<Session>({ defaultTTL: 5 * 60 * 1000 });Métodos Específicos
isStale(key: string): boolean
Verifica se um item está expirado (stale) sem removê-lo.
Complexidade: O(1)
Parâmetros:
key: Chave do item a ser verificado
Retorna: true se o item existe mas está expirado, false caso contrário
Exemplo:
if (cache.isStale('key')) {
// Item expirado - fazer refresh
cache.set('key', await fetchFreshData(), { ttl: 3600000 });
}getRemainingTTL(key: string): number | undefined
Retorna o tempo restante até a expiração de um item.
Complexidade: O(1)
Parâmetros:
key: Chave do item a ser verificado
Retorna: Tempo restante em milissegundos, ou undefined se o item não existe
Exemplo:
const remaining = cache.getRemainingTTL('session:123');
if (remaining !== undefined && remaining < 60000) {
// Menos de 1 minuto restante - avisar usuário
console.log('Sessão expira em breve');
}Métodos Herdados
TTLCache implementa a interface ICache<T>, então todos os métodos de BaseCache estão disponíveis:
get(key),getAsync(key),set(key, value, options),has(key),delete(key),clear(),size
Nota: Em TTLCache, o método set() sempre aplica TTL. Se options.ttl não for fornecido, usa o defaultTTL.
SWRCache<T>
Estratégia de cache baseada em Stale-While-Revalidate (SWR). Retorna dados stale imediatamente enquanto revalida em background.
Construtor
new SWRCache<T>(options: { defaultTTL: number })Cria uma nova instância de cache SWR com TTL obrigatório.
Parâmetros:
options.defaultTTL: TTL padrão em milissegundos (obrigatório)
Exemplo:
// Cache SWR com TTL padrão de 1 hora
const cache = new SWRCache<string>({ defaultTTL: 3600000 });Métodos Específicos
setWithFetch(key: string, initialValue: T | Promise<T>, options: SWROptions<T>): Promise<void>
Configura um item no cache com função de fetch para revalidação automática.
Parâmetros:
key: Chave do iteminitialValue: Valor inicial (ou Promise que resolve para o valor)options: Opções incluindottlefetch
Exemplo:
await cache.setWithFetch('user:1', 'John Doe', {
ttl: 3600000,
fetch: async () => {
const response = await fetch('/api/user/1');
return response.json().name;
}
});isStale(key: string): boolean
Verifica se um item está expirado (stale).
Complexidade: O(1)
Parâmetros:
key: Chave do item a ser verificado
Retorna: true se o item está stale, false caso contrário
Métodos Herdados
SWRCache implementa a interface ICache<T>, então todos os métodos de BaseCache estão disponíveis:
get(key),getAsync(key),set(key, value, options),has(key),delete(key),clear(),size
Nota: Quando um item está stale, get() retorna o valor stale imediatamente e inicia revalidação em background (se houver função de fetch configurada).
LRUCache<T>
Estratégia de cache baseada em LRU (Least Recently Used). Remove o item menos recentemente usado quando a capacidade máxima é atingida.
Construtor
new LRUCache<T>(options: { maxSize: number })Cria uma nova instância de cache LRU com capacidade máxima.
Parâmetros:
options.maxSize: Capacidade máxima do cache (obrigatório, mínimo 1)
Exemplo:
// Cache LRU com capacidade máxima de 100 itens
const cache = new LRUCache<string>({ maxSize: 100 });Métodos Específicos
maxSizeProperty: number (readonly)
Retorna a capacidade máxima do cache.
Complexidade: O(1)
Métodos Herdados
LRUCache implementa a interface ICache<T>, então todos os métodos de BaseCache estão disponíveis:
get(key),getAsync(key),set(key, value, options),has(key),delete(key),clear(),size
Nota:
- Ao acessar um item (
get()ouset()), ele torna-se mais recentemente usado - Quando a capacidade máxima é atingida e um novo item é inserido, o item menos recentemente usado é removido automaticamente
- Ordem de uso é atualizada a cada acesso (leitura ou escrita)
DTTCache<T>
Estratégia de cache baseada em Dynamic TTL (TTL Dinâmico). Calcula TTL dinamicamente com base em metadados da resposta (headers HTTP, propriedades do dado, etc.).
Construtor
new DTTCache<T>()Cria uma nova instância de cache DTTC.
Exemplo:
const cache = new DTTCache<string>();Métodos Específicos
setWithDynamicTTL(key: string, value: T | Promise<T>, options: DTTCOptions<T>): Promise<void>
Armazena um valor no cache com TTL calculado dinamicamente.
Parâmetros:
key: Chave do itemvalue: Valor a ser armazenado (ou Promise que resolve para o valor)options: Opções incluindo função de cálculo de TTL e metadados
Exemplo:
// TTL baseado em headers HTTP
await cache.setWithDynamicTTL('api:data', responseData, {
ttlCalculator: (value, metadata) => {
const maxAge = metadata?.headers?.['cache-control'];
return parseMaxAge(maxAge) * 1000;
},
metadata: { headers: response.headers },
});
// TTL baseado em propriedade do dado
await cache.setWithDynamicTTL('product:1', product, {
ttlCalculator: (value) => {
return value.isPremium ? 7200000 : 3600000;
},
});Nota:
- Se a função
ttlCalculatorretornarundefinedou lançar erro, ottlestático será usado como fallback (se fornecido) - A função recebe o valor resolvido e metadados opcionais (headers HTTP, etc.)
- Suporta Promises - resolve automaticamente antes de calcular TTL
Métodos Herdados
DTTCache implementa a interface ICache<T>, então todos os métodos de BaseCache estão disponíveis:
get(key),getAsync(key),set(key, value, options),has(key),delete(key),clear(),size
Nota: Para usar TTL dinâmico, use setWithDynamicTTL(). O método set() padrão funciona normalmente com TTL estático.
BaseCache<T>
Classe base para cache em memória. Permite TTL opcional.
Construtor
new BaseCache<T>()Cria uma nova instância de cache.
Exemplo:
const cache = new BaseCache<string>();
const numberCache = new BaseCache<number>();Métodos
get(key: string): T | undefined
Recupera um valor do cache pela chave (síncrono).
Complexidade: O(1)
Parâmetros:
key: Chave do item a ser recuperado
Retorna: O valor associado à chave (síncrono), ou undefined se não existir, tiver expirado, ou for uma Promise ainda pendente
Nota: Para valores assíncronos (Promises), use getAsync.
Exemplo:
const value = cache.get('myKey');
if (value !== undefined) {
console.log(value);
}getAsync(key: string): Promise<T | undefined>
Recupera um valor do cache pela chave, aguardando resolução de Promises se necessário.
Complexidade: O(1) para acesso, mas pode aguardar resolução de Promise
Este método é inteligente o suficiente para:
- Retornar valores síncronos imediatamente
- Aguardar a resolução de Promises se os dados estiverem sendo buscados pela primeira vez
- Retornar valores já resolvidos de Promises anteriores
Parâmetros:
key: Chave do item a ser recuperado
Retorna: Promise que resolve para o valor, ou undefined se não existir ou tiver expirado
Exemplo:
// Valor síncrono
const syncValue = await cache.getAsync('syncKey'); // Retorna imediatamente
// Valor assíncrono
const asyncValue = await cache.getAsync('asyncKey'); // Aguarda resoluçãoset(key: string, value: T | Promise<T>, options?: CacheOptions): boolean
Armazena um valor no cache. Suporta valores síncronos e assíncronos (Promises).
Complexidade: O(1)
Parâmetros:
key: Chave do item a ser armazenadovalue: Valor a ser armazenado (síncrono ou Promise)options(opcional): Opções adicionaisttl: Tempo de vida em milissegundos
Retorna: true se o valor foi armazenado com sucesso
Exemplo:
// Armazenar valor síncrono
cache.set('key', 'value');
// Armazenar Promise
cache.set('key', fetch('/api/data').then(r => r.json()));
// Armazenar com TTL de 1 hora
cache.set('key', 'value', { ttl: 3600000 });has(key: string): boolean
Verifica se uma chave existe no cache e não expirou.
Complexidade: O(1)
Parâmetros:
key: Chave a ser verificada
Retorna: true se a chave existe e não expirou, false caso contrário
Exemplo:
if (cache.has('myKey')) {
const value = cache.get('myKey');
}delete(key: string): boolean
Remove um item do cache pela chave.
Complexidade: O(1)
Parâmetros:
key: Chave do item a ser removido
Retorna: true se o item foi removido, false se não existia
Exemplo:
const removed = cache.delete('myKey');
if (removed) {
console.log('Item removido com sucesso');
}clear(): void
Remove todos os itens do cache.
Complexidade: O(n) onde n é o número de itens
Exemplo:
cache.clear();
console.log(cache.size); // 0Propriedades
size: number (readonly)
Retorna o número de itens ativos no cache (não expirados).
Exemplo:
cache.set('key1', 'value1');
cache.set('key2', 'value2');
console.log(cache.size); // 2💡 Exemplos de Uso
Cache com TTL (Time To Live)
import { BaseCache } from 'cachemind';
const cache = new BaseCache<string>();
// Armazenar com expiração de 1 hora (3600000ms)
cache.set('session:user123', 'active', { ttl: 3600000 });
// Armazenar com expiração de 5 minutos
cache.set('temp:data', 'temporary', { ttl: 5 * 60 * 1000 });
// O item será automaticamente removido após expirar
setTimeout(() => {
const value = cache.get('temp:data'); // undefined (expirado)
}, 6 * 60 * 1000);Cache de Objetos Complexos
import { BaseCache } from 'cachemind';
interface User {
id: number;
name: string;
email: string;
}
const userCache = new BaseCache<User>();
userCache.set('user:1', {
id: 1,
name: 'John Doe',
email: '[email protected]'
});
const user = userCache.get('user:1');
console.log(user?.name); // 'John Doe'Cache de Resultados de Funções (Memoization)
import { BaseCache } from 'cachemind';
const resultCache = new BaseCache<number>();
function expensiveCalculation(n: number): number {
// Verifica se o resultado já está em cache
const cached = resultCache.get(`calc:${n}`);
if (cached !== undefined) {
return cached;
}
// Realiza cálculo caro
const result = n * n * n; // Exemplo simplificado
// Armazena no cache por 1 hora
resultCache.set(`calc:${n}`, result, { ttl: 3600000 });
return result;
}
// Primeira chamada: calcula e armazena
const result1 = expensiveCalculation(10); // Calcula
// Segunda chamada: retorna do cache
const result2 = expensiveCalculation(10); // Cache hit!Cache de Requisições HTTP com Promises
import { BaseCache } from 'cachemind';
const apiCache = new BaseCache<{ id: string; name: string }>();
async function fetchUserData(userId: string) {
const cacheKey = `api:user:${userId}`;
// Verifica cache síncrono (valores já resolvidos)
const cached = apiCache.get(cacheKey);
if (cached) {
return cached;
}
// Faz requisição e armazena a Promise no cache
const promise = fetch(`/api/users/${userId}`)
.then(r => r.json());
// Armazena a Promise no cache por 5 minutos
apiCache.set(cacheKey, promise, { ttl: 5 * 60 * 1000 });
// Aguarda resolução (primeira vez) ou retorna valor cacheado
return await apiCache.getAsync(cacheKey);
}Cache de Funções Assíncronas (Memoization)
import { BaseCache } from 'cachemind';
const resultCache = new BaseCache<number>();
async function expensiveAsyncCalculation(n: number): Promise<number> {
const cacheKey = `calc:${n}`;
// Verifica se já está em cache
const cached = await resultCache.getAsync(cacheKey);
if (cached !== undefined) {
return cached;
}
// Realiza cálculo assíncrono caro
const promise = new Promise<number>(resolve => {
setTimeout(() => {
resolve(n * n * n);
}, 1000);
});
// Armazena a Promise no cache
resultCache.set(cacheKey, promise, { ttl: 3600000 });
// Aguarda e retorna o resultado
return await resultCache.getAsync(cacheKey);
}
// Primeira chamada: calcula e cacheia
const result1 = await expensiveAsyncCalculation(10); // Aguarda 1s
// Segunda chamada: retorna do cache imediatamente
const result2 = await expensiveAsyncCalculation(10); // Retorna instantaneamente🏗️ Arquitetura
A biblioteca foi projetada com modularidade em mente (RNF06). A interface ICache define o contrato base que todas as estratégias devem seguir:
interface ICache<T> {
get(key: string): T | undefined;
set(key: string, value: T, options?: CacheOptions): boolean;
has(key: string): boolean;
delete(key: string): boolean;
clear(): void;
readonly size: number;
}Isso permite que futuras estratégias (LRU, FIFO, LFU, etc.) sejam facilmente adicionadas mantendo a mesma API.
⚡ Performance
A implementação utiliza Map nativo do JavaScript, garantindo:
- O(1) para operações
get,set,hasedelete - O(n) para
clearesize(onde n é o número de itens)
Benchmarks
Em testes com 10.000 itens:
set: ~0.001ms por operaçãoget: ~0.001ms por operaçãohas: ~0.001ms por operaçãodelete: ~0.001ms por operação
🧪 Testes
Execute os testes:
npm testCom cobertura:
npm run test:coverageInterface de testes (UI):
npm run test:ui📝 Requisitos Não-Funcionais
- ✅ RNF01: Performance O(1) para get/set em memória
- ✅ RNF02: Universal (browser + Node.js)
- ✅ RNF03: Bundle size reduzido (sem dependências)
- ✅ RNF04: Alta cobertura de testes
- ✅ RNF05: Documentação completa (JSDoc + README)
- ✅ RNF06: Arquitetura modular e extensível
🎯 Estratégias de Cache
TTLCache (RF03) ✅
Estratégia baseada em TTL (Time-To-Live) que garante que todos os itens tenham tempo de expiração definido.
Características:
- TTL obrigatório para todos os itens
- Métodos
isStale()egetRemainingTTL()para gerenciamento avançado - Ideal para caches que precisam de expiração automática
Exemplo:
const cache = new TTLCache<string>({ defaultTTL: 3600000 });
// Todos os itens terão TTL
cache.set('key1', 'value1'); // Usa defaultTTL
cache.set('key2', 'value2', { ttl: 5000 }); // TTL customizado
// Verificar expiração
if (cache.isStale('key1')) {
// Fazer refresh
}SWRCache (RF04) ✅
Estratégia Stale-While-Revalidate (SWR) que retorna dados stale imediatamente enquanto revalida em background.
Características:
- Retorna dados stale imediatamente quando item expirou
- Revalidação automática em background quando há função de fetch
- Atualização silenciosa do cache quando novos dados chegam
- Deduplicação de revalidações simultâneas
- Ideal para caches de API com revalidação automática
Exemplo:
import { SWRCache } from 'cachemind';
const cache = new SWRCache<string>({ defaultTTL: 3600000 });
// Configurar item com função de fetch
await cache.setWithFetch('user:1', 'initial value', {
ttl: 3600000,
fetch: async () => {
const response = await fetch('/api/user/1');
return response.json().name;
}
});
// Primeira vez: retorna valor imediatamente
const user = cache.get('user:1'); // 'initial value'
// Após expirar: retorna valor stale e revalida em background
// (o valor stale é retornado imediatamente, enquanto a revalidação acontece)
const staleUser = cache.get('user:1'); // 'initial value' (stale, mas retornado)
// Em background: fetch é chamado e cache é atualizado silenciosamenteLRUCache (RF05) ✅
Estratégia Least Recently Used (LRU) que remove o item menos recentemente usado quando a capacidade máxima é atingida.
Características:
- Capacidade máxima configurável
- Remove automaticamente o item menos recentemente usado quando atinge capacidade
- Ordem de uso atualizada a cada acesso (leitura ou escrita)
- Ideal para caches com limite de memória
Exemplo:
const cache = new LRUCache<string>({ maxSize: 3 });
// Preenche cache até capacidade máxima
cache.set('key1', 'value1');
cache.set('key2', 'value2');
cache.set('key3', 'value3');
// Acessa key1 (torna-se mais recentemente usado)
cache.get('key1');
// Adiciona novo item - key2 é removido (menos recentemente usado)
cache.set('key4', 'value4');
// key2 não está mais no cacheDTTCache (RF06) ✅
Estratégia Dynamic TTL (TTL Dinâmico) que calcula TTL dinamicamente com base em metadados da resposta.
Características:
- TTL calculado dinamicamente com base em metadados (headers HTTP, propriedades do dado)
- Suporte para headers HTTP (Cache-Control, Expires)
- Suporte para cálculo baseado em propriedades do dado
- Fallback para TTL estático se função não calcular TTL
- Ideal para caches de API que respeitam headers HTTP
Exemplo:
const cache = new DTTCache<string>();
// TTL baseado em Cache-Control header
await cache.setWithDynamicTTL('api:data', 'response data', {
ttlCalculator: (value, metadata) => {
const cacheControl = metadata?.headers?.['cache-control'];
if (cacheControl) {
const maxAgeMatch = cacheControl.match(/max-age=(\d+)/);
if (maxAgeMatch) {
return parseInt(maxAgeMatch[1], 10) * 1000;
}
}
return 3600000; // Fallback
},
metadata: {
headers: { 'cache-control': 'max-age=300' },
},
});🔄 Roadmap
- [x] Estratégia TTL (Time-To-Live) - ✅ Implementado
- [x] Estratégia SWR (Stale-While-Revalidate) - ✅ Implementado
- [x] Estratégia LRU (Least Recently Used) - ✅ Implementado
- [x] Estratégia DTTC (Dynamic TTL) - ✅ Implementado
- [ ] Estratégia FIFO (First In First Out)
- [ ] Estratégia LFU (Least Frequently Used)
- [ ] Cache em disco (persistência)
- [ ] Cache distribuído (Redis adapter)
- [ ] Métricas e estatísticas de cache
📄 Licença
MIT
🤝 Contribuindo
Contribuições são bem-vindas! Por favor, abra uma issue ou pull request.
Desenvolvido com foco em performance, modularidade e simplicidade.
