forked from defunkt/toes
60 lines
1.3 KiB
TypeScript
60 lines
1.3 KiB
TypeScript
import type { ToesEvent, ToesEventType } from '../shared/events'
|
|
|
|
export type { ToesEvent, ToesEventType }
|
|
|
|
type EventCallback = (event: ToesEvent) => void
|
|
|
|
interface Listener {
|
|
types: ToesEventType[]
|
|
callback: EventCallback
|
|
}
|
|
|
|
const _listeners = new Set<Listener>()
|
|
|
|
let _source: EventSource | undefined
|
|
|
|
function ensureConnection() {
|
|
if (_source) return
|
|
const url = `${process.env.TOES_URL}/api/events/stream`
|
|
_source = new EventSource(url)
|
|
|
|
_source.onerror = () => {
|
|
if (_source?.readyState === EventSource.CLOSED) {
|
|
console.warn('[toes] Event stream closed unexpectedly')
|
|
_source = undefined
|
|
}
|
|
}
|
|
|
|
_source.onmessage = (e) => {
|
|
try {
|
|
const event: ToesEvent = JSON.parse(e.data)
|
|
_listeners.forEach(l => {
|
|
if (l.types.includes(event.type)) l.callback(event)
|
|
})
|
|
} catch {
|
|
// Ignore malformed events
|
|
}
|
|
}
|
|
}
|
|
|
|
function closeConnection() {
|
|
if (_source) {
|
|
_source.close()
|
|
_source = undefined
|
|
}
|
|
}
|
|
|
|
export function on(type: ToesEventType | ToesEventType[], callback: EventCallback): () => void {
|
|
const listener: Listener = {
|
|
types: Array.isArray(type) ? type : [type],
|
|
callback,
|
|
}
|
|
_listeners.add(listener)
|
|
ensureConnection()
|
|
|
|
return () => {
|
|
_listeners.delete(listener)
|
|
if (_listeners.size === 0) closeConnection()
|
|
}
|
|
}
|