UNPKG

alm

Version:

The best IDE for TypeScript

214 lines (213 loc) 9.56 kB
"use strict"; // 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;