├── magic.gif
├── rawr.jpg
├── .coveralls.yml
├── .eslintignore
├── global.js
├── examples
├── mqtt
│ ├── server.js
│ ├── README.md
│ ├── package.json
│ ├── client-a.js
│ └── client-b.js
├── websocket
│ ├── README.md
│ ├── public
│ │ └── index.html
│ ├── package.json
│ ├── client.js
│ └── server.js
└── webworker
│ ├── README.md
│ ├── public
│ └── index.html
│ ├── worker.js
│ ├── package.json
│ └── main.js
├── .gitignore
├── transports
├── index.js
├── socketio
│ └── index.js
├── mqtt
│ └── index.js
├── websocket
│ └── index.js
└── worker
│ └── index.js
├── .github
└── workflows
│ └── workflow.yml
├── .eslintrc
├── LICENSE
├── package.json
├── test
├── websocket_transport.js
├── worker_transport.js
├── socketio_transport.js
├── mqtt_transport.js
└── index.js
├── README.md
├── index.js
└── dist
└── bundle.js
/magic.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iceddev/rawr/HEAD/magic.gif
--------------------------------------------------------------------------------
/rawr.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iceddev/rawr/HEAD/rawr.jpg
--------------------------------------------------------------------------------
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | service_name: circleci
2 | repo_token: KqreYWh2mPceG9bZOGcYnkkVtpt4FwcJU
3 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | build/
3 | coverage/
4 | examples/webworker/public
5 | examples/websocket/public
6 |
--------------------------------------------------------------------------------
/global.js:
--------------------------------------------------------------------------------
1 | const rawr = require('./');
2 | const EventEmitter = require('eventemitter3');
3 | rawr.EventEmitter = EventEmitter;
4 |
5 | globalThis.Rawr = rawr;
6 |
7 | module.exports = rawr;
8 |
--------------------------------------------------------------------------------
/examples/mqtt/server.js:
--------------------------------------------------------------------------------
1 | const aedes = require('aedes')();
2 | const server = require('net').createServer(aedes.handle);
3 |
4 | const port = 1883;
5 |
6 | server.listen(port, () => {
7 | console.log('mqtt server listening on port', port);
8 | });
9 |
--------------------------------------------------------------------------------
/examples/websocket/README.md:
--------------------------------------------------------------------------------
1 | # using rawr over websockets example
2 |
3 |
4 | ## install deps
5 |
6 | `npm i`
7 |
8 | ## start
9 |
10 | `npm run start`
11 |
12 | ## open browser
13 |
14 | [http://localhost:8080](http://localhost:8080)
15 |
--------------------------------------------------------------------------------
/examples/webworker/README.md:
--------------------------------------------------------------------------------
1 | # using rawr with a webworker example
2 |
3 |
4 | ## install deps
5 |
6 | `npm i`
7 |
8 | ## start
9 |
10 | `npm run start`
11 |
12 | ## open browser
13 |
14 | [http://localhost:8081](http://localhost:8081)
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependency directory
2 | # Deployed apps should consider commenting this line out:
3 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
4 | node_modules
5 | package-lock.json
6 |
7 | # Other
8 | .DS_Store
9 | coverage
10 |
--------------------------------------------------------------------------------
/transports/index.js:
--------------------------------------------------------------------------------
1 | const mqtt = require('./mqtt');
2 | const socketio = require('./socketio');
3 | const websocket = require('./websocket');
4 | const worker = require('./worker');
5 |
6 | module.exports = {
7 | mqtt,
8 | socketio,
9 | websocket,
10 | worker
11 | };
--------------------------------------------------------------------------------
/examples/mqtt/README.md:
--------------------------------------------------------------------------------
1 | # using rawr with mqtt example
2 |
3 |
4 | ## install deps
5 |
6 | `npm i`
7 |
8 | ## start the mqtt server
9 |
10 | `npm run server`
11 |
12 | ## start client-a
13 |
14 | `npm run client-a`
15 |
16 | ## start client-b
17 |
18 | `npm run client-b`
19 |
--------------------------------------------------------------------------------
/examples/webworker/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | + =
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/websocket/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | + =
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/webworker/worker.js:
--------------------------------------------------------------------------------
1 | const rawr = require('../../');
2 |
3 | function add(x, y) {
4 | return x + y;
5 | }
6 |
7 | // create the rawr peer
8 | const rawrPeer = rawr({ transport: rawr.transports.worker(), handlers: { add } });
9 |
10 | // make RPC calls to the DOM
11 | setInterval(async () => {
12 | const val = await rawrPeer.methods.getRandom();
13 | console.log('random from DOM', val);
14 | }, 1000);
15 |
--------------------------------------------------------------------------------
/.github/workflows/workflow.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: push
3 | jobs:
4 | test:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v1
8 | with:
9 | fetch-depth: 1
10 | - name: Setup Node.js
11 | uses: actions/setup-node@v1
12 | with:
13 | node-version: 14.15.1
14 | - name: Installing dependencies
15 | run: npm install
16 | - name: Running tests
17 | run: npm test
18 |
--------------------------------------------------------------------------------
/examples/mqtt/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rawr-mqtt-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "server": "node server",
9 | "client-a": "node client-a",
10 | "client-b": "node client-b"
11 | },
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "aedes": "^0.37.0",
16 | "mqtt": "^2.18.8"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/transports/socketio/index.js:
--------------------------------------------------------------------------------
1 | const EventEmitter = require('eventemitter3');
2 |
3 | function transport({ connection, subTopic, pubTopic }) {
4 | const emitter = new EventEmitter();
5 | connection.on(subTopic, (msg) => {
6 | if (msg.method || (msg.id && ('result' in msg || 'error' in msg))) {
7 | emitter.emit('rpc', msg);
8 | }
9 | });
10 | emitter.send = (msg) => {
11 | connection.emit(pubTopic, msg);
12 | };
13 | return emitter;
14 | }
15 |
16 | module.exports = transport;
17 |
--------------------------------------------------------------------------------
/examples/websocket/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rawr-websocket-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "build": "browserify client.js -o public/client-bundle.js",
8 | "start": "npm run build && node server"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "browserify": "^14.4.0",
14 | "express": "^4.15.4",
15 | "rawr": "^0.7.0",
16 | "ws": "^3.1.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/webworker/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rawr-webworker-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "build": "browserify main.js -o public/main-bundle.js && browserify worker.js -o public/worker-bundle.js",
9 | "start": "npm run build && ecstatic ./public --port 8081"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "browserify": "^14.4.0",
15 | "ecstatic": "^3.3.1"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/mqtt/client-a.js:
--------------------------------------------------------------------------------
1 | const mqtt = require('mqtt');
2 | const rawr = require('../../');
3 |
4 | const connection = mqtt.connect('mqtt://localhost');
5 |
6 | function add(x, y) {
7 | return x + y;
8 | }
9 |
10 | // create the rawr peer
11 | const rawrPeer = rawr({
12 | transport: rawr.transports.mqtt({ connection, pubTopic: 'client-b', subTopic: 'client-a' }),
13 | handlers: { add },
14 | timeout: 1000
15 | });
16 |
17 | // make RPC calls to the DOM
18 | setInterval(async () => {
19 | try {
20 | const val = await rawrPeer.methods.getRandom();
21 | console.log('result sent from client-b', val);
22 | } catch (error) {
23 | console.log('error calling client-b', error.message);
24 | }
25 | }, 1000);
26 |
--------------------------------------------------------------------------------
/examples/mqtt/client-b.js:
--------------------------------------------------------------------------------
1 | const mqtt = require('mqtt');
2 | const rawr = require('../../');
3 |
4 | const connection = mqtt.connect('mqtt://localhost');
5 |
6 | function getRandom() {
7 | return Math.random();
8 | }
9 |
10 | // create the rawr peer
11 | const rawrPeer = rawr({
12 | transport: rawr.transports.mqtt({ connection, pubTopic: 'client-a', subTopic: 'client-b' }),
13 | handlers: { getRandom },
14 | timeout: 1000
15 | });
16 |
17 | // make RPC calls to the DOM
18 | setInterval(async () => {
19 | try {
20 | const val = await rawrPeer.methods.add(1, 3);
21 | console.log('result sent from client-a', val);
22 | } catch (error) {
23 | console.log('error calling client-a', error.message);
24 | }
25 | }, 1000);
26 |
--------------------------------------------------------------------------------
/examples/webworker/main.js:
--------------------------------------------------------------------------------
1 | const rawr = require('../../');
2 |
3 | const myWorker = new Worker('/worker-bundle.js');
4 |
5 | // create the rawr peer
6 | const rawPeer = rawr({ transport: rawr.transports.worker(myWorker) });
7 |
8 | // handle requests from the webworker
9 | rawPeer.addHandler('getRandom', () => Math.random());
10 |
11 | // make an RPC call to the webworker server on a button click
12 | document.getElementById('addBtn').addEventListener('click', async () => {
13 | const num1 = parseFloat(document.getElementById('number1').value);
14 | const num2 = parseFloat(document.getElementById('number2').value);
15 | const result = await rawPeer.methods.add(num1, num2);
16 | document.getElementById('result').innerHTML = result;
17 | }, false);
18 |
--------------------------------------------------------------------------------
/examples/websocket/client.js:
--------------------------------------------------------------------------------
1 | const rawr = require('../../');
2 |
3 | const ws = new WebSocket('ws://localhost:8080');
4 |
5 | ws.onopen = () => {
6 | // create the rawr peer
7 | const rawPeer = rawr({ transport: rawr.transports.websocket(ws) });
8 |
9 | // handle requests from the websocket server
10 | rawPeer.addHandler('getRandom', () => Math.random());
11 |
12 | // make an RPC call to the websocket server on a button click
13 | document.getElementById('addBtn').addEventListener('click', async () => {
14 | const num1 = parseFloat(document.getElementById('number1').value);
15 | const num2 = parseFloat(document.getElementById('number2').value);
16 | const result = await rawPeer.methods.add(num1, num2);
17 | document.getElementById('result').innerHTML = result;
18 | }, false);
19 | };
20 |
--------------------------------------------------------------------------------
/transports/mqtt/index.js:
--------------------------------------------------------------------------------
1 | const EventEmitter = require('eventemitter3');
2 |
3 | function transport({ connection, subTopic, pubTopic, subscribe = true }) {
4 | const emitter = new EventEmitter();
5 | if (subscribe) {
6 | connection.subscribe(subTopic);
7 | }
8 | connection.on('message', (topic, message) => {
9 | if (topic === subTopic) {
10 | try {
11 | const msg = JSON.parse(message.toString());
12 | if (msg.method || (msg.id && ('result' in msg || 'error' in msg))) {
13 | emitter.emit('rpc', msg);
14 | }
15 | } catch (err) {
16 | console.error(err);
17 | }
18 | }
19 | });
20 | emitter.send = (msg) => {
21 | connection.publish(pubTopic, JSON.stringify(msg));
22 | };
23 | return emitter;
24 | }
25 |
26 | module.exports = transport;
27 |
--------------------------------------------------------------------------------
/transports/websocket/index.js:
--------------------------------------------------------------------------------
1 | const EventEmitter = require('eventemitter3');
2 |
3 | function transport(socket, allowBinary = false) {
4 | const emitter = new EventEmitter();
5 | socket.addEventListener('message', async (evt) => {
6 | let { data } = evt;
7 | if (allowBinary && data instanceof Blob) {
8 | data = await (new Response(data)).text().catch(() => null);
9 | }
10 | if (typeof evt.data === 'string') {
11 | try {
12 | const msg = JSON.parse(evt.data);
13 | if (msg.method || (msg.id && ('result' in msg || 'error' in msg))) {
14 | emitter.emit('rpc', msg);
15 | }
16 | } catch (err) {
17 | // wasn't a JSON message
18 | }
19 | }
20 | });
21 | emitter.send = (msg) => {
22 | socket.send(JSON.stringify(msg));
23 | };
24 | return emitter;
25 | }
26 |
27 | module.exports = transport;
28 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | // enable airbnb eslint from https://www.npmjs.com/package/eslint-config-airbnb
3 | "extends": "airbnb",
4 | "rules": {
5 | "consistent-return": [0, { "treatUndefinedAsUnspecified": true }],
6 | "import/no-extraneous-dependencies": ["error", {"devDependencies": true}],
7 | "no-multi-assign": "off",
8 | "no-console": "off",
9 | "no-plusplus": "off",
10 | "camelcase": "off",
11 | "comma-dangle": "off",
12 | "prefer-template": "off",
13 | "object-curly-newline": "off",
14 | "no-restricted-globals": "off", // WTF? `self` should be fine
15 | "quotes": [ "error", "single", { "allowTemplateLiterals": true}],
16 | "arrow-body-style": "off",
17 | "import/no-unresolved": "off",
18 | "max-len": [2, 120, 2]
19 | },
20 | "env": {
21 | "browser": true,
22 | "node": true,
23 | "worker": true
24 | },
25 | "globals": {
26 | "it": "readonly",
27 | "describe": "readonly"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/websocket/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const http = require('http');
3 | const WebSocket = require('ws');
4 | const rawr = require('../../');
5 |
6 | const app = express();
7 | app.use(express.static('public'));
8 |
9 | const server = http.createServer(app);
10 | const wss = new WebSocket.Server({ server });
11 |
12 | function add(x, y) {
13 | return x + y;
14 | }
15 |
16 | wss.on('connection', (socket) => {
17 | const rawrPeer = rawr({ transport: rawr.transports.websocket(socket) });
18 | rawrPeer.addHandler('add', add);
19 |
20 | // make RPC calls to the client
21 | const intervalId = setInterval(async () => {
22 | const val = await rawrPeer.methods.getRandom();
23 | console.log('random from client', val);
24 | }, 1000);
25 |
26 | // cleanup
27 | socket.on('close', () => {
28 | console.log('disconnected');
29 | clearInterval(intervalId);
30 | });
31 | });
32 |
33 | server.listen(8080, () => {
34 | console.log('Listening on %d', server.address().port);
35 | });
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013-2017 Iced Development, LLC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/transports/worker/index.js:
--------------------------------------------------------------------------------
1 | const EventEmitter = require('eventemitter3');
2 |
3 | function dom(webWorker) {
4 | const emitter = new EventEmitter();
5 | webWorker.addEventListener('message', (msg) => {
6 | const { data } = msg;
7 | if (data && (data.method || (data.id && ('result' in data || 'error' in data)))) {
8 | emitter.emit('rpc', data);
9 | }
10 | });
11 | emitter.send = (msg, config) => {
12 | webWorker.postMessage(msg, config ? config.postMessageOptions : undefined);
13 | };
14 | return emitter;
15 | }
16 |
17 | function worker() {
18 | const emitter = new EventEmitter();
19 | self.onmessage = (msg) => {
20 | const { data } = msg;
21 | if (data && (data.method || (data.id && ('result' in data || 'error' in data)))) {
22 | emitter.emit('rpc', data);
23 | }
24 | };
25 | emitter.send = (msg, config) => {
26 | self.postMessage(msg, config ? config.postMessageOptions : undefined);
27 | };
28 | return emitter;
29 | }
30 |
31 | function transport(webWorker) {
32 | if (webWorker) {
33 | return dom(webWorker);
34 | }
35 | return worker();
36 | }
37 |
38 | // backwards compat
39 | transport.dom = dom;
40 | transport.worker = worker;
41 |
42 | module.exports = transport;
43 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rawr",
3 | "version": "0.19.0",
4 | "description": "JSON-RPC over simple node style event emitters",
5 | "dependencies": {
6 | "eventemitter3": "^5.0.1"
7 | },
8 | "devDependencies": {
9 | "b64id": "^1.0.1",
10 | "browserify": "^16.2.3",
11 | "chai": "^4.2.0",
12 | "coveralls": "^3.0.3",
13 | "eslint": "^5.15.3",
14 | "eslint-config-airbnb": "^17.1.0",
15 | "eslint-plugin-import": "^2.16.0",
16 | "eslint-plugin-jsx-a11y": "^6.2.1",
17 | "eslint-plugin-react": "^7.12.4",
18 | "istanbul": "^0.4.5",
19 | "mocha": "^10.2.0"
20 | },
21 | "main": "index.js",
22 | "scripts": {
23 | "lint": "eslint ./index.js --ext .js",
24 | "test": "npm run lint && istanbul cover _mocha && npm run check-coverage",
25 | "mocha": "_mocha",
26 | "build": "browserify global.js -o dist/bundle.js",
27 | "check-coverage": "istanbul check-coverage --statements 100 --branches 75 --lines 100 --functions 100",
28 | "coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls"
29 | },
30 | "repository": {
31 | "type": "git",
32 | "url": "git://github.com/iceddev/rawr"
33 | },
34 | "keywords": [
35 | "rpc",
36 | "json-rpc",
37 | "promises",
38 | "websocket",
39 | "ws",
40 | "promise",
41 | "mqtt"
42 | ],
43 | "author": "Luis Montes (http://iceddev.com/)",
44 | "license": "MIT",
45 | "readmeFilename": "README.md",
46 | "bugs": {
47 | "url": "https://github.com/iceddev/rawr/issues"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/test/websocket_transport.js:
--------------------------------------------------------------------------------
1 | const chai = require('chai');
2 | const EventEmitter = require('eventemitter3');
3 | const rawr = require('../');
4 |
5 | chai.should();
6 |
7 | function mockTransports() {
8 | const a = new EventEmitter();
9 | const b = new EventEmitter();
10 |
11 | a.send = (msg) => {
12 | b.emit('message', {data: msg});
13 | };
14 | a.addEventListener = (topic, cb) => {
15 | a.on(topic, cb);
16 | }
17 |
18 | b.send = (msg) => {
19 | a.emit('message', {data: msg});
20 | };
21 | b.addEventListener = (topic, cb) => {
22 | b.on(topic, cb);
23 | }
24 |
25 | const transportA = rawr.transports.websocket(a);
26 | const transportB = rawr.transports.websocket(b);
27 |
28 | return { transportA, transportB };
29 | }
30 |
31 | function helloTest(name) {
32 | return new Promise((resolve, reject) => {
33 | if (name === 'bad') {
34 | const error = new Error('bad name !');
35 | error.code = 9000;
36 | return reject(error);
37 | }
38 | setTimeout(() => {
39 | return resolve(`hello, ${name}`);
40 | }, 100);
41 | });
42 | }
43 |
44 | function add(a, b) {
45 | return a + b;
46 | }
47 |
48 | function subtract(a, b) {
49 | return a - b;
50 | }
51 |
52 | describe('websocket', () => {
53 | it('should make a client', (done) => {
54 | const { transportA } = mockTransports();
55 | const client = rawr({ transport: transportA });
56 | client.should.be.a('object');
57 | client.addHandler.should.be.a('function');
58 | done();
59 | });
60 |
61 | it('client should make a successful rpc call to another peer', async () => {
62 | const { transportA, transportB } = mockTransports();
63 | const clientA = rawr({ transport: transportA, handlers: { add } });
64 | const clientB = rawr({ transport: transportB, handlers: { subtract } });
65 |
66 | const resultA = await clientA.methods.subtract(7, 2);
67 | const resultB = await clientB.methods.add(1, 2);
68 | resultA.should.equal(5);
69 | resultB.should.equal(3);
70 | });
71 |
72 | it('client should make an unsuccessful rpc call to a peer', async () => {
73 | const { transportA, transportB } = mockTransports();
74 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
75 | const clientB = rawr({ transport: transportB });
76 |
77 | clientA.should.be.an('object');
78 | try {
79 | await clientB.methods.helloTest('bad');
80 | } catch (error) {
81 | error.code.should.equal(9000);
82 | }
83 | });
84 |
85 | it('client handle an rpc under a specified timeout', async () => {
86 | const { transportA, transportB } = mockTransports();
87 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
88 | const clientB = rawr({ transport: transportB, timeout: 1000 });
89 |
90 | clientA.should.be.an('object');
91 | const result = await clientB.methods.helloTest('luis');
92 | result.should.equal('hello, luis');
93 | });
94 |
95 | it('client handle an rpc timeout', async () => {
96 | const { transportA, transportB } = mockTransports();
97 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
98 | const clientB = rawr({ transport: transportB, timeout: 10 });
99 |
100 | clientA.should.be.an('object');
101 | try {
102 | await clientB.methods.helloTest('luis');
103 | } catch (error) {
104 | error.code.should.equal(504);
105 | }
106 | });
107 |
108 | it('client should be able to send a notification to a server', (done) => {
109 | const { transportA, transportB } = mockTransports();
110 | const clientA = rawr({ transport: transportA });
111 | const clientB = rawr({ transport: transportB });
112 |
113 | clientA.notifications.ondoSomething((someData) => {
114 | someData.should.equal('testing_notification');
115 | done();
116 | });
117 |
118 | clientB.notifiers.doSomething('testing_notification');
119 | });
120 | });
121 |
--------------------------------------------------------------------------------
/test/worker_transport.js:
--------------------------------------------------------------------------------
1 | const chai = require('chai');
2 | const EventEmitter = require('eventemitter3');
3 | const rawr = require('../');
4 |
5 | chai.should();
6 |
7 | function mockTransports() {
8 | const fakeWorkerRef = new EventEmitter(); //in the dom
9 | const fakeWorkerInstance = new EventEmitter(); // the worker thread
10 |
11 | fakeWorkerInstance.postMessage = (msg) => {
12 | fakeWorkerRef.emit('message', {data: msg});
13 | };
14 |
15 | fakeWorkerRef.postMessage = (msg) => {
16 | fakeWorkerInstance.onmessage({data: msg});
17 | };
18 |
19 | fakeWorkerRef.addEventListener = (topic, cb) => {
20 | fakeWorkerRef.on(topic, cb);
21 | };
22 |
23 | const transportA = rawr.transports.worker(fakeWorkerRef);
24 | global.self = fakeWorkerInstance;
25 | const transportB = rawr.transports.worker();
26 |
27 | return { transportA, transportB };
28 | }
29 |
30 | function helloTest(name) {
31 | return new Promise((resolve, reject) => {
32 | if (name === 'bad') {
33 | const error = new Error('bad name !');
34 | error.code = 9000;
35 | return reject(error);
36 | }
37 | setTimeout(() => {
38 | return resolve(`hello, ${name}`);
39 | }, 100);
40 | });
41 | }
42 |
43 | function add(a, b) {
44 | return a + b;
45 | }
46 |
47 | function subtract(a, b) {
48 | return a - b;
49 | }
50 |
51 | describe('worker', () => {
52 | it('should make a client', (done) => {
53 | const { transportA } = mockTransports();
54 | const client = rawr({ transport: transportA });
55 | client.should.be.a('object');
56 | client.addHandler.should.be.a('function');
57 | done();
58 | });
59 |
60 | it('client should make a successful rpc call to another peer', async () => {
61 | const { transportA, transportB } = mockTransports();
62 | const clientA = rawr({ transport: transportA, handlers: { add } });
63 | const clientB = rawr({ transport: transportB, handlers: { subtract } });
64 |
65 | const resultA = await clientA.methods.subtract(7, 2);
66 | const resultB = await clientB.methods.add(1, 2);
67 | resultA.should.equal(5);
68 | resultB.should.equal(3);
69 | });
70 |
71 | it('client should make an unsuccessful rpc call to a peer', async () => {
72 | const { transportA, transportB } = mockTransports();
73 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
74 | const clientB = rawr({ transport: transportB });
75 |
76 | clientA.should.be.an('object');
77 | try {
78 | await clientB.methods.helloTest('bad');
79 | } catch (error) {
80 | error.code.should.equal(9000);
81 | }
82 | });
83 |
84 | it('client handle an rpc under a specified timeout', async () => {
85 | const { transportA, transportB } = mockTransports();
86 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
87 | const clientB = rawr({ transport: transportB, timeout: 1000 });
88 |
89 | clientA.should.be.an('object');
90 | const result = await clientB.methods.helloTest('luis');
91 | result.should.equal('hello, luis');
92 | });
93 |
94 | it('client handle an rpc timeout', async () => {
95 | const { transportA, transportB } = mockTransports();
96 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
97 | const clientB = rawr({ transport: transportB, timeout: 10 });
98 |
99 | clientA.should.be.an('object');
100 | try {
101 | await clientB.methods.helloTest('luis');
102 | } catch (error) {
103 | error.code.should.equal(504);
104 | }
105 | });
106 |
107 | it('client should be able to send a notification to a server', (done) => {
108 | const { transportA, transportB } = mockTransports();
109 | const clientA = rawr({ transport: transportA });
110 | const clientB = rawr({ transport: transportB });
111 |
112 | clientA.notifications.ondoSomething((someData) => {
113 | someData.should.equal('testing_notification');
114 | done();
115 | });
116 |
117 | clientB.notifiers.doSomething('testing_notification');
118 | });
119 | });
120 |
--------------------------------------------------------------------------------
/test/socketio_transport.js:
--------------------------------------------------------------------------------
1 | const chai = require('chai');
2 | const EventEmitter = require('eventemitter3');
3 | const rawr = require('../');
4 |
5 | chai.should();
6 |
7 | function mockTransports() {
8 | const a = new EventEmitter();
9 | const b = new EventEmitter();
10 |
11 | a.originalEmit = a.emit;
12 | a.emit = (topic, msg) => {
13 | if(topic === 'aPub') {
14 | b.emit(topic, msg);
15 | } else {
16 | a.originalEmit(topic, msg);
17 | }
18 | };
19 |
20 | b.originalEmit = b.emit;
21 | b.emit = (topic, msg) => {
22 | if(topic === 'bPub') {
23 | a.emit(topic, msg);
24 | } else {
25 | b.originalEmit(topic, msg);
26 | }
27 | };
28 |
29 | const transportA = rawr.transports.socketio({
30 | connection: a,
31 | pubTopic: 'aPub',
32 | subTopic: 'bPub'
33 | });
34 | const transportB = rawr.transports.socketio({
35 | connection: b,
36 | pubTopic: 'bPub',
37 | subTopic: 'aPub'
38 | });
39 |
40 | return { transportA, transportB };
41 | }
42 |
43 | function helloTest(name) {
44 | return new Promise((resolve, reject) => {
45 | if (name === 'bad') {
46 | const error = new Error('bad name !');
47 | error.code = 9000;
48 | return reject(error);
49 | }
50 | setTimeout(() => {
51 | return resolve(`hello, ${name}`);
52 | }, 100);
53 | });
54 | }
55 |
56 | function add(a, b) {
57 | return a + b;
58 | }
59 |
60 | function subtract(a, b) {
61 | return a - b;
62 | }
63 |
64 | describe('socketio', () => {
65 | it('should make a client', (done) => {
66 | const { transportA } = mockTransports();
67 | const client = rawr({ transport: transportA });
68 | client.should.be.a('object');
69 | client.addHandler.should.be.a('function');
70 | done();
71 | });
72 |
73 | it('client should make a successful rpc call to another peer', async () => {
74 | const { transportA, transportB } = mockTransports();
75 | const clientA = rawr({ transport: transportA, handlers: { add } });
76 | const clientB = rawr({ transport: transportB, handlers: { subtract } });
77 |
78 | const resultA = await clientA.methods.subtract(7, 2);
79 | const resultB = await clientB.methods.add(1, 2);
80 | resultA.should.equal(5);
81 | resultB.should.equal(3);
82 | });
83 |
84 | it('client should make an unsuccessful rpc call to a peer', async () => {
85 | const { transportA, transportB } = mockTransports();
86 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
87 | const clientB = rawr({ transport: transportB });
88 |
89 | clientA.should.be.an('object');
90 | try {
91 | await clientB.methods.helloTest('bad');
92 | } catch (error) {
93 | error.code.should.equal(9000);
94 | }
95 | });
96 |
97 | it('client handle an rpc under a specified timeout', async () => {
98 | const { transportA, transportB } = mockTransports();
99 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
100 | const clientB = rawr({ transport: transportB, timeout: 1000 });
101 |
102 | clientA.should.be.an('object');
103 | const result = await clientB.methods.helloTest('luis');
104 | result.should.equal('hello, luis');
105 | });
106 |
107 | it('client handle an rpc timeout', async () => {
108 | const { transportA, transportB } = mockTransports();
109 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
110 | const clientB = rawr({ transport: transportB, timeout: 10 });
111 |
112 | clientA.should.be.an('object');
113 | try {
114 | await clientB.methods.helloTest('luis');
115 | } catch (error) {
116 | error.code.should.equal(504);
117 | }
118 | });
119 |
120 | it('client should be able to send a notification to a server', (done) => {
121 | const { transportA, transportB } = mockTransports();
122 | const clientA = rawr({ transport: transportA });
123 | const clientB = rawr({ transport: transportB });
124 |
125 | clientA.notifications.ondoSomething((someData) => {
126 | someData.should.equal('testing_notification');
127 | done();
128 | });
129 |
130 | clientB.notifiers.doSomething('testing_notification');
131 | });
132 | });
133 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rawr (a.k.a. RAWRpc)
2 |
3 | [](https://nodei.co/npm/rawr/) 
4 |
5 | Remote Procedure Calls ([JSON-RPC](https://www.jsonrpc.org/specification)) sent over any [EventEmitter](https://nodejs.org/dist/latest-v12.x/docs/api/events.html#events_class_eventemitter)-based transport. [WebWorkers](/transports/worker), [WebSockets](/transports/websocket), [MQTT](/transports/mqtt), and more!
6 |
7 | 
8 |
9 | ## Installation
10 |
11 | `npm install rawr`
12 |
13 |
14 | ## Using rawr with a webworker
15 |
16 | Every rawr peer can act as both a client and a server, and make remote method calls in either direction.
17 |
18 | For example, we can use methods that belong to a webworker.
19 |
20 | #### In our worker.js file:
21 | ```javascript
22 | import rawr, { transports } from 'rawr';
23 |
24 | // In this instantiation, we can pass in an object to
25 | // `methods` that is exposed to our web page (see below)
26 | const peer = rawr({
27 | transport: transports.worker(),
28 | methods: { calculatePrimes },
29 | });
30 |
31 | function calculatePrimes(howMany) {
32 | // Do something CPU intensive in this thread that
33 | // would otherwise be too expensive for our web page
34 | ...
35 | return primes;
36 | }
37 | ```
38 |
39 | #### In our web page:
40 | ```javascript
41 | import rawr, { transports } from 'rawr';
42 |
43 | const myWorker = new Worker('/worker.js');
44 | const peer = rawr({transport: transports.worker(myWorker)});
45 |
46 | // Remote methods are *~automatically available~*
47 | const result = await peer.methods.calculatePrimes(349582);
48 | ```
49 |
50 | The methods are available to the rawr peer through the magic of [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
51 |
52 | 
53 |
54 | ## Using rawr with a websocket
55 |
56 | We could use rawr to make calls to a remote server such as a websocket.
57 | Simply use a different transport.
58 |
59 | #### on our web page:
60 | ```javascript
61 | import rawr, { transports } from 'rawr';
62 |
63 | const socket = new WebSocket('ws://localhost:8080');
64 |
65 | socket.onopen = (event) => {
66 | // create the rawr peer
67 | const peer = rawr({
68 | transport: transports.websocket(socket)
69 | });
70 | };
71 | ```
72 |
73 | The websocket server could even make *arbitrary calls to the client!*
74 |
75 | #### on the server:
76 | ```javascript
77 | socketServer.on('connection', (socket) => {
78 | const peer = rawr({
79 | transport: transports.websocket(socket)
80 | });
81 |
82 | const result = await peer.methods.doSomethingOnClient();
83 | });
84 | ```
85 |
86 | ## Handling Notifications
87 |
88 | Peers can also send each other [notifications](https://www.jsonrpc.org/specification#notification):
89 |
90 | ```javascript
91 | peer.notifiers.saySomething('hello');
92 | ```
93 |
94 | Receiving those notifications from another peer is just as simple:
95 | ```javascript
96 | peer.notifications.onsaySomething((words) => {
97 | console.log(words); //hello
98 | });
99 | ```
100 |
101 |
102 | ## Transports
103 |
104 | Transporst are simply [EventEmitters](https://nodejs.org/dist/latest-v12.x/docs/api/events.html#events_class_eventemitter) that do two things:
105 |
106 | They emit ([json-rpc](https://www.jsonrpc.org/specification)) objects on an `rpc` topic when receiving data.
107 | ```javascript
108 | transport.emit('rpc', {jsonrpc:'2.0', id: 1, method: 'add', params: [2, 3]});
109 | ```
110 |
111 | They send rpc objects out.
112 | ```javascript
113 | transport.send({jsonrpc:'2.0', id: 1, method: 'subtract', params: [5, 4]});
114 | ```
115 |
116 | While, websockets, mqtt, and webworkers are common, transports could be built from any form of communication you wish!
117 |
118 |
119 | ## Custom Configuration for Method invocations
120 |
121 | if you need to pass configuration specific method invocations, you can uses the `methodsExt` property of a rawr instance.
122 |
123 | For example, if you want to specify a specific timeout for a method call you can use a configuration object as the last parameter:
124 | ```javascript
125 | try {
126 | const result = await peer.methodsExt.doSomething(a, b, { timeout: 100 });
127 | } catch(e) {
128 | // method took longer than a 100 millseconds
129 | }
130 | ```
131 |
132 | This also works for customizaton of the transport.
133 | For example, you may want to pass configuration for transferable objects to a webWorker:
134 | ```javascript
135 | const result = await peer.methodsExt.processImage({ imageBitmap, stuff }, {
136 | postMessageOptions: { transfer: [imageBitmap] }
137 | });
138 | ```
139 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const EventEmitter = require('eventemitter3');
2 | const transports = require('./transports');
3 |
4 | function rawr({ transport, timeout = 0, handlers = {}, methods, idGenerator }) {
5 | let callId = 0;
6 | // eslint-disable-next-line no-param-reassign
7 | methods = methods || handlers || {}; // backwards compat
8 | const pendingCalls = {};
9 | const methodHandlers = {};
10 | const notificationEvents = new EventEmitter();
11 | notificationEvents.on = notificationEvents.on.bind(notificationEvents);
12 |
13 | transport.on('rpc', (msg) => {
14 | if (msg.id) {
15 | // handle an RPC request
16 | if (msg.params && methodHandlers[msg.method]) {
17 | methodHandlers[msg.method](msg);
18 | return;
19 | }
20 | // handle an RPC result
21 | const promise = pendingCalls[msg.id];
22 | if (promise) {
23 | if (promise.timeoutId) {
24 | clearTimeout(promise.timeoutId);
25 | }
26 | delete pendingCalls[msg.id];
27 | if (msg.error) {
28 | promise.reject(msg.error);
29 | }
30 | return promise.resolve(msg.result);
31 | }
32 | return;
33 | }
34 | // handle a notification
35 | msg.params.unshift(msg.method);
36 | notificationEvents.emit(...msg.params);
37 | });
38 |
39 | function addHandler(methodName, handler) {
40 | methodHandlers[methodName] = (msg) => {
41 | Promise.resolve()
42 | .then(() => {
43 | return handler.apply(this, msg.params);
44 | })
45 | .then((result) => {
46 | transport.send({
47 | id: msg.id,
48 | result
49 | });
50 | })
51 | .catch((error) => {
52 | const serializedError = { message: error.message };
53 | if (error.code) {
54 | serializedError.code = error.code;
55 | }
56 | transport.send({
57 | id: msg.id,
58 | error: serializedError
59 | });
60 | });
61 | };
62 | }
63 |
64 | Object.keys(methods).forEach((m) => {
65 | addHandler(m, methods[m]);
66 | });
67 |
68 | function sendMessage(method, params, config) {
69 | const id = idGenerator ? idGenerator() : ++callId;
70 | const msg = {
71 | jsonrpc: '2.0',
72 | method,
73 | params,
74 | id
75 | };
76 |
77 | let timeoutId;
78 | if (config.timeout || timeout) {
79 | timeoutId = setTimeout(() => {
80 | if (pendingCalls[id]) {
81 | const err = new Error('RPC timeout');
82 | err.code = 504;
83 | pendingCalls[id].reject(err);
84 | delete pendingCalls[id];
85 | }
86 | }, config.timeout || timeout);
87 | }
88 |
89 | const response = new Promise((resolve, reject) => {
90 | pendingCalls[id] = { resolve, reject, timeoutId };
91 | });
92 |
93 | transport.send(msg, config);
94 |
95 | return response;
96 | }
97 |
98 | const methodsProxy = new Proxy({}, {
99 | get: (target, name) => {
100 | return (...args) => {
101 | return sendMessage(name, args, {});
102 | };
103 | }
104 | });
105 |
106 | const configurableMethodsProxy = new Proxy({}, {
107 | get: (target, name) => {
108 | return (...args) => {
109 | let config;
110 | if (args.length) {
111 | const testArg = args.pop();
112 | if (testArg && typeof testArg === 'object' && !Array.isArray(testArg)) {
113 | config = testArg;
114 | } else {
115 | args.push(testArg);
116 | }
117 | }
118 | return sendMessage(name, args, config || {});
119 | };
120 | }
121 | });
122 |
123 |
124 | const notifiers = new Proxy({}, {
125 | get: (target, name) => {
126 | return (...args) => {
127 | const msg = {
128 | jsonrpc: '2.0',
129 | method: name,
130 | params: args
131 | };
132 | transport.send(msg);
133 | };
134 | }
135 | });
136 |
137 | const configurableNotifiersProxy = new Proxy({}, {
138 | get: (target, name) => {
139 | return (...args) => {
140 | let config;
141 | if (args.length) {
142 | const testArg = args.pop();
143 | if (testArg && typeof testArg === 'object' && !Array.isArray(testArg)) {
144 | config = testArg;
145 | } else {
146 | args.push(testArg);
147 | }
148 | }
149 | const msg = {
150 | jsonrpc: '2.0',
151 | method: name,
152 | params: args
153 | };
154 | transport.send(msg, config || {});
155 | };
156 | }
157 | });
158 |
159 | const notifications = new Proxy({}, {
160 | get: (target, name) => {
161 | return (callback) => {
162 | notificationEvents.on(name.substring(2), (...args) => {
163 | return callback.apply(callback, args);
164 | });
165 | };
166 | }
167 | });
168 |
169 | return {
170 | methods: methodsProxy,
171 | methodsExt: configurableMethodsProxy,
172 | addHandler,
173 | notifications,
174 | notifiers,
175 | notifiersExt: configurableNotifiersProxy,
176 | transport,
177 | };
178 | }
179 |
180 | rawr.transports = transports;
181 |
182 | module.exports = rawr;
183 |
--------------------------------------------------------------------------------
/test/mqtt_transport.js:
--------------------------------------------------------------------------------
1 | const chai = require('chai');
2 | const EventEmitter = require('eventemitter3');
3 | const rawr = require('../');
4 |
5 | chai.should();
6 |
7 | function mockTransports() {
8 | const a = new EventEmitter();
9 | const b = new EventEmitter();
10 |
11 | a.publish = (topic, msg) => {
12 | b.emit('message', 'aPub', msg);
13 | };
14 | a.subscribe = () => {};
15 |
16 | b.publish = (topic, msg) => {
17 | a.emit('message', 'bPub', msg);
18 | };
19 | b.subscribe = () => {};
20 |
21 | const transportA = rawr.transports.mqtt({
22 | connection: a,
23 | pubTopic: 'aPub',
24 | subTopic: 'bPub'
25 | });
26 | transportA.a = a;
27 | const transportB = rawr.transports.mqtt({
28 | connection: b,
29 | pubTopic: 'bPub',
30 | subTopic: 'aPub'
31 | });
32 | transportB.b = b;
33 |
34 | const transportDontSub = rawr.transports.mqtt({
35 | connection: a,
36 | pubTopic: 'aPub',
37 | subTopic: 'bPub',
38 | subscribe: false,
39 | });
40 |
41 | const transportBadTopic = rawr.transports.mqtt({
42 | connection: a,
43 | pubTopic: 'somethingElse',
44 | subTopic: 'somethingElse',
45 | });
46 |
47 | return { transportA, transportB, transportDontSub, transportBadTopic };
48 | }
49 |
50 | function helloTest(name) {
51 | return new Promise((resolve, reject) => {
52 | if (name === 'bad') {
53 | const error = new Error('bad name !');
54 | error.code = 9000;
55 | return reject(error);
56 | }
57 | setTimeout(() => {
58 | return resolve(`hello, ${name}`);
59 | }, 100);
60 | });
61 | }
62 |
63 | function add(a, b) {
64 | return a + b;
65 | }
66 |
67 | function subtract(a, b) {
68 | return a - b;
69 | }
70 |
71 | describe('mqtt', () => {
72 | it('should make a client', (done) => {
73 | const { transportA, transportB } = mockTransports();
74 | transportB.b.publish('bPub', 'check bad json');
75 | const client = rawr({ transport: transportA });
76 | client.should.be.a('object');
77 | client.addHandler.should.be.a('function');
78 | done();
79 | });
80 |
81 | it('should make a client with an already subscribed transport', (done) => {
82 | const { transportDontSub, transportB } = mockTransports();
83 | transportB.b.publish('bPub', 'check bad json');
84 | const client = rawr({ transport: transportDontSub });
85 | client.should.be.a('object');
86 | client.addHandler.should.be.a('function');
87 | done();
88 | });
89 |
90 | it('client should make a successful rpc call to another peer', async () => {
91 | const { transportA, transportB } = mockTransports();
92 | const clientA = rawr({ transport: transportA, handlers: { add } });
93 | const clientB = rawr({ transport: transportB, handlers: { subtract } });
94 |
95 | const resultA = await clientA.methods.subtract(7, 2);
96 | const resultB = await clientB.methods.add(1, 2);
97 | resultA.should.equal(5);
98 | resultB.should.equal(3);
99 | });
100 |
101 | it('client should handle bad messages on topic', async () => {
102 | const { transportA, transportB } = mockTransports();
103 | const clientA = rawr({ transport: transportA });
104 | const clientB = rawr({ transport: transportB, handlers: { subtract } });
105 |
106 | transportA.a.publish('aPub', `{"something": "bad"}`);
107 | const resultA = await clientA.methods.subtract(7, 2);
108 | resultA.should.equal(5);
109 | });
110 |
111 | it('client should make an unsuccessful rpc call to a peer', async () => {
112 | const { transportA, transportB } = mockTransports();
113 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
114 | const clientB = rawr({ transport: transportB });
115 |
116 | clientA.should.be.an('object');
117 | try {
118 | await clientB.methods.helloTest('bad');
119 | } catch (error) {
120 | error.code.should.equal(9000);
121 | }
122 | });
123 |
124 | it('client handle an rpc under a specified timeout', async () => {
125 | const { transportA, transportB } = mockTransports();
126 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
127 | const clientB = rawr({ transport: transportB, timeout: 1000 });
128 |
129 | clientA.should.be.an('object');
130 | const result = await clientB.methods.helloTest('luis');
131 | result.should.equal('hello, luis');
132 | });
133 |
134 | it('client handle an rpc timeout', async () => {
135 | const { transportA, transportB } = mockTransports();
136 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
137 | const clientB = rawr({ transport: transportB, timeout: 10 });
138 |
139 | clientA.should.be.an('object');
140 | try {
141 | await clientB.methods.helloTest('luis');
142 | } catch (error) {
143 | error.code.should.equal(504);
144 | }
145 | });
146 |
147 | it('client handle an rpc timeout becuase topic didnt match', async () => {
148 | const { transportA, transportBadTopic } = mockTransports();
149 | const clientA = rawr({ transport: transportA, handlers: { helloTest } });
150 | const clientB = rawr({ transport: transportBadTopic, timeout: 10 });
151 |
152 | clientA.should.be.an('object');
153 | try {
154 | await clientB.methods.helloTest('luis');
155 | } catch (error) {
156 | error.code.should.equal(504);
157 | }
158 | });
159 |
160 | it('client should be able to send a notification to a server', (done) => {
161 | const { transportA, transportB } = mockTransports();
162 | const clientA = rawr({ transport: transportA });
163 | const clientB = rawr({ transport: transportB });
164 |
165 | clientA.notifications.ondoSomething((someData) => {
166 | someData.should.equal('testing_notification');
167 | done();
168 | });
169 |
170 | clientB.notifiers.doSomething('testing_notification');
171 | });
172 | });
173 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | const chai = require('chai');
2 | const EventEmitter = require('eventemitter3');
3 | const b64id = require('b64id');
4 | const rawr = require('../');
5 |
6 | chai.should();
7 |
8 | function mockTransports() {
9 | const a = new EventEmitter();
10 | const b = new EventEmitter();
11 |
12 | a.on('message', (msg) => {
13 | a.emit('rpc', msg);
14 | });
15 | a.send = (msg, config) => {
16 | b.emit('message', msg);
17 | if (config) {
18 | b.emit('config', config);
19 | }
20 | };
21 |
22 | b.on('message', (msg) => {
23 | b.emit('rpc', msg);
24 | });
25 | b.send = (msg, config) => {
26 | if (config) {
27 | a.emit('config', config);
28 | }
29 | a.emit('message', msg);
30 | };
31 |
32 | return { a, b };
33 | }
34 |
35 |
36 | function helloTest(name) {
37 | return new Promise((resolve, reject) => {
38 | if (name === 'bad') {
39 | const error = new Error('bad name !');
40 | error.code = 9000;
41 | return reject(error);
42 | }
43 | setTimeout(() => {
44 | return resolve(`hello, ${name}`);
45 | }, 100);
46 | });
47 | }
48 |
49 | function add(a, b) {
50 | return a + b;
51 | }
52 |
53 | function subtract(a, b) {
54 | return a - b;
55 | }
56 |
57 | function slowFunction() {
58 | return new Promise((resolve) => {
59 | setTimeout(() => {
60 | resolve('slow');
61 | }, 300);
62 | });
63 | }
64 |
65 | function hi() {
66 | return 'hi';
67 | }
68 |
69 | describe('rawr', () => {
70 | it('should make a client', (done) => {
71 | const client = rawr({ transport: mockTransports().a });
72 | client.should.be.a('object');
73 | client.addHandler.should.be.a('function');
74 | done();
75 | });
76 |
77 | it('client should make a successful rpc call to another peer', async () => {
78 | const { a, b } = mockTransports();
79 | const clientA = rawr({ transport: a, handlers: { add } });
80 | const clientB = rawr({ transport: b, handlers: { subtract } });
81 |
82 | const resultA = await clientA.methods.subtract(7, 2);
83 | const resultB = await clientB.methods.add(1, 2);
84 | resultA.should.equal(5);
85 | resultB.should.equal(3);
86 | });
87 |
88 | it('client should make a successful rpc call to another peer with custom id generators', async () => {
89 | const { a, b } = mockTransports();
90 | const clientA = rawr({ transport: a, handlers: { add }, idGenerator: b64id.generateId });
91 | const clientB = rawr({ transport: b, handlers: { subtract, idGenerator: b64id.generateId } });
92 |
93 | const resultA = await clientA.methods.subtract(7, 2);
94 | const resultB = await clientB.methods.add(1, 2);
95 | resultA.should.equal(5);
96 | resultB.should.equal(3);
97 | });
98 |
99 | it('client should make an unsuccessful rpc call to a peer', async () => {
100 | const { a, b } = mockTransports();
101 | const clientA = rawr({ transport: a, handlers: { helloTest } });
102 | const clientB = rawr({ transport: b });
103 |
104 | clientA.should.be.an('object');
105 | try {
106 | await clientB.methods.helloTest('bad');
107 | } catch (error) {
108 | error.code.should.equal(9000);
109 | }
110 | });
111 |
112 | it('client handle an rpc under a specified timeout', async () => {
113 | const { a, b } = mockTransports();
114 | const clientA = rawr({ transport: a, handlers: { helloTest } });
115 | const clientB = rawr({ transport: b, timeout: 1000 });
116 |
117 | clientA.should.be.an('object');
118 | const result = await clientB.methods.helloTest('luis');
119 | result.should.equal('hello, luis');
120 | });
121 |
122 | it('client handle an rpc timeout', async () => {
123 | const { a, b } = mockTransports();
124 | const clientA = rawr({ transport: a, handlers: { helloTest } });
125 | const clientB = rawr({ transport: b, timeout: 10 });
126 |
127 | clientA.should.be.an('object');
128 | try {
129 | await clientB.methods.helloTest('luis');
130 | } catch (error) {
131 | error.code.should.equal(504);
132 | }
133 | });
134 |
135 | it('client should be able to send a notification to a server', (done) => {
136 | const { a, b } = mockTransports();
137 | const clientA = rawr({ transport: a });
138 | const clientB = rawr({ transport: b });
139 |
140 | clientA.notifications.ondoSomething((someData) => {
141 | someData.should.equal('testing_notification');
142 | done();
143 | });
144 |
145 | clientB.notifiers.doSomething('testing_notification');
146 | });
147 |
148 | it('client should have notifiersExt method', () => {
149 | const { a } = mockTransports();
150 | const client = rawr({ transport: a });
151 | client.should.have.property('notifiersExt');
152 | (typeof client.notifiersExt).should.equal('object');
153 | });
154 |
155 | it('client should be able to send a notification with notifiersExt', (done) => {
156 | const { a, b } = mockTransports();
157 | const clientA = rawr({ transport: a });
158 | const clientB = rawr({ transport: b });
159 |
160 | clientA.notifications.ondoSomething((someData) => {
161 | someData.should.equal('testing_notification_ext');
162 | done();
163 | });
164 |
165 | clientB.notifiersExt.doSomething('testing_notification_ext');
166 | });
167 |
168 | it('client should pass config to transport when using notifiersExt', (done) => {
169 | const { a, b } = mockTransports();
170 | const clientA = rawr({ transport: a });
171 | const clientB = rawr({ transport: b });
172 |
173 | let receivedConfig = false;
174 | a.on('config', (config) => {
175 | config.should.deep.equal({ postMessageOptions: { transfer: ['test'] } });
176 | receivedConfig = true;
177 | });
178 |
179 | clientA.notifications.ondoConfigTest((someData) => {
180 | someData.should.equal('config_test');
181 | receivedConfig.should.equal(true);
182 | done();
183 | });
184 |
185 | clientB.notifiersExt.doConfigTest('config_test', { postMessageOptions: { transfer: ['test'] } });
186 | });
187 |
188 | it('client should fail on a configured timeout', async () => {
189 | const { a, b } = mockTransports();
190 | const clientA = rawr({ transport: a, handlers: { slowFunction, hi } });
191 | const clientB = rawr({ transport: b, handlers: { slowFunction, add } });
192 |
193 | const resultA = await clientA.methodsExt.slowFunction({ timeout: 1000 });
194 | resultA.should.equal('slow');
195 | const resultA2 = await clientA.methodsExt.add(1, 2, null);
196 | resultA2.should.equal(3);
197 | try {
198 | await clientB.methodsExt.slowFunction({ timeout: 100 });
199 |
200 | } catch (error) {
201 | error.code.should.equal(504);
202 | }
203 | try {
204 | await clientB.methodsExt.slowFunction('useless param', { timeout: 100 });
205 | } catch (error) {
206 | error.code.should.equal(504);
207 | }
208 | const resultB2 = await clientB.methodsExt.hi();
209 | resultB2.should.equal('hi');
210 |
211 | });
212 | });
213 |
--------------------------------------------------------------------------------
/dist/bundle.js:
--------------------------------------------------------------------------------
1 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i {
24 | if (msg.id) {
25 | // handle an RPC request
26 | if (msg.params && methodHandlers[msg.method]) {
27 | methodHandlers[msg.method](msg);
28 | return;
29 | }
30 | // handle an RPC result
31 | const promise = pendingCalls[msg.id];
32 | if (promise) {
33 | if (promise.timeoutId) {
34 | clearTimeout(promise.timeoutId);
35 | }
36 | delete pendingCalls[msg.id];
37 | if (msg.error) {
38 | promise.reject(msg.error);
39 | }
40 | return promise.resolve(msg.result);
41 | }
42 | return;
43 | }
44 | // handle a notification
45 | msg.params.unshift(msg.method);
46 | notificationEvents.emit(...msg.params);
47 | });
48 |
49 | function addHandler(methodName, handler) {
50 | methodHandlers[methodName] = (msg) => {
51 | Promise.resolve()
52 | .then(() => {
53 | return handler.apply(this, msg.params);
54 | })
55 | .then((result) => {
56 | transport.send({
57 | id: msg.id,
58 | result
59 | });
60 | })
61 | .catch((error) => {
62 | const serializedError = { message: error.message };
63 | if (error.code) {
64 | serializedError.code = error.code;
65 | }
66 | transport.send({
67 | id: msg.id,
68 | error: serializedError
69 | });
70 | });
71 | };
72 | }
73 |
74 | Object.keys(methods).forEach((m) => {
75 | addHandler(m, methods[m]);
76 | });
77 |
78 | function sendMessage(method, params, config) {
79 | const id = idGenerator ? idGenerator() : ++callId;
80 | const msg = {
81 | jsonrpc: '2.0',
82 | method,
83 | params,
84 | id
85 | };
86 |
87 | let timeoutId;
88 | if (config.timeout || timeout) {
89 | timeoutId = setTimeout(() => {
90 | if (pendingCalls[id]) {
91 | const err = new Error('RPC timeout');
92 | err.code = 504;
93 | pendingCalls[id].reject(err);
94 | delete pendingCalls[id];
95 | }
96 | }, config.timeout || timeout);
97 | }
98 |
99 | const response = new Promise((resolve, reject) => {
100 | pendingCalls[id] = { resolve, reject, timeoutId };
101 | });
102 |
103 | transport.send(msg, config);
104 |
105 | return response;
106 | }
107 |
108 | const methodsProxy = new Proxy({}, {
109 | get: (target, name) => {
110 | return (...args) => {
111 | return sendMessage(name, args, {});
112 | };
113 | }
114 | });
115 |
116 | const configurableMethodsProxy = new Proxy({}, {
117 | get: (target, name) => {
118 | return (...args) => {
119 | let config;
120 | if (args.length) {
121 | const testArg = args.pop();
122 | if (testArg && typeof testArg === 'object' && !Array.isArray(testArg)) {
123 | config = testArg;
124 | } else {
125 | args.push(testArg);
126 | }
127 | }
128 | return sendMessage(name, args, config || {});
129 | };
130 | }
131 | });
132 |
133 |
134 | const notifiers = new Proxy({}, {
135 | get: (target, name) => {
136 | return (...args) => {
137 | const msg = {
138 | jsonrpc: '2.0',
139 | method: name,
140 | params: args
141 | };
142 | transport.send(msg);
143 | };
144 | }
145 | });
146 |
147 | const configurableNotifiersProxy = new Proxy({}, {
148 | get: (target, name) => {
149 | return (...args) => {
150 | let config;
151 | if (args.length) {
152 | const testArg = args.pop();
153 | if (testArg && typeof testArg === 'object' && !Array.isArray(testArg)) {
154 | config = testArg;
155 | } else {
156 | args.push(testArg);
157 | }
158 | }
159 | const msg = {
160 | jsonrpc: '2.0',
161 | method: name,
162 | params: args
163 | };
164 | transport.send(msg, config || {});
165 | };
166 | }
167 | });
168 |
169 | const notifications = new Proxy({}, {
170 | get: (target, name) => {
171 | return (callback) => {
172 | notificationEvents.on(name.substring(2), (...args) => {
173 | return callback.apply(callback, args);
174 | });
175 | };
176 | }
177 | });
178 |
179 | return {
180 | methods: methodsProxy,
181 | methodsExt: configurableMethodsProxy,
182 | addHandler,
183 | notifications,
184 | notifiers,
185 | notifiersExt: configurableNotifiersProxy,
186 | transport,
187 | };
188 | }
189 |
190 | rawr.transports = transports;
191 |
192 | module.exports = rawr;
193 |
194 | },{"./transports":4,"eventemitter3":3}],3:[function(require,module,exports){
195 | 'use strict';
196 |
197 | var has = Object.prototype.hasOwnProperty
198 | , prefix = '~';
199 |
200 | /**
201 | * Constructor to create a storage for our `EE` objects.
202 | * An `Events` instance is a plain object whose properties are event names.
203 | *
204 | * @constructor
205 | * @private
206 | */
207 | function Events() {}
208 |
209 | //
210 | // We try to not inherit from `Object.prototype`. In some engines creating an
211 | // instance in this way is faster than calling `Object.create(null)` directly.
212 | // If `Object.create(null)` is not supported we prefix the event names with a
213 | // character to make sure that the built-in object properties are not
214 | // overridden or used as an attack vector.
215 | //
216 | if (Object.create) {
217 | Events.prototype = Object.create(null);
218 |
219 | //
220 | // This hack is needed because the `__proto__` property is still inherited in
221 | // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
222 | //
223 | if (!new Events().__proto__) prefix = false;
224 | }
225 |
226 | /**
227 | * Representation of a single event listener.
228 | *
229 | * @param {Function} fn The listener function.
230 | * @param {*} context The context to invoke the listener with.
231 | * @param {Boolean} [once=false] Specify if the listener is a one-time listener.
232 | * @constructor
233 | * @private
234 | */
235 | function EE(fn, context, once) {
236 | this.fn = fn;
237 | this.context = context;
238 | this.once = once || false;
239 | }
240 |
241 | /**
242 | * Add a listener for a given event.
243 | *
244 | * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
245 | * @param {(String|Symbol)} event The event name.
246 | * @param {Function} fn The listener function.
247 | * @param {*} context The context to invoke the listener with.
248 | * @param {Boolean} once Specify if the listener is a one-time listener.
249 | * @returns {EventEmitter}
250 | * @private
251 | */
252 | function addListener(emitter, event, fn, context, once) {
253 | if (typeof fn !== 'function') {
254 | throw new TypeError('The listener must be a function');
255 | }
256 |
257 | var listener = new EE(fn, context || emitter, once)
258 | , evt = prefix ? prefix + event : event;
259 |
260 | if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;
261 | else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
262 | else emitter._events[evt] = [emitter._events[evt], listener];
263 |
264 | return emitter;
265 | }
266 |
267 | /**
268 | * Clear event by name.
269 | *
270 | * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
271 | * @param {(String|Symbol)} evt The Event name.
272 | * @private
273 | */
274 | function clearEvent(emitter, evt) {
275 | if (--emitter._eventsCount === 0) emitter._events = new Events();
276 | else delete emitter._events[evt];
277 | }
278 |
279 | /**
280 | * Minimal `EventEmitter` interface that is molded against the Node.js
281 | * `EventEmitter` interface.
282 | *
283 | * @constructor
284 | * @public
285 | */
286 | function EventEmitter() {
287 | this._events = new Events();
288 | this._eventsCount = 0;
289 | }
290 |
291 | /**
292 | * Return an array listing the events for which the emitter has registered
293 | * listeners.
294 | *
295 | * @returns {Array}
296 | * @public
297 | */
298 | EventEmitter.prototype.eventNames = function eventNames() {
299 | var names = []
300 | , events
301 | , name;
302 |
303 | if (this._eventsCount === 0) return names;
304 |
305 | for (name in (events = this._events)) {
306 | if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
307 | }
308 |
309 | if (Object.getOwnPropertySymbols) {
310 | return names.concat(Object.getOwnPropertySymbols(events));
311 | }
312 |
313 | return names;
314 | };
315 |
316 | /**
317 | * Return the listeners registered for a given event.
318 | *
319 | * @param {(String|Symbol)} event The event name.
320 | * @returns {Array} The registered listeners.
321 | * @public
322 | */
323 | EventEmitter.prototype.listeners = function listeners(event) {
324 | var evt = prefix ? prefix + event : event
325 | , handlers = this._events[evt];
326 |
327 | if (!handlers) return [];
328 | if (handlers.fn) return [handlers.fn];
329 |
330 | for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
331 | ee[i] = handlers[i].fn;
332 | }
333 |
334 | return ee;
335 | };
336 |
337 | /**
338 | * Return the number of listeners listening to a given event.
339 | *
340 | * @param {(String|Symbol)} event The event name.
341 | * @returns {Number} The number of listeners.
342 | * @public
343 | */
344 | EventEmitter.prototype.listenerCount = function listenerCount(event) {
345 | var evt = prefix ? prefix + event : event
346 | , listeners = this._events[evt];
347 |
348 | if (!listeners) return 0;
349 | if (listeners.fn) return 1;
350 | return listeners.length;
351 | };
352 |
353 | /**
354 | * Calls each of the listeners registered for a given event.
355 | *
356 | * @param {(String|Symbol)} event The event name.
357 | * @returns {Boolean} `true` if the event had listeners, else `false`.
358 | * @public
359 | */
360 | EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
361 | var evt = prefix ? prefix + event : event;
362 |
363 | if (!this._events[evt]) return false;
364 |
365 | var listeners = this._events[evt]
366 | , len = arguments.length
367 | , args
368 | , i;
369 |
370 | if (listeners.fn) {
371 | if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
372 |
373 | switch (len) {
374 | case 1: return listeners.fn.call(listeners.context), true;
375 | case 2: return listeners.fn.call(listeners.context, a1), true;
376 | case 3: return listeners.fn.call(listeners.context, a1, a2), true;
377 | case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
378 | case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
379 | case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
380 | }
381 |
382 | for (i = 1, args = new Array(len -1); i < len; i++) {
383 | args[i - 1] = arguments[i];
384 | }
385 |
386 | listeners.fn.apply(listeners.context, args);
387 | } else {
388 | var length = listeners.length
389 | , j;
390 |
391 | for (i = 0; i < length; i++) {
392 | if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
393 |
394 | switch (len) {
395 | case 1: listeners[i].fn.call(listeners[i].context); break;
396 | case 2: listeners[i].fn.call(listeners[i].context, a1); break;
397 | case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
398 | case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;
399 | default:
400 | if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
401 | args[j - 1] = arguments[j];
402 | }
403 |
404 | listeners[i].fn.apply(listeners[i].context, args);
405 | }
406 | }
407 | }
408 |
409 | return true;
410 | };
411 |
412 | /**
413 | * Add a listener for a given event.
414 | *
415 | * @param {(String|Symbol)} event The event name.
416 | * @param {Function} fn The listener function.
417 | * @param {*} [context=this] The context to invoke the listener with.
418 | * @returns {EventEmitter} `this`.
419 | * @public
420 | */
421 | EventEmitter.prototype.on = function on(event, fn, context) {
422 | return addListener(this, event, fn, context, false);
423 | };
424 |
425 | /**
426 | * Add a one-time listener for a given event.
427 | *
428 | * @param {(String|Symbol)} event The event name.
429 | * @param {Function} fn The listener function.
430 | * @param {*} [context=this] The context to invoke the listener with.
431 | * @returns {EventEmitter} `this`.
432 | * @public
433 | */
434 | EventEmitter.prototype.once = function once(event, fn, context) {
435 | return addListener(this, event, fn, context, true);
436 | };
437 |
438 | /**
439 | * Remove the listeners of a given event.
440 | *
441 | * @param {(String|Symbol)} event The event name.
442 | * @param {Function} fn Only remove the listeners that match this function.
443 | * @param {*} context Only remove the listeners that have this context.
444 | * @param {Boolean} once Only remove one-time listeners.
445 | * @returns {EventEmitter} `this`.
446 | * @public
447 | */
448 | EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
449 | var evt = prefix ? prefix + event : event;
450 |
451 | if (!this._events[evt]) return this;
452 | if (!fn) {
453 | clearEvent(this, evt);
454 | return this;
455 | }
456 |
457 | var listeners = this._events[evt];
458 |
459 | if (listeners.fn) {
460 | if (
461 | listeners.fn === fn &&
462 | (!once || listeners.once) &&
463 | (!context || listeners.context === context)
464 | ) {
465 | clearEvent(this, evt);
466 | }
467 | } else {
468 | for (var i = 0, events = [], length = listeners.length; i < length; i++) {
469 | if (
470 | listeners[i].fn !== fn ||
471 | (once && !listeners[i].once) ||
472 | (context && listeners[i].context !== context)
473 | ) {
474 | events.push(listeners[i]);
475 | }
476 | }
477 |
478 | //
479 | // Reset the array, or remove it completely if we have no more listeners.
480 | //
481 | if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
482 | else clearEvent(this, evt);
483 | }
484 |
485 | return this;
486 | };
487 |
488 | /**
489 | * Remove all listeners, or those of the specified event.
490 | *
491 | * @param {(String|Symbol)} [event] The event name.
492 | * @returns {EventEmitter} `this`.
493 | * @public
494 | */
495 | EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
496 | var evt;
497 |
498 | if (event) {
499 | evt = prefix ? prefix + event : event;
500 | if (this._events[evt]) clearEvent(this, evt);
501 | } else {
502 | this._events = new Events();
503 | this._eventsCount = 0;
504 | }
505 |
506 | return this;
507 | };
508 |
509 | //
510 | // Alias methods names because people roll like that.
511 | //
512 | EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
513 | EventEmitter.prototype.addListener = EventEmitter.prototype.on;
514 |
515 | //
516 | // Expose the prefix.
517 | //
518 | EventEmitter.prefixed = prefix;
519 |
520 | //
521 | // Allow `EventEmitter` to be imported as module namespace.
522 | //
523 | EventEmitter.EventEmitter = EventEmitter;
524 |
525 | //
526 | // Expose the module.
527 | //
528 | if ('undefined' !== typeof module) {
529 | module.exports = EventEmitter;
530 | }
531 |
532 | },{}],4:[function(require,module,exports){
533 | const mqtt = require('./mqtt');
534 | const socketio = require('./socketio');
535 | const websocket = require('./websocket');
536 | const worker = require('./worker');
537 |
538 | module.exports = {
539 | mqtt,
540 | socketio,
541 | websocket,
542 | worker
543 | };
544 | },{"./mqtt":5,"./socketio":6,"./websocket":7,"./worker":8}],5:[function(require,module,exports){
545 | const EventEmitter = require('eventemitter3');
546 |
547 | function transport({ connection, subTopic, pubTopic, subscribe = true }) {
548 | const emitter = new EventEmitter();
549 | if (subscribe) {
550 | connection.subscribe(subTopic);
551 | }
552 | connection.on('message', (topic, message) => {
553 | if (topic === subTopic) {
554 | try {
555 | const msg = JSON.parse(message.toString());
556 | if (msg.method || (msg.id && ('result' in msg || 'error' in msg))) {
557 | emitter.emit('rpc', msg);
558 | }
559 | } catch (err) {
560 | console.error(err);
561 | }
562 | }
563 | });
564 | emitter.send = (msg) => {
565 | connection.publish(pubTopic, JSON.stringify(msg));
566 | };
567 | return emitter;
568 | }
569 |
570 | module.exports = transport;
571 |
572 | },{"eventemitter3":3}],6:[function(require,module,exports){
573 | const EventEmitter = require('eventemitter3');
574 |
575 | function transport({ connection, subTopic, pubTopic }) {
576 | const emitter = new EventEmitter();
577 | connection.on(subTopic, (msg) => {
578 | if (msg.method || (msg.id && ('result' in msg || 'error' in msg))) {
579 | emitter.emit('rpc', msg);
580 | }
581 | });
582 | emitter.send = (msg) => {
583 | connection.emit(pubTopic, msg);
584 | };
585 | return emitter;
586 | }
587 |
588 | module.exports = transport;
589 |
590 | },{"eventemitter3":3}],7:[function(require,module,exports){
591 | const EventEmitter = require('eventemitter3');
592 |
593 | function transport(socket, allowBinary = false) {
594 | const emitter = new EventEmitter();
595 | socket.addEventListener('message', async (evt) => {
596 | let { data } = evt;
597 | if (allowBinary && data instanceof Blob) {
598 | data = await (new Response(data)).text().catch(() => null);
599 | }
600 | if (typeof evt.data === 'string') {
601 | try {
602 | const msg = JSON.parse(evt.data);
603 | if (msg.method || (msg.id && ('result' in msg || 'error' in msg))) {
604 | emitter.emit('rpc', msg);
605 | }
606 | } catch (err) {
607 | // wasn't a JSON message
608 | }
609 | }
610 | });
611 | emitter.send = (msg) => {
612 | socket.send(JSON.stringify(msg));
613 | };
614 | return emitter;
615 | }
616 |
617 | module.exports = transport;
618 |
619 | },{"eventemitter3":3}],8:[function(require,module,exports){
620 | const EventEmitter = require('eventemitter3');
621 |
622 | function dom(webWorker) {
623 | const emitter = new EventEmitter();
624 | webWorker.addEventListener('message', (msg) => {
625 | const { data } = msg;
626 | if (data && (data.method || (data.id && ('result' in data || 'error' in data)))) {
627 | emitter.emit('rpc', data);
628 | }
629 | });
630 | emitter.send = (msg, config) => {
631 | webWorker.postMessage(msg, config ? config.postMessageOptions : undefined);
632 | };
633 | return emitter;
634 | }
635 |
636 | function worker() {
637 | const emitter = new EventEmitter();
638 | self.onmessage = (msg) => {
639 | const { data } = msg;
640 | if (data && (data.method || (data.id && ('result' in data || 'error' in data)))) {
641 | emitter.emit('rpc', data);
642 | }
643 | };
644 | emitter.send = (msg, config) => {
645 | self.postMessage(msg, config ? config.postMessageOptions : undefined);
646 | };
647 | return emitter;
648 | }
649 |
650 | function transport(webWorker) {
651 | if (webWorker) {
652 | return dom(webWorker);
653 | }
654 | return worker();
655 | }
656 |
657 | // backwards compat
658 | transport.dom = dom;
659 | transport.worker = worker;
660 |
661 | module.exports = transport;
662 |
663 | },{"eventemitter3":3}]},{},[1]);
664 |
--------------------------------------------------------------------------------