FesaComponents
Más módulos

Utilidades

Errores, paginación, formato es-PA, contraste WCAG, sanitización, cache y puertos de infraestructura.

import { AppError, NotFoundError, cn, slugify, contrastRatio } from "@fesa/components/utils";

Errores de dominio

Jerarquía AppError con statusCode y código estable — el contrato de error único del ecosistema:

NotFoundError (404) · ValidationError (400, con fieldErrors) · UnauthorizedError (401) · ForbiddenError (403) · AccountLockedError (423) · ConflictError (409) · TooManyRequestsError (429) · ServiceUnavailableError (503)

if (!order) throw new NotFoundError("La orden no existe");

ActionState

El contrato estandarizado de useActionState:

interface ActionState { success: boolean; message: string; errors?: Record<string, string[]> }

Paginación

const result = createPaginatedResult(items, total, { page: 1, pageSize: 20 });
// { items, totalItems, totalPages, page, pageSize, hasNext, hasPrev }

Tipos PaginationParams, PaginatedResult<T>, SortParams + DEFAULT_PAGE_SIZE (20) y MAX_PAGE_SIZE (100) — el mismo techo que usa el datatable.

Formato FESA

formatCurrency(amount) (es-PA, USD) · formatDate(date, options?) (América/Panamá) · cn(...) (clsx + tailwind-merge) · slugify / toSlug (normaliza acentos por NFD) · stripUndefined(obj) · calendarDayRange(from, to) (rango de día local con fin exclusivo — evita el off-by-one de UTC) · toPlain(value) (convierte Decimal de Prisma a number para cruzar la frontera server→client).

Contraste WCAG 2.1

contrastRatio("#1e40af", "#ffffff")  // 8.59
wcagLevel(ratio, isLargeText)        // 'AAA' | 'AA' | 'AA Large' | 'Fail'
parseColor("rgb(30, 64, 175)")       // parsea hex y rgb()

Cero dependencias, con suite de tests. Lo usa el editor de tarjetas para avisar cuando un diseño no se lee.

Sanitización

sanitizeHtml(html) — DOMPurify con allowlist (prohíbe style/form/button) · sanitizeText · sanitizeAttr · sanitizeMarkdownHtml. Una sola política anti-XSS para todo el ecosistema.

Cache y rate limit en memoria

  • MemoryCache — TTL por clave con limpieza unref() (interfaz estilo Redis: get/set/delete). Para producción distribuida, Redis en la app detrás de ICache.
  • MemoryRateLimitService — sliding window en memoria que implementa IRateLimitService.

Inyección de dependencias

const emailProvider = createProvider<IEmailService>(() => new ResendEmailService());
// emailProvider.get() — singleton lazy · .set(mock) — seam de tests · .reset()

Puertos de infraestructura

Contratos para intercambiar proveedores sin tocar consumidores: IStorageService, IEmailService, ICache, IRateLimitService (con sus DTOs).

En esta pagina