{"id":13447014,"url":"https://github.com/pubkey/broadcast-channel","last_synced_at":"2025-05-11T03:38:22.479Z","repository":{"id":32624879,"uuid":"135900664","full_name":"pubkey/broadcast-channel","owner":"pubkey","description":":satellite: BroadcastChannel to send data between different browser-tabs or nodejs-processes :satellite: + LeaderElection over the channels  https://pubkey.github.io/broadcast-channel/","archived":false,"fork":false,"pushed_at":"2025-05-06T17:38:18.000Z","size":4164,"stargazers_count":1905,"open_issues_count":11,"forks_count":126,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-05-08T23:44:51.216Z","etag":null,"topics":["broadcast","broadcast-channel","broker","channels","client-side","communication-channel","cookie","crosstab","indexeddb","ipc","leader-election","localforage","localstorage","messaging","offline","polyfill","postmessage","shared","shim","stream"],"latest_commit_sha":null,"homepage":"https://pubkey.github.io/broadcast-channel/","language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pubkey.png","metadata":{"files":{"readme":".github/README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"pubkey","custom":["https://rxdb.info/consulting"]}},"created_at":"2018-06-03T12:06:23.000Z","updated_at":"2025-05-07T09:04:15.000Z","dependencies_parsed_at":"2023-10-23T12:35:22.548Z","dependency_job_id":"51657934-efdb-42da-99b3-5935b7028f49","html_url":"https://github.com/pubkey/broadcast-channel","commit_stats":{"total_commits":1374,"total_committers":29,"mean_commits":47.37931034482759,"dds":"0.42649199417758366","last_synced_commit":"0edf845aa4f720652dbd12c13db0d56f554291f0"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pubkey%2Fbroadcast-channel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pubkey%2Fbroadcast-channel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pubkey%2Fbroadcast-channel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pubkey%2Fbroadcast-channel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pubkey","download_url":"https://codeload.github.com/pubkey/broadcast-channel/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253514439,"owners_count":21920330,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["broadcast","broadcast-channel","broker","channels","client-side","communication-channel","cookie","crosstab","indexeddb","ipc","leader-election","localforage","localstorage","messaging","offline","polyfill","postmessage","shared","shim","stream"],"created_at":"2024-07-31T05:01:05.922Z","updated_at":"2025-05-11T03:38:22.448Z","avatar_url":"https://github.com/pubkey.png","language":"JavaScript","funding_links":["https://github.com/sponsors/pubkey","https://rxdb.info/consulting"],"categories":["JavaScript"],"sub_categories":[],"readme":"\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/pubkey/broadcast-channel\"\u003e\n    \u003cimg src=\"../docs/files/icon.png\" width=\"150px\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eBroadcastChannel\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eA BroadcastChannel to send data across multiple browser-tabs or nodejs-processes or Deno-Deploy-Instances\u003c/strong\u003e\n  \u003cbr/\u003e\n  \u003cspan\u003e+ LeaderElection over the channels\u003c/span\u003e\u003cbr /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://twitter.com/pubkeypubkey\"\u003e\n        \u003cimg src=\"https://img.shields.io/twitter/follow/pubkeypubkey.svg?style=social\u0026logo=twitter\"\n            alt=\"follow on Twitter\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n![demo.gif](../docs/files/demo.gif)\n\n* * *\n\nA BroadcastChannel that allows you to send data between different browser-tabs or nodejs-processes or Deno-Deploy-Instances.\n\n- It works completely **client-side** and **offline**,\n- Tested on **old browsers**, **new browsers**, **WebWorkers**, **Iframes** and **NodeJs**.\n\nThis behaves similar to the [BroadcastChannel-API](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API) which is currently only featured in [some browsers](https://caniuse.com/#feat=broadcastchannel).\n\n\n# Sponsored by\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://rxdb.info/?utm_source=github\u0026utm_medium=repo\u0026utm_campaign=github-broadcast-channel-readme\"\u003e\n        \u003cimg\n            src=\"https://rxdb.info/files/logo/logo_text.svg\"\n            alt=\"JavaScript Database\"\n            width=\"300\"\n         /\u003e\n         \u003cbr /\u003e\n         \u003cbr /\u003e\n         \u003cspan\u003eThe \u003cb\u003eJavaScript Database\u003c/b\u003e\u003c/span\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n## Using the BroadcastChannel\n\n```bash\nnpm install --save broadcast-channel\n```\n\n#### Create a channel in one tab/process and send a message\n\n```ts\nimport { BroadcastChannel } from 'broadcast-channel';\nconst channel = new BroadcastChannel('foobar');\nchannel.postMessage('I am not alone');\n```\n\n#### Create a channel with the same name in another tab/process and recieve messages\n\n```ts\nimport { BroadcastChannel } from 'broadcast-channel';\nconst channel = new BroadcastChannel('foobar');\nchannel.onmessage = msg =\u003e console.dir(msg);\n// \u003e 'I am not alone'\n```\n\n\n#### Add and remove multiple eventlisteners\n\n```ts\nimport { BroadcastChannel } from 'broadcast-channel';\nconst channel = new BroadcastChannel('foobar');\n\nconst handler = msg =\u003e console.log(msg);\nchannel.addEventListener('message', handler);\n\n// remove it\nchannel.removeEventListener('message', handler);\n```\n\n#### Close the channel if you do not need it anymore\nReturns a `Promise` which is resolved when everything is processed.\n\n```js\nawait channel.close();\n```\n\n#### Set options when creating a channel (optional)\n\n```js\nconst options = {\n    type: 'localstorage', // (optional) enforce a type, oneOf['native', 'idb', 'localstorage', 'node']\n    webWorkerSupport: true; // (optional) set this to false if you know that your channel will never be used in a WebWorker (increases performance)\n};\nconst channel = new BroadcastChannel('foobar', options);\n```\n\n#### Create a typed channel in typescript\n\n```typescript\nimport { BroadcastChannel } from 'broadcast-channel';\ndeclare type Message = {\n  foo: string;\n};\nconst channel: BroadcastChannel\u003cMessage\u003e = new BroadcastChannel('foobar');\nchannel.postMessage({\n  foo: 'bar'\n});\n```\n\n#### Enforce options globally\n\nWhen you use this module in a test-suite, it is recommended to enforce the fast `simulate` method on all channels so your tests run faster. You can do this with `enforceOptions()`. If you set this, all channels have the enforced options, no mather what options are given in the constructor.\n\n```typescript\nimport { enforceOptions } from 'broadcast-channel';\n\n// enforce this config for all channels\nenforceOptions({\n  type: 'simulate'\n});\n\n// reset the enforcement\nenforceOptions(null);\n```\n\n\n#### Clear tmp-folder\n\nWhen used in NodeJs, the BroadcastChannel will communicate with other processes over filesystem sockets.\nWhen you create a huge amount of channels, like you would do when running unit tests, you might get problems because there are too many folders in the tmp-directory. Calling `BroadcastChannel.clearNodeFolder()` will clear the tmp-folder. It is recommended to run this at the beginning of your test-suite.\n\n```typescript\nimport { clearNodeFolder } from 'broadcast-channel';\n// jest\nbeforeAll(async () =\u003e {\n  const hasRun = await clearNodeFolder();\n  console.log(hasRun); // \u003e true on NodeJs, false on Browsers\n})\n```\n\n```typescript\nimport { clearNodeFolder } from 'broadcast-channel';\n// mocha\nbefore(async () =\u003e {\n  const hasRun = await clearNodeFolder();\n  console.log(hasRun); // \u003e true on NodeJs, false on Browsers\n})\n```\n\n#### Handling IndexedDB onclose events\n\nIndexedDB databases can close unexpectedly for various reasons. This could happen, for example, if the underlying storage is removed or if a user clears the database in the browser's history preferences. Most often we have seen this happen in Mobile Safari. By default, we let the connection close and stop polling for changes. If you would like to continue listening you should close BroadcastChannel and create a new one.\n\nExample of how you might do this:\n\n```typescript\nimport { BroadcastChannel } from 'broadcast-channel';\n\nlet channel;\n\nconst createChannel = () =\u003e {\n  channel = new BroadcastChannel(CHANNEL_NAME, {\n    idb: {\n      onclose: () =\u003e {\n        // the onclose event is just the IndexedDB closing.\n        // you should also close the channel before creating\n        // a new one.\n        channel.close();\n        createChannel();\n      },\n    },\n  });\n\n  channel.onmessage = message =\u003e {\n    // handle message\n  };\n};\n```\n\n## Methods\n\nDepending in which environment this is used, a proper method is automatically selected to ensure it always works.\n\n| Method           | Used in                                                         | Description                                                                                                                                             |\n| ---------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Native**       | [Modern Browsers](https://caniuse.com/broadcastchannel)         | If the browser supports the BroadcastChannel-API, this method will be used because it is the fastest. This is also used in Deno runtimes.               |\n| **IndexedDB**    | [Browsers with WebWorkers](https://caniuse.com/#feat=indexeddb) | If there is no native BroadcastChannel support, the IndexedDB method is used because it supports messaging between browser-tabs, iframes and WebWorkers |\n| **LocalStorage** | [Older Browsers](https://caniuse.com/#feat=namevalue-storage)   | In older browsers that do not support IndexedDb, a localstorage-method is used                                                                          |\n| **Sockets**      | NodeJs                                                          | In NodeJs the communication is handled by sockets that send each other messages                                                                         |\n| **Simulate**      | none per default                                                          | This method simulates the behavior of the other methods but only runs in the current process without sharing data between processes. Use this method in your test-suite because it is much faster.                                                                  |\n\n\n\n## Using the LeaderElection\n\nThis module also comes with a leader-election which can be used to elect a leader between different BroadcastChannels.\nFor example if you have a stable connection from the frontend to your server, you can use the LeaderElection to save server-side performance by only connecting once, even if the user has opened your website in multiple tabs.\n\nIn the background it will use the [Web Locks API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Locks_API) if possible and fall back to a message-based election algorithm if WebLocks is not available.\n\n\nIn this example the leader is marked with the crown ♛:\n![leader-election.gif](../docs/files/leader-election.gif)\n\n\nCreate a channel and an elector:\n\n```ts\nimport {\n  BroadcastChannel,\n  createLeaderElection\n} from 'broadcast-channel';\nconst channel = new BroadcastChannel('foobar');\nconst elector = createLeaderElection(channel);\n```\n\nWait until the elector becomes leader:\n\n```js\nimport { createLeaderElection } from 'broadcast-channel';\nconst elector = createLeaderElection(channel);\nelector.awaitLeadership().then(()=\u003e {\n  console.log('this tab is now leader');\n});\n```\n\nCheck if there is a leader at this point in time. `hasLeader()` returns true when there is a leader. It returns false, if the leader is dead. Then it returns true again when a new leader is elected.\n\n```js\nconst elector = createLeaderElection(channel);\nconst hasLeader = await elector.hasLeader();\nconsole.log('leader exists: ' + hasLeader);\n```\n\nIf more than one tab is becoming leader adjust `LeaderElectionOptions` configuration.\n\n```js\nimport { createLeaderElection } from 'broadcast-channel';\nconst elector = createLeaderElection(channel, {\n  fallbackInterval: 2000, // optional configuration for how often will renegotiation for leader occur\n  responseTime: 1000, // optional configuration for how long will instances have to respond\n});\nelector.awaitLeadership().then(()=\u003e {\n  console.log('this tab is now leader');\n})\n```\n\nLet the leader die. (automatically happens if a tab is closed or the process exits).\n\n```js\nconst elector = createLeaderElection(channel);\nawait elector.die();\n```\n\n### Handle duplicate leaders\n\nDuplicate leadership can happen on rare occurences like when the [CPU is on 100%](https://web.archive.org/web/20201221051328/https://github.com/pubkey/broadcast-channel/issues/385) for longer time, or the browser [has throttled the javascript timers](https://web.archive.org/web/20201221051312/https://github.com/pubkey/broadcast-channel/issues/414).\n\n```js\nconst elector = createLeaderElection(channel);\nelector.onduplicate = () =\u003e {\n  alert('have duplicate leaders!');\n}\n```\n\n\n## What this is\n\nThis module is optimised for:\n\n- **low latency**: When you `postMessage` to a channel, it will be delivered to other channels as soon as possible,\n- **lossless**: When you send a message, it should be impossible that the message is lost before other channels recieved it,\n- **low idle workload**: During the time when no messages are send, there should be a low processor footprint.\n\n## What this is not\n\n-   This is not a polyfill. Do not set this module to `window.BroadcastChannel`. This implementation behaves similiar to the [BroadcastChannel-Standard](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API) with these limitations:\n    - You can only send data that can be `JSON.stringify`-ed,\n    - While the offical API emits [onmessage-events](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel/onmessage), this module directly emitts the data which was posted.\n-   This is not a replacement for a message queue. If you use this in NodeJs or Deno and want send more than 50 messages per second, you should use proper [IPC-Tooling](https://en.wikipedia.org/wiki/Message_queue).\n\n\n## Browser Support\nI have tested this in all browsers that I could find. For ie8 and ie9 you must transpile the code before you can use this. If you want to know if this works with your browser, [open the demo page](https://pubkey.github.io/broadcast-channel/e2e.html).\n\n## Thanks\nThanks to [Hemanth.HM](https://github.com/hemanth) for the module name.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpubkey%2Fbroadcast-channel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpubkey%2Fbroadcast-channel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpubkey%2Fbroadcast-channel/lists"}