immortal-js
v1.0.0
Published
Ultra-resilient Node.js runtime enhancement — Containment, Recovery, Isolation, Supervision, Observability
Readme
Immortal.js
Production-grade resilience framework for Node.js — five composable layers that make your services impossible to kill.
What is Immortal.js?
Immortal.js is a monorepo of battle-tested resilience primitives for Node.js microservices. It implements a strict five-layer architecture:
┌──────────────────────────────────────────────────────┐
│ Layer 5 · Observability (Metrics, Health, Logs) │
├──────────────────────────────────────────────────────┤
│ Layer 4 · Supervision (Cluster, Process restart) │
├──────────────────────────────────────────────────────┤
│ Layer 3 · Isolation (Bulkhead, Worker threads) │
├──────────────────────────────────────────────────────┤
│ Layer 2 · Recovery (Retry, Circuit Breaker) │
├──────────────────────────────────────────────────────┤
│ Layer 1 · Containment (Safe Zone, Error Trap) │
└──────────────────────────────────────────────────────┘Each layer is independently usable; together they provide defense-in-depth against every class of production failure.
Packages
| Package | Description | Version |
|---------|-------------|---------|
| @immortal/core | Core resilience primitives | 1.0.0 |
| @immortal/fastify | Fastify integration plugin | 1.0.0 |
| @immortal/nestjs | NestJS module + decorators | 1.0.0 |
| @immortal/koa | Koa middleware | 1.0.0 |
| @immortal/dashboard | Real-time health dashboard | 1.0.0 |
Quick Start
npm install @immortal/coreimport { createImmortal, withRetry, CircuitBreaker } from '@immortal/core';
// 1. Initialize the runtime
const immortal = await createImmortal({
logger: { level: 'info' },
healthMonitor: { enabled: true },
gracefulShutdown: { enabled: true, timeoutMs: 30_000 },
});
// 2. Add a circuit breaker around any I/O call
const breaker = new CircuitBreaker('payment-service', {
threshold: 5,
timeout: 60_000,
halfOpenRequests: 2,
});
const result = await breaker.run(async () => {
return await paymentApi.charge(amount);
});
// 3. Retry with exponential backoff
const data = await withRetry(
() => fetch('https://api.example.com/data'),
{ maxAttempts: 3, baseDelayMs: 200, backoffFactor: 2 }
);Installation
npm workspaces (monorepo)
git clone https://github.com/Brah-Timo/immortal-js.git
cd immortal-js
npm install
npm testSingle package
npm install @immortal/coreWith framework adapter
# Fastify
npm install @immortal/core @immortal/fastify fastify
# NestJS
npm install @immortal/core @immortal/nestjs @nestjs/core @nestjs/common reflect-metadata
# Koa
npm install @immortal/core @immortal/koa koaCore API
Layer 1 — Containment (Safe Zone)
Catches all unhandled errors at process and request boundaries.
import { AsyncBoundary, asyncBoundary, ErrorTrap, classifyError } from '@immortal/core';
// Classify errors for routing
const kind = classifyError(error);
// → 'operational' | 'programming' | 'transient' | 'external' | 'unknown'
// Wrap a handler with automatic boundary context
const safeFn = asyncBoundary(myHandler, { fallback: () => defaultResponse });
// Static boundary methods
AsyncBoundary.run(async () => {
const ctx = AsyncBoundary.getContext(); // trace ID, request ID, etc.
});
// Global error trap (uncaught exceptions + unhandled rejections)
const trap = new ErrorTrap(bus);
trap.install();Layer 2 — Recovery
Retry Engine with exponential backoff and jitter:
import { withRetry, withTimeout, calculateBackoff } from '@immortal/core';
const result = await withRetry(operation, {
maxAttempts: 5,
baseDelayMs: 100,
backoffFactor: 2,
jitter: true,
retryOn: (err) => err.code === 'ECONNRESET',
});
const bounded = await withTimeout(operation, 5_000, 'payment-charge');Circuit Breaker with half-open probing:
import { CircuitBreaker, CircuitBreakerRegistry } from '@immortal/core';
const cb = new CircuitBreaker('db', {
threshold: 5, // failures before opening
timeout: 30_000, // ms before trying half-open
halfOpenRequests: 3,
});
// States: 'closed' | 'open' | 'half-open'
const state = cb.getCurrentState();
// Force state (testing)
cb.forceState('open');
// Registry for multi-service management
const registry = CircuitBreakerRegistry.getInstance();
registry.get('db').getCurrentState();Fallback Cache — serve stale on failure:
import { RouteFallbackCache } from '@immortal/core';
const cache = new RouteFallbackCache({ maxEntries: 500, ttlMs: 60_000 });
cache.set('GET /products/42', productData, { ttlMs: 300_000 });
const stale = cache.get('GET /products/42');Layer 3 — Isolation
Bulkhead Pool — limit concurrency per service:
import { BulkheadPool, BulkheadRegistry } from '@immortal/core';
const pool = new BulkheadPool('database', {
maxConcurrent: 10,
maxQueueSize: 50,
defaultTimeoutMs: 5_000,
});
const result = await pool.run(async () => db.query(sql), 'high', 3_000);
const status = pool.getStatus();
// → { name, active, queued, maxConcurrent, maxQueueSize, rejected, totalExecuted }Worker Sandbox — run untrusted code in a thread:
import { WorkerSandbox } from '@immortal/core';
const sandbox = new WorkerSandbox({ timeoutMs: 5_000, memoryLimitMb: 64 });
const result = await sandbox.execute(`return input * 2`, { input: 21 });
// → 42Layer 4 — Supervision
Supervisor — manages named async processes with automatic restart:
import { Supervisor } from '@immortal/core';
const supervisor = new Supervisor({
config: { maxRestartsInWindow: 5, windowMs: 60_000, baseRestartDelayMs: 1_000 },
onEscalation: (id) => alertTeam(`Worker ${id} is broken!`),
});
supervisor.register('http-server', {
spawnFn: () => startHttpServer(),
terminate: (instance) => instance.close(),
});
// Get all worker states
const statuses = supervisor.getWorkerStatuses();
// → [{ id, state, restartCount, uptime, pid?, lastRestartTime? }]
await supervisor.stop('http-server');
await supervisor.stopAll();Cluster Manager — multi-core process management:
import { ClusterManager } from '@immortal/core';
if (ClusterManager.isPrimary()) {
const mgr = new ClusterManager({
workerCount: 4,
onEscalation: (id) => console.error(`Worker ${id} escalated`),
});
await mgr.start();
// Zero-downtime rolling restart
await mgr.rollingRestart();
} else {
await startApp();
}Layer 5 — Observability
Metrics Collector — system metrics snapshot:
import { MetricsCollector } from '@immortal/core';
const collector = new MetricsCollector();
const snapshot = collector.getSnapshot();
// → { timestamp, eventLoopLag, memory, cpu, handles, requests, circuits, bulkheads, workers }Health Monitor — periodic health checks:
import { HealthMonitor } from '@immortal/core';
const monitor = new HealthMonitor(
{ checkIntervalMs: 30_000, eventLoopLagCriticalMs: 100 },
bus,
metricsCollector
);
monitor.start();Memory Leak Guard — detect and react to memory growth:
import { MemoryLeakGuard } from '@immortal/core';
const guard = new MemoryLeakGuard({
checkIntervalMs: 30_000,
warningThresholdMb: 400,
restartThresholdMb: 600,
growthWindowCount: 5,
}, bus);
guard.start();Diagnostics Channel — OpenTelemetry-compatible tracing:
import { DiagnosticsChannel } from '@immortal/core';
const dc = new DiagnosticsChannel({ otlpEndpoint: 'http://otel-collector:4318' });
dc.recordRequest({ route: '/api/users', method: 'GET', statusCode: 200, durationMs: 45 });Plugins
Immortal.js has a first-class plugin system:
import {
createImmortal,
ConsoleLogPlugin,
RequestTracingPlugin,
createAnomalyDetectionPlugin,
createSlackAlertPlugin,
} from '@immortal/core';
const immortal = await createImmortal({
plugins: [
ConsoleLogPlugin,
RequestTracingPlugin,
createAnomalyDetectionPlugin({ threshold: 0.1 }),
createSlackAlertPlugin({ webhookUrl: process.env.SLACK_WEBHOOK! }),
],
});Built-in plugins:
| Plugin | Description |
|--------|-------------|
| ConsoleLogPlugin | Structured JSON logging to stdout |
| RequestTracingPlugin | Request ID propagation via AsyncLocalStorage |
| createAnomalyDetectionPlugin | Statistical anomaly detection on metrics |
| createSlackAlertPlugin | Slack webhook notifications for critical events |
Custom plugins:
import type { ImmortalPlugin } from '@immortal/core';
const myPlugin: ImmortalPlugin = {
name: 'my-plugin',
async onInit(ctx) {
ctx.logger.info('Plugin initialized', { config: ctx.config });
},
async onShutdown(signal) {
// cleanup
},
};Chaos Engineering
Test your resilience in staging:
import { ChaosEngine } from '@immortal/core';
const chaos = new ChaosEngine();
// Inject latency into specific routes
chaos.addFault({
type: 'latency',
probability: 0.1, // 10% of requests
minMs: 100,
maxMs: 2000,
targetRoutes: ['/api/orders'],
});
// Random errors
chaos.addFault({
type: 'error',
probability: 0.05, // 5% error rate
errorMessage: 'Chaos: simulated service failure',
});
// Wrap a handler
const chaosHandler = chaos.wrapHandler(originalHandler);
// Enable/disable at runtime
chaos.enable();
chaos.disable();Framework Integrations
Fastify
import Fastify from 'fastify';
import immortalPlugin from '@immortal/fastify';
const app = Fastify();
await app.register(immortalPlugin, {
circuitBreakers: true,
bulkheads: true,
metrics: true,
gracefulShutdown: true,
});See examples/fastify-microservice for a full example.
NestJS
import { Module } from '@nestjs/common';
import { ImmortalModule } from '@immortal/nestjs';
@Module({
imports: [
ImmortalModule.forRoot({
logger: { level: 'info' },
gracefulShutdown: { enabled: true },
}),
],
})
export class AppModule {}Decorators:
import { Bulkhead, Circuit, Retry } from '@immortal/nestjs';
@Injectable()
export class OrdersService {
@Bulkhead({ pool: 'orders', maxConcurrent: 5 })
@Circuit({ name: 'order-db', threshold: 3 })
@Retry({ maxAttempts: 2 })
async createOrder(dto: CreateOrderDto) {
return this.db.orders.create(dto);
}
}See examples/nestjs-enterprise for a full example.
Koa
import Koa from 'koa';
import { immortalMiddleware } from '@immortal/koa';
const app = new Koa();
app.use(immortalMiddleware({ circuitBreakers: true, metrics: true }));Configuration Reference
interface ImmortalConfig {
logger?: {
level?: 'debug' | 'info' | 'warn' | 'error' | 'silent';
transport?: 'console' | 'json' | 'pretty';
prefix?: string;
};
healthMonitor?: {
enabled?: boolean;
checkIntervalMs?: number;
eventLoopLagWarningMs?: number;
eventLoopLagCriticalMs?: number;
heapUsageWarningPercent?: number;
heapUsageCriticalPercent?: number;
};
memoryGuard?: {
enabled?: boolean;
checkIntervalMs?: number;
warningThresholdMb?: number;
restartThresholdMb?: number;
growthWindowCount?: number;
adaptive?: boolean;
};
gracefulShutdown?: {
enabled?: boolean;
timeoutMs?: number;
signals?: NodeJS.Signals[];
};
chaos?: {
enabled?: boolean;
};
plugins?: ImmortalPlugin[];
}Testing
# All tests
npm test --workspaces --if-present
# Core package only
cd packages/core && npm test
# Watch mode
cd packages/core && npm test -- --watch
# Coverage
cd packages/core && npm test -- --coverage102 tests across 6 test suites — all passing.
Project Structure
immortal/
├── packages/
│ ├── core/ # Main resilience library
│ │ ├── src/
│ │ │ ├── safe-zone/ # Layer 1: Containment
│ │ │ ├── recovery/ # Layer 2: Retry, CircuitBreaker, FallbackCache
│ │ │ ├── isolation/ # Layer 3: BulkheadPool, WorkerSandbox
│ │ │ ├── supervision/ # Layer 4: Supervisor, ClusterManager
│ │ │ ├── monitoring/ # Layer 5: Metrics, Health, MemoryGuard
│ │ │ ├── lifecycle/ # GracefulShutdown
│ │ │ ├── chaos/ # ChaosEngine
│ │ │ ├── plugins/ # Built-in plugins
│ │ │ ├── config/ # Defaults & validation
│ │ │ ├── event-bus.ts # Typed internal event bus
│ │ │ ├── logger.ts # Structured logger
│ │ │ ├── runtime.ts # ImmortalRuntime orchestrator
│ │ │ ├── types.ts # All TypeScript interfaces
│ │ │ └── index.ts # Public API surface
│ │ └── test/ # Vitest test suites
│ ├── adapter-fastify/ # Fastify plugin
│ ├── adapter-nestjs/ # NestJS module
│ ├── adapter-koa/ # Koa middleware
│ └── dashboard/ # Real-time dashboard
├── examples/
│ ├── fastify-microservice/ # Complete Fastify example
│ └── nestjs-enterprise/ # Complete NestJS example
├── docs/
│ ├── api/ # Per-module API docs
│ └── guides/ # Integration guides
├── package.json # npm workspaces root
├── tsconfig.json # TypeScript 5.4+ strict config
└── LICENSE # MITContributing
See docs/CONTRIBUTING.md.
Changelog
See docs/CHANGELOG.md.
License
MIT © Immortal.js Contributors — see LICENSE.
