blob: 158443d4fc9ab937ec07880c4bdbd80fff1c0aea (
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
|
/**
* src/adapters/ws.ts — injected browser effect: WebSocket client.
*
* Connects to `/ws` (same origin — the Vite proxy forwards to the backend).
* Exposes a typed subscribe/unsubscribe API + an async iterable of server
* messages. The pure download-status reducer consumes these; this holds no
* business logic.
*/
import type { JobId, WsServerMessage } from "@yt-music/contract";
export interface WsClient {
connect(): void;
subscribe(jobId: JobId): void;
unsubscribe(jobId: JobId): void;
onMessage(cb: (msg: WsServerMessage) => void): () => void;
close(): void;
}
export function createWsClient(wsUrl: string): WsClient {
let ws: WebSocket | null = null;
const listeners = new Set<(msg: WsServerMessage) => void>();
return {
connect() {
ws = new WebSocket(wsUrl);
ws.onmessage = (ev) => {
try {
const msg = JSON.parse(ev.data as string) as WsServerMessage;
for (const cb of listeners) cb(msg);
} catch {
// ignore malformed
}
};
},
subscribe(jobId) {
ws?.send(JSON.stringify({ type: "subscribe", jobId }));
},
unsubscribe(jobId) {
ws?.send(JSON.stringify({ type: "unsubscribe", jobId }));
},
onMessage(cb) {
listeners.add(cb);
return () => listeners.delete(cb);
},
close() {
ws?.close();
ws = null;
},
};
}
|