Wormhole
It's better EventEmitter for communication between tabs with supporting Master/Slave.
npm i --save-dev wormhole.js
Features
- One connection on WebSocket for all tabs.
- Support Master/Slave
- Cross-domain communication
- SharedWorker or fallback to localStorage
- IE 8+, Chrome 10+, FireFox 10+, Opera 10+, Safari 6+
- Test coverage (run)
Basic example
import wormhole from 'wormhole.js'; // yes, with ".js", it's not mistake
// All tabs
wormhole().on('coords', (x, y) => {
console.log(x, y);
});
// Some tab
wormhole().emit('coords', [5, 10]);
// Master tab
if (wormhole().master) {
// ..
}
wormhole().on('master', () => {
console.log('Wow!');
});
One connection on WebSocket for all tabs
Module wormhole.js/ws
implements WebSocket-like interface:
https://rubaxa.github.io/wormhole/?ws=y
import WS from 'wormhole.js/ws';
// Create WebScoket (wormhole-socket)
const socket = new WS('ws://echo.websocket.org'); // OR new WS('...', null, hole);
socket.onopen = () => console.log('Connected');
socket.onmessage = ({data}) => console.log('Received:', data);
// Unique event
socket.onmaster = () => {
console.log('Yes, I\'m master!');
};
// Some tab
socket.send({foo: 'bar'})
// All tabs:
// "Received:" {foo: 'bar'}
CORS example
- Create a subdomain, ex.:
http://wormhole.youdomain.com/
; - Copy-paste universal.html and wormhole.js into root;
- Check access
http://wormhole.youdomain.com/universal.html
; - Profit:
// http://foo.youdomain.com/
import {Universal} from 'wormhole.js';
const hole = new Universal('http://wormhole.youdomain.com/universal.html');
hole.on('data', (data) => {
console.log(data);
});
// http://bar.youdomain.com/
import {Universal} from 'wormhole.js';
const hole = new Universal('http://wormhole.youdomain.com/universal.html');
hole.emit('data', 'any data');
Master/slave example
import wormhole from 'wormhole.js';
// All tabs (main.js)
// Define remote command (master)
wormhole()['get-data'] = (function (_cache) {
return function getData(req, callback) {
if (!_cache.hasOwnProperty(req.url)) {
_cache[req.url] = fetch(req.url).then(res => res.json());
}
return _cache[key];
};
})({});
// Get remote data method
function getData(url) {
return new Promise((resolve, reject) => {
// Calling command on master (from slave... or the master, is not important)
wormhole().call(
'get-data', // command
{url}, // arguments
(err, json) => err ? reject(err) : resolve(json) // callback(err, result)
);
});
};
// I'm master!
wormhole().on('master', () => {
// some code
});
// Tab #X
getData('/path/to/api').then((json) => {
// Send ajax request
console.log(result);
});
// Tab #Y
getData('/path/to/api').then((result) => {
// From master cache
console.log(result);
});
Peers
wormhole()
.on('peers', (peers) => {
console.log('ids:', peers); // ['tab-id-1', 'tab-id-2', ..]
})
.on('peers:add', (id) => {
// ..
})
.on('peers:remove', (id) => {
// ..
})
;
Executing the command on master
// Register command (all tabs)
wormhole()['foo'] = (data, next) => {
// bla-bla-bla
next(null, data.reverse()); // or `next('error')`
};
// Calling the command (some tab)
wormhole().call('foo', [1, 2, 3], (err, results) => {
console.log(results); // [3, 2, 1]
})
Modules
- Emitter — Micro event emitter
- cors — Handy wrapper over
postMessage
. - store — Safe and a handy wrapper over
localStorage
.
wormhole.Emitter
Micro event emitter.
- on(type:
String
, fn:Function
):this
- off(type:
String
, fn:Function
):this
- emit(type:
String
[, args:*, Array
]):this
import {Emitter} from 'wormhole.js';
const obj = Emitter.apply({}); // or new wormhole.Emitter();
obj.on('foo', () => {
console.log(arguments);
});
obj.emit('foo'); // []
obj.emit('foo', 1); // [1]
obj.emit('foo', [1, 2, 3]); // [1, 2, 3]
wormhole.cors
Handy wrapper over postMessage
.
import {cors} from 'wormhole.js';
// Main-document
cors.on('data', (data) => {
console.log('Received:', data);
});
cors['some:command'] = (value) => value * 2;
// IFrame
cors(parent).send({foo: 'bar'});
// [main-document] "Received:" {foo: 'bar'}
cors(parent).call('some:command', 3, (err, result) => {
console.log('Error:', err, 'Result:', result);
// [iframe] "Error:" null "Result:" 6
});
wormhole.store
Safe and a handy wrapper over localStorage
.
- get(key:
String
):*
- set(key:
String
, value:*
) - remove(key:
String
) - on(type:
String
, fn:Function
) - off(type:
String
, fn:Function
)
import {store} from 'wormhole.js';
store.on('change', (key, data) => {
console.log('change -> ', key, data);
});
store.on('change:prop', (key, value) => {
console.log('change:prop -> ', key, value);
});
store.set('foo', {bar: 'baz'});
// change -> foo {bar: 'baz'}
store.set('prop', {qux: 'ok'});
// change -> prop {qux: 'ok'}
// change:prop -> prop {qux: 'ok'}
Utils
wormhole.uuid():String
A universally unique identifier (UUID) is an identifier standard used in software construction,
standardized by the Open Software Foundation (OSF) as part of the Distributed Computing Environment (DCE)
(c) wiki.
wormhole.debounce(fn:Function
, delay:Number
[, immediate:Boolean
]):Function
Creates and returns a new debounced version of the passed function that will postpone its execution until after wait milliseconds have elapsed since the last time it was invoked.
Development
npm test
npm run dev
— run dev watchernpm run build