blob: 14e0cec379b520713de09a30677f9745425768a3 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
type EventHandler = (data: unknown) => void;
class WsClient {
private ws: WebSocket | null = null;
private handlers = new Map<string, Set<EventHandler>>();
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
private subscribedProjects = new Set<string>();
connect(): void {
if (this.ws?.readyState === WebSocket.OPEN) return;
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
this.ws = new WebSocket(`${protocol}//${location.host}/ws`);
this.ws.onopen = () => {
// Re-subscribe to projects
for (const project of this.subscribedProjects) {
this.send({ type: 'subscribe', project });
}
};
this.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
const handlers = this.handlers.get(data.type);
if (handlers) {
for (const handler of handlers) {
handler(data);
}
}
// Also fire wildcard handlers
const wildcardHandlers = this.handlers.get('*');
if (wildcardHandlers) {
for (const handler of wildcardHandlers) {
handler(data);
}
}
} catch {
// ignore
}
};
this.ws.onclose = () => {
this.reconnectTimer = setTimeout(() => this.connect(), 3000);
};
}
disconnect(): void {
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
this.ws?.close();
this.ws = null;
}
subscribe(project: string): void {
this.subscribedProjects.add(project);
this.send({ type: 'subscribe', project });
}
unsubscribe(project: string): void {
this.subscribedProjects.delete(project);
this.send({ type: 'unsubscribe', project });
}
on(event: string, handler: EventHandler): () => void {
if (!this.handlers.has(event)) {
this.handlers.set(event, new Set());
}
this.handlers.get(event)!.add(handler);
return () => this.handlers.get(event)?.delete(handler);
}
private send(data: unknown): void {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
}
}
export const wsClient = new WsClient();
|