# JSM Exceptions

[![npm version](https://badge.fury.io/js/jsm-exceptions.svg)](https://badge.fury.io/js/jsm-exceptions)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A comprehensive TypeScript exception library with HTTP status code support, detailed JSDoc documentation, and backward compatibility. Provides structured error handling for web applications and APIs.

## ✨ Features

- 🎯 **HTTP Status Code Alignment**: Each exception corresponds to appropriate HTTP status codes
- 📝 **Comprehensive JSDoc**: Detailed documentation with examples for every exception
- 🔄 **Backward Compatibility**: Maintains compatibility with existing code using `@deprecated` annotations
- 🏗️ **TypeScript Support**: Full TypeScript support with proper type definitions
- 🧩 **Modular Design**: Import only what you need
- 🔍 **Rich Context**: Support for additional context and metadata in exceptions
- ⏰ **Timestamps**: Automatic timestamp tracking for debugging
- 🎨 **Validation Support**: Built-in support for Joi validation errors

## 📦 Installation

```bash
npm install jsm-exceptions
```

## 🚀 Quick Start

### Basic Usage

```typescript
import { BadRequestException, ValidationException, ItemNotFoundException } from 'jsm-exceptions';

// Simple usage
throw new BadRequestException('Invalid request format');

// With additional context
throw new ItemNotFoundException('User not found', { userId: '12345' });

// Validation with field errors
throw new ValidationException('Validation failed', {
  email: { value: 'invalid-email', message: 'Email format is invalid' },
  age: { value: -1, message: 'Age must be positive' }
});
```

### Using the Base Exception

```typescript
import { BaseException } from 'jsm-exceptions';

// Custom exception extending BaseException
class CustomBusinessException extends BaseException {
  constructor(message: string, context?: Record<string, any>) {
    super(message, 422, context); // HTTP 422 Unprocessable Entity
  }
}

throw new CustomBusinessException('Business rule violation', {
  rule: 'maximum-orders-per-day',
  limit: 10,
  current: 11
});
```

## 📚 Available Exceptions

### 4xx Client Errors

| Exception | HTTP Code | Description |
|-----------|-----------|-------------|
| `BadRequestException` | 400 | Malformed or invalid requests |
| `UnauthorizedException` | 401 | Authentication required or invalid |
| `InvalidPasswordException` | 401 | Password authentication failures |
| `JWTTokenExpiredException` | 401 | Expired JWT tokens |
| `ForbiddenException` | 403 | Insufficient permissions |
| `ItemNotFoundException` | 404 | Resource not found |
| `NoItemsFoundException` | 404 | Search returned no results |
| `MethodNotAllowedException` | 405 | HTTP method not supported |
| `RequestTimeoutException` | 408 | Request timeouts |
| `ValueAlreadyExistsException` | 409 | Resource conflicts |
| `ValidationException` | 422 | Input validation failures |
| `UnprocessableEntityException` | 422 | Semantic errors |
| `TooManyRequestsException` | 429 | Rate limiting |

### 5xx Server Errors

| Exception | HTTP Code | Description |
|-----------|-----------|-------------|
| `InternalServerErrorException` | 500 | Unexpected server errors |
| `NotImplementedException` | 501 | Unimplemented features |
| `ServiceUnavailableException` | 503 | Temporary service unavailability |

### Special Cases

| Exception | HTTP Code | Description |
|-----------|-----------|-------------|
| `NoContentException` | 204 | Valid request with no response body |

## 🔧 Advanced Usage

### Validation with Joi

```typescript
import Joi from 'joi';
import { ValidationException } from 'jsm-exceptions';

const schema = Joi.object({
  email: Joi.string().email().required(),
  age: Joi.number().min(0).required()
});

try {
  const { error } = schema.validate(data);
  if (error) {
    throw new ValidationException('Validation failed', error);
  }
} catch (joiError) {
  // ValidationException automatically handles Joi.ValidationError
  throw new ValidationException('Input validation failed', joiError);
}
```

### Error Handling in Express.js

```typescript
import express from 'express';
import { BaseException, InternalServerErrorException } from 'jsm-exceptions';

const app = express();

// Error handling middleware
app.use((error: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
  if (error instanceof BaseException) {
    return res.status(error.status).json({
      success: false,
      error: {
        name: error.name,
        message: error.message,
        context: error.context,
        timestamp: error.timestamp
      }
    });
  }
  
  // Handle unexpected errors
  const internalError = new InternalServerErrorException('Unexpected error occurred');
  res.status(500).json({
    success: false,
    error: internalError.toJSON()
  });
});
```

### Rate Limiting Example

```typescript
import { TooManyRequestsException } from 'jsm-exceptions';

class RateLimiter {
  checkLimit(userId: string, limit: number, windowMs: number) {
    const requests = this.getUserRequests(userId, windowMs);
    
    if (requests.length >= limit) {
      throw new TooManyRequestsException('Rate limit exceeded', {
        userId,
        limit,
        windowMs,
        requestCount: requests.length,
        retryAfter: this.getRetryAfter(requests[0], windowMs)
      });
    }
  }
}
```

## 🔄 Migration from v1.x

The library maintains backward compatibility, but we recommend migrating to the new exception names:

```typescript
// ❌ Old (deprecated)
import { JsmException, InternalServerError, JWTTokenExpired } from 'jsm-exceptions';

// ✅ New (recommended)
import { BaseException, InternalServerErrorException, JWTTokenExpiredException } from 'jsm-exceptions';
```

Deprecated exports will show warnings in TypeScript and include `@deprecated` JSDoc annotations.

## 🎛️ Exception Properties

All exceptions extend `BaseException` and include:

```typescript
interface ExceptionProperties {
  name: string;              // Exception class name
  message: string;           // Error message
  status: number;            // HTTP status code
  context?: Record<string, any>; // Additional context
  timestamp: Date;           // When the exception was created
  stack?: string;            // Stack trace
}
```

### JSON Serialization

```typescript
const exception = new BadRequestException('Invalid input', { field: 'email' });

console.log(exception.toJSON());
// Output:
// {
//   name: 'BadRequestException',
//   message: 'Invalid input',
//   status: 400,
//   context: { field: 'email' },
//   timestamp: '2025-07-22T10:30:00.000Z',
//   stack: '...'
// }
```

## 📋 API Reference

### BaseException

The foundation class for all exceptions.

```typescript
class BaseException extends Error {
  constructor(message?: string, status?: number, context?: Record<string, any>)
  toJSON(): object
  toString(): string
}
```

### ValidationException

Special handling for validation errors with field-specific information.

```typescript
class ValidationException extends BaseException {
  fields: ErrorFields;
  isJoi: boolean;
  isMongoose: boolean;
  isCelebrate: boolean;
}
```

## 🤝 Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests for any improvements.

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🔗 Links

- [GitHub Repository](https://github.com/reevosolutions/jsm-exceptions)
- [npm Package](https://www.npmjs.com/package/jsm-exceptions)
- [Issue Tracker](https://github.com/reevosolutions/jsm-exceptions/issues)
