Files
arbpulse/src/interfaces/sse/sse.ts
T
Mauricio Barragan 2e8744ccf0 Initial commit: Arb Pulse monolith with CI and optional Fly deploy.
Real-time BTC cross-exchange arbitrage detection (Kraken, Bybit, OKX, Binance)
with React dashboard, GitHub Actions CI, and documented Fly.io deploy workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 21:04:53 -06:00

51 lines
1.5 KiB
TypeScript

import type { Request, Response } from "express";
import { config } from "../../infrastructure/config/config.js";
import { createLogger } from "../../infrastructure/logging/logger.js";
import type { ApplicationService } from "../../composition/application-service.js";
const log = createLogger("sse");
export class SseHub {
private clients = new Set<Response>();
private timer: NodeJS.Timeout | null = null;
constructor(private readonly app: ApplicationService) {}
start(): void {
this.timer = setInterval(() => this.broadcast(), config.broadcastMs);
}
stop(): void {
if (this.timer) clearInterval(this.timer);
for (const res of this.clients) res.end();
this.clients.clear();
}
handle(req: Request, res: Response): void {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache, no-transform",
Connection: "keep-alive",
"X-Accel-Buffering": "no",
});
res.write(`retry: 2000\n\n`);
this.send(res, this.app.getSnapshot());
this.clients.add(res);
log.info(`client connected (${this.clients.size} total)`);
req.on("close", () => {
this.clients.delete(res);
});
}
private broadcast(): void {
if (this.clients.size === 0) return;
const snapshot = this.app.getSnapshot();
for (const res of this.clients) this.send(res, snapshot);
}
private send(res: Response, data: unknown): void {
res.write(`data: ${JSON.stringify(data)}\n\n`);
}
}