Advanced error handling utilities for TypeScript applications with middleware, metrics, and monitoring support.
Quick Start β’ Documentation β’ Examples β’ API Reference β’ Contributing
- Features
- Installation
- Quick Start
- Documentation
- Examples
- API Reference
- Configuration
- Best Practices
- Troubleshooting
- Migration Guide
- Performance
- Contributing
- License
Feature | Description | Status |
---|---|---|
π Structured Error Handling | Consistent error types and codes | β |
π§ TRPC Integration | Seamless integration with tRPC | β |
π‘οΈ Type Safety | Full TypeScript support | β |
π Circuit Breaker | Built-in circuit breaker pattern | β |
π Error Metadata | Rich error context and metadata | β |
π― Factory Functions | Pre-built error creators | β |
π Type Guards | Runtime error type checking | β |
π Comprehensive Logging | Structured error logging | β |
π Framework Middleware | Express.js, Fastify, and Hono support | β |
π Error Metrics | Built-in monitoring and analytics | β |
π Prometheus Integration | Production-ready metrics | β |
ποΈ Error Dashboard | Real-time error monitoring | β |
π Security Features | Context sanitization and sensitive data protection | β |
π Retry Strategies | Multiple retry strategies for different scenarios | β |
π¦ Error Caching | Deduplication and caching system | β |
# npm
npm install @osmanekrem/error-handler
# yarn
yarn add @osmanekrem/error-handler
# pnpm
pnpm add @osmanekrem/error-handler
# bun
bun add @osmanekrem/error-handler
# For tRPC integration (optional)
npm install @trpc/server
# For framework integrations (optional)
npm install express fastify hono
import {
AppError,
createError,
errorHandler,
isAppError
} from '@osmanekrem/error-handler';
// Create a structured error
const userError = createError.notFound('User', { userId: '123' });
// Handle errors with context
try {
await riskyOperation();
} catch (error) {
const appError = errorHandler(error, 'user-operation', {
logErrors: true,
sanitizeContext: true
});
throw appError;
}
// Type-safe error checking
if (isAppError(error)) {
console.log('Error code:', error.code);
console.log('Severity:', error.getSeverity());
}
// Express.js
import express from 'express';
import { expressErrorMiddleware } from '@osmanekrem/error-handler';
const app = express();
app.use(expressErrorMiddleware({
logErrors: true,
includeStack: process.env.NODE_ENV === 'development'
}));
// Hono
import { Hono } from 'hono';
import { honoErrorMiddleware } from '@osmanekrem/error-handler';
const app = new Hono();
app.use('*', honoErrorMiddleware({
logErrors: true,
sanitizeContext: true
}));
// Fastify
import Fastify from 'fastify';
import { fastifyErrorPlugin } from '@osmanekrem/error-handler';
const fastify = Fastify();
await fastify.register(fastifyErrorPlugin, {
logErrors: true
});
The AppError
class is the foundation of the error handling system:
import { AppError } from '@osmanekrem/error-handler';
const error = new AppError(
'USER_NOT_FOUND', // Error code
'User not found', // Message
404, // HTTP status code
true, // Is operational
{ userId: '123' } // Context
);
// Error properties
console.log(error.code); // 'USER_NOT_FOUND'
console.log(error.statusCode); // 404
console.log(error.isOperational); // true
console.log(error.getSeverity()); // 'medium'
console.log(error.timestamp); // Date object
The library includes 68+ predefined error codes organized by category:
import { ErrorCodes } from '@osmanekrem/error-handler';
// Authentication & Authorization
ErrorCodes.UNAUTHORIZED
ErrorCodes.FORBIDDEN
ErrorCodes.TOKEN_EXPIRED
// Validation
ErrorCodes.VALIDATION_ERROR
ErrorCodes.MISSING_REQUIRED_FIELD
ErrorCodes.INVALID_FORMAT
// Database
ErrorCodes.DATABASE_ERROR
ErrorCodes.CONNECTION_ERROR
ErrorCodes.QUERY_ERROR
// External Services
ErrorCodes.EXTERNAL_SERVICE_ERROR
ErrorCodes.RATE_LIMIT_EXCEEDED
ErrorCodes.SERVICE_UNAVAILABLE
Pre-built error creators for common scenarios:
import { createError } from '@osmanekrem/error-handler';
// Authentication errors
const authError = createError.unauthorized('Invalid token');
const forbiddenError = createError.forbidden('Access denied');
// Validation errors
const validationError = createError.validation('Invalid email format');
const missingField = createError.missingRequiredField('email');
// Resource errors
const notFound = createError.notFound('User', { userId: '123' });
const conflict = createError.conflict('Email already exists');
// Database errors
const dbError = createError.database('Connection failed');
const queryError = createError.queryError('SELECT * FROM users');
// External service errors
const serviceError = createError.external('PaymentService', 'API timeout');
const rateLimit = createError.rateLimitExceeded('API', 60); // retry after 60s
// Business logic errors
const businessError = createError.businessRuleViolation('Insufficient balance');
const quotaError = createError.quotaExceeded('API calls', 1000);
import { BaseCustomError, DatabaseError, ValidationError } from '@osmanekrem/error-handler';
// Database-specific error
const dbError = new DatabaseError('Connection failed', {
host: 'localhost',
port: 5432,
operation: 'connect'
});
// Validation error with field details
const validationError = new ValidationError('Invalid email format', {
field: 'email',
value: 'invalid-email',
expectedFormat: 'user@domain.com'
});
import express from 'express';
import {
expressErrorMiddleware,
asyncHandler,
createError
} from '@osmanekrem/error-handler';
const app = express();
// Global error middleware
app.use(expressErrorMiddleware({
logErrors: true,
includeStack: process.env.NODE_ENV === 'development',
sanitizeContext: true,
onError: (error) => {
// Custom error handling
console.log('Custom error handler:', error.code);
}
}));
// Async route handler
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await getUser(req.params.id);
if (!user) {
throw createError.notFound('User', { userId: req.params.id });
}
res.json(user);
}));
import { Hono } from 'hono';
import {
honoErrorMiddleware,
honoErrorHandler,
createHonoError
} from '@osmanekrem/error-handler';
const app = new Hono();
// Middleware approach
app.use('*', honoErrorMiddleware({
logErrors: true,
includeStack: process.env.NODE_ENV === 'development',
sanitizeContext: true,
includeRequestInfo: true
}));
// Or using onError hook
app.onError(honoErrorHandler({
logErrors: true,
includeStack: process.env.NODE_ENV === 'development'
}));
// Route handlers
app.get('/users/:id', async (c) => {
const user = await getUser(c.req.param('id'));
if (!user) {
throw createHonoError('USER_NOT_FOUND', 'User not found', 404, {
userId: c.req.param('id')
});
}
return c.json(user);
});
import Fastify from 'fastify';
import { fastifyErrorPlugin, createFastifyError } from '@osmanekrem/error-handler';
const fastify = Fastify();
// Register error plugin
await fastify.register(fastifyErrorPlugin, {
logErrors: true,
includeStack: process.env.NODE_ENV === 'development'
});
// Route handlers
fastify.get('/users/:id', async (request, reply) => {
const user = await getUser(request.params.id);
if (!user) {
throw createFastifyError('USER_NOT_FOUND', 'User not found', 404, {
userId: request.params.id
});
}
return user;
});
import { initTRPC } from '@trpc/server';
import { toTRPCError, isAppError } from '@osmanekrem/error-handler';
const t = initTRPC.create();
const userRouter = t.router({
getUser: t.procedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
try {
const user = await getUser(input.id);
if (!user) {
throw createError.notFound('User', { userId: input.id });
}
return user;
} catch (error) {
if (isAppError(error)) {
throw toTRPCError(error);
}
throw error;
}
})
});
import { CircuitBreaker } from '@osmanekrem/error-handler';
const circuitBreaker = new CircuitBreaker({
failureThreshold: 5, // Open after 5 failures
recoveryTimeout: 60000, // 1 minute recovery time
monitoringPeriod: 10000, // 10 seconds monitoring
halfOpenMaxCalls: 3 // Max calls in half-open state
});
// Execute with circuit breaker protection
try {
const result = await circuitBreaker.execute(
() => callExternalService(),
'external-service'
);
} catch (error) {
if (error.code === 'SERVICE_UNAVAILABLE') {
// Circuit breaker is open
console.log('Service is down, using fallback');
}
}
// Monitor circuit breaker state
console.log('State:', circuitBreaker.getState());
console.log('Healthy:', circuitBreaker.isHealthy());
console.log('Stats:', circuitBreaker.getStats());
import { ErrorMetrics, PrometheusMetrics } from '@osmanekrem/error-handler';
const metrics = new ErrorMetrics({
enablePrometheus: true,
customLabels: { service: 'user-service' },
retentionPeriod: 24 * 60 * 60 * 1000, // 24 hours
maxErrors: 10000
});
// Record errors
metrics.recordError(error, {
service: 'user-service',
userId: '123',
requestId: 'req-456'
});
// Get statistics
const stats = metrics.getStats();
console.log('Total errors:', stats.totalErrors);
console.log('Error rate:', stats.errorRate);
console.log('Top errors:', stats.topErrors);
// Prometheus metrics
const prometheus = new PrometheusMetrics({
serviceName: 'user-service',
version: '1.0.0'
});
// Export metrics for Prometheus
app.get('/metrics', (req, res) => {
res.set('Content-Type', 'text/plain');
res.send(prometheus.export());
});
import {
retry,
RetryStrategy,
DatabaseRetryStrategy,
ApiRetryStrategy
} from '@osmanekrem/error-handler';
// Basic retry
const result = await retry(
() => riskyOperation(),
{ maxAttempts: 3, delay: 1000 }
);
// Database retry strategy
const dbRetry = new DatabaseRetryStrategy({
maxAttempts: 5,
baseDelay: 1000,
maxDelay: 10000,
backoffMultiplier: 2
});
const user = await dbRetry.execute(() => getUserFromDB(id));
// API retry strategy
const apiRetry = new ApiRetryStrategy({
maxAttempts: 3,
baseDelay: 500,
maxDelay: 5000,
retryableStatusCodes: [500, 502, 503, 504]
});
const data = await apiRetry.execute(() => callExternalAPI());
import { ErrorCache, DeduplicationService } from '@osmanekrem/error-handler';
const errorCache = new ErrorCache({
maxSize: 1000,
ttl: 300000, // 5 minutes
cleanupInterval: 60000 // 1 minute
});
const deduplication = new DeduplicationService({
windowSize: 60000, // 1 minute
maxDuplicates: 10
});
// Cache errors
errorCache.set('user-123', error);
// Deduplicate errors
const result = await deduplication.processError(error, {
userId: '123',
operation: 'getUser'
});
if (result.isDuplicate) {
console.log('Duplicate error detected');
} else {
console.log('New error, processing...');
}
import {
createError,
errorHandler,
CircuitBreaker,
ErrorMetrics
} from '@osmanekrem/error-handler';
class OrderService {
private circuitBreaker = new CircuitBreaker();
private metrics = new ErrorMetrics();
async createOrder(orderData: OrderData) {
try {
// Validate order data
this.validateOrderData(orderData);
// Check inventory with circuit breaker
const inventory = await this.circuitBreaker.execute(
() => this.checkInventory(orderData.items),
'inventory-service'
);
// Process payment
const payment = await this.processPayment(orderData.payment);
// Create order
const order = await this.saveOrder(orderData);
return order;
} catch (error) {
const appError = errorHandler(error, 'create-order', {
logErrors: true,
sanitizeContext: true
});
this.metrics.recordError(appError, {
service: 'order-service',
operation: 'createOrder'
});
throw appError;
}
}
private validateOrderData(data: OrderData) {
if (!data.items || data.items.length === 0) {
throw createError.validation('Order must contain at least one item');
}
if (!data.payment) {
throw createError.missingRequiredField('payment');
}
if (data.total <= 0) {
throw createError.businessRuleViolation('Order total must be greater than 0');
}
}
private async checkInventory(items: OrderItem[]) {
// External service call
const response = await fetch('/api/inventory/check', {
method: 'POST',
body: JSON.stringify({ items })
});
if (!response.ok) {
throw createError.external('InventoryService', 'Failed to check inventory');
}
return response.json();
}
}
import {
createError,
CircuitBreaker,
retry,
ApiRetryStrategy
} from '@osmanekrem/error-handler';
class UserService {
private userCircuitBreaker = new CircuitBreaker({
failureThreshold: 3,
recoveryTimeout: 30000
});
private paymentRetry = new ApiRetryStrategy({
maxAttempts: 3,
baseDelay: 1000,
retryableStatusCodes: [500, 502, 503, 504]
});
async getUserWithPaymentInfo(userId: string) {
try {
// Get user with circuit breaker protection
const user = await this.userCircuitBreaker.execute(
() => this.getUser(userId),
'user-service'
);
if (!user) {
throw createError.notFound('User', { userId });
}
// Get payment info with retry strategy
const paymentInfo = await this.paymentRetry.execute(
() => this.getPaymentInfo(userId),
'payment-service'
);
return {
...user,
paymentInfo
};
} catch (error) {
if (error.code === 'SERVICE_UNAVAILABLE') {
// Circuit breaker is open, return cached data
return this.getCachedUser(userId);
}
throw error;
}
}
}
The main error class with structured error handling.
import { AppError } from '@osmanekrem/error-handler';
const error = new AppError(
'USER_NOT_FOUND',
'User not found',
404,
true, // isOperational
{ userId: '123' } // context
);
// Check error properties
console.log(error.code); // 'USER_NOT_FOUND'
console.log(error.statusCode); // 404
console.log(error.isOperational); // true
console.log(error.getSeverity()); // 'medium'
Pre-built error creators for common scenarios.
import { createError } from '@osmanekrem/error-handler';
// Authentication errors
const authError = createError.unauthorized('Invalid token');
const forbiddenError = createError.forbidden('Access denied');
// Validation errors
const validationError = createError.validation('Invalid email format');
const missingField = createError.missingRequiredField('email');
// Resource errors
const notFound = createError.notFound('User', { userId: '123' });
const conflict = createError.conflict('Email already exists');
// Database errors
const dbError = createError.database('Connection failed');
const queryError = createError.queryError('SELECT * FROM users');
// External service errors
const serviceError = createError.external('PaymentService', 'API timeout');
const rateLimit = createError.rateLimitExceeded('API', 60); // retry after 60s
Convert AppError to TRPCError for tRPC compatibility.
import { toTRPCError } from '@osmanekrem/error-handler';
import { TRPCError } from '@trpc/server';
try {
// Some operation
} catch (error) {
if (isAppError(error)) {
throw toTRPCError(error);
}
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unknown error' });
}
Global error handler with logging and context.
import { errorHandler, withErrorHandling } from '@osmanekrem/error-handler';
// Manual error handling
try {
// Some operation
} catch (error) {
const appError = errorHandler(error, 'user-operation', {
logErrors: true,
logLevel: 'error',
includeStack: true,
sanitizeContext: true,
});
throw appError;
}
// Higher-order function
const safeOperation = withErrorHandling(
async (userId: string) => {
// Risky operation
return await getUser(userId);
},
'get-user',
{ logErrors: true }
);
Protect against cascading failures.
import { CircuitBreaker } from '@osmanekrem/error-handler';
const circuitBreaker = new CircuitBreaker({
failureThreshold: 5,
recoveryTimeout: 60000, // 1 minute
halfOpenMaxCalls: 3,
});
// Execute with circuit breaker protection
try {
const result = await circuitBreaker.execute(
() => callExternalService(),
'external-service'
);
} catch (error) {
// Handle circuit breaker errors
}
// Check circuit breaker state
console.log(circuitBreaker.getState());
console.log(circuitBreaker.isHealthy());
console.log(circuitBreaker.getStats());
Runtime error type checking.
import { isAppError, isAppErrorWithCode, isRetryableAppError } from '@osmanekrem/error-handler';
try {
// Some operation
} catch (error) {
if (isAppError(error)) {
console.log('AppError:', error.code);
}
if (isAppErrorWithCode(error, 'USER_NOT_FOUND')) {
// Handle specific error
}
if (isRetryableAppError(error)) {
// Retry logic
}
}
Seamless integration with Hono framework.
import { Hono } from 'hono';
import { honoErrorMiddleware, honoErrorHandler, createHonoError } from '@osmanekrem/error-handler';
const app = new Hono();
// Using middleware approach
app.use('*', honoErrorMiddleware({
logErrors: true,
includeStack: process.env.NODE_ENV === 'development',
sanitizeContext: true
}));
// Or using onError hook
app.onError(honoErrorHandler({
logErrors: true,
includeStack: process.env.NODE_ENV === 'development'
}));
// Route handlers
app.get('/users/:id', async (c) => {
const user = await getUser(c.req.param('id'));
if (!user) {
throw createHonoError('USER_NOT_FOUND', 'User not found', 404, {
userId: c.req.param('id')
});
}
return c.json(user);
});
app.post('/users', async (c) => {
const body = await c.req.json();
if (!body.email) {
throw createHonoError('MISSING_REQUIRED_FIELD', 'Email is required', 400, {
field: 'email'
});
}
// Create user logic
return c.json({ success: true });
});
The library includes predefined error codes for consistency:
UNAUTHORIZED
- Authentication requiredFORBIDDEN
- Access deniedTOKEN_EXPIRED
- Token has expiredINVALID_TOKEN
- Invalid tokenINSUFFICIENT_PERMISSIONS
- Insufficient permissions
VALIDATION_ERROR
- Validation failedINVALID_INPUT
- Invalid input providedMISSING_REQUIRED_FIELD
- Required field is missingINVALID_FORMAT
- Invalid format
NOT_FOUND
- Resource not foundCONFLICT
- Resource conflictDUPLICATE_RESOURCE
- Resource already existsRESOURCE_LOCKED
- Resource is lockedRESOURCE_EXPIRED
- Resource has expired
DATABASE_ERROR
- Database operation failedCONNECTION_ERROR
- Database connection failedQUERY_ERROR
- Database query failedTRANSACTION_ERROR
- Database transaction failedCONSTRAINT_VIOLATION
- Database constraint violation
EXTERNAL_SERVICE_ERROR
- External service errorSERVICE_UNAVAILABLE
- Service temporarily unavailableRATE_LIMIT_EXCEEDED
- Rate limit exceededAPI_ERROR
- API error occurred
INTERNAL_SERVER_ERROR
- Internal server errorTIMEOUT
- Operation timed outMEMORY_ERROR
- Memory error occurredDISK_ERROR
- Disk error occurredNETWORK_ERROR
- Network error occurred
Each error includes metadata for monitoring and alerting:
import { getErrorMetadata } from '@osmanekrem/error-handler';
const metadata = getErrorMetadata(error);
console.log(metadata);
// {
// code: 'USER_NOT_FOUND',
// severity: 'medium',
// category: 'resource',
// retryable: false,
// userMessage: 'User not found',
// isOperational: true,
// statusCode: 404,
// timestamp: Date
// }
// β
Good - Structured and informative
throw createError.notFound('User', {
userId: '123',
operation: 'getUser',
timestamp: new Date().toISOString()
});
// β Bad - Generic and unhelpful
throw new Error('User not found');
// β
Good - Rich context for debugging
throw createError.database('Query failed', {
query: 'SELECT * FROM users WHERE id = ?',
parameters: ['123'],
userId: '123',
operation: 'getUser',
database: 'postgres',
connectionId: 'conn-456'
});
// β Bad - Minimal context
throw createError.database('Query failed');
// β
Good - Protected external calls
const circuitBreaker = new CircuitBreaker();
const result = await circuitBreaker.execute(
() => callExternalAPI(),
'external-service'
);
// β Bad - No protection
const result = await callExternalAPI();
// β
Good - Comprehensive error handling
try {
const user = await getUser(id);
} catch (error) {
if (isAppErrorWithCode(error, 'USER_NOT_FOUND')) {
return { success: false, message: 'User not found' };
}
if (isRetryableAppError(error)) {
// Implement retry logic
return retry(() => getUser(id), { maxAttempts: 3 });
}
throw errorHandler(error, 'getUser', {
logErrors: true,
sanitizeContext: true
});
}
// β Bad - Poor error handling
try {
const user = await getUser(id);
} catch (error) {
console.log(error); // Just logging
}
// β
Good - Type-safe error checking
if (isAppError(error)) {
console.log('Error code:', error.code);
console.log('Severity:', error.getSeverity());
if (error.isTypeOf('USER_NOT_FOUND')) {
// Handle specific error type
}
}
// β Bad - Unsafe error checking
if (error.code === 'USER_NOT_FOUND') {
// TypeScript error - error might not have code property
}
// β
Good - Comprehensive monitoring
const metrics = new ErrorMetrics({
enablePrometheus: true,
customLabels: { service: 'user-service' }
});
try {
await riskyOperation();
} catch (error) {
const appError = errorHandler(error, 'risky-operation');
metrics.recordError(appError, {
service: 'user-service',
userId: '123',
requestId: 'req-456'
});
throw appError;
}
// β
Good - Appropriate error codes
if (!user) {
throw createError.notFound('User', { userId });
}
if (!isValidEmail(email)) {
throw createError.invalidFormat('email', 'user@domain.com');
}
if (balance < amount) {
throw createError.insufficientFunds(amount, balance);
}
// β Bad - Generic error codes
if (!user) {
throw createError.internal('User not found');
}
Problem: TypeScript can't find module types
// Error: Cannot find module '@osmanekrem/error-handler'
Solution: Ensure proper installation and TypeScript configuration
npm install @osmanekrem/error-handler
# or
yarn add @osmanekrem/error-handler
Problem: Circuit breaker always returns errors
// Circuit breaker is always OPEN
Solution: Check configuration and failure threshold
const circuitBreaker = new CircuitBreaker({
failureThreshold: 5, // Increase if needed
recoveryTimeout: 60000, // Check timeout value
monitoringPeriod: 10000 // Adjust monitoring period
});
Problem: Sensitive data in error logs
// Password appears in error context
Solution: Enable context sanitization
const appError = errorHandler(error, 'operation', {
sanitizeContext: true // Enable sanitization
});
Problem: Error metrics not being recorded
// metrics.getStats() returns empty data
Solution: Ensure metrics are properly initialized and errors are recorded
const metrics = new ErrorMetrics({
enablePrometheus: true,
retentionPeriod: 24 * 60 * 60 * 1000
});
// Record errors explicitly
metrics.recordError(error, { service: 'my-service' });
Enable debug mode for detailed error information:
const appError = errorHandler(error, 'operation', {
logErrors: true,
logLevel: 'debug',
includeStack: true
});
If you experience performance issues:
- Disable stack traces in production:
const appError = errorHandler(error, 'operation', {
includeStack: process.env.NODE_ENV === 'development'
});
- Limit error cache size:
const errorCache = new ErrorCache({
maxSize: 1000, // Reduce if needed
ttl: 300000 // 5 minutes
});
- Use error batching:
// Batch errors for better performance
const errorBatcher = new ErrorBatcher({
batchSize: 100,
flushInterval: 5000
});
Before:
try {
const user = await getUser(id);
} catch (error) {
console.error('Error:', error.message);
throw new Error('User not found');
}
After:
import { createError, errorHandler } from '@osmanekrem/error-handler';
try {
const user = await getUser(id);
} catch (error) {
const appError = errorHandler(error, 'getUser', {
logErrors: true,
sanitizeContext: true
});
throw appError;
}
Before:
app.use((error, req, res, next) => {
console.error(error);
res.status(500).json({ error: 'Internal server error' });
});
After:
import { expressErrorMiddleware } from '@osmanekrem/error-handler';
app.use(expressErrorMiddleware({
logErrors: true,
includeStack: process.env.NODE_ENV === 'development'
}));
Before:
class UserNotFoundError extends Error {
constructor(userId: string) {
super(`User not found: ${userId}`);
this.name = 'UserNotFoundError';
}
}
After:
import { createError } from '@osmanekrem/error-handler';
// Use factory function
throw createError.notFound('User', { userId });
Operation | Time (ms) | Memory (MB) |
---|---|---|
Create AppError | 0.01 | 0.1 |
Error Handler | 0.05 | 0.2 |
Circuit Breaker | 0.02 | 0.3 |
Error Metrics | 0.03 | 0.5 |
Error Caching | 0.01 | 0.2 |
- Use error caching for frequently occurring errors
- Disable stack traces in production
- Batch error metrics for better performance
- Use circuit breakers to prevent cascade failures
- Implement error deduplication to reduce noise
// Monitor memory usage
const metrics = new ErrorMetrics({
maxErrors: 10000, // Limit error records
retentionPeriod: 24 * 60 * 60 * 1000 // 24 hours
});
// Clean up old errors
setInterval(() => {
metrics.reset();
}, 24 * 60 * 60 * 1000); // Reset every 24 hours
We welcome contributions! Please see our Contributing Guide for details.
# Clone the repository
git clone https://github.com/osmanekrem/error-handler.git
# Install dependencies
npm install
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Build the project
npm run build
# Run linting
npm run lint
- Use TypeScript strict mode
- Follow ESLint rules
- Write comprehensive tests
- Update documentation
- Follow conventional commits
MIT License - see LICENSE file for details.
- π Documentation
- π Report Issues
- π¬ Discussions
- π§ Email Support
Made with β€οΈ by Osman Ekrem