UNPKG

melf

Version:

(A)Synchronous remote procedure calls

122 lines (109 loc) 3.86 kB
// alias#token%rpname/body // &token/body // |token/body const AntenaEmitter = require("antena/lib/emitter"); const max = parseInt("zzzzzz", 36); const rpcallhelper = (melf, recipient, rpname, data, callback) => { melf._counter++; if (melf._counter === max) melf._counter = 1; const token = melf._counter.toString(36); melf._callbacks[token] = callback; return recipient+"/"+melf.alias+"#"+token+"%"+rpname+"/"+JSON.stringify(data); }; const localize = (alias) => (line) => line.startsWith(" at ") ? " " + alias + " " + line.substring(4) : line; const makeonmeteor = (melf) => (meteor) => { const head = meteor.substring(0, meteor.indexOf("/")); if (!melf._done.delete(head)) { melf._done.add(head); const body = JSON.parse(meteor.substring(head.length+1)); if (head[0] === "&" || head[0] === "|") { const token = head.substring(1); const callback = melf._callbacks[token]; delete melf._callbacks[token]; if (head[0] === "&") { callback(null, body); } else { error = new Error(body[1]); error.name = body[0]; error.stack = body[0]+": "+body[1]+"\n"+body[2].join("\n"); callback(error); } } else { const alias = head.substring(0, head.indexOf("#")); const token = head.substring(alias.length+1, head.indexOf("%")); const pname = head.substring(alias.length+1+token.length+1); const callback = (error, result) => { if (error) { const lines = error.stack.substring(error.name.length + 2 + error.message.length + 1).split("\n"); melf._emitter.post(alias+"/|"+token+"/"+JSON.stringify([error.name, error.message, lines.map(localize(melf.alias))])); } else { melf._emitter.post(alias+"/&"+token+"/"+JSON.stringify(result)); } }; if (pname in melf.rprocedures) { melf.rprocedures[pname](alias, body, callback); } else { callback(new Error("Procedure not found: "+pname)); } } } }; function rpcall (recipient, rpname, data, callback) { if (callback) return this._emitter.post(rpcallhelper(this, recipient, rpname, data, callback)); let pending = true; let result = null; this._emitter.pull(rpcallhelper(this, recipient, rpname, data, (error, data) => { if (error) { error.stack = ( error.name + ": " + error.message + "\n" + error.stack.substring(error.name.length+2+error.message.length+1) + "\n" + (new Error("foo")).stack.substring("Error: foo\n".length).split("\n").map(localize(this.alias)).join("\n")); throw error; } pending = false; result = data; })).split("\n").forEach(this._onmeteor); while (pending) this._emitter.pull("").split("\n").forEach(this._onmeteor); return result; } function terminate () { return this._emitter.terminate(); } function destroy () { return this._emitter.destroy(); } function onterminate () { this._melf.onterminate(); }; const noop = () => {}; module.exports = (address, alias, callback) => { if (address && typeof address === "object" && "_receptor" in address) address = address._receptor; AntenaEmitter(address, alias, (error, emitter) => { if (error) return callback(error); const melf = new Promise((resolve, reject) => { emitter.then(resolve, reject) }); const onmeteor = makeonmeteor(melf); melf._emitter = emitter; melf._callbacks = {__proto__:null}; melf._counter = 0; melf._done = new Set(); melf._onmeteor = onmeteor; melf.rprocedures = {__proto__:null}; melf.rpcall = rpcall; melf.alias = alias; melf.destroy = destroy; melf.terminate = terminate; melf.onterminate = noop; emitter._melf = melf; emitter.onpush = onmeteor; emitter.onterminate = onterminate; callback(null, melf); }); };