@chapanda/mock-service
v3.0.0-beta.3
Published
mock service plugin for webpack and vite
Downloads
404
Readme
@chapanda/mock-service
Mock service plugin for Webpack and Vite, supporting HTTP, SSE (Server-Sent Events), and WebSocket protocols. Perfect for mocking Agent conversation scenarios.
Features
- ✅ HTTP Mock - Traditional REST API mocking
- ✅ SSE Mock - Server-Sent Events for streaming responses (OpenAI/Anthropic compatible)
- ✅ WebSocket Mock - Bidirectional real-time communication for Agent conversations
- ✅ Protocol Support - Built-in OpenAI and Anthropic streaming formats
- ✅ Custom Agent Protocol - Flexible custom event structure support
- ✅ Backward Compatible - Existing HTTP mock files work without changes
Installation
pnpm add @chapanda/mock-service
# or
npm install @chapanda/mock-serviceQuick Start
Webpack Configuration
const MockServiceWebpackPlugin = require('@chapanda/mock-service')
module.exports = {
plugins: [
new MockServiceWebpackPlugin({
source: path.resolve(__dirname, './mock'), // Required: mock directory path
port: 9009, // Optional: server port (default: 9009)
websocket: { // Optional: WebSocket configuration
enabled: true,
heartbeatInterval: 30000
},
sse: { // Optional: SSE configuration
enabled: true,
heartbeatInterval: 15000
}
})
]
}Vite Configuration
import mockServiceVite from '@chapanda/mock-service/vite'
export default {
plugins: [
mockServiceVite({
source: './mock', // Required: mock directory path
port: 9009 // Optional: server port (default: 9009)
})
]
}Mock File Formats
1. HTTP Mock (Backward Compatible)
// mock/api/users.js
// Old format - still works
module.exports = {
path: '/api/users',
methods: 'GET',
data: { users: [{ id: 1, name: 'John' }] }
}
// New format - explicit type
module.exports = {
type: 'http',
path: '/api/users',
methods: 'GET',
method: 'GET',
data: (req) => ({ id: Date.now(), ...req.body })
}2. SSE Mock (OpenAI Protocol)
// mock/api/chat.js
const { textDelta, done, usage } = require('@chapanda/mock-service/helpers')
module.exports = {
type: 'sse',
path: '/v1/chat/completions',
method: 'POST',
protocol: 'openai', // Use OpenAI streaming format
interval: 50, // Event interval in ms
// Static events array
events: [
textDelta('Hello'),
textDelta(' from Mock!'),
usage(100, 50),
done()
]
}3. SSE Mock (Anthropic Protocol)
// mock/api/messages.js
const { textDelta, thinkingDelta, done } = require('@chapanda/mock-service/helpers')
module.exports = {
type: 'sse',
path: '/v1/messages',
method: 'POST',
protocol: 'anthropic', // Use Anthropic streaming format
// Async generator for real streaming effect
events: async function* (req) {
const userMessage = req.body.messages?.slice(-1)[0]?.content
yield thinkingDelta('Processing...')
await delay(100)
const response = `You said: ${userMessage}`
for (const word of response.split(' ')) {
yield textDelta(word + ' ')
await delay(30)
}
yield done()
}
}
async function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}4. SSE Mock (Custom Agent Protocol)
// mock/api/agent.js
module.exports = {
type: 'sse',
path: '/api/agent/chat',
method: 'POST',
protocol: 'custom', // Custom protocol - returns raw StreamEvent JSON
events: async function* (req) {
const { prompt, agentType } = req.body
// Phase 1: Init
yield {
eventType: 'init',
sessionId: `session-${Date.now()}`,
turnId: `turn-${Date.now()}`
}
// Phase 2: Thinking
yield { eventType: 'thinking_delta', text: 'Analyzing...' }
// Phase 3: Tool calls
yield { eventType: 'tool_use_start', toolName: 'search', toolUseId: 'tool_1' }
yield { eventType: 'tool_progress', toolUseId: 'tool_1', toolInputSummary: '{"query":"..."}' }
yield { eventType: 'tool_use_end', toolUseId: 'tool_1' }
// Phase 4: Response
yield { eventType: 'text_delta', text: 'Here is the response...' }
// Done
yield { eventType: 'done' }
}
}5. WebSocket Mock
// mock/ws/chat.js
module.exports = {
type: 'websocket',
path: '/ws/chat',
heartbeatInterval: 30000, // 30s heartbeat
// Message handlers - key is message type
handlers: {
'message': async (ctx, data) => {
console.log('Received:', data)
// Send event directly
ctx.sendEvent({ eventType: 'text_delta', text: 'Processing...' })
// Return events array
return [
{ eventType: 'text_delta', text: `Echo: ${data.content}` },
{ eventType: 'done' }
]
},
'ping': (ctx, data) => {
return [{ eventType: 'pong', timestamp: Date.now() }]
},
// Default handler for unknown types
'*': (ctx, data) => {
return [{ eventType: 'error', error: 'Unknown message type' }]
}
},
// Connection lifecycle callbacks
onConnection: (ctx) => {
console.log('Client connected:', ctx.id)
ctx.send({ type: 'connected', connectionId: ctx.id })
ctx.data.sessionId = `session-${Date.now()}` // Store connection-level data
},
onClose: (ctx) => {
console.log('Client disconnected:', ctx.id)
}
}Helper Functions
Simplify event creation with built-in helpers:
const {
textDelta, // Create text delta event
thinkingDelta, // Create thinking delta event
toolUseStart, // Create tool use start event
toolProgress, // Create tool progress event
toolUseEnd, // Create tool use end event
done, // Create completion event
usage, // Create usage statistics event
error, // Create error event
delay, // Async delay helper
mockOpenAIChatCompletion, // Complete OpenAI response generator
mockAnthropicMessages, // Complete Anthropic response generator
mockAgentConversation // Complete Agent conversation generator
} = require('@chapanda/mock-service/helpers')
// Basic usage
textDelta('Hello world')
thinkingDelta('Processing request...')
toolUseStart('search_database', 'tool_123')
toolProgress('tool_123', '{"query":"users"}')
toolUseEnd('tool_123')
usage(150, 80, 0.002)
done()
// Advanced: Complete response generator
events: async function* (req) {
yield* mockOpenAIChatCompletion('Hello, how can I help?', {
includeThinking: true,
thinkingContent: 'Analyzing your question...',
toolCalls: [{
name: 'search',
args: '{"query":"help"}',
result: 'Found 10 results'
}]
})
}StreamEvent Types
Supported event types for custom Agent protocols:
text_delta- Text incrementthinking_delta- Thinking/reasoning incrementtool_use_start- Tool execution starttool_progress- Tool execution progresstool_use_end- Tool execution enderror- Error messagedone- Stream completionusage- Token usage statistics
WebSocket Context API
The ctx object in WebSocket handlers provides:
interface WebSocketContext {
id: string // Connection ID
send: (data: unknown) => void // Send raw JSON data
sendEvent: (event: StreamEvent) => void // Send stream event
close: (code?: number, reason?: string) => void // Close connection
data: Record<string, unknown> // Connection-level data storage
query: Record<string, string> // Query parameters from URL
headers: Record<string, string> // Request headers
}Testing
Test SSE with curl
curl -X POST http://localhost:9009/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"messages":[{"role":"user","content":"Hello"}]}' \
--no-bufferTest WebSocket with Node.js
const WebSocket = require('ws')
const ws = new WebSocket('ws://localhost:9009/ws/chat')
ws.on('open', () => {
ws.send(JSON.stringify({ type: 'message', content: 'Hello' }))
})
ws.on('message', (data) => {
console.log('Received:', JSON.parse(data))
})Examples
Check the mock-examples directory for complete working examples:
api/chat-openai.js- OpenAI Chat Completions streamingapi/chat-anthropic.js- Anthropic Messages streamingapi/agent-chat.js- Custom Agent protocolapi/users.js- HTTP mock (backward compatible)ws/chat.js- WebSocket Agent conversation
Playground
We provide an interactive playground to test SSE and WebSocket features:
# From project root
pnpm playground
# Or from playground directory
cd playground && pnpm devThen open http://localhost:3000 to see:
- OpenAI Chat Completions SSE streaming
- Anthropic Messages SSE streaming
- Custom Agent protocol SSE streaming
- WebSocket real-time communication
- HTTP mock (backward compatibility test)
The playground includes:
- 🎨 Beautiful UI with real-time streaming display
- 📊 Different event types highlighted with different colors
- 🔧 All features can be tested directly in the browser
- 📝 Mock files can be modified and tested immediately
License
MIT
Author
baiwusanyu-c
