{"id":29003889,"url":"https://github.com/braintree/framebus","last_synced_at":"2025-10-04T12:52:51.336Z","repository":{"id":30086939,"uuid":"33636571","full_name":"braintree/framebus","owner":"braintree","description":"A message bus that operates across iframes","archived":false,"fork":false,"pushed_at":"2025-03-29T04:39:41.000Z","size":2338,"stargazers_count":170,"open_issues_count":4,"forks_count":38,"subscribers_count":72,"default_branch":"main","last_synced_at":"2025-06-21T16:42:17.980Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"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/braintree.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-04-08T22:55:38.000Z","updated_at":"2025-06-17T05:31:23.000Z","dependencies_parsed_at":"2023-02-14T00:01:04.837Z","dependency_job_id":"7adbf475-e4d4-4489-bc31-b2cb002910be","html_url":"https://github.com/braintree/framebus","commit_stats":{"total_commits":160,"total_committers":22,"mean_commits":"7.2727272727272725","dds":0.7125,"last_synced_commit":"391fd27a2ec1645c46d04b8f20270df68bf06aea"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"purl":"pkg:github/braintree/framebus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/braintree%2Fframebus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/braintree%2Fframebus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/braintree%2Fframebus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/braintree%2Fframebus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/braintree","download_url":"https://codeload.github.com/braintree/framebus/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/braintree%2Fframebus/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261348205,"owners_count":23145300,"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":[],"created_at":"2025-06-25T10:39:52.844Z","updated_at":"2025-10-04T12:52:46.316Z","avatar_url":"https://github.com/braintree.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Framebus [![Build Status](https://github.com/braintree/framebus/workflows/Unit%20Tests/badge.svg)](https://github.com/braintree/framebus/actions?query=workflow%3A%22Unit+Tests%22) [![Build Status](https://github.com/braintree/framebus/workflows/Functional%20Tests/badge.svg)](https://github.com/braintree/framebus/actions?query=workflow%3A%22Functional+Tests%22) [![npm version](https://badge.fury.io/js/framebus.svg)](http://badge.fury.io/js/framebus)\n\nFramebus allows you to easily send messages across frames (and iframes)\nwith a simple bus.\n\nIn one frame:\n\n```js\nvar Framebus = require(\"framebus\");\nvar bus = new Framebus();\n\nbus.emit(\"message\", {\n  from: \"Ron\",\n  contents: \"they named it...San Diago\",\n});\n```\n\nIn another frame:\n\n```js\nvar Framebus = require(\"framebus\");\nvar bus = new Framebus();\n\nbus.on(\"message\", function (data) {\n  console.log(data.from + \" said: \" + data.contents);\n});\n```\n\nThe Framebus class takes a configuration object, where all the params\nare optional.\n\n```js\ntype FramebusOptions = {\n  origin?: string, // default: \"*\"\n  channel?: string, // no default\n  verifyDomain?: (url: string) =\u003e boolean, // no default\n  targetFrames?: \u003cHTMLFrameElement | Window\u003e[], // by default, all frames available to broadcast to\n};\n```\n\nThe `origin` sets the framebus instance to only operate on the chosen\norigin.\n\nThe `channel` namespaces the events called with `on` and `emit` so you\ncan have multiple bus instances on the page and have them only\ncommunicate with busses with the same channel value.\n\nIf a `verifyDomain` function is passed, then the `on` listener will only\nfire if the domain of the origin of the post message matches the\n`location.href` value of page or the function passed for `verifyDomain`\nreturns `true`.\n\n```js\nvar bus = new Framebus({\n  verifyDomain: function (url) {\n    // only return true if the domain of the url matches exactly\n    url.indexOf(\"https://my-domain\") === 0;\n  },\n});\n```\n\nIf a `targetFrames` array is passed, then framebus will only send\nmessages to those frames and listen for messages from those frames. You\ncan pass a reference to a `Window` (the return value of `window.open`)\nor an `HTMLFrameElement` (a DOM node representing an iframe).\n\n```js\nvar myIframe = document.getElementById(\"my-iframe\");\n\nvar bus = new Framebus({\n  targetFrames: [myIframe],\n});\n```\n\nTo add additional frames to the `targetFrames` array in the future, use\nthe `addTargetFrame` method. `targetFrames` must be set, even if it's an\nempty array, for this method to work.\n\n```js\nvar myIframe = document.getElementById(\"my-iframe\");\n\nvar bus = new Framebus({\n  targetFrames: [],\n});\n\nbus.addTargetFrame(myIframe);\n```\n\n## API\n\n#### `target(options: FramebusOptions): framebus`\n\n**returns**: a chainable instance of framebus that operates on the\nchosen origin.\n\nThis method is used in conjuction with `emit`, `on`, and `off` to\nrestrict their results to the given origin. By default, an origin of\n`'*'` is used.\n\n```javascript\nframebus\n  .target({\n    origin: \"https://example.com\",\n  })\n  .on(\"my cool event\", function () {});\n// will ignore all incoming 'my cool event' NOT from 'https://example.com'\n```\n\n| Argument  | Type            | Description                        |\n| --------- | --------------- | ---------------------------------- |\n| `options` | FramebusOptions | See above section for more details |\n\n#### `emit('event', data?, callback?): boolean`\n\n**returns**: `true` if the event was successfully published, `false`\notherwise\n\n| Argument         | Type     | Description                                          |\n| ---------------- | -------- | ---------------------------------------------------- |\n| `event`          | String   | The name of the event                                |\n| `data`           | Object   | The data to give to subscribers                      |\n| `callback(data)` | Function | Give subscribers a function for easy, direct replies |\n\n#### `emitAsPromise('event', data?): Promise`\n\n**returns**: A promise that resolves when the emitted event is responded\nto the first time. It will reject if the event could not be succesfully\npublished.\n\n| Argument | Type   | Description                     |\n| -------- | ------ | ------------------------------- |\n| `event`  | String | The name of the event           |\n| `data`   | Object | The data to give to subscribers |\n\nUsing this method assumes the browser context you are using supports\nPromises. If it does not, set a polyfill for the Framebus class with\n`setPromise`\n\n```js\n// or however you want to polyfill the promise\nconst PolyfilledPromise = require(\"promise-polyfill\");\n\nFramebus.setPromise(PolyfilledPromise);\n```\n\n#### `on('event', fn): boolean`\n\n**returns**: `true` if the subscriber was successfully added, `false`\notherwise\n\nUnless already bound to a scope, the listener will be executed with\n`this` set to the `MessageEvent` received over\npostMessage.\n\n| Argument               | Type     | Description                                                 |\n| ---------------------- | -------- | ----------------------------------------------------------- |\n| `event`                | String   | The name of the event                                       |\n| `fn(data?, callback?)` | Function | Event handler. Arguments are from the `emit` invocation     |\n| ↳ `this`               | scope    | The `MessageEvent` object from the underlying `postMessage` |\n\n#### `off('event', fn): boolean`\n\n**returns**: `true` if the subscriber was successfully removed, `false`\notherwise\n\n| Argument | Type     | Description                      |\n| -------- | -------- | -------------------------------- |\n| `event`  | String   | The name of the event            |\n| `fn`     | Function | The function that was subscribed |\n\n#### `include(popup): boolean`\n\n**returns**: `true` if the popup was successfully included, `false`\notherwise\n\n```javascript\nvar popup = window.open(\"https://example.com\");\n\nframebus.include(popup);\nframebus.emit(\"hello popup and friends!\");\n```\n\n| Argument | Type   | Description                                  |\n| -------- | ------ | -------------------------------------------- |\n| `popup`  | Window | The popup refrence returned by `window.open` |\n\n#### `addTargetFrame(frame): boolean`\n\nUsed in conjunction with `targetFrames` configuration. If a\n`targetFrames` array is not passed on instantiation, this method will\nnoop.\n\n```javascript\nvar frame = document.getElementById(\"my-iframe\");\n\nframebus.addTargetFrame(frame);\nframebus.emit(\"hello targetted iframe!\");\n```\n\n| Argument | Type                        | Description                                  |\n| -------- | --------------------------- | -------------------------------------------- |\n| `frame`  | Window or HTMLIFrameElement | The iframe or popup to add to `targetFrames` |\n\n#### `teardown(): void`\n\nCalls `off` on all listeners used for this bus instance and makes\nsubsequent calls to all methods `noop`.\n\n```javascript\nbus.on(\"event-name\", handler);\n\n// event-name listener is torn down\nbus.teardown();\n\n// these now do nothing\nbus.on(\"event-name\", handler);\nbus.emit(\"event-name\", data);\nbus.off(\"event-name\", handler);\n```\n\n## Pitfalls\n\nThese are some things to keep in mind while using **framebus** to handle\nyour event delegation\n\n### Cross-site scripting (XSS)\n\n**framebus** allows convenient event delegation across iframe borders.\nBy default it will broadcast events to all iframes on the page,\nregardless of origin. Use the optional `target()` method when you know\nthe exact domain of the iframes you are communicating with. This will\nprotect your event data from malicious domains.\n\n### Data is serialized as JSON\n\n**framebus** operates over `postMessage` using `JSON.parse` and\n`JSON.stringify` to facilitate message data passing. Keep in mind that\nnot all JavaScript objects serialize cleanly into and out of JSON, such\nas `undefined`.\n\n### Asynchronicity\n\nEven when the subscriber and publisher are within the same frame, events\ngo through `postMessage`. Keep in mind that `postMessage` is an\nasynchronous protocol and that publication and subscription handling\noccur on separate iterations of the [event loop\n(MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop#Event_loop).\n\n### Published callback functions are an abstraction\n\nWhen you specify a `callback` while using `emit`, the function is not\nactually given to the subscriber. The subscriber receives a one-time-use\nfunction that is generated locally by the subscriber's **framebus**.\nThis one-time-use callback function is pre-configured to publish an\nevent back to the event origin's domain using a\n[UUID](http://tools.ietf.org/html/rfc4122) as the event name. The events\noccur as follows:\n\n1.  `http://emitter.example.com` publishes an event with a function as\n    the event data\n\n    ```javascript\n    var callback = function (data) {\n      console.log(\"Got back %s as a reply!\", data);\n    };\n\n    framebus.emit(\"Marco!\", callback, \"http://listener.example.com\");\n    ```\n\n2.  The **framebus** on `http://emitter.example.com` generates a UUID as\n    an event name and adds the `callback` as a subscriber to this event.\n\n3.  The **framebus** on `http://listener.example.com` sees that a\n    special callback event is in the event payload. A one-time-use\n    function is created locally and given to subscribers of `'Marco!'`\n    as the event data.\n\n4.  The subscriber on `http://listener.example.com` uses the local\n    one-time-use callback function to send data back to the emitter's\n    origin\n\n    ```javascript\n    framebus\n      .target(\"http://emitter.example.com\")\n      .on(\"Marco!\", function (callback) {\n        callback(\"Polo!\");\n      });\n    ```\n\n5.  The one-time-use function on `http://listener.example.com` publishes\n    an event as the UUID generated in **step 2** to the origin that\n    emitted the event.\n\n6.  Back on `http://emitter.example.com`, the `callback` is called and\n    unsubscribed from the special UUID event afterward.\n\n## Development and contributing\n\nSee [**CONTRIBUTING.md**](CONTRIBUTING.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbraintree%2Fframebus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbraintree%2Fframebus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbraintree%2Fframebus/lists"}