UNPKG

noflo

Version:

Flow-Based Programming environment for JavaScript

862 lines (812 loc) 25.9 kB
(function() { var EventEmitter, Network, componentLoader, graph, internalSocket, platform, _, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; _ = require("underscore"); internalSocket = require("./InternalSocket"); graph = require("./Graph"); EventEmitter = require('events').EventEmitter; platform = require('./Platform'); if (!platform.isBrowser()) { componentLoader = require("./nodejs/ComponentLoader"); } else { componentLoader = require('./ComponentLoader'); } Network = (function(_super) { __extends(Network, _super); Network.prototype.processes = {}; Network.prototype.connections = []; Network.prototype.initials = []; Network.prototype.defaults = []; Network.prototype.graph = null; Network.prototype.startupDate = null; Network.prototype.portBuffer = {}; function Network(graph, options) { this.options = options != null ? options : {}; this.processes = {}; this.connections = []; this.initials = []; this.nextInitials = []; this.defaults = []; this.graph = graph; this.started = false; this.debug = true; this.connectionCount = 0; if (!platform.isBrowser()) { this.baseDir = graph.baseDir || process.cwd(); } else { this.baseDir = graph.baseDir || '/'; } this.startupDate = null; if (graph.componentLoader) { this.loader = graph.componentLoader; } else { this.loader = new componentLoader.ComponentLoader(this.baseDir, this.options); } } Network.prototype.uptime = function() { if (!this.startupDate) { return 0; } return new Date() - this.startupDate; }; Network.prototype.increaseConnections = function() { if (this.connectionCount === 0) { this.setStarted(true); } return this.connectionCount++; }; Network.prototype.decreaseConnections = function() { this.connectionCount--; if (this.connectionCount) { return; } if (!this.debouncedEnd) { this.debouncedEnd = _.debounce((function(_this) { return function() { if (_this.connectionCount) { return; } return _this.setStarted(false); }; })(this), 50); } return this.debouncedEnd(); }; Network.prototype.load = function(component, metadata, callback) { return this.loader.load(component, callback, metadata); }; Network.prototype.addNode = function(node, callback) { var process; if (this.processes[node.id]) { if (callback) { callback(null, this.processes[node.id]); } return; } process = { id: node.id }; if (!node.component) { this.processes[process.id] = process; if (callback) { callback(null, process); } return; } return this.load(node.component, node.metadata, (function(_this) { return function(err, instance) { var name, port, _ref, _ref1; if (err) { return callback(err); } instance.nodeId = node.id; process.component = instance; _ref = process.component.inPorts; for (name in _ref) { port = _ref[name]; if (!port || typeof port === 'function' || !port.canAttach) { continue; } port.node = node.id; port.nodeInstance = instance; port.name = name; } _ref1 = process.component.outPorts; for (name in _ref1) { port = _ref1[name]; if (!port || typeof port === 'function' || !port.canAttach) { continue; } port.node = node.id; port.nodeInstance = instance; port.name = name; } if (instance.isSubgraph()) { _this.subscribeSubgraph(process); } _this.subscribeNode(process); _this.processes[process.id] = process; if (callback) { return callback(null, process); } }; })(this)); }; Network.prototype.removeNode = function(node, callback) { if (!this.processes[node.id]) { return callback(new Error("Node " + node.id + " not found")); } this.processes[node.id].component.shutdown(); delete this.processes[node.id]; if (callback) { return callback(null); } }; Network.prototype.renameNode = function(oldId, newId, callback) { var name, port, process, _ref, _ref1; process = this.getNode(oldId); if (!process) { return callback(new Error("Process " + oldId + " not found")); } process.id = newId; _ref = process.component.inPorts; for (name in _ref) { port = _ref[name]; port.node = newId; } _ref1 = process.component.outPorts; for (name in _ref1) { port = _ref1[name]; port.node = newId; } this.processes[newId] = process; delete this.processes[oldId]; if (callback) { return callback(null); } }; Network.prototype.getNode = function(id) { return this.processes[id]; }; Network.prototype.connect = function(done) { var callStack, edges, initializers, nodes, serialize, setDefaults, subscribeGraph; if (done == null) { done = function() {}; } callStack = 0; serialize = (function(_this) { return function(next, add) { return function(type) { return _this["add" + type](add, function(err) { if (err) { console.log(err); } if (err) { return done(err); } callStack++; if (callStack % 100 === 0) { setTimeout(function() { return next(type); }, 0); return; } return next(type); }); }; }; })(this); subscribeGraph = (function(_this) { return function() { _this.subscribeGraph(); return done(); }; })(this); setDefaults = _.reduceRight(this.graph.nodes, serialize, subscribeGraph); initializers = _.reduceRight(this.graph.initializers, serialize, function() { return setDefaults("Defaults"); }); edges = _.reduceRight(this.graph.edges, serialize, function() { return initializers("Initial"); }); nodes = _.reduceRight(this.graph.nodes, serialize, function() { return edges("Edge"); }); return nodes("Node"); }; Network.prototype.connectPort = function(socket, process, port, index, inbound) { if (inbound) { socket.to = { process: process, port: port, index: index }; if (!(process.component.inPorts && process.component.inPorts[port])) { throw new Error("No inport '" + port + "' defined in process " + process.id + " (" + (socket.getId()) + ")"); return; } if (process.component.inPorts[port].isAddressable()) { return process.component.inPorts[port].attach(socket, index); } return process.component.inPorts[port].attach(socket); } socket.from = { process: process, port: port, index: index }; if (!(process.component.outPorts && process.component.outPorts[port])) { throw new Error("No outport '" + port + "' defined in process " + process.id + " (" + (socket.getId()) + ")"); return; } if (process.component.outPorts[port].isAddressable()) { return process.component.outPorts[port].attach(socket, index); } return process.component.outPorts[port].attach(socket); }; Network.prototype.subscribeGraph = function() { var graphOps, processOps, processing, registerOp; graphOps = []; processing = false; registerOp = function(op, details) { return graphOps.push({ op: op, details: details }); }; processOps = (function(_this) { return function(err) { var cb, op; if (err) { if (_this.listeners('process-error').length === 0) { throw err; } _this.emit('process-error', err); } if (!graphOps.length) { processing = false; return; } processing = true; op = graphOps.shift(); cb = processOps; switch (op.op) { case 'renameNode': return _this.renameNode(op.details.from, op.details.to, cb); default: return _this[op.op](op.details, cb); } }; })(this); this.graph.on('addNode', (function(_this) { return function(node) { registerOp('addNode', node); if (!processing) { return processOps(); } }; })(this)); this.graph.on('removeNode', (function(_this) { return function(node) { registerOp('removeNode', node); if (!processing) { return processOps(); } }; })(this)); this.graph.on('renameNode', (function(_this) { return function(oldId, newId) { registerOp('renameNode', { from: oldId, to: newId }); if (!processing) { return processOps(); } }; })(this)); this.graph.on('addEdge', (function(_this) { return function(edge) { registerOp('addEdge', edge); if (!processing) { return processOps(); } }; })(this)); this.graph.on('removeEdge', (function(_this) { return function(edge) { registerOp('removeEdge', edge); if (!processing) { return processOps(); } }; })(this)); this.graph.on('addInitial', (function(_this) { return function(iip) { registerOp('addInitial', iip); if (!processing) { return processOps(); } }; })(this)); return this.graph.on('removeInitial', (function(_this) { return function(iip) { registerOp('removeInitial', iip); if (!processing) { return processOps(); } }; })(this)); }; Network.prototype.subscribeSubgraph = function(node) { var emitSub; if (!node.component.isReady()) { node.component.once('ready', (function(_this) { return function() { return _this.subscribeSubgraph(node); }; })(this)); return; } if (!node.component.network) { return; } node.component.network.setDebug(this.debug); emitSub = (function(_this) { return function(type, data) { if (type === 'process-error' && _this.listeners('process-error').length === 0) { throw data; } if (type === 'connect') { _this.increaseConnections(); } if (type === 'disconnect') { _this.decreaseConnections(); } if (!data) { data = {}; } if (data.subgraph) { if (!data.subgraph.unshift) { data.subgraph = [data.subgraph]; } data.subgraph = data.subgraph.unshift(node.id); } else { data.subgraph = [node.id]; } return _this.emit(type, data); }; })(this); node.component.network.on('connect', function(data) { return emitSub('connect', data); }); node.component.network.on('begingroup', function(data) { return emitSub('begingroup', data); }); node.component.network.on('data', function(data) { return emitSub('data', data); }); node.component.network.on('endgroup', function(data) { return emitSub('endgroup', data); }); node.component.network.on('disconnect', function(data) { return emitSub('disconnect', data); }); return node.component.network.on('process-error', function(data) { return emitSub('process-error', data); }); }; Network.prototype.subscribeSocket = function(socket) { socket.on('connect', (function(_this) { return function() { _this.increaseConnections(); return _this.emit('connect', { id: socket.getId(), socket: socket, metadata: socket.metadata }); }; })(this)); socket.on('begingroup', (function(_this) { return function(group) { return _this.emit('begingroup', { id: socket.getId(), socket: socket, group: group, metadata: socket.metadata }); }; })(this)); socket.on('data', (function(_this) { return function(data) { return _this.emit('data', { id: socket.getId(), socket: socket, data: data, metadata: socket.metadata }); }; })(this)); socket.on('endgroup', (function(_this) { return function(group) { return _this.emit('endgroup', { id: socket.getId(), socket: socket, group: group, metadata: socket.metadata }); }; })(this)); socket.on('disconnect', (function(_this) { return function() { _this.decreaseConnections(); return _this.emit('disconnect', { id: socket.getId(), socket: socket, metadata: socket.metadata }); }; })(this)); return socket.on('error', (function(_this) { return function(event) { if (_this.listeners('process-error').length === 0) { throw event; } return _this.emit('process-error', event); }; })(this)); }; Network.prototype.subscribeNode = function(node) { if (!node.component.getIcon) { return; } return node.component.on('icon', (function(_this) { return function() { return _this.emit('icon', { id: node.id, icon: node.component.getIcon() }); }; })(this)); }; Network.prototype.addEdge = function(edge, callback) { var from, socket, to; socket = internalSocket.createSocket(edge.metadata); socket.setDebug(this.debug); from = this.getNode(edge.from.node); if (!from) { return callback(new Error("No process defined for outbound node " + edge.from.node)); } if (!from.component) { return callback(new Error("No component defined for outbound node " + edge.from.node)); } if (!from.component.isReady()) { from.component.once("ready", (function(_this) { return function() { return _this.addEdge(edge, callback); }; })(this)); return; } to = this.getNode(edge.to.node); if (!to) { return callback(new Error("No process defined for inbound node " + edge.to.node)); } if (!to.component) { return callback(new Error("No component defined for inbound node " + edge.to.node)); } if (!to.component.isReady()) { to.component.once("ready", (function(_this) { return function() { return _this.addEdge(edge, callback); }; })(this)); return; } this.subscribeSocket(socket); this.connectPort(socket, to, edge.to.port, edge.to.index, true); this.connectPort(socket, from, edge.from.port, edge.from.index, false); this.connections.push(socket); if (callback) { return callback(); } }; Network.prototype.removeEdge = function(edge, callback) { var connection, _i, _len, _ref, _results; _ref = this.connections; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { connection = _ref[_i]; if (!connection) { continue; } if (!(edge.to.node === connection.to.process.id && edge.to.port === connection.to.port)) { continue; } connection.to.process.component.inPorts[connection.to.port].detach(connection); if (edge.from.node) { if (connection.from && edge.from.node === connection.from.process.id && edge.from.port === connection.from.port) { connection.from.process.component.outPorts[connection.from.port].detach(connection); } } this.connections.splice(this.connections.indexOf(connection), 1); if (callback) { _results.push(callback()); } else { _results.push(void 0); } } return _results; }; Network.prototype.addDefaults = function(node, callback) { var key, port, process, socket, _ref; process = this.processes[node.id]; if (!process.component.isReady()) { if (process.component.setMaxListeners) { process.component.setMaxListeners(0); } process.component.once("ready", (function(_this) { return function() { return _this.addDefaults(process, callback); }; })(this)); return; } _ref = process.component.inPorts.ports; for (key in _ref) { port = _ref[key]; if (typeof port.hasDefault === 'function' && port.hasDefault() && !port.isAttached()) { socket = internalSocket.createSocket(); socket.setDebug(this.debug); this.subscribeSocket(socket); this.connectPort(socket, process, key, void 0, true); this.connections.push(socket); this.defaults.push(socket); } } if (callback) { return callback(); } }; Network.prototype.addInitial = function(initializer, callback) { var init, socket, to; socket = internalSocket.createSocket(initializer.metadata); socket.setDebug(this.debug); this.subscribeSocket(socket); to = this.getNode(initializer.to.node); if (!to) { return callback(new Error("No process defined for inbound node " + initializer.to.node)); } if (!(to.component.isReady() || to.component.inPorts[initializer.to.port])) { if (to.component.setMaxListeners) { to.component.setMaxListeners(0); } to.component.once("ready", (function(_this) { return function() { return _this.addInitial(initializer, callback); }; })(this)); return; } this.connectPort(socket, to, initializer.to.port, initializer.to.index, true); this.connections.push(socket); init = { socket: socket, data: initializer.from.data }; this.initials.push(init); this.nextInitials.push(init); if (this.isStarted()) { this.sendInitials(); } if (callback) { return callback(); } }; Network.prototype.removeInitial = function(initializer, callback) { var connection, init, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2; _ref = this.connections; for (_i = 0, _len = _ref.length; _i < _len; _i++) { connection = _ref[_i]; if (!connection) { continue; } if (!(initializer.to.node === connection.to.process.id && initializer.to.port === connection.to.port)) { continue; } connection.to.process.component.inPorts[connection.to.port].detach(connection); this.connections.splice(this.connections.indexOf(connection), 1); _ref1 = this.initials; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { init = _ref1[_j]; if (!init) { continue; } if (init.socket !== connection) { continue; } this.initials.splice(this.initials.indexOf(init), 1); } _ref2 = this.nextInitials; for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { init = _ref2[_k]; if (!init) { continue; } if (init.socket !== connection) { continue; } this.nextInitials.splice(this.nextInitials.indexOf(init), 1); } } if (callback) { return callback(); } }; Network.prototype.sendInitial = function(initial) { initial.socket.connect(); initial.socket.send(initial.data); return initial.socket.disconnect(); }; Network.prototype.sendInitials = function(callback) { var send; if (!callback) { callback = function() {}; } send = (function(_this) { return function() { var initial, _i, _len, _ref; _ref = _this.initials; for (_i = 0, _len = _ref.length; _i < _len; _i++) { initial = _ref[_i]; _this.sendInitial(initial); } _this.initials = []; return callback(); }; })(this); if (typeof process !== 'undefined' && process.execPath && process.execPath.indexOf('node') !== -1) { return process.nextTick(send); } else { return setTimeout(send, 0); } }; Network.prototype.isStarted = function() { return this.started; }; Network.prototype.isRunning = function() { if (!this.started) { return false; } return this.connectionCount > 0; }; Network.prototype.startComponents = function(callback) { var id, process, _ref; if (!callback) { callback = function() {}; } _ref = this.processes; for (id in _ref) { process = _ref[id]; process.component.start(); } return callback(); }; Network.prototype.sendDefaults = function(callback) { var socket, _i, _len, _ref; if (!callback) { callback = function() {}; } if (!this.defaults.length) { return callback(); } _ref = this.defaults; for (_i = 0, _len = _ref.length; _i < _len; _i++) { socket = _ref[_i]; if (socket.to.process.component.inPorts[socket.to.port].sockets.length !== 1) { continue; } socket.connect(); socket.send(); socket.disconnect(); } return callback(); }; Network.prototype.start = function(callback) { if (!callback) { callback = function() {}; } if (this.started) { this.stop(); } this.initials = this.nextInitials.slice(0); return this.startComponents((function(_this) { return function(err) { if (err) { return callback(err); } return _this.sendInitials(function(err) { if (err) { return callback(err); } return _this.sendDefaults(function(err) { if (err) { return callback(err); } _this.setStarted(true); return callback(null); }); }); }; })(this)); }; Network.prototype.stop = function() { var connection, id, process, _i, _len, _ref, _ref1; _ref = this.connections; for (_i = 0, _len = _ref.length; _i < _len; _i++) { connection = _ref[_i]; if (!connection.isConnected()) { continue; } connection.disconnect(); } _ref1 = this.processes; for (id in _ref1) { process = _ref1[id]; process.component.shutdown(); } return this.setStarted(false); }; Network.prototype.setStarted = function(started) { if (this.started === started) { return; } if (!started) { this.started = false; this.emit('end', { start: this.startupDate, end: new Date, uptime: this.uptime() }); return; } if (!this.startupDate) { this.startupDate = new Date; } this.started = true; return this.emit('start', { start: this.startupDate }); }; Network.prototype.getDebug = function() { return this.debug; }; Network.prototype.setDebug = function(active) { var instance, process, processId, socket, _i, _len, _ref, _ref1, _results; if (active === this.debug) { return; } this.debug = active; _ref = this.connections; for (_i = 0, _len = _ref.length; _i < _len; _i++) { socket = _ref[_i]; socket.setDebug(active); } _ref1 = this.processes; _results = []; for (processId in _ref1) { process = _ref1[processId]; instance = process.component; if (instance.isSubgraph()) { _results.push(instance.network.setDebug(active)); } else { _results.push(void 0); } } return _results; }; return Network; })(EventEmitter); exports.Network = Network; }).call(this);