Skip to content
This repository was archived by the owner on Jun 18, 2025. It is now read-only.

Commit 08bf153

Browse files
authored
Merge pull request #5 from Rabrennie/reconnect
Update to reconnect when eventsource disconnects
2 parents 758170e + 1b5781b commit 08bf153

File tree

4 files changed

+36
-21
lines changed

4 files changed

+36
-21
lines changed

src/lib/room/SseClient.server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { EventEmitter } from 'node:events';
22
import type { RoomEvent, RoomEvents } from './events';
3+
import crypto from 'crypto';
34

45
export class SseClient {
56
eventEmitter: EventEmitter;
7+
id: string;
68

79
constructor() {
810
this.eventEmitter = new EventEmitter();
11+
this.id = crypto.randomBytes(16).toString('base64url');
912
}
1013

1114
send<T extends RoomEvent>(event: T, data: RoomEvents[T]) {

src/routes/room/[room]/+page.svelte

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
import SearchModal from './SearchModal.svelte';
99
import SpinnerModal from './SpinnerModal.svelte';
1010
import type { PageData } from './$types';
11-
import { browser } from '$app/environment';
1211
import { roomStore } from '$lib/stores/RoomStore';
1312
import { enhance } from '$app/forms';
13+
import { invalidateAll } from '$app/navigation';
14+
import { onMount } from 'svelte';
1415
1516
export let data: PageData;
1617
roomStore.set(data.room);
@@ -19,8 +20,7 @@
1920
2021
async function subscribe() {
2122
const RoomEventSource = (await import('$lib/room/RoomEventSource')).default;
22-
// TODO: handle reconnecting
23-
const sse = new RoomEventSource(`${window.location.pathname}/events`);
23+
let sse = new RoomEventSource(`${window.location.pathname}/events`);
2424
2525
sse.addEventListener('room:users:update', (event) => {
2626
console.log('myevent', event);
@@ -34,22 +34,26 @@
3434
eliminating = $roomStore?.choices[data.userId];
3535
});
3636
37-
return () => sse.close();
38-
}
3937
40-
if (browser) {
41-
subscribe();
38+
sse.onerror = async (ev) => {
39+
console.log(ev);
40+
sse.close();
41+
await invalidateAll();
42+
subscribe();
43+
};
4244
}
4345
46+
onMount(subscribe);
47+
4448
function onSpinnerComplete() {
45-
setTimeout(() => {
49+
setTimeout(() => {
4650
if (!eliminating) {
4751
return;
4852
}
4953
5054
roomStore.onEliminated({ userId: eliminating.userId });
5155
eliminating = undefined;
52-
}, 1000);
56+
}, 1000);
5357
}
5458
5559
$: winner = Object.values($roomStore?.choices ?? {}).find((c) => !c.eliminated);

src/routes/room/[room]/events/+server.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,34 @@ import type { RoomEvent } from '$lib/room/events';
66
export const GET = (async ({ params }) => {
77
const sseClient = new SseClient();
88

9-
rooms.set(params.room, [...(rooms.get(params.room) ?? []), sseClient]);
9+
rooms.set(params.room, { ...(rooms.get(params.room) ?? {}), [sseClient.id]: sseClient });
1010

1111
const stream = new ReadableStream({
1212
start(controller) {
1313
sseClient.eventEmitter.on('send', (event: RoomEvent, data) => {
14-
controller.enqueue(`event: ${event}\ndata:${JSON.stringify(data)}\n\n`);
14+
try {
15+
controller.enqueue(`event: ${event}\ndata:${JSON.stringify(data)}\n\n`);
16+
} catch {
17+
this.cancel?.();
18+
}
1519
});
1620

1721
sseClient.eventEmitter.on('delaySend', (event: RoomEvent, delay: number, data) => {
18-
setTimeout(
19-
() => controller.enqueue(`event: ${event}\ndata:${JSON.stringify(data)}\n\n`),
20-
delay
21-
);
22+
try {
23+
setTimeout(
24+
() =>
25+
controller.enqueue(`event: ${event}\ndata:${JSON.stringify(data)}\n\n`),
26+
delay
27+
);
28+
} catch {
29+
this.cancel?.();
30+
}
2231
});
2332
},
2433
cancel() {
25-
const clients = rooms.get(params.room) ?? [];
26-
const index = clients.indexOf(sseClient);
27-
if (index >= 0) {
28-
clients.splice(index, 1);
34+
const clients = rooms.get(params.room) ?? {};
35+
if (clients[sseClient.id]) {
36+
delete clients[sseClient.id];
2937
}
3038
}
3139
});
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import type { SseClient } from '$lib/room/SseClient.server';
22
import type { RoomEvent, RoomEvents } from '$lib/room/events';
33

4-
export const rooms = new Map<string, SseClient[]>();
4+
export const rooms = new Map<string, { [key: string]: SseClient }>();
55

66
export const roomsState = {
77
rooms,
88
broadcast<T extends RoomEvent>(roomId: string, event: T, data: RoomEvents[T]) {
9-
rooms.get(roomId)?.forEach((c) => c.send(event, data));
9+
Object.values(rooms.get(roomId) ?? {})?.forEach((c) => c.send(event, data));
1010
}
1111
};

0 commit comments

Comments
 (0)