npm package: @hiennc24/audit
Description: Audit trail client SDK for BISO24 services
Latest version: 2.1.0

# @hiennc24/audit

Audit trail client SDK for BISO24 microservices.

## Installation

```bash
npm install @hiennc24/audit
```

## Usage

```typescript
import { AuditClient, sanitize } from '@hiennc24/audit'

// Initialize client (once per service)
const audit = new AuditClient({
  redisUrl: process.env.REDIS_URL,
  serviceName: 'svc-inventory',
  enabled: process.env.NODE_ENV !== 'test'
})

// Log audit event
await audit.log({
  action: 'UPDATE',
  entity: 'Product',
  entityId: productId,
  userId: meta.userId,
  orgIds: meta.orgIds,
  before: sanitize(originalProduct, ['password']),
  after: sanitize(updatedProduct, ['password']),
  metadata: { reason: 'Price adjustment' }
})
```

## Repository Interceptor (v2.1+)

**Zero-code audit integration** - Automatically audit all repository operations without manual logging.

### Quick Start

```typescript
import { AuditClient } from '@hiennc24/audit'

// 1. Initialize client
const auditClient = new AuditClient({
  redisUrl: process.env.REDIS_URL,
  serviceName: 'svc-inventory'
})

// 2. Wrap repository with interceptor (setup once)
const productRepo = auditClient.intercept(baseProductRepo, {
  entity: 'Product',
  extractContext: (meta) => ({
    userId: meta.userId,
    orgIds: meta.orgIds,
    domain: meta.domain
  })
})

// 3. Use normally - audit is automatic!
await productRepo.create(data, meta)              // ✅ Auto audited (CREATE)
await productRepo.update(id, updates, meta)       // ✅ Auto audited (UPDATE)
await productRepo.delete(id, meta)                // ✅ Auto audited (DELETE)
await productRepo.findByIdAndUpdate(id, data, meta) // ✅ Auto audited (UPDATE)

// Non-auditable methods pass through unchanged
const products = await productRepo.findAll()     // No audit
const count = await productRepo.count()          // No audit
```

### Supported Methods

| Method | Action | Before State Fetched |
|--------|--------|---------------------|
| `create`, `insertOne` | CREATE | No |
| `update`, `updateById`, `findByIdAndUpdate`, `findOneAndUpdate` | UPDATE | Yes |
| `delete`, `deleteById`, `remove`, `findByIdAndDelete`, `findByIdAndRemove` | DELETE | Yes |

### Configuration Options

```typescript
interface InterceptConfig<TMeta> {
  // Required
  entity: string                              // Entity name (e.g., 'Product')
  extractContext: (meta: TMeta) => AuditContext  // Extract userId, orgIds, domain

  // Optional
  idExtractor?: (args: any[], methodName: string) => string  // Custom ID extraction
  sensitiveFields?: string[]                  // Fields to sanitize (default: SENSITIVE_FIELDS)
  findByIdMethod?: string                     // Method to fetch before state (default: 'findById')
  methods?: Record<string, MethodConfig>      // Custom method configurations
  debug?: boolean                             // Enable verbose logging
}
```

### Advanced Example

```typescript
// Custom configuration
const orderRepo = auditClient.intercept(baseOrderRepo, {
  entity: 'Order',
  extractContext: (meta) => ({
    userId: meta.user?._id || 'system',
    orgIds: meta.orgIds || [],
    domain: meta.domain
  }),

  // Custom ID extraction for non-standard patterns
  idExtractor: (args, methodName) => {
    if (methodName === 'updateByOrderNumber') {
      return args[0].orderNumber
    }
    return args[0]
  },

  // Additional sensitive fields
  sensitiveFields: ['creditCard', 'cvv', 'apiKey'],

  // Use custom findById method
  findByIdMethod: 'findOne',

  // Debug mode for troubleshooting
  debug: process.env.DEBUG === 'true'
})
```

### Benefits

- **87% Code Reduction**: From 15 lines per operation to 2 lines one-time setup
- **Consistency**: Impossible to forget audit logging
- **Type-Safe**: Full TypeScript support with generics
- **Performance**: <50ms overhead per operation
- **Fire-and-Forget**: Audit failures don't break business logic

## API

### `AuditClient`

- `constructor(config)` - Initialize client
- `log(event)` - Publish audit event (async, fire-and-forget)
- `intercept(repository, config)` - Create intercepted repository with auto-audit (v2.1+)
- `close()` - Close Redis connection

### `sanitize(obj, fields)`

Remove sensitive fields from object before logging.

### `autoSanitize(obj)`

Auto-sanitize using common sensitive fields (password, apiKey, creditCard, etc.).

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `redisUrl` | string | required | Redis connection URL |
| `serviceName` | string | required | Service name (e.g., 'svc-inventory') |
| `enabled` | boolean | true | Enable/disable auditing |
| `streamName` | string | 'audit:events' | Redis Stream name |

## Features

- **Fire-and-forget**: Non-blocking, doesn't throw on errors
- **Idempotency**: Unique eventId for each event
- **Sanitizer**: Remove sensitive fields before logging
- **Lightweight**: Zero dependencies on svc-audit service
- **Type-safe**: Full TypeScript support

## Example: Sanitizing Data

```typescript
import { sanitize, autoSanitize, SENSITIVE_FIELDS } from '@hiennc24/audit'

// Manual sanitization
const sanitized = sanitize(user, ['password', 'ssn'])

// Auto-sanitize common fields
const sanitized = autoSanitize(user)

// Custom sensitive fields
const customSanitized = sanitize(data, [...SENSITIVE_FIELDS, 'customSecret'])
```

## Error Handling

The SDK follows a fire-and-forget pattern. Errors are logged to console but never thrown, ensuring audit logging doesn't impact application flow.

## License

MIT