nmsg
Version:
Interprocess messenger for Node.js
321 lines (320 loc) • 11.2 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Frame = (function () {
function Frame() {
this.data = null;
this.id = 0;
this.event = '';
this.args = [];
this.callbacks = [];
this.rid = 0; // Response ID.
this.func = 0; // Response callback position.
this.timeout = Frame.timeout; // Timeout in seconds for how long to wait for callbacks.
}
Frame.getNextId = function () {
return Frame.id = (Frame.id % 1000000000) + 1; // Always greater than 0.
};
Frame.prototype.hasCallbacks = function () {
for (var _i = 0, _a = this.args; _i < _a.length; _i++) {
var arg = _a[_i];
if (typeof arg === 'function')
return true;
}
return false;
};
Frame.prototype.isResponse = function () {
return !!this.rid;
};
Frame.id = 0;
Frame.timeout = 5000; // Default timeout (in milliseconds), so that we don't send timeout value with every request.
return Frame;
}());
exports.Frame = Frame;
var FrameOutgoing = (function (_super) {
__extends(FrameOutgoing, _super);
function FrameOutgoing(args, event) {
if (args === void 0) { args = []; }
if (event === void 0) { event = ''; }
_super.call(this);
this.id = Frame.getNextId();
this.event = event;
this.args = args;
}
FrameOutgoing.createResponse = function (request, cb_pos, args) {
var response = new FrameOutgoing(args);
response.rid = request.id;
response.func = cb_pos;
return response;
};
// When a response to some callback is received.
FrameOutgoing.prototype.processResponse = function (response) {
var pos = response.func;
var callback = this.args[pos];
if (typeof callback !== 'function')
return; // Invalid response or function already called.
this.args[pos] = null; // Remove the function as, we will call it now.
callback.apply(null, response.args);
};
FrameOutgoing.prototype.serialize = function () {
var data = {
i: this.id,
e: this.event
};
if (this.args.length) {
data.a = [];
var cbs = [];
for (var i = 0; i < this.args.length; i++) {
var arg = this.args[i];
if (typeof arg === 'function') {
// data.args.push(0); // Just fill function spots with 0, they will be ignored anyways.
cbs.push(i);
this.callbacks.push(arg);
}
else {
data.a.push(arg);
if (Frame.timeout != this.timeout)
data.t = this.timeout / 1000;
}
}
if (cbs.length) {
data.c = cbs;
}
}
// IFrameDataResponse
if (this.rid) {
data.r = this.rid;
data.f = this.func;
}
this.data = data;
return this.data;
};
return FrameOutgoing;
}(Frame));
exports.FrameOutgoing = FrameOutgoing;
var FrameIncoming = (function (_super) {
__extends(FrameIncoming, _super);
function FrameIncoming() {
_super.apply(this, arguments);
}
FrameIncoming.prototype.reply = function (index, args) {
};
FrameIncoming.prototype.createTimedFunction = function (index) {
var _this = this;
var timed_out = false;
var ms = this.timeout * 1000;
var func = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i - 0] = arguments[_i];
}
if (timed_out) {
}
else {
// Send response frame with args.
_this.reply(index, args);
}
};
var timeout = setTimeout(function () {
timed_out = true;
}, ms);
};
FrameIncoming.prototype.unserialize = function (data, onCallback) {
this.data = data;
// IFrameData
if (typeof data.i === 'number')
this.id = data.i;
else
throw Error('Error parsing id');
if (data.t) {
if (typeof data.t == 'number')
this.timeout = data.t;
else
throw Error('Error parsing timeout');
}
else
this.timeout = Frame.timeout;
this.args = [];
if (data.a) {
if (data.a instanceof Array) {
for (var _i = 0, _a = data.a; _i < _a.length; _i++) {
var arg = _a[_i];
this.args.push(arg);
}
}
else
throw Error('Error parsing arguments');
}
else
data.a = [];
this.callbacks = [];
if (data.c) {
if (!(data.c instanceof Array))
throw Error('Error parsing callbacks');
for (var _b = 0, _c = data.c; _b < _c.length; _b++) {
var pos = _c[_b];
var callback = onCallback(this, pos);
this.callbacks.push(callback);
this.args.splice(pos, 0, callback);
}
}
this.event = '';
this.rid = 0;
this.func = 0;
if (data.e) {
// IFrameDataInitiation
if (typeof data.e === 'string')
this.event = data.e;
else
throw Error('Error parsing event');
}
else if (data.r) {
// IFrameDataResponse
if (typeof data.r === 'number')
this.rid = data.r;
else
throw Error('Error parsing resposne id');
if (typeof data.f === 'number')
this.func = data.f;
else
throw Error('Error parsing reponse position');
}
};
return FrameIncoming;
}(Frame));
exports.FrameIncoming = FrameIncoming;
var Router = (function () {
function Router(socket) {
var _this = this;
this.latency = 500; // Client to server latency in milliseconds, expected.
// List of frames (by ID) which had callbacks, we keep track of them to send back responses to callbacks, if received.
this.frame = {};
// List of subscriber functions .on()
this.subs = {};
if (socket) {
this.send = socket.send.bind(socket);
socket.onmessage = function (msg) { _this.onmessage(msg); };
}
}
Router.prototype.genCallack = function (frame, pos) {
var _this = this;
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i - 0] = arguments[_i];
}
_this.dispatch(FrameOutgoing.createResponse(frame, pos, args));
};
};
Router.prototype.getSubList = function (event) {
if (!this.subs[event])
this.subs[event] = [];
return this.subs[event];
};
Router.prototype.pub = function (frame) {
var event = frame.event;
if (!event)
return;
var list = this.getSubList(event);
for (var _i = 0, list_1 = list; _i < list_1.length; _i++) {
var sub = list_1[_i];
sub.apply(null, frame.args);
}
};
Router.prototype.sendData = function (data) {
this.send(data);
};
Router.prototype.dispatch = function (frame) {
var _this = this;
if (frame.hasCallbacks()) {
this.frame[frame.id] = frame;
// Remove this frame after some timeout, if callbacks not called.
setTimeout(function () { delete _this.frame[frame.id]; }, frame.timeout + this.latency);
}
var data = frame.serialize();
// console.log('dispatch', data);
this.sendData(data);
};
Router.prototype.processResponse = function (frame) {
var request = this.frame[frame.rid];
if (!request)
return; // Cannot find the original request.
request.processResponse(frame);
// Remove the original request frame, if all callbacks processed.
if (!request.hasCallbacks())
delete this.frame[request.id];
};
// This function is called by user.
Router.prototype.onmessage = function (msg) {
var frame = new FrameIncoming;
frame.unserialize(msg, this.genCallack.bind(this));
if (frame.isResponse())
this.processResponse(frame);
else
this.pub(frame);
};
Router.prototype.on = function (event, callback) {
var list = this.getSubList(event);
list.push(callback);
return this;
};
Router.prototype.emit = function (event) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var frame = new FrameOutgoing(args, event);
this.dispatch(frame);
return this;
};
return Router;
}());
exports.Router = Router;
// Same as `Router`, but buffers all frames for 5 milliseconds and then sends a list of all frames at once.
var RouterBuffered = (function (_super) {
__extends(RouterBuffered, _super);
function RouterBuffered() {
_super.apply(this, arguments);
this.cycle = 5; // Milliseconds for how long to buffer requests.
this.timer = 0;
this.buffer = [];
}
RouterBuffered.prototype.flush = function () {
var data = { b: this.buffer };
this.send(data);
this.buffer = [];
};
RouterBuffered.prototype.sendData = function (data) {
this.buffer.push(data);
this.startTimer();
};
RouterBuffered.prototype.startTimer = function () {
var _this = this;
if (!this.timer) {
this.timer = setTimeout(function () {
_this.timer = 0;
_this.flush();
}, this.cycle);
}
};
RouterBuffered.prototype.onmessage = function (msg) {
console.log('msg', msg);
if (typeof msg != 'object')
return;
if (msg.b) {
if (!(msg.b instanceof Array))
return;
for (var _i = 0, _a = msg.b; _i < _a.length; _i++) {
var fmsg = _a[_i];
_super.prototype.onmessage.call(this, fmsg);
}
}
else
_super.prototype.onmessage.call(this, msg);
};
return RouterBuffered;
}(Router));
exports.RouterBuffered = RouterBuffered;