npm package: @gulibs/tegg-socket.io
Description: Socket.IO plugin for Egg.js with decorator-based routing and first-class TypeScript support
Author: gulibs
Latest version: 0.0.20

# @gulibs/tegg-socket.io

Socket.IO plugin for Egg.js 4.x with decorator-based routing and first-class TypeScript support.

## Features

- ✅ **Decorator-Based Routing** - Modern decorator pattern inspired by Tegg HTTPController
- ✅ **TypeScript Support** - Full TypeScript type definitions with zero config
- ✅ **Middleware System** - Connection and packet middleware with Koa-style composition
- ✅ **Controller System** - Event-based controller routing with auto-discovery
- ✅ **Namespace Management** - Multi-namespace support with per-namespace middleware
- ✅ **Redis Adapter** - Optional Redis adapter for cluster mode
- ✅ **Helper Decorators** - @Room, @Broadcast, @Subscribe for common Socket.IO patterns
- ✅ **Performance Monitoring** - @PerformanceMonitor decorator for metrics collection
- ✅ **Rate Limiting** - @RateLimit decorator for request throttling
- ✅ **Message Storage** - @MessageStorage decorator for database persistence
- ✅ **Backward Compatible** - Works with both decorator and traditional routing

## Requirements

- Node.js >= 22.18.0
- Egg.js >= 4.1.0-beta.35

## Install

```bash
npm i @gulibs/tegg-socket.io
# or
pnpm add @gulibs/tegg-socket.io
```

## Quick Start

### 1. Enable Plugin

```typescript
// config/plugin.ts
import socketIOPlugin from '@gulibs/tegg-socket.io';

export default {
  ...socketIOPlugin(),
};
```

### 2. Create Controller with Decorators

```typescript
// app/io/controller/chat.ts
import { SocketIOController, SocketIOEvent, Room, Broadcast } from '@gulibs/tegg-socket.io';
import { Context } from 'egg';
import { AuthMiddleware } from '../middleware/auth';

@SocketIOController({
  namespace: '/',
  connectionMiddleware: [AuthMiddleware], // Type-safe class reference
})
export default class ChatController {
  @SocketIOEvent({ event: 'message' })
  async handleMessage(ctx: Context) {
    const msg = ctx.args![0];
    ctx.socket!.emit('response', `Received: ${msg}`);
  }

  @SocketIOEvent({ event: 'joinRoom' })
  @Room({ name: 'lobby' })
  async joinLobby(ctx: Context) {
    ctx.socket!.emit('joined', 'Welcome to lobby!');
  }

  @SocketIOEvent({ event: 'broadcast' })
  @Broadcast({ to: 'lobby' })
  async broadcastMessage(ctx: Context) {
    return { text: ctx.args![0], from: ctx.socket!.id };
  }
}
```

### 3. Create Middleware with Decorators

```typescript
// app/io/middleware/auth.ts
import { ConnectionMiddleware } from '@gulibs/tegg-socket.io';
import type { Context } from 'egg';

@ConnectionMiddleware({ priority: 10 })
export class AuthMiddleware {
  async use(ctx: Context, next: () => Promise<void>) {
    const token = ctx.socket!.handshake.query.token as string;
    if (!token || token.length < 6) {
      ctx.socket!.emit('error', 'Authentication required');
      ctx.socket!.disconnect();
      return;
    }
    ctx.state.user = { id: 'user123', token };
    await next();
  }
}
```

### 4. Configure (Optional)

```typescript
// config/config.default.ts
export default {
  teggSocketIO: {
    // Optional: Redis adapter for cluster mode
    redis: {
      host: '127.0.0.1',
      port: 6379,
    },
    // Optional: Engine.IO options
    init: {
      pingTimeout: 60000,
    },
  },
};
```

## Decorator API Reference

### @SocketIOController

Marks a class as a Socket.IO controller and configures its namespace and middleware.

```typescript
@SocketIOController({
  namespace?: string;                        // Default: '/'
  connectionMiddleware?: Array<Constructor | string>; // Middleware classes or names
  packetMiddleware?: Array<Constructor | string>;     // Middleware classes or names
})
```

**Example:**

```typescript
import { AuthMiddleware, AdminAuthMiddleware } from '../middleware';

@SocketIOController({
  namespace: '/admin',
  connectionMiddleware: [AuthMiddleware, AdminAuthMiddleware],
  packetMiddleware: ['log', 'validate'],
})
export default class AdminController {
  // ...
}
```

### @SocketIOEvent

Marks a method as a Socket.IO event handler.

```typescript
@SocketIOEvent({
  event: string;                   // Event name (required)
  packetMiddleware?: string[];     // Event-specific middleware
})
```

### @ConnectionMiddleware

Marks a class as connection-level middleware.

```typescript
@ConnectionMiddleware({
  priority?: number;  // Default: 100 (lower executes first)
})
```

### @PacketMiddleware

Marks a class as packet-level middleware.

```typescript
@PacketMiddleware({
  priority?: number;  // Default: 100
})
```

### @Room

Automatically joins socket to a room before method execution.

```typescript
@Room({
  name: string | ((ctx: Context) => string | Promise<string>);
  autoLeave?: boolean;  // Default: false
})
```

### @Broadcast

Automatically broadcasts method return value to specified rooms.

```typescript
@Broadcast({
  to?: string | string[];     // Target room(s)
  event?: string;             // Custom event name
  includeSelf?: boolean;      // Default: false
})
```

### @PerformanceMonitor

Monitors method performance metrics.

```typescript
@PerformanceMonitor({
  enabled?: boolean;        // Default: true
  sampleRate?: number;      // Default: 1.0 (0.0 to 1.0)
  metrics?: PerformanceMetric[];
  logMetrics?: boolean;     // Default: false
})
```

### @RateLimit

Limits the number of requests per time window.

```typescript
@RateLimit({
  max?: number;            // Default: 10
  window?: number | string; // Default: 60000 or '1m'
  key?: 'socket' | 'user' | 'ip' | ((ctx: Context) => string);
  message?: string;
})
```

### @MessageStorage

Automatically saves messages to database.

```typescript
@MessageStorage({
  adapter?: 'mysql' | 'mongodb' | 'redis';
  table?: string;
  enabled?: boolean;
  events?: string[];
  excludeEvents?: string[];
})
```

### @Subscribe

Subscribes to Socket.IO system events.

```typescript
@Subscribe({
  event: 'connect' | 'disconnect' | 'disconnecting' | 'error';
})
```

## Configuration

### Redis Adapter (Cluster Mode)

```typescript
teggSocketIO: {
  redis: {
    host: '127.0.0.1',
    port: 6379,
    auth_pass: 'password',
    db: 0,
  },
}
```

### Engine.IO Options

```typescript
teggSocketIO: {
  init: {
    // Options passed to engine.io
    // See: https://socket.io/docs/v4/server-api/#new-serveroptions
  },
}
```

### Message Storage

Enable message persistence:

```typescript
teggSocketIO: {
  messageStorage: {
    enabled: true, // Must be enabled to use @MessageStorage decorator
  },
}
```

### Connection Limit

```typescript
teggSocketIO: {
  connectionLimit: {
    maxConnections: 1000,
    message: 'Connection limit exceeded',
  },
}
```

## Deployment

### Cluster Mode

Socket.IO requires sticky sessions in cluster mode:

```bash
egg-bin dev --sticky
egg-scripts start --sticky
```

### Nginx Configuration

```nginx
location / {
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_pass http://127.0.0.1:7001;
}
```

## API Reference

### `app.io`

Socket.IO server instance.

```typescript
// Get Socket.IO server
const io = app.io;

// Get namespace
const nsp = app.io.of('/');

// Emit to all clients
app.io.emit('broadcast', data);
```

### `ctx.socket`

Socket.IO socket instance available in middleware and controllers.

```typescript
// Emit event to client
ctx.socket.emit('event', data);

// Join room
ctx.socket.join('room');

// Leave room
ctx.socket.leave('room');

// Disconnect
ctx.socket.disconnect();
```

## Migration from Old Plugin

If you're migrating from the old `@gulibs/tegg-socket.io` (pre-Egg.js 4.x):

1. **Update plugin configuration**:
   ```typescript
   // Old (Egg.js 3.x)
   export default {
     teggSocketIO: {
       enable: true,
       package: '@gulibs/tegg-socket.io',
     },
   };

   // New (Egg.js 4.x)
   import socketIOPlugin from '@gulibs/tegg-socket.io';
   export default {
     ...socketIOPlugin(),
   };
   ```

2. **Update config format**:
   ```typescript
   // Old
   export default () => {
     const config = {};
     config.teggSocketIO = { /* ... */ };
     return config;
   };

   // New
   export default {
     teggSocketIO: { /* ... */ },
   };
   ```

3. **Decorators and controllers remain the same** - No changes needed!

## License

MIT

## Support

- [GitHub Issues](https://github.com/gulibs/tegg-socket.io/issues)
- [Documentation](https://github.com/gulibs/tegg-socket.io)