alm
Version:
The best IDE for TypeScript
214 lines (213 loc) • 9.56 kB
JavaScript
;
// This code is designed to be used by both the server and the client
// Very similar to workerlib but designed to work with socket.io
// The client is the requestor (hence parent like) and the server is the responder (hence child like)
// client == parent
// server == child
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Allcast and Broadcast both come through as a message of the following type
*/
exports.anycastMessageName = 'anycast';
__export(require("../common/events"));
/** Creates a Guid (UUID v4) */
function createId() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
var RequesterResponder = /** @class */ (function () {
function RequesterResponder() {
var _this = this;
///////////////////////////////// REQUESTOR /////////////////////////
this.currentListeners = {};
/** Only relevant when we only want the last of this type */
this.currentLastOfType = {};
this.pendingRequests = Object.create(null);
this.pendingRequestsChanged = function (pending) { return null; };
this.sendToServerHeart = function (data, message) {
// If we don't have a server exit
if (!_this.getSocket()) {
console.log('SEND ERR: no server when you tried to send :', message);
return Promise.reject(new Error("No socket active to recieve message: " + message));
}
// Initialize if this is the first call of this type
if (!_this.currentListeners[message])
_this.currentListeners[message] = {};
// Create an id unique to this call and store the defered against it
var id = createId();
var promise = new Promise(function (resolve, reject) {
_this.currentListeners[message][id] = { resolve: resolve, reject: reject, promise: promise };
});
// Send data to worker
_this.pendingRequests[id] = message;
_this.pendingRequestsChanged(Object.keys(_this.pendingRequests).map(function (k) { return _this.pendingRequests[k]; }));
_this.getSocket().emit('message', { message: message, id: id, data: data, isRequest: true });
return promise;
};
this.responders = {};
this.processRequest = function (m) {
var parsed = m;
if (!parsed.message || !_this.responders[parsed.message]) {
// TODO: handle this error scenario. Either the message is invalid or we do not have a registered responder
return;
}
var message = parsed.message;
var responsePromise;
try {
responsePromise = _this.responders[message](parsed.data, _this.client);
}
catch (err) {
responsePromise = Promise.reject({ method: message, message: err.message, stack: err.stack, details: err.details || {} });
}
responsePromise
.then(function (response) {
_this.getSocket().emit('message', {
message: message,
/** Note: to process a request we just pass the id as we recieve it */
id: parsed.id,
data: response,
error: null,
isRequest: false
});
})
.catch(function (error) {
_this.getSocket().emit('message', {
message: message,
/** Note: to process a request we just pass the id as we recieve it */
id: parsed.id,
data: null,
error: error,
isRequest: false
});
});
};
}
RequesterResponder.prototype.startListening = function () {
var _this = this;
try {
if (!this.getSocket()) {
console.log('You started listening without a socket!');
return;
}
var socket = this.getSocket();
socket.on('error', function (err) {
});
socket.on('message', function (message) {
if (message.isRequest) {
_this.processRequest(message);
}
else {
_this.processResponse(message);
}
});
}
catch (err) {
console.log('Socket : terminal error in listening', err);
}
};
/** process a message from the server */
RequesterResponder.prototype.processResponse = function (m) {
var _this = this;
var parsed = m;
if (!parsed.message || !parsed.id) {
console.log('SERVER ERR: Invalid JSON data from server:', m);
}
else if (!this.currentListeners[parsed.message] || !this.currentListeners[parsed.message][parsed.id]) {
console.log('SERVER ERR: No one was listening:', parsed.message, parsed.data);
}
else {
delete this.pendingRequests[parsed.id];
this.pendingRequestsChanged(Object.keys(this.pendingRequests).map(function (k) { return _this.pendingRequests[k]; }));
if (parsed.error) {
this.currentListeners[parsed.message][parsed.id].reject(parsed.error);
console.log(parsed.error);
console.log(parsed.error.stack);
}
else {
this.currentListeners[parsed.message][parsed.id].resolve(parsed.data);
}
delete this.currentListeners[parsed.message][parsed.id];
// If there is current last one queued then that needs to be resurrected
if (this.currentLastOfType[parsed.message]) {
var last_1 = this.currentLastOfType[parsed.message];
delete this.currentLastOfType[parsed.message];
var lastPromise = this.sendToServerHeart(last_1.data, parsed.message);
lastPromise.then(function (res) { return last_1.defer.resolve(res); }, function (rej) { return last_1.defer.reject(rej); });
}
}
};
/**
* Send all the member functions to IPC
*/
RequesterResponder.prototype.sendAllToSocket = function (contract) {
var _this = this;
var toret = {};
Object.keys(contract).forEach(function (key) {
toret[key] = _this.sendToSocket(contract[key], key);
});
return toret;
};
/**
* Takes a sync named function
* and returns a function that will execute this function by name using IPC
* (will only work if the process on the other side has this function as a registered responder)
*/
RequesterResponder.prototype.sendToSocket = function (func, name) {
var _this = this;
var message = name;
return function (data) { return _this.sendToServerHeart(data, message); };
};
/**
* If there are more than one pending then we only want the last one as they come in.
* All others will get the default value
*/
RequesterResponder.prototype.sendToSocketOnlyLast = function (func, defaultResponse, name) {
var _this = this;
return function (data) {
var message = name;
// If we don't have a child exit
if (!_this.getSocket()) {
console.log('SEND ERR: no socket when you tried to send :', message);
return Promise.reject(new Error("No worker active to recieve message: " + message));
}
// Allow if this is the only call of this type
if (!Object.keys(_this.currentListeners[message] || {}).length) {
return _this.sendToServerHeart(data, message);
}
else {
// Note:
// The last needs to continue once the current one finishes
// That is done in our response handler
// If there is already something queued as last.
// Then it is no longer last and needs to be fed a default value
if (_this.currentLastOfType[message]) {
_this.currentLastOfType[message].defer.resolve(defaultResponse);
}
// this needs to be the new last
var promise_1 = new Promise(function (resolve, reject) {
_this.currentLastOfType[message] = {
data: data,
defer: { promise: promise_1, resolve: resolve, reject: reject }
};
});
return promise_1;
}
};
};
RequesterResponder.prototype.addToResponders = function (func, name) {
this.responders[name] = func;
};
RequesterResponder.prototype.registerAllFunctionsExportedFromAsResponders = function (aModule) {
var _this = this;
Object.keys(aModule)
.filter(function (funcName) { return typeof aModule[funcName] == 'function'; })
.forEach(function (funcName) { return _this.addToResponders(aModule[funcName], funcName); });
};
return RequesterResponder;
}());
exports.RequesterResponder = RequesterResponder;