Skip to Content
Packages@loop/core

@loop/core

Foundational patterns for the Loop Health ecosystem. Zero internal dependencies (except @upstash/redis for distributed features).

Installation

pnpm add @loop/core

Result Type

Monadic error handling without exceptions. Every fallible operation returns Result<T> instead of throwing.

import { ok, err, map, flatMap, combine, tryCatch, tryCatchSync } from '@loop/core'; import type { Result } from '@loop/core'; // Create results const success = ok(42); // Result<number> const failure = err(notFound()); // Result<never> // Check and unwrap if (success.ok) { console.log(success.value); // 42 } // Transform const doubled = map(success, (n) => n * 2); // ok(84) // Chain operations const result = flatMap(getUserId(), (id) => getProfile(id)); // Combine multiple results const combined = combine([ok(1), ok(2), ok(3)]); // ok([1, 2, 3]) // Wrap throwing code const fetched = await tryCatch(() => fetch('/api')); const parsed = tryCatchSync(() => JSON.parse(str));

Utility Functions

FunctionDescription
ok(value)Create a success result
err(error)Create a failure result
isOk(result)Type guard for success
isErr(result)Type guard for failure
unwrap(result)Get value or throw
unwrapOr(result, fallback)Get value or fallback
map(result, fn)Transform the success value
mapErr(result, fn)Transform the error
flatMap(result, fn)Chain results
combine(results)Combine array of results
tryCatch(fn)Wrap async throwing function
tryCatchSync(fn)Wrap sync throwing function

Error Types

Structured error types for consistent error handling across all packages.

import { AppError, ErrorCode, createError, createValidationError, notFound, unauthorized, forbidden, dbError, networkError, fromUnknown, } from '@loop/core'; // Create errors const error = createError('VALIDATION', 'Email is required'); const notFoundErr = notFound('User', 'user_123'); const authErr = unauthorized('Invalid token'); const forbiddenErr = forbidden('Insufficient permissions'); const dbErr = dbError('Connection failed'); const netErr = networkError('Timeout'); // Wrap unknown errors const wrapped = fromUnknown(caughtError);

Error Codes

NOT_FOUND, UNAUTHORIZED, FORBIDDEN, VALIDATION, CONFLICT, RATE_LIMIT, DATABASE, NETWORK, EXTERNAL_SERVICE, INTERNAL

Logger

Structured logging with pretty output in development and JSON in production.

import { createLogger, logger } from '@loop/core'; // Default logger logger.info('Server started', { port: 3000 }); // Named logger const log = createLogger('patient-graph'); log.info('Request received', { path: '/profiles', method: 'GET' }); log.warn('Slow query', { duration: 500 }); log.error('Failed to process', { error });

Distributed Locks

Redis-based distributed locking using Upstash.

import { acquireLock, releaseLock, withLock } from '@loop/core'; // Manual lock management const lock = await acquireLock('checkout:user_123', { ttl: 30000 }); try { await processCheckout(); } finally { await releaseLock(lock); } // Automatic lock management const result = await withLock('sync:user_123', async () => { return await syncData(); }, { ttl: 60000 });

Lock Key Helpers

import { checkoutLockKey, cronLockKey } from '@loop/core'; checkoutLockKey('user_123'); // 'checkout:user_123' cronLockKey('sync-products'); // 'cron:sync-products'

Rate Limiting

Sliding-window rate limiting via Upstash Redis.

import { checkRateLimit } from '@loop/core'; const result = await checkRateLimit({ key: `api:${userId}`, limit: 100, window: 60, // seconds }); if (!result.allowed) { // Rate limited — result.retryAfter contains seconds to wait }

Circuit Breaker

Protect against failing external services.

import { createCircuitBreaker, CircuitOpenError } from '@loop/core'; const breaker = createCircuitBreaker({ failureThreshold: 5, resetTimeout: 30000, halfOpenRequests: 2, }); try { const result = await breaker.execute(() => callExternalAPI()); } catch (error) { if (error instanceof CircuitOpenError) { // Circuit is open — external service is down } }

Cache

In-memory caching with namespace support and TTL.

import { getCache, setCache, deleteCache, invalidateNamespace, withCache } from '@loop/core'; import { CACHE_NAMESPACES, CACHE_TTL } from '@loop/core'; // Manual caching await setCache('user:123', userData, CACHE_TTL.SHORT); const cached = await getCache('user:123'); await deleteCache('user:123'); // Cache-aside pattern const data = await withCache('profile:123', () => fetchProfile('123'), { ttl: CACHE_TTL.MEDIUM, namespace: CACHE_NAMESPACES.PROFILES, }); // Invalidate all entries in a namespace await invalidateNamespace(CACHE_NAMESPACES.PROFILES);

Used By

@loop/core is used by all applications in the Loop Platform: