# @lilith/websocket-client
WebSocket client library with React hooks for real-time features in the lilith-platform.
## Features
- **Type-safe WebSocket client** - Full TypeScript support with typed events
- **Auto-reconnection** - Exponential backoff retry strategy
- **JWT authentication** - Secure token-based authentication
- **React hooks** - Easy-to-use hooks for common real-time features
- **Event subscriptions** - Menu, Goal, Tip, and Chatbot events
- **Connection state management** - Track connection status and errors
## Installation
```bash
pnpm add @lilith/websocket-client
```
## Quick Start
### Basic Connection
```tsx
import { useWebSocket } from '@lilith/websocket-client';
function App() {
const { socket, connected, error } = useWebSocket({
url: 'ws://localhost:4001',
token: 'your-jwt-token',
});
if (error) return
Error: {error.message}
;
if (!connected) return Connecting...
;
return Connected!
;
}
```
## Hooks
### useWebSocket
Main connection hook. Manages WebSocket lifecycle.
```tsx
const { socket, client, connected, connecting, error } = useWebSocket({
url: 'ws://localhost:4001',
token: userToken,
reconnection: true,
reconnectionAttempts: Infinity,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
autoConnect: true,
});
```
**Parameters:**
- `url` (string, required) - WebSocket server URL
- `token` (string, optional) - JWT authentication token
- `reconnection` (boolean, default: true) - Enable auto-reconnection
- `reconnectionAttempts` (number, default: Infinity) - Max reconnection attempts
- `reconnectionDelay` (number, default: 1000) - Initial reconnection delay (ms)
- `reconnectionDelayMax` (number, default: 5000) - Max reconnection delay (ms)
- `autoConnect` (boolean, default: true) - Connect automatically on mount
**Returns:**
- `socket` (Socket | null) - Socket.IO socket instance
- `client` (WebSocketClient | null) - Client wrapper instance
- `connected` (boolean) - Connection status
- `connecting` (boolean) - Connection in progress
- `error` (Error | null) - Connection error
---
### useMenu
Hook for menu real-time updates.
```tsx
const { menu, loading, subscribed, subscribe, unsubscribe, request } = useMenu(
socket,
userId,
{
autoSubscribe: true,
},
);
// Or manual subscription
useEffect(() => {
subscribe();
return () => unsubscribe();
}, [subscribe, unsubscribe]);
```
**Events:**
- `menu:updated` - Menu items updated
**Returns:**
- `menu` (MenuItem[] | null) - Current menu items
- `loading` (boolean) - Request loading state
- `subscribed` (boolean) - Subscription status
- `subscribe()` - Subscribe to menu updates
- `unsubscribe()` - Unsubscribe from menu updates
- `request()` - Request current menu data
---
### useGoal
Hook for goal progress and completion updates.
```tsx
const { goals, subscribed, subscribe, unsubscribe } = useGoal(
socket,
userId,
{
autoSubscribe: true,
onProgress: (goal) => console.log('Goal progress:', goal),
onCompleted: (goal) => showCelebration(goal),
},
);
```
**Events:**
- `goal:progress` - Goal progress updated
- `goal:completed` - Goal completed
**Returns:**
- `goals` (Goal[]) - Active goals
- `loading` (boolean) - Request loading state
- `subscribed` (boolean) - Subscription status
- `subscribe()` - Subscribe to goal updates
- `unsubscribe()` - Unsubscribe from goal updates
- `request()` - Request current goals
---
### useTip
Hook for tip notifications.
```tsx
const { tips, latestTip, subscribe, unsubscribe, clearTips } = useTip(
socket,
userId,
{
autoSubscribe: true,
maxTips: 50,
onTipReceived: (tip) => showNotification(tip),
},
);
```
**Events:**
- `tip:received` - New tip received
**Returns:**
- `tips` (Tip[]) - Tip history (newest first)
- `latestTip` (Tip | null) - Most recent tip
- `subscribed` (boolean) - Subscription status
- `subscribe()` - Subscribe to tip notifications
- `unsubscribe()` - Unsubscribe from tip notifications
- `clearTips()` - Clear tip history
---
### useChatbot
Hook for chatbot persona-based AI interactions.
```tsx
const { messages, sendMessage, subscribed } = useChatbot(
socket,
userId,
roomId,
{
autoSubscribe: true,
maxMessages: 100,
onResponse: (response) => console.log('Bot says:', response.message),
onError: (error) => console.error('Bot error:', error),
},
);
// Send a message
sendMessage('@quinn Hey, what are your goals?');
```
**Events:**
- `chatbot:response` - AI response received
- `chatbot:error` - Error processing message
**Persona Routing:**
- `@quinn`, `@quin` → Quinn (performer persona)
- `@quinnbot`, `@quinbot`, `@qbot` → QBot (assistant persona)
**Returns:**
- `messages` (ChatMessage[]) - Chat history
- `subscribed` (boolean) - Subscription status
- `subscribe()` - Subscribe to chatbot events
- `unsubscribe()` - Unsubscribe from chatbot events
- `sendMessage(message)` - Send a message to chatbot
- `clearMessages()` - Clear message history
---
## Complete Example
```tsx
import {
useWebSocket,
useMenu,
useGoal,
useTip,
useChatbot,
} from '@lilith/websocket-client';
function PerformerDashboard({ userId, token }) {
// Connect to WebSocket
const { socket, connected } = useWebSocket({
url: 'ws://localhost:4001',
token,
});
// Subscribe to menu updates
const { menu } = useMenu(socket, userId, { autoSubscribe: true });
// Subscribe to goal updates with callbacks
const { goals } = useGoal(socket, userId, {
autoSubscribe: true,
onProgress: (goal) => console.log('Goal progress:', goal.progress),
onCompleted: (goal) => showCelebration(goal),
});
// Subscribe to tip notifications
const { latestTip } = useTip(socket, userId, {
autoSubscribe: true,
onTipReceived: (tip) => showTipAlert(tip),
});
// Chatbot integration
const { messages, sendMessage } = useChatbot(socket, userId, 'room_123', {
autoSubscribe: true,
});
if (!connected) return Connecting...
;
return (
Dashboard
Menu ({menu?.length || 0} items)
{menu?.map((item) => (
{item.title}
))}
Goals
{goals.map((goal) => (
{goal.title}: {goal.progress}%
))}
Latest Tip
{latestTip && (
{latestTip.tipperName} tipped {latestTip.amount} tokens!
)}
Chat with AI
{messages.map((msg) => (
{msg.sender === 'user' ? 'You' : msg.personaName}:{' '}
{msg.message}
))}
{
if (e.key === 'Enter') {
sendMessage(e.currentTarget.value);
e.currentTarget.value = '';
}
}}
placeholder="Type @quinn or @qbot..."
/>
);
}
```
## Messaging Namespaces (Stream 18)
### ChatNamespace (/chat)
Direct messaging with 1-on-1 and group chat support.
```typescript
import { SocketClient } from '@lilith/websocket-client'
const client = new SocketClient({
url: 'ws://localhost:4001',
auth: { userId: 'user_123' },
})
const chat = client.chat()
await chat.connect()
// Join a room
const response = await chat.joinRoom('room_abc')
// Send a message
await chat.sendMessage({
roomId: 'room_abc',
content: 'Hello!',
})
// Listen for messages
chat.onMessage((message) => {
console.log('New message:', message)
})
// Typing indicators
chat.onTyping((data) => {
console.log(`${data.userId} is typing...`)
})
chat.sendTyping('room_abc', true) // Start typing
chat.sendTyping('room_abc', false) // Stop typing
// Mark as read
await chat.markAsRead('message_id')
// Cleanup
chat.leaveRoom('room_abc')
chat.disconnect()
```
### BroadcastNamespace (/broadcast)
High-volume live chat for streams and broadcasts with SuperChat support.
```typescript
const broadcast = client.broadcast()
await broadcast.connect()
// Join broadcast
const response = await broadcast.joinBroadcast('stream_xyz')
console.log('Viewer count:', response.viewerCount)
// Send message
await broadcast.sendMessage({
roomId: 'stream_xyz',
content: 'Great stream!',
})
// Send SuperChat
await broadcast.sendMessage({
roomId: 'stream_xyz',
content: 'Amazing content!',
superChatAmount: 100,
superChatCurrency: 'USD',
})
// Listen for messages
broadcast.onMessage((message) => {
console.log('Chat:', message)
})
// Listen for SuperChats
broadcast.onSuperChat((data) => {
console.log(`SuperChat: $${data.amount} from ${data.message.senderId}`)
})
// Listen for viewer count
broadcast.onViewerCount((data) => {
console.log('Viewers:', data.count)
})
// Send emoji reaction
await broadcast.sendEmoji('stream_xyz', '❤️')
// React to message
broadcast.sendReaction('stream_xyz', 'message_id', '👍')
// Vote in poll
broadcast.sendPollVote('stream_xyz', 'poll_id', 'option_a')
// Cleanup
broadcast.leaveBroadcast('stream_xyz')
broadcast.disconnect()
```
### SocketClient API
```typescript
const client = new SocketClient(config)
// Namespace accessors
const chatNamespace = client.chat()
const broadcastNamespace = client.broadcast()
// Disconnect all namespaces
client.disconnectAll()
```
## Direct Client Usage (No React)
```typescript
import { WebSocketClient } from '@lilith/websocket-client';
const client = new WebSocketClient({
url: 'ws://localhost:4001',
token: 'your-jwt-token',
});
const socket = client.getSocket();
// Subscribe to menu updates
socket?.emit('menu:subscribe', { userId: 'user_123' });
socket?.on('menu:updated', (data) => {
console.log('Menu updated:', data.menu);
});
// Cleanup
client.disconnect();
```
## Type Definitions
All events and payloads are fully typed. Import types as needed:
```typescript
import type {
MenuItem,
Goal,
Tip,
ChatbotResponsePayload,
MenuUpdatedPayload,
GoalProgressPayload,
} from '@lilith/websocket-client';
```
## Development
```bash
# Type check
pnpm typecheck
# Build
pnpm build
# Test
pnpm test
# Lint
pnpm lint
```
## Architecture
This library wraps Socket.IO client and provides:
1. **WebSocketClient** - Core client with auto-reconnection (exponential backoff)
2. **React Hooks** - State management and event handling
3. **Type Safety** - Full TypeScript definitions for all events
4. **Developer Experience** - Simple API, sensible defaults, cleanup handling
## Troubleshooting
### Connection Issues
```tsx
const { error } = useWebSocket({ url: 'ws://localhost:4001', token });
if (error) {
console.error('Connection error:', error.message);
// Common issues:
// - WebSocket service not running
// - Invalid JWT token
// - CORS configuration
// - Firewall blocking port 4001
}
```
### Subscriptions Not Working
```tsx
// Make sure socket is connected before subscribing
const { socket, connected } = useWebSocket({ ... });
const { subscribe } = useMenu(socket, userId);
useEffect(() => {
if (connected) {
subscribe();
}
}, [connected, subscribe]);
```
### Missing Events
Check that you're subscribed to the correct userId/roomId and that the WebSocket service is emitting to the correct rooms.
## License
Private - Part of lilith-platform monorepo