Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,25 @@ transport.send({jsonrpc:'2.0', id: 1, method: 'subtract', params: [5, 4]});
```

While, websockets, mqtt, and webworkers are common, transports could be built from any form of communication you wish!


## Custom Configuration for Method invocations

if you need to pass configuration specific method invocations, you can uses the `methodsExt` property of a rawr instance.

For example, if you want to specify a specific timeout for a method call you can use a configuration object as the last parameter:
```javascript
try {
const result = await peer.methodsExt.doSomething(a, b, { timeout: 100 });
} catch(e) {
// method took longer than a 100 millseconds
}
```

This also works for customizaton of the transport.
For example, you may want to pass configuration for transferable objects to a webWorker:
```javascript
const result = await peer.methodsExt.processImage({ imageBitmap, stuff }, {
postMessageOptions: { transfer: [imageBitmap] }
});
```
79 changes: 50 additions & 29 deletions dist/bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,40 +66,60 @@ function rawr({ transport, timeout = 0, handlers = {}, methods, idGenerator }) {
addHandler(m, methods[m]);
});

const methodsProxy = new Proxy({}, {
get: (target, name) => {
return (...args) => {
const id = idGenerator ? idGenerator() : ++callId;
const msg = {
jsonrpc: '2.0',
method: name,
params: args,
id
};
function sendMessage(method, params, config) {
const id = idGenerator ? idGenerator() : ++callId;
const msg = {
jsonrpc: '2.0',
method,
params,
id
};

let timeoutId;
if (timeout) {
timeoutId = setTimeout(() => {
if (pendingCalls[id]) {
const err = new Error('RPC timeout');
err.code = 504;
pendingCalls[id].reject(err);
delete pendingCalls[id];
}
}, timeout);
let timeoutId;
if (config.timeout || timeout) {
timeoutId = setTimeout(() => {
if (pendingCalls[id]) {
const err = new Error('RPC timeout');
err.code = 504;
pendingCalls[id].reject(err);
delete pendingCalls[id];
}
}, config.timeout || timeout);
}

const response = new Promise((resolve, reject) => {
pendingCalls[id] = { resolve, reject, timeoutId };
});
const response = new Promise((resolve, reject) => {
pendingCalls[id] = { resolve, reject, timeoutId };
});

transport.send(msg);
transport.send(msg, config);

return response;
return response;
}

const methodsProxy = new Proxy({}, {
get: (target, name) => {
return (...args) => {
return sendMessage(name, args, {});
};
}
});

const configurableMethodsProxy = new Proxy({}, {
get: (target, name) => {
return (...args) => {
let config;
if (args.length) {
const testArg = args.pop();
if (testArg && typeof testArg === 'object' && !Array.isArray(testArg)) {
config = testArg;
}
}
return sendMessage(name, args, config || {});
};
}
});


const notifiers = new Proxy({}, {
get: (target, name) => {
return (...args) => {
Expand All @@ -125,6 +145,7 @@ function rawr({ transport, timeout = 0, handlers = {}, methods, idGenerator }) {

return {
methods: methodsProxy,
methodsExt: configurableMethodsProxy,
addHandler,
notifications,
notifiers,
Expand Down Expand Up @@ -759,8 +780,8 @@ function dom(webWorker) {
emitter.emit('rpc', data);
}
});
emitter.send = (msg) => {
webWorker.postMessage(msg);
emitter.send = (msg, config) => {
webWorker.postMessage(msg, config ? config.postMessageOptions : undefined);
};
return emitter;
}
Expand All @@ -773,8 +794,8 @@ function worker() {
emitter.emit('rpc', data);
}
};
emitter.send = (msg) => {
self.postMessage(msg);
emitter.send = (msg, config) => {
self.postMessage(msg, config ? config.postMessageOptions : undefined);
};
return emitter;
}
Expand Down
71 changes: 46 additions & 25 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,40 +65,60 @@ function rawr({ transport, timeout = 0, handlers = {}, methods, idGenerator }) {
addHandler(m, methods[m]);
});

const methodsProxy = new Proxy({}, {
get: (target, name) => {
return (...args) => {
const id = idGenerator ? idGenerator() : ++callId;
const msg = {
jsonrpc: '2.0',
method: name,
params: args,
id
};
function sendMessage(method, params, config) {
const id = idGenerator ? idGenerator() : ++callId;
const msg = {
jsonrpc: '2.0',
method,
params,
id
};

let timeoutId;
if (timeout) {
timeoutId = setTimeout(() => {
if (pendingCalls[id]) {
const err = new Error('RPC timeout');
err.code = 504;
pendingCalls[id].reject(err);
delete pendingCalls[id];
}
}, timeout);
let timeoutId;
if (config.timeout || timeout) {
timeoutId = setTimeout(() => {
if (pendingCalls[id]) {
const err = new Error('RPC timeout');
err.code = 504;
pendingCalls[id].reject(err);
delete pendingCalls[id];
}
}, config.timeout || timeout);
}

const response = new Promise((resolve, reject) => {
pendingCalls[id] = { resolve, reject, timeoutId };
});
const response = new Promise((resolve, reject) => {
pendingCalls[id] = { resolve, reject, timeoutId };
});

transport.send(msg);
transport.send(msg, config);

return response;
}

return response;
const methodsProxy = new Proxy({}, {
get: (target, name) => {
return (...args) => {
return sendMessage(name, args, {});
};
}
});

const configurableMethodsProxy = new Proxy({}, {
get: (target, name) => {
return (...args) => {
let config;
if (args.length) {
const testArg = args.pop();
if (testArg && typeof testArg === 'object' && !Array.isArray(testArg)) {
config = testArg;
}
}
return sendMessage(name, args, config || {});
};
}
});


const notifiers = new Proxy({}, {
get: (target, name) => {
return (...args) => {
Expand All @@ -124,6 +144,7 @@ function rawr({ transport, timeout = 0, handlers = {}, methods, idGenerator }) {

return {
methods: methodsProxy,
methodsExt: configurableMethodsProxy,
addHandler,
notifications,
notifiers,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rawr",
"version": "0.15.1",
"version": "0.16.0",
"description": "JSON-RPC over simple event emitters",
"dependencies": {},
"devDependencies": {
Expand Down
37 changes: 37 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ function subtract(a, b) {
return a - b;
}

function slowFunction() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('slow');
}, 300);
});
}

function hi() {
return 'hi';
}

describe('rawr', () => {
it('should make a client', (done) => {
const client = rawr({ transport: mockTransports().a });
Expand Down Expand Up @@ -126,4 +138,29 @@ describe('rawr', () => {

clientB.notifiers.doSomething('testing_notification');
});

it('client should fail on a configured timeout', async () => {
const { a, b } = mockTransports();
const clientA = rawr({ transport: a, handlers: { slowFunction, hi } });
const clientB = rawr({ transport: b, handlers: { slowFunction, add } });

const resultA = await clientA.methodsExt.slowFunction({ timeout: 1000 });
resultA.should.equal('slow');
const resultA2 = await clientA.methodsExt.add(1, 2, null);
resultA2.should.equal(3);
try {
await clientB.methodsExt.slowFunction({ timeout: 100 });

} catch (error) {
error.code.should.equal(504);
}
try {
await clientB.methodsExt.slowFunction('useless param', { timeout: 100 });
} catch (error) {
error.code.should.equal(504);
}
const resultB2 = await clientB.methodsExt.hi();
resultB2.should.equal('hi');

});
});
8 changes: 4 additions & 4 deletions transports/worker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ function dom(webWorker) {
emitter.emit('rpc', data);
}
});
emitter.send = (msg) => {
webWorker.postMessage(msg);
emitter.send = (msg, config) => {
webWorker.postMessage(msg, config ? config.postMessageOptions : undefined);
};
return emitter;
}
Expand All @@ -22,8 +22,8 @@ function worker() {
emitter.emit('rpc', data);
}
};
emitter.send = (msg) => {
self.postMessage(msg);
emitter.send = (msg, config) => {
self.postMessage(msg, config ? config.postMessageOptions : undefined);
};
return emitter;
}
Expand Down