2020-07-03 09:55:36 +00:00
|
|
|
class Source {
|
2024-12-15 21:02:32 +00:00
|
|
|
url: string;
|
|
|
|
eventSource: EventSource;
|
2024-12-21 18:59:25 +00:00
|
|
|
listening: Record<string, boolean>;
|
|
|
|
clients: Array<MessagePort>;
|
2024-12-15 21:02:32 +00:00
|
|
|
|
2024-12-21 18:59:25 +00:00
|
|
|
constructor(url: string) {
|
2020-07-03 09:55:36 +00:00
|
|
|
this.url = url;
|
|
|
|
this.eventSource = new EventSource(url);
|
|
|
|
this.listening = {};
|
|
|
|
this.clients = [];
|
|
|
|
this.listen('open');
|
2021-04-04 21:37:50 +00:00
|
|
|
this.listen('close');
|
2020-07-03 09:55:36 +00:00
|
|
|
this.listen('logout');
|
|
|
|
this.listen('notification-count');
|
2021-02-19 10:05:35 +00:00
|
|
|
this.listen('stopwatches');
|
2020-07-03 09:55:36 +00:00
|
|
|
this.listen('error');
|
|
|
|
}
|
|
|
|
|
2024-12-21 18:59:25 +00:00
|
|
|
register(port: MessagePort) {
|
2020-07-04 22:04:00 +00:00
|
|
|
if (this.clients.includes(port)) return;
|
2020-07-03 09:55:36 +00:00
|
|
|
|
|
|
|
this.clients.push(port);
|
|
|
|
|
|
|
|
port.postMessage({
|
|
|
|
type: 'status',
|
|
|
|
message: `registered to ${this.url}`,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-12-21 18:59:25 +00:00
|
|
|
deregister(port: MessagePort) {
|
2020-07-03 09:55:36 +00:00
|
|
|
const portIdx = this.clients.indexOf(port);
|
|
|
|
if (portIdx < 0) {
|
|
|
|
return this.clients.length;
|
|
|
|
}
|
|
|
|
this.clients.splice(portIdx, 1);
|
|
|
|
return this.clients.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
close() {
|
|
|
|
if (!this.eventSource) return;
|
|
|
|
|
|
|
|
this.eventSource.close();
|
|
|
|
this.eventSource = null;
|
|
|
|
}
|
|
|
|
|
2024-12-21 18:59:25 +00:00
|
|
|
listen(eventType: string) {
|
2020-07-03 09:55:36 +00:00
|
|
|
if (this.listening[eventType]) return;
|
|
|
|
this.listening[eventType] = true;
|
|
|
|
this.eventSource.addEventListener(eventType, (event) => {
|
2021-03-22 04:04:19 +00:00
|
|
|
this.notifyClients({
|
2020-07-03 09:55:36 +00:00
|
|
|
type: eventType,
|
2024-03-22 14:06:53 +00:00
|
|
|
data: event.data,
|
2020-07-03 09:55:36 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-12-21 18:59:25 +00:00
|
|
|
notifyClients(event: {type: string, data: any}) {
|
2020-07-03 09:55:36 +00:00
|
|
|
for (const client of this.clients) {
|
|
|
|
client.postMessage(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-21 18:59:25 +00:00
|
|
|
status(port: MessagePort) {
|
2020-07-03 09:55:36 +00:00
|
|
|
port.postMessage({
|
|
|
|
type: 'status',
|
|
|
|
message: `url: ${this.url} readyState: ${this.eventSource.readyState}`,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-21 18:59:25 +00:00
|
|
|
const sourcesByUrl: Map<string, Source | null> = new Map();
|
|
|
|
const sourcesByPort: Map<MessagePort, Source | null> = new Map();
|
|
|
|
|
|
|
|
// @ts-expect-error: typescript bug?
|
|
|
|
self.addEventListener('connect', (e: MessageEvent) => {
|
2020-07-03 09:55:36 +00:00
|
|
|
for (const port of e.ports) {
|
|
|
|
port.addEventListener('message', (event) => {
|
2022-08-03 19:58:27 +00:00
|
|
|
if (!self.EventSource) {
|
|
|
|
// some browsers (like PaleMoon, Firefox<53) don't support EventSource in SharedWorkerGlobalScope.
|
|
|
|
// this event handler needs EventSource when doing "new Source(url)", so just post a message back to the caller,
|
|
|
|
// in case the caller would like to use a fallback method to do its work.
|
|
|
|
port.postMessage({type: 'no-event-source'});
|
|
|
|
return;
|
|
|
|
}
|
2020-07-03 09:55:36 +00:00
|
|
|
if (event.data.type === 'start') {
|
|
|
|
const url = event.data.url;
|
2024-12-21 18:59:25 +00:00
|
|
|
if (sourcesByUrl.get(url)) {
|
2020-07-03 09:55:36 +00:00
|
|
|
// we have a Source registered to this url
|
2024-12-21 18:59:25 +00:00
|
|
|
const source = sourcesByUrl.get(url);
|
2020-07-03 09:55:36 +00:00
|
|
|
source.register(port);
|
2024-12-21 18:59:25 +00:00
|
|
|
sourcesByPort.set(port, source);
|
2020-07-03 09:55:36 +00:00
|
|
|
return;
|
|
|
|
}
|
2024-12-21 18:59:25 +00:00
|
|
|
let source = sourcesByPort.get(port);
|
2020-07-03 09:55:36 +00:00
|
|
|
if (source) {
|
|
|
|
if (source.eventSource && source.url === url) return;
|
|
|
|
|
|
|
|
// How this has happened I don't understand...
|
|
|
|
// deregister from that source
|
|
|
|
const count = source.deregister(port);
|
2020-07-04 14:01:25 +00:00
|
|
|
// Clean-up
|
2020-07-03 09:55:36 +00:00
|
|
|
if (count === 0) {
|
|
|
|
source.close();
|
2024-12-21 18:59:25 +00:00
|
|
|
sourcesByUrl.set(source.url, null);
|
2020-07-03 09:55:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Create a new Source
|
|
|
|
source = new Source(url);
|
|
|
|
source.register(port);
|
2024-12-21 18:59:25 +00:00
|
|
|
sourcesByUrl.set(url, source);
|
|
|
|
sourcesByPort.set(port, source);
|
2020-07-03 09:55:36 +00:00
|
|
|
} else if (event.data.type === 'listen') {
|
2024-12-21 18:59:25 +00:00
|
|
|
const source = sourcesByPort.get(port);
|
2020-07-03 09:55:36 +00:00
|
|
|
source.listen(event.data.eventType);
|
|
|
|
} else if (event.data.type === 'close') {
|
2024-12-21 18:59:25 +00:00
|
|
|
const source = sourcesByPort.get(port);
|
2020-07-03 09:55:36 +00:00
|
|
|
|
|
|
|
if (!source) return;
|
|
|
|
|
|
|
|
const count = source.deregister(port);
|
|
|
|
if (count === 0) {
|
|
|
|
source.close();
|
2024-12-21 18:59:25 +00:00
|
|
|
sourcesByUrl.set(source.url, null);
|
|
|
|
sourcesByPort.set(port, null);
|
2020-07-03 09:55:36 +00:00
|
|
|
}
|
|
|
|
} else if (event.data.type === 'status') {
|
2024-12-21 18:59:25 +00:00
|
|
|
const source = sourcesByPort.get(port);
|
2020-07-03 09:55:36 +00:00
|
|
|
if (!source) {
|
|
|
|
port.postMessage({
|
|
|
|
type: 'status',
|
|
|
|
message: 'not connected',
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
source.status(port);
|
|
|
|
} else {
|
|
|
|
// just send it back
|
|
|
|
port.postMessage({
|
|
|
|
type: 'error',
|
|
|
|
message: `received but don't know how to handle: ${event.data}`,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
port.start();
|
|
|
|
}
|
2021-08-17 05:32:48 +00:00
|
|
|
});
|