Comparing version 6.0.0-rc.3 to 6.0.0
@@ -1,2 +0,2 @@ | ||
export * from "./methods"; | ||
export type { FramebusConfig, FramebusOptions } from "./framebus-config"; | ||
import { Framebus } from "./framebus"; | ||
export = Framebus; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__exportStar(require("./methods"), exports); | ||
var lib_1 = require("./lib"); | ||
var framebus_1 = require("./framebus"); | ||
(0, lib_1.attach)(); | ||
module.exports = framebus_1.Framebus; |
@@ -9,3 +9,3 @@ { | ||
"homepage": "https://github.com/braintree/framebus", | ||
"version": "6.0.0-rc.3", | ||
"version": "6.0.0", | ||
"main": "dist/index.js", | ||
@@ -34,33 +34,32 @@ "files": [ | ||
"devDependencies": { | ||
"@types/jest": "^27.4.0", | ||
"@typescript-eslint/eslint-plugin": "^5.12.0", | ||
"@wdio/cli": "^7.16.15", | ||
"@wdio/local-runner": "^7.16.15", | ||
"@wdio/mocha-framework": "^7.16.15", | ||
"@wdio/spec-reporter": "^7.16.14", | ||
"@wdio/sync": "^7.16.15", | ||
"async": "^3.2.3", | ||
"@types/jest": "^29.4.0", | ||
"@wdio/cli": "^7.25.2", | ||
"@wdio/local-runner": "^7.25.2", | ||
"@wdio/mocha-framework": "^7.25.2", | ||
"@wdio/spec-reporter": "^7.25.1", | ||
"@wdio/sync": "^7.25.2", | ||
"async": "^3.2.4", | ||
"browserify": "^17.0.0", | ||
"chromedriver": "^98.0.1", | ||
"chromedriver": "^118.0.1", | ||
"del": "^6.0.0", | ||
"ejs": "^3.1.6", | ||
"eslint": "^8.9.0", | ||
"ejs": "^3.1.8", | ||
"eslint": "^8.35.0", | ||
"eslint-config-braintree": "6.0.0-typescript-prep-rc.2", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"express": "^4.17.3", | ||
"express": "^4.18.2", | ||
"gulp": "^4.0.2", | ||
"gulp-concat": "^2.4.2", | ||
"gulp-remove-code": "^3.0.4", | ||
"gulp-size": "^4.0.1", | ||
"gulp-size": "^3.0.0", | ||
"gulp-streamify": "^1.0.2", | ||
"gulp-uglify": "^3.0.2", | ||
"jest": "^27.5.1", | ||
"jest": "^29.5.0", | ||
"jest-environment-jsdom": "^29.5.0", | ||
"morgan": "^1.10.0", | ||
"prettier": "^2.5.1", | ||
"ts-jest": "^27.1.3", | ||
"prettier": "^2.8.4", | ||
"ts-jest": "^29.0.5", | ||
"tsify": "^5.0.4", | ||
"typescript": "^4.5.5", | ||
"typescript": "^5.1.6", | ||
"vinyl-buffer": "^1.0.1", | ||
"vinyl-source-stream": "^2.0.0", | ||
"wdio-chromedriver-service": "^7.2.8" | ||
"wdio-chromedriver-service": "^8.1.1" | ||
}, | ||
@@ -67,0 +66,0 @@ "jest": { |
321
README.md
# 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) | ||
Framebus allows you to easily send messages across frames (and iframes). | ||
Framebus allows you to easily send messages across frames (and iframes) | ||
with a simple bus. | ||
@@ -8,7 +9,6 @@ In one frame: | ||
```js | ||
import { initialize as initializeFramebus, emit } from "framebus"; | ||
var Framebus = require("framebus"); | ||
var bus = new Framebus(); | ||
const bus = initializeFramebus(); | ||
emit(bus, "message", { | ||
bus.emit("message", { | ||
from: "Ron", | ||
@@ -22,7 +22,6 @@ contents: "they named it...San Diago", | ||
```js | ||
import { initialize as initializeFramebus, on } from "framebus"; | ||
var Framebus = require("framebus"); | ||
var bus = new Framebus(); | ||
const bus = initializeFramebus(); | ||
on(bus, "message", (data) => { | ||
bus.on("message", function (data) { | ||
console.log(data.from + " said: " + data.contents); | ||
@@ -32,3 +31,4 @@ }); | ||
The Framebus class takes a configuration object, where all the params are optional. | ||
The Framebus class takes a configuration object, where all the params | ||
are optional. | ||
@@ -40,16 +40,23 @@ ```js | ||
verifyDomain?: (url: string) => boolean, // no default | ||
targetFrames?: <HTMLFrameElement | Window>[], // by default, all frames available to broadcast to | ||
}; | ||
``` | ||
The `origin` sets the framebus configuration to only operate on the chosen origin. | ||
The `origin` sets the framebus instance to only operate on the chosen | ||
origin. | ||
The `channel` namespaces the events called with `on` and `emit` so you can have multiple buses on the page and have them only communicate with buses with the same channel value. | ||
The `channel` namespaces the events called with `on` and `emit` so you | ||
can have multiple bus instances on the page and have them only | ||
communicate with busses with the same channel value. | ||
If a `verifyDomain` is passed, then the `on` listener will only fire if the domain of the origin of the post message matches the `location.href` value of page or the function passed for `verifyDomain` returns `true`. | ||
If a `verifyDomain` function is passed, then the `on` listener will only | ||
fire if the domain of the origin of the post message matches the | ||
`location.href` value of page or the function passed for `verifyDomain` | ||
returns `true`. | ||
```js | ||
const bus = initializeFramebus({ | ||
verifyDomain(url) { | ||
var bus = new Framebus({ | ||
verifyDomain: function (url) { | ||
// only return true if the domain of the url matches exactly | ||
return url.startsWith("https://my-domain/"); | ||
url.indexOf("https://my-domain") === 0; | ||
}, | ||
@@ -59,94 +66,159 @@ }); | ||
If a `targetFrames` array is passed, then framebus will only send | ||
messages to those frames and listen for messages from those frames. You | ||
can pass a reference to a `Window` (the return value of `window.open`) | ||
or an `HTMLFrameElement` (a DOM node representing an iframe). | ||
```js | ||
var myIframe = document.getElementById("my-iframe"); | ||
var bus = new Framebus({ | ||
targetFrames: [myIframe], | ||
}); | ||
``` | ||
To add additional frames to the `targetFrames` array in the future, use | ||
the `addTargetFrame` method. `targetFrames` must be set, even if it's an | ||
empty array, for this method to work. | ||
```js | ||
var myIframe = document.getElementById("my-iframe"); | ||
var bus = new Framebus({ | ||
targetFrames: [], | ||
}); | ||
bus.addTargetFrame(myIframe); | ||
``` | ||
## API | ||
#### `emit<T = unknown>(config, 'event', data?, callback?): boolean` | ||
#### `target(options: FramebusOptions): framebus` | ||
**returns**: `true` if the event was successfully published, `false` otherwise | ||
**returns**: a chainable instance of framebus that operates on the | ||
chosen origin. | ||
| Argument | Type | Description | | ||
| ---------------- | -------------- | ---------------------------------------------------- | | ||
| `config` | FramebusConfig | The Framebus configuration to use | | ||
| `event` | String | The name of the event | | ||
| `data` | Object | The data to give to subscribers | | ||
| `callback(data)` | Function | Give subscribers a function for easy, direct replies | | ||
This method is used in conjuction with `emit`, `on`, and `off` to | ||
restrict their results to the given origin. By default, an origin of | ||
`'*'` is used. | ||
#### `emitAsPromise<T = unknown>('event', data?): Promise<T>` | ||
```javascript | ||
framebus | ||
.target({ | ||
origin: "https://example.com", | ||
}) | ||
.on("my cool event", function () {}); | ||
// will ignore all incoming 'my cool event' NOT from 'https://example.com' | ||
``` | ||
**returns**: A promise that resolves when the emitted event is responded to the first time. It will reject if the event could not be succesfully published. | ||
| Argument | Type | Description | | ||
| --------- | --------------- | ---------------------------------- | | ||
| `options` | FramebusOptions | See above section for more details | | ||
| Argument | Type | Description | | ||
| -------- | -------------- | --------------------------------- | | ||
| `config` | FramebusConfig | The Framebus configuration to use | | ||
| `event` | String | The name of the event | | ||
| `data` | Object | The data to give to subscribers | | ||
#### `emit('event', data?, callback?): boolean` | ||
**Note:**: If the `replyCallback` is used with `on` (see below), `emitAsPromise` will only resolve for the first `replyCallback`. Any subsequent calls to `replyCallback` will be ignored. | ||
**returns**: `true` if the event was successfully published, `false` | ||
otherwise | ||
#### `on(config, 'event', fn): boolean` | ||
| Argument | Type | Description | | ||
| ---------------- | -------- | ---------------------------------------------------- | | ||
| `event` | String | The name of the event | | ||
| `data` | Object | The data to give to subscribers | | ||
| `callback(data)` | Function | Give subscribers a function for easy, direct replies | | ||
**returns**: `true` if the subscriber was successfully added, `false` otherwise | ||
#### `emitAsPromise('event', data?): Promise` | ||
Unless already bound to a scope, the listener will be executed with `this` set | ||
to the `MessageEvent` received over postMessage. | ||
**returns**: A promise that resolves when the emitted event is responded | ||
to the first time. It will reject if the event could not be succesfully | ||
published. | ||
| Argument | Type | Description | | ||
| ---------------------- | -------------- | ----------------------------------------------------------- | | ||
| `config` | FramebusConfig | The Framebus configuration to use | | ||
| `event` | String | The name of the event | | ||
| `fn(data?, callback?)` | Function | Event handler. Arguments are from the `emit` invocation | | ||
| ↳ `this` | scope | The `MessageEvent` object from the underlying `postMessage` | | ||
| Argument | Type | Description | | ||
| -------- | ------ | ------------------------------- | | ||
| `event` | String | The name of the event | | ||
| `data` | Object | The data to give to subscribers | | ||
#### `once(config, 'event', fn): boolean` | ||
Using this method assumes the browser context you are using supports | ||
Promises. If it does not, set a polyfill for the Framebus class with | ||
`setPromise` | ||
**returns**: `true` if the subscriber was successfully added, `false` otherwise | ||
```js | ||
// or however you want to polyfill the promise | ||
const PolyfilledPromise = require("promise-polyfill"); | ||
This is essentially the same as `on`, except it will only ever trigger once. It's equivalent to using `on` and then calling `off` as soon as the callback is called. | ||
Framebus.setPromise(PolyfilledPromise); | ||
``` | ||
| Argument | Type | Description | | ||
| ---------------------- | -------------- | ----------------------------------------------------------- | | ||
| `config` | FramebusConfig | The Framebus configuration to use | | ||
| `event` | String | The name of the event | | ||
| `fn(data?, callback?)` | Function | Event handler. Arguments are from the `emit` invocation | | ||
| ↳ `this` | scope | The `MessageEvent` object from the underlying `postMessage` | | ||
#### `on('event', fn): boolean` | ||
#### `off(config, 'event', fn): boolean` | ||
**returns**: `true` if the subscriber was successfully added, `false` | ||
otherwise | ||
**returns**: `true` if the subscriber was successfully removed, `false` otherwise | ||
Unless already bound to a scope, the listener will be executed with | ||
`this` set to the `MessageEvent` received over | ||
postMessage. | ||
| Argument | Type | Description | | ||
| -------- | -------------- | --------------------------------- | | ||
| `config` | FramebusConfig | The Framebus configuration to use | | ||
| `event` | String | The name of the event | | ||
| `fn` | Function | The function that was subscribed | | ||
| Argument | Type | Description | | ||
| ---------------------- | -------- | ----------------------------------------------------------- | | ||
| `event` | String | The name of the event | | ||
| `fn(data?, callback?)` | Function | Event handler. Arguments are from the `emit` invocation | | ||
| ↳ `this` | scope | The `MessageEvent` object from the underlying `postMessage` | | ||
#### `include(config, popup): boolean` | ||
#### `off('event', fn): boolean` | ||
**returns**: `true` if the popup was successfully included, `false` otherwise | ||
**returns**: `true` if the subscriber was successfully removed, `false` | ||
otherwise | ||
| Argument | Type | Description | | ||
| -------- | -------- | -------------------------------- | | ||
| `event` | String | The name of the event | | ||
| `fn` | Function | The function that was subscribed | | ||
#### `include(popup): boolean` | ||
**returns**: `true` if the popup was successfully included, `false` | ||
otherwise | ||
```javascript | ||
const popup = window.open("https://example.com"); | ||
var popup = window.open("https://example.com"); | ||
include(bus, popup); | ||
emit(bus, "hello popup and friends!"); | ||
framebus.include(popup); | ||
framebus.emit("hello popup and friends!"); | ||
``` | ||
| Argument | Type | Description | | ||
| -------- | -------------- | -------------------------------------------- | | ||
| `config` | FramebusConfig | The Framebus configuration to use | | ||
| `popup` | Window | The popup refrence returned by `window.open` | | ||
| Argument | Type | Description | | ||
| -------- | ------ | -------------------------------------------- | | ||
| `popup` | Window | The popup refrence returned by `window.open` | | ||
#### `addTargetFrame(frame): boolean` | ||
Used in conjunction with `targetFrames` configuration. If a | ||
`targetFrames` array is not passed on instantiation, this method will | ||
noop. | ||
```javascript | ||
var frame = document.getElementById("my-iframe"); | ||
framebus.addTargetFrame(frame); | ||
framebus.emit("hello targetted iframe!"); | ||
``` | ||
| Argument | Type | Description | | ||
| -------- | --------------------------- | -------------------------------------------- | | ||
| `frame` | Window or HTMLIFrameElement | The iframe or popup to add to `targetFrames` | | ||
#### `teardown(): void` | ||
Calls `off` on all listeners used for this bus instance and makes subsequent calls to all methods `noop`. | ||
Calls `off` on all listeners used for this bus instance and makes | ||
subsequent calls to all methods `noop`. | ||
```javascript | ||
on(bus, "event-name", handler); | ||
bus.on("event-name", handler); | ||
// the listener for the "event-name" event is removed | ||
teardown(bus); | ||
// event-name listener is torn down | ||
bus.teardown(); | ||
// these now do nothing | ||
on(bus, "event-name", handler); | ||
emit(bus, "event-name", data); | ||
off(bus, "event-name", handler); | ||
bus.on("event-name", handler); | ||
bus.emit("event-name", data); | ||
bus.off("event-name", handler); | ||
``` | ||
@@ -156,71 +228,78 @@ | ||
These are some things to keep in mind while using **framebus** to handle your | ||
event delegation | ||
These are some things to keep in mind while using **framebus** to handle | ||
your event delegation | ||
### Cross-site scripting (XSS) | ||
**framebus** allows convenient event delegation across iframe borders. By | ||
default it will broadcast events to all iframes on the page, regardless of | ||
origin. Use the optional `origin` parameter when you know the exact domain of | ||
the iframes you are communicating with. This will protect your event data from | ||
malicious domains. | ||
**framebus** allows convenient event delegation across iframe borders. | ||
By default it will broadcast events to all iframes on the page, | ||
regardless of origin. Use the optional `target()` method when you know | ||
the exact domain of the iframes you are communicating with. This will | ||
protect your event data from malicious domains. | ||
### Data is serialized as JSON | ||
**framebus** operates over `postMessage` using `JSON.parse` and `JSON.stringify` | ||
to facilitate message data passing. Keep in mind that not all JavaScript objects | ||
serialize cleanly into and out of JSON, such as `undefined`. | ||
**framebus** operates over `postMessage` using `JSON.parse` and | ||
`JSON.stringify` to facilitate message data passing. Keep in mind that | ||
not all JavaScript objects serialize cleanly into and out of JSON, such | ||
as `undefined`. | ||
### Asynchronicity | ||
Even when the subscriber and publisher are within the same frame, events go | ||
through `postMessage`. Keep in mind that `postMessage` is an asynchronous | ||
protocol and that publication and subscription handling occur on separate | ||
iterations of the [event | ||
loop (MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop#Event_loop). | ||
Even when the subscriber and publisher are within the same frame, events | ||
go through `postMessage`. Keep in mind that `postMessage` is an | ||
asynchronous protocol and that publication and subscription handling | ||
occur on separate iterations of the [event loop | ||
(MDN)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop#Event_loop). | ||
### Published callback functions are an abstraction | ||
When you specify a `callback` while using `emit`, the function is not actually | ||
given to the subscriber. The subscriber receives a one-time-use function that is | ||
generated locally by the subscriber's **framebus**. This one-time-use callback function | ||
is pre-configured to publish an event back to the event origin's domain using a | ||
[UUID](http://tools.ietf.org/html/rfc4122) as the event name. The events occur | ||
as follows: | ||
When you specify a `callback` while using `emit`, the function is not | ||
actually given to the subscriber. The subscriber receives a one-time-use | ||
function that is generated locally by the subscriber's **framebus**. | ||
This one-time-use callback function is pre-configured to publish an | ||
event back to the event origin's domain using a | ||
[UUID](http://tools.ietf.org/html/rfc4122) as the event name. The events | ||
occur as follows: | ||
1. `http://emitter.example.com` publishes an event with a function as the event data | ||
1. `http://emitter.example.com` publishes an event with a function as | ||
the event data | ||
```javascript | ||
const callback = function (data) { | ||
console.log("Got back %s as a reply!", data); | ||
}; | ||
```javascript | ||
var callback = function (data) { | ||
console.log("Got back %s as a reply!", data); | ||
}; | ||
emit(bus, "Marco!", callback); | ||
``` | ||
framebus.emit("Marco!", callback, "http://listener.example.com"); | ||
``` | ||
1. The **framebus** on `http://emitter.example.com` generates a UUID as an event name | ||
and adds the `callback` as a subscriber to this event. | ||
1. The **framebus** on `http://listener.example.com` sees that a special callback | ||
event is in the event payload. A one-time-use function is created locally and | ||
given to subscribers of `'Marco!'` as the event data. | ||
1. The subscriber on `http://listener.example.com` uses the local one-time-use | ||
callback function to send data back to the emitter's origin | ||
2. The **framebus** on `http://emitter.example.com` generates a UUID as | ||
an event name and adds the `callback` as a subscriber to this event. | ||
```javascript | ||
const bus = initializeFramebus({ | ||
origin: "http://emitter.example.com") | ||
}); | ||
3. The **framebus** on `http://listener.example.com` sees that a | ||
special callback event is in the event payload. A one-time-use | ||
function is created locally and given to subscribers of `'Marco!'` | ||
as the event data. | ||
on(bus, "Marco!", function (callback) { | ||
callback("Polo!"); | ||
}); | ||
``` | ||
4. The subscriber on `http://listener.example.com` uses the local | ||
one-time-use callback function to send data back to the emitter's | ||
origin | ||
1. The one-time-use function on `http://listener.example.com` publishes an event | ||
as the UUID generated in **step 2** to the origin that emitted the event. | ||
1. Back on `http://emitter.example.com`, the `callback` is called and | ||
unsubscribed from the special UUID event afterward. | ||
```javascript | ||
framebus | ||
.target("http://emitter.example.com") | ||
.on("Marco!", function (callback) { | ||
callback("Polo!"); | ||
}); | ||
``` | ||
5. The one-time-use function on `http://listener.example.com` publishes | ||
an event as the UUID generated in **step 2** to the origin that | ||
emitted the event. | ||
6. Back on `http://emitter.example.com`, the `callback` is called and | ||
unsubscribed from the special UUID event afterward. | ||
## Development and contributing | ||
See [**CONTRIBUTING.md**](CONTRIBUTING.md) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
30
649
0
300
36384
37
1