Created
June 15, 2023 15:16
-
-
Save rabelloo/51eed4457180f5bad18c7f708741da85 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export {}; | |
/* | |
Taken from https://gist.github.com/sechel/e6aff22d9e56df02c5bd09c4afc516e6 | |
which is the basis for https://github.com/JSmith01/broadcastchannel-polyfill | |
but rewritten into TypeScript and class rather than JS with .prototype extension | |
*/ | |
const channels: Record<string, Set<BroadcastChannel>> = {}; | |
class BroadcastChannel { | |
readonly name: string; | |
private id: string; | |
private closed: boolean; | |
private mc: MessageChannel; | |
constructor(name: string) { | |
this.name = String(name); | |
this.id = `$BroadcastChannel$${this.name}$`; | |
(channels[this.id] ??= new Set()).add(this); | |
this.closed = false; | |
this.mc = new MessageChannel(); | |
this.mc.port1.start(); | |
this.mc.port2.start(); | |
globalThis.addEventListener('storage', (evt) => { | |
if (evt.storageArea !== globalThis.localStorage) return; | |
if (!evt.newValue) return; | |
if (evt.key?.substring(0, this.id.length) !== this.id) return; | |
this.mc.port2.postMessage(JSON.parse(evt.newValue)); | |
}); | |
} | |
// BroadcastChannel API | |
postMessage(message: unknown) { | |
if (this.closed) { | |
const error = new Error(); | |
error.name = 'InvalidStateError'; | |
throw error; | |
} | |
const value = JSON.stringify(message); | |
const key = `${this.id}${Date.now()}$${Math.random()}`; | |
// Broadcast to other contexts via storage events... | |
globalThis.localStorage.setItem(key, value); | |
setTimeout(() => globalThis.localStorage.removeItem(key)); | |
// Broadcast to current context via ports | |
channels[this.id].forEach((bc) => { | |
if (bc === this) return; | |
bc.mc.port2.postMessage(JSON.parse(value)); | |
}); | |
} | |
close() { | |
if (this.closed) return; | |
this.closed = true; | |
this.mc.port1.close(); | |
this.mc.port2.close(); | |
channels[this.id].delete(this); | |
} | |
// EventTarget API | |
get onmessage() { | |
return this.mc.port1.onmessage; | |
} | |
set onmessage(value) { | |
this.mc.port1.onmessage = value; | |
} | |
get onmessageerror() { | |
return ''; | |
} | |
addEventListener( | |
type: string, | |
listener: EventListenerOrEventListenerObject, | |
options?: boolean | AddEventListenerOptions, | |
) { | |
return this.mc.port1.addEventListener(type, listener, options); | |
} | |
removeEventListener( | |
type: string, | |
listener: EventListenerOrEventListenerObject, | |
options?: boolean | EventListenerOptions, | |
) { | |
return this.mc.port1.removeEventListener(type, listener, options); | |
} | |
dispatchEvent(event: Event) { | |
return this.mc.port1.dispatchEvent(event); | |
} | |
} | |
globalThis.BroadcastChannel = globalThis.BroadcastChannel || BroadcastChannel; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment