feat: Implement Worker Service v2 with improved architecture

- Complete rewrite of the Worker Service following object-oriented principles.
- Introduced a single long-lived database connection to reduce overhead.
- Implemented event-driven queues to eliminate polling.
- Added DRY utilities for pagination and settings management.
- Reduced code size significantly from 1173 lines to approximately 600-700 lines.
- Created various composed services including DatabaseManager, SessionManager, and SDKAgent.
- Enhanced SSE broadcasting capabilities for real-time client updates.
- Established a structured approach for session lifecycle management and event handling.
- Introduced type safety with shared TypeScript interfaces for better maintainability.
This commit is contained in:
Alex Newman
2025-11-06 23:56:25 -05:00
parent 9eddc51979
commit 980151a50e
12 changed files with 3721 additions and 2 deletions
+83
View File
@@ -0,0 +1,83 @@
/**
* SSEBroadcaster: SSE client management
*
* Responsibility:
* - Manage SSE client connections
* - Broadcast events to all connected clients
* - Handle disconnections gracefully
* - Single-pass broadcast (no two-step cleanup)
*/
import type { Response } from 'express';
import { logger } from '../../utils/logger.js';
import type { SSEEvent, SSEClient } from '../worker-types.js';
export class SSEBroadcaster {
private sseClients: Set<SSEClient> = new Set();
/**
* Add a new SSE client connection
*/
addClient(res: Response): void {
this.sseClients.add(res);
logger.debug('WORKER', 'Client connected', { total: this.sseClients.size });
// Setup cleanup on disconnect
res.on('close', () => {
this.removeClient(res);
});
// Send initial event
this.sendToClient(res, { type: 'connected', timestamp: Date.now() });
}
/**
* Remove a client connection
*/
removeClient(res: Response): void {
this.sseClients.delete(res);
logger.debug('WORKER', 'Client disconnected', { total: this.sseClients.size });
}
/**
* Broadcast an event to all connected clients (single-pass)
*/
broadcast(event: SSEEvent): void {
if (this.sseClients.size === 0) {
return; // Short-circuit if no clients
}
const eventWithTimestamp = { ...event, timestamp: Date.now() };
const data = `data: ${JSON.stringify(eventWithTimestamp)}\n\n`;
// Single-pass write + cleanup
for (const client of this.sseClients) {
try {
client.write(data);
} catch (err) {
// Remove failed client immediately
this.sseClients.delete(client);
logger.debug('WORKER', 'Client removed due to write error');
}
}
}
/**
* Get number of connected clients
*/
getClientCount(): number {
return this.sseClients.size;
}
/**
* Send event to a specific client
*/
private sendToClient(res: Response, event: SSEEvent): void {
const data = `data: ${JSON.stringify(event)}\n\n`;
try {
res.write(data);
} catch (err) {
this.sseClients.delete(res);
}
}
}