sseSource.ts 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import { EventSourcePolyfill } from "event-source-polyfill";
  2. class SseSource {
  3. private readonly url: URL
  4. private readonly onOpen: (() => void)
  5. private readonly onMessage: ((eventData: Array<any>) => void)
  6. private readonly onClose: (() => void)
  7. private readonly withCredentials: boolean
  8. protected eventSource: EventSource | null = null
  9. constructor(
  10. mercureHubBaseUrl: string,
  11. topic: string,
  12. onOpen: (() => void),
  13. onMessage: ((eventData: Array<any>) => void),
  14. onClose: (() => void),
  15. withCredentials: boolean = true
  16. ) {
  17. this.url = new URL(mercureHubBaseUrl)
  18. this.url.searchParams.append('topic', topic)
  19. this.onOpen = onOpen
  20. this.onMessage = onMessage
  21. this.onClose = onClose
  22. this.withCredentials = withCredentials
  23. }
  24. protected createEventSource(url: string, withCredentials: boolean): EventSourcePolyfill {
  25. return new EventSourcePolyfill(
  26. url,
  27. {
  28. withCredentials: withCredentials,
  29. heartbeatTimeout: 45 * 1000 // in ms, timeout can not be disabled yet, so I set it very large instead
  30. }
  31. );
  32. }
  33. public isConnected () {
  34. return this.eventSource !== null && this.eventSource.readyState === EventSourcePolyfill.OPEN
  35. }
  36. public subscribe () {
  37. if (this.isConnected()) {
  38. throw new Error('SSE - Already subscribed to this event source')
  39. }
  40. if (process.server) {
  41. throw new Error('SSE - Cannot subscribe on server side')
  42. }
  43. this.eventSource = this.createEventSource(this.url.toString(), this.withCredentials)
  44. this.eventSource.onerror = (event) => {
  45. console.error('SSE - An error happened')
  46. }
  47. this.eventSource.onopen = () => {
  48. console.log('SSE - Listening for events...')
  49. this.onOpen()
  50. }
  51. this.eventSource.onmessage = event => {
  52. const data = JSON.parse(event.data)
  53. this.onMessage(data)
  54. }
  55. }
  56. public unsubscribe () {
  57. if (this.eventSource === null || this.eventSource.readyState === EventSource.CLOSED) {
  58. return
  59. }
  60. this.eventSource.close()
  61. this.onClose()
  62. console.log('SSE - Subscription closed')
  63. }
  64. }
  65. export default SseSource