npm package: @jamx-framework/cache
Description: JAMX Framework â€” Cache with memory and Redis drivers
Author: Stiven-21
Latest version: 1.0.0

# @jamx-framework/cache

## Descripción
Módulo de caching para JAMX Framework. Proporciona una API unificada para almacenamiento en caché con múltiples backends (memoria, Redis, etc.), soportando TTL, invalidación por clave, y estrategias de caché como write-through y write-behind. Ideal para acelerar respuestas de APIs, bases de datos y cálculos costosos.

## Cómo funciona
El módulo implementa un patrón de repositorio de caché con drivers intercambiables:

1. **Cache**: Clase principal que maneja operaciones de get/set/delete con TTL
2. **Drivers**: Implementaciones concretas (MemoryDriver, RedisDriver) que definen la persistencia
3. **Estrategias**: Soporte para write-through (actualizar caché y origen simultáneamente) y write-behind (actualizar caché primero, origen después)
4. **Invalidación**: Permite invalidar por clave, por patrón (prefix) o por completo

## Componentes principales
- **src/cache.ts**: Clase `Cache` que gestiona operaciones de caché
- **src/drivers/memory.ts**: Driver que almacena en memoria (Map)
- **src/drivers/redis.ts**: Driver que almacena en Redis
- **src/drivers/types.ts**: Tipos compartidos (`CacheDriver`, `CacheEntry`, etc.)
- **src/index.ts**: Punto de exportación

## Uso básico
```typescript
import { Cache, MemoryDriver } from '@jamx-framework/cache';

// Crear caché en memoria
const cache = new Cache(new MemoryDriver());

// Guardar con TTL de 5 minutos
await cache.set('users:123', { name: 'Juan', email: 'juan@example.com' }, 300);

// Obtener (con deserialización automática)
const user = await cache.get('users:123');
console.log('Usuario desde caché:', user);

// Verificar existencia
const exists = await cache.has('users:123');
console.log('¿Existe?', exists);

// Invalidar
await cache.delete('users:123');
```

### Con Redis
```typescript
import { Cache, RedisDriver } from '@jamx-framework/cache';

const redisDriver = new RedisDriver({
  host: 'localhost',
  port: 6379,
  password: 'secret',
});

const cache = new Cache(redisDriver);
await cache.set('session:abc123', { userId: 1 }, 3600);
```

## Ejemplos
### Caché de consultas de base de datos
```typescript
import { Cache, MemoryDriver } from '@jamx-framework/cache';

const cache = new Cache(new MemoryDriver());

async function findUser(id: string) {
  const key = `user:${id}`;
  
  // Intentar obtener desde caché
  const cached = await cache.get(key);
  if (cached) {
    console.log('Hit caché');
    return cached;
  }
  
  // Consultar base de datos
  const user = await db.users.findById(id);
  if (user) {
    // Guardar en caché por 10 minutos
    await cache.set(key, user, 600);
  }
  
  return user;
}
```

### Caché con invalidación por patrón
```typescript
import { Cache, MemoryDriver } from '@jamx-framework/cache';

const cache = new Cache(new MemoryDriver());

// Guardar múltiples claves con prefijo común
await cache.set('products:1', product1, 300);
await cache.set('products:2', product2, 300);
await cache.set('products:3', product3, 300);

// Invalidar todas las claves que empiezan con 'products:'
await cache.invalidatePattern('products:*');

// Invalidar todo el caché
await cache.clear();
```

### Write-through pattern
```typescript
import { Cache, MemoryDriver } from '@jamx-framework/cache';

const cache = new Cache(new MemoryDriver());

async function updateUser(id: string, data: Partial<User>) {
  // 1. Actualizar caché primero
  const existing = await cache.get(`user:${id}`) ?? null;
  const updated = { ...existing, ...data };
  await cache.set(`user:${id}`, updated, 600);
  
  // 2. Actualizar base de datos
  await db.users.update(id, data);
  
  return updated;
}
```

### Caché de resultados de API
```typescript
import { Cache, RedisDriver } from '@jamx-framework/cache';

const cache = new Cache(new RedisDriver({ url: 'redis://localhost:6379' }));

async function fetchExternalApi(endpoint: string, params: Record<string, string>) {
  const query = new URLSearchParams(params).toString();
  const key = `api:${endpoint}:${query}`;
  
  // Intentar obtener desde caché
  const cached = await cache.get(key);
  if (cached) {
    return cached as ApiResponse;
  }
  
  // Hacer request HTTP
  const response = await fetch(`${endpoint}?${query}`);
  const data = await response.json();
  
  // Guardar en caché por 1 minuto
  await cache.set(key, data, 60);
  
  return data;
}
```

### Uso con inyección de dependencias
```typescript
import { Container } from '@jamx-framework/core';
import { Cache, MemoryDriver } from '@jamx-framework/cache';

// Registrar en contenedor
Container.registerSingleton('cache', () => new Cache(new MemoryDriver()));

// Resolver y usar
const cache = Container.resolve<Cache>('cache');
await cache.set('config', { theme: 'dark' }, 3600);
```

## Flujo interno
1. **Inicialización**: Se crea un `Cache` con un driver específico (Memory, Redis, etc.)
2. **Operaciones**: `get()`, `set()`, `has()`, `delete()` se delegan al driver
3. **TTL management**: Cada entrada almacena `expiresAt`; `get()` elimina entradas expiradas
4. **Serialización**: Los valores se serializan automáticamente (JSON.stringify/parse)
5. **Invalidación por patrón**: `invalidatePattern()` usa `keys()` + `delete()` para eliminar coincidencias
6. **Estadísticas**: El driver puede reportar hits/misses (opcional)

## API Reference (Resumen)
### Cache
- `constructor(driver: CacheDriver)`
- `async get<T>(key: string): Promise<T | null>`
- `async set<T>(key: string, value: T, ttl?: number): Promise<void>`
- `async has(key: string): Promise<boolean>`
- `async delete(key: string): Promise<void>`
- `async invalidatePattern(pattern: string): Promise<number>` (número de claves eliminadas)
- `async clear(): Promise<void>`
- `async keys(): Promise<string[]>`

### CacheDriver (interface)
- `async get<T>(key: string): Promise<CacheEntry<T> | null>`
- `async set<T>(key: string, value: T, ttl: number): Promise<void>`
- `async delete(key: string): Promise<void>`
- `async clear(): Promise<void>`
- `async keys(): Promise<string[]>`

### CacheEntry<T>
- `value: T`
- `expiresAt: number` (timestamp en ms)
- `createdAt: number`

## Performance Considerations
- **MemoryDriver**: Máxima velocidad, datos volátiles, ideal para development y small-scale
- **RedisDriver**: Persistencia, clustering, TTL nativo, ideal para production y distributed systems
- **TTL granular**: Cada clave puede tener su propio TTL
- **Lazy expiration**: Las entradas se eliminan al acceder (no hay background cleanup)
- **Batch operations**: `delete()` y `keys()` pueden ser costosos en Redis con muchas claves

## Configuration Options
```typescript
// MemoryDriver (sin configuración)
const memoryDriver = new MemoryDriver();

// RedisDriver
const redisDriver = new RedisDriver({
  host: 'localhost',
  port: 6379,
  password: 'secret',
  db: 0,
  ttlSupport: true, // usar EXPIRE de Redis
  prefix: 'jamx:',   // prefijo para claves
});
```

## Testing
Tests en `packages/cache/tests/unit/`:

```bash
pnpm test
```

Cubre:
- Operaciones básicas (get/set/has/delete)
- TTL y expiración automática
- Invalidación por patrón
- Clear completo
- Drivers múltiples (memory, redis mock)

## Compatibility
- Compatible con Node.js 18+
- Funciona en Windows, macOS, Linux
- RedisDriver requiere servidor Redis accesible
- No requiere dependencias globales

## CLI Integration
- `jamx cache:clear`: Limpia todo el caché
- `jamx cache:stats`: Muestra estadísticas (hits, misses, tamaño)
- `jamx cache:invalidate <pattern>`: Invalida claves por patrón

This cache module provides a flexible, type-safe caching layer for JAMX applications, enabling developers to improve performance by reducing database load and accelerating response times with minimal code changes.