sseSource.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import { EventSourcePolyfill } from 'event-source-polyfill'
  2. import type { MercureEntityUpdate } from '~/types/interfaces'
  3. class SseSource {
  4. protected readonly url: URL
  5. protected readonly onOpen: () => void
  6. protected readonly onMessage: (eventData: MercureEntityUpdate) => void
  7. protected readonly onClose: () => void
  8. protected readonly withCredentials: boolean
  9. protected eventSource: EventSourcePolyfill | null = null
  10. constructor(
  11. mercureHubBaseUrl: string,
  12. topic: string,
  13. onOpen: () => void,
  14. onMessage: (eventData: MercureEntityUpdate) => void,
  15. onClose: () => void,
  16. withCredentials: boolean = true,
  17. ) {
  18. this.url = new URL(mercureHubBaseUrl)
  19. this.url.searchParams.append('topic', topic)
  20. this.onOpen = onOpen
  21. this.onMessage = onMessage
  22. this.onClose = onClose
  23. this.withCredentials = withCredentials
  24. }
  25. protected createEventSource(
  26. url: string,
  27. withCredentials: boolean,
  28. ): EventSourcePolyfill {
  29. return new EventSourcePolyfill(url, {
  30. withCredentials,
  31. heartbeatTimeout: 45 * 1000, // in ms
  32. })
  33. }
  34. public isConnected() {
  35. return (
  36. this.eventSource !== null &&
  37. this.eventSource.readyState === EventSourcePolyfill.OPEN
  38. )
  39. }
  40. public subscribe() {
  41. if (this.isConnected()) {
  42. throw new Error('SSE - Already subscribed to this event source')
  43. }
  44. if (import.meta.server) {
  45. throw new Error('SSE - Cannot subscribe on server side')
  46. }
  47. this.eventSource = this.createEventSource(
  48. this.url.toString(),
  49. this.withCredentials,
  50. )
  51. this.eventSource.onerror = (event) => {
  52. if (event.target.readyState === EventSourcePolyfill.CLOSED) {
  53. console.log('SSE closed')
  54. }
  55. this.eventSource!.close()
  56. }
  57. this.eventSource.onopen = () => {
  58. console.log('SSE - Listening for events...')
  59. this.onOpen()
  60. }
  61. this.eventSource.onmessage = (event) => {
  62. const data = JSON.parse(event.data)
  63. this.onMessage(data)
  64. }
  65. }
  66. public unsubscribe() {
  67. if (
  68. this.eventSource === null ||
  69. this.eventSource.readyState === EventSourcePolyfill.CLOSED
  70. ) {
  71. return
  72. }
  73. this.eventSource.close()
  74. this.onClose()
  75. console.log('SSE - Subscription closed')
  76. }
  77. }
  78. export default SseSource